Skip to content
Merged
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
28 changes: 26 additions & 2 deletions client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,10 @@ export OPENROUTER_API_KEY=sk-or-...

### `llm` 字段说明

- `adapter`:模型适配器,当前支持 `openai``openrouter`。不写时默认是 `openai`。
- `adapter`:模型适配器,当前支持 `openai``openrouter`、`anthropic`。不写时默认是 `openai`。
- `model`:模型名,例如 `gpt-4o-mini`、`gpt-4.1`、`qwen/qwen3-coder:free`。
- `api_key`:模型服务密钥。也可以通过环境变量提供。
- `endpoint`:自定义 OpenAI-compatible base URL。对 `openrouter` 来说会作为 `base_url` 使用
- `endpoint`:自定义 provider base URL。对 `openrouter`/`anthropic` 来说分别映射到各自 SDK 的 `base_url`。
- `proxy`:代理配置,支持 `http`、`https`、`no_proxy`、`use_system_proxy`、`disabled`。
- 其他未显式声明的字段会进入 `llm.extra`,并透传给 adapter;例如可以直接写 `temperature`、`max_tokens`。

Expand Down Expand Up @@ -258,6 +258,29 @@ export OPENROUTER_MODEL=qwen/qwen3-coder:free
export OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
```

Anthropic:

```json
{
"llm": {
"adapter": "anthropic",
"model": "claude-sonnet-4-5"
}
}
```

配合环境变量:

```bash
export ANTHROPIC_API_KEY=sk-ant-...
# 建议显式写完整模型名;CLI 会直接透传到 Anthropic SDK
export ANTHROPIC_MODEL=claude-sonnet-4-5
```

仓库示例文件:

- `client/examples/config.anthropic.example.json`

OpenAI-compatible / 自建模型网关:

```json
Expand Down Expand Up @@ -388,6 +411,7 @@ OpenAI-compatible / 自建模型网关:

- `openai` adapter:需要 `langchain-openai`
- `openrouter` adapter:需要 `openai`
- `anthropic` adapter:需要 `anthropic`

否则 `doctor` 会提示 adapter probe 或依赖缺失,runtime 也无法正常启动。

Expand Down
6 changes: 5 additions & 1 deletion client/commands/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ def build_doctor_report(
api_key_sources = {
"openrouter": bool(config.llm.api_key or os.getenv("OPENROUTER_API_KEY")),
"openai": bool(config.llm.api_key or os.getenv("OPENAI_API_KEY")),
"anthropic": bool(config.llm.api_key or os.getenv("ANTHROPIC_API_KEY")),
}
workspace = Path(config.workspace_dir).expanduser().resolve()
user_dir = Path(config.user_dir).expanduser().resolve()
Expand All @@ -72,6 +73,7 @@ def build_doctor_report(
"httpx_installed": importlib.util.find_spec("httpx") is not None,
"openai_sdk_installed": importlib.util.find_spec("openai") is not None,
"langchain_openai_installed": importlib.util.find_spec("langchain_openai") is not None,
"anthropic_sdk_installed": importlib.util.find_spec("anthropic") is not None,
}

diagnostics: dict[str, Any] = {
Expand All @@ -95,7 +97,7 @@ def build_doctor_report(
warnings: list[str] = []
if not diagnostics["workspace_exists"]:
warnings.append("workspace_dir does not exist")
if adapter not in {"openai", "openrouter"}:
if adapter not in {"openai", "openrouter", "anthropic"}:
warnings.append(f"unsupported adapter configured: {adapter}")
if not diagnostics["llm"]["api_key_present"]:
warnings.append(f"missing API key for adapter={adapter}")
Expand All @@ -111,6 +113,8 @@ def build_doctor_report(
warnings.append("openai SDK is required for openrouter adapter")
if adapter == "openai" and not deps["langchain_openai_installed"]:
warnings.append("langchain-openai is required for openai adapter")
if adapter == "anthropic" and not deps["anthropic_sdk_installed"]:
warnings.append("anthropic SDK is required for anthropic adapter")
diagnostics["warnings"] = warnings
diagnostics["ok"] = len(warnings) == 0
return diagnostics
6 changes: 6 additions & 0 deletions client/examples/config.anthropic.example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"llm": {
"adapter": "anthropic",
"model": "claude-sonnet-4-5"
}
}
2 changes: 1 addition & 1 deletion client/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -1532,7 +1532,7 @@ def _build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="DARE external CLI")
parser.add_argument("--workspace", default=str(Path.cwd()), help="workspace root path")
parser.add_argument("--user-dir", default=str(Path.home()), help="user directory path")
parser.add_argument("--adapter", default=None, help="llm adapter override (openai/openrouter)")
parser.add_argument("--adapter", default=None, help="llm adapter override (openai/openrouter/anthropic)")
parser.add_argument("--model", default=None, help="llm model override")
parser.add_argument("--api-key", default=None, help="llm api key override")
parser.add_argument("--endpoint", default=None, help="llm endpoint override")
Expand Down
3 changes: 2 additions & 1 deletion dare_framework/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from dare_framework.model.builtin_prompt_loader import BuiltInPromptLoader
from dare_framework.model.filesystem_prompt_loader import FileSystemPromptLoader
from dare_framework.model.layered_prompt_store import LayeredPromptStore
from dare_framework.model.adapters import *
from dare_framework.model.adapters import AnthropicModelAdapter, OpenAIModelAdapter, OpenRouterModelAdapter

__all__ = [
"IModelAdapter",
Expand All @@ -24,6 +24,7 @@
"BuiltInPromptLoader",
"FileSystemPromptLoader",
"LayeredPromptStore",
"AnthropicModelAdapter",
"OpenAIModelAdapter",
"OpenRouterModelAdapter",
]
3 changes: 2 additions & 1 deletion dare_framework/model/adapters/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Model adapters."""

from dare_framework.model.adapters.anthropic_adapter import AnthropicModelAdapter
from dare_framework.model.adapters.openai_adapter import OpenAIModelAdapter
from dare_framework.model.adapters.openrouter_adapter import OpenRouterModelAdapter

__all__ = ["OpenAIModelAdapter", "OpenRouterModelAdapter"]
__all__ = ["AnthropicModelAdapter", "OpenAIModelAdapter", "OpenRouterModelAdapter"]
Loading
Loading