Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/sdk-smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ jobs:
tests/harness/test_agent_type_refusal.py
tests/harness/test_python_source_codecs.py
tests/harness/test_executor_completed_counts.py
tests/harness/test_deep_agents_reference.py
tests/harness/test_reading_entry.py
tests/harness/test_agent_reading.py
tests/harness/test_scan_walk.py
Expand Down
1 change: 1 addition & 0 deletions examples/harness/deep_agents_support/.python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
23 changes: 23 additions & 0 deletions examples/harness/deep_agents_support/README.md
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.
60 changes: 60 additions & 0 deletions examples/harness/deep_agents_support/agent.py
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(

Copy link
Copy Markdown
Member

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.run per request (langgraph_http_adapter.py:70), and the ChatOpenAI built 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 with run_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.

model=ChatOpenAI(
model=MODEL,
base_url=os.getenv("OPENAI_BASE_URL"),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2. If the job maps OPENAI_API_KEY but not OPENAI_BASE_URL, ChatOpenAI falls back to https://api.openai.com/v1/ and sends the gateway key there as a bearer token on every turn, with model vertex_ai/gemini-3.7-flash. The README documents that fallback. #311 refuses to start in the same case, because CrewAI won't take custom_openai without a URL.

Fix: refuse at import when OPENAI_BASE_URL is unset, with a test.

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."
),
)
6 changes: 6 additions & 0 deletions examples/harness/deep_agents_support/langgraph.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"dependencies": ["."],
"graphs": {
"support": "./agent.py:graph"
}
}
2 changes: 2 additions & 0 deletions examples/harness/deep_agents_support/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
deepagents==0.7.32
langchain-openai==1.7.3
Loading
Loading