@mmmbuto/zai-codex-bridge 0.1.11 → 0.1.13
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 +68 -26
package/package.json
CHANGED
package/src/server.js
CHANGED
|
@@ -199,16 +199,28 @@ function translateChatToResponses(chatResponse) {
|
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
+
const responseId = 'resp_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
203
|
+
|
|
204
|
+
// OpenAI Responses API format
|
|
202
205
|
const response = {
|
|
206
|
+
id: responseId,
|
|
207
|
+
object: 'response',
|
|
208
|
+
created_at: Math.floor(Date.now() / 1000),
|
|
209
|
+
status: 'completed',
|
|
210
|
+
model: chatResponse.model || 'glm-4.7',
|
|
203
211
|
output: [{
|
|
204
|
-
|
|
205
|
-
|
|
212
|
+
type: 'message',
|
|
213
|
+
role: 'assistant',
|
|
214
|
+
content: [{
|
|
215
|
+
type: 'output_text',
|
|
216
|
+
text: text
|
|
217
|
+
}]
|
|
206
218
|
}],
|
|
207
|
-
status: 'completed',
|
|
208
219
|
usage: Object.keys(usage).length > 0 ? usage : undefined
|
|
209
220
|
};
|
|
210
221
|
|
|
211
222
|
log('debug', 'Translated Chat->Responses:', {
|
|
223
|
+
id: response.id,
|
|
212
224
|
outputLength: text.length,
|
|
213
225
|
status: response.status
|
|
214
226
|
});
|
|
@@ -255,15 +267,30 @@ async function makeUpstreamRequest(path, body, headers) {
|
|
|
255
267
|
/**
|
|
256
268
|
* Handle streaming response from Z.AI
|
|
257
269
|
*/
|
|
258
|
-
async function streamChatToResponses(stream, res) {
|
|
270
|
+
async function streamChatToResponses(stream, res, responseId, itemId) {
|
|
259
271
|
const decoder = new TextDecoder();
|
|
260
272
|
let buffer = '';
|
|
261
273
|
let chunkCount = 0;
|
|
262
274
|
let deltaCount = 0;
|
|
263
|
-
let lastParsed = null;
|
|
275
|
+
let lastParsed = null;
|
|
264
276
|
|
|
265
277
|
log('debug', 'Starting to process stream');
|
|
266
278
|
|
|
279
|
+
// Send initial event to create the output item
|
|
280
|
+
const addEvent = {
|
|
281
|
+
type: 'response.output_item.add',
|
|
282
|
+
item: {
|
|
283
|
+
type: 'message',
|
|
284
|
+
role: 'assistant',
|
|
285
|
+
content: [{ type: 'output_text', text: '' }],
|
|
286
|
+
id: itemId
|
|
287
|
+
},
|
|
288
|
+
output_index: 0,
|
|
289
|
+
response_id: responseId
|
|
290
|
+
};
|
|
291
|
+
res.write(`data: ${JSON.stringify(addEvent)}\n\n`);
|
|
292
|
+
log('debug', 'Sent output_item.add event');
|
|
293
|
+
|
|
267
294
|
for await (const chunk of stream) {
|
|
268
295
|
buffer += decoder.decode(chunk, { stream: true });
|
|
269
296
|
const lines = buffer.split('\n');
|
|
@@ -286,24 +313,32 @@ async function streamChatToResponses(stream, res) {
|
|
|
286
313
|
if (data === '[DONE]') {
|
|
287
314
|
log('info', `Stream end received - wrote ${deltaCount} deltas total`);
|
|
288
315
|
|
|
289
|
-
// Send response.completed event
|
|
290
|
-
// Map Z.AI usage format to Responses API format
|
|
316
|
+
// Send response.completed event in OpenAI Responses API format
|
|
291
317
|
const zaiUsage = lastParsed?.usage;
|
|
292
318
|
const completedEvent = {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
319
|
+
type: 'response.completed',
|
|
320
|
+
response: {
|
|
321
|
+
id: responseId,
|
|
322
|
+
status: 'completed',
|
|
323
|
+
output: [{
|
|
324
|
+
type: 'message',
|
|
325
|
+
role: 'assistant',
|
|
326
|
+
content: [{ type: 'output_text', text: '' }]
|
|
327
|
+
}],
|
|
328
|
+
usage: zaiUsage ? {
|
|
329
|
+
input_tokens: zaiUsage.prompt_tokens || 0,
|
|
330
|
+
output_tokens: zaiUsage.completion_tokens || 0,
|
|
331
|
+
total_tokens: zaiUsage.total_tokens || 0
|
|
332
|
+
} : {
|
|
333
|
+
input_tokens: 0,
|
|
334
|
+
output_tokens: 0,
|
|
335
|
+
total_tokens: 0
|
|
336
|
+
}
|
|
337
|
+
},
|
|
338
|
+
sequence_number: deltaCount
|
|
303
339
|
};
|
|
304
340
|
|
|
305
|
-
log('info', 'Sending response.completed event
|
|
306
|
-
res.write(`event: response.completed\n`);
|
|
341
|
+
log('info', 'Sending response.completed event');
|
|
307
342
|
res.write(`data: ${JSON.stringify(completedEvent)}\n\n`);
|
|
308
343
|
log('info', 'Sent response.completed event');
|
|
309
344
|
return;
|
|
@@ -311,26 +346,31 @@ async function streamChatToResponses(stream, res) {
|
|
|
311
346
|
|
|
312
347
|
try {
|
|
313
348
|
const parsed = JSON.parse(data);
|
|
314
|
-
lastParsed = parsed;
|
|
349
|
+
lastParsed = parsed;
|
|
315
350
|
log('debug', 'Parsed SSE:', JSON.stringify(parsed).substring(0, 150));
|
|
316
351
|
|
|
317
352
|
const delta = parsed.choices?.[0]?.delta;
|
|
318
|
-
|
|
319
|
-
// Z.AI uses reasoning_content instead of content
|
|
320
353
|
const content = delta?.content || delta?.reasoning_content || '';
|
|
321
354
|
|
|
322
355
|
if (content) {
|
|
323
356
|
deltaCount++;
|
|
324
357
|
log('debug', 'Writing delta:', content.substring(0, 30));
|
|
325
|
-
|
|
326
|
-
|
|
358
|
+
// OpenAI Responses API format for text delta
|
|
359
|
+
const deltaEvent = {
|
|
360
|
+
type: 'response.output_text.delta',
|
|
361
|
+
delta: content,
|
|
362
|
+
output_index: 0,
|
|
363
|
+
item_id: itemId,
|
|
364
|
+
sequence_number: deltaCount - 1
|
|
365
|
+
};
|
|
366
|
+
res.write(`data: ${JSON.stringify(deltaEvent)}\n\n`);
|
|
327
367
|
}
|
|
328
368
|
} catch (e) {
|
|
329
369
|
log('warn', 'Failed to parse SSE chunk:', e.message, 'data:', data.substring(0, 100));
|
|
330
370
|
}
|
|
331
371
|
}
|
|
332
372
|
|
|
333
|
-
if (chunkCount >
|
|
373
|
+
if (chunkCount > 1000) {
|
|
334
374
|
log('warn', 'Too many chunks, possible loop');
|
|
335
375
|
return;
|
|
336
376
|
}
|
|
@@ -413,6 +453,8 @@ async function handlePostRequest(req, res) {
|
|
|
413
453
|
|
|
414
454
|
// Handle streaming response
|
|
415
455
|
if (upstreamBody.stream) {
|
|
456
|
+
const responseId = 'resp_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
457
|
+
const itemId = 'item_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
416
458
|
log('info', 'Starting streaming response');
|
|
417
459
|
res.writeHead(200, {
|
|
418
460
|
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
@@ -421,7 +463,7 @@ async function handlePostRequest(req, res) {
|
|
|
421
463
|
});
|
|
422
464
|
|
|
423
465
|
try {
|
|
424
|
-
await streamChatToResponses(upstreamResponse.body, res);
|
|
466
|
+
await streamChatToResponses(upstreamResponse.body, res, responseId, itemId);
|
|
425
467
|
log('info', 'Streaming completed');
|
|
426
468
|
} catch (e) {
|
|
427
469
|
log('error', 'Streaming error:', e);
|