@mmmbuto/zai-codex-bridge 0.4.4 → 0.4.6
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 +12 -0
- package/package.json +1 -1
- package/src/server.js +72 -32
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.4.6] - 2026-01-16
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- Avoid empty output_text items in tool-only streaming responses
|
|
12
|
+
- Only emit output_text.done/content_part.done when output text exists
|
|
13
|
+
|
|
14
|
+
## [0.4.5] - 2026-01-16
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Handle streaming tool_calls without `index` by assigning a stable fallback index
|
|
18
|
+
- Improve tool name logging when tools define top-level `name`
|
|
19
|
+
|
|
8
20
|
## [0.4.4] - 2026-01-16
|
|
9
21
|
|
|
10
22
|
### Fixed
|
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -130,7 +130,7 @@ function summarizeTools(tools, limit = 8) {
|
|
|
130
130
|
types[type] = (types[type] || 0) + 1;
|
|
131
131
|
if (names.length < limit) {
|
|
132
132
|
if (type === 'function') {
|
|
133
|
-
names.push(tool?.function?.name || '(missing_name)');
|
|
133
|
+
names.push(tool?.function?.name || tool?.name || '(missing_name)');
|
|
134
134
|
} else {
|
|
135
135
|
names.push(type);
|
|
136
136
|
}
|
|
@@ -359,7 +359,9 @@ function translateChatToResponses(chatResponse, responsesRequest, ids, allowTool
|
|
|
359
359
|
if (reasoningText) {
|
|
360
360
|
content.push({ type: 'reasoning_text', text: reasoningText, annotations: [] });
|
|
361
361
|
}
|
|
362
|
-
|
|
362
|
+
if (outputText) {
|
|
363
|
+
content.push({ type: 'output_text', text: outputText, annotations: [] });
|
|
364
|
+
}
|
|
363
365
|
|
|
364
366
|
const msgItem = {
|
|
365
367
|
id: msgId,
|
|
@@ -369,11 +371,16 @@ function translateChatToResponses(chatResponse, responsesRequest, ids, allowTool
|
|
|
369
371
|
content,
|
|
370
372
|
};
|
|
371
373
|
|
|
372
|
-
// Build output array: message item + any function_call items
|
|
373
|
-
const finalOutput = [
|
|
374
|
+
// Build output array: message item (if any) + any function_call items
|
|
375
|
+
const finalOutput = [];
|
|
376
|
+
|
|
377
|
+
const hasToolCalls = allowTools && msg.tool_calls && Array.isArray(msg.tool_calls);
|
|
378
|
+
if (content.length > 0 || !hasToolCalls) {
|
|
379
|
+
finalOutput.push(msgItem);
|
|
380
|
+
}
|
|
374
381
|
|
|
375
382
|
// Handle tool_calls (only if allowTools)
|
|
376
|
-
if (
|
|
383
|
+
if (hasToolCalls) {
|
|
377
384
|
for (const tc of msg.tool_calls) {
|
|
378
385
|
const callId = tc.id || `call_${randomUUID().replace(/-/g, '')}`;
|
|
379
386
|
const name = tc.function?.name || '';
|
|
@@ -517,20 +524,17 @@ async function streamChatToResponses(upstreamBody, res, responsesRequest, ids, a
|
|
|
517
524
|
item: msgItemInProgress,
|
|
518
525
|
});
|
|
519
526
|
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
item_id: msgId,
|
|
523
|
-
output_index: OUTPUT_INDEX,
|
|
524
|
-
content_index: CONTENT_INDEX,
|
|
525
|
-
part: { type: 'output_text', text: '', annotations: [] },
|
|
526
|
-
});
|
|
527
|
+
// content_part.added emitted only if we receive output_text
|
|
528
|
+
let contentPartAdded = false;
|
|
527
529
|
|
|
528
530
|
let out = '';
|
|
529
531
|
let reasoning = '';
|
|
530
532
|
|
|
531
533
|
// Tool call tracking (only if allowTools)
|
|
532
534
|
const toolCallsMap = new Map(); // index -> { callId, name, outputIndex, arguments, partialArgs }
|
|
535
|
+
const toolCallsById = new Map(); // callId -> index
|
|
533
536
|
const TOOL_BASE_INDEX = 1; // After message item
|
|
537
|
+
let nextToolIndex = 0;
|
|
534
538
|
|
|
535
539
|
while (true) {
|
|
536
540
|
const { done, value } = await reader.read();
|
|
@@ -563,12 +567,22 @@ async function streamChatToResponses(upstreamBody, res, responsesRequest, ids, a
|
|
|
563
567
|
// Handle tool_calls (only if allowTools)
|
|
564
568
|
if (allowTools && delta.tool_calls && Array.isArray(delta.tool_calls)) {
|
|
565
569
|
for (const tc of delta.tool_calls) {
|
|
566
|
-
|
|
567
|
-
|
|
570
|
+
let index = tc.index;
|
|
571
|
+
const tcId = tc.id;
|
|
572
|
+
|
|
573
|
+
if (index == null) {
|
|
574
|
+
if (tcId && toolCallsById.has(tcId)) {
|
|
575
|
+
index = toolCallsById.get(tcId);
|
|
576
|
+
} else {
|
|
577
|
+
index = nextToolIndex++;
|
|
578
|
+
}
|
|
579
|
+
} else if (index >= nextToolIndex) {
|
|
580
|
+
nextToolIndex = index + 1;
|
|
581
|
+
}
|
|
568
582
|
|
|
569
583
|
if (!toolCallsMap.has(index)) {
|
|
570
584
|
// New tool call - send output_item.added
|
|
571
|
-
const callId =
|
|
585
|
+
const callId = tcId || `call_${randomUUID().replace(/-/g, '')}`;
|
|
572
586
|
const name = tc.function?.name || '';
|
|
573
587
|
const outputIndex = TOOL_BASE_INDEX + index;
|
|
574
588
|
|
|
@@ -579,6 +593,7 @@ async function streamChatToResponses(upstreamBody, res, responsesRequest, ids, a
|
|
|
579
593
|
arguments: '',
|
|
580
594
|
partialArgs: ''
|
|
581
595
|
});
|
|
596
|
+
if (callId) toolCallsById.set(callId, index);
|
|
582
597
|
|
|
583
598
|
const fnItemInProgress = {
|
|
584
599
|
id: callId,
|
|
@@ -675,6 +690,16 @@ async function streamChatToResponses(upstreamBody, res, responsesRequest, ids, a
|
|
|
675
690
|
}
|
|
676
691
|
|
|
677
692
|
if (typeof delta.content === 'string' && delta.content.length) {
|
|
693
|
+
if (!contentPartAdded) {
|
|
694
|
+
sse({
|
|
695
|
+
type: 'response.content_part.added',
|
|
696
|
+
item_id: msgId,
|
|
697
|
+
output_index: OUTPUT_INDEX,
|
|
698
|
+
content_index: CONTENT_INDEX,
|
|
699
|
+
part: { type: 'output_text', text: '', annotations: [] },
|
|
700
|
+
});
|
|
701
|
+
contentPartAdded = true;
|
|
702
|
+
}
|
|
678
703
|
out += delta.content;
|
|
679
704
|
sse({
|
|
680
705
|
type: 'response.output_text.delta',
|
|
@@ -699,28 +724,40 @@ async function streamChatToResponses(upstreamBody, res, responsesRequest, ids, a
|
|
|
699
724
|
});
|
|
700
725
|
}
|
|
701
726
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
727
|
+
if (out.length) {
|
|
728
|
+
sse({
|
|
729
|
+
type: 'response.output_text.done',
|
|
730
|
+
item_id: msgId,
|
|
731
|
+
output_index: OUTPUT_INDEX,
|
|
732
|
+
content_index: CONTENT_INDEX,
|
|
733
|
+
text: out,
|
|
734
|
+
});
|
|
709
735
|
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
736
|
+
if (contentPartAdded) {
|
|
737
|
+
sse({
|
|
738
|
+
type: 'response.content_part.done',
|
|
739
|
+
item_id: msgId,
|
|
740
|
+
output_index: OUTPUT_INDEX,
|
|
741
|
+
content_index: CONTENT_INDEX,
|
|
742
|
+
part: { type: 'output_text', text: out, annotations: [] },
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const msgContent = [];
|
|
748
|
+
if (reasoning.length) {
|
|
749
|
+
msgContent.push({ type: 'reasoning_text', text: reasoning, annotations: [] });
|
|
750
|
+
}
|
|
751
|
+
if (out.length) {
|
|
752
|
+
msgContent.push({ type: 'output_text', text: out, annotations: [] });
|
|
753
|
+
}
|
|
717
754
|
|
|
718
755
|
const msgItemDone = {
|
|
719
756
|
id: msgId,
|
|
720
757
|
type: 'message',
|
|
721
758
|
status: 'completed',
|
|
722
759
|
role: 'assistant',
|
|
723
|
-
content:
|
|
760
|
+
content: msgContent,
|
|
724
761
|
};
|
|
725
762
|
|
|
726
763
|
sse({
|
|
@@ -729,8 +766,11 @@ async function streamChatToResponses(upstreamBody, res, responsesRequest, ids, a
|
|
|
729
766
|
item: msgItemDone,
|
|
730
767
|
});
|
|
731
768
|
|
|
732
|
-
// Build final output array: message item + any function_call items
|
|
733
|
-
const finalOutput = [
|
|
769
|
+
// Build final output array: message item (if any) + any function_call items
|
|
770
|
+
const finalOutput = [];
|
|
771
|
+
if (msgContent.length > 0 || toolCallsMap.size === 0) {
|
|
772
|
+
finalOutput.push(msgItemDone);
|
|
773
|
+
}
|
|
734
774
|
if (allowTools && toolCallsMap.size > 0) {
|
|
735
775
|
const ordered = Array.from(toolCallsMap.entries()).sort((a, b) => a[0] - b[0]);
|
|
736
776
|
for (const [, tcData] of ordered) {
|