ai-lab-bridge 0.1.4 → 0.1.5
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/index.js +60 -18
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -267,11 +267,26 @@ class Session {
|
|
|
267
267
|
}
|
|
268
268
|
}
|
|
269
269
|
|
|
270
|
+
/** 归一化后缓存 + 回传 usage:各家 CLI 字段名不同(input_tokens/input/prompt_tokens...),统一为平台口径 */
|
|
270
271
|
emitUsage(usage) {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
272
|
+
const num = (v) => (typeof v === "number" && isFinite(v) ? v : undefined);
|
|
273
|
+
const raw = usage && typeof usage === "object" ? usage : null;
|
|
274
|
+
if (!raw) return;
|
|
275
|
+
// opencode 系: {input, output, cache:{read}} / claude: {input_tokens, output_tokens, cache_read_input_tokens}
|
|
276
|
+
// codex: {input_tokens, output_tokens, total_tokens} / gemini: {inputTokenCount, outputTokenCount, ...}
|
|
277
|
+
const input = num(raw.input_tokens) ?? num(raw.input) ?? num(raw.prompt_tokens) ?? num(raw.inputTokenCount);
|
|
278
|
+
const output =
|
|
279
|
+
num(raw.output_tokens) ?? num(raw.output) ?? num(raw.completion_tokens) ?? num(raw.outputTokenCount) ?? num(raw.candidatesTokenCount);
|
|
280
|
+
const cacheRead = num(raw.cache_read_input_tokens) ?? num(raw.cache && raw.cache.read) ?? num(raw.cachedContentTokenCount);
|
|
281
|
+
if (input === undefined && output === undefined && cacheRead === undefined) return;
|
|
282
|
+
const normalized = {
|
|
283
|
+
input_tokens: input ?? null,
|
|
284
|
+
output_tokens: output ?? null,
|
|
285
|
+
total_tokens: input !== undefined || output !== undefined ? (input ?? 0) + (output ?? 0) : null,
|
|
286
|
+
cache_read_input_tokens: cacheRead ?? null,
|
|
287
|
+
};
|
|
288
|
+
this.usage = normalized;
|
|
289
|
+
this.emitEvent("usage", { usage: normalized });
|
|
275
290
|
}
|
|
276
291
|
|
|
277
292
|
emitToolUse(summary) {
|
|
@@ -472,6 +487,9 @@ function handleOpencodeLine(session, obj) {
|
|
|
472
487
|
session.emitToolUse(part.tool || part.name || (state && state.title));
|
|
473
488
|
}
|
|
474
489
|
}
|
|
490
|
+
// usage 提取:opencode 系在 step_finish 的 part.tokens / 顶层 usage 携带本轮 token 统计
|
|
491
|
+
const usageRaw = (part && (part.tokens || part.usage)) || obj.tokens || obj.usage;
|
|
492
|
+
if (usageRaw && typeof usageRaw === "object") session.emitUsage(usageRaw);
|
|
475
493
|
if (obj.type === "error" || (obj.error && typeof obj.error === "object")) {
|
|
476
494
|
const err = obj.error && typeof obj.error === "object" ? obj.error : null;
|
|
477
495
|
// 提取可读错误消息(opencode 系: error.data.message / error.message;附带 name/ref 便于定位)
|
|
@@ -551,8 +569,10 @@ function handleCursorLine(session, obj) {
|
|
|
551
569
|
|
|
552
570
|
/**
|
|
553
571
|
* Gemini CLI 家族事件解析(gemini / qwen / iflow 的 `-p --output-format stream-json`,JSONL)。
|
|
554
|
-
*
|
|
555
|
-
*
|
|
572
|
+
* 事件格式两代并存,宽松归一化:
|
|
573
|
+
* - Gemini 原生:message(text/content) / tool_use / result(response, stats)
|
|
574
|
+
* - Claude 风格(qwen 0.24+ 同源仿制):assistant(message.content[]) / tool_call / result(result, usage)
|
|
575
|
+
* 终态以进程退出码兜底。
|
|
556
576
|
*/
|
|
557
577
|
function handleGeminiLine(session, obj) {
|
|
558
578
|
if (!obj || typeof obj !== "object") return;
|
|
@@ -562,28 +582,50 @@ function handleGeminiLine(session, obj) {
|
|
|
562
582
|
if (obj.type === "message" || obj.type === "assistant") {
|
|
563
583
|
const role = obj.role || (obj.message && obj.message.role);
|
|
564
584
|
if (role === "user") return;
|
|
585
|
+
// 文本提取顺序:obj.text → obj.content → obj.message.content[](Claude 风格嵌套)
|
|
565
586
|
let text = obj.text;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
587
|
+
let blocks = obj.content;
|
|
588
|
+
if (!blocks && obj.message && Array.isArray(obj.message.content)) {
|
|
589
|
+
blocks = obj.message.content;
|
|
590
|
+
}
|
|
591
|
+
if (!text && blocks) {
|
|
592
|
+
if (typeof blocks === "string") text = blocks;
|
|
593
|
+
else if (Array.isArray(blocks)) {
|
|
594
|
+
const parts = [];
|
|
595
|
+
for (const b of blocks) {
|
|
596
|
+
if (!b) continue;
|
|
597
|
+
if (b.type === "text" && b.text) {
|
|
598
|
+
session.endThink();
|
|
599
|
+
parts.push(b.text);
|
|
600
|
+
} else if (b.type === "thinking" && b.thinking) {
|
|
601
|
+
session.emitThink(b.thinking);
|
|
602
|
+
} else if (typeof b === "string") {
|
|
603
|
+
parts.push(b);
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
text = parts.join("");
|
|
573
607
|
}
|
|
574
608
|
}
|
|
575
609
|
if (text) session.emitText(text);
|
|
576
610
|
} else if (obj.type === "tool_use" || obj.type === "tool_call") {
|
|
577
611
|
session.emitToolUse(obj.name || obj.tool_name || obj.tool || "tool");
|
|
578
612
|
} else if (obj.type === "result") {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
613
|
+
// usage:gemini 原生在 stats,qwen/claude 风格在 usage
|
|
614
|
+
session.emitUsage(obj.stats || obj.usage);
|
|
615
|
+
const failed = obj.error || obj.is_error || (obj.subtype && obj.subtype !== "success");
|
|
616
|
+
if (failed) {
|
|
617
|
+
const msg = typeof obj.error === "string"
|
|
618
|
+
? obj.error
|
|
619
|
+
: (obj.error && obj.error.message)
|
|
620
|
+
|| (typeof obj.result === "string" ? obj.result : undefined)
|
|
621
|
+
|| `执行异常: ${obj.subtype || "unknown"}`;
|
|
582
622
|
session.finish("failed", msg);
|
|
583
623
|
return;
|
|
584
624
|
}
|
|
585
|
-
|
|
586
|
-
|
|
625
|
+
// 终态文本兜底:gemini 原生在 response,qwen/claude 风格在 result
|
|
626
|
+
const finalText = (typeof obj.response === "string" && obj.response) || (typeof obj.result === "string" && obj.result);
|
|
627
|
+
if (finalText && !session.textParts.length) {
|
|
628
|
+
session.emitText(finalText);
|
|
587
629
|
}
|
|
588
630
|
session.finish("completed");
|
|
589
631
|
}
|