@mmmbuto/zai-codex-bridge 0.1.10 → 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 -25
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,23 +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
|
-
|
|
327
|
+
log('info', 'Sending response.completed event');
|
|
306
328
|
res.write(`data: ${JSON.stringify(completedEvent)}\n\n`);
|
|
307
329
|
log('info', 'Sent response.completed event');
|
|
308
330
|
return;
|
|
@@ -310,26 +332,31 @@ async function streamChatToResponses(stream, res) {
|
|
|
310
332
|
|
|
311
333
|
try {
|
|
312
334
|
const parsed = JSON.parse(data);
|
|
313
|
-
lastParsed = parsed;
|
|
335
|
+
lastParsed = parsed;
|
|
314
336
|
log('debug', 'Parsed SSE:', JSON.stringify(parsed).substring(0, 150));
|
|
315
337
|
|
|
316
338
|
const delta = parsed.choices?.[0]?.delta;
|
|
317
|
-
|
|
318
|
-
// Z.AI uses reasoning_content instead of content
|
|
319
339
|
const content = delta?.content || delta?.reasoning_content || '';
|
|
320
340
|
|
|
321
341
|
if (content) {
|
|
322
342
|
deltaCount++;
|
|
323
343
|
log('debug', 'Writing delta:', content.substring(0, 30));
|
|
324
|
-
|
|
325
|
-
|
|
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`);
|
|
326
353
|
}
|
|
327
354
|
} catch (e) {
|
|
328
355
|
log('warn', 'Failed to parse SSE chunk:', e.message, 'data:', data.substring(0, 100));
|
|
329
356
|
}
|
|
330
357
|
}
|
|
331
358
|
|
|
332
|
-
if (chunkCount >
|
|
359
|
+
if (chunkCount > 1000) {
|
|
333
360
|
log('warn', 'Too many chunks, possible loop');
|
|
334
361
|
return;
|
|
335
362
|
}
|
|
@@ -412,6 +439,7 @@ async function handlePostRequest(req, res) {
|
|
|
412
439
|
|
|
413
440
|
// Handle streaming response
|
|
414
441
|
if (upstreamBody.stream) {
|
|
442
|
+
const responseId = 'resp_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
|
415
443
|
log('info', 'Starting streaming response');
|
|
416
444
|
res.writeHead(200, {
|
|
417
445
|
'Content-Type': 'text/event-stream; charset=utf-8',
|
|
@@ -420,7 +448,7 @@ async function handlePostRequest(req, res) {
|
|
|
420
448
|
});
|
|
421
449
|
|
|
422
450
|
try {
|
|
423
|
-
await streamChatToResponses(upstreamResponse.body, res);
|
|
451
|
+
await streamChatToResponses(upstreamResponse.body, res, responseId);
|
|
424
452
|
log('info', 'Streaming completed');
|
|
425
453
|
} catch (e) {
|
|
426
454
|
log('error', 'Streaming error:', e);
|