Skip to content

Commit a9f3572

Browse files
authored
Merge pull request #18 from mirror29/feat/unified-env-and-multi-llm-provider
feat(orchestration,research): 统一 .env + 7 家 LLM provider 接入
2 parents 845dfff + f4699ca commit a9f3572

20 files changed

Lines changed: 1184 additions & 144 deletions

File tree

.env.example

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# ============================================================
2+
# Inalpha · 统一环境配置(单 .env,全 service 共享)
3+
# ============================================================
4+
# 拷贝本文件为根目录的 .env 后填值:
5+
#
6+
# cp .env.example .env
7+
#
8+
# 加载顺序:
9+
# 1) `scripts/dev.sh` 启动前 source 根 `.env` → 子进程继承
10+
# 2) 各 service 代码层 fallback:Python pydantic-settings 显式读
11+
# `<repo-root>/.env`;TS Mastra 用 `dotenv` 同样指向根。
12+
# 3) 旧 `services/*/.env` / `packages/orchestration/.env` 仍作为
13+
# cwd-level fallback **覆盖**根 .env(迁移期友好;迁移完可删)。
14+
#
15+
# 字段按 service 分组,但全部住在这一个文件里。
16+
# ============================================================
17+
18+
19+
# ───── 1. 共享基础(所有 Python service 都读) ──────────────
20+
# Postgres 连接串;services/data 与 services/paper 使用
21+
DATABASE_URL=postgresql+psycopg://quant:devpass@localhost:5433/inalpha
22+
23+
# JWT 跨 service 共享密钥(≥32 字节强随机)
24+
JWT_SECRET=please-change-me-32-chars-minimum-xxxx
25+
JWT_ALGORITHM=HS256
26+
LOG_LEVEL=INFO
27+
28+
29+
# ───── 2. Service URLs & ports(本地默认) ─────────────────
30+
DATA_SERVICE_URL=http://localhost:8001
31+
PAPER_SERVICE_URL=http://localhost:8002
32+
RESEARCH_SERVICE_URL=http://localhost:8003
33+
34+
DATA_SERVICE_PORT=8001
35+
PAPER_SERVICE_PORT=8002
36+
RESEARCH_SERVICE_PORT=8003
37+
38+
39+
# ───── 3. LLM Provider(必填一个 API key) ─────────────────
40+
# 选 provider:deepseek | anthropic | openai | gemini | kimi | zhipu | ollama
41+
LLM_PROVIDER=deepseek
42+
43+
# 留空 = 各 provider 默认模型。要换模型时填具体 ID,例如:
44+
# deepseek → deepseek-chat / deepseek-reasoner / deepseek-v4-pro
45+
# anthropic → claude-opus-4-7 / claude-sonnet-4-6 / claude-haiku-4-5
46+
# openai → gpt-5 / gpt-5-mini / gpt-4o
47+
# gemini → gemini-2.0-flash-exp / gemini-2.5-pro
48+
# kimi → moonshot-v1-8k / moonshot-v1-32k / moonshot-v1-128k
49+
# zhipu → glm-4-plus / glm-4-air / glm-4-flash
50+
# ollama → llama3.2 / qwen2.5 / 任何已 `ollama pull` 的本地模型
51+
LLM_MODEL=
52+
53+
# 各 provider 的 API key(只需填你 LLM_PROVIDER 选中的那家)
54+
DEEPSEEK_API_KEY=
55+
ANTHROPIC_API_KEY=
56+
OPENAI_API_KEY=
57+
GEMINI_API_KEY=
58+
KIMI_API_KEY=
59+
ZHIPU_API_KEY=
60+
61+
# Ollama 本地不需要 key,配 base URL 即可(默认 http://localhost:11434/v1)
62+
OLLAMA_BASE_URL=http://localhost:11434/v1
63+
64+
# LLM 护栏(可选,有默认值)
65+
LLM_TIMEOUT_SECONDS=60
66+
LLM_MAX_CONCURRENT=5
67+
LLM_MAX_RETRIES=3
68+
# Bull/Bear 辩论轮数(services/research);0 = 跳过
69+
RESEARCH_MAX_DEBATE_ROUNDS=1
70+
71+
72+
# ───── 4. services/data · Binance ──────────────────────────
73+
# K 线 / OHLCV 公开接口可空;私有接口才填
74+
BINANCE_API_KEY=
75+
BINANCE_API_SECRET=
76+
77+
78+
# ───── 5. services/paper · RiskEngine(可选) ──────────────
79+
# false = HTTP 下单链路跳过风控(运维窗口 / 数据迁移可临时关,默认 true)
80+
# INALPHA_RISK_ENGINE_ENABLED=true
81+
# risk_rules.toml 路径(默认 configs/risk_rules.toml)
82+
# INALPHA_RISK_RULES_CONFIG=configs/risk_rules.toml
83+
84+
85+
# ───── 6. orchestration / Mastra(可选) ──────────────────
86+
# 类 cron agent 模式;默认 false
87+
SCHEDULER_ENABLED=false

