mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(open-sse): satisfy T11 explicit-any budget (regex counts word any)
- Reword comments that contained the token any; replace any types with typed shapes - stream.ts: passthrough tool-call flag via local boolean (state is null in passthrough) - Document T11 in zws_docs/ZWS_README_V8.md Made-with: Cursor
This commit is contained in:
@@ -1353,7 +1353,7 @@ export const REGISTRY: Record<string, RegistryEntry> = {
|
||||
{ id: "qwen/qwen3-32b", name: "Qwen3 32B (Puter)" },
|
||||
{ id: "qwen/qwen3-coder", name: "Qwen3 Coder 480B (Puter)" },
|
||||
],
|
||||
passthroughModels: true, // 500+ models available — users can type any Puter model ID
|
||||
passthroughModels: true, // 500+ models available — users can type arbitrary Puter model IDs
|
||||
},
|
||||
|
||||
"cloudflare-ai": {
|
||||
|
||||
@@ -1081,6 +1081,21 @@ async function handleComfyUIImageGeneration({ model, provider, providerConfig, b
|
||||
}
|
||||
}
|
||||
|
||||
type Imagen3ImageGenArgs = {
|
||||
model: string;
|
||||
provider: string;
|
||||
providerConfig: { baseUrl: string };
|
||||
body: { prompt?: string; size?: string; n?: number };
|
||||
credentials: { apiKey?: string; accessToken?: string };
|
||||
log?: { info?: (tag: string, msg: string) => void; error?: (tag: string, msg: string) => void } | null;
|
||||
};
|
||||
|
||||
type Imagen3NormalizedImage = {
|
||||
b64_json?: unknown;
|
||||
url?: unknown;
|
||||
revised_prompt?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handle Imagen 3 image generation
|
||||
*/
|
||||
@@ -1091,7 +1106,7 @@ async function handleImagen3ImageGeneration({
|
||||
body,
|
||||
credentials,
|
||||
log,
|
||||
}: any) {
|
||||
}: Imagen3ImageGenArgs) {
|
||||
const startTime = Date.now();
|
||||
const token = credentials.apiKey || credentials.accessToken;
|
||||
const aspectRatio = mapImageSize(body.size);
|
||||
@@ -1142,11 +1157,11 @@ async function handleImagen3ImageGeneration({
|
||||
const data = await response.json();
|
||||
|
||||
// Normalize response to OpenAI format
|
||||
const images: any[] = [];
|
||||
const images: Imagen3NormalizedImage[] = [];
|
||||
if (Array.isArray(data.images)) {
|
||||
images.push(
|
||||
...data.images.map((img: any) => ({
|
||||
b64_json: img.image || img.b64_json || img.url || img,
|
||||
...data.images.map((img: Record<string, unknown>) => ({
|
||||
b64_json: img.image ?? img.b64_json ?? img.url ?? img,
|
||||
revised_prompt: body.prompt,
|
||||
}))
|
||||
);
|
||||
@@ -1174,8 +1189,9 @@ async function handleImagen3ImageGeneration({
|
||||
success: true,
|
||||
data: { created: data.created || Math.floor(Date.now() / 1000), data: images },
|
||||
};
|
||||
} catch (err: any) {
|
||||
if (log) log.error("IMAGE", `${provider} fetch error: ${err.message}`);
|
||||
} catch (err: unknown) {
|
||||
const errMsg = err instanceof Error ? err.message : String(err);
|
||||
if (log) log.error("IMAGE", `${provider} fetch error: ${errMsg}`);
|
||||
|
||||
saveCallLog({
|
||||
method: "POST",
|
||||
@@ -1184,9 +1200,9 @@ async function handleImagen3ImageGeneration({
|
||||
model: `${provider}/${model}`,
|
||||
provider,
|
||||
duration: Date.now() - startTime,
|
||||
error: err.message,
|
||||
error: errMsg,
|
||||
}).catch(() => {});
|
||||
|
||||
return { success: false, status: 502, error: `Image provider error: ${err.message}` };
|
||||
return { success: false, status: 502, error: `Image provider error: ${errMsg}` };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,12 +23,19 @@ export function parseSSEToOpenAIResponse(rawSSE, fallbackModel) {
|
||||
const first = chunks[0];
|
||||
const contentParts = [];
|
||||
const reasoningParts = [];
|
||||
const accumulatedToolCalls = new Map<string, any>();
|
||||
type AccumulatedToolCall = {
|
||||
id: string | null;
|
||||
index: number;
|
||||
type: string;
|
||||
function: { name: string; arguments: string };
|
||||
};
|
||||
|
||||
const accumulatedToolCalls = new Map<string, AccumulatedToolCall>();
|
||||
let unknownToolCallSeq = 0;
|
||||
let finishReason = "stop";
|
||||
let usage = null;
|
||||
|
||||
const getToolCallKey = (toolCall: any) => {
|
||||
const getToolCallKey = (toolCall: Record<string, unknown>) => {
|
||||
if (toolCall?.id) return `id:${toolCall.id}`;
|
||||
if (Number.isInteger(toolCall?.index)) return `idx:${toolCall.index}`;
|
||||
unknownToolCallSeq += 1;
|
||||
|
||||
@@ -29,7 +29,7 @@ type ClaudeTool = {
|
||||
|
||||
/**
|
||||
* T02: Recursively strips empty text blocks from content arrays.
|
||||
* Anthropic returns 400 "text content blocks must be non-empty" if any
|
||||
* Anthropic returns 400 "text content blocks must be non-empty" when a
|
||||
* text block has text: "". Must also recurse into nested tool_result.content.
|
||||
* Ref: sub2api PR #1212
|
||||
*/
|
||||
|
||||
@@ -57,6 +57,8 @@ type TranslateState = ReturnType<typeof initState> & {
|
||||
accumulatedContent?: string;
|
||||
};
|
||||
|
||||
type UsageTokenRecord = Record<string, number>;
|
||||
|
||||
function getOpenAIIntermediateChunks(value: unknown): unknown[] {
|
||||
if (!value || typeof value !== "object") return [];
|
||||
const candidate = (value as JsonRecord)._openaiIntermediate;
|
||||
@@ -108,7 +110,9 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
} = options;
|
||||
|
||||
let buffer = "";
|
||||
let usage = null;
|
||||
let usage: UsageTokenRecord | null = null;
|
||||
/** Passthrough (OpenAI CC shape): saw tool_calls in stream before finish_reason */
|
||||
let passthroughHasToolCalls = false;
|
||||
|
||||
// State for translate mode (accumulatedContent for call log response body)
|
||||
const state: TranslateState | null =
|
||||
@@ -223,9 +227,9 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
if (extracted) {
|
||||
// Non-destructive merge: never overwrite a positive value with 0
|
||||
// message_start carries input_tokens, message_delta carries output_tokens;
|
||||
if (!usage) usage = {} as any;
|
||||
const u = usage as Record<string, number>;
|
||||
const eu = extracted as Record<string, number>;
|
||||
if (!usage) usage = {};
|
||||
const u = usage;
|
||||
const eu = extracted as UsageTokenRecord;
|
||||
if (eu.prompt_tokens > 0) u.prompt_tokens = eu.prompt_tokens;
|
||||
if (eu.completion_tokens > 0) u.completion_tokens = eu.completion_tokens;
|
||||
if (eu.total_tokens > 0) u.total_tokens = eu.total_tokens;
|
||||
@@ -266,7 +270,7 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
|
||||
// T18: Track if we saw tool calls
|
||||
if (delta?.tool_calls && delta.tool_calls.length > 0) {
|
||||
(state as any).passthroughHasToolCalls = true;
|
||||
passthroughHasToolCalls = true;
|
||||
}
|
||||
|
||||
const content = delta?.content || delta?.reasoning_content;
|
||||
@@ -288,7 +292,7 @@ export function createSSEStream(options: StreamOptions = {}) {
|
||||
// T18: Normalize finish_reason to 'tool_calls' if tool calls were used
|
||||
if (
|
||||
isFinishChunk &&
|
||||
(state as any).passthroughHasToolCalls &&
|
||||
passthroughHasToolCalls &&
|
||||
parsed.choices[0].finish_reason !== "tool_calls"
|
||||
) {
|
||||
parsed.choices[0].finish_reason = "tool_calls";
|
||||
|
||||
@@ -140,7 +140,7 @@ export function createDisconnectAwareStream(transformStream, streamController) {
|
||||
const errorMsg = error instanceof Error ? error.message : "Upstream stream error";
|
||||
const statusCode =
|
||||
typeof error === "object" && error !== null && "statusCode" in error
|
||||
? (error as any).statusCode
|
||||
? Number((error as { statusCode?: unknown }).statusCode) || 500
|
||||
: 500;
|
||||
|
||||
const errorEvent = {
|
||||
|
||||
@@ -229,3 +229,7 @@ Zod 4 下全键 `record` 与「只提交部分协议」的 PATCH 语义冲突,
|
||||
`src/lib/zed-oauth/keychain-reader.ts` 顶层 **`import keytar`** 会在 `next build` 收集路由数据时加载原生模块;无 `libsecret` 的 Linux runner 会失败。改为 **`await import("keytar")`** 动态加载,失败则 **跳过读钥匙串**(返回空列表 / null),构建不再依赖本机 keytar。
|
||||
|
||||
> 若本文随上游合并,可删除文首「仅供 ZWS 作者自用」一句;**ZWS** 为贡献者笔名,可保留作变更索引。
|
||||
|
||||
### T11:`check:any-budget:t11`
|
||||
|
||||
脚本 `scripts/check-t11-any-budget.mjs` 用正则统计文件中 **单词 `any`**(含注释里的英文 *any*)。失败原因通常是:注释误触(如 “type **any** model ID”)、或真实 `: any` / `as any`。处理方式:改写注释用词、用 `unknown` / `Record<string, unknown>` / 显式接口替代 `any`。`open-sse/utils/stream.ts` 中 passthrough 分支原先对 `state`(在 passthrough 模式下为 `null`)做 `(state as any).passthroughHasToolCalls`,已改为闭包内独立布尔变量 `passthroughHasToolCalls`。
|
||||
|
||||
Reference in New Issue
Block a user