@wengine-ai/llms 2.3.2417 → 2.3.2419
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/api/empty-answer-guard.d.ts +66 -0
- package/dist/ccr/models.d.ts +9 -1
- package/dist/ccr/plugin-install-routes.d.ts +11 -0
- package/dist/cjs/server.cjs +190 -189
- package/dist/cjs/server.cjs.map +4 -4
- package/dist/esm/server.mjs +183 -182
- package/dist/esm/server.mjs.map +4 -4
- package/dist/index.html +16 -16
- package/dist/provider-plugins/types.d.ts +54 -0
- package/dist/services/active-probe.d.ts +44 -3
- package/dist/services/provider-health.d.ts +17 -0
- package/dist/transformer/openai.responses.transformer.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -93,7 +93,8 @@ npm install -g @wengine-ai/claude-code-router-next@latest && ccr restart
|
|
|
93
93
|
|
|
94
94
|
| 版本 | 发布内容 |
|
|
95
95
|
| --- | --- |
|
|
96
|
-
| **v2.3.
|
|
96
|
+
| **v2.3.2419** | <ul><li>**codex 0 输出回合根因修复**:数据面此前绕过 `SUBSCRIPTION_PROXY_URL` 走直连路径,中间盒剥离响应头并破坏压缩体(插件改为走订阅代理 + identity 编码,ccr-provider-codex 1.0.7);同时单次读取的格式探测在代理分块下漏掉格式标记,Responses 流被误送入 Chat 转换器输出「完整但空」的消息(现在累积读取判定格式,且原生 Responses 流仅对 codex 客户端放行,其他客户端走 Responses→Chat 转换)。</li><li>**gemini-oauth 数据面加 identity 编码**,登录失败记录 Google 原始错误与失败步骤(1.0.7)。</li></ul> |
|
|
97
|
+
| **v2.3.2418** | <ul><li>**空回复现在会重试并降级**:上游返回 200 且 `content` 为空时不再被记成成功,改为触发同模型重试与 fallback;判据是 content 集合为空(tool_use-only 等合法回合不受影响),探测有字节与时间上限,慢速流不丢字节;Codex 侧同样拦住空 `response.completed`。</li><li>**流式回合不再记录 `output_tokens: 0`**:普通流式请求此前没有 usage 读取器,健康回复被记成全零且无 TTFT;现从 tee 分支采集并按流序合并。</li><li>**不健康 provider 从模型列表降级剔除**:熔断模型对所有 provider 类型生效隐藏,恢复后自动放回;订阅类 provider 新增两级后台探活(先查账号状态,再发真实最小请求),不再依赖真实请求反复撞死账号。</li></ul> |
|
|
97
98
|
| **v2.3.2415** | <ul><li>**新增 Qoder 订阅 provider(qoder.com + qoder.cn)**: 同一插件同引擎支持国际站与国内站 PAT 订阅,双 managed provider + 卡片双区块(中英双语);含整配置写串行化、401 单次刷新重放、每帧 1MiB 上限等健壮性修复。</li><li>**修复 WorkBuddy 6004 重置时间戳时区**: 测试改用 Asia/Shanghai 渲染,UTC CI 不再挂。</li><li>**修复 tool_result 图片按 base64 文本发送**: 图片改为视觉块发送,避免 200 万+ token 膨胀与上游拒绝。</li></ul> |
|
|
98
99
|
| **v2.3.2414** | <ul><li>**CCR Desktop(可选 Tauri 客户端)与 Pi 配置生命周期修复**: 新增可选 Tauri 桌面壳(托盘快速切换全局默认模型、quota 摘要、单实例;生命周期伴侣化、退出不杀 gateway);修复 `ccr restart` 的 disable→enable 与 legacy 项目 provider 改名迁移丢失 Pi 自定义模型字段(thinkingLevelMap/modelOverrides)。</li></ul> |
|
|
99
100
|
| **v2.3.2413** | <ul><li>**修复 TTFT 永久为空**: #65(pi 会话粘连)与 #67(temp-file 重写)合并冲突解决时丢失了 #65 的 \`statsKey\` 文件名优先逻辑,handler 用会话 UUID 命名临时文件而 reader 用 requestId 查找 → 永久不匹配 → 所有流式请求的 TTFT 都是 null、tokens_per_second 退化为按完整时长重算。修复后 handler 优先用 \`metadata.statsKey\` 命名文件(对齐 reader),TTFT 与真实速度恢复。</li></ul> |
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect an upstream answer that completed successfully (HTTP 200) but carried
|
|
3
|
+
* no content blocks at all.
|
|
4
|
+
*
|
|
5
|
+
* The client shows such a turn as "no response" — a message is sent and nothing
|
|
6
|
+
* happens. Because the wire response is an ordinary 200 with a normal
|
|
7
|
+
* message_start / message_delta / message_stop sequence, nothing in the request
|
|
8
|
+
* path throws, so neither the same-model retry nor the fallback chain runs, and
|
|
9
|
+
* the empty turn is recorded as a provider success (which also keeps the
|
|
10
|
+
* unhealthy model out of the fail pool).
|
|
11
|
+
*
|
|
12
|
+
* Judging the whole content-block set rather than the text keeps legitimate
|
|
13
|
+
* turns alive: a tool_use-only turn, a stop_sequence hit, and a refusal (which
|
|
14
|
+
* still emits a text block) all contain at least one content block.
|
|
15
|
+
*
|
|
16
|
+
* The probe is bounded by EMPTY_ANSWER_INSPECT_BYTES, so a healthy answer is
|
|
17
|
+
* released right after its first content block and is never buffered further.
|
|
18
|
+
*/
|
|
19
|
+
/** Upper bound on the bytes inspected before a stream is forwarded untouched. */
|
|
20
|
+
export declare const EMPTY_ANSWER_INSPECT_BYTES: number;
|
|
21
|
+
/**
|
|
22
|
+
* Upper bound on how long the probe may hold the response before forwarding.
|
|
23
|
+
*
|
|
24
|
+
* Long enough that a thinking model's first content block arrives well within it
|
|
25
|
+
* on a healthy stream, short enough that a stalled upstream cannot pin the
|
|
26
|
+
* request (and its connection) open.
|
|
27
|
+
*/
|
|
28
|
+
export declare const EMPTY_ANSWER_INSPECT_DEADLINE_MS = 10000;
|
|
29
|
+
/** `content` = real output; `empty` = finished with no block; `pending` = undecided. */
|
|
30
|
+
export type AnswerVerdict = "content" | "empty" | "pending";
|
|
31
|
+
/**
|
|
32
|
+
* Judge the buffered prefix of an answer.
|
|
33
|
+
*
|
|
34
|
+
* Line-prefix scanning (rather than JSON parsing) is deliberate: Anthropic SSE
|
|
35
|
+
* names each event on both `event:` and `data:` lines, and payloads may be split
|
|
36
|
+
* across reads, so a marker scan stays correct and cheap on every chunk.
|
|
37
|
+
*/
|
|
38
|
+
export declare function inspectAnswerPrefix(text: string): AnswerVerdict;
|
|
39
|
+
export interface AnswerProbeResult {
|
|
40
|
+
verdict: AnswerVerdict;
|
|
41
|
+
/** Chunks consumed while probing, in order, to be re-emitted verbatim. */
|
|
42
|
+
chunks: Uint8Array[];
|
|
43
|
+
/** Bytes consumed while probing. */
|
|
44
|
+
bytes: number;
|
|
45
|
+
/** Remaining upstream stream, or null when the answer ended / was cancelled. */
|
|
46
|
+
rest: ReadableStream<Uint8Array> | null;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Read enough of `body` to decide whether the answer carries any content.
|
|
50
|
+
*
|
|
51
|
+
* Consumed bytes are returned in `chunks` so a healthy stream can be rebuilt
|
|
52
|
+
* byte-for-byte. Reading stops at the first content block (fast, non-blocking
|
|
53
|
+
* for real answers), at the byte budget (forwarded untouched — an unknown shape
|
|
54
|
+
* is never second-guessed), at the terminal event (verdict `empty`), or when
|
|
55
|
+
* `deadlineMs` elapses.
|
|
56
|
+
*
|
|
57
|
+
* The deadline matters: before this guard, the first SSE event was forwarded as
|
|
58
|
+
* soon as it arrived. Waiting for a content block is open-ended by nature (a
|
|
59
|
+
* thinking model may emit nothing but heartbeats for a long time), so an
|
|
60
|
+
* upstream that stalls after `message_start` would hold the response — and the
|
|
61
|
+
* whole request — forever. On timeout the answer is treated as `pending` and
|
|
62
|
+
* forwarded as-is, which preserves the old behaviour for slow-but-alive streams.
|
|
63
|
+
*/
|
|
64
|
+
export declare function probeAnswer(body: ReadableStream<Uint8Array>, limitBytes?: number, deadlineMs?: number): Promise<AnswerProbeResult>;
|
|
65
|
+
/** Re-emit probed chunks, then continue with `rest`. */
|
|
66
|
+
export declare function rebuildAnswerStream(chunks: Uint8Array[], rest: ReadableStream<Uint8Array> | null): ReadableStream<Uint8Array>;
|
package/dist/ccr/models.d.ts
CHANGED
|
@@ -23,4 +23,12 @@ export interface AnthropicModelInfo {
|
|
|
23
23
|
maxOutputTokens: number;
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
-
|
|
26
|
+
/**
|
|
27
|
+
* Predicate used to hide a provider/model pair from the model list.
|
|
28
|
+
*
|
|
29
|
+
* Pairs it returns true for are still routed (fallback, an explicit request for
|
|
30
|
+
* that exact model) but the client never sees them, so an unhealthy model stops
|
|
31
|
+
* being picked session after session.
|
|
32
|
+
*/
|
|
33
|
+
export type ModelAvailabilityFilter = (provider: string, model: string) => boolean;
|
|
34
|
+
export declare function listAnthropicCompatibleModels(config: any, isAvailable?: ModelAvailabilityFilter): AnthropicModelInfo[];
|
|
@@ -31,6 +31,17 @@ export interface CatalogEntry {
|
|
|
31
31
|
version: string;
|
|
32
32
|
installed: boolean;
|
|
33
33
|
installedVersion: string | null;
|
|
34
|
+
/**
|
|
35
|
+
* Present only when `installed` and the versions differ: the client shows an
|
|
36
|
+
* update affordance for exactly these entries. Compares the npm `version`
|
|
37
|
+
* against the installed manifest version, so a downgrade is also surfaced
|
|
38
|
+
* rather than silently hidden.
|
|
39
|
+
*/
|
|
40
|
+
updateAvailable?: boolean;
|
|
41
|
+
/** True for a plugin installed locally that npm search did not return (unlisted,
|
|
42
|
+
* renamed, or an offline/local-only install). No npm version is known, so the
|
|
43
|
+
* row shows the installed version without an update affordance. */
|
|
44
|
+
localOnly?: boolean;
|
|
34
45
|
}
|
|
35
46
|
export interface CatalogResult {
|
|
36
47
|
ok: true;
|