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
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ AI_BASE_URL=https://api.deepseek.com/v1
AI_API_KEY=
AI_MODEL=deepseek-chat
AI_DAILY_TOKEN_BUDGET=500000
# OrcaRouter(OpenAI 兼容网关)示例:
# AI_BASE_URL=https://api.orcarouter.ai/v1
# AI_MODEL=orcarouter/auto

# ===== Server =====
HOST=0.0.0.0
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ PORT=3018 # 服务端口
| **数据** | Polars(计算)· DuckDB(查询)· Parquet(存储) |
| **回测** | vectorbt(全项目唯一 pandas 边界) |
| **数据源** | [TickFlow](https://tickflow.org/auth/register?ref=V3KDKGXPEA) 官方 SDK · 其他数据源后续迭代实装 |
| **AI**(可选) | OpenAI 兼容接口(DeepSeek / 通义 / Ollama 等) |
| **AI**(可选) | OpenAI 兼容接口(DeepSeek / 通义 / OrcaRouter / Ollama 等) |
| **前端** | React 18 · Vite · TypeScript · Tailwind · Tanstack Query · Lightweight Charts · ECharts · dnd-kit |
| **部署** | Docker 两阶段构建,前端 dist 拷进后端镜像,**单容器** |

Expand Down
13 changes: 13 additions & 0 deletions backend/tests/test_ai_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ def test_normalize_openai_base_url_strips_trailing_slash():
assert normalize_openai_base_url("https://open.bigmodel.cn/api/paas/v4/") == "https://open.bigmodel.cn/api/paas/v4"


def test_normalize_openai_base_url_orcarouter():
"""OrcaRouter 官方网关 base_url 已是 /v1, 不应被重复补 /v1。"""
assert normalize_openai_base_url("https://api.orcarouter.ai/v1") == "https://api.orcarouter.ai/v1"
assert normalize_openai_base_url("https://api.orcarouter.ai") == "https://api.orcarouter.ai/v1"
assert normalize_openai_base_url("https://api.orcarouter.ai/v1/chat/completions") == "https://api.orcarouter.ai/v1"


def test_orcarouter_matches_openai_compat_provider():
"""OrcaRouter 走 openai_compat 通道(base_url 可配), 不触发 codex_cli 分支。"""
assert not ai_provider.is_codex_cli_provider("openai_compat")
assert ai_provider.is_codex_cli_provider("codex_cli")


def test_format_openai_error_hides_html_gateway_body():
response = httpx.Response(
504,
Expand Down
7 changes: 7 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ AI_DAILY_TOKEN_BUDGET=500000 # 每日 token 预算上限
| `AI_MODEL` | 模型名,如 `deepseek-chat` |
| `AI_DAILY_TOKEN_BUDGET` | 每日 token 预算,超限后当日不再调用 |

**OrcaRouter**:也可以通过 OpenAI 兼容接口接入 [OrcaRouter](https://www.orcarouter.ai) 网关(聚合前沿模型,并在同一端点提供网关级零信任安全防护)。设置页「快速预设」已内置 OrcaRouter 一键配置,或手动填写:

```ini
AI_BASE_URL=https://api.orcarouter.ai/v1
AI_MODEL=orcarouter/auto
```

接入示例见 [strategy.md](./strategy.md) 的「AI 生成策略」章节。

---
Expand Down
1 change: 1 addition & 0 deletions frontend/src/pages/settings/AI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const PRESETS: { label: string; provider?: string; url: string; model: string; c
{ label: 'Kimi', url: 'https://api.moonshot.cn/v1', model: 'kimi-k2.7-code', website: 'https://platform.moonshot.cn/', websiteLabel: 'platform.moonshot.cn', description: '月之暗面 Moonshot 官方 OpenAI 兼容接口,支持超长上下文。' },
{ label: 'Codex CLI', provider: CODEX_PROVIDER, url: '', model: DEFAULT_CODEX_MODEL, codexCommand: CODEX_COMMAND, website: 'https://developers.openai.com/codex/noninteractive', websiteLabel: 'codex exec', description: '调用本机 Codex CLI 的 codex exec, 适合已登录 ChatGPT/Codex 的本地环境。' },
{ label: '炸鸡中转站', url: 'https://api.zhaji.dev/v1', model: 'gpt-5.5', website: 'https://api.zhaji.dev', websiteLabel: 'api.zhaji.dev', description: 'OpenAI 兼容中转服务,适合直接使用国际模型。' },
{ label: 'OrcaRouter', url: 'https://api.orcarouter.ai/v1', model: 'orcarouter/auto', website: 'https://www.orcarouter.ai', websiteLabel: 'orcarouter.ai', description: 'OrcaRouter 官方 OpenAI 兼容网关,聚合前沿模型并在同一端点提供网关级零信任安全防护。' },
]

export function SettingsAIPanel() {
Expand Down