Closed yudhiesh closed 3 weeks ago
Hi @yudhiesh, I'm looking into this now and will get back to you once I've found something.
One thing to call out is that we still have a PR open with LangChain to update the GuardrailsOutputParser in order to support Guardrails >=0.3.x, so it may not work with the newer versions at the moment: https://github.com/langchain-ai/langchain/pull/14657
@yudhiesh along with your updates to GuardrailsOutputParser.from_pydantic
, have you tried specifying the prompt as we show in the integration docs?
This would look like:
ZERO_SHOT_PROMPT_GUARDRAILS_TEMPLATE = """
Generate PostgreSQL code based on the query.
Only generate PostgreSQL code and nothing else.
Explanations are not needed.
Query: ${query}
${gr.complete_json_suffix_v2}
"""
output_parser = GuardrailsOutputParser.from_pydantic(LLMResponse, api=openai.chat.completions.create, prompt=ZERO_SHOT_PROMPT_GUARDRAILS_TEMPLATE)
prompt = PromptTemplate(
template=output_parser.guard.prompt.escape(),
input_variables=output_parser.guard.prompt.variable_names,
)
output = llm(prompt.format_prompt(query="Select the name of the employee who has the highest salary.").to_string())
I believe you will still encounter the version mismatch issues address in the above referenced PR, but this should resolve the prompt formatting issue you're encountering.
@yudhiesh along with your updates to
GuardrailsOutputParser.from_pydantic
, have you tried specifying the prompt as we show in the integration docs?This would look like:
ZERO_SHOT_PROMPT_GUARDRAILS_TEMPLATE = """ Generate PostgreSQL code based on the query. Only generate PostgreSQL code and nothing else. Explanations are not needed. Query: ${query} ${gr.complete_json_suffix_v2} """ output_parser = GuardrailsOutputParser.from_pydantic(LLMResponse, api=openai.chat.completions.create, prompt=ZERO_SHOT_PROMPT_GUARDRAILS_TEMPLATE) prompt = PromptTemplate( template=output_parser.guard.prompt.escape(), input_variables=output_parser.guard.prompt.variable_names, ) output = llm(prompt.format_prompt(query="Select the name of the employee who has the highest salary.").to_string())
I believe you will still encounter the version mismatch issues address in the above referenced PR, but this should resolve the prompt formatting issue you're encountering.
Lemme give it a try and get back to you.
@yudhiesh along with your updates to
GuardrailsOutputParser.from_pydantic
, have you tried specifying the prompt as we show in the integration docs?This would look like:
ZERO_SHOT_PROMPT_GUARDRAILS_TEMPLATE = """ Generate PostgreSQL code based on the query. Only generate PostgreSQL code and nothing else. Explanations are not needed. Query: ${query} ${gr.complete_json_suffix_v2} """ output_parser = GuardrailsOutputParser.from_pydantic(LLMResponse, api=openai.chat.completions.create, prompt=ZERO_SHOT_PROMPT_GUARDRAILS_TEMPLATE) prompt = PromptTemplate( template=output_parser.guard.prompt.escape(), input_variables=output_parser.guard.prompt.variable_names, ) output = llm(prompt.format_prompt(query="Select the name of the employee who has the highest salary.").to_string())
I believe you will still encounter the version mismatch issues address in the above referenced PR, but this should resolve the prompt formatting issue you're encountering.
I downgraded to guardrails-ai==0.2.9
and tried the code but got the following error:
---------------------------------------------------------------------------
BadRequestError Traceback (most recent call last)
[<ipython-input-15-cbf49d77c939>](https://localhost:8080/#) in <cell line: 26>()
24 )
25
---> 26 output = llm(prompt.format_prompt(query=SAMPLE_QUERY).to_string())
11 frames
[/usr/local/lib/python3.10/dist-packages/openai/_base_client.py](https://localhost:8080/#) in _request(self, cast_to, options, remaining_retries, stream, stream_cls)
957
958 log.debug("Re-raising status error")
--> 959 raise self._make_status_error_from_response(err.response) from None
960
961 return self._process_response(
BadRequestError: Error code: 400 - {'error': {'message': "[] is too short - 'messages'", 'type': 'invalid_request_error', 'param': None, 'code': None}}
@yudhiesh my apologies, the example of constructing the prompt template I posted is for using the completions API. If you are using a chat model you will likely want to construct the prompt template as messages like this:
from langchain.prompts.chat import (
ChatPromptTemplate,
HumanMessagePromptTemplate,
SystemMessagePromptTemplate,
)
system_message_prompt = SystemMessagePromptTemplate.from_template(
template=output_parser.guard.instructions.escape(),
input_variables=output_parser.guard.instructions.variable_names,
)
human_message_prompt = HumanMessagePromptTemplate.from_template(
template=output_parser.guard.prompt.escape(),
input_variables=output_parser.guard.prompt.variable_names,
)
chat_prompt = ChatPromptTemplate.from_messages(
[system_message_prompt, human_message_prompt]
)
Then when you call the llm, you'll likely want to use to_messages
instead of to_string
:
output = llm(chat_prompt.format_prompt(query="Select the name of the employee who has the highest salary.").to_messages())
output = llm(chat_prompt.format_prompt(query="Select the name of the employee who has the highest salary.").to_messages())
Still throws an error, but this time due to the Guard
object not having any instructions Guard(RAIL=Rail(input_schema=None, output_schema=JsonSchema(Object({'text': String({})})), instructions=None, prompt=Prompt(), version='0.1'))
system_message_prompt = SystemMessagePromptTemplate.from_template(
template=output_parser.guard.instructions.escape(), # Throws an AttributeError
input_variables=output_parser.guard.instructions.variable_names,
)
I guess for now I will not use Langchain with Guardrails until things are more stable and will fallback to the following code:
import openai
from rich import print
import guardrails as gd
ZERO_SHOT_PROMPT_GUARDRAILS_TEMPLATE = """
Generate a valid SQL query for the following natural language instruction:
${query}
${gr.complete_json_suffix_v2}
"""
guard = gd.Guard.from_pydantic(output_class=LLMResponse, prompt=ZERO_SHOT_PROMPT_GUARDRAILS_TEMPLATE)
raw_llm_output, validated_output, *rest = guard(
llm_api=openai.chat.completions.create,
model=MODEL_NAME,
prompt_params={
"query": SAMPLE_QUERY
},
)
Just that I will need to handle retries to OpenAI myself or via another library for now as Langchain did that for me.
@yudhiesh I would say that this issue is partially fixed by that PR which was released in guardrails 0.4.0. You should now be able to use any guard in a Langchain Expression Language chain (I'll include an example below). The caveat is that we do not yet support reasking with this pattern yet.
Example:
from rich import print
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from guardrails import Guard
from guardrails.validators import ValidRange
from pydantic import BaseModel, Field
class Patient(BaseModel):
gender: str = Field(description="Patient's gender")
age: int = Field(validators=[ValidRange(min=0, max=100, on_fail="fix")])
symptoms: str = Field(description="Symptoms that the patient is currently experiencing")
prompt = ChatPromptTemplate.from_template("""
You are a helpful assistant only capable of communicating with valid JSON, and no other text.
Given the following doctor's notes about a patient, please extract a dictionary that contains the patient's information.
{doctors_notes}
ONLY return a valid JSON object (no other text is necessary). Be correct and concise.
Here's an example:
```json
{{
"gender": "Female",
"age": 42,
"symptoms": "hair loss, loss of appetite, sudden decrease in bodyweight"
}}
""")
doctors_notes = """ 49 y/o Male with chronic macular rash to face & hair, worse in beard, eyebrows & nares. Itchy, flaky, slightly scaly. Moderate response to OTC steroid cream """
model = ChatOpenAI(model="gpt-3.5-turbo")
guard = Guard.from_pydantic(output_class=Patient)
output_parser = JsonOutputParser()
chain = prompt | model | guard | output_parser
response = chain.invoke({ "doctors_notes": doctors_notes })
print("type(response): ", type(response)) print("response: ", response)
print(guard.history.last.tree)
Thanks let me try that out! I would be more than happy to help out with the reasking logic but would need some assistance with understanding how it works.
This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 14 days.
This issue was closed because it has been stalled for 14 days with no activity.
Describe the bug I am referencing the documentation about the LangChain Integration which only showcases how it works using
RAILSPEC
and trying to make it work with Pydantic.To Reproduce Steps to reproduce the behavior:
class LLMResponse(BaseModel): generated_sql : str = Field(description="Generated SQL for the given natural language instruction.")
This throws the following error:
KeyError Traceback (most recent call last)
7 frames
/usr/lib/python3.10/string.py in get_value(self, key, args, kwargs) 225 return args[key] 226 else: --> 227 return kwargs[key] 228 229
KeyError: 'gr'
Should be:
Library version:
Additional context I have tried making some changes to the code as I noticed in GuardrailsOutputParser.from_pydantic() that the prompt is set by default to a
""
. I made a change to get it to reference the prompt as a kwargThis seems to fix it to a certain extent where now the output_parser
Prompt Template:
Prompt from Output Parser:
Output from above:
But when I try to validate using:
I get the following error:
Which seems to be caused by
${gr.complete_json_suffix_v2}
not being picked up and updated with the JSON Schema prompt which is added via the RAILSpec.