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
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,7 @@ OPENCODE_PROXY_CLEANUP_MAX_AGE_MS=86400000
# OPENCODE_SERVER_PORT=10001
# OPENCODE_USE_ISOLATED_HOME=false
# OPENCODE_PROXY_REQUEST_TIMEOUT_MS=180000
# Retry budget after the initial attempt (0-5, default 3; total attempts = 1+n)
# OPENCODE_PROXY_RETRY_MAX_RETRIES=3
# OPENCODE_PROXY_DEBUG=false
# OPENCODE_ZEN_API_KEY=
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

- **Anthropic Messages API**:新增 `POST /v1/messages`(`max_tokens` 必填,支持 `system`/`tools(input_schema)`/`tool_choice{auto,any,tool,none}`/`thinking→reasoning`/`image`;`tool_use.id` 原样往返;非流式回 `message` 对象,流式回 `message_start/content_block_*/message_delta/message_stop` 无 `[DONE]`;认证同时支持 `x-api-key`;CORS 放行 `x-api-key/anthropic-version`)。新增 `src/converters/anthropic.js` 纯函数转换层与 `tests/messages-anthropic.test.js`(9 例)。附带修复 `EXTERNAL_TOOL_PREFIX` 缺 import 的 latent `ReferenceError`。
- **官方对齐重试策略**:移植上游 `session/retry.ts`(`2s×2ⁿ⁻¹` 指数退避 +25% jitter;优先 `retry-after-ms`/`retry-after`,header 等待 clamp 30s;`5xx` 强制、`isRetryable`、`ContextOverflow` 永不)。重试次数 `n` 经 `OPENCODE_PROXY_RETRY_MAX_RETRIES` 控制(默认 3,总尝试 1+n,上限对齐官方 5)。chat 双路由与 messages 非串流改用新退避;responses 非串流新增同结构重试迴圈(仅无产出且 transient 时;responses/messages 串流保持单次尝试)。三处非串流的 prompt/poll 抛错同样走 transient 重试路由(传输层 throw 不再直接逃逸);真鉴权失败(invalid api key/unauthorized/authentication failed)立即透出不再烧退避。新增 `src/retry/policy.js` 与 `tests/retry-policy.test.js`(11 例)及接线/回归测试 14 例。

### Fixed

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ curl -X POST http://127.0.0.1:10000/v1/messages \
| `OPENCODE_PROXY_CLEANUP_INTERVAL_MS` | `43200000` | 清理间隔 (毫秒) |
| `OPENCODE_PROXY_CLEANUP_MAX_AGE_MS` | `86400000` | 最大存储时间 (毫秒) |
| `OPENCODE_PROXY_REQUEST_TIMEOUT_MS` | `180000` | 请求超时时间 (毫秒) |
| `OPENCODE_PROXY_RETRY_MAX_RETRIES` | `3` | 首次失败后重试次数 (0-5,总尝试 1+n;退避指数+jitter 并优先 `retry-after`) |
| `OPENCODE_SERVER_URL` | `http://127.0.0.1:10001` | OpenCode 后端地址 |
| `OPENCODE_SERVER_PASSWORD` | - | OpenCode 后端密码 |
| `OPENCODE_PATH` | `opencode` | OpenCode 可执行文件路径 |
Expand Down
3 changes: 2 additions & 1 deletion config.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@
"DEBUG": false,
"OPENCODE_SERVER_URL": "http://127.0.0.1:10001",
"OPENCODE_PATH": "opencode",
"REQUEST_TIMEOUT_MS": 180000
"REQUEST_TIMEOUT_MS": 180000,
"RETRY_MAX_RETRIES": 3
}
6 changes: 5 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
| `CLEANUP_INTERVAL_MS` | `43200000` | 清理间隔 (毫秒) |
| `CLEANUP_MAX_AGE_MS` | `86400000` | 最大存储时间 (毫秒) |
| `REQUEST_TIMEOUT_MS` | `180000` | 请求超时时间 (毫秒) |
| `RETRY_MAX_RETRIES` / `OPENCODE_PROXY_RETRY_MAX_RETRIES` | `3` | 首次失败后重试次数 (0-5,总尝试 1+n;退避指数+jitter 并优先 `retry-after`) |

> 重试退避移植自上游 `session/retry.ts`(`2s×2ⁿ⁻¹` +25% jitter),但 `retry-after` 等待 clamp 在 30s(上游近无界;网关面对自带超时的客户端不宜久睡)。旧部署注意:默认总尝试由 3 次变为 1+3=4 次,如需接近旧次数可设 `2`。

### 调试配置

Expand Down Expand Up @@ -81,7 +84,8 @@
"DEBUG": false,
"OPENCODE_SERVER_URL": "http://127.0.0.1:10001",
"OPENCODE_PATH": "opencode",
"REQUEST_TIMEOUT_MS": 180000
"REQUEST_TIMEOUT_MS": 180000,
"RETRY_MAX_RETRIES": 3
}
```

Expand Down
3 changes: 3 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { startProxy } from './src/proxy.js';
import { resolveMaxRetries } from './src/retry/policy.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
Expand Down Expand Up @@ -91,6 +92,7 @@ const finalConfig = {
METRICS_REQUIRE_AUTH: parseBool(process.env.OPENCODE_METRICS_REQUIRE_AUTH, parseBool(fileConfig.METRICS_REQUIRE_AUTH, defaultConfig.METRICS_REQUIRE_AUTH)),
USE_ISOLATED_HOME: parseBool(process.env.OPENCODE_USE_ISOLATED_HOME, parseBool(fileConfig.USE_ISOLATED_HOME, false)),
REQUEST_TIMEOUT_MS: parseInt(process.env.OPENCODE_PROXY_REQUEST_TIMEOUT_MS) || fileConfig.REQUEST_TIMEOUT_MS || 180000,
RETRY_MAX_RETRIES: process.env.OPENCODE_PROXY_RETRY_MAX_RETRIES ?? fileConfig.RETRY_MAX_RETRIES ?? 3,
DEBUG: parseBool(process.env.OPENCODE_PROXY_DEBUG, parseBool(fileConfig.DEBUG, false)),
ZEN_API_KEY: process.env.OPENCODE_ZEN_API_KEY || fileConfig.ZEN_API_KEY || '',
PROMPT_MODE: process.env.OPENCODE_PROXY_PROMPT_MODE || fileConfig.PROMPT_MODE || defaultConfig.PROMPT_MODE,
Expand Down Expand Up @@ -140,6 +142,7 @@ console.log(` - Metrics Enabled: ${finalConfig.METRICS_ENABLED ? 'Yes' : 'No'}`
console.log(` - Metrics Require Auth: ${finalConfig.METRICS_REQUIRE_AUTH ? 'Yes' : 'No'}`);
console.log(` - Use Isolated Home: ${finalConfig.USE_ISOLATED_HOME ? 'Yes' : 'No'}`);
console.log(` - Request Timeout: ${finalConfig.REQUEST_TIMEOUT_MS}ms`);
console.log(` - Max Retries: ${resolveMaxRetries(finalConfig.RETRY_MAX_RETRIES)} (raw: ${finalConfig.RETRY_MAX_RETRIES}, total attempts 1+n)`);
console.log(` - Prompt Mode: ${finalConfig.PROMPT_MODE}`);
console.log(` - Omit System Prompt: ${finalConfig.OMIT_SYSTEM_PROMPT ? 'Yes' : 'No'}`);
console.log(` - Auto Cleanup Conversations: ${finalConfig.AUTO_CLEANUP_CONVERSATIONS ? 'Yes' : 'No'}`);
Expand Down
Loading
Loading