@armadra/agent 0.7.3 → 0.7.5
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 +48 -0
- package/CHANGELOG.zh-CN.md +48 -0
- package/dist/agent/retry.d.ts +14 -5
- package/dist/agent/retry.js +30 -8
- package/dist/agent/session-cache-key.d.ts +8 -0
- package/dist/agent/session-cache-key.js +46 -0
- package/dist/agent/session-cache.d.ts +2 -3
- package/dist/agent/session-cache.js +19 -37
- package/dist/agent/session-classifier.d.ts +3 -2
- package/dist/agent/session-classifier.js +36 -10
- package/dist/agent/session-compaction.d.ts +24 -5
- package/dist/agent/session-compaction.js +54 -20
- package/dist/agent/session-core.d.ts +2 -0
- package/dist/agent/session-run.d.ts +12 -0
- package/dist/agent/session-run.js +29 -10
- package/dist/agent/session-subagent.d.ts +8 -1
- package/dist/agent/session-subagent.js +37 -14
- package/dist/agent/session-sync.d.ts +8 -2
- package/dist/agent/session-sync.js +7 -10
- package/dist/agent/session-tools.js +3 -2
- package/dist/agent/subagent-fork.d.ts +67 -0
- package/dist/agent/subagent-fork.js +87 -0
- package/dist/agent/subagent-registry.d.ts +2 -2
- package/dist/agent/subagent-registry.js +2 -2
- package/dist/agent/tool-availability.d.ts +8 -0
- package/dist/agent/tool-availability.js +8 -0
- package/dist/agent/tool-runner.js +13 -4
- package/dist/agent/worktree.d.ts +8 -0
- package/dist/agent/worktree.js +51 -4
- package/dist/agents/parse.js +6 -1
- package/dist/agents/task-record.js +3 -0
- package/dist/agents/types.d.ts +2 -0
- package/dist/ai/apis/anthropic-messages.js +10 -5
- package/dist/ai/apis/anthropic-request.d.ts +2 -1
- package/dist/ai/apis/anthropic-request.js +15 -4
- package/dist/ai/apis/cache-params.js +2 -1
- package/dist/ai/apis/google-generative-ai.js +4 -2
- package/dist/ai/apis/google-request.js +2 -1
- package/dist/ai/apis/max-tokens.d.ts +42 -0
- package/dist/ai/apis/max-tokens.js +121 -0
- package/dist/ai/apis/openai-completions.js +10 -5
- package/dist/ai/apis/openai-request.js +4 -2
- package/dist/ai/apis/openai-responses-request.js +3 -2
- package/dist/ai/apis/openai-responses.js +9 -5
- package/dist/ai/apis/shared.d.ts +3 -1
- package/dist/ai/apis/shared.js +21 -3
- package/dist/ai/cache/fingerprint.d.ts +4 -1
- package/dist/ai/cache/fingerprint.js +18 -1
- package/dist/ai/cache/miss.d.ts +2 -1
- package/dist/ai/cache/miss.js +9 -3
- package/dist/ai/cache/reporting.js +5 -1
- package/dist/ai/cache/types.d.ts +10 -2
- package/dist/ai/cache/warmer.d.ts +6 -1
- package/dist/ai/cache/warmer.js +7 -0
- package/dist/ai/context.d.ts +12 -4
- package/dist/ai/context.js +43 -24
- package/dist/ai/fake/fake-provider.d.ts +8 -1
- package/dist/ai/fake/fake-provider.js +19 -7
- package/dist/ai/fake/fake-script.d.ts +2 -0
- package/dist/ai/http.d.ts +17 -9
- package/dist/ai/http.js +33 -13
- package/dist/ai/image-intern.d.ts +29 -0
- package/dist/ai/image-intern.js +58 -0
- package/dist/ai/json-body.d.ts +63 -0
- package/dist/ai/json-body.js +286 -0
- package/dist/ai/overflow.js +5 -0
- package/dist/ai/providers/catalog-data.js +13 -13
- package/dist/ai/providers/catalog.d.ts +33 -0
- package/dist/ai/providers/catalog.js +105 -6
- package/dist/ai/providers/enrich.d.ts +13 -3
- package/dist/ai/providers/enrich.js +87 -31
- package/dist/ai/sse.d.ts +3 -2
- package/dist/ai/sse.js +3 -2
- package/dist/ai/types.d.ts +9 -2
- package/dist/bundle/ama.cjs +3637 -1973
- package/dist/cli/bootstrap.js +13 -5
- package/dist/cli/compose-agents.js +3 -0
- package/dist/cli/compose-extensions.js +2 -0
- package/dist/cli/compose-request.d.ts +20 -0
- package/dist/cli/compose-request.js +37 -0
- package/dist/cli/compose-session.js +2 -2
- package/dist/cli/deps.d.ts +1 -1
- package/dist/cli/subcommands/model-meta.d.ts +2 -1
- package/dist/cli/subcommands/model-meta.js +3 -2
- package/dist/codemode/host-side.d.ts +11 -4
- package/dist/codemode/host-side.js +25 -6
- package/dist/codemode/tool.d.ts +2 -0
- package/dist/codemode/tool.js +4 -0
- package/dist/config/json-schema.js +9 -2
- package/dist/config/key-docs.js +13 -4
- package/dist/config/schema-w5.js +9 -1
- package/dist/config/schema.d.ts +1 -1
- package/dist/config/schema.js +19 -3
- package/dist/config/settings-registry.js +6 -0
- package/dist/config/types-w5.d.ts +2 -0
- package/dist/config/types.d.ts +19 -1
- package/dist/config/types.js +4 -0
- package/dist/i18n/catalog.d.ts +13 -2
- package/dist/i18n/messages/cli.d.ts +2 -0
- package/dist/i18n/messages/cli.js +2 -0
- package/dist/i18n/messages/config-keys.d.ts +10 -0
- package/dist/i18n/messages/config-keys.js +12 -2
- package/dist/i18n/messages/config.d.ts +14 -0
- package/dist/i18n/messages/config.js +4 -0
- package/dist/i18n/messages/errors.d.ts +3 -2
- package/dist/i18n/messages/errors.js +15 -2
- package/dist/i18n/messages/report.d.ts +2 -0
- package/dist/i18n/messages/report.js +2 -0
- package/dist/i18n/messages/settings.d.ts +4 -0
- package/dist/i18n/messages/settings.js +6 -2
- package/dist/i18n/messages/subcommands.d.ts +2 -2
- package/dist/i18n/messages/subcommands.js +4 -2
- package/dist/modes/acp/acp-server.js +30 -18
- package/dist/modes/acp/acp-sessions.d.ts +22 -0
- package/dist/modes/acp/acp-sessions.js +68 -0
- package/dist/modes/context-report.js +3 -2
- package/dist/modes/print/json-event.d.ts +12 -1
- package/dist/modes/print/json-event.js +48 -1
- package/dist/modes/rpc/rpc-mode.d.ts +1 -0
- package/dist/modes/rpc/rpc-mode.js +9 -2
- package/dist/modes/session-report.d.ts +1 -1
- package/dist/modes/session-report.js +12 -6
- package/dist/rpc.d.ts +5 -2
- package/dist/session/line-reader.d.ts +28 -0
- package/dist/session/line-reader.js +82 -0
- package/dist/session/list.d.ts +14 -0
- package/dist/session/list.js +150 -0
- package/dist/session/manager.d.ts +23 -3
- package/dist/session/manager.js +91 -62
- package/dist/session/migrate.d.ts +2 -1
- package/dist/session/migrate.js +2 -1
- package/dist/session/offload.d.ts +51 -0
- package/dist/session/offload.js +195 -0
- package/dist/session/projection.d.ts +16 -3
- package/dist/session/projection.js +96 -29
- package/dist/session/scan.d.ts +3 -3
- package/dist/session/scan.js +9 -18
- package/dist/session/store.d.ts +15 -4
- package/dist/session/store.js +54 -38
- package/dist/session/types.d.ts +7 -2
- package/dist/tools/bash.d.ts +1 -1
- package/dist/tools/bash.js +5 -4
- package/dist/tools/glob.d.ts +2 -2
- package/dist/tools/glob.js +2 -2
- package/dist/tools/grep.js +4 -6
- package/dist/tools/image-file.js +2 -1
- package/dist/tools/read-lines.d.ts +71 -0
- package/dist/tools/read-lines.js +224 -0
- package/dist/tools/read.d.ts +6 -2
- package/dist/tools/read.js +47 -27
- package/dist/tools/task.d.ts +9 -0
- package/dist/tools/task.js +21 -8
- package/dist/tools/truncate.d.ts +7 -0
- package/dist/tools/truncate.js +11 -0
- package/dist/tools/types.d.ts +13 -0
- package/dist/trace/build-index.js +13 -1
- package/dist/tui/ansi.d.ts +2 -0
- package/dist/tui/ansi.js +7 -3
- package/dist/tui/components/editor-buffer.js +2 -2
- package/dist/tui/components/editor.js +5 -6
- package/dist/tui/keys.js +2 -2
- package/dist/tui/stdin-buffer.js +3 -3
- package/docs/acp.md +8 -1
- package/docs/agents.md +42 -6
- package/docs/codemode.md +4 -2
- package/docs/en/acp.md +3 -2
- package/docs/en/host-api.md +1 -1
- package/docs/en/permissions.md +2 -2
- package/docs/en/providers.md +54 -20
- package/docs/en/rpc.md +23 -7
- package/docs/en/sessions.md +4 -2
- package/docs/en/tui.md +2 -2
- package/docs/permissions.md +4 -1
- package/docs/providers.md +85 -22
- package/docs/rpc.md +22 -6
- package/docs/session-format.md +13 -2
- package/docs/sessions.md +3 -1
- package/docs/tui.md +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `max_tokens` 的主动收紧与被动修正(docs/model-efficiency-plan.md §1.9、D9)。[ME-C]
|
|
3
|
+
*
|
|
4
|
+
* - 主动收紧:窗口已知时 `max_tokens = min(请求值, max(MIN_OUTPUT_TOKENS, 窗口 − 估算输入 − 余量))`,
|
|
5
|
+
* 输入按请求体字符 / 4 估算(本模块自带,不依赖 compaction/)。预算型思考(`fixed`)不收紧:
|
|
6
|
+
* 预算由 max_tokens 推导,改它会让消息缓存失效。
|
|
7
|
+
* - 被动修正:端点以 400 拒收并说出上限(「max_tokens 范围」类文案)时,记入进程级 `maxTokensCaps`、
|
|
8
|
+
* 以上限重发一次;之后同一模型的请求直接用上限。Anthropic「输入 + max_tokens 超出上下文」只按这次
|
|
9
|
+
* 的输入算出可用值重发、不记上限;可用值不足 `MIN_OUTPUT_TOKENS` 判为溢出(overflow.ts 认得该文案)。
|
|
10
|
+
* 重发只发生在 `start` 之前,流契约不变(与 `postWithCacheFallback` 同模式,包在它外层)。
|
|
11
|
+
*/
|
|
12
|
+
import { HttpError, errorText } from "../http.js";
|
|
13
|
+
import { postWithCacheFallback } from "./cache-params.js";
|
|
14
|
+
/** 主动收紧的下限:再小就不如让请求原样发出、由被动修正或溢出处理接手。 */
|
|
15
|
+
export const MIN_OUTPUT_TOKENS = 1024;
|
|
16
|
+
/** 窗口里给估算误差留的余量。 */
|
|
17
|
+
export const OUTPUT_HEADROOM_TOKENS = 2048;
|
|
18
|
+
/** `${provider}/${model}` → 已知上限(被动修正时记入,之后的请求直接用)。 */
|
|
19
|
+
export const maxTokensCaps = new Map();
|
|
20
|
+
/** 主动收紧后的 max_tokens;`window` 未知或 `fixed`(预算型思考)时返回 requested。 */
|
|
21
|
+
export function clampMaxTokens(requested, window, estimatedInput, fixed) {
|
|
22
|
+
if (fixed || window === undefined || !Number.isFinite(window) || window <= 0)
|
|
23
|
+
return requested;
|
|
24
|
+
const room = Math.floor(window - estimatedInput - OUTPUT_HEADROOM_TOKENS);
|
|
25
|
+
return Math.min(requested, Math.max(MIN_OUTPUT_TOKENS, room));
|
|
26
|
+
}
|
|
27
|
+
/** 请求体的输入 token 估算:JSON 字符数 / 4(CJK 偏低,由被动修正兜底)。 */
|
|
28
|
+
export function estimateInputTokens(body) {
|
|
29
|
+
try {
|
|
30
|
+
return Math.ceil(JSON.stringify(body).length / 4);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return 0;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* 就地收紧 `holder[field]`(缺省 holder = body;Google 的 `generationConfig.maxOutputTokens`
|
|
38
|
+
* 传 holder)。字段不是数字时不动。
|
|
39
|
+
*/
|
|
40
|
+
export function clampRequestMaxTokens(body, field, window, fixed = false, holder = body) {
|
|
41
|
+
const requested = holder[field];
|
|
42
|
+
if (typeof requested !== "number" || fixed || window === undefined)
|
|
43
|
+
return;
|
|
44
|
+
holder[field] = clampMaxTokens(requested, window, estimateInputTokens(body), false);
|
|
45
|
+
}
|
|
46
|
+
/** Google `generationConfig.maxOutputTokens`;带正的思考预算时不收紧(预算按回答上限留白算出)。 */
|
|
47
|
+
export function clampGoogleMaxTokens(body, window) {
|
|
48
|
+
const config = body["generationConfig"];
|
|
49
|
+
if (typeof config !== "object" || config === null)
|
|
50
|
+
return;
|
|
51
|
+
const thinking = config["thinkingConfig"];
|
|
52
|
+
const budget = typeof thinking === "object" && thinking !== null
|
|
53
|
+
? thinking["thinkingBudget"]
|
|
54
|
+
: undefined;
|
|
55
|
+
const fixed = typeof budget === "number" && budget > 0;
|
|
56
|
+
clampRequestMaxTokens(body, "maxOutputTokens", window, fixed, config);
|
|
57
|
+
}
|
|
58
|
+
const NUM = "`?(\\d[\\d,_]*)`?";
|
|
59
|
+
const toInt = (text) => Number((text ?? "").replace(/[,_]/g, ""));
|
|
60
|
+
const RANGE = new RegExp(`range of \`?max_\\w*tokens\`?\\s*(?:should be|is|must be)?\\s*[[(]\\s*\\d+\\s*,\\s*${NUM}\\s*[\\])]`, "i");
|
|
61
|
+
const LIMIT = new RegExp(`max_\\w*tokens\`?[^\\n]{0,200}?(?:must be|should be|less than or equal to|at most|<=)\\s*${NUM}`, "i");
|
|
62
|
+
const CONTEXT_LIMIT = /input length and `?max_tokens`? exceed context limit:?\s*(\d+)\s*\+\s*(\d+)\s*>\s*(\d+)/i;
|
|
63
|
+
function classify(error) {
|
|
64
|
+
if (error instanceof HttpError && error.status !== 400 && error.status !== 422)
|
|
65
|
+
return undefined;
|
|
66
|
+
const text = errorText(error);
|
|
67
|
+
const context = CONTEXT_LIMIT.exec(text);
|
|
68
|
+
if (context) {
|
|
69
|
+
const cap = toInt(context[3]) - toInt(context[1]);
|
|
70
|
+
return cap < MIN_OUTPUT_TOKENS ? { overflow: true } : { cap, transient: true };
|
|
71
|
+
}
|
|
72
|
+
const match = RANGE.exec(text) ?? LIMIT.exec(text);
|
|
73
|
+
if (!match)
|
|
74
|
+
return undefined;
|
|
75
|
+
const cap = toInt(match[1]);
|
|
76
|
+
return Number.isFinite(cap) && cap >= 1 ? { cap, transient: false } : undefined;
|
|
77
|
+
}
|
|
78
|
+
/** 400 文案里的上限;`{ cap }` 可重发,`{ overflow: true }` 判溢出,undefined 不是本类错误。 */
|
|
79
|
+
export function parseMaxTokensRejection(error) {
|
|
80
|
+
const rejection = classify(error);
|
|
81
|
+
return rejection === undefined || "overflow" in rejection ? rejection : { cap: rejection.cap };
|
|
82
|
+
}
|
|
83
|
+
/** body[field] 超过 cap 时返回改小后的浅拷贝,否则原样返回。 */
|
|
84
|
+
function withCap(body, field, cap) {
|
|
85
|
+
if (cap === undefined || typeof body !== "object" || body === null)
|
|
86
|
+
return body;
|
|
87
|
+
const value = body[field];
|
|
88
|
+
return typeof value === "number" && value > cap ? { ...body, [field]: cap } : body;
|
|
89
|
+
}
|
|
90
|
+
/** Anthropic 预算型思考:budget_tokens 必须小于 max_tokens,上限压不下时不重发。 */
|
|
91
|
+
function budgetFits(body, cap) {
|
|
92
|
+
const thinking = body["thinking"];
|
|
93
|
+
if (typeof thinking !== "object" || thinking === null)
|
|
94
|
+
return true;
|
|
95
|
+
const budget = thinking["budget_tokens"];
|
|
96
|
+
return typeof budget !== "number" || budget < cap;
|
|
97
|
+
}
|
|
98
|
+
/** `postWithCacheFallback` 之外再包一层:max_tokens 超上限的 400 以收紧值重发一次(只在 `start` 之前)。 */
|
|
99
|
+
export async function postWithMaxTokensFallback(model, url, options, field) {
|
|
100
|
+
const key = `${model.provider}/${model.id}`;
|
|
101
|
+
const body = withCap(options.body, field, maxTokensCaps.get(key));
|
|
102
|
+
try {
|
|
103
|
+
return await postWithCacheFallback(model, url, { ...options, body });
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
if (options.signal.aborted || typeof body !== "object" || body === null)
|
|
107
|
+
throw error;
|
|
108
|
+
const rejection = classify(error);
|
|
109
|
+
const sent = body[field];
|
|
110
|
+
if (rejection === undefined || !("cap" in rejection) || typeof sent !== "number")
|
|
111
|
+
throw error;
|
|
112
|
+
if (rejection.cap >= sent || !budgetFits(body, rejection.cap))
|
|
113
|
+
throw error;
|
|
114
|
+
if (!rejection.transient)
|
|
115
|
+
maxTokensCaps.set(key, rejection.cap);
|
|
116
|
+
return postWithCacheFallback(model, url, {
|
|
117
|
+
...options,
|
|
118
|
+
body: withCap(body, field, rejection.cap),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
* 内容推断。
|
|
15
15
|
*/
|
|
16
16
|
import { AssistantEventStreamImpl } from "../event-stream.js";
|
|
17
|
-
import { authHeaders, describeErrorJson, idleTimeoutOf, joinUrl, mergeHeaders, USER_AGENT, } from "../http.js";
|
|
17
|
+
import { authHeaders, describeErrorJson, idleTimeoutOf, streamIdleTimeoutOf, joinUrl, mergeHeaders, USER_AGENT, } from "../http.js";
|
|
18
18
|
import { readSseEvents } from "../sse.js";
|
|
19
|
-
import { affinityHeaders,
|
|
19
|
+
import { affinityHeaders, resolveCacheRetention } from "./cache-params.js";
|
|
20
|
+
import { clampRequestMaxTokens, postWithMaxTokensFallback } from "./max-tokens.js";
|
|
20
21
|
import { detectCompat } from "./openai-compat.js";
|
|
21
22
|
import { REASONING_FIELDS, buildOpenAIRequest } from "./openai-request.js";
|
|
22
23
|
import { BlockTracker, STREAM_ENDED_MESSAGE, ProviderStopError, createOutput, finishDone, finishError, requireApiKey, } from "./shared.js";
|
|
@@ -183,16 +184,20 @@ async function run(stream, model, context, options) {
|
|
|
183
184
|
if (request.providerThinkingLevel !== undefined) {
|
|
184
185
|
tracker.output.providerThinkingLevel = request.providerThinkingLevel;
|
|
185
186
|
}
|
|
187
|
+
const field = request.compat.maxTokensField;
|
|
188
|
+
clampRequestMaxTokens(request.body, field, model.contextWindow);
|
|
186
189
|
const replaced = options.onPayload?.(request.body);
|
|
187
190
|
const baseUrl = model.baseUrl ?? "https://api.openai.com/v1";
|
|
188
|
-
const
|
|
191
|
+
const url = joinUrl(baseUrl, "/chat/completions");
|
|
192
|
+
const post = {
|
|
189
193
|
headers: buildHeaders(model, options),
|
|
190
194
|
body: replaced === undefined ? request.body : replaced,
|
|
191
195
|
signal: options.signal,
|
|
192
196
|
timeoutMs: options.timeoutMs,
|
|
193
197
|
idleTimeoutMs: idleTimeoutOf(options),
|
|
194
198
|
onResponse: options.onResponse,
|
|
195
|
-
}
|
|
199
|
+
};
|
|
200
|
+
const response = await postWithMaxTokensFallback(model, url, post, field);
|
|
196
201
|
stream.push({ type: "start", partial: tracker.output });
|
|
197
202
|
const state = {
|
|
198
203
|
text: undefined,
|
|
@@ -204,7 +209,7 @@ async function run(stream, model, context, options) {
|
|
|
204
209
|
sawDone: false,
|
|
205
210
|
};
|
|
206
211
|
const body = response.body;
|
|
207
|
-
for await (const sse of readSseEvents(body, options.signal,
|
|
212
|
+
for await (const sse of readSseEvents(body, options.signal, streamIdleTimeoutOf(options))) {
|
|
208
213
|
const data = sse.data.trim();
|
|
209
214
|
if (data === "")
|
|
210
215
|
continue;
|
|
@@ -19,12 +19,14 @@ import { contentText, normalizeContext, normalizeContextInline, sanitizeText, }
|
|
|
19
19
|
import { DEFAULT_THINKING_BUDGETS, clampBudgetToAnswerRoom, clampThinkingLevel, mappedThinkingValue, } from "../thinking.js";
|
|
20
20
|
import { cacheControlFor } from "./anthropic-request.js";
|
|
21
21
|
import { effectiveRetention, resolveCacheRetention, resolvePromptCacheCompat, } from "./cache-params.js";
|
|
22
|
+
import { largeString } from "../json-body.js";
|
|
22
23
|
import { detectCompat } from "./openai-compat.js";
|
|
23
24
|
/** 思考文本回放用的字段名;thinkingSignature 记录来源字段,回放时原样放回。 */
|
|
24
25
|
export const REASONING_FIELDS = ["reasoning_content", "reasoning", "reasoning_text"];
|
|
25
26
|
export const ASSISTANT_BRIDGE_TEXT = "Tool results received.";
|
|
26
27
|
function imagePart(block) {
|
|
27
|
-
|
|
28
|
+
const url = largeString(`data:${block.mimeType};base64,`, block, block.data);
|
|
29
|
+
return { type: "image_url", image_url: { url } };
|
|
28
30
|
}
|
|
29
31
|
function convertUser(message) {
|
|
30
32
|
if (typeof message.content === "string") {
|
|
@@ -67,7 +69,7 @@ function convertAssistant(model, message, compat) {
|
|
|
67
69
|
out["tool_calls"] = calls.map((call) => ({
|
|
68
70
|
id: normalizeCallId(model, call.id),
|
|
69
71
|
type: "function",
|
|
70
|
-
function: { name: call.name, arguments: JSON.stringify(call.arguments) },
|
|
72
|
+
function: { name: call.name, arguments: call.rawArguments ?? JSON.stringify(call.arguments) },
|
|
71
73
|
}));
|
|
72
74
|
}
|
|
73
75
|
if (out["content"] === null && calls.length === 0)
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
* - [W6-O] compat `chatgptBackend`(ChatGPT 订阅后端):最后经 chatgpt-backend.ts 按后端白名单改写。
|
|
22
22
|
*/
|
|
23
23
|
import { contentText, normalizeContext, sanitizeText } from "../context.js";
|
|
24
|
+
import { largeString } from "../json-body.js";
|
|
24
25
|
import { clampThinkingLevel, mappedThinkingValue } from "../thinking.js";
|
|
25
26
|
import { resolveCacheRetention, resolvePromptCacheCompat } from "./cache-params.js";
|
|
26
27
|
import { transformChatGptBody } from "./chatgpt-backend.js";
|
|
@@ -110,7 +111,7 @@ function imagePart(block) {
|
|
|
110
111
|
return {
|
|
111
112
|
type: "input_image",
|
|
112
113
|
detail: "auto",
|
|
113
|
-
image_url: `data:${block.mimeType};base64
|
|
114
|
+
image_url: largeString(`data:${block.mimeType};base64,`, block, block.data),
|
|
114
115
|
};
|
|
115
116
|
}
|
|
116
117
|
function convertUser(message) {
|
|
@@ -162,7 +163,7 @@ function convertAssistant(model, message) {
|
|
|
162
163
|
...(sameModel && itemId?.startsWith("fc") ? { id: itemId } : {}),
|
|
163
164
|
call_id: block.id,
|
|
164
165
|
name: block.name,
|
|
165
|
-
arguments: JSON.stringify(block.arguments),
|
|
166
|
+
arguments: block.rawArguments ?? JSON.stringify(block.arguments),
|
|
166
167
|
});
|
|
167
168
|
}
|
|
168
169
|
}
|
|
@@ -17,9 +17,10 @@
|
|
|
17
17
|
* 错误映射与订阅用量见 chatgpt-backend.ts。
|
|
18
18
|
*/
|
|
19
19
|
import { AssistantEventStreamImpl } from "../event-stream.js";
|
|
20
|
-
import { HttpError, authHeaders, describeErrorJson, idleTimeoutOf, joinUrl, mergeHeaders, USER_AGENT, } from "../http.js";
|
|
20
|
+
import { HttpError, authHeaders, describeErrorJson, idleTimeoutOf, streamIdleTimeoutOf, joinUrl, mergeHeaders, USER_AGENT, } from "../http.js";
|
|
21
21
|
import { readSseEvents } from "../sse.js";
|
|
22
|
-
import { affinityHeaders,
|
|
22
|
+
import { affinityHeaders, resolveCacheRetention } from "./cache-params.js";
|
|
23
|
+
import { clampRequestMaxTokens, postWithMaxTokensFallback } from "./max-tokens.js";
|
|
23
24
|
import { chatgptHeaders, chatgptPrecheck, handleChatGptEvent, subscriptionModel, quotaOnResponse, recoverChatGptError, } from "./chatgpt-backend.js";
|
|
24
25
|
import { buildResponsesRequest, detectResponsesCompat, encodeMessageSignature, } from "./openai-responses-request.js";
|
|
25
26
|
import { BlockTracker, ProviderStopError, STREAM_ENDED_MESSAGE, createOutput, finishDone, finishError, requireApiKey, } from "./shared.js";
|
|
@@ -251,17 +252,20 @@ async function post(model, context, initial, tracker) {
|
|
|
251
252
|
const chatgpt = compat.chatgptBackend !== undefined;
|
|
252
253
|
if (chatgpt)
|
|
253
254
|
chatgptPrecheck(model, compat, options.apiKey);
|
|
255
|
+
clampRequestMaxTokens(request.body, "max_output_tokens", model.contextWindow);
|
|
254
256
|
const replaced = options.onPayload?.(request.body);
|
|
255
257
|
const baseUrl = model.baseUrl ?? "https://api.openai.com/v1";
|
|
256
258
|
try {
|
|
257
|
-
const
|
|
259
|
+
const url = joinUrl(baseUrl, "/responses");
|
|
260
|
+
const post = {
|
|
258
261
|
headers: buildHeaders(model, options, compat),
|
|
259
262
|
body: replaced === undefined ? request.body : replaced,
|
|
260
263
|
signal: options.signal,
|
|
261
264
|
timeoutMs: options.timeoutMs,
|
|
262
265
|
idleTimeoutMs: idleTimeoutOf(options),
|
|
263
266
|
onResponse: chatgpt ? quotaOnResponse(compat, options) : options.onResponse,
|
|
264
|
-
}
|
|
267
|
+
};
|
|
268
|
+
const response = await postWithMaxTokensFallback(model, url, post, "max_output_tokens");
|
|
265
269
|
return { response, options, compat };
|
|
266
270
|
}
|
|
267
271
|
catch (error) {
|
|
@@ -292,7 +296,7 @@ async function run(stream, model, context, options) {
|
|
|
292
296
|
};
|
|
293
297
|
let finished = false;
|
|
294
298
|
const body = response.body;
|
|
295
|
-
for await (const sse of readSseEvents(body, options.signal,
|
|
299
|
+
for await (const sse of readSseEvents(body, options.signal, streamIdleTimeoutOf(options))) {
|
|
296
300
|
const raw = sse.data.trim();
|
|
297
301
|
if (raw === "" || raw === "[DONE]")
|
|
298
302
|
continue;
|
package/dist/ai/apis/shared.d.ts
CHANGED
|
@@ -5,7 +5,9 @@
|
|
|
5
5
|
* - 每个 `*_start` 恰好对应一个 `*_end`;终止事件之前所有打开的块都会被关闭(出错 / 中止时也
|
|
6
6
|
* 先关块再发 error,消费者不必处理悬空块);
|
|
7
7
|
* - `toolcall_end` 时 `toolCall.arguments` 一定是对象(容错解析,坏 JSON 退化为 `{}`);
|
|
8
|
-
* - 流式期间 `arguments`
|
|
8
|
+
* - 流式期间 `arguments` 随增量更新为「到目前为止」的部分对象;
|
|
9
|
+
* - [ME-C] 拼接出的参数串能严格 `JSON.parse` 成对象时原样记进 `rawArguments`,同协议回放时逐字节发回;
|
|
10
|
+
* - [ME-C] 失败响应带 `Retry-After` 时写 `retryAfterMs`(会话层退避取两者较大值)。
|
|
9
11
|
*/
|
|
10
12
|
import type { AssistantEventStreamImpl } from "../event-stream.js";
|
|
11
13
|
import type { AssistantMessage, Model, StopReason, StreamOptions, TextBlock, ThinkingBlock, ToolCallBlock } from "../types.js";
|
package/dist/ai/apis/shared.js
CHANGED
|
@@ -5,11 +5,13 @@
|
|
|
5
5
|
* - 每个 `*_start` 恰好对应一个 `*_end`;终止事件之前所有打开的块都会被关闭(出错 / 中止时也
|
|
6
6
|
* 先关块再发 error,消费者不必处理悬空块);
|
|
7
7
|
* - `toolcall_end` 时 `toolCall.arguments` 一定是对象(容错解析,坏 JSON 退化为 `{}`);
|
|
8
|
-
* - 流式期间 `arguments`
|
|
8
|
+
* - 流式期间 `arguments` 随增量更新为「到目前为止」的部分对象;
|
|
9
|
+
* - [ME-C] 拼接出的参数串能严格 `JSON.parse` 成对象时原样记进 `rawArguments`,同协议回放时逐字节发回;
|
|
10
|
+
* - [ME-C] 失败响应带 `Retry-After` 时写 `retryAfterMs`(会话层退避取两者较大值)。
|
|
9
11
|
*/
|
|
10
12
|
import { AmaError } from "../../errors.js";
|
|
11
13
|
import { finalizeUsage, emptyUsage } from "../cost.js";
|
|
12
|
-
import { errorText, hasAuthHeader } from "../http.js";
|
|
14
|
+
import { HttpError, errorText, hasAuthHeader } from "../http.js";
|
|
13
15
|
import { parseToolArguments } from "../json-partial.js";
|
|
14
16
|
export function createOutput(model) {
|
|
15
17
|
return {
|
|
@@ -128,8 +130,11 @@ export class BlockTracker {
|
|
|
128
130
|
this.stream.push({ type: "thinking_end", contentIndex: index, partial: this.output });
|
|
129
131
|
}
|
|
130
132
|
else {
|
|
131
|
-
if (entry.json.length > 0)
|
|
133
|
+
if (entry.json.length > 0) {
|
|
132
134
|
entry.block.arguments = parseToolArguments(entry.json);
|
|
135
|
+
if (isStrictObject(entry.json))
|
|
136
|
+
entry.block.rawArguments = entry.json;
|
|
137
|
+
}
|
|
133
138
|
this.stream.push({
|
|
134
139
|
type: "toolcall_end",
|
|
135
140
|
contentIndex: index,
|
|
@@ -150,6 +155,16 @@ export class BlockTracker {
|
|
|
150
155
|
return this.output.content.length - 1;
|
|
151
156
|
}
|
|
152
157
|
}
|
|
158
|
+
/** 严格 JSON 且是对象(数组 / 标量容错解析后是 `{}`,回放原串会与执行的参数不一致)。 */
|
|
159
|
+
function isStrictObject(json) {
|
|
160
|
+
try {
|
|
161
|
+
const value = JSON.parse(json);
|
|
162
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
153
168
|
export const ABORTED_MESSAGE = "Request was aborted";
|
|
154
169
|
export const STREAM_ENDED_MESSAGE = "Stream ended before completion";
|
|
155
170
|
/** 正常结束:关块、算 usage / cost、发 done。 */
|
|
@@ -167,6 +182,9 @@ export function finishError(stream, tracker, model, error, signal) {
|
|
|
167
182
|
const aborted = signal.aborted;
|
|
168
183
|
output.stopReason = aborted ? "aborted" : "error";
|
|
169
184
|
output.errorMessage = aborted ? ABORTED_MESSAGE : errorText(error);
|
|
185
|
+
if (!aborted && error instanceof HttpError && error.retryAfterMs !== undefined) {
|
|
186
|
+
output.retryAfterMs = error.retryAfterMs;
|
|
187
|
+
}
|
|
170
188
|
finalizeUsage(model, output.usage);
|
|
171
189
|
stream.push({ type: "error", reason: aborted ? "aborted" : "error", message: output });
|
|
172
190
|
}
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* - `system` = sha256(normalizeContext().systemPrompt) 前 64 bit;
|
|
5
5
|
* - `tools` = sha256(JSON.stringify(工具表按名排序)) 前 64 bit;
|
|
6
|
-
* - `model` = `${provider}/${id}`
|
|
6
|
+
* - `model` = `${provider}/${id}` 原文;
|
|
7
|
+
* - `sections` = 折叠后每个非空节的 hash16([ME-B] D15:归因时说出哪一节变了)。
|
|
7
8
|
* 纯函数、零请求;每次真实请求算一次(system + tools 通常 < 20 KB)。未命中归因时与上一条
|
|
8
9
|
* 记录比对,说出「变了什么」。
|
|
9
10
|
*/
|
|
@@ -13,5 +14,7 @@ import type { PrefixFingerprint } from "./types.js";
|
|
|
13
14
|
export declare function hash16(text: string): string;
|
|
14
15
|
export declare function modelKey(model: Pick<Model, "provider" | "id" | "channel"> | ModelRef): string;
|
|
15
16
|
export declare function fingerprintContext(context: TranscriptContext, model: Pick<Model, "provider" | "id">): PrefixFingerprint;
|
|
17
|
+
/** 变化的节名:先按 `cur` 的节顺序列新增 / 改动的,再列 `prev` 里消失的;任一方没有按节指纹 → []。 */
|
|
18
|
+
export declare function changedSections(prev: PrefixFingerprint, cur: PrefixFingerprint): string[];
|
|
16
19
|
/** 两个指纹的差异(按归因顺序:system → tools → model);相同返回 undefined。 */
|
|
17
20
|
export declare function fingerprintChange(prev: PrefixFingerprint, cur: PrefixFingerprint): "system" | "tools" | "model" | undefined;
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* - `system` = sha256(normalizeContext().systemPrompt) 前 64 bit;
|
|
5
5
|
* - `tools` = sha256(JSON.stringify(工具表按名排序)) 前 64 bit;
|
|
6
|
-
* - `model` = `${provider}/${id}`
|
|
6
|
+
* - `model` = `${provider}/${id}` 原文;
|
|
7
|
+
* - `sections` = 折叠后每个非空节的 hash16([ME-B] D15:归因时说出哪一节变了)。
|
|
7
8
|
* 纯函数、零请求;每次真实请求算一次(system + tools 通常 < 20 KB)。未命中归因时与上一条
|
|
8
9
|
* 记录比对,说出「变了什么」。
|
|
9
10
|
*/
|
|
@@ -21,12 +22,28 @@ function sortedTools(tools) {
|
|
|
21
22
|
}
|
|
22
23
|
export function fingerprintContext(context, model) {
|
|
23
24
|
const normalized = normalizeContext(context);
|
|
25
|
+
const sections = {};
|
|
26
|
+
for (const section of normalized.systemSections)
|
|
27
|
+
sections[section.name] = hash16(section.text);
|
|
24
28
|
return {
|
|
25
29
|
system: hash16(normalized.systemPrompt),
|
|
26
30
|
tools: hash16(JSON.stringify(sortedTools(normalized.tools))),
|
|
27
31
|
model: modelKey(model),
|
|
32
|
+
sections,
|
|
28
33
|
};
|
|
29
34
|
}
|
|
35
|
+
/** 变化的节名:先按 `cur` 的节顺序列新增 / 改动的,再列 `prev` 里消失的;任一方没有按节指纹 → []。 */
|
|
36
|
+
export function changedSections(prev, cur) {
|
|
37
|
+
if (prev.sections === undefined || cur.sections === undefined)
|
|
38
|
+
return [];
|
|
39
|
+
const before = prev.sections;
|
|
40
|
+
const after = cur.sections;
|
|
41
|
+
const changed = Object.keys(after).filter((name) => before[name] !== after[name]);
|
|
42
|
+
for (const name of Object.keys(before))
|
|
43
|
+
if (!(name in after))
|
|
44
|
+
changed.push(name);
|
|
45
|
+
return changed;
|
|
46
|
+
}
|
|
30
47
|
/** 两个指纹的差异(按归因顺序:system → tools → model);相同返回 undefined。 */
|
|
31
48
|
export function fingerprintChange(prev, cur) {
|
|
32
49
|
if (prev.system !== cur.system)
|
package/dist/ai/cache/miss.d.ts
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* 4. 成本:用本条实付反推——`(cost.input + cost.cacheWrite) / (input + cacheWrite)` 减去读价
|
|
11
11
|
* (本条有读用实付,否则用目录价);无价格 → `missedCost` 缺省。
|
|
12
12
|
* 5. 重置点(压缩 / 分支摘要 / 档一裁剪之后的首个请求)由调用方清空 prev;模型切换不豁免。
|
|
13
|
-
* 6. 归因:指纹 system / tools 变 → `prefix_changed
|
|
13
|
+
* 6. 归因:指纹 system / tools 变 → `prefix_changed`(system 变时 `detail` 带变化的节名,如
|
|
14
|
+
* `system:hooks,memory`,[ME-B] D15);模型变 → `model_changed`;
|
|
14
15
|
* `idleMs > ttl`(无承诺 TTL 的隐式缓存按 600 s)→ `idle`;task 运行占间隔 ≥ 80% →
|
|
15
16
|
* `subtask`;其余 → `evicted`。
|
|
16
17
|
*/
|
package/dist/ai/cache/miss.js
CHANGED
|
@@ -10,11 +10,12 @@
|
|
|
10
10
|
* 4. 成本:用本条实付反推——`(cost.input + cost.cacheWrite) / (input + cacheWrite)` 减去读价
|
|
11
11
|
* (本条有读用实付,否则用目录价);无价格 → `missedCost` 缺省。
|
|
12
12
|
* 5. 重置点(压缩 / 分支摘要 / 档一裁剪之后的首个请求)由调用方清空 prev;模型切换不豁免。
|
|
13
|
-
* 6. 归因:指纹 system / tools 变 → `prefix_changed
|
|
13
|
+
* 6. 归因:指纹 system / tools 变 → `prefix_changed`(system 变时 `detail` 带变化的节名,如
|
|
14
|
+
* `system:hooks,memory`,[ME-B] D15);模型变 → `model_changed`;
|
|
14
15
|
* `idleMs > ttl`(无承诺 TTL 的隐式缓存按 600 s)→ `idle`;task 运行占间隔 ≥ 80% →
|
|
15
16
|
* `subtask`;其余 → `evicted`。
|
|
16
17
|
*/
|
|
17
|
-
import { fingerprintChange } from "./fingerprint.js";
|
|
18
|
+
import { changedSections, fingerprintChange } from "./fingerprint.js";
|
|
18
19
|
/** 无承诺 TTL 的端点(隐式缓存)按 10 分钟估。 */
|
|
19
20
|
export const IMPLICIT_CACHE_TTL_MS = 600_000;
|
|
20
21
|
/** 没有目录 `minTokens` 时的最小可缓存长度与噪声下限。 */
|
|
@@ -76,10 +77,15 @@ export function detectMiss(prev, cur, ttlMs, options = {}) {
|
|
|
76
77
|
if (cost !== undefined)
|
|
77
78
|
miss.missedCost = cost;
|
|
78
79
|
const change = fingerprintChange(prev.fingerprint, cur.fingerprint);
|
|
79
|
-
if (change === "
|
|
80
|
+
if (change === "tools") {
|
|
80
81
|
miss.reason = "prefix_changed";
|
|
81
82
|
miss.detail = change;
|
|
82
83
|
}
|
|
84
|
+
else if (change === "system") {
|
|
85
|
+
miss.reason = "prefix_changed";
|
|
86
|
+
const names = changedSections(prev.fingerprint, cur.fingerprint);
|
|
87
|
+
miss.detail = names.length > 0 ? `system:${names.join(",")}` : "system";
|
|
88
|
+
}
|
|
83
89
|
else if (change === "model")
|
|
84
90
|
miss.reason = "model_changed";
|
|
85
91
|
else if (idleMs > (ttlMs ?? IMPLICIT_CACHE_TTL_MS))
|
|
@@ -64,7 +64,11 @@ export class CacheReportingTracker {
|
|
|
64
64
|
};
|
|
65
65
|
this.endpoints.set(key, entry);
|
|
66
66
|
const last = entry.last;
|
|
67
|
-
entry.last =
|
|
67
|
+
entry.last = {
|
|
68
|
+
at: record.at,
|
|
69
|
+
promptTokens: record.promptTokens,
|
|
70
|
+
fingerprint: record.fingerprint,
|
|
71
|
+
};
|
|
68
72
|
const { cacheRead, cacheWrite, cacheReported } = record.usage;
|
|
69
73
|
if (cacheRead > 0 && Number.isInteger(cacheRead)) {
|
|
70
74
|
entry.divisor = gcd(entry.divisor, cacheRead);
|
package/dist/ai/cache/types.d.ts
CHANGED
|
@@ -18,6 +18,8 @@ export interface PrefixFingerprint {
|
|
|
18
18
|
tools: string;
|
|
19
19
|
/** `${provider}/${id}`(原文,不哈希)。 */
|
|
20
20
|
model: string;
|
|
21
|
+
/** 节名 → hash16,归因时说出哪一节变了。 */
|
|
22
|
+
sections?: Record<string, string>;
|
|
21
23
|
}
|
|
22
24
|
/** 每次真实请求在会话层记一条(内存,不落盘)。 */
|
|
23
25
|
export interface RequestRecord {
|
|
@@ -36,6 +38,12 @@ export interface RequestRecord {
|
|
|
36
38
|
contextRef: TranscriptContext;
|
|
37
39
|
options: Omit<StreamOptions, "signal">;
|
|
38
40
|
}
|
|
41
|
+
/** [M-A] 端点上一条可比请求的摘要;只存比较需要的字段,不持有转录与回调。 */
|
|
42
|
+
export interface LastRequest {
|
|
43
|
+
at: number;
|
|
44
|
+
promptTokens: number;
|
|
45
|
+
fingerprint: PrefixFingerprint;
|
|
46
|
+
}
|
|
39
47
|
/** 「供应商不报缓存」三态(§1.6);按 `(provider, baseUrl 主机名, model)` 维护。 */
|
|
40
48
|
export type CacheReporting = "unknown" | "reported" | "silent";
|
|
41
49
|
/** 未命中原因(§1.5 第 6 步的归因顺序)。 */
|
|
@@ -47,8 +55,8 @@ export interface CacheMiss {
|
|
|
47
55
|
/** 重计费金额(美元);模型无价格时缺省(显示 `$?`)。 */
|
|
48
56
|
missedCost?: number;
|
|
49
57
|
reason: CacheMissReason;
|
|
50
|
-
/** `prefix_changed`
|
|
51
|
-
detail?: "system" | "tools"
|
|
58
|
+
/** `prefix_changed` 时哪一段变了;[ME-B] system 变时带节名:`system:hooks,memory`。 */
|
|
59
|
+
detail?: "system" | "tools" | `system:${string}`;
|
|
52
60
|
/** 与上一条可比请求的间隔。 */
|
|
53
61
|
idleMs: number;
|
|
54
62
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* - `pause()`:真实请求发出时静默挂起(新请求本身就刷新了缓存);`cancel()` 同时丢弃记录。
|
|
15
15
|
* 只有已排期之后的异常停止(截止、上限、零命中、过期、否决、失败)回调 `onStopped`。
|
|
16
16
|
*/
|
|
17
|
-
import type { AssistantMessage, Model, StreamOptions } from "../types.js";
|
|
17
|
+
import type { Api, AssistantMessage, Model, StreamOptions } from "../types.js";
|
|
18
18
|
import type { CacheReporting, RequestRecord, WarmDecision, WarmerStatus, WarmingMode, WarmingPhase } from "./types.js";
|
|
19
19
|
export declare const WARM_MIN_TTL_MS = 10000;
|
|
20
20
|
export declare const WARM_STREAMING_CAP_MS: number;
|
|
@@ -42,6 +42,11 @@ export interface WarmerDeps {
|
|
|
42
42
|
onScheduled?(nextWarmAt: number): void;
|
|
43
43
|
onStopped(reason: string): void;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* 保温重放的输出上限([ME-B] D17):Responses 的 `max_output_tokens` 下限是 16,其余协议 1。
|
|
47
|
+
* 官方下限无法实测,按文档取值;中转同样适用。
|
|
48
|
+
*/
|
|
49
|
+
export declare function warmReplayMaxTokens(api: Api): number;
|
|
45
50
|
export declare function warmDelay(ttlMs: number): number;
|
|
46
51
|
export interface ReplayCheck {
|
|
47
52
|
model: Pick<Model, "api" | "reasoning" | "compat">;
|
package/dist/ai/cache/warmer.js
CHANGED
|
@@ -30,6 +30,13 @@ export const REAL_TIMERS = {
|
|
|
30
30
|
clearTimeout(handle);
|
|
31
31
|
},
|
|
32
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* 保温重放的输出上限([ME-B] D17):Responses 的 `max_output_tokens` 下限是 16,其余协议 1。
|
|
35
|
+
* 官方下限无法实测,按文档取值;中转同样适用。
|
|
36
|
+
*/
|
|
37
|
+
export function warmReplayMaxTokens(api) {
|
|
38
|
+
return api === "openai-responses" ? 16 : 1;
|
|
39
|
+
}
|
|
33
40
|
export function warmDelay(ttlMs) {
|
|
34
41
|
return Math.max(1000, Math.floor(Math.min(0.9 * ttlMs, ttlMs - 10_000)));
|
|
35
42
|
}
|
package/dist/ai/context.d.ts
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* 得到 `systemSections`(保持节首次出现的顺序)与拼好的 `systemPrompt`(节间空一行);
|
|
7
7
|
* - 中途节补丁(设计 §9.1「会话中途变化的上下文只追加」):对话开始之后的 system 补丁不改写开头,
|
|
8
8
|
* 渲染成 `<system-reminder>` 包裹的 user 消息按位置插回,开头与之前的消息逐字节不变;
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* - 工具表([ME-B] D5):对话开始前按顺序重放 `toolsRemoved`(先)与 `toolsAdded`(后),同名后者覆盖;
|
|
10
|
+
* 开始之后声明冻结——移除不删声明、只在尾部提醒(执行层拒绝调用),已有名字不覆盖(加回只提醒
|
|
11
|
+
* 「available again」),新名字追加在末尾。开头与工具表在会话内只写一次;
|
|
11
12
|
* - 模态过滤:模型不收图片时,用户消息与工具结果里的图片块换成文字占位。
|
|
12
13
|
*
|
|
13
14
|
* 支持「中途 system 消息」的协议(compat.supportsMidConvoSystemMessages)用
|
|
@@ -20,11 +21,18 @@ export interface NormalizeOptions {
|
|
|
20
21
|
model?: Pick<Model, "input"> | undefined;
|
|
21
22
|
}
|
|
22
23
|
export declare const IMAGE_OMITTED_TEXT = "[image omitted: the model does not accept image input]";
|
|
24
|
+
/** 对话开始后移除工具:声明保留,尾部提醒(固定英文;执行层拒绝文案见 agent/tool-availability.ts)。 */
|
|
25
|
+
export declare const toolRemovedReminder: (name: string) => string;
|
|
26
|
+
/** 移除后又加回(声明沿用首次版本)。 */
|
|
27
|
+
export declare const toolRestoredReminder: (name: string) => string;
|
|
23
28
|
export declare function normalizeContext(context: TranscriptContext, options?: NormalizeOptions): NormalizedContext;
|
|
24
29
|
/** 中途节补丁作为尾部上下文消息送达时的文本(固定英文,与界面语言无关)。 */
|
|
25
30
|
export declare function systemReminderText(updates: readonly string[]): string;
|
|
26
|
-
/**
|
|
27
|
-
|
|
31
|
+
/**
|
|
32
|
+
* 渲染一条中途 system 补丁的文本:节变更全文;移除的工具各一句提醒;`removed` 里的名字被加回时
|
|
33
|
+
* 一句「available again」(新工具的声明由请求工具表末尾体现,不另写)。`removed` 随之更新。
|
|
34
|
+
*/
|
|
35
|
+
export declare function renderSystemUpdate(message: SystemMessage, removed: Set<string>): string;
|
|
28
36
|
export interface InlineSystemUpdate {
|
|
29
37
|
/** 插在 `messages[beforeIndex]` 之前;等于 messages.length 表示放在末尾。 */
|
|
30
38
|
beforeIndex: number;
|