@namzu/sdk 60.0.0 → 60.2.0
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/CHANGELOG.md +40 -0
- package/dist/agents/QueryAgent.d.ts.map +1 -1
- package/dist/agents/QueryAgent.js +1 -0
- package/dist/agents/QueryAgent.js.map +1 -1
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +19 -0
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/acp/update.d.ts.map +1 -1
- package/dist/bridge/acp/update.js +1 -0
- package/dist/bridge/acp/update.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +28 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/constants/continuation.d.ts +9 -0
- package/dist/constants/continuation.d.ts.map +1 -1
- package/dist/constants/continuation.js +9 -0
- package/dist/constants/continuation.js.map +1 -1
- package/dist/contracts/session/api.d.ts +8 -2
- package/dist/contracts/session/api.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.d.ts +7 -1
- package/dist/manager/session/turn-recorder.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.js +20 -0
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/peers/dir.d.ts.map +1 -1
- package/dist/peers/dir.js +3 -0
- package/dist/peers/dir.js.map +1 -1
- package/dist/provider/collect-chat-completion.d.ts.map +1 -1
- package/dist/provider/collect-chat-completion.js +4 -0
- package/dist/provider/collect-chat-completion.js.map +1 -1
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +11 -0
- package/dist/provider/fallback.js.map +1 -1
- package/dist/provider/idle-timeout.d.ts.map +1 -1
- package/dist/provider/idle-timeout.js +5 -0
- package/dist/provider/idle-timeout.js.map +1 -1
- package/dist/provider/mock.d.ts +2 -0
- package/dist/provider/mock.d.ts.map +1 -1
- package/dist/provider/mock.js +16 -8
- package/dist/provider/mock.js.map +1 -1
- package/dist/provider/model-window-suffix.d.ts.map +1 -1
- package/dist/provider/model-window-suffix.js +1 -0
- package/dist/provider/model-window-suffix.js.map +1 -1
- package/dist/provider/retry.d.ts.map +1 -1
- package/dist/provider/retry.js +5 -0
- package/dist/provider/retry.js.map +1 -1
- package/dist/public-runtime.d.ts +1 -0
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +1 -0
- package/dist/public-runtime.js.map +1 -1
- package/dist/runtime/query/events.d.ts.map +1 -1
- package/dist/runtime/query/events.js +17 -0
- package/dist/runtime/query/events.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +23 -0
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +88 -9
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/index.d.ts +14 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +14 -0
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/effort-clamp.d.ts +13 -0
- package/dist/runtime/query/iteration/effort-clamp.d.ts.map +1 -0
- package/dist/runtime/query/iteration/effort-clamp.js +42 -0
- package/dist/runtime/query/iteration/effort-clamp.js.map +1 -0
- package/dist/runtime/query/iteration/index.d.ts +34 -0
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +199 -6
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +2 -0
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +6 -0
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +6 -0
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +12 -2
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +2 -0
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +1 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/sandbox/provider/local.d.ts.map +1 -1
- package/dist/sandbox/provider/local.js +6 -5
- package/dist/sandbox/provider/local.js.map +1 -1
- package/dist/session/paths.d.ts +15 -22
- package/dist/session/paths.d.ts.map +1 -1
- package/dist/session/paths.js +19 -46
- package/dist/session/paths.js.map +1 -1
- package/dist/store/memory/disk.d.ts.map +1 -1
- package/dist/store/memory/disk.js +1 -1
- package/dist/store/memory/disk.js.map +1 -1
- package/dist/store/memory/markdown.d.ts.map +1 -1
- package/dist/store/memory/markdown.js +1 -1
- package/dist/store/memory/markdown.js.map +1 -1
- package/dist/store/memory/operation-lock.d.ts +20 -4
- package/dist/store/memory/operation-lock.d.ts.map +1 -1
- package/dist/store/memory/operation-lock.js +235 -58
- package/dist/store/memory/operation-lock.js.map +1 -1
- package/dist/store/session-log/core.d.ts +43 -0
- package/dist/store/session-log/core.d.ts.map +1 -1
- package/dist/store/session-log/core.js +190 -3
- package/dist/store/session-log/core.js.map +1 -1
- package/dist/store/session-log/disk.d.ts +1 -1
- package/dist/store/session-log/disk.d.ts.map +1 -1
- package/dist/store/session-log/disk.js +1 -0
- package/dist/store/session-log/disk.js.map +1 -1
- package/dist/store/session-log/index.d.ts +1 -1
- package/dist/store/session-log/index.d.ts.map +1 -1
- package/dist/store/session-log/index.js +1 -1
- package/dist/store/session-log/index.js.map +1 -1
- package/dist/tools/builtins/bash-cwd.d.ts.map +1 -1
- package/dist/tools/builtins/bash-cwd.js +2 -3
- package/dist/tools/builtins/bash-cwd.js.map +1 -1
- package/dist/tools/builtins/read-media.js +3 -3
- package/dist/tools/builtins/read-media.js.map +1 -1
- package/dist/turn/reporter.d.ts.map +1 -1
- package/dist/turn/reporter.js +13 -0
- package/dist/turn/reporter.js.map +1 -1
- package/dist/types/agent/query.d.ts +2 -0
- package/dist/types/agent/query.d.ts.map +1 -1
- package/dist/types/hitl/index.d.ts +8 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/message/index.d.ts +1 -1
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +1 -0
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/provider/chat.d.ts +19 -0
- package/dist/types/provider/chat.d.ts.map +1 -1
- package/dist/types/provider/config.d.ts +7 -0
- package/dist/types/provider/config.d.ts.map +1 -1
- package/dist/types/provider/index.d.ts +1 -1
- package/dist/types/provider/index.d.ts.map +1 -1
- package/dist/types/provider/interface.d.ts +15 -0
- package/dist/types/provider/interface.d.ts.map +1 -1
- package/dist/types/provider/stream.d.ts +6 -0
- package/dist/types/provider/stream.d.ts.map +1 -1
- package/dist/types/session/events.d.ts +72 -2
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js +4 -0
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/session/records.d.ts +34 -34
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +5 -0
- package/dist/types/session/records.js.map +1 -1
- package/dist/types/session/stop-reason.d.ts +11 -0
- package/dist/types/session/stop-reason.d.ts.map +1 -1
- package/dist/utils/temp.d.ts +82 -0
- package/dist/utils/temp.d.ts.map +1 -0
- package/dist/utils/temp.js +196 -0
- package/dist/utils/temp.js.map +1 -0
- package/package.json +1 -1
- package/src/agents/QueryAgent.ts +1 -0
- package/src/bridge/a2a/mapper.ts +22 -0
- package/src/bridge/acp/update.ts +6 -1
- package/src/bridge/sse/mapper.ts +29 -0
- package/src/constants/continuation.ts +13 -0
- package/src/contracts/session/api.ts +8 -0
- package/src/manager/session/turn-recorder.ts +22 -0
- package/src/peers/dir.ts +3 -0
- package/src/provider/collect-chat-completion.ts +3 -0
- package/src/provider/fallback.ts +17 -0
- package/src/provider/idle-timeout.ts +9 -0
- package/src/provider/mock.ts +19 -9
- package/src/provider/model-window-suffix.ts +1 -0
- package/src/provider/retry.ts +9 -0
- package/src/public-runtime.ts +13 -0
- package/src/runtime/query/events.ts +19 -0
- package/src/runtime/query/executor.ts +92 -8
- package/src/runtime/query/index.ts +27 -0
- package/src/runtime/query/iteration/effort-clamp.ts +46 -0
- package/src/runtime/query/iteration/index.ts +260 -6
- package/src/runtime/query/iteration/phases/context.ts +2 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +6 -0
- package/src/runtime/query/iteration/stream-turn.ts +5 -0
- package/src/runtime/query/review-policy.ts +13 -2
- package/src/runtime/query/tooling.ts +3 -0
- package/src/sandbox/provider/local.ts +8 -5
- package/src/session/paths.ts +32 -57
- package/src/store/memory/disk.ts +1 -0
- package/src/store/memory/markdown.ts +1 -0
- package/src/store/memory/operation-lock.ts +288 -57
- package/src/store/session-log/core.ts +235 -3
- package/src/store/session-log/disk.ts +7 -1
- package/src/store/session-log/index.ts +4 -0
- package/src/tools/builtins/bash-cwd.ts +2 -3
- package/src/tools/builtins/read-media.ts +3 -3
- package/src/turn/reporter.ts +14 -0
- package/src/types/agent/query.ts +2 -0
- package/src/types/hitl/index.ts +8 -0
- package/src/types/message/index.ts +5 -3
- package/src/types/provider/chat.ts +20 -0
- package/src/types/provider/config.ts +7 -0
- package/src/types/provider/index.ts +1 -0
- package/src/types/provider/interface.ts +20 -0
- package/src/types/provider/stream.ts +6 -0
- package/src/types/session/events.ts +106 -6
- package/src/types/session/records.ts +5 -0
- package/src/types/session/stop-reason.ts +11 -0
- package/src/utils/temp.ts +250 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { SpanStatusCode } from '@opentelemetry/api';
|
|
2
2
|
import { extractFromAssistantMessage, extractFromUserMessage, } from '../../../compaction/extractor.js';
|
|
3
3
|
import { estimateMessageTokens } from '../../../compaction/token-estimate.js';
|
|
4
|
-
import { AUTO_CONTINUATION_USER_MESSAGE, REASONING_CUT_USER_MESSAGE, } from '../../../constants/continuation.js';
|
|
4
|
+
import { AUTO_CONTINUATION_USER_MESSAGE, REASONING_CUT_USER_MESSAGE, REFUSED_REPLY_CALL_RESULT, REFUSED_REPLY_RETRY_MESSAGE, } from '../../../constants/continuation.js';
|
|
5
5
|
import { CHARS_PER_TOKEN } from '../../../constants/limits.js';
|
|
6
6
|
import { DEFAULT_STRUCTURED_OUTPUT_RETRIES, STRUCTURED_OUTPUT_REPROMPT, } from '../../../constants/tools/index.js';
|
|
7
7
|
import { withStreamedMessageIdentity } from '../../../manager/session/streamed-message-identity.js';
|
|
@@ -14,7 +14,7 @@ import { GENAI, NAMZU, agentIterationSpanName, parentContext, } from '../../../t
|
|
|
14
14
|
import { getTracer } from '../../../telemetry/runtime-accessors.js';
|
|
15
15
|
import { STRUCTURED_OUTPUT_TOOL_NAME } from '../../../tools/builtins/structuredOutput.js';
|
|
16
16
|
import { NamzuError } from '../../../types/errors/index.js';
|
|
17
|
-
import { createAssistantMessage, createRuntimeContextMessage, createSystemMessage, } from '../../../types/message/index.js';
|
|
17
|
+
import { createAssistantMessage, createRuntimeContextMessage, createSystemMessage, createToolMessage, } from '../../../types/message/index.js';
|
|
18
18
|
import { classifyProviderError, readOutputOverflow } from '../../../types/provider/errors.js';
|
|
19
19
|
import { toErrorMessage } from '../../../utils/error.js';
|
|
20
20
|
import { stableDigest } from '../../../utils/hash.js';
|
|
@@ -28,6 +28,7 @@ import { applyLifecycleHookResults } from '../plugin-hooks.js';
|
|
|
28
28
|
import { diffRequestContext, snapshotRequestContext, } from '../request-context.js';
|
|
29
29
|
import { DEFAULT_MAX_REQUEST_RICH_CONTENT_BYTES, markProviderRejectedImage, projectRequestRichContent, } from '../request-rich-content.js';
|
|
30
30
|
import { formatSteeringNote, isOperatorUserMessage } from '../steering.js';
|
|
31
|
+
import { effortForModel } from './effort-clamp.js';
|
|
31
32
|
import { parseNativeCandidate } from './native-output.js';
|
|
32
33
|
import { drainArrivedTaskCompletions, holdForOutstandingWork, settleOutstandingWork, } from './outstanding-work.js';
|
|
33
34
|
import { runAdvisoryPhase } from './phases/advisory.js';
|
|
@@ -191,6 +192,16 @@ export class IterationOrchestrator {
|
|
|
191
192
|
stopDeferredForOutstandingWork = false;
|
|
192
193
|
/** Nudges sent since the model last finished a call without hitting its output cap. */
|
|
193
194
|
outputContinuations = 0;
|
|
195
|
+
/**
|
|
196
|
+
* The model a refused request moved to. Once set it replaces the model the
|
|
197
|
+
* step asked for, for the rest of the turn: the request that was refused
|
|
198
|
+
* would only be refused again.
|
|
199
|
+
*/
|
|
200
|
+
refusalPinnedModel;
|
|
201
|
+
/** The request has already been re-run on another model once this turn. */
|
|
202
|
+
refusalFallbackUsed = false;
|
|
203
|
+
/** The request has already been re-run on the same model once this turn. */
|
|
204
|
+
refusalRetryUsed = false;
|
|
194
205
|
/**
|
|
195
206
|
* `turn_end` (and, for a child, `subagent_stop`) fired when the model
|
|
196
207
|
* finished and no hook asked for more. `finalizeTurn` then does not fire
|
|
@@ -637,7 +648,7 @@ export class IterationOrchestrator {
|
|
|
637
648
|
// Preparation inference belongs to the turn, not the main-model step.
|
|
638
649
|
usageBefore = { ...recorder.tokenUsage };
|
|
639
650
|
costBefore = { ...recorder.costInfo };
|
|
640
|
-
stepModel = step.model ?? model;
|
|
651
|
+
stepModel = this.refusalPinnedModel ?? step.model ?? model;
|
|
641
652
|
await selectContextModel(shaping, stepModel);
|
|
642
653
|
// Preserve post-compaction preparation/recall semantics. A changed
|
|
643
654
|
// model needs a second check against its own window; never replay
|
|
@@ -664,7 +675,7 @@ export class IterationOrchestrator {
|
|
|
664
675
|
// still call any of them by name.
|
|
665
676
|
this.ctx.toolExecutor.setStepAllowedTools(stepAllowedTools);
|
|
666
677
|
const enforceToolInputSchema = enforcedModelInputToolNames(this.ctx.tools, llmTools);
|
|
667
|
-
stepModel = step.model ?? model;
|
|
678
|
+
stepModel = this.refusalPinnedModel ?? step.model ?? model;
|
|
668
679
|
// The closing directive is appended LAST, after every piece of
|
|
669
680
|
// request-only context below, so it is the final thing the model
|
|
670
681
|
// reads on a forced-final step.
|
|
@@ -869,6 +880,20 @@ export class IterationOrchestrator {
|
|
|
869
880
|
model: requestedMember.model ?? stepModel,
|
|
870
881
|
chainIndex: requestedMember.index,
|
|
871
882
|
};
|
|
883
|
+
// The turn's effort was chosen for the turn's own model. A request
|
|
884
|
+
// moved to another model (a refusal fallback, a live pick) carries
|
|
885
|
+
// the nearest level that model offers, for this request only.
|
|
886
|
+
let requestEffort = turnConfig.effort;
|
|
887
|
+
if (requestEffort && stepModel !== model) {
|
|
888
|
+
requestEffort = effortForModel(this.ctx.provider, stepModel, requestEffort, turnConfig.thinking);
|
|
889
|
+
if (requestEffort !== turnConfig.effort) {
|
|
890
|
+
this.ctx.log.debug('Effort lowered to a level this model offers', {
|
|
891
|
+
'namzu.runtime.model': stepModel,
|
|
892
|
+
'namzu.runtime.effort_requested': turnConfig.effort,
|
|
893
|
+
'namzu.runtime.effort_used': requestEffort,
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
}
|
|
872
897
|
const operatorInputAtDispatch = this.latestUserMessage;
|
|
873
898
|
const latestReviewUserMessage = (this.ctx.reviewAnswer || this.ctx.structuredOutput?.review) && operatorInputAtDispatch
|
|
874
899
|
? structuredClone(operatorInputAtDispatch)
|
|
@@ -905,7 +930,7 @@ export class IterationOrchestrator {
|
|
|
905
930
|
maxTokens: outputCapOverride ?? step.maxResponseTokens ?? turnConfig.maxResponseTokens,
|
|
906
931
|
cacheControl: { type: 'auto' },
|
|
907
932
|
...(turnConfig.thinking ? { thinking: turnConfig.thinking } : {}),
|
|
908
|
-
...(
|
|
933
|
+
...(requestEffort ? { effort: requestEffort } : {}),
|
|
909
934
|
...(!forceFinalize && turnConfig.webSearch
|
|
910
935
|
? { webSearch: turnConfig.webSearch }
|
|
911
936
|
: {}),
|
|
@@ -1032,6 +1057,34 @@ export class IterationOrchestrator {
|
|
|
1032
1057
|
: {}),
|
|
1033
1058
|
...contextFigures,
|
|
1034
1059
|
});
|
|
1060
|
+
// A reply the provider declined to give is its own case. It is not
|
|
1061
|
+
// an empty completion: asking again with the same history on the
|
|
1062
|
+
// same model is refused again, at the full price of the prompt.
|
|
1063
|
+
// Recovered here, before the reply can enter the history.
|
|
1064
|
+
if (response.finishReason === 'content_filter') {
|
|
1065
|
+
iterSpan.setStatus({ code: SpanStatusCode.OK });
|
|
1066
|
+
if (iterationActivity) {
|
|
1067
|
+
this.ctx.activityStore.complete(iterationActivity.id, {
|
|
1068
|
+
content: response.message.content,
|
|
1069
|
+
hasToolCalls: false,
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
const outcome = yield* this.recoverFromRefusal({
|
|
1073
|
+
response,
|
|
1074
|
+
messageId,
|
|
1075
|
+
stepModel,
|
|
1076
|
+
servedBy,
|
|
1077
|
+
forceFinalize,
|
|
1078
|
+
limitStopReason: forceFinalize ? guardResult.stopReason : undefined,
|
|
1079
|
+
iterationNum,
|
|
1080
|
+
stepStartedAt,
|
|
1081
|
+
usageBefore,
|
|
1082
|
+
costBefore,
|
|
1083
|
+
});
|
|
1084
|
+
if (outcome === 'retry')
|
|
1085
|
+
continue;
|
|
1086
|
+
break;
|
|
1087
|
+
}
|
|
1035
1088
|
// Durable reasoning and its adapter-private replay envelope ride
|
|
1036
1089
|
// with the turn they belong to. Trimming therefore removes both;
|
|
1037
1090
|
// retaining them gives the target adapter enough evidence to
|
|
@@ -1396,7 +1449,7 @@ export class IterationOrchestrator {
|
|
|
1396
1449
|
[NAMZU.ITERATION]: iterationNum,
|
|
1397
1450
|
'namzu.runtime.finish_reason': response.finishReason,
|
|
1398
1451
|
});
|
|
1399
|
-
closingStopReason = await this.requestFinalResponse(model, 'end_turn');
|
|
1452
|
+
closingStopReason = await this.requestFinalResponse(this.refusalPinnedModel ?? model, 'end_turn');
|
|
1400
1453
|
yield* this.ctx.drainPending();
|
|
1401
1454
|
}
|
|
1402
1455
|
await this.ctx.emitEvent({
|
|
@@ -2460,6 +2513,137 @@ export class IterationOrchestrator {
|
|
|
2460
2513
|
providerRejectedImagesSuppressed: repaired.count,
|
|
2461
2514
|
});
|
|
2462
2515
|
}
|
|
2516
|
+
/**
|
|
2517
|
+
* A reply the provider declined to give.
|
|
2518
|
+
*
|
|
2519
|
+
* The refused reply never enters the history sent next, except on the
|
|
2520
|
+
* same-model retry, where the stopped partial reply stays so the model can
|
|
2521
|
+
* see what to avoid. At most one automatic retry of each kind per turn:
|
|
2522
|
+
*
|
|
2523
|
+
* 1. Another model of the same provider, when the provider names one and
|
|
2524
|
+
* the host has not turned `refusalFallback` off. The model is kept for
|
|
2525
|
+
* the rest of the turn and announced in a `model_refusal` event.
|
|
2526
|
+
* 2. The same model once more, only when the refused reply had partial
|
|
2527
|
+
* text or tool calls: unfinished calls get an error result and the
|
|
2528
|
+
* model is told the reply was stopped.
|
|
2529
|
+
* 3. Otherwise the turn settles `refused`.
|
|
2530
|
+
*/
|
|
2531
|
+
async *recoverFromRefusal(input) {
|
|
2532
|
+
const { recorder } = this.ctx;
|
|
2533
|
+
const { response, servedBy, iterationNum } = input;
|
|
2534
|
+
const refusal = response.refusal ?? {};
|
|
2535
|
+
const toolCalls = response.message.toolCalls ?? [];
|
|
2536
|
+
const hasPartial = (response.message.content !== null && response.message.content.length > 0) ||
|
|
2537
|
+
toolCalls.length > 0;
|
|
2538
|
+
this.ctx.log.warn('The model declined to answer', {
|
|
2539
|
+
[NAMZU.TURN_ID]: recorder.turnId,
|
|
2540
|
+
[NAMZU.ITERATION]: iterationNum,
|
|
2541
|
+
'namzu.runtime.model': servedBy.model,
|
|
2542
|
+
'namzu.runtime.refusal_category': refusal.category,
|
|
2543
|
+
'namzu.runtime.has_partial_output': hasPartial,
|
|
2544
|
+
});
|
|
2545
|
+
const announce = async (outcome, fallbackModel) => {
|
|
2546
|
+
await this.ctx.emitEvent({
|
|
2547
|
+
type: 'model_refusal',
|
|
2548
|
+
turnId: recorder.turnId,
|
|
2549
|
+
iteration: iterationNum,
|
|
2550
|
+
model: servedBy.model,
|
|
2551
|
+
providerId: servedBy.providerId,
|
|
2552
|
+
outcome,
|
|
2553
|
+
...(fallbackModel !== undefined ? { fallbackModel } : {}),
|
|
2554
|
+
...(refusal.category ? { category: refusal.category } : {}),
|
|
2555
|
+
...(refusal.explanation ? { explanation: refusal.explanation } : {}),
|
|
2556
|
+
});
|
|
2557
|
+
};
|
|
2558
|
+
const closeIteration = async () => {
|
|
2559
|
+
this.recordStep({
|
|
2560
|
+
stepNumber: iterationNum,
|
|
2561
|
+
model: input.stepModel,
|
|
2562
|
+
servedBy,
|
|
2563
|
+
messageId: input.messageId,
|
|
2564
|
+
response,
|
|
2565
|
+
toolResults: [],
|
|
2566
|
+
toolExecutionMs: 0,
|
|
2567
|
+
startedAt: input.stepStartedAt,
|
|
2568
|
+
usageBefore: input.usageBefore,
|
|
2569
|
+
costBefore: input.costBefore,
|
|
2570
|
+
});
|
|
2571
|
+
await this.ctx.emitEvent({
|
|
2572
|
+
type: 'iteration_completed',
|
|
2573
|
+
turnId: recorder.turnId,
|
|
2574
|
+
iteration: iterationNum,
|
|
2575
|
+
hasToolCalls: false,
|
|
2576
|
+
});
|
|
2577
|
+
};
|
|
2578
|
+
if (this.ctx.abortController.signal.aborted) {
|
|
2579
|
+
recorder.setStopReason('cancelled');
|
|
2580
|
+
recorder.markCancelled();
|
|
2581
|
+
await closeIteration();
|
|
2582
|
+
yield* this.ctx.drainPending();
|
|
2583
|
+
return 'settled';
|
|
2584
|
+
}
|
|
2585
|
+
if (!input.forceFinalize && !this.refusalFallbackUsed && this.ctx.refusalFallback !== 'off') {
|
|
2586
|
+
const next = await this.refusalFallbackModel(input.stepModel, refusal);
|
|
2587
|
+
if (next !== undefined) {
|
|
2588
|
+
this.refusalFallbackUsed = true;
|
|
2589
|
+
this.refusalPinnedModel = next;
|
|
2590
|
+
await announce('fallback', next);
|
|
2591
|
+
await closeIteration();
|
|
2592
|
+
yield* this.ctx.drainPending();
|
|
2593
|
+
return 'retry';
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
if (!input.forceFinalize && !this.refusalRetryUsed && hasPartial) {
|
|
2597
|
+
this.refusalRetryUsed = true;
|
|
2598
|
+
recorder.pushMessage(withStreamedMessageIdentity(createAssistantMessage(response.message.content, toolCalls.length > 0 ? toolCalls : undefined, response.message.reasoning, response.message.citations, {
|
|
2599
|
+
type: 'model',
|
|
2600
|
+
...servedBy,
|
|
2601
|
+
...(response.message.replayState !== undefined
|
|
2602
|
+
? { replayState: response.message.replayState }
|
|
2603
|
+
: {}),
|
|
2604
|
+
}, response.message.textParts), input.messageId));
|
|
2605
|
+
for (const call of toolCalls) {
|
|
2606
|
+
recorder.pushMessage(createToolMessage(REFUSED_REPLY_CALL_RESULT, call.id, true));
|
|
2607
|
+
}
|
|
2608
|
+
recorder.pushMessage(createRuntimeContextMessage(REFUSED_REPLY_RETRY_MESSAGE, 'refusal-retry'));
|
|
2609
|
+
await announce('retry');
|
|
2610
|
+
await closeIteration();
|
|
2611
|
+
yield* this.ctx.drainPending();
|
|
2612
|
+
return 'retry';
|
|
2613
|
+
}
|
|
2614
|
+
recorder.setLastError(`${servedBy.model} declined to answer this message${refusal.explanation ? `: ${refusal.explanation}` : '.'}`);
|
|
2615
|
+
// A closing step that a limit asked for keeps that limit as the reason
|
|
2616
|
+
// the turn ended; the refusal is still announced.
|
|
2617
|
+
recorder.setStopReason(input.limitStopReason ?? 'refused');
|
|
2618
|
+
await announce('declined');
|
|
2619
|
+
await closeIteration();
|
|
2620
|
+
yield* this.ctx.drainPending();
|
|
2621
|
+
return 'settled';
|
|
2622
|
+
}
|
|
2623
|
+
/**
|
|
2624
|
+
* Another model to try after `model` declined, or `undefined` when there is
|
|
2625
|
+
* none. Only the provider can say: it names its own recommendation or its
|
|
2626
|
+
* own declared order, checked against what this credential can serve. A
|
|
2627
|
+
* chain member declared with a model of its own cannot be swapped, because
|
|
2628
|
+
* the chain would put its own model back on the request.
|
|
2629
|
+
*/
|
|
2630
|
+
async refusalFallbackModel(model, refusal) {
|
|
2631
|
+
if (this.ctx.servingMember?.()?.model !== undefined)
|
|
2632
|
+
return undefined;
|
|
2633
|
+
const pick = this.ctx.provider.refusalFallbackFor;
|
|
2634
|
+
if (!pick)
|
|
2635
|
+
return undefined;
|
|
2636
|
+
try {
|
|
2637
|
+
const next = await pick.call(this.ctx.provider, model, refusal, this.ctx.abortController.signal);
|
|
2638
|
+
return next !== undefined && next.length > 0 && next !== model ? next : undefined;
|
|
2639
|
+
}
|
|
2640
|
+
catch (err) {
|
|
2641
|
+
this.ctx.log.warn('The provider could not name a model to retry a refusal on', {
|
|
2642
|
+
'exception.message': toErrorMessage(err),
|
|
2643
|
+
});
|
|
2644
|
+
return undefined;
|
|
2645
|
+
}
|
|
2646
|
+
}
|
|
2463
2647
|
async requestFinalResponse(model, reason) {
|
|
2464
2648
|
if (this.ctx.structuredOutput?.mode === 'native')
|
|
2465
2649
|
return 'structured_output_failed';
|
|
@@ -2537,6 +2721,15 @@ export class IterationOrchestrator {
|
|
|
2537
2721
|
providerId: servedBy.providerId,
|
|
2538
2722
|
model: servedBy.model,
|
|
2539
2723
|
});
|
|
2724
|
+
// A closing request the provider declined has nothing to add, and its
|
|
2725
|
+
// empty reply must not be stored or sent again.
|
|
2726
|
+
if (response.finishReason === 'content_filter') {
|
|
2727
|
+
this.ctx.log.warn('The closing response was declined by the model', {
|
|
2728
|
+
[NAMZU.TURN_ID]: this.ctx.recorder.turnId,
|
|
2729
|
+
'namzu.runtime.model': servedBy.model,
|
|
2730
|
+
});
|
|
2731
|
+
return undefined;
|
|
2732
|
+
}
|
|
2540
2733
|
const finalMessageId = generateMessageId();
|
|
2541
2734
|
const assistantMsg = withStreamedMessageIdentity(createAssistantMessage(response.message.content, undefined, response.message.reasoning, response.message.citations, {
|
|
2542
2735
|
type: 'model',
|