@mmmbuto/zai-codex-bridge 0.3.0 → 0.3.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.
- package/package.json +1 -1
- package/src/server.js +115 -95
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
|
|
13
13
|
const http = require('http');
|
|
14
14
|
const crypto = require('crypto');
|
|
15
|
+
const { createGunzip } = require('zlib');
|
|
16
|
+
const { pipeline } = require('stream');
|
|
15
17
|
|
|
16
18
|
// Configuration from environment
|
|
17
19
|
const PORT = parseInt(process.env.PORT || '31415', 10);
|
|
@@ -269,7 +271,8 @@ async function makeUpstreamRequest(path, body, headers) {
|
|
|
269
271
|
const auth = pickAuth(headers);
|
|
270
272
|
const upstreamHeaders = {
|
|
271
273
|
'Content-Type': 'application/json',
|
|
272
|
-
'Authorization': auth
|
|
274
|
+
'Authorization': auth,
|
|
275
|
+
'Accept-Encoding': 'identity' // Disable compression to avoid gzip issues
|
|
273
276
|
};
|
|
274
277
|
|
|
275
278
|
log('info', 'Upstream request:', {
|
|
@@ -456,107 +459,124 @@ async function streamChatToResponses(stream, res, responseId, messageItemId) {
|
|
|
456
459
|
|
|
457
460
|
try {
|
|
458
461
|
for await (const chunk of stream) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
if (
|
|
472
|
-
|
|
473
|
-
let json;
|
|
474
|
-
try { json = JSON.parse(payload); } catch { continue; }
|
|
475
|
-
|
|
476
|
-
const choice = json?.choices?.[0];
|
|
477
|
-
const delta = choice?.delta ?? {};
|
|
478
|
-
|
|
479
|
-
// 1) reasoning
|
|
480
|
-
if (typeof delta.reasoning_content === 'string' && delta.reasoning_content.length) {
|
|
481
|
-
reasoningText += delta.reasoning_content;
|
|
482
|
-
send({
|
|
483
|
-
type: 'response.reasoning_text.delta',
|
|
484
|
-
sequence_number: seq++,
|
|
485
|
-
item_id: messageItemId,
|
|
486
|
-
output_index: 0,
|
|
487
|
-
content_index: 0,
|
|
488
|
-
delta: delta.reasoning_content,
|
|
489
|
-
});
|
|
490
|
-
log('debug', `Reasoning delta: ${delta.reasoning_content.substring(0, 30)}...`);
|
|
462
|
+
const chunkStr = Buffer.from(chunk).toString('utf8');
|
|
463
|
+
buffer += chunkStr;
|
|
464
|
+
|
|
465
|
+
// Z.ai stream: SSE lines "data: {...}\n"
|
|
466
|
+
// Split by newline and process each complete line
|
|
467
|
+
const lines = buffer.split('\n');
|
|
468
|
+
// Keep the last line if it's incomplete (doesn't end with data pattern)
|
|
469
|
+
buffer = lines.pop() || '';
|
|
470
|
+
|
|
471
|
+
for (const line of lines) {
|
|
472
|
+
if (!line.trim() || !line.startsWith('data:')) {
|
|
473
|
+
// Skip empty lines and comments (starting with :)
|
|
474
|
+
if (line.trim() && !line.startsWith(':')) {
|
|
475
|
+
log('debug', 'Non-data line:', line.substring(0, 50));
|
|
491
476
|
}
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
492
479
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
480
|
+
const payload = line.slice(5).trim();
|
|
481
|
+
if (payload === '[DONE]') {
|
|
482
|
+
log('info', 'Stream received [DONE]');
|
|
483
|
+
await finalizeAndClose();
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (!payload) continue;
|
|
488
|
+
|
|
489
|
+
let json;
|
|
490
|
+
try {
|
|
491
|
+
json = JSON.parse(payload);
|
|
492
|
+
} catch (e) {
|
|
493
|
+
log('warn', 'Failed to parse SSE payload:', e.message, 'payload:', payload.substring(0, 100));
|
|
494
|
+
continue;
|
|
495
|
+
}
|
|
506
496
|
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
497
|
+
const choice = json?.choices?.[0];
|
|
498
|
+
const delta = choice?.delta ?? {};
|
|
499
|
+
|
|
500
|
+
// 1) reasoning
|
|
501
|
+
if (typeof delta.reasoning_content === 'string' && delta.reasoning_content.length) {
|
|
502
|
+
reasoningText += delta.reasoning_content;
|
|
503
|
+
send({
|
|
504
|
+
type: 'response.reasoning_text.delta',
|
|
505
|
+
sequence_number: seq++,
|
|
506
|
+
item_id: messageItemId,
|
|
507
|
+
output_index: 0,
|
|
508
|
+
content_index: 0,
|
|
509
|
+
delta: delta.reasoning_content,
|
|
510
|
+
});
|
|
511
|
+
log('debug', `Reasoning delta: ${delta.reasoning_content.substring(0, 30)}...`);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// 2) normal output
|
|
515
|
+
if (typeof delta.content === 'string' && delta.content.length) {
|
|
516
|
+
outputText += delta.content;
|
|
517
|
+
send({
|
|
518
|
+
type: 'response.output_text.delta',
|
|
519
|
+
sequence_number: seq++,
|
|
520
|
+
item_id: messageItemId,
|
|
521
|
+
output_index: 0,
|
|
522
|
+
content_index: reasoningText ? 1 : 0,
|
|
523
|
+
delta: delta.content,
|
|
524
|
+
});
|
|
525
|
+
log('debug', `Output delta: ${delta.content.substring(0, 30)}...`);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// 3) tool calls (OpenAI-style in chat.completions delta.tool_calls)
|
|
529
|
+
if (Array.isArray(delta.tool_calls)) {
|
|
530
|
+
for (const tc of delta.tool_calls) {
|
|
531
|
+
// tc: {id, type:"function", function:{name, arguments}}
|
|
532
|
+
const callId = tc.id || `call_${tc.index ?? 0}`;
|
|
533
|
+
const name = tc.function?.name || 'unknown';
|
|
534
|
+
const argsDelta = tc.function?.arguments || '';
|
|
535
|
+
|
|
536
|
+
let st = toolCalls.get(callId);
|
|
537
|
+
if (!st) {
|
|
538
|
+
st = {
|
|
539
|
+
itemId: `fc_${crypto.randomUUID().replace(/-/g, '')}`,
|
|
540
|
+
outputIndex: nextOutputIndex++,
|
|
541
|
+
name,
|
|
542
|
+
args: '',
|
|
543
|
+
};
|
|
544
|
+
toolCalls.set(callId, st);
|
|
545
|
+
|
|
546
|
+
send({
|
|
547
|
+
type: 'response.output_item.added',
|
|
548
|
+
sequence_number: seq++,
|
|
549
|
+
output_index: st.outputIndex,
|
|
550
|
+
item: {
|
|
551
|
+
type: 'function_call',
|
|
552
|
+
id: st.itemId,
|
|
553
|
+
call_id: callId,
|
|
554
|
+
name: st.name,
|
|
555
|
+
arguments: '',
|
|
556
|
+
},
|
|
557
|
+
});
|
|
558
|
+
log('debug', `Tool call added: ${name} (${callId})`);
|
|
550
559
|
}
|
|
551
|
-
}
|
|
552
560
|
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
561
|
+
if (argsDelta) {
|
|
562
|
+
st.args += argsDelta;
|
|
563
|
+
send({
|
|
564
|
+
type: 'response.function_call_arguments.delta',
|
|
565
|
+
sequence_number: seq++,
|
|
566
|
+
item_id: st.itemId,
|
|
567
|
+
output_index: st.outputIndex,
|
|
568
|
+
delta: argsDelta,
|
|
569
|
+
});
|
|
570
|
+
}
|
|
558
571
|
}
|
|
559
572
|
}
|
|
573
|
+
|
|
574
|
+
// 4) finish
|
|
575
|
+
if (choice?.finish_reason) {
|
|
576
|
+
log('info', `Stream finish_reason: ${choice.finish_reason}`);
|
|
577
|
+
await finalizeAndClose();
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
560
580
|
}
|
|
561
581
|
}
|
|
562
582
|
} catch (e) {
|