AGENTS.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,22 @@ Inalpha = AI agent 编排 + 多 Python kernel 的**量化实验框架**,重度
4242
pnpm i # Node 包(packages/orchestration)
4343
uv sync # Python 包(services/*)
4444

45+
# 配置统一 .env(所有 service 共享根目录一份 .env)
46+
cp .env.example .env # 在 .env 里填 LLM_PROVIDER + 对应 *_API_KEY
47+
# 详见 README.md §Quick Start 的 provider/model 表
48+
4549
# DB schema 升到最新(dev.sh 不会自动跑;漏跑会导致 paper 服务 500:表不存在)
4650
cd infra/migrations && uv run alembic upgrade head && cd ../..
4751

4852
# 一键起所有 service(推荐)
49-
bash scripts/dev.sh # data:8001 + paper:8002 + mastra:4111
53+
bash scripts/dev.sh # data:8001 + paper:8002 + research:8003 + mastra:4111
5054
bash scripts/dev.sh logs # 跟随日志
5155
bash scripts/dev.sh stop # 停止全部
5256

53-
# 手动起(如果想要 3 个独立 terminal)
54-
cd services/data && uv run uvicorn inalpha_data.main:app --port 8001 --reload
55-
cd services/paper && uv run uvicorn inalpha_paper.main:app --port 8002 --reload
57+
# 手动起(如果想要 4 个独立 terminal)
58+
cd services/data && uv run uvicorn inalpha_data.main:app --port 8001 --reload
59+
cd services/paper && uv run uvicorn inalpha_paper.main:app --port 8002 --reload
60+
cd services/research && uv run uvicorn inalpha_research.main:app --port 8003 --reload
5661
cd packages/orchestration && pnpm dev
5762

5863
# 跨文件一致性检验(提交前跑一次)

README.md

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,16 +190,48 @@ Inalpha is not invented from scratch. It selectively inherits proven designs fro
190190

191191
## Quick Start
192192

193+
### 1 · Install dependencies
194+
195+
```bash
196+
pnpm i # Node packages (packages/orchestration)
197+
uv sync # Python packages (services/_shared, data, paper, research)
198+
```
199+
200+
### 2 · Configure your LLM key (required)
201+
202+
A single `.env` at the repo root is read by Mastra (TS) **and** all Python services. Copy the template and fill in the LLM provider you want to use:
203+
193204
```bash
194-
pnpm i # Node packages (packages/orchestration)
195-
uv sync # Python packages (services/_shared, data, paper)
205+
cp .env.example .env
206+
```
207+
208+
Inside `.env`, set `LLM_PROVIDER` to one of `deepseek | anthropic | openai | gemini | kimi | zhipu | ollama` and fill in the matching key:
196209

