Repository navigation
test(harness): a Deep Agents reference agent for the hosted path (TH-8810) - #306
khushalsonawat wants to merge 2 commits into
Conversation
…8810) A small support agent on LangChain Deep Agents 0.7.32, the same desk as the other T43 references, written the way LangGraph apps ship: the graph built once at import and named in langgraph.json. Its model is ChatOpenAI pointed at the model gateway, held on chat completions (use_responses_api=False, since ChatOpenAI can switch to the Responses API by itself), with a placeholder key so a missing key fails a turn rather than the import. Its three tools are plain functions. Offline tests pin what the hosted path does with it, with stand-ins for the libraries: it is detected as deepagents; the build runs it through the kit's LangGraph adapter with the tool tracer bound to its three tools; the real tracer bootstrap records each tool as langchain-core calls it (a worker thread for plain tools); and the model wiring. Signed-off-by: khushalsonawat <khushalsonawat@gmail.com>
Signed-off-by: khushalsonawat <khushalsonawat@gmail.com>
| # 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( |
There was a problem hiding this comment.
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.
| graph = create_deep_agent( | ||
| model=ChatOpenAI( | ||
| model=MODEL, | ||
| base_url=os.getenv("OPENAI_BASE_URL"), |
There was a problem hiding this comment.
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.
| assert agent.MODEL == DEFAULT_MODEL | ||
| # Built at import, the way a graph named in langgraph.json is loaded: reaching the | ||
| # graph again builds nothing. | ||
| assert agent.graph is agent.graph |
There was a problem hiding this comment.
P3. agent.graph is agent.graph is always true. The next line (len(calls) == 1) already pins "built once", so this one can go.
| } | ||
|
|
||
|
|
||
| def _copy_example(destination: Path) -> Path: |
There was a problem hiding this comment.
P3. Same copied setup as the other reference-agent PRs (_copy_example, the stand-in writer, the fresh-import fixture, the detection tests, the build test skeleton, the tracer driver, the pin tests). See the shared-helper comment on #301: this file would import from that one module instead.
Summary
TH-8810 is part of T43: qualify the hosted harness across frameworks. This is Deep Agents' offline half, on the same desk as #301 (OpenAI Agents SDK), #304 (Pydantic AI) and #305 (Google ADK). A local end-to-end run with a real reading and a real model is summarised under Verification.
examples/harness/deep_agents_support/is written the way LangGraph apps ship:create_deep_agent(...)builds the graph once at import, andlanggraph.jsonnames it. The kit already runs that natively through its LangGraph adapter, which gives the graph the whole conversation each turn and returns the last message's text.ChatOpenAIpointed at the model gateway (OPENAI_BASE_URL,OPENAI_API_KEY,vertex_ai/gemini-3.7-flash).use_responses_api=False, becauseChatOpenAIcan switch to the Responses API by itself, which the gateway serves only for OpenAI-format routes.ChatOpenAIbuilds its client eagerly and would otherwise fail the import.tests/harness/test_deep_agents_reference.py, with stand-ins for the libraries:deepagentswith no blocker, and adeepagents.pyshadow flips it.No kit code changes.
Verification
ruff checkon the new filestest_bundle_author_v2.py. 321 passed.uv run pytest -q: not run, because no shared runtime is touched.sdk-smokeruns a fixed list of test files, which didn't include this one. It now does (tests/harness/test_deep_agents_reference.py).clean-installfailed once ontests/harness/test_contract_tools_subfolder.py::test_deep_flow_nesting_does_not_hold_up_the_save[docker-compose.yml], a timing test in a file this PR doesn't touch (1.47 s against 1 s): https://github.com/future-agi/agent-learning-kit/actions/runs/43166681740. The push run of the same commit is green (https://github.com/future-agi/agent-learning-kit/actions/runs/43166676440); the failed job was re-run. The flaky test is in the older-findings register and with Maple.ChatOpenAImade keyword-only like the real class, and a no-blockers assert) had a fix-only check: CLEAR, nothing above Low, and its plant (a positionalChatOpenAI(MODEL)) was caught. The audit fixes (this CI-list line, and this description) had a fix-only check as well: CLEAR, nothing above Low.langgraph dev, which the agent doesn't install, so the start failed before any model call. An earlier local run through the kit's own LangGraph adapter (this folder'slanggraph.json) recorded the tool calls.base_urlor the key placeholder dropped;langchain-openaidropped;deepagents.pyshadow;ChatOpenAI(MODEL), which the real class rejects.Known limits
deepagentsorlangchain-openai.sdk-smoke.ymlinstalls only.[livekit], and the kit'slangchainextra has neither. So these tests run against stand-ins only; the pinned sources were read in review.langgraph devfor this agent, which isn't installed, instead of the kit'slanggraph.jsonroute. Outside this PR; a register row.contentonly when it's a string.Checklist
agent_learning.*,agent-learn, or@future-agi/agent-learning-kit