Add zai-sdk dependency and update model references to glm-4.5-air

This commit is contained in:
Xin Wang
2026-03-31 15:00:11 +08:00
parent 2219d82eb6
commit 65516c65a3
7 changed files with 36 additions and 27 deletions

View File

@@ -21,7 +21,7 @@ def main() -> None:
)
parser.add_argument(
"--reasoning-model",
default="GLM-4.7",
default="glm-4.5-air",
help="Model for the final answer",
)
args = parser.parse_args()

View File

@@ -18,6 +18,7 @@ dependencies = [
"langgraph-api",
"fastapi",
"tavily-python",
"zai-sdk",
]

View File

@@ -9,21 +9,21 @@ class Configuration(BaseModel):
"""The configuration for the agent."""
query_generator_model: str = Field(
default="GLM-4.7",
default="glm-4.5-air",
metadata={
"description": "The name of the language model to use for the agent's query generation."
},
)
reflection_model: str = Field(
default="GLM-4.7",
default="glm-4.5-air",
metadata={
"description": "The name of the language model to use for the agent's reflection."
},
)
answer_model: str = Field(
default="GLM-4.7",
default="glm-4.5-air",
metadata={
"description": "The name of the language model to use for the agent's answer."
},

View File

@@ -11,6 +11,7 @@ from langgraph.graph import START, END
from langchain_core.runnables import RunnableConfig
from langchain_openai import ChatOpenAI
from tavily import TavilyClient
from zai import ZhipuAiClient
from agent.state import (
OverallState,
@@ -40,6 +41,7 @@ if os.getenv("TAVILY_API_KEY") is None:
raise ValueError("TAVILY_API_KEY is not set")
tavily_client = TavilyClient(api_key=os.getenv("TAVILY_API_KEY"))
zhipu_client = ZhipuAiClient(api_key=os.getenv("OPENAI_API_KEY"))
SchemaT = TypeVar("SchemaT", bound=BaseModel)
@@ -73,13 +75,17 @@ def _extract_json_payload(content: str) -> str:
def _invoke_json_model(
llm: ChatOpenAI,
model: str,
prompt: str,
schema: type[SchemaT],
) -> SchemaT:
"""Invoke the model for JSON text and validate it with Pydantic."""
result = llm.invoke(prompt)
content = result.content if isinstance(result.content, str) else str(result.content)
"""Invoke the model via zai-sdk JSON mode and validate with Pydantic."""
response = zhipu_client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"},
)
content = response.choices[0].message.content
json_payload = _extract_json_payload(content)
try:
@@ -93,22 +99,25 @@ def _invoke_json_model(
def _supports_native_structured_output(model: str) -> bool:
"""Return whether the selected model should use LangChain structured output."""
normalized = model.strip().lower()
return normalized.startswith("glm-4.7")
return not normalized.startswith("glm")
def _invoke_structured_model(
llm: ChatOpenAI,
model: str,
prompt: str,
schema: type[SchemaT],
) -> SchemaT:
"""Use native structured output when supported, otherwise fall back to JSON parsing."""
"""Use direct provider JSON mode for GLM, otherwise try native structured output."""
if not _supports_native_structured_output(model):
return _invoke_json_model(model, prompt, schema)
if _supports_native_structured_output(model):
try:
llm = create_llm(model, temperature=1.0)
return llm.with_structured_output(schema).invoke(prompt)
except ValueError:
return _invoke_json_model(llm, prompt, schema)
return _invoke_json_model(llm, prompt, schema)
return _invoke_json_model(model, prompt, schema)
return _invoke_json_model(model, prompt, schema)
# Nodes
@@ -130,8 +139,6 @@ def generate_query(state: OverallState, config: RunnableConfig) -> QueryGenerati
if state.get("initial_search_query_count") is None:
state["initial_search_query_count"] = configurable.number_of_initial_queries
llm = create_llm(configurable.query_generator_model, temperature=1.0)
current_date = get_current_date()
formatted_prompt = query_writer_instructions.format(
current_date=current_date,
@@ -139,7 +146,6 @@ def generate_query(state: OverallState, config: RunnableConfig) -> QueryGenerati
number_queries=state["initial_search_query_count"],
)
result = _invoke_structured_model(
llm,
configurable.query_generator_model,
formatted_prompt,
SearchQueryList,
@@ -216,9 +222,7 @@ def reflection(state: OverallState, config: RunnableConfig) -> ReflectionState:
research_topic=get_research_topic(state["messages"]),
summaries="\n\n---\n\n".join(state["web_research_result"]),
)
llm = create_llm(reasoning_model, temperature=1.0)
result = _invoke_structured_model(
llm,
reasoning_model,
formatted_prompt,
Reflection,

View File

@@ -25,12 +25,10 @@ Format:
Example:
Topic: What revenue grew more last year apple stock or the number of people buying an iphone
```json
{{
"rationale": "To answer this comparative growth question accurately, we need specific data points on Apple's stock performance and iPhone sales metrics. These queries target the precise financial information needed: company revenue trends, product-specific unit sales figures, and stock price movement over the same fiscal period for direct comparison.",
"query": ["Apple total revenue growth fiscal year 2024", "iPhone unit sales growth fiscal year 2024", "Apple stock price growth fiscal year 2024"]
}}
```
Context: {research_topic}"""
@@ -67,13 +65,11 @@ Output Format:
- "follow_up_queries": Write a specific question to address this gap
Example:
```json
{{
"is_sufficient": true, // or false
"knowledge_gap": "The summary lacks information about performance metrics and benchmarks", // "" if is_sufficient is true
"follow_up_queries": ["What are typical performance benchmarks and metrics used to evaluate [specific technology]?"] // [] if is_sufficient is true
"is_sufficient": true,
"knowledge_gap": "",
"follow_up_queries": []
}}
```
Reflect carefully on the Summaries to identify knowledge gaps and produce a follow-up query. Then, produce your output following this JSON format:

View File

@@ -26,7 +26,7 @@ export const InputForm: React.FC<InputFormProps> = ({
}) => {
const [internalInputValue, setInternalInputValue] = useState("");
const [effort, setEffort] = useState("medium");
const [model, setModel] = useState("GLM-4.7");
const [model, setModel] = useState("glm-4.5-air");
const handleInternalSubmit = (e?: React.FormEvent) => {
if (e) e.preventDefault();
@@ -135,6 +135,14 @@ export const InputForm: React.FC<InputFormProps> = ({
<SelectValue placeholder="Model" />
</SelectTrigger>
<SelectContent className="bg-neutral-700 border-neutral-600 text-neutral-300 cursor-pointer">
<SelectItem
value="glm-4.5-air"
className="hover:bg-neutral-600 focus:bg-neutral-600 cursor-pointer"
>
<div className="flex items-center">
<Zap className="h-4 w-4 mr-2 text-emerald-400" /> glm-4.5-air
</div>
</SelectItem>
<SelectItem
value="GLM-4.7"
className="hover:bg-neutral-600 focus:bg-neutral-600 cursor-pointer"

View File

@@ -33,7 +33,7 @@ export const WelcomeScreen: React.FC<WelcomeScreenProps> = ({
/>
</div>
<p className="text-xs text-neutral-500">
Powered by GLM-4.7, Tavily, and LangChain LangGraph.
Powered by glm-4.5-air, GLM-4.7, Tavily, and LangChain LangGraph.
</p>
</div>
);