@acosmi/sdk-ts 2.19.0 → 2.19.2

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.
@@ -46,10 +46,13 @@ var OpenAIAdapter = class {
46
46
  if (req.tools != null) {
47
47
  body["tools"] = req.tools;
48
48
  }
49
- const eff = resolveOpenAIReasoningEffort(req);
49
+ const eff = resolveOpenAIReasoningEffort(req, _caps.supports_max_effort);
50
50
  if (eff !== "") {
51
51
  body["reasoning_effort"] = eff;
52
52
  }
53
+ if (_caps.supports_thinking && req.thinking?.level === ThinkingOff) {
54
+ body["thinking"] = { type: "disabled" };
55
+ }
53
56
  if (req.speed && req.speed !== "") {
54
57
  body["speed"] = req.speed;
55
58
  }
@@ -118,23 +121,28 @@ var OpenAIAdapter = class {
118
121
  return { event: { event: eventType, data }, done: false };
119
122
  }
120
123
  };
121
- function resolveOpenAIReasoningEffort(req) {
124
+ function resolveOpenAIReasoningEffort(req, supportsMax = false) {
122
125
  if (req.effort && req.effort.level !== "") {
123
126
  switch (req.effort.level) {
124
127
  case "low":
125
128
  case "medium":
126
129
  case "high":
130
+ case "xhigh":
127
131
  return req.effort.level;
128
132
  case "max":
129
- return "high";
133
+ return supportsMax ? "max" : "high";
130
134
  }
131
135
  }
132
136
  if (req.thinking) {
133
137
  switch (req.thinking.level) {
138
+ case "low":
139
+ case "medium":
140
+ case "xhigh":
141
+ return req.thinking.level;
134
142
  case ThinkingHigh:
135
143
  return "high";
136
144
  case ThinkingMax:
137
- return "high";
145
+ return supportsMax ? "max" : "high";
138
146
  case ThinkingOff:
139
147
  return "";
140
148
  }
@@ -306,16 +314,90 @@ var OpenAIStreamConverter = class {
306
314
  * 可能已被 text/tool 推进的 this.blockIndex (否则 content_block_stop 索引错配)。 */
307
315
  thinkingBlockIndex = 0;
308
316
  textStarted = false;
309
- /** OpenAI tool_call index → Anthropic block index */
317
+ /** OpenAI tool_call → Anthropic block index。键正常是 `tc.index`;上游省略
318
+ * index 时退化为 `id:<tool_call_id>`,两者都没有时沿用上一个键(见
319
+ * {@link resolveToolKey})。 */
310
320
  toolBlockIndex = /* @__PURE__ */ new Map();
311
321
  blockIndex = 0;
322
+ /** 每个 tool block 已发出的 `partial_json` 累积,用于识别「每片重发全量参数」
323
+ * 的上游(见 tool_calls 分支的累计判别)。 */
324
+ toolArgsAccum = /* @__PURE__ */ new Map();
325
+ /** 上一次解析出的 tool 键,供缺 index 且缺 id 的后续增量沿用。 */
326
+ lastToolKey = null;
327
+ /** 已发出 message_delta/message_stop,避免 finish_reason 与 `[DONE]` 各收一次。 */
328
+ messageClosed = false;
329
+ /**
330
+ * 解析一条 tool_call delta 归属的块键。
331
+ *
332
+ * OpenAI 流式规范里 `index` 是必填,但兼容实现常有省略。此前这里直接用
333
+ * `tc.index` 做 Map 键:两个都省略 index 的 tool_call 会共用键 `undefined`,
334
+ * 于是只开一个块、两段参数拼进同一条 `partial_json` 流,产出 `{…}{…}` 这种
335
+ * 必然非法的 JSON。这里按「index → id → 沿用上一个」三级降级,让至少一种
336
+ * 稳定标识生效。
337
+ */
338
+ resolveToolKey(tc) {
339
+ if (typeof tc.index === "number" && Number.isFinite(tc.index)) {
340
+ this.lastToolKey = tc.index;
341
+ return tc.index;
342
+ }
343
+ if (typeof tc.id === "string" && tc.id !== "") {
344
+ const key = `id:${tc.id}`;
345
+ this.lastToolKey = key;
346
+ return key;
347
+ }
348
+ if (this.lastToolKey !== null) return this.lastToolKey;
349
+ this.lastToolKey = 0;
350
+ return 0;
351
+ }
352
+ /**
353
+ * 关闭仍打开的 text / thinking / tool 块,并收口 message。
354
+ *
355
+ * 由 `finish_reason` 分支与 `[DONE]` 分支共用:上游断流或只发 `[DONE]` 而不发
356
+ * `finish_reason` 时,此前一个 `content_block_stop` 都不会发,下游拿到的是一个
357
+ * 永不闭合的 tool_use 块。
358
+ */
359
+ closeOpenBlocks(events, stopReason) {
360
+ if (this.messageClosed) return;
361
+ this.messageClosed = true;
362
+ if (this.textStarted) {
363
+ events.push({
364
+ event: "content_block_stop",
365
+ data: JSON.stringify({ type: "content_block_stop", index: this.blockIndex })
366
+ });
367
+ this.textStarted = false;
368
+ } else if (this.thinkingStarted && !this.thinkingStopped) {
369
+ this.thinkingStopped = true;
370
+ events.push({
371
+ event: "content_block_stop",
372
+ data: JSON.stringify({ type: "content_block_stop", index: this.thinkingBlockIndex })
373
+ });
374
+ }
375
+ for (const idx of this.toolBlockIndex.values()) {
376
+ events.push({
377
+ event: "content_block_stop",
378
+ data: JSON.stringify({ type: "content_block_stop", index: idx })
379
+ });
380
+ }
381
+ events.push({
382
+ event: "message_delta",
383
+ data: JSON.stringify({ type: "message_delta", delta: { stop_reason: stopReason } })
384
+ });
385
+ events.push({
386
+ event: "message_stop",
387
+ data: JSON.stringify({ type: "message_stop" })
388
+ });
389
+ }
312
390
  /**
313
391
  * 将一行 OpenAI SSE data 转换为零或多个 Anthropic 格式 StreamEvent
314
392
  * 返回 { events, done }
315
393
  */
316
394
  convert(data) {
317
395
  if (data === "[DONE]") {
318
- return { events: [], done: true };
396
+ const events2 = [];
397
+ if (this.messageStarted) {
398
+ this.closeOpenBlocks(events2, "end_turn");
399
+ }
400
+ return { events: events2, done: true };
319
401
  }
320
402
  let chunk;
321
403
  try {
@@ -387,7 +469,9 @@ var OpenAIStreamConverter = class {
387
469
  events.push({ event: "content_block_delta", data: deltaJSON });
388
470
  }
389
471
  for (const tc of choice.delta.tool_calls ?? []) {
390
- if (!this.toolBlockIndex.has(tc.index)) {
472
+ const fn = tc.function;
473
+ const toolKey = this.resolveToolKey(tc);
474
+ if (!this.toolBlockIndex.has(toolKey)) {
391
475
  if (this.thinkingStarted && !this.thinkingStopped) {
392
476
  this.thinkingStopped = true;
393
477
  const stopJSON = JSON.stringify({
@@ -406,56 +490,46 @@ var OpenAIStreamConverter = class {
406
490
  this.blockIndex++;
407
491
  this.textStarted = false;
408
492
  }
409
- this.toolBlockIndex.set(tc.index, this.blockIndex);
493
+ this.toolBlockIndex.set(toolKey, this.blockIndex);
410
494
  const blockJSON = JSON.stringify({
411
495
  type: "content_block_start",
412
496
  index: this.blockIndex,
413
497
  content_block: {
414
498
  type: "tool_use",
415
499
  id: tc.id,
416
- name: tc.function.name,
500
+ name: fn?.name,
417
501
  input: {}
418
502
  }
419
503
  });
420
504
  events.push({ event: "content_block_start", data: blockJSON });
421
505
  this.blockIndex++;
422
506
  }
423
- if (tc.function.arguments && tc.function.arguments !== "") {
424
- const idx = this.toolBlockIndex.get(tc.index);
425
- const deltaJSON = JSON.stringify({
426
- type: "content_block_delta",
427
- index: idx,
428
- delta: {
429
- type: "input_json_delta",
430
- partial_json: tc.function.arguments
431
- }
432
- });
433
- events.push({ event: "content_block_delta", data: deltaJSON });
507
+ if (fn?.arguments !== void 0 && fn.arguments !== null && fn.arguments !== "") {
508
+ const rawArgs = typeof fn.arguments === "string" ? fn.arguments : JSON.stringify(fn.arguments);
509
+ const accum = this.toolArgsAccum.get(toolKey) ?? "";
510
+ let emit = rawArgs;
511
+ if (accum !== "" && rawArgs.length > accum.length && rawArgs.startsWith(accum)) {
512
+ emit = rawArgs.slice(accum.length);
513
+ this.toolArgsAccum.set(toolKey, rawArgs);
514
+ } else {
515
+ this.toolArgsAccum.set(toolKey, accum + rawArgs);
516
+ }
517
+ if (emit !== "") {
518
+ const idx = this.toolBlockIndex.get(toolKey);
519
+ const deltaJSON = JSON.stringify({
520
+ type: "content_block_delta",
521
+ index: idx,
522
+ delta: {
523
+ type: "input_json_delta",
524
+ partial_json: emit
525
+ }
526
+ });
527
+ events.push({ event: "content_block_delta", data: deltaJSON });
528
+ }
434
529
  }
435
530
  }
436
531
  if (choice.finish_reason != null && choice.finish_reason !== "") {
437
- if (this.textStarted) {
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";
532
+ let stopReason;
459
533
  switch (choice.finish_reason) {
460
534
  case "tool_calls":
461
535
  stopReason = "tool_use";
@@ -463,14 +537,13 @@ var OpenAIStreamConverter = class {
463
537
  case "length":
464
538
  stopReason = "max_tokens";
465
539
  break;
540
+ case "stop":
541
+ stopReason = "end_turn";
542
+ break;
543
+ default:
544
+ stopReason = choice.finish_reason;
466
545
  }
467
- const deltaJSON = JSON.stringify({
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 });
546
+ this.closeOpenBlocks(events, stopReason);
474
547
  }
475
548
  return { events, done: false };
476
549
  }