Open Royisaboy opened 3 months ago
@Royisaboy Hey there! I'm here to help you with any bugs, questions, or contributions you might have. Let's get to the bottom of this together.
To resolve the ValueError: Single '}' encountered in format string
when using pydantic response with the chat engine in LlamaIndex version 0.10.36, you need to ensure that any braces in your strings are properly escaped. The provided code already includes a utility function _escape_braces
that handles this.
You should use the _escape_braces
function to escape braces in your chat messages before they are processed. This function is already being used in the _escape_json
function, which is applied to the messages in the chat
and astream_chat
methods.
Here is the relevant part of the code that shows how _escape_braces
is used:
def _escape_braces(text: str) -> str:
"""
Escape braces in text.
Only captures template variables, skips already escaped braces.
"""
def replace(match):
if match.group(0).startswith("{{") and match.group(0).endswith("}}"):
return match.group(0) # Already escaped, return as is
return "{{" + match.group(1) + "}}"
pattern = r"(?<!\{)\{([^{}]+?)\}(?!\})"
return re.sub(pattern, replace, text)
def _escape_json(messages: Sequence[ChatMessage]) -> Sequence[ChatMessage]:
"""Escape JSON in messages."""
new_messages = []
for message in messages:
if isinstance(message.content, str):
escaped_msg = _escape_braces(message.content)
new_messages.append(
ChatMessage(
role=message.role,
content=escaped_msg,
additional_kwargs=message.additional_kwargs,
)
)
else:
new_messages.append(message)
return new_messages
In the chat
method, _escape_json
is called to escape the braces in the messages:
@llm_chat_callback()
def chat(self, messages: Sequence[ChatMessage], **kwargs: Any) -> ChatResponse:
"""Chat endpoint for LLM."""
chat_prompt = ChatPromptTemplate(message_templates=_escape_json(messages))
output = self.llm.structured_predict(
output_cls=self.output_cls, prompt=chat_prompt
)
return ChatResponse(
message=ChatMessage(
role=MessageRole.ASSISTANT, content=output.model_dump_json()
),
raw=output,
)
Ensure that your messages are passed through _escape_json
before being used in the chat engine to avoid the ValueError
[1].
To continue talking to Dosu, mention @dosu.
I suspect you are using llama-index-core v0.11.x with lower versions of llama-index-embeddings-openai
We migrated to pydantic v2 in v0.11.x and you'll need to use the latest versions of integration packages as well for compatibility
UPDATE:
My bad, it looks like I have it the other way around. You're core is llama-index-core
v0.10.36 but maybe you're using the latest version of llama-index-embeddings-openai
If you install these versions of the packages, then everything should run as expected.
!pip install llama-index-vector-stores-pinecone==0.2.1 llama-index-embeddings-openai==0.2.3
!pip install llama-index==0.11.2
KeyError Traceback (most recent call last)
Sorry, could we also import both BaseModel
and Field
from our pydantic
bridge:
from llama_index.core.bridge.pydantic import BaseModel, Field
still same error
Could you try this and see if you get the same errors? I was able to run through this code with no problem. It looks like there's an issue with the output from the llm from your traceback, where there is an extra "\n " in the project_name
key.
from llama_index.core.bridge.pydantic import BaseModel, Field
from llama_index.core.llms import ChatMessage
from typing import List
class ExecSummary(BaseModel):
"""Data model for a executive summary for a project."""
project_name: str = Field(..., description="The project name.")
summary: str = Field(..., description="A high level summary of the project progress. Please limit it to max 100 characters.")
risk_description: str = Field(..., description="A risk description that highlights the most outstanding risk. Please limit it to max 15 characters")
risk_level: str = Field(..., description="A risk level that is choosen from 'high', 'medium' and 'low'.")
class AllExecSummary(BaseModel):
"""Data model for a list of executive summaries for all projects."""
summaries: List[ExecSummary] = Field(..., description="A list of executive summaries.")
sllm = llm.as_structured_llm(output_cls=AllExecSummary)
input_msg = ChatMessage.from_str("Generate an all exec summary.")
output = sllm.chat([input_msg])
output_obj = output.raw
print(output_obj)
this one works for me (it also worked before) but why it doesn't work for chat engine?
I suspect there's something not right with the LLM output of the chat engine which breaks things when trying to construct the Pydantic BaseModel. Doing some more digging.
Would you be able to share a bit more code for me to replicate the error?
openai.api_key = "XXX" embed_model = OpenAIEmbedding(model="text-embedding-3-small")
llm = llama_index_openai(temperature=0.1, model="gpt-4o") pc = Pinecone(api_key="XXX") pinecone_index = pc.Index("get_started") vector_store = PineconeVectorStore(pinecone_index=pinecone_index) loaded_index = VectorStoreIndex.from_vector_store(vector_store=vector_store, embed_model=embed_model) sllm = llm.as_structured_llm(AllExecSummary) query_engine = loaded_index.as_chat_engine( chat_mode="context", similarity_top_k=5, llm=sllm ) prompt = ''' Provide executive summaries for project amber and project lina from last two weeks. The response of this instruction must be in JSON format. ''' response = query_engine.chat(prompt)
hmmm this seems to work for me rather than producing the error from before (shared again below)
KeyError: '\n "project_name"'
project_name: str = Field(..., description="The project name.")
The other thing of note tho is that my response.response
is not a Pydantic object but rather is a str
type (doesn't look like we support pydantic objects as outputs with chat_engine.chat()
.
can you share your full working codes?
%pip install llama-index-vector-stores-pinecone==0.2.1 llama-index-embeddings-openai==0.2.3 -q
import json
from llama_index.core import VectorStoreIndex
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.vector_stores.pinecone import PineconeVectorStore
from pinecone import Pinecone
from llama_index.core.bridge.pydantic import BaseModel, Field
from llama_index.core.llms import ChatMessage
from typing import List
class ExecSummary(BaseModel):
"""Data model for a executive summary for a project."""
project_name: str = Field(..., description="The project name.")
summary: str = Field(..., description="A high level summary of the project progress. Please limit it to max 100 characters.")
risk_description: str = Field(..., description="A risk description that highlights the most outstanding risk. Please limit it to max 15 characters")
risk_level: str = Field(..., description="A risk level that is choosen from 'high', 'medium' and 'low'.")
class AllExecSummary(BaseModel):
"""Data model for a list of executive summaries for all projects."""
summaries: List[ExecSummary] = Field(..., description="A list of executive summaries.")
pinecone_api_key = "..."
embed_model = OpenAIEmbedding(model="text-embedding-3-small")
llm = OpenAI(temperature=0.1, model="gpt-4o")
pc = Pinecone(api_key=pinecone_api_key)
pinecone_index = pc.Index("quickstart")
vector_store = PineconeVectorStore(pinecone_index=pinecone_index)
loaded_index = VectorStoreIndex.from_vector_store(vector_store=vector_store, embed_model=embed_model)
sllm = llm.as_structured_llm(output_cls=AllExecSummary)
chat_engine = loaded_index.as_chat_engine(
chat_mode="context",
similarity_top_k=5,
llm=sllm
)
prompt = '''
Provide executive summaries for project amber and project lina from last two weeks.
You must call the tool to generate the formatted output.
The response of this instruction must be in JSON format.
'''
response = chat_engine.chat(prompt)
obj = AllExecSummary(**json.loads(response.response))
it cannot be reproduced. for example, openai is not being imported. but after i get all libraries ready i saw same issue. can you show a full google colab script?
Hmmm. I don't really need to import openai directly here. The script above should work completely, though it does require two api keys:
I can share a Google collab version of this shortly.
@Royisaboy: here is a google colab version of the code snippet I shared earlier.
same code producing error when i am trying to run it on my end:
KeyError Traceback (most recent call last)
10 frames /usr/local/lib/python3.10/dist-packages/llama_index/core/instrumentation/dispatcher.py in wrapper(func, instance, args, kwargs) 259 ) 260 try: --> 261 result = func(*args, **kwargs) 262 except BaseException as e: 263 self.event(SpanDropEvent(spanid=id, err_str=str(e)))
/usr/local/lib/python3.10/dist-packages/llama_index/core/callbacks/utils.py in wrapper(self, *args, *kwargs) 39 callback_manager = cast(CallbackManager, callback_manager) 40 with callback_manager.as_trace(trace_id): ---> 41 return func(self, args, **kwargs) 42 43 @functools.wraps(func) # preserve signature, name, etc. of func
/usr/local/lib/python3.10/dist-packages/llama_index/core/chat_engine/context.py in chat(self, message, chat_history, prev_chunks) 184 initial_token_count=prefix_messages_token_count 185 ) --> 186 chat_response = self._llm.chat(all_messages) 187 ai_message = chat_response.message 188 self._memory.put(ai_message)
/usr/local/lib/python3.10/dist-packages/llama_index/core/instrumentation/dispatcher.py in wrapper(func, instance, args, kwargs) 259 ) 260 try: --> 261 result = func(*args, **kwargs) 262 except BaseException as e: 263 self.event(SpanDropEvent(spanid=id, err_str=str(e)))
/usr/local/lib/python3.10/dist-packages/llama_index/core/llms/callbacks.py in wrapped_llm_chat(_self, messages, kwargs) 174 ) 175 try: --> 176 f_return_val = f(_self, messages, kwargs) 177 except BaseException as e: 178 callback_manager.on_event_end(
/usr/local/lib/python3.10/dist-packages/llama_index/core/llms/structured_llm.py in chat(self, messages, **kwargs) 107 chat_prompt = ChatPromptTemplate(message_templates=_escape_json(messages)) 108 --> 109 output = self.llm.structured_predict( 110 output_cls=self.output_cls, prompt=chat_prompt 111 )
/usr/local/lib/python3.10/dist-packages/llama_index/core/instrumentation/dispatcher.py in wrapper(func, instance, args, kwargs) 259 ) 260 try: --> 261 result = func(*args, **kwargs) 262 except BaseException as e: 263 self.event(SpanDropEvent(spanid=id, err_str=str(e)))
/usr/local/lib/python3.10/dist-packages/llama_index/core/llms/llm.py in structured_predict(self, output_cls, prompt, prompt_args) 360 ) 361 --> 362 result = program(prompt_args) 363 dispatcher.event(LLMStructuredPredictEndEvent(output=result)) 364 return result
/usr/local/lib/python3.10/dist-packages/llama_index/core/instrumentation/dispatcher.py in wrapper(func, instance, args, kwargs) 259 ) 260 try: --> 261 result = func(*args, **kwargs) 262 except BaseException as e: 263 self.event(SpanDropEvent(spanid=id, err_str=str(e)))
/usr/local/lib/python3.10/dist-packages/llama_index/core/program/function_program.py in call(self, llm_kwargs, *args, kwargs) 193 tool = _get_function_tool(self._output_cls) 194 --> 195 messages = self._prompt.format_messages(llm=self._llm, kwargs) 196 messages = self._llm._extend_messages(messages) 197
/usr/local/lib/python3.10/dist-packages/llama_index/core/prompts/base.py in format_messages(failed resolving arguments) 312 313 # if there's mappings specified, make sure those are used --> 314 content = content_template.format(**relevant_kwargs) 315 316 message: ChatMessage = message_template.model_copy()
KeyError: '\n "project_name"'
are you using python3.10?
Hi !
I encountered the same issue. I tried using llama-index-agent-openai==0.3.1 and 0.3.2 (latest version). I'm using in-memory storage so it probably has nothing to do with the pinecone db rather than the openAI package.
import json
import os
from typing import List
from llama_index.core import Settings, SimpleDirectoryReader, VectorStoreIndex
from llama_index.core.bridge.pydantic import BaseModel, Field
from llama_index.embeddings.openai import OpenAIEmbedding
from llama_index.llms.openai import OpenAI
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
class Movie(BaseModel):
name: str = Field(..., alias="name")
length: int = Field(..., alias="length")
class MovieList(BaseModel):
name: str = Field(..., alias="name")
movies: List[Movie] = Field(..., alias="movies")
Settings.embed_model = OpenAIEmbedding()
Settings.llm = OpenAI("gpt-4", temperature=0).as_structured_llm(
output_cls=MovieList
)
movies_lists = [
{
"name": "Horror movies list",
"movies": [
{"name": "The Conjuring", "length": 112},
{"name": "The Exorcist", "length": 122},
{"name": "The Shining", "length": 146},
],
},
{
"name": "Sci-fi movies list",
"movies": [
{"name": "Star Wars", "length": 121},
{"name": "Interstellar", "length": 169},
{"name": "The Matrix", "length": 136},
],
},
]
# Save as text file
with open("movie_list.txt", "w") as f:
f.write(json.dumps(movies_lists))
# load only the file
documents = SimpleDirectoryReader(input_files=["movie_list.txt"]).load_data()
index = VectorStoreIndex.from_documents(
documents,
)
query_engine = index.as_query_engine(response_mode="refine", verbose=True)
response = query_engine.query(
"Give me the horror movies lists and their associated movies"
)
# Print the response to debug
print(f"Response: {response}")
Output
python scripts/demo/testError.py
Traceback (most recent call last):
File "/home/ubuntu/Documents/.../scripts/demo/testError.py", line 60, in <module>
response = query_engine.query(
^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/instrumentation/dispatcher.py", line 265, in wrapper
result = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/base/base_query_engine.py", line 52, in query
query_result = self._query(str_or_query_bundle)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/instrumentation/dispatcher.py", line 265, in wrapper
result = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/query_engine/retriever_query_engine.py", line 176, in _query
response = self._response_synthesizer.synthesize(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/instrumentation/dispatcher.py", line 265, in wrapper
result = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/response_synthesizers/base.py", line 241, in synthesize
response_str = self.get_response(
^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/instrumentation/dispatcher.py", line 265, in wrapper
result = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/response_synthesizers/refine.py", line 177, in get_response
response = self._give_response_single(
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/response_synthesizers/refine.py", line 234, in _give_response_single
program(
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/instrumentation/dispatcher.py", line 265, in wrapper
result = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/response_synthesizers/refine.py", line 84, in __call__
answer = self._llm.predict(
^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/instrumentation/dispatcher.py", line 265, in wrapper
result = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/llms/llm.py", line 578, in predict
chat_response = self.chat(messages)
^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/instrumentation/dispatcher.py", line 265, in wrapper
result = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/llms/callbacks.py", line 173, in wrapped_llm_chat
f_return_val = f(_self, messages, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/llms/structured_llm.py", line 109, in chat
output = self.llm.structured_predict(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/instrumentation/dispatcher.py", line 265, in wrapper
result = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/llms/openai/base.py", line 963, in structured_predict
return super().structured_predict(*args, llm_kwargs=llm_kwargs, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/instrumentation/dispatcher.py", line 265, in wrapper
result = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/llms/llm.py", line 363, in structured_predict
result = program(llm_kwargs=llm_kwargs, **prompt_args)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/instrumentation/dispatcher.py", line 265, in wrapper
result = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/program/function_program.py", line 199, in __call__
messages = self._prompt.format_messages(llm=self._llm, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/ubuntu/Documents/.../venv/lib/python3.12/site-packages/llama_index/core/prompts/base.py", line 314, in format_messages
content = content_template.format(**relevant_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
KeyError: '"name"'
Bug Description
I'm trying to get pydantic response with chat engine but it threw errors. i tried different versions of llamaindex but none of them worked.
Version
0.10.36
Steps to Reproduce
!pip install llama-index-vector-stores-pinecone==0.1.7 pinecone-client==3.2.2 llama-index-embeddings-openai !pip install llama-index==0.10.36
from llama_index.llms.openai import OpenAI as llama_index_openai import openai from llama_index.embeddings.openai import OpenAIEmbedding from pinecone import Pinecone from llama_index.vector_stores.pinecone import PineconeVectorStore from llama_index.core import ( VectorStoreIndex ) from typing import List from pydantic.v1 import BaseModel, Field
class Movie(BaseModel): """Object representing a single movie."""
name: str = Field(..., description="Name of the movie.") year: int = Field(..., description="Year of the movie.")
class Movies(BaseModel): """Object representing a list of movies."""
movies: List[Movie] = Field(..., description="List of movies.")
openai.api_key = "XXX" embed_model = OpenAIEmbedding(model="text-embedding-3-small")
llm = llama_index_openai(temperature=0.1, model="gpt-4o") pc = Pinecone(api_key="XXX") pinecone_index = pc.Index("quick_start") vector_store = PineconeVectorStore(pinecone_index=pinecone_index) loaded_index = VectorStoreIndex.from_vector_store(vector_store=vector_store, embed_model=embed_model) sllm = llm.as_structured_llm(Movies) query_engine = loaded_index.as_chat_engine( chat_mode="context", similarity_top_k=5, llm=sllm ) prompt = ''' Please generate related movies to Titanic ''' response = query_engine.chat(prompt)
Relevant Logs/Tracbacks