@acosmi/sdk-ts 2.18.0 → 2.19.1
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/CHANGELOG.md +28 -0
- package/README.md +30 -2
- package/dist/browser/index.mjs +1228 -171
- package/dist/browser/index.mjs.map +1 -1
- package/dist/index.mjs +1228 -171
- package/dist/index.mjs.map +1 -1
- package/dist/node/adapters/anthropic.cjs.map +1 -1
- package/dist/node/adapters/anthropic.mjs.map +1 -1
- package/dist/node/adapters/openai.cjs +110 -45
- package/dist/node/adapters/openai.cjs.map +1 -1
- package/dist/node/adapters/openai.d.cts +1 -1
- package/dist/node/adapters/openai.d.ts +1 -1
- package/dist/node/adapters/openai.mjs +110 -45
- package/dist/node/adapters/openai.mjs.map +1 -1
- package/dist/node/index.cjs +1230 -170
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.d.cts +376 -153
- package/dist/node/index.d.ts +376 -153
- package/dist/node/index.mjs +1228 -171
- package/dist/node/index.mjs.map +1 -1
- package/dist/node/{openai-Cyvi8g6B.d.ts → openai-BbCQOMNY.d.ts} +28 -1
- package/dist/node/{openai-DR_KhEdM.d.cts → openai-CMrvATF_.d.cts} +28 -1
- package/package.json +1 -1
|
@@ -306,16 +306,90 @@ var OpenAIStreamConverter = class {
|
|
|
306
306
|
* 可能已被 text/tool 推进的 this.blockIndex (否则 content_block_stop 索引错配)。 */
|
|
307
307
|
thinkingBlockIndex = 0;
|
|
308
308
|
textStarted = false;
|
|
309
|
-
/** OpenAI tool_call
|
|
309
|
+
/** OpenAI tool_call 键 → Anthropic block index。键正常是 `tc.index`;上游省略
|
|
310
|
+
* index 时退化为 `id:<tool_call_id>`,两者都没有时沿用上一个键(见
|
|
311
|
+
* {@link resolveToolKey})。 */
|
|
310
312
|
toolBlockIndex = /* @__PURE__ */ new Map();
|
|
311
313
|
blockIndex = 0;
|
|
314
|
+
/** 每个 tool block 已发出的 `partial_json` 累积,用于识别「每片重发全量参数」
|
|
315
|
+
* 的上游(见 tool_calls 分支的累计判别)。 */
|
|
316
|
+
toolArgsAccum = /* @__PURE__ */ new Map();
|
|
317
|
+
/** 上一次解析出的 tool 键,供缺 index 且缺 id 的后续增量沿用。 */
|
|
318
|
+
lastToolKey = null;
|
|
319
|
+
/** 已发出 message_delta/message_stop,避免 finish_reason 与 `[DONE]` 各收一次。 */
|
|
320
|
+
messageClosed = false;
|
|
321
|
+
/**
|
|
322
|
+
* 解析一条 tool_call delta 归属的块键。
|
|
323
|
+
*
|
|
324
|
+
* OpenAI 流式规范里 `index` 是必填,但兼容实现常有省略。此前这里直接用
|
|
325
|
+
* `tc.index` 做 Map 键:两个都省略 index 的 tool_call 会共用键 `undefined`,
|
|
326
|
+
* 于是只开一个块、两段参数拼进同一条 `partial_json` 流,产出 `{…}{…}` 这种
|
|
327
|
+
* 必然非法的 JSON。这里按「index → id → 沿用上一个」三级降级,让至少一种
|
|
328
|
+
* 稳定标识生效。
|
|
329
|
+
*/
|
|
330
|
+
resolveToolKey(tc) {
|
|
331
|
+
if (typeof tc.index === "number" && Number.isFinite(tc.index)) {
|
|
332
|
+
this.lastToolKey = tc.index;
|
|
333
|
+
return tc.index;
|
|
334
|
+
}
|
|
335
|
+
if (typeof tc.id === "string" && tc.id !== "") {
|
|
336
|
+
const key = `id:${tc.id}`;
|
|
337
|
+
this.lastToolKey = key;
|
|
338
|
+
return key;
|
|
339
|
+
}
|
|
340
|
+
if (this.lastToolKey !== null) return this.lastToolKey;
|
|
341
|
+
this.lastToolKey = 0;
|
|
342
|
+
return 0;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* 关闭仍打开的 text / thinking / tool 块,并收口 message。
|
|
346
|
+
*
|
|
347
|
+
* 由 `finish_reason` 分支与 `[DONE]` 分支共用:上游断流或只发 `[DONE]` 而不发
|
|
348
|
+
* `finish_reason` 时,此前一个 `content_block_stop` 都不会发,下游拿到的是一个
|
|
349
|
+
* 永不闭合的 tool_use 块。
|
|
350
|
+
*/
|
|
351
|
+
closeOpenBlocks(events, stopReason) {
|
|
352
|
+
if (this.messageClosed) return;
|
|
353
|
+
this.messageClosed = true;
|
|
354
|
+
if (this.textStarted) {
|
|
355
|
+
events.push({
|
|
356
|
+
event: "content_block_stop",
|
|
357
|
+
data: JSON.stringify({ type: "content_block_stop", index: this.blockIndex })
|
|
358
|
+
});
|
|
359
|
+
this.textStarted = false;
|
|
360
|
+
} else if (this.thinkingStarted && !this.thinkingStopped) {
|
|
361
|
+
this.thinkingStopped = true;
|
|
362
|
+
events.push({
|
|
363
|
+
event: "content_block_stop",
|
|
364
|
+
data: JSON.stringify({ type: "content_block_stop", index: this.thinkingBlockIndex })
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
for (const idx of this.toolBlockIndex.values()) {
|
|
368
|
+
events.push({
|
|
369
|
+
event: "content_block_stop",
|
|
370
|
+
data: JSON.stringify({ type: "content_block_stop", index: idx })
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
events.push({
|
|
374
|
+
event: "message_delta",
|
|
375
|
+
data: JSON.stringify({ type: "message_delta", delta: { stop_reason: stopReason } })
|
|
376
|
+
});
|
|
377
|
+
events.push({
|
|
378
|
+
event: "message_stop",
|
|
379
|
+
data: JSON.stringify({ type: "message_stop" })
|
|
380
|
+
});
|
|
381
|
+
}
|
|
312
382
|
/**
|
|
313
383
|
* 将一行 OpenAI SSE data 转换为零或多个 Anthropic 格式 StreamEvent
|
|
314
384
|
* 返回 { events, done }
|
|
315
385
|
*/
|
|
316
386
|
convert(data) {
|
|
317
387
|
if (data === "[DONE]") {
|
|
318
|
-
|
|
388
|
+
const events2 = [];
|
|
389
|
+
if (this.messageStarted) {
|
|
390
|
+
this.closeOpenBlocks(events2, "end_turn");
|
|
391
|
+
}
|
|
392
|
+
return { events: events2, done: true };
|
|
319
393
|
}
|
|
320
394
|
let chunk;
|
|
321
395
|
try {
|
|
@@ -387,7 +461,9 @@ var OpenAIStreamConverter = class {
|
|
|
387
461
|
events.push({ event: "content_block_delta", data: deltaJSON });
|
|
388
462
|
}
|
|
389
463
|
for (const tc of choice.delta.tool_calls ?? []) {
|
|
390
|
-
|
|
464
|
+
const fn = tc.function;
|
|
465
|
+
const toolKey = this.resolveToolKey(tc);
|
|
466
|
+
if (!this.toolBlockIndex.has(toolKey)) {
|
|
391
467
|
if (this.thinkingStarted && !this.thinkingStopped) {
|
|
392
468
|
this.thinkingStopped = true;
|
|
393
469
|
const stopJSON = JSON.stringify({
|
|
@@ -406,56 +482,46 @@ var OpenAIStreamConverter = class {
|
|
|
406
482
|
this.blockIndex++;
|
|
407
483
|
this.textStarted = false;
|
|
408
484
|
}
|
|
409
|
-
this.toolBlockIndex.set(
|
|
485
|
+
this.toolBlockIndex.set(toolKey, this.blockIndex);
|
|
410
486
|
const blockJSON = JSON.stringify({
|
|
411
487
|
type: "content_block_start",
|
|
412
488
|
index: this.blockIndex,
|
|
413
489
|
content_block: {
|
|
414
490
|
type: "tool_use",
|
|
415
491
|
id: tc.id,
|
|
416
|
-
name:
|
|
492
|
+
name: fn?.name,
|
|
417
493
|
input: {}
|
|
418
494
|
}
|
|
419
495
|
});
|
|
420
496
|
events.push({ event: "content_block_start", data: blockJSON });
|
|
421
497
|
this.blockIndex++;
|
|
422
498
|
}
|
|
423
|
-
if (
|
|
424
|
-
const
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
}
|
|
433
|
-
|
|
499
|
+
if (fn?.arguments !== void 0 && fn.arguments !== null && fn.arguments !== "") {
|
|
500
|
+
const rawArgs = typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments);
|
|
501
|
+
const accum = this.toolArgsAccum.get(toolKey) ?? "";
|
|
502
|
+
let emit = rawArgs;
|
|
503
|
+
if (accum !== "" && rawArgs.length > accum.length && rawArgs.startsWith(accum)) {
|
|
504
|
+
emit = rawArgs.slice(accum.length);
|
|
505
|
+
this.toolArgsAccum.set(toolKey, rawArgs);
|
|
506
|
+
} else {
|
|
507
|
+
this.toolArgsAccum.set(toolKey, accum + rawArgs);
|
|
508
|
+
}
|
|
509
|
+
if (emit !== "") {
|
|
510
|
+
const idx = this.toolBlockIndex.get(toolKey);
|
|
511
|
+
const deltaJSON = JSON.stringify({
|
|
512
|
+
type: "content_block_delta",
|
|
513
|
+
index: idx,
|
|
514
|
+
delta: {
|
|
515
|
+
type: "input_json_delta",
|
|
516
|
+
partial_json: emit
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
events.push({ event: "content_block_delta", data: deltaJSON });
|
|
520
|
+
}
|
|
434
521
|
}
|
|
435
522
|
}
|
|
436
523
|
if (choice.finish_reason != null && choice.finish_reason !== "") {
|
|
437
|
-
|
|
438
|
-
const stopJSON2 = JSON.stringify({
|
|
439
|
-
type: "content_block_stop",
|
|
440
|
-
index: this.blockIndex
|
|
441
|
-
});
|
|
442
|
-
events.push({ event: "content_block_stop", data: stopJSON2 });
|
|
443
|
-
} else if (this.thinkingStarted && !this.thinkingStopped) {
|
|
444
|
-
this.thinkingStopped = true;
|
|
445
|
-
const stopJSON2 = JSON.stringify({
|
|
446
|
-
type: "content_block_stop",
|
|
447
|
-
index: this.thinkingBlockIndex
|
|
448
|
-
});
|
|
449
|
-
events.push({ event: "content_block_stop", data: stopJSON2 });
|
|
450
|
-
}
|
|
451
|
-
for (const idx of this.toolBlockIndex.values()) {
|
|
452
|
-
const stopJSON2 = JSON.stringify({
|
|
453
|
-
type: "content_block_stop",
|
|
454
|
-
index: idx
|
|
455
|
-
});
|
|
456
|
-
events.push({ event: "content_block_stop", data: stopJSON2 });
|
|
457
|
-
}
|
|
458
|
-
let stopReason = "end_turn";
|
|
524
|
+
let stopReason;
|
|
459
525
|
switch (choice.finish_reason) {
|
|
460
526
|
case "tool_calls":
|
|
461
527
|
stopReason = "tool_use";
|
|
@@ -463,14 +529,13 @@ var OpenAIStreamConverter = class {
|
|
|
463
529
|
case "length":
|
|
464
530
|
stopReason = "max_tokens";
|
|
465
531
|
break;
|
|
532
|
+
case "stop":
|
|
533
|
+
stopReason = "end_turn";
|
|
534
|
+
break;
|
|
535
|
+
default:
|
|
536
|
+
stopReason = choice.finish_reason;
|
|
466
537
|
}
|
|
467
|
-
|
|
468
|
-
type: "message_delta",
|
|
469
|
-
delta: { stop_reason: stopReason }
|
|
470
|
-
});
|
|
471
|
-
events.push({ event: "message_delta", data: deltaJSON });
|
|
472
|
-
const stopJSON = JSON.stringify({ type: "message_stop" });
|
|
473
|
-
events.push({ event: "message_stop", data: stopJSON });
|
|
538
|
+
this.closeOpenBlocks(events, stopReason);
|
|
474
539
|
}
|
|
475
540
|
return { events, done: false };
|
|
476
541
|
}
|