feat(langchain): add support for reasoning content with new reasoning…#107
Conversation
… providers and integrate ChatDeepSeek model Change-Id: I60b0bdc5837f88c554957d322273e8dbd63dc677 Signed-off-by: 久氢 <mapenghui.mph@alibaba-inc.com>
OhYee
left a comment
There was a problem hiding this comment.
Code Review — feat(langchain): reasoning content support via ChatDeepSeek
变更概述:根据 provider 类型将模型路由到 ChatDeepSeek(支持 reasoning_content)或 ChatOpenAI。改动简洁,拆分方法清晰。以下是需要关注的点:
🔴 H1: "custom" 不应默认归入 _REASONING_PROVIDERS
_REASONING_PROVIDERS 包含 "custom",意味着所有自定义 provider 都会走 ChatDeepSeek 路径。但很多自定义 provider 是 OpenAI 兼容的,不支持 reasoning_content。这会导致用户使用自定义 OpenAI 兼容 provider 时被错误路由到 ChatDeepSeek,可能出现不兼容问题。
建议:移除 "custom",或改为检查模型是否真正支持 reasoning_content(如通过 model info 的某个字段判断)。
🟡 M1: _create_reasoning_model 使用 api_base 而非 base_url
ChatOpenAI 使用 base_url 参数,但 ChatDeepSeek 使用 api_base。请确认 ChatDeepSeek 确实接受 api_base——如果它也是 base_url,则自定义端点配置会静默失效。
🟡 M2: _create_reasoning_model 缺少 name=info.model
_create_openai_model 传了 name=info.model,但 _create_reasoning_model 没有,可能影响日志/trace 中的模型标识。
🟡 M3: 无测试覆盖
新增的 provider 路由逻辑和 ChatDeepSeek 集成没有对应的单测。建议至少测试路由分支和边界情况(unknown provider、None provider)。
建议修复 H1 后再合入。
There was a problem hiding this comment.
Pull request overview
Adds a routing branch in the LangChain model adapter so that "reasoning" providers are wrapped with langchain_deepseek.ChatDeepSeek (to surface reasoning_content), while everything else continues to be wrapped with langchain_openai.ChatOpenAI. The langchain-deepseek package is added to the langchain optional extra.
Changes:
- Introduce
_REASONING_PROVIDERSset and provider-based branching inLangChainModelAdapter.wrap_model(). - Add
_create_reasoning_model()(usingChatDeepSeek) and split out_create_openai_model(). - Declare
langchain-deepseek>=1.0.1in thelangchainextra inpyproject.toml.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
| agentrun/integration/langchain/model_adapter.py | Routes reasoning-capable providers to ChatDeepSeek, keeps others on ChatOpenAI. |
| pyproject.toml | Adds langchain-deepseek dependency to the langchain extra. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # 支持 reasoning_content 的供应商列表 | ||
| _REASONING_PROVIDERS = frozenset({ | ||
| "tongyi", | ||
| "custom", | ||
| "deepseek", | ||
| "zhipuai", | ||
| "moonshot", | ||
| "minimax", |
| provider = (info.provider or "").lower() | ||
|
|
||
| if provider in _REASONING_PROVIDERS: | ||
| return self._create_reasoning_model(info) | ||
| return self._create_openai_model(info) |
| return ChatDeepSeek( | ||
| model=info.model, | ||
| api_key=info.api_key, | ||
| api_base=info.base_url, | ||
| default_headers=info.headers, | ||
| stream_usage=True, | ||
| streaming=True, | ||
| ) |
| "langchain-openai>=1.0.0; python_version >= '3.10'", | ||
| "langchain-deepseek>=1.0.1; python_version >= '3.10'", | ||
| ] | ||
|
|
| def _create_reasoning_model(self, info: Any) -> Any: | ||
| """创建支持 reasoning_content 的模型(使用 ChatDeepSeek)""" | ||
| from langchain_deepseek import ChatDeepSeek | ||
|
|
||
| return ChatDeepSeek( | ||
| model=info.model, | ||
| api_key=info.api_key, | ||
| api_base=info.base_url, | ||
| default_headers=info.headers, | ||
| stream_usage=True, | ||
| streaming=True, | ||
| ) |
Constraint: Reasoning exposure must follow MODEL_PARAMETER_RULES.thinking from the agent environment. Rejected: Querying model metadata per invoke | The requirement pins behavior to agent env and would widen runtime behavior. Confidence: high Scope-risk: moderate Directive: Keep protocol gating centralized so future protocol handlers do not leak reasoning when thinking is disabled. Tested: uv run --extra server pytest tests/unittests/server -q; uv run --extra server ruff check agentrun/utils/reasoning.py agentrun/server/model.py agentrun/server/invoker.py agentrun/server/openai_protocol.py agentrun/server/agui_protocol.py scripts/smoke_reasoning_protocol.py tests/unittests/server/test_reasoning.py tests/unittests/server/test_openai_protocol.py tests/unittests/server/test_agui_protocol.py; real-model smoke with thinking=true and thinking=false for OpenAI and AG-UI Change-Id: I3b1ae025db4c0d26631cf4b4bb8e322dec77ae18 Not-tested: Remote CI and hosted preprod endpoint Signed-off-by: congxiao.wxx <congxiao.wxx@alibaba-inc.com>
Preserve the existing support_reasoning_content branch work while adding the protocol-level reasoning_content gate and verification surface.
Constraint: Keep PR 107 branch history intact and avoid force-pushing over existing work.
Rejected: Replace support_reasoning_content with codex/82508048-reasoning-content | would discard the existing LangChain provider change.
Confidence: high
Scope-risk: moderate
Directive: Continue to gate reasoning exposure from MODEL_PARAMETER_RULES.thinking, not model metadata.
Tested: uv run --extra server ruff check agentrun/utils/reasoning.py agentrun/server/model.py agentrun/server/invoker.py agentrun/server/openai_protocol.py agentrun/server/agui_protocol.py agentrun/integration/langchain/model_adapter.py scripts/smoke_reasoning_protocol.py tests/unittests/server/test_reasoning.py tests/unittests/server/test_openai_protocol.py tests/unittests/server/test_agui_protocol.py
Tested: uv run --extra server pytest tests/unittests/server -q
Tested: uv run --extra server --extra langchain pytest tests/unittests/integration/langchain tests/unittests/conversation_service/test_langchain_adapter.py -q
Tested: MODEL_PARAMETER_RULES='{"thinking": true}' AGENTRUN_SMOKE_INSECURE_SSL=true uv run --extra server python scripts/smoke_reasoning_protocol.py --env-file /Users/congxiao/workspace/agent-quickstart-langchain/.env --model qwen3-235b-a22b-thinking-2507 --protocol both --response-mode stream --expect-reasoning --expect-content
Tested: MODEL_PARAMETER_RULES='{"thinking": false}' AGENTRUN_SMOKE_INSECURE_SSL=true uv run --extra server python scripts/smoke_reasoning_protocol.py --env-file /Users/congxiao/workspace/agent-quickstart-langchain/.env --model qwen3-235b-a22b-thinking-2507 --protocol both --response-mode stream --expect-no-reasoning --expect-content
Signed-off-by: congxiao.wxx <congxiao.wxx@alibaba-inc.com>
Change-Id: I91a2e9f01eae198c297daf1e8d1819f4f8cf53d6
Review feedback showed that provider-based routing sent custom and other OpenAI-compatible providers through the DeepSeek LangChain integration. Restrict the DeepSeek-specific wrapper to the DeepSeek provider, preserve provider metadata from ModelService, and cover the routing boundaries in LangChain tests. Constraint: ChatDeepSeek is provider-specific while ChatOpenAI remains the generic OpenAI-compatible path. Rejected: Routing tongyi, zhipuai, moonshot, minimax, or custom through ChatDeepSeek | those providers are not guaranteed to follow DeepSeek integration semantics. Confidence: high Scope-risk: moderate Directive: Add provider-specific LangChain wrappers only when the provider integration is explicitly supported and tested. Tested: uv run --python 3.10 --all-extras pytest tests/unittests/integration/test_langchain.py tests/unittests/server/test_reasoning.py tests/unittests/server/test_openai_protocol.py::TestOpenAIReasoningContent tests/unittests/server/test_agui_protocol.py::TestAGUIReasoningContent Tested: uv run --python 3.10 --all-extras make mypy-check Tested: uv run --python 3.10 --all-extras make coverage Signed-off-by: congxiao.wxx <congxiao.wxx@alibaba-inc.com> Change-Id: I29d5dc26571cad77280860b8bb854bc1ad994f24
Add deterministic protocol-level tests for MODEL_PARAMETER_RULES.thinking so reasoning_content exposure is guarded in OpenAI and AG-UI outputs.
Constraint: Keep changes limited to test coverage and current e2e expectations.
Rejected: Rely only on the smoke script | it does not make the regression part of the test suite.
Confidence: high
Scope-risk: narrow
Directive: Keep reasoning_content coverage in both unit and e2e layers when changing protocol output.
Tested: uv run --extra server ruff check tests/unittests/server/test_reasoning.py tests/unittests/server/test_openai_protocol.py tests/unittests/server/test_agui_protocol.py tests/e2e/test_reasoning_protocol.py tests/e2e/integration/langchain/test_agent_invoke_methods.py
Tested: uv run --extra server pytest tests/unittests/server -q
Tested: uv run --extra server --extra langchain pytest tests/e2e/test_reasoning_protocol.py tests/e2e/integration/langchain/test_agent_invoke_methods.py -q
Tested: uv run --extra server --extra langchain pytest tests/unittests/integration/langchain tests/unittests/conversation_service/test_langchain_adapter.py -q
Tested: MODEL_PARAMETER_RULES='{"thinking": true}' AGENTRUN_SMOKE_INSECURE_SSL=true uv run --extra server python scripts/smoke_reasoning_protocol.py --env-file /Users/congxiao/workspace/agent-quickstart-langchain/.env --model qwen3-235b-a22b-thinking-2507 --protocol both --response-mode stream --expect-reasoning --expect-content
Tested: MODEL_PARAMETER_RULES='{"thinking": false}' AGENTRUN_SMOKE_INSECURE_SSL=true uv run --extra server python scripts/smoke_reasoning_protocol.py --env-file /Users/congxiao/workspace/agent-quickstart-langchain/.env --model qwen3-235b-a22b-thinking-2507 --protocol both --response-mode stream --expect-no-reasoning --expect-content
Signed-off-by: congxiao.wxx <congxiao.wxx@alibaba-inc.com>
Change-Id: Id273ba4afeb70c63aedcdba01bc31d46f2db3bd1
Constraint: GitHub e2e ignores tests/e2e/integration and filters node ids containing invoke. Rejected: Use real ModelService, sandbox, or MemoryCollection resources | CI should verify SDK wiring without external service flakiness. Confidence: high Scope-risk: narrow Directive: Keep this demo case in a GitHub e2e-collected path and avoid excluded node-id terms. Tested: uv run --python 3.10 --all-extras pytest tests/unittests/integration/test_langchain_convert.py::TestConvertAstreamEventsFormat::test_on_chain_stream_model_command_update_text tests/e2e/test_langchain_server_demo.py -v --tb=short Tested: uv run --python 3.10 --all-extras pytest tests/e2e/ --collect-only -q --ignore=tests/e2e/integration --ignore=tests/e2e/test_agent_ruintime.py --ignore=tests/e2e/test_workspace_id.py --ignore=tests/e2e/test_sandbox_browser.py --ignore=tests/e2e/test_sandbox_code_interpreter.py -k 'not (invoke or with_credential or model_proxy or process_get or delete_sandbox or delete_nonexistent_sandbox or connect_nonexistent or connect_sandbox_async or sandbox_lifecycle or connect_with_wrong_template or template_validation_code_interpreter_network or playwright or browser_recordings or aio_combined_workflow or browser_code_file_integration or aio_lifecycle)' Tested: uv run --python 3.10 --all-extras make mypy-check Tested: uv run --python 3.10 --all-extras make coverage Change-Id: I1769d00572e34d1516fca6e3b4add452122d3c83 Not-tested: Real cloud sandbox and MemoryCollection resources are mocked in the e2e case. Signed-off-by: congxiao.wxx <congxiao.wxx@alibaba-inc.com>
… providers and integrate ChatDeepSeek model
Change-Id: I60b0bdc5837f88c554957d322273e8dbd63dc677
Fix bugs
Bug detail
Pull request tasks
Update docs
Reason for update
Pull request tasks
Add contributor
Contributed content
Content detail
Others
Reason for update