@mmmbuto/zai-codex-bridge 0.1.11 → 0.1.12
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 +53 -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,12 +267,13 @@ 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) {
|
|
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;
|
|
276
|
+
const itemId = 'item_' + Date.now();
|
|
264
277
|
|
|
265
278
|
log('debug', 'Starting to process stream');
|
|
266
279
|
|
|
@@ -286,24 +299,32 @@ async function streamChatToResponses(stream, res) {
|
|
|
286
299
|
if (data === '[DONE]') {
|
|
287
300
|
log('info', `Stream end received - wrote ${deltaCount} deltas total`);
|
|
288
301
|
|
|
289
|
-
// Send response.completed event
|
|
290
|
-
// Map Z.AI usage format to Responses API format
|
|
302
|
+
// Send response.completed event in OpenAI Responses API format
|
|
291
303
|
const zaiUsage = lastParsed?.usage;
|
|
292
304
|
const completedEvent = {
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
305
|
+
type: 'response.completed',
|
|
306
|
+
response: {
|
|
307
|
+
id: responseId,
|
|
308
|
+
status: 'completed',
|
|
309
|
+
output: [{
|
|
310
|
+
type: 'message',
|
|
311
|
+
role: 'assistant',
|
|
312
|
+
content: [{ type: 'output_text', text: '' }]
|
|
313
|
+
}],
|
|
314
|
+
usage: zaiUsage ? {
|
|
315
|
+
input_tokens: zaiUsage.prompt_tokens || 0,
|
|
316
|
+
output_tokens: zaiUsage.completion_tokens || 0,
|
|
317
|
+
total_tokens: zaiUsage.total_tokens || 0
|
|
318
|
+
} : {
|
|
319
|
+
input_tokens: 0,
|
|
320
|
+
output_tokens: 0,
|
|
321
|
+
total_tokens: 0
|
|
322
|
+
}
|
|
323
|
+
},
|
|
324
|
+
sequence_number: deltaCount
|
|
303
325
|
};
|
|
304
326
|
|
|
305
|
-
log('info', 'Sending response.completed event
|
|
306
|
-
res.write(`event: response.completed\n`);
|
|
327
|
+
log('info', 'Sending response.completed event');
|
|
307
328
|
res.write(`data: ${JSON.stringify(completedEvent)}\n\n`);
|
|
308
329
|
log('info', 'Sent response.completed event');
|
|
309
330
|
return;
|
|
@@ -311,26 +332,31 @@ async function streamChatToResponses(stream, res) {
|
|
|
311
332
|
|
|
312
333
|
try {
|
|
313
334
|
const parsed = JSON.parse(data);
|
|
314
|
-
lastParsed = parsed;
|
|
335
|
+
lastParsed = parsed;
|
|
315
336
|
log('debug', 'Parsed SSE:', JSON.stringify(parsed).substring(0, 150));
|
|
316
337
|
|
|
317
338
|
const delta = parsed.choices?.[0]?.delta;
|
|
318
|
-
|
|
319
|
-
// Z.AI uses reasoning_content instead of content
|
|
320
339
|
const content = delta?.content || delta?.reasoning_content || '';
|
|
321
340
|
|
|
322
341
|
if (content) {
|
|
323
342
|
deltaCount++;
|
|
324
343
|
log('debug', 'Writing delta:', content.substring(0, 30));
|
|
325
|
-
|
|
326
|
-
|
|
344
|
+
// OpenAI Responses API format for text delta
|
|
345
|
+
const deltaEvent = {
|
|
346
|
+
type: 'response.output_text.delta',
|
|
347
|
+
delta: content,
|
|
348
|
+
output_index: 0,
|
|
349
|
+
item_id: itemId,
|
|
350
|
+
sequence_number: deltaCount - 1
|
|
351
|
+
};
|
|
352
|
+
res.write(`data: ${JSON.stringify(deltaEvent)}\n\n`);
|
|
327
353
|
}
|
|
328
354
|
} catch (e) {
|
|
329
355
|
log('warn', 'Failed to parse SSE chunk:', e.message, 'data:', data.substring(0, 100));
|
|
330
356
|
}
|
|
331
357
|
}
|
|
332
358
|
|
|
333
|
-
if (chunkCount >
|
|
359
|
+
if (chunkCount > 1000) {
|
|
334
360
|
log('warn', 'Too many chunks, possible loop');
|
|
335
361
|
return;
|
|
336
362
|
}
|
|
@@ -413,6 +439,7 @@ async function handlePostRequest(req, res) {
|
|
|
413
439
|
|
|
414
440
|
// Handle streaming response
|
|
415
441
|
if (upstreamBody.stream) {
|
|
442
|
+
const responseId = 'resp_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
416
443
|
log('info', 'Starting streaming response');
|
|
417
444
|
res.writeHead(200, {
|
|
418
445
|
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
@@ -421,7 +448,7 @@ async function handlePostRequest(req, res) {
|
|
|
421
448
|
});
|
|
422
449
|
|
|
423
450
|
try {
|
|
424
|
-
await streamChatToResponses(upstreamResponse.body, res);
|
|
451
|
+
await streamChatToResponses(upstreamResponse.body, res, responseId);
|
|
425
452
|
log('info', 'Streaming completed');
|
|
426
453
|
} catch (e) {
|
|
427
454
|
log('error', 'Streaming error:', e);
|