mirror of
https://github.com/lfnovo/open-notebook.git
synced 2026-04-28 11:30:00 +00:00
Some LLM providers (notably Gemini, DeepSeek via OpenAI-compatible
proxies) return ai_message.content as a list of content parts:
[{'type': 'text', 'text': '...', 'extras': {...}}]
The current code uses str() on non-string content, which produces the
Python repr of the entire list — not valid JSON. This breaks
PydanticOutputParser.parse() with OutputParserException.
This adds extract_text_content() to properly unwrap text from both
string and structured content formats, applied in ask.py, chat.py,
and prompt.py.
Fixes #329
45 lines
1.4 KiB
Python
45 lines
1.4 KiB
Python
from typing import Any, Optional
|
|
|
|
from ai_prompter import Prompter
|
|
from langchain_core.messages import HumanMessage, SystemMessage
|
|
from langchain_core.runnables import RunnableConfig
|
|
from langgraph.graph import END, START, StateGraph
|
|
from typing_extensions import TypedDict
|
|
|
|
from open_notebook.ai.provision import provision_langchain_model
|
|
from open_notebook.utils.text_utils import clean_thinking_content, extract_text_content
|
|
|
|
|
|
class PatternChainState(TypedDict):
|
|
prompt: str
|
|
parser: Optional[Any]
|
|
input_text: str
|
|
output: str
|
|
|
|
|
|
async def call_model(state: dict, config: RunnableConfig) -> dict:
|
|
content = state["input_text"]
|
|
system_prompt = Prompter(
|
|
template_text=state["prompt"], parser=state.get("parser")
|
|
).render(data=state)
|
|
payload = [SystemMessage(content=system_prompt)] + [HumanMessage(content=content)]
|
|
chain = await provision_langchain_model(
|
|
str(payload),
|
|
config.get("configurable", {}).get("model_id"),
|
|
"transformation",
|
|
max_tokens=5000,
|
|
)
|
|
|
|
response = await chain.ainvoke(payload)
|
|
|
|
# Clean thinking tags from response (handles extended thinking models)
|
|
output = clean_thinking_content(extract_text_content(response.content))
|
|
return {"output": output}
|
|
|
|
|
|
agent_state = StateGraph(PatternChainState)
|
|
agent_state.add_node("agent", call_model) # type: ignore[type-var]
|
|
agent_state.add_edge(START, "agent")
|
|
agent_state.add_edge("agent", END)
|
|
|
|
graph = agent_state.compile()
|