mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-20 17:14:47 +00:00
* refactor: remove stale file-backed shims * fix: harden sqlite state ci boundaries * refactor: store matrix idb snapshots in sqlite * fix: satisfy rebased CI guardrails * refactor: store current conversation bindings in sqlite table * refactor: store tui last sessions in sqlite table * refactor: reset sqlite schema history * refactor: drop unshipped sqlite table migration * refactor: remove plugin index file rollback * refactor: drop unshipped sqlite sidecar migrations * refactor: remove runtime commitments kv migration * refactor: preserve kysely sync result types * refactor: drop unshipped sqlite schema migration table * test: keep session usage coverage sqlite-backed * refactor: keep sqlite migration doctor-only * refactor: isolate device legacy imports * refactor: isolate push voicewake legacy imports * refactor: isolate remaining runtime legacy imports * refactor: tighten sqlite migration guardrails * test: cover sqlite persisted enum parsing * refactor: isolate legacy update and tui imports * refactor: tighten sqlite state ownership * refactor: move legacy imports behind doctor * refactor: remove legacy session row lookup * refactor: canonicalize memory transcript locators * refactor: drop transcript path scope fallbacks * refactor: drop runtime legacy session delivery pruning * refactor: store tts prefs only in sqlite * refactor: remove cron store path runtime * refactor: use cron sqlite store keys * refactor: rename telegram message cache scope * refactor: read memory dreaming status from sqlite * refactor: rename cron status store key * refactor: stop remembering transcript file paths * test: use sqlite locators in agent fixtures * refactor: remove file-shaped commitments and cron store surfaces * refactor: keep compaction transcript handles out of session rows * refactor: derive transcript handles from session identity * refactor: derive runtime transcript handles * refactor: remove gateway session locator reads * refactor: remove transcript locator from session rows * refactor: store raw stream diagnostics in sqlite * refactor: remove file-shaped transcript rotation * refactor: hide legacy trajectory paths from runtime * refactor: remove runtime transcript file bridges * refactor: repair database-first rebase fallout * refactor: align tests with database-first state * refactor: remove transcript file handoffs * refactor: sync post-compaction memory by transcript scope * refactor: run codex app-server sessions by id * refactor: bind codex runtime state by session id * refactor: pass memory transcripts by sqlite scope * refactor: remove transcript locator cleanup leftovers * test: remove stale transcript file fixtures * refactor: remove transcript locator test helper * test: make cron sqlite keys explicit * test: remove cron runtime store paths * test: remove stale session file fixtures * test: use sqlite cron keys in diagnostics * refactor: remove runtime delivery queue backfill * test: drop fake export session file mocks * refactor: rename acp session read failure flag * refactor: rename acp row session key * refactor: remove session store test seams * refactor: move legacy session parser tests to doctor * refactor: reindex managed memory in place * refactor: drop stale session store wording * refactor: rename session row helpers * refactor: rename sqlite session entry modules * refactor: remove transcript locator leftovers * refactor: trim file-era audit wording * refactor: clean managed media through sqlite * fix: prefer explicit agent for exports * fix: use prepared agent for session resets * fix: canonicalize legacy codex binding import * test: rename state cleanup helper * docs: align backup docs with sqlite state * refactor: drop legacy Pi usage auth fallback * refactor: move legacy auth profile imports to doctor * refactor: keep Pi model discovery auth in memory * refactor: remove MSTeams legacy learning key fallback * refactor: store model catalog config in sqlite * refactor: use sqlite model catalog at runtime * refactor: remove model json compatibility aliases * refactor: store auth profiles in sqlite * refactor: seed copied auth profiles in sqlite * refactor: make auth profile runtime sqlite-addressed * refactor: migrate hermes secrets into sqlite auth store * refactor: move plugin install config migration to doctor * refactor: rename plugin index audit checks * test: drop auth file assumptions * test: remove legacy transcript file assertions * refactor: drop legacy cli session aliases * refactor: store skill uploads in sqlite * refactor: keep subagent attachments in sqlite vfs * refactor: drop subagent attachment cleanup state * refactor: move legacy session aliases to doctor * refactor: require node 24 for sqlite state runtime * refactor: move provider caches into sqlite state * fix: harden virtual agent filesystem * refactor: enforce database-first runtime state * refactor: rename compaction transcript rotation setting * test: clean sqlite refactor test types * refactor: consolidate sqlite runtime state * refactor: model session conversations in sqlite * refactor: stop deriving cron delivery from session keys * refactor: stop classifying sessions from key shape * refactor: hydrate announce targets from typed delivery * refactor: route heartbeat delivery from typed sqlite context * refactor: tighten typed sqlite session routing * refactor: remove session origin routing shadow * refactor: drop session origin shadow fixtures * perf: query sqlite vfs paths by prefix * refactor: use typed conversation metadata for sessions * refactor: prefer typed session routing metadata * refactor: require typed session routing metadata * refactor: resolve group tool policy from typed sessions * refactor: delete dead session thread info bridge * Show Codex subscription reset times in channel errors (#80456) * feat(plugin-sdk): consolidate session workflow APIs * fix(agents): allow read-only agent mount reads * [codex] refresh plugin regression fixtures * fix(agents): restore compaction gateway logs * test: tighten gateway startup assertions * Redact persisted secret-shaped payloads [AI] (#79006) * test: tighten device pair notify assertions * test: tighten hermes secret assertions * test: assert matrix client error shapes * test: assert config compat warnings * fix(heartbeat): remap cron-run exec events to session keys (#80214) * fix(codex): route btw through native side threads * fix(auth): accept friendly OpenAI order for Codex profiles * fix(codex): rotate auth profiles inside harness * fix: keep browser status page probe within timeout * test: assert agents add outputs * test: pin cron read status * fix(agents): avoid Pi resource discovery stalls Co-authored-by: dataCenter430 <titan032000@gmail.com> * fix: retire timed-out codex app-server clients * test: tighten qa lab runtime assertions * test: check security fix outputs * test: verify extension runtime messages * feat(wake): expose typed sessionKey on wake protocol + system event CLI * fix(gateway): await session_end during shutdown drain and track channel + compaction lifecycle paths (#57790) * test: guard talk consult call helper * fix(codex): scale context engine projection (#80761) * fix(codex): scale context engine projection * fix: document Codex context projection scaling * fix: document Codex context projection scaling * fix: document Codex context projection scaling * fix: document Codex context projection scaling * chore: align Codex projection changelog * chore: realign Codex projection changelog * fix: isolate Codex projection patch --------- Co-authored-by: Eva (agent) <eva+agent-78055@100yen.org> Co-authored-by: Josh Lehman <josh@martian.engineering> * refactor: move agent runtime state toward piless * refactor: remove cron session reaper * refactor: move session management to sqlite * refactor: finish database-first state migration * chore: refresh generated sqlite db types * refactor: remove stale file-backed shims * test: harden kysely type coverage # Conflicts: # .agents/skills/kysely-database-access/SKILL.md # src/infra/kysely-sync.types.test.ts # src/proxy-capture/store.sqlite.test.ts # src/state/openclaw-agent-db.test.ts # src/state/openclaw-state-db.test.ts * refactor: remove cron store path runtime * refactor: keep compaction transcript handles out of session rows * refactor: derive embedded transcripts from sqlite identity * refactor: remove embedded transcript locator handoff * refactor: remove runtime transcript file bridges * refactor: remove transcript file handoffs * refactor: remove MSTeams legacy learning key fallback * refactor: store model catalog config in sqlite * refactor: use sqlite model catalog at runtime # Conflicts: # docs/cli/secrets.md # docs/gateway/authentication.md # docs/gateway/secrets.md * fix: keep oauth sibling sync sqlite-local # Conflicts: # src/commands/onboard-auth.test.ts * refactor: remove task session store maintenance # Conflicts: # src/commands/tasks.ts * refactor: keep diagnostics in state sqlite * refactor: enforce database-first runtime state * refactor: consolidate sqlite runtime state * Show Codex subscription reset times in channel errors (#80456) * fix(codex): refresh subscription limit resets * fix(codex): format reset times for channels * Update CHANGELOG with latest changes and fixes Updated CHANGELOG with recent fixes and improvements. * fix(codex): keep command load failures on codex surface * fix(codex): format account rate limits as rows * fix(codex): summarize account limits as usage status * fix(codex): simplify account limit status * test: tighten subagent announce queue assertion * test: tighten session delete lifecycle assertions * test: tighten cron ops assertions * fix: track cron execution milestones * test: tighten hermes secret assertions * test: assert matrix sync store payloads * test: assert config compat warnings * fix(codex): align btw side thread semantics * fix(codex): honor codex fallback blocking * fix(agents): avoid Pi resource discovery stalls * test: tighten codex event assertions * test: tighten cron assertions * Fix Codex app-server OAuth harness auth * refactor: move agent runtime state toward piless * refactor: move device and push state to sqlite * refactor: move runtime json state imports to doctor * refactor: finish database-first state migration * chore: refresh generated sqlite db types * refactor: clarify cron sqlite store keys * refactor: remove stale file-backed shims * refactor: bind codex runtime state by session id * test: expect sqlite trajectory branch export * refactor: rename session row helpers * fix: keep legacy device identity import in doctor * refactor: enforce database-first runtime state * refactor: consolidate sqlite runtime state * build: align pi contract wrappers * chore: repair database-first rebase * refactor: remove session file test contracts * test: update gateway session expectations * refactor: stop routing from session compatibility shadows * refactor: stop persisting session route shadows * refactor: use typed delivery context in clients * refactor: stop echoing session route shadows * refactor: repair embedded runner rebase imports # Conflicts: # src/agents/pi-embedded-runner/run/attempt.tool-call-argument-repair.ts * refactor: align pi contract imports * refactor: satisfy kysely sync helper guard * refactor: remove file transcript bridge remnants * refactor: remove session locator compatibility * refactor: remove session file test contracts * refactor: keep rebase database-first clean * refactor: remove session file assumptions from e2e * docs: clarify database-first goal state * test: remove legacy store markers from sqlite runtime tests * refactor: remove legacy store assumptions from runtime seams * refactor: align sqlite runtime helper seams * test: update memory recall sqlite audit mock * refactor: align database-first runtime type seams * test: clarify doctor cron legacy store names * fix: preserve sqlite session route projections * test: fix copilot token cache test syntax * docs: update database-first proof status * test: align database-first test fixtures * docs: update database-first proof status * refactor: clean extension database-first drift * test: align agent session route proof * test: clarify doctor legacy path fixtures * chore: clean database-first changed checks * chore: repair database-first rebase markers * build: allow baileys git subdependency * chore: repair exp-vfs rebase drift * chore: finish exp-vfs rebase cleanup * chore: satisfy rebase lint drift * chore: fix qqbot rebase type seam * chore: fix rebase drift leftovers * fix: keep auth profile oauth secrets out of sqlite * fix: repair rebase drift tests * test: stabilize pairing request ordering * test: use source manifests in plugin contract checks * fix: restore gateway session metadata after rebase * fix: repair database-first rebase drift * fix: clean up database-first rebase fallout * test: stabilize line quick reply receipt time * fix: repair extension rebase drift * test: keep transcript redaction tests sqlite-backed * fix: carry injected transcript redaction through sqlite * chore: clean database branch rebase residue * fix: repair database branch CI drift * fix: repair database branch CI guard drift * fix: stabilize oauth tls preflight test * test: align database branch fast guards * test: repair build artifact boundary guards * chore: clean changelog rebase markers --------- Co-authored-by: pashpashpash <nik@vault77.ai> Co-authored-by: Eva <eva@100yen.org> Co-authored-by: stainlu <stainlu@newtype-ai.org> Co-authored-by: Jason Zhou <jason.zhou.design@gmail.com> Co-authored-by: Ruben Cuevas <hi@rubencu.com> Co-authored-by: Pavan Kumar Gondhi <pavangondhi@gmail.com> Co-authored-by: Shakker <shakkerdroid@gmail.com> Co-authored-by: Kaspre <36520309+Kaspre@users.noreply.github.com> Co-authored-by: dataCenter430 <titan032000@gmail.com> Co-authored-by: Kaspre <kaspre@gmail.com> Co-authored-by: pandadev66 <nova.full.stack@outlook.com> Co-authored-by: Eva <admin@100yen.org> Co-authored-by: Eva (agent) <eva+agent-78055@100yen.org> Co-authored-by: Josh Lehman <josh@martian.engineering> Co-authored-by: jeffjhunter <support@aipersonamethod.com>
348 lines
11 KiB
TypeScript
348 lines
11 KiB
TypeScript
import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
|
|
import type { ProviderWrapStreamFnContext } from "openclaw/plugin-sdk/plugin-entry";
|
|
import { streamSimple } from "openclaw/plugin-sdk/provider-ai";
|
|
import { streamWithPayloadPatch } from "openclaw/plugin-sdk/provider-stream-shared";
|
|
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/string-coerce-runtime";
|
|
|
|
const TOOL_CALLS_SECTION_BEGIN = "<|tool_calls_section_begin|>";
|
|
const TOOL_CALLS_SECTION_END = "<|tool_calls_section_end|>";
|
|
const TOOL_CALL_BEGIN = "<|tool_call_begin|>";
|
|
const TOOL_CALL_ARGUMENT_BEGIN = "<|tool_call_argument_begin|>";
|
|
const TOOL_CALL_END = "<|tool_call_end|>";
|
|
|
|
type KimiToolCallBlock = {
|
|
type: "toolCall";
|
|
id: string;
|
|
name: string;
|
|
arguments: Record<string, unknown>;
|
|
};
|
|
|
|
type KimiThinkingType = "enabled" | "disabled";
|
|
type KimiThinkingConfig = {
|
|
type: KimiThinkingType;
|
|
budget_tokens?: number;
|
|
};
|
|
type KimiThinkingLevel =
|
|
| "off"
|
|
| "minimal"
|
|
| "low"
|
|
| "medium"
|
|
| "high"
|
|
| "xhigh"
|
|
| "adaptive"
|
|
| "max";
|
|
|
|
const KIMI_ANTHROPIC_THINKING_BUDGETS: Record<Exclude<KimiThinkingLevel, "off">, number> = {
|
|
minimal: 1024,
|
|
low: 1024,
|
|
medium: 4096,
|
|
high: 8192,
|
|
adaptive: 8192,
|
|
xhigh: 8192,
|
|
max: 8192,
|
|
};
|
|
const KIMI_ANTHROPIC_VISIBLE_OUTPUT_RESERVE_TOKENS = 1024;
|
|
const KIMI_ANTHROPIC_MIN_OUTPUT_TOKENS = 16000;
|
|
|
|
function normalizeKimiThinkingBudgetTokens(value: unknown): number | undefined {
|
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
return undefined;
|
|
}
|
|
const normalized = Math.floor(value);
|
|
return normalized >= 1024 ? normalized : undefined;
|
|
}
|
|
|
|
function normalizeKimiAnthropicMaxTokens(value: unknown): number | undefined {
|
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
return undefined;
|
|
}
|
|
const normalized = Math.floor(value);
|
|
return normalized > 0 ? normalized : undefined;
|
|
}
|
|
|
|
function ensureKimiAnthropicMaxTokens(
|
|
payloadObj: Record<string, unknown>,
|
|
thinkingConfig: KimiThinkingConfig,
|
|
): void {
|
|
if (thinkingConfig.type !== "enabled" || thinkingConfig.budget_tokens === undefined) {
|
|
return;
|
|
}
|
|
const required = Math.max(
|
|
KIMI_ANTHROPIC_MIN_OUTPUT_TOKENS,
|
|
thinkingConfig.budget_tokens + KIMI_ANTHROPIC_VISIBLE_OUTPUT_RESERVE_TOKENS,
|
|
);
|
|
const current = normalizeKimiAnthropicMaxTokens(payloadObj.max_tokens);
|
|
payloadObj.max_tokens = current === undefined ? required : Math.max(current, required);
|
|
}
|
|
|
|
function normalizeKimiThinkingType(value: unknown): KimiThinkingType | undefined {
|
|
if (typeof value === "boolean") {
|
|
return value ? "enabled" : "disabled";
|
|
}
|
|
if (typeof value === "string") {
|
|
const normalized = normalizeOptionalLowercaseString(value);
|
|
if (!normalized) {
|
|
return undefined;
|
|
}
|
|
if (["enabled", "enable", "on", "true"].includes(normalized)) {
|
|
return "enabled";
|
|
}
|
|
if (["disabled", "disable", "off", "false"].includes(normalized)) {
|
|
return "disabled";
|
|
}
|
|
return undefined;
|
|
}
|
|
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
return normalizeKimiThinkingType((value as Record<string, unknown>).type);
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function normalizeKimiThinkingConfig(value: unknown): KimiThinkingConfig | undefined {
|
|
const type = normalizeKimiThinkingType(value);
|
|
if (!type) {
|
|
return undefined;
|
|
}
|
|
if (type === "disabled") {
|
|
return { type: "disabled" };
|
|
}
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
return { type: "enabled" };
|
|
}
|
|
const record = value as Record<string, unknown>;
|
|
const budgetTokens = normalizeKimiThinkingBudgetTokens(
|
|
record.budget_tokens ?? record.budgetTokens,
|
|
);
|
|
return budgetTokens === undefined
|
|
? { type: "enabled" }
|
|
: { type: "enabled", budget_tokens: budgetTokens };
|
|
}
|
|
|
|
function resolveKimiAnthropicThinkingBudgetTokens(
|
|
thinkingLevel: KimiThinkingLevel | undefined,
|
|
): number | undefined {
|
|
if (!thinkingLevel || thinkingLevel === "off") {
|
|
return undefined;
|
|
}
|
|
return KIMI_ANTHROPIC_THINKING_BUDGETS[thinkingLevel];
|
|
}
|
|
|
|
export function resolveKimiThinkingConfig(params: {
|
|
configuredThinking: unknown;
|
|
thinkingLevel?: KimiThinkingLevel;
|
|
}): KimiThinkingConfig {
|
|
const configured = normalizeKimiThinkingConfig(params.configuredThinking);
|
|
const levelBudgetTokens = resolveKimiAnthropicThinkingBudgetTokens(params.thinkingLevel);
|
|
if (configured) {
|
|
return configured.type === "enabled" && configured.budget_tokens === undefined
|
|
? { type: "enabled", budget_tokens: levelBudgetTokens ?? 1024 }
|
|
: configured;
|
|
}
|
|
if (!params.thinkingLevel || params.thinkingLevel === "off") {
|
|
return { type: "disabled" };
|
|
}
|
|
return levelBudgetTokens === undefined
|
|
? { type: "enabled" }
|
|
: { type: "enabled", budget_tokens: levelBudgetTokens };
|
|
}
|
|
|
|
export function resolveKimiThinkingType(params: {
|
|
configuredThinking: unknown;
|
|
thinkingLevel?: KimiThinkingLevel;
|
|
}): KimiThinkingType {
|
|
return resolveKimiThinkingConfig(params).type;
|
|
}
|
|
|
|
function stripTaggedToolCallCounter(value: string): string {
|
|
return value.trim().replace(/:\d+$/, "");
|
|
}
|
|
|
|
function parseKimiTaggedToolCalls(text: string): KimiToolCallBlock[] | null {
|
|
const trimmed = text.trim();
|
|
// Kimi emits tagged tool-call sections as standalone text blocks on this path.
|
|
if (!trimmed.startsWith(TOOL_CALLS_SECTION_BEGIN) || !trimmed.endsWith(TOOL_CALLS_SECTION_END)) {
|
|
return null;
|
|
}
|
|
|
|
let cursor = TOOL_CALLS_SECTION_BEGIN.length;
|
|
const sectionEndIndex = trimmed.length - TOOL_CALLS_SECTION_END.length;
|
|
const toolCalls: KimiToolCallBlock[] = [];
|
|
|
|
while (cursor < sectionEndIndex) {
|
|
while (cursor < sectionEndIndex && /\s/.test(trimmed[cursor] ?? "")) {
|
|
cursor += 1;
|
|
}
|
|
if (cursor >= sectionEndIndex) {
|
|
break;
|
|
}
|
|
if (!trimmed.startsWith(TOOL_CALL_BEGIN, cursor)) {
|
|
return null;
|
|
}
|
|
|
|
const nameStart = cursor + TOOL_CALL_BEGIN.length;
|
|
const argMarkerIndex = trimmed.indexOf(TOOL_CALL_ARGUMENT_BEGIN, nameStart);
|
|
if (argMarkerIndex < 0 || argMarkerIndex >= sectionEndIndex) {
|
|
return null;
|
|
}
|
|
|
|
const rawId = trimmed.slice(nameStart, argMarkerIndex).trim();
|
|
if (!rawId) {
|
|
return null;
|
|
}
|
|
|
|
const argsStart = argMarkerIndex + TOOL_CALL_ARGUMENT_BEGIN.length;
|
|
const callEndIndex = trimmed.indexOf(TOOL_CALL_END, argsStart);
|
|
if (callEndIndex < 0 || callEndIndex > sectionEndIndex) {
|
|
return null;
|
|
}
|
|
|
|
const rawArgs = trimmed.slice(argsStart, callEndIndex).trim();
|
|
let parsedArgs: unknown;
|
|
try {
|
|
parsedArgs = JSON.parse(rawArgs);
|
|
} catch {
|
|
return null;
|
|
}
|
|
if (!parsedArgs || typeof parsedArgs !== "object" || Array.isArray(parsedArgs)) {
|
|
return null;
|
|
}
|
|
|
|
const name = stripTaggedToolCallCounter(rawId);
|
|
if (!name) {
|
|
return null;
|
|
}
|
|
|
|
toolCalls.push({
|
|
type: "toolCall",
|
|
id: rawId,
|
|
name,
|
|
arguments: parsedArgs as Record<string, unknown>,
|
|
});
|
|
|
|
cursor = callEndIndex + TOOL_CALL_END.length;
|
|
}
|
|
|
|
return toolCalls.length > 0 ? toolCalls : null;
|
|
}
|
|
|
|
function rewriteKimiTaggedToolCallsInMessage(message: unknown): void {
|
|
if (!message || typeof message !== "object") {
|
|
return;
|
|
}
|
|
|
|
const content = (message as { content?: unknown }).content;
|
|
if (!Array.isArray(content)) {
|
|
return;
|
|
}
|
|
|
|
let changed = false;
|
|
const nextContent: unknown[] = [];
|
|
for (const block of content) {
|
|
if (!block || typeof block !== "object") {
|
|
nextContent.push(block);
|
|
continue;
|
|
}
|
|
const typedBlock = block as { type?: unknown; text?: unknown };
|
|
if (typedBlock.type !== "text" || typeof typedBlock.text !== "string") {
|
|
nextContent.push(block);
|
|
continue;
|
|
}
|
|
|
|
const parsed = parseKimiTaggedToolCalls(typedBlock.text);
|
|
if (!parsed) {
|
|
nextContent.push(block);
|
|
continue;
|
|
}
|
|
|
|
nextContent.push(...parsed);
|
|
changed = true;
|
|
}
|
|
|
|
if (!changed) {
|
|
return;
|
|
}
|
|
|
|
(message as { content: unknown[] }).content = nextContent;
|
|
const typedMessage = message as { stopReason?: unknown };
|
|
if (typedMessage.stopReason === "stop") {
|
|
typedMessage.stopReason = "toolUse";
|
|
}
|
|
}
|
|
|
|
function wrapStreamMessageObjects(
|
|
stream: ReturnType<typeof streamSimple>,
|
|
transformMessage: (message: unknown) => void,
|
|
): ReturnType<typeof streamSimple> {
|
|
const originalResult = stream.result.bind(stream);
|
|
stream.result = async () => {
|
|
const message = await originalResult();
|
|
transformMessage(message);
|
|
return message;
|
|
};
|
|
|
|
const originalAsyncIterator = stream[Symbol.asyncIterator].bind(stream);
|
|
(stream as { [Symbol.asyncIterator]: typeof originalAsyncIterator })[Symbol.asyncIterator] =
|
|
function () {
|
|
const iterator = originalAsyncIterator();
|
|
return {
|
|
async next() {
|
|
const result = await iterator.next();
|
|
if (!result.done && result.value && typeof result.value === "object") {
|
|
const event = result.value as { partial?: unknown; message?: unknown };
|
|
transformMessage(event.partial);
|
|
transformMessage(event.message);
|
|
}
|
|
return result;
|
|
},
|
|
async return(value?: unknown) {
|
|
return iterator.return?.(value) ?? { done: true as const, value: undefined };
|
|
},
|
|
async throw(error?: unknown) {
|
|
return iterator.throw?.(error) ?? { done: true as const, value: undefined };
|
|
},
|
|
};
|
|
};
|
|
return stream;
|
|
}
|
|
|
|
export function createKimiToolCallMarkupWrapper(baseStreamFn: StreamFn | undefined): StreamFn {
|
|
const underlying = baseStreamFn ?? streamSimple;
|
|
return (model, context, options) => {
|
|
const maybeStream = underlying(model, context, options);
|
|
if (maybeStream && typeof maybeStream === "object" && "then" in maybeStream) {
|
|
return Promise.resolve(maybeStream).then((stream) =>
|
|
wrapStreamMessageObjects(stream, rewriteKimiTaggedToolCallsInMessage),
|
|
);
|
|
}
|
|
return wrapStreamMessageObjects(maybeStream, rewriteKimiTaggedToolCallsInMessage);
|
|
};
|
|
}
|
|
|
|
export function createKimiThinkingWrapper(
|
|
baseStreamFn: StreamFn | undefined,
|
|
thinkingConfig: KimiThinkingConfig | KimiThinkingType,
|
|
): StreamFn {
|
|
const underlying = baseStreamFn ?? streamSimple;
|
|
return (model, context, options) =>
|
|
streamWithPayloadPatch(underlying, model, context, options, (payloadObj) => {
|
|
const normalized =
|
|
typeof thinkingConfig === "string" ? { type: thinkingConfig } : thinkingConfig;
|
|
payloadObj.thinking =
|
|
model.api === "anthropic-messages" ? { ...normalized } : { type: normalized.type };
|
|
if (model.api === "anthropic-messages") {
|
|
ensureKimiAnthropicMaxTokens(payloadObj, normalized);
|
|
}
|
|
delete payloadObj.reasoning;
|
|
delete payloadObj.reasoning_effort;
|
|
delete payloadObj.reasoningEffort;
|
|
});
|
|
}
|
|
|
|
export function wrapKimiProviderStream(ctx: ProviderWrapStreamFnContext): StreamFn {
|
|
const thinkingConfig = resolveKimiThinkingConfig({
|
|
configuredThinking: ctx.extraParams?.thinking,
|
|
thinkingLevel: ctx.thinkingLevel,
|
|
});
|
|
return createKimiToolCallMarkupWrapper(createKimiThinkingWrapper(ctx.streamFn, thinkingConfig));
|
|
}
|