@code-yeongyu/senpi-ai 2026.7.29 → 2026.7.30

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.
Files changed (26) hide show
  1. package/dist/api/openai-completions.d.ts.map +1 -1
  2. package/dist/api/openai-completions.js +121 -38
  3. package/dist/api/openai-completions.js.map +1 -1
  4. package/dist/providers/data/.manifest.json +1 -1
  5. package/dist/providers/data/openrouter.json +1 -1
  6. package/dist/tool-call-middleware/recovery-diagnostics.d.ts +2 -1
  7. package/dist/tool-call-middleware/recovery-diagnostics.d.ts.map +1 -1
  8. package/dist/tool-call-middleware/recovery-diagnostics.js +2 -2
  9. package/dist/tool-call-middleware/recovery-diagnostics.js.map +1 -1
  10. package/dist/tool-call-middleware/recovery-native-projection.d.ts +3 -2
  11. package/dist/tool-call-middleware/recovery-native-projection.d.ts.map +1 -1
  12. package/dist/tool-call-middleware/recovery-native-projection.js +3 -2
  13. package/dist/tool-call-middleware/recovery-native-projection.js.map +1 -1
  14. package/dist/tool-call-middleware/recovery-stream-failure.d.ts +6 -1
  15. package/dist/tool-call-middleware/recovery-stream-failure.d.ts.map +1 -1
  16. package/dist/tool-call-middleware/recovery-stream-failure.js +3 -2
  17. package/dist/tool-call-middleware/recovery-stream-failure.js.map +1 -1
  18. package/dist/tool-call-middleware/recovery-stream-wrapper.d.ts +6 -1
  19. package/dist/tool-call-middleware/recovery-stream-wrapper.d.ts.map +1 -1
  20. package/dist/tool-call-middleware/recovery-stream-wrapper.js +11 -5
  21. package/dist/tool-call-middleware/recovery-stream-wrapper.js.map +1 -1
  22. package/dist/tool-call-middleware/recovery-text-projection.d.ts +6 -1
  23. package/dist/tool-call-middleware/recovery-text-projection.d.ts.map +1 -1
  24. package/dist/tool-call-middleware/recovery-text-projection.js +6 -4
  25. package/dist/tool-call-middleware/recovery-text-projection.js.map +1 -1
  26. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"openai-completions.d.ts","sourceRoot":"","sources":["../../src/api/openai-completions.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,KAAK,EAOX,0BAA0B,EAI1B,MAAM,sCAAsC,CAAC;AAG9C,OAAO,KAAK,EAIX,OAAO,EAGP,KAAK,EAIL,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAMrB,OAAO,EAEN,KAAK,+BAA+B,EACpC,MAAM,8BAA8B,CAAC;AA4BtC,OAAO,EACN,0BAA0B,IAAI,SAAS,EACvC,KAAK,+BAA+B,GACpC,MAAM,8BAA8B,CAAC;AA4MtC,MAAM,WAAW,wBAAyB,SAAQ,aAAa;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,8BAA8B,CAAC;IACpE,eAAe,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;CAC1E;AAED,MAAM,WAAW,iCAAiC;IACjD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0BAA0B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzD;AAwCD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,wBAAwB,CAgbjF,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAyBlF,CAAC;AA8XF,wBAAgB,eAAe,CAC9B,KAAK,EAAE,KAAK,CAAC,oBAAoB,CAAC,EAClC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,+BAA+B,EACvC,OAAO,GAAE,iCAAsC,GAC7C,0BAA0B,EAAE,CAgS9B"}
1
+ {"version":3,"file":"openai-completions.d.ts","sourceRoot":"","sources":["../../src/api/openai-completions.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,KAAK,EAOX,0BAA0B,EAI1B,MAAM,sCAAsC,CAAC;AAG9C,OAAO,KAAK,EAIX,OAAO,EAGP,KAAK,EAIL,mBAAmB,EAEnB,cAAc,EACd,aAAa,EAMb,MAAM,aAAa,CAAC;AAMrB,OAAO,EAEN,KAAK,+BAA+B,EACpC,MAAM,8BAA8B,CAAC;AA4BtC,OAAO,EACN,0BAA0B,IAAI,SAAS,EACvC,KAAK,+BAA+B,GACpC,MAAM,8BAA8B,CAAC;AA4MtC,MAAM,WAAW,wBAAyB,SAAQ,aAAa;IAC9D,UAAU,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,8BAA8B,CAAC;IACpE,eAAe,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC;CAC1E;AAED,MAAM,WAAW,iCAAiC;IACjD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0BAA0B,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACzD;AAwCD,eAAO,MAAM,MAAM,EAAE,cAAc,CAAC,oBAAoB,EAAE,wBAAwB,CA0fjF,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,cAAc,CAAC,oBAAoB,EAAE,mBAAmB,CAyBlF,CAAC;AA8XF,wBAAgB,eAAe,CAC9B,KAAK,EAAE,KAAK,CAAC,oBAAoB,CAAC,EAClC,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,+BAA+B,EACvC,OAAO,GAAE,iCAAsC,GAC7C,0BAA0B,EAAE,CAgS9B"}
@@ -240,7 +240,12 @@ export const stream = (model, context, options) => {
240
240
  stream.push({ type: "start", partial: output });
241
241
  let textBlock = null;
242
242
  let thinkingBlock = null;
243
+ let activeBlock = null;
244
+ let deferMixedEvents = false;
243
245
  let hasFinishReason = false;
246
+ const deferredTextDeltas = [];
247
+ const deferredThinkingDeltas = [];
248
+ const deferredToolCallDeltas = new Map();
244
249
  const toolCallBlocksByIndex = new Map();
245
250
  const toolCallBlocksById = new Map();
246
251
  const pendingReasoningDetailsByToolCallId = new Map();
@@ -310,23 +315,47 @@ export const stream = (model, context, options) => {
310
315
  });
311
316
  }
312
317
  };
318
+ const finishActiveBlock = () => {
319
+ if (!activeBlock)
320
+ return;
321
+ finishBlock(activeBlock);
322
+ activeBlock = null;
323
+ };
313
324
  const ensureTextBlock = () => {
314
325
  if (!textBlock) {
326
+ if (!deferMixedEvents) {
327
+ finishActiveBlock();
328
+ thinkingBlock = null;
329
+ }
315
330
  textBlock = { type: "text", text: "" };
316
331
  blocks.push(textBlock);
317
- stream.push({ type: "text_start", contentIndex: getContentIndex(textBlock), partial: output });
332
+ if (!deferMixedEvents) {
333
+ activeBlock = textBlock;
334
+ stream.push({ type: "text_start", contentIndex: getContentIndex(textBlock), partial: output });
335
+ }
318
336
  }
319
337
  return textBlock;
320
338
  };
321
339
  const ensureThinkingBlock = (thinkingSignature) => {
322
340
  if (!thinkingBlock) {
341
+ if (!deferMixedEvents) {
342
+ finishActiveBlock();
343
+ textBlock = null;
344
+ }
323
345
  thinkingBlock = {
324
346
  type: "thinking",
325
347
  thinking: "",
326
348
  thinkingSignature,
327
349
  };
328
350
  blocks.push(thinkingBlock);
329
- stream.push({ type: "thinking_start", contentIndex: getContentIndex(thinkingBlock), partial: output });
351
+ if (!deferMixedEvents) {
352
+ activeBlock = thinkingBlock;
353
+ stream.push({
354
+ type: "thinking_start",
355
+ contentIndex: getContentIndex(thinkingBlock),
356
+ partial: output,
357
+ });
358
+ }
330
359
  }
331
360
  return thinkingBlock;
332
361
  };
@@ -348,6 +377,11 @@ export const stream = (model, context, options) => {
348
377
  block = toolCallBlocksById.get(toolCall.id);
349
378
  }
350
379
  if (!block) {
380
+ if (!deferMixedEvents) {
381
+ finishActiveBlock();
382
+ textBlock = null;
383
+ thinkingBlock = null;
384
+ }
351
385
  // Note: the "input" fallback here should/must not be taken. in case the LLM makes up
352
386
  // a tool we don't knwo about, we at least have a place to stash our stuff.
353
387
  const customInputProperty = toolCall.custom
@@ -372,11 +406,14 @@ export const stream = (model, context, options) => {
372
406
  toolCallBlocksById.set(toolCall.id, block);
373
407
  }
374
408
  blocks.push(block);
375
- stream.push({
376
- type: "toolcall_start",
377
- contentIndex: getContentIndex(block),
378
- partial: output,
379
- });
409
+ if (!deferMixedEvents) {
410
+ activeBlock = block;
411
+ stream.push({
412
+ type: "toolcall_start",
413
+ contentIndex: getContentIndex(block),
414
+ partial: output,
415
+ });
416
+ }
380
417
  }
381
418
  if (streamIndex !== undefined && block.streamIndex === undefined) {
382
419
  block.streamIndex = streamIndex;
@@ -400,6 +437,30 @@ export const stream = (model, context, options) => {
400
437
  applyPendingReasoningDetail(block);
401
438
  return block;
402
439
  };
440
+ const flushDeferredBlocks = () => {
441
+ for (const block of blocks) {
442
+ const contentIndex = getContentIndex(block);
443
+ if (block.type === "text") {
444
+ stream.push({ type: "text_start", contentIndex, partial: output });
445
+ for (const delta of deferredTextDeltas) {
446
+ stream.push({ type: "text_delta", contentIndex, delta, partial: output });
447
+ }
448
+ }
449
+ else if (block.type === "thinking") {
450
+ stream.push({ type: "thinking_start", contentIndex, partial: output });
451
+ for (const delta of deferredThinkingDeltas) {
452
+ stream.push({ type: "thinking_delta", contentIndex, delta, partial: output });
453
+ }
454
+ }
455
+ else {
456
+ stream.push({ type: "toolcall_start", contentIndex, partial: output });
457
+ for (const delta of deferredToolCallDeltas.get(block) ?? []) {
458
+ stream.push({ type: "toolcall_delta", contentIndex, delta, partial: output });
459
+ }
460
+ }
461
+ finishBlock(block);
462
+ }
463
+ };
403
464
  for await (const chunk of openaiStream) {
404
465
  if (!chunk || typeof chunk !== "object")
405
466
  continue;
@@ -430,22 +491,9 @@ export const stream = (model, context, options) => {
430
491
  hasFinishReason = true;
431
492
  }
432
493
  if (choice.delta) {
433
- if (choice.delta.content !== null &&
434
- choice.delta.content !== undefined &&
435
- choice.delta.content.length > 0) {
436
- const block = ensureTextBlock();
437
- block.text += choice.delta.content;
438
- stream.push({
439
- type: "text_delta",
440
- contentIndex: getContentIndex(block),
441
- delta: choice.delta.content,
442
- partial: output,
443
- });
444
- }
445
- // Some endpoints return reasoning in reasoning_content (llama.cpp),
446
- // or reasoning (other openai compatible endpoints)
447
- // Use the first non-empty reasoning field to avoid duplication
448
- // (e.g., chutes.ai returns both reasoning_content and reasoning with same content)
494
+ const contentDelta = typeof choice.delta.content === "string" && choice.delta.content.length > 0
495
+ ? choice.delta.content
496
+ : null;
449
497
  const reasoningFields = ["reasoning_content", "reasoning", "reasoning_text"];
450
498
  const deltaFields = choice.delta;
451
499
  let foundReasoningField = null;
@@ -456,6 +504,28 @@ export const stream = (model, context, options) => {
456
504
  break;
457
505
  }
458
506
  }
507
+ if (blocks.length === 0 && contentDelta && foundReasoningField) {
508
+ deferMixedEvents = true;
509
+ }
510
+ if (contentDelta) {
511
+ const block = ensureTextBlock();
512
+ block.text += contentDelta;
513
+ if (deferMixedEvents) {
514
+ deferredTextDeltas.push(contentDelta);
515
+ }
516
+ else {
517
+ stream.push({
518
+ type: "text_delta",
519
+ contentIndex: getContentIndex(block),
520
+ delta: contentDelta,
521
+ partial: output,
522
+ });
523
+ }
524
+ }
525
+ // Some endpoints return reasoning in reasoning_content (llama.cpp),
526
+ // or reasoning (other openai compatible endpoints)
527
+ // Use the first non-empty reasoning field to avoid duplication
528
+ // (e.g., chutes.ai returns both reasoning_content and reasoning with same content)
459
529
  if (foundReasoningField) {
460
530
  const delta = deltaFields[foundReasoningField];
461
531
  if (typeof delta === "string" && delta.length > 0) {
@@ -464,12 +534,17 @@ export const stream = (model, context, options) => {
464
534
  : foundReasoningField;
465
535
  const block = ensureThinkingBlock(thinkingSignature);
466
536
  block.thinking += delta;
467
- stream.push({
468
- type: "thinking_delta",
469
- contentIndex: getContentIndex(block),
470
- delta,
471
- partial: output,
472
- });
537
+ if (deferMixedEvents) {
538
+ deferredThinkingDeltas.push(delta);
539
+ }
540
+ else {
541
+ stream.push({
542
+ type: "thinking_delta",
543
+ contentIndex: getContentIndex(block),
544
+ delta,
545
+ partial: output,
546
+ });
547
+ }
473
548
  }
474
549
  }
475
550
  if (choice?.delta?.tool_calls) {
@@ -493,12 +568,19 @@ export const stream = (model, context, options) => {
493
568
  const nextInput = getCustomToolCallInput(block) + toolCall.custom.input;
494
569
  delta = appendCustomToolCallInput(block, nextInput, false) ?? "";
495
570
  }
496
- stream.push({
497
- type: "toolcall_delta",
498
- contentIndex: getContentIndex(block),
499
- delta,
500
- partial: output,
501
- });
571
+ if (deferMixedEvents) {
572
+ const deltas = deferredToolCallDeltas.get(block) ?? [];
573
+ deltas.push(delta);
574
+ deferredToolCallDeltas.set(block, deltas);
575
+ }
576
+ else {
577
+ stream.push({
578
+ type: "toolcall_delta",
579
+ contentIndex: getContentIndex(block),
580
+ delta,
581
+ partial: output,
582
+ });
583
+ }
502
584
  }
503
585
  }
504
586
  const reasoningDetails = choice.delta.reasoning_details;
@@ -518,9 +600,10 @@ export const stream = (model, context, options) => {
518
600
  }
519
601
  }
520
602
  }
521
- for (const block of blocks) {
522
- finishBlock(block);
523
- }
603
+ if (deferMixedEvents)
604
+ flushDeferredBlocks();
605
+ else
606
+ finishActiveBlock();
524
607
  if (options?.signal?.aborted) {
525
608
  throw new Error("Request was aborted");
526
609
  }