-
Notifications
You must be signed in to change notification settings - Fork 0
Try to get web search working #4
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -2,10 +2,9 @@ | |||||
| from django.db import models | ||||||
| import uuid | ||||||
| from langchain_aws import ChatBedrock | ||||||
| from langchain_core.messages import HumanMessage, SystemMessage, AIMessage | ||||||
| from langchain_core.messages import HumanMessage, SystemMessage, AIMessage, ToolMessage | ||||||
| from langchain_core.tools import tool | ||||||
| from langchain_core.callbacks.base import BaseCallbackHandler | ||||||
| from langchain.agents import create_agent | ||||||
| from tavily import TavilyClient | ||||||
| import logging | ||||||
| import base64 | ||||||
|
|
@@ -116,47 +115,100 @@ def web_search(query: str) -> str: | |||||
| return f"Error during search: {str(e)}" | ||||||
|
|
||||||
|
|
||||||
| # Create chat model | ||||||
| # Create chat model with tool binding | ||||||
| chat_model = ChatBedrock(model_id=self.ai.model_id) | ||||||
| tools = [web_search] | ||||||
|
|
||||||
| # Create modern agent with tool calling support | ||||||
| # This is the recommended approach per LangChain docs | ||||||
| agent = create_agent( | ||||||
| model=chat_model, | ||||||
| tools=tools, | ||||||
| system_prompt=self.get_system_message(), | ||||||
| debug=settings.DEBUG | ||||||
| ) | ||||||
| # Bind tools to the model for proper tool calling | ||||||
| model_with_tools = chat_model.bind_tools(tools) | ||||||
|
Comment on lines
+118
to
+123
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't bypass Line 119 creates a new 🤖 Prompt for AI Agents |
||||||
|
|
||||||
| # Extract text input from message_list for agent | ||||||
| agent_input = self._extract_agent_input(message_list) | ||||||
|
|
||||||
| logger.info(f"Invoking agent with input: {agent_input[:100]}...") | ||||||
| logger.info("🤖 AGENT_INVOKE_START: web_search tool available") | ||||||
|
|
||||||
| # Invoke agent - the CompiledStateGraph handles tool loop internally | ||||||
| response = agent.invoke({"messages": [HumanMessage(content=agent_input)]}) | ||||||
| # Build and run the agent loop manually | ||||||
| messages = [SystemMessage(content=self.get_system_message()), HumanMessage(content=agent_input)] | ||||||
|
|
||||||
|
Comment on lines
125
to
+133
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Preserve prior turns when web search is enabled. Lines 125-132 throw away the history that 🧩 Minimal fix- # Build and run the agent loop manually
- messages = [SystemMessage(content=self.get_system_message()), HumanMessage(content=agent_input)]
+ # Build and run the agent loop manually with the existing chat history
+ messages = list(message_list)🤖 Prompt for AI Agents |
||||||
| # Agentagent loop - keep invoking until no more tool calls | ||||||
|
||||||
| # Agentagent loop - keep invoking until no more tool calls | |
| # Agent loop - keep invoking until no more tool calls |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accumulate token usage for every model invoke.
model_with_tools.invoke(messages) can run multiple times here, but usage_metadata is taken only from the last AIMessage. Tool-enabled chats will under-report tokens whenever a tool is used, which skews any quota/billing logic that reads input_tokens / output_tokens.
📊 Suggested fix
response_text = ""
usage_metadata = {"input_tokens": 0, "output_tokens": 0}
@@
# Call the model
response = model_with_tools.invoke(messages)
+ if getattr(response, "usage_metadata", None):
+ usage_metadata["input_tokens"] += response.usage_metadata.get("input_tokens", 0)
+ usage_metadata["output_tokens"] += response.usage_metadata.get("output_tokens", 0)
messages.append(response)
@@
- # Extract token usage
- if hasattr(msg, 'usage_metadata') and msg.usage_metadata:
- usage_metadata = msg.usage_metadata
-
logger.info(f"🤖 FINAL_RESPONSE: {len(response_text)} chars")
breakAlso applies to: 176-197
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@back/bots/models/chat.py` around lines 143 - 144, The code calls
model_with_tools.invoke(messages) which may append multiple AIMessage responses
but only captures usage_metadata from the last AIMessage, causing under-reported
token usage; update the handling after each invoke to accumulate token counts
from every AIMessage's usage_metadata (e.g., sum input_tokens/output_tokens from
each AIMessage in messages or from the returned response objects) after each
call to model_with_tools.invoke and before appending additional messages so that
total input/output/token usage reflects all tool-enabled invocations; apply the
same accumulation fix where model_with_tools.invoke is used (including the block
around lines 176-197) and ensure you update whatever counters or usage fields
(input_tokens, output_tokens, total_tokens, usage_metadata) used by
billing/quota logic.
Copilot
AI
Apr 4, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The manual tool loop stops after max_iterations, but if the model is still returning tool calls at that point there may be no final AIMessage without tool_calls. In that case response_text falls back to the last AIMessage which is likely a tool-call message (often empty), resulting in a blank or incorrect assistant reply. Handle the max-iteration exit explicitly (e.g., raise/log an error and return a safe message, or force one final model call after the last ToolMessage) so callers always get a real final response.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle loop exhaustion explicitly.
If the model is still returning tool_calls on the fifth pass, the loop exits without a terminal assistant answer. The fallback at Lines 202-211 then persists the last AIMessage, which in that case is usually an empty/partial tool-call stub, and back/bots/views/get_chat_response.py:82-83 returns that straight to the client.
🛑 Safer fallback
max_iterations = 5
iteration = 0
+ exhausted = True
while iteration < max_iterations:
iteration += 1
logger.info(f"🤖 AGENT_LOOP_ITERATION: {iteration}")
@@
# Check if model wants to call a tool
if not response.tool_calls:
+ exhausted = False
logger.info(f"🤖 AGENT_LOOP_COMPLETE: no more tool calls after {iteration} iterations")
break
@@
- if not response_text:
+ if exhausted:
+ logger.error("🤖 AGENT_LOOP_ABORTED: max iterations reached without a final response")
+ response_text = "I'm sorry, I couldn't complete the web search. Please try again."
+ elif not response_text:
# Fallback: get the last message
for msg in reversed(messages):
if isinstance(msg, AIMessage):Also applies to: 202-211
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@back/bots/models/chat.py` around lines 135 - 149, The loop can exit on
max_iterations while the last response still contains a tool_call stub, so after
the while using max_iterations/iteration and the last response from
model_with_tools.invoke, detect if iteration >= max_iterations and
response.tool_calls is truthy; in that case produce and append a proper terminal
assistant message (e.g., an AIMessage summarizing that the agent could not
finish the task or asking to try again) or re-invoke model_with_tools.invoke
with instructions to return a final assistant answer (no tool_calls) before
persisting — update the code paths around the loop and the fallback persistence
(the messages list, response variable, and the code that saves the last
AIMessage) to ensure a non-tool_call terminal assistant message is saved and
returned.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
create_agentis removed in this PR, but the existing unit testback/bots/tests/test_chat.pypatchesbots.models.chat.create_agent. With this change, that test will start failing (patch target no longer exists). Update the tests to patch/mock the newbind_tools(...).invoke(...)flow instead.