mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-19 20:34:45 +08:00
feat: add Claude Code OAuth and Codex CLI as LLM providers (#1166)
* feat: add Claude Code OAuth and Codex CLI providers Port of bytedance/deer-flow#1136 from @solanian's feat/cli-oauth-providers branch.\n\nCarries the feature forward on top of current main without the original CLA-blocked commit metadata, while preserving attribution in the commit message for review. * fix: harden CLI credential loading Align Codex auth loading with the current ~/.codex/auth.json shape, make Docker credential mounts directory-based to avoid broken file binds on hosts without exported credential files, and add focused loader tests. * refactor: tighten codex auth typing Replace the temporary Any return type in CodexChatModel._load_codex_auth with the concrete CodexCliCredential type after the credential loader was stabilized. * fix: load Claude Code OAuth from Keychain Match Claude Code's macOS storage strategy more closely by checking the Keychain-backed credentials store before falling back to ~/.claude/.credentials.json. Keep explicit file overrides and add focused tests for the Keychain path. * fix: require explicit Claude OAuth handoff * style: format thread hooks reasoning request * docs: document CLI-backed auth providers * fix: address provider review feedback * fix: harden provider edge cases * Fix deferred tools, Codex message normalization, and local sandbox paths * chore: narrow PR scope to OAuth providers * chore: remove unrelated frontend changes * chore: reapply OAuth branch frontend scope cleanup * fix: preserve upload guards with reasoning effort wiring --------- Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
This commit is contained in:
@@ -9,6 +9,7 @@ from deerflow.config.app_config import AppConfig
|
||||
from deerflow.config.model_config import ModelConfig
|
||||
from deerflow.config.sandbox_config import SandboxConfig
|
||||
from deerflow.models import factory as factory_module
|
||||
from deerflow.models import openai_codex_provider as codex_provider_module
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
@@ -30,6 +31,7 @@ def _make_model(
|
||||
supports_reasoning_effort: bool = False,
|
||||
when_thinking_enabled: dict | None = None,
|
||||
thinking: dict | None = None,
|
||||
max_tokens: int | None = None,
|
||||
) -> ModelConfig:
|
||||
return ModelConfig(
|
||||
name=name,
|
||||
@@ -37,6 +39,7 @@ def _make_model(
|
||||
description=None,
|
||||
use=use,
|
||||
model=name,
|
||||
max_tokens=max_tokens,
|
||||
supports_thinking=supports_thinking,
|
||||
supports_reasoning_effort=supports_reasoning_effort,
|
||||
when_thinking_enabled=when_thinking_enabled,
|
||||
@@ -500,6 +503,96 @@ def test_openai_compatible_provider_multiple_models(monkeypatch):
|
||||
assert captured.get("model") == "MiniMax-M2.5-highspeed"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Codex provider reasoning_effort mapping
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class FakeCodexChatModel(FakeChatModel):
|
||||
pass
|
||||
|
||||
|
||||
def test_codex_provider_disables_reasoning_when_thinking_disabled(monkeypatch):
|
||||
cfg = _make_app_config(
|
||||
[
|
||||
_make_model(
|
||||
"codex",
|
||||
use="deerflow.models.openai_codex_provider:CodexChatModel",
|
||||
supports_thinking=True,
|
||||
supports_reasoning_effort=True,
|
||||
)
|
||||
]
|
||||
)
|
||||
_patch_factory(monkeypatch, cfg, model_class=FakeCodexChatModel)
|
||||
monkeypatch.setattr(codex_provider_module, "CodexChatModel", FakeCodexChatModel)
|
||||
|
||||
FakeChatModel.captured_kwargs = {}
|
||||
factory_module.create_chat_model(name="codex", thinking_enabled=False)
|
||||
|
||||
assert FakeChatModel.captured_kwargs.get("reasoning_effort") == "none"
|
||||
|
||||
|
||||
def test_codex_provider_preserves_explicit_reasoning_effort(monkeypatch):
|
||||
cfg = _make_app_config(
|
||||
[
|
||||
_make_model(
|
||||
"codex",
|
||||
use="deerflow.models.openai_codex_provider:CodexChatModel",
|
||||
supports_thinking=True,
|
||||
supports_reasoning_effort=True,
|
||||
)
|
||||
]
|
||||
)
|
||||
_patch_factory(monkeypatch, cfg, model_class=FakeCodexChatModel)
|
||||
monkeypatch.setattr(codex_provider_module, "CodexChatModel", FakeCodexChatModel)
|
||||
|
||||
FakeChatModel.captured_kwargs = {}
|
||||
factory_module.create_chat_model(name="codex", thinking_enabled=True, reasoning_effort="high")
|
||||
|
||||
assert FakeChatModel.captured_kwargs.get("reasoning_effort") == "high"
|
||||
|
||||
|
||||
def test_codex_provider_defaults_reasoning_effort_to_medium(monkeypatch):
|
||||
cfg = _make_app_config(
|
||||
[
|
||||
_make_model(
|
||||
"codex",
|
||||
use="deerflow.models.openai_codex_provider:CodexChatModel",
|
||||
supports_thinking=True,
|
||||
supports_reasoning_effort=True,
|
||||
)
|
||||
]
|
||||
)
|
||||
_patch_factory(monkeypatch, cfg, model_class=FakeCodexChatModel)
|
||||
monkeypatch.setattr(codex_provider_module, "CodexChatModel", FakeCodexChatModel)
|
||||
|
||||
FakeChatModel.captured_kwargs = {}
|
||||
factory_module.create_chat_model(name="codex", thinking_enabled=True)
|
||||
|
||||
assert FakeChatModel.captured_kwargs.get("reasoning_effort") == "medium"
|
||||
|
||||
|
||||
def test_codex_provider_strips_unsupported_max_tokens(monkeypatch):
|
||||
cfg = _make_app_config(
|
||||
[
|
||||
_make_model(
|
||||
"codex",
|
||||
use="deerflow.models.openai_codex_provider:CodexChatModel",
|
||||
supports_thinking=True,
|
||||
supports_reasoning_effort=True,
|
||||
max_tokens=4096,
|
||||
)
|
||||
]
|
||||
)
|
||||
_patch_factory(monkeypatch, cfg, model_class=FakeCodexChatModel)
|
||||
monkeypatch.setattr(codex_provider_module, "CodexChatModel", FakeCodexChatModel)
|
||||
|
||||
FakeChatModel.captured_kwargs = {}
|
||||
factory_module.create_chat_model(name="codex", thinking_enabled=True)
|
||||
|
||||
assert "max_tokens" not in FakeChatModel.captured_kwargs
|
||||
|
||||
|
||||
def test_openai_responses_api_settings_are_passed_to_chatopenai(monkeypatch):
|
||||
model = ModelConfig(
|
||||
name="gpt-5-responses",
|
||||
|
||||
Reference in New Issue
Block a user