deepccc 0.2.10 → 0.2.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/README.md +2 -0
- package/dist/context.js +28 -35
- package/dist/index.js +17 -0
- package/package.json +1 -1
package/README.md
CHANGED
package/dist/context.js
CHANGED
|
@@ -392,38 +392,35 @@ function replayStructuredAssistantMessage(message, messageIndex) {
|
|
|
392
392
|
];
|
|
393
393
|
const messages = [];
|
|
394
394
|
let assistantParts = [];
|
|
395
|
-
|
|
396
|
-
const
|
|
397
|
-
const knownNames = new Map();
|
|
395
|
+
const batchCalls = new Map();
|
|
396
|
+
const batchResults = new Map();
|
|
398
397
|
const emittedCallIds = new Set();
|
|
399
|
-
const
|
|
400
|
-
if (
|
|
398
|
+
const completeToolBatch = () => {
|
|
399
|
+
if (assistantParts.length > 0) {
|
|
400
|
+
messages.push({ role: "assistant", content: assistantParts });
|
|
401
|
+
assistantParts = [];
|
|
402
|
+
}
|
|
403
|
+
if (batchCalls.size === 0) {
|
|
404
|
+
batchResults.clear();
|
|
401
405
|
return;
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
// OpenAI/LiteLLM requires every tool message to immediately follow the
|
|
407
|
-
// assistant message that declared all matching tool_calls for that step.
|
|
408
|
-
flushAssistant();
|
|
409
|
-
for (const [toolCallId, toolName] of pendingCalls) {
|
|
410
|
-
toolParts.push({
|
|
406
|
+
}
|
|
407
|
+
const toolParts = [];
|
|
408
|
+
for (const [toolCallId, toolName] of batchCalls) {
|
|
409
|
+
toolParts.push(batchResults.get(toolCallId) ?? {
|
|
411
410
|
type: "tool-result",
|
|
412
411
|
toolCallId,
|
|
413
412
|
toolName,
|
|
414
413
|
output: { type: "text", value: "[tool execution interrupted; result unavailable]" },
|
|
415
414
|
});
|
|
416
415
|
}
|
|
417
|
-
pendingCalls.clear();
|
|
418
|
-
if (!toolParts.length)
|
|
419
|
-
return;
|
|
420
416
|
messages.push({ role: "tool", content: toolParts });
|
|
421
|
-
|
|
417
|
+
batchCalls.clear();
|
|
418
|
+
batchResults.clear();
|
|
422
419
|
};
|
|
423
420
|
for (const entry of timeline) {
|
|
424
421
|
if (entry.type === "text") {
|
|
425
|
-
if (
|
|
426
|
-
|
|
422
|
+
if (batchCalls.size > 0 || batchResults.size > 0)
|
|
423
|
+
completeToolBatch();
|
|
427
424
|
const previous = assistantParts[assistantParts.length - 1];
|
|
428
425
|
if (previous?.type === "text")
|
|
429
426
|
previous.text += entry.text;
|
|
@@ -431,12 +428,13 @@ function replayStructuredAssistantMessage(message, messageIndex) {
|
|
|
431
428
|
assistantParts.push({ type: "text", text: entry.text });
|
|
432
429
|
}
|
|
433
430
|
else if (entry.type === "tool_use") {
|
|
434
|
-
//
|
|
435
|
-
//
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
431
|
+
// Providers may interleave parallel tool results with later tool-call
|
|
432
|
+
// events from the same assistant step. Keep the whole contiguous tool
|
|
433
|
+
// segment together until text resumes; closing on the first result can
|
|
434
|
+
// emit a later real result as an orphaned `role: tool` message.
|
|
435
|
+
if (emittedCallIds.has(entry.id))
|
|
436
|
+
continue;
|
|
437
|
+
batchCalls.set(entry.id, entry.name);
|
|
440
438
|
emittedCallIds.add(entry.id);
|
|
441
439
|
assistantParts.push({
|
|
442
440
|
type: "tool-call",
|
|
@@ -446,14 +444,12 @@ function replayStructuredAssistantMessage(message, messageIndex) {
|
|
|
446
444
|
});
|
|
447
445
|
}
|
|
448
446
|
else {
|
|
449
|
-
if (!
|
|
447
|
+
if (!batchCalls.has(entry.tool_use_id))
|
|
450
448
|
continue;
|
|
451
|
-
const toolName = entry.name ??
|
|
449
|
+
const toolName = entry.name ?? batchCalls.get(entry.tool_use_id);
|
|
452
450
|
if (!toolName)
|
|
453
451
|
continue;
|
|
454
|
-
|
|
455
|
-
pendingCalls.delete(entry.tool_use_id);
|
|
456
|
-
toolParts.push({
|
|
452
|
+
batchResults.set(entry.tool_use_id, {
|
|
457
453
|
type: "tool-result",
|
|
458
454
|
toolCallId: entry.tool_use_id,
|
|
459
455
|
toolName,
|
|
@@ -461,10 +457,7 @@ function replayStructuredAssistantMessage(message, messageIndex) {
|
|
|
461
457
|
});
|
|
462
458
|
}
|
|
463
459
|
}
|
|
464
|
-
|
|
465
|
-
completeToolStep();
|
|
466
|
-
else
|
|
467
|
-
flushAssistant();
|
|
460
|
+
completeToolBatch();
|
|
468
461
|
return messages;
|
|
469
462
|
}
|
|
470
463
|
function serializeMessagesForCompaction(messages) {
|
package/dist/index.js
CHANGED
|
@@ -478,16 +478,25 @@ export class ChatSession {
|
|
|
478
478
|
messages: attemptMessages,
|
|
479
479
|
};
|
|
480
480
|
let stream;
|
|
481
|
+
let requiresFinish = false;
|
|
482
|
+
let receivedFinish = false;
|
|
483
|
+
let finishReason;
|
|
481
484
|
if (this.streaming) {
|
|
482
485
|
const result = streamText(generationOptions);
|
|
486
|
+
requiresFinish = result.fullStream != null;
|
|
483
487
|
stream = result.fullStream ?? textStreamToFullStream(result.textStream);
|
|
484
488
|
}
|
|
485
489
|
else {
|
|
486
490
|
const result = await generateText(generationOptions);
|
|
491
|
+
finishReason = result.finishReason;
|
|
487
492
|
stream = generateResultToFullStream(result);
|
|
488
493
|
}
|
|
489
494
|
for await (const part of stream) {
|
|
490
495
|
rawLog?.writeLine(safeRawStreamJson(part));
|
|
496
|
+
if (part.type === "finish") {
|
|
497
|
+
receivedFinish = true;
|
|
498
|
+
finishReason = part.finishReason;
|
|
499
|
+
}
|
|
491
500
|
if (part.type === "reasoning-start" || part.type === "reasoning-delta") {
|
|
492
501
|
// Reasoning content remains private. A throttled heartbeat is enough
|
|
493
502
|
// for ChatCCC to distinguish active inference from a stalled stream.
|
|
@@ -561,6 +570,14 @@ export class ChatSession {
|
|
|
561
570
|
throw new Error(message);
|
|
562
571
|
}
|
|
563
572
|
}
|
|
573
|
+
if (!signal?.aborted) {
|
|
574
|
+
if (requiresFinish && !receivedFinish)
|
|
575
|
+
throw new Error("DeepCCC 输出流中断:未收到模型完成事件,回复可能不完整");
|
|
576
|
+
if (finishReason === "error" || finishReason === "length")
|
|
577
|
+
throw new Error(`DeepCCC 未正常完成:finishReason=${finishReason},回复可能不完整`);
|
|
578
|
+
if (!fullText.trim() && toolCallOrder.length === 0)
|
|
579
|
+
throw new Error("DeepCCC 本轮未产生有效回复");
|
|
580
|
+
}
|
|
564
581
|
if (hasMalformedToolProtocolText(fullText)) {
|
|
565
582
|
console.warn(`[DeepCCC] malformed tool protocol text detected for ${this.context.sessionId} `
|
|
566
583
|
+ `(attempt ${attempt + 1}/2, structuredToolCalls=${toolCallOrder.length})`);
|