Repository navigation
test(harness): a Deep Agents reference agent for the hosted path (TH-8810) #306
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
base: feat/TH-8804-source-subfolder
Are you sure you want to change the base?
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 |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| 3.12 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # Deep Agents support agent | ||
|
|
||
| A customer support chat agent built on LangChain's Deep Agents. It has three tools over a | ||
| small in-memory set of orders: `look_up_order`, `start_return` and `return_policy`. | ||
|
|
||
| ## How the kit runs it | ||
|
|
||
| `langgraph.json` names the one graph, `agent.py:graph`, which is built once when the module | ||
| is imported. The kit runs it with its LangGraph adapter: each turn the adapter sends the | ||
| whole conversation as `messages` and replies with the last message's text. Tool calls are | ||
| not returned: the graph runs the tools itself, and the kit's tool tracer records each call | ||
| made to the three tool functions, including the ones made from a worker thread. Deep | ||
| Agents' own built-in tools (files, planning, sub-agents) are not traced. | ||
|
|
||
| ## Environment | ||
|
|
||
| - `OPENAI_API_KEY`: the key for the endpoint (required for a turn; without it the module | ||
| still imports, with a placeholder key, and the turn fails). | ||
| - `OPENAI_BASE_URL`: the endpoint's base URL. Set it to the model gateway for the default model; unset means the OpenAI API, which then needs `OPENAI_MODEL` set to an OpenAI model. | ||
| - `OPENAI_MODEL`: the model name, default `vertex_ai/gemini-3.7-flash` (a gateway name). | ||
|
|
||
| The endpoint must speak chat completions: the model is built with `use_responses_api=False`. | ||
| Install with `pip install -r requirements.txt` on Python 3.12. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| import os | ||
|
|
||
| from deepagents import create_deep_agent | ||
| from langchain_openai import ChatOpenAI | ||
|
|
||
| MODEL = os.getenv("OPENAI_MODEL", "vertex_ai/gemini-3.7-flash") | ||
|
|
||
| _ORDERS = { | ||
| "A1001": {"item": "trail running shoes", "status": "delivered", "return": None}, | ||
| "A1002": {"item": "rain jacket", "status": "shipped", "return": None}, | ||
| "A1003": {"item": "water bottle", "status": "delivered", "return": None}, | ||
| } | ||
|
|
||
|
|
||
| def look_up_order(order_id: str) -> str: | ||
| """Look up an order by its id and report its item, status and any return.""" | ||
| order = _ORDERS.get(order_id) | ||
| if order is None: | ||
| return f"No order {order_id} exists." | ||
| returned = order["return"] or "none" | ||
| return f"Order {order_id}: {order['item']}, status {order['status']}, return: {returned}." | ||
|
|
||
|
|
||
| def start_return(order_id: str, reason: str) -> str: | ||
| """Start a return for a delivered order, giving the customer's reason.""" | ||
| order = _ORDERS.get(order_id) | ||
| if order is None: | ||
| return f"No order {order_id} exists." | ||
| if order["status"] != "delivered": | ||
| return f"Order {order_id} is {order['status']} and cannot be returned yet." | ||
| if order["return"] is not None: | ||
| return f"Order {order_id} already has a return started." | ||
| order["return"] = reason | ||
| return f"Return started for order {order_id}: {reason}." | ||
|
|
||
|
|
||
| def return_policy() -> str: | ||
| """State the store's return policy.""" | ||
| return "Delivered items can be returned within 30 days for a full refund." | ||
|
|
||
|
|
||
| # Built once at import, which is how a graph named in langgraph.json is loaded. The client | ||
| # is built eagerly and refuses a missing key, so a placeholder keeps the import alive and | ||
| # the turn is what fails without a real key. `use_responses_api=False` because ChatOpenAI | ||
| # switches to the Responses API by itself for some models and arguments, and the gateway | ||
| # serves that only for OpenAI-format routes. | ||
| graph = create_deep_agent( | ||
| model=ChatOpenAI( | ||
| model=MODEL, | ||
| base_url=os.getenv("OPENAI_BASE_URL"), | ||
|
Member
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. P2. If the job maps Fix: refuse at import when |
||
| api_key=os.getenv("OPENAI_API_KEY") or "unset", | ||
| use_responses_api=False, | ||
| ), | ||
| tools=[look_up_order, start_return, return_policy], | ||
| system_prompt=( | ||
| "You are a customer support agent for an online store. Use the order tools to " | ||
| "look up orders and start returns, and never guess an order's status. Answer " | ||
| "briefly." | ||
| ), | ||
| ) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| { | ||
| "dependencies": ["."], | ||
| "graphs": { | ||
| "support": "./agent.py:graph" | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| deepagents==0.7.32 | ||
| langchain-openai==1.7.3 |
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.
P1 (cause is in the adapter, not this diff). The second turn of every conversation fails. The kit's LangGraph adapter calls
asyncio.runper request (langgraph_http_adapter.py:70), and theChatOpenAIbuilt here at import keeps its pooled connection on the first turn's loop, which is closed by turn 2.Failure: turn 1 returns 200, turn 2 returns 500
{"error": "RuntimeError: Event loop is closed"}. Seen 3 of 3 times with deepagents 0.7.32 and langchain-openai 1.7.3 through the kit adapter against a local stand-in gateway. The description says this may cost a 500 and isn't proven offline; it is proven, and it happens every time.Fix: give the adapter one long-lived loop (a background thread on
run_forever, requests sent withrun_coroutine_threadsafe). With that, both turns return 200 and both tool calls are traced. Or build the model per call as #301, #304 and #311 do. Either way, correct the known-limit text.