197-
bash scripts/dev.sh # one shot — starts data (8001) + paper (8002) + mastra (4111)
210+
| Provider | env var | Default model | Get a key |
211+
|---|---|---|---|
212+
| `deepseek` | `DEEPSEEK_API_KEY` | `deepseek-chat` | [platform.deepseek.com](https://platform.deepseek.com) |
213+
| `anthropic` | `ANTHROPIC_API_KEY` | `claude-opus-4-7` | [console.anthropic.com](https://console.anthropic.com) |
214+
| `openai` | `OPENAI_API_KEY` | `gpt-5` | [platform.openai.com](https://platform.openai.com) |
215+
| `gemini` | `GEMINI_API_KEY` | `gemini-2.0-flash-exp` | [aistudio.google.com](https://aistudio.google.com) |
216+
| `kimi` | `KIMI_API_KEY` | `moonshot-v1-8k` | [platform.moonshot.cn](https://platform.moonshot.cn) |
217+
| `zhipu` | `ZHIPU_API_KEY` | `glm-4-plus` | [open.bigmodel.cn](https://open.bigmodel.cn) |
218+
| `ollama` | — (local) | `llama3.2` | `ollama pull llama3.2` |
219+
220+
Override the default model by setting `LLM_MODEL=...` in the same file. Mastra and `services/research` both read this one file — no per-service config to juggle.
221+
222+
> Already have keys in `services/*/.env` or `packages/orchestration/.env` from earlier? Those still work as cwd-level overrides while you migrate. Once you copy them up into the root `.env`, the per-service files can be deleted.
223+
224+
### 3 · Start everything
225+
226+
```bash
227+
bash scripts/dev.sh # one shot — data (8001) + paper (8002) + research (8003) + mastra (4111)
198228
bash scripts/dev.sh logs # follow service logs
199229
bash scripts/dev.sh stop # stop everything
200230
```
201231

202-
Then open the `mastra dev` playground at `http://127.0.0.1:4111` to start a conversation with the orchestrator agent.
232+
### 4 · Talk to the orchestrator
233+
234+
Open the `mastra dev` playground at **<http://127.0.0.1:4111>** — that's where you chat with the orchestrator agent and watch every tool call, hook event, and approval token in the live trace UI. `services/paper` does not call any LLM directly; only the orchestrator (Mastra) and `services/research` consume your key.
203235

204236
Prefer the manual three-terminal flow? See [`AGENTS.md §4`](AGENTS.md).
205237

README.zh-CN.md

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,16 +190,48 @@ Inalpha 不是从零发明——它有选择地继承前人的最优解,并明
190190

191191
## Quick Start
192192

193+
### 1 · 安装依赖
194+
195+
```bash
196+
pnpm i # Node 包(packages/orchestration)
197+
uv sync # Python 包(services/_shared, data, paper, research)
198+
```
199+
200+
### 2 · 配置 LLM Key(必须)
201+
202+
仓库根目录**单一 `.env`** 同时供 Mastra(TS)和所有 Python service 读取。拷贝模板后填你要用的那家 provider:
203+
193204
```bash
194-
pnpm i # Node 包(packages/orchestration)
195-
uv sync # Python 包(services/_shared, data, paper)
205+
cp .env.example .env
206+
```
207+
208+
`.env` 里把 `LLM_PROVIDER` 设成 `deepseek | anthropic | openai | gemini | kimi | zhipu | ollama` 之一,填对应 key:
196209

197-
bash scripts/dev.sh # 一键起 data(8001) + paper(8002) + mastra(4111)
210+
| Provider | env 字段 | 默认模型 | 获取 key |
211+
|---|---|---|---|
212+
| `deepseek` | `DEEPSEEK_API_KEY` | `deepseek-chat` | [platform.deepseek.com](https://platform.deepseek.com) |
213+
| `anthropic` | `ANTHROPIC_API_KEY` | `claude-opus-4-7` | [console.anthropic.com](https://console.anthropic.com) |
214+
| `openai` | `OPENAI_API_KEY` | `gpt-5` | [platform.openai.com](https://platform.openai.com) |
215+
| `gemini` | `GEMINI_API_KEY` | `gemini-2.0-flash-exp` | [aistudio.google.com](https://aistudio.google.com) |
216+
| `kimi` | `KIMI_API_KEY` | `moonshot-v1-8k` | [platform.moonshot.cn](https://platform.moonshot.cn) |
217+
| `zhipu` | `ZHIPU_API_KEY` | `glm-4-plus` | [open.bigmodel.cn](https://open.bigmodel.cn) |
218+
| `ollama` | — (本地) | `llama3.2` | `ollama pull llama3.2` |
219+
220+
要换模型?把 `LLM_MODEL=...` 一起填。Mastra 和 `services/research` 共读这一份配置——不再需要在每个 service 各自维护 .env。
221+
222+
> 旧用户 `services/*/.env` / `packages/orchestration/.env` 里已填的值仍作为 cwd-level fallback 生效(迁移期友好)。把它们合并到根 `.env` 后即可删掉。
223+
224+
### 3 · 启动全部 service
225+
226+
```bash
227+
bash scripts/dev.sh # 一键起 data(8001) + paper(8002) + research(8003) + mastra(4111)
198228
bash scripts/dev.sh logs # 跟随日志
199229
bash scripts/dev.sh stop # 停止全部
200230
```
201231

202-
随后打开 `http://127.0.0.1:4111``mastra dev` playground,与 orchestrator agent 对话。
232+
### 4 · 跟 orchestrator 对话
233+
234+
打开 `mastra dev` playground **<http://127.0.0.1:4111>** —— 在这里跟 orchestrator agent 聊天,并在 live trace UI 里看每一次 tool 调用、hook 事件、approval token 流转。`services/paper` 不直接调 LLM;只有 orchestrator(Mastra)和 `services/research` 会消耗你的 key。
203235

204236
想用 3 个独立 terminal 手动起?见 [`AGENTS.md §4`](AGENTS.md)
205237

packages/orchestration/.env.example

Lines changed: 0 additions & 20 deletions
This file was deleted.

packages/orchestration/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,19 @@
2626
"dev": "mastra dev --dir src/mastra"
2727
},
2828
"dependencies": {
29+
"@ai-sdk/anthropic": "^2.0.35",
2930
"@ai-sdk/deepseek": "^2.0.35",
31+
"@ai-sdk/google": "^2.0.35",
32+
"@ai-sdk/openai": "^2.0.35",
33+
"@ai-sdk/openai-compatible": "^2.0.48",
3034
"@mastra/core": "^1.36.0",
3135
"@mastra/libsql": "^1.11.1",
3236
"@mastra/loggers": "^1.1.1",
3337
"@mastra/memory": "^1.19.0",
3438
"@mastra/observability": "^1.13.0",
3539
"@modelcontextprotocol/sdk": "^1.29.0",
3640
"croner": "^9.0.0",
41+
"dotenv": "^17.2.3",
3742
"hono": "^4.12.21",
3843
"jose": "^6.2.3",
3944
"pg": "^8.13.0",

packages/orchestration/pnpm-lock.yaml

Lines changed: 63 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/orchestration/src/env.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* dotenv 加载根目录 .env(统一 env 入口)。
3+
*
4+
* **Side-effect import** —— 本模块顶层立刻加载、不导出任何符号。
5+
* 任何使用 `process.env.{LLM_*, DEEPSEEK_*, ANTHROPIC_*, ...}` 的入口
6+
* 必须把 `import "./env.js"` 放在 import 列表的**最前面**,以保证 dotenv
7+
* 在模块顶层 `createXxx({ apiKey: process.env.* })` 类调用之前生效。
8+
*
9+
* 加载顺序(`override: false`,即"已有 env 不覆盖"):
10+
*
11+
* 1. shell 显式 `export FOO=...` —— 最优先(启动时已在 process.env)
12+
* 2. `<cwd>/.env` —— 旧用户 `packages/orchestration/.env` 兼容
13+
* 3. `<repo-root>/.env` —— **统一入口**(新用户填这里)
14+
*
15+
* 与 Python 端 `services/_shared/config.py` 的 `env_file=(<root>/.env, ./.env)`
16+
* 设计对称。
17+
*/
18+
import { config as loadDotenv } from "dotenv";
19+
import { dirname, resolve } from "node:path";
20+
import { fileURLToPath } from "node:url";
21+
22+
const here = dirname(fileURLToPath(import.meta.url));
23+
// packages/orchestration/src/env.ts → repo root 3 层向上
24+
const repoRoot = resolve(here, "..", "..", "..");
25+
26+
// override:false → 已有的 process.env 优先;本调用只填空缺
27+
loadDotenv({ path: resolve(process.cwd(), ".env"), override: false });
28+
loadDotenv({ path: resolve(repoRoot, ".env"), override: false });

0 commit comments

Comments
 (0)