Stack used: Python, Flask App, plain JavaScript in the frontend
Yeah, I know this title is kind of mystic, but I honestly don’t know how to phrase it any better. Here’s my situation:
- I have a vector of a file I uploaded. I added this file to an agent. The agent is set to “file search” and response_format “auto,” with the instructions. I also provide an example of the output I expect, like:
... some important instructions ...
example answer:
{
"KEY1": "VALUE1",
"KEY2": [
{"KEY3": "VALUE2", "KEY4": "VALUE3", "KEY5": "VALUE4", "KEY6": "VALUE5", "KEY7": ["VALUE6", "VALUE7", "VALUE8"]},
{"KEY3": "VALUE9", "KEY4": "VALUE10", "KEY5": "VALUE11", "KEY6": "VALUE12", "KEY7": ["VALUE13", "VALUE14"]}
],
"KEY8": None
}
- I create a thread, add a message with a starting prompt, and run the thread with streaming enabled because streaming is what I want.
The API now returns chunks of the JSON, which I have to manually “sanitize,” not a problem at all. Eventually, I have the streaming mechanism implemented successfully.
BUT… here it comes:
The API does not respond with proper JSON; it’s using single quotes instead of double quotes. I assume it’s because I am providing this “quick’n’dirty” JSON schema within the instructions only.
So I tried going the “official” way:
I tried to define the response schema and edit either the assistant or provide it at “run time” via “response_format”:
Something like this:
response_format={"type": "json_schema", "json_schema": response_schema}
There are two ways to define the response_schema:
The easy way:
response_schema = {
"name": "ConfigurationAssistant",
"schema": {
"type": "object",
"properties": {
"KEY1": {
"type": "string"
},
"KEY2": {
"type": "array",
"items": {
"type": "object",
"properties": {
"KEY3": {"type": "string"},
"KEY4": {"type": "string"},
"KEY5": {"type": "string"},
"KEY6": {"type": "string"},
"KEY7": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
},
"KEY8": {
"type": "array",
"items": {
"type": "object",
"properties": {
"KEY3": {"type": "string"},
"KEY4": {"type": "string"},
"KEY5": {"type": "string"},
"KEY6": {"type": "string"}
}
}
}
},
"required": ["KEY1", "KEY2", "KEY8"]
}
}
Or the hard way:
class OpenAiResponseFormatGenerator(pydantic.json_schema.GenerateJsonSchema):
# https://docs.pydantic.dev/latest/concepts/json_schema/#customizing-the-json-schema-generation-process
def generate(self, schema, mode="validation"):
json_schema = super().generate(schema, mode=mode)
json_schema = {
"type": "json_schema",
"json_schema": {
"name": json_schema.pop("title"),
"schema": {
"type": "object",
"properties": json_schema["properties"],
"required": json_schema.get("required", [])
}
}
}
return json_schema
class StrictBaseModel(pydantic.BaseModel):
model_config = {"extra": "forbid"}
@classmethod
def model_json_schema(cls, **kwargs):
return super().model_json_schema(
schema_generator=OpenAiResponseFormatGenerator, **kwargs
)
class Suggestion(StrictBaseModel):
KEY1: str
KEY2: str
KEY3: str
KEY4: str
class ConfigurationAssistant(StrictBaseModel):
KEY5: str
KEY6: list[Suggestion]
KEY7: list[Suggestion]
required: list[str] = ["KEY5", "KEY6", "KEY7"]
response_schema = ConfigurationAssistant.model_json_schema()
# Adjust the generated schema to match the desired dictionary structure
response_schema = {
"name": "ConfigurationAssistant",
"schema": response_schema["json_schema"]["schema"]
}
Running a thread now never leads to an end. Either I get messages like “active run,” or it just runs into timeouts.
ReadTimeout: The read operation timed out
or
“message”: “Can’t add messages to thread_abc123 is active.”, ‘type’: ‘invalid_request_error’
Apparently, I am doing something wrong with my response schema, and I tend to stick to my quick’n’dirty solution as it is working quite fine.
But also, I want to understand what is the proper way to define a neat response JSON schema?