Merge remote-tracking branch 'origin/master' into gitea/feature/end

Resolved conflicts taking origin/master (v0.5.55) as canonical, with local
features re-applied:
- runtime log level (LOG_LEVEL env + dashboard Settings → Logging, applied
  immediately and persisted across restarts)
- free/noAuth provider enable/disable toggle via providerStrategies.enabled
- parallel model testing (Test All Models / Test Selected Keys)
This commit is contained in:
2026-08-17 00:38:31 +07:00
parent e7470e955e
commit 7e45ead2ac
557 changed files with 53396 additions and 6935 deletions

View File

@@ -18,14 +18,24 @@ function jsonBytes(value) {
function messagePayload(body) {
if (Array.isArray(body?.messages)) return body.messages;
if (Array.isArray(body?.input)) return body.input;
const kiro = collectKiroHeadroomMessages(body);
if (kiro) return kiro.messages;
return null;
}
function captureSizeSnapshot(body) {
const messages = messagePayload(body);
const toolHistory = messages?.filter((message) =>
message?.role === "tool"
|| message?.role === "function"
|| message?.tool_calls?.length
|| message?.content?.some?.((part) => part?.type === "tool_use" || part?.type === "tool_result")
) || [];
return {
bodyBytes: jsonBytes(body),
messageBytes: messages ? jsonBytes(messages) : 0,
toolSchemaBytes: jsonBytes(body?.tools || []),
toolHistoryBytes: jsonBytes(toolHistory),
};
}
@@ -81,6 +91,121 @@ function hasUnsafeResponsesInputForCompression(body) {
});
}
function collectKiroHeadroomMessages(body) {
const state = body?.conversationState;
if (!state || typeof state !== "object") return null;
const messages = [];
const targets = [];
const addTextTarget = (role, text, target, extra = {}) => {
if (typeof text !== "string") return;
messages.push({ role, content: text, ...extra });
targets.push(target);
};
const toToolCalls = (toolUses) => {
if (!Array.isArray(toolUses) || toolUses.length === 0) return undefined;
const calls = toolUses.map((toolUse) => ({
id: toolUse?.toolUseId,
type: "function",
function: {
name: toolUse?.name || "",
arguments: JSON.stringify(toolUse?.input || {}),
},
})).filter((call) => call.id || call.function.name);
return calls.length > 0 ? calls : undefined;
};
const visit = (item) => {
const user = item?.userInputMessage;
if (user) {
addTextTarget("system", user.systemInstruction, { object: user, key: "systemInstruction" });
addTextTarget("user", user.content, { object: user, key: "content" });
const toolResults = user.userInputMessageContext?.toolResults;
if (Array.isArray(toolResults)) {
for (const toolResult of toolResults) {
const content = toolResult?.content;
if (!Array.isArray(content)) continue;
for (const part of content) {
addTextTarget(
"tool",
part?.text,
{ object: part, key: "text" },
toolResult?.toolUseId ? { tool_call_id: toolResult.toolUseId } : {}
);
}
}
}
return;
}
const assistant = item?.assistantResponseMessage;
if (assistant) {
const toolCalls = toToolCalls(assistant.toolUses);
addTextTarget(
"assistant",
assistant.content,
{ object: assistant, key: "content" },
toolCalls ? { tool_calls: toolCalls } : {}
);
}
};
if (Array.isArray(state.history)) {
for (const item of state.history) visit(item);
}
if (state.currentMessage) visit(state.currentMessage);
return messages.length > 0 ? { messages, targets } : null;
}
function textFromHeadroomMessage(message) {
const content = message?.content;
if (typeof content === "string") return content;
if (!Array.isArray(content)) return null;
const parts = [];
for (const part of content) {
if (typeof part === "string") {
parts.push(part);
} else if (typeof part?.text === "string") {
parts.push(part.text);
}
}
return parts.length > 0 ? parts.join("\n") : null;
}
function applyKiroHeadroomMessages(projection, compressedMessages, diagnostics) {
if (!Array.isArray(compressedMessages) || compressedMessages.length !== projection.messages.length) {
setDiagnostic(diagnostics, "proxy response did not match Kiro message count");
return false;
}
const updates = [];
for (let i = 0; i < projection.messages.length; i++) {
const expected = projection.messages[i];
const actual = compressedMessages[i];
if (!actual || actual.role !== expected.role) {
setDiagnostic(diagnostics, "proxy response did not preserve Kiro message order");
return false;
}
const text = textFromHeadroomMessage(actual);
if (text === null) {
setDiagnostic(diagnostics, "proxy response missing Kiro text content");
return false;
}
updates.push({ target: projection.targets[i], text });
}
for (const update of updates) {
update.target.object[update.target.key] = update.text;
}
return true;
}
// POST messages to Headroom /v1/compress; returns compressed messages + stats or null.
async function callCompress(url, messages, model, timeoutMs, compressUserMessages, diagnostics) {
const endpoint = buildCompressEndpoint(url);
@@ -171,6 +296,22 @@ export async function compressWithHeadroom(body, { enabled, url, model, format,
return data;
}
// Kiro shape: conversationState.history/currentMessage are projected to
// OpenAI messages for the proxy, then copied back into the original Kiro
// fields. Keep the provider payload shape intact for Kiro's executor.
if (format === "kiro") {
const projection = collectKiroHeadroomMessages(body);
if (!projection) {
setDiagnostic(diagnostics, "Kiro request did not project to messages[]");
return null;
}
const data = await callCompress(url, projection.messages, model, timeoutMs, compressUserMessages, diagnostics || {});
if (!data) return null;
if (!applyKiroHeadroomMessages(projection, data.messages, diagnostics)) return null;
if (diagnostics) diagnostics.after = captureSizeSnapshot(body);
return data;
}
// OpenAI shape: messages/input go straight to the proxy.
const key = Array.isArray(body.messages) ? "messages"
: Array.isArray(body.input) ? "input"
@@ -203,7 +344,10 @@ export function formatHeadroomSizeLog(diagnostics) {
const before = diagnostics?.before;
const after = diagnostics?.after;
if (!before || !after) return "";
return `body=${before.bodyBytes}B→${after.bodyBytes}B messages=${before.messageBytes}B→${after.messageBytes}B`;
const effective = before.bodyBytes > 0
? (((before.bodyBytes - after.bodyBytes) / before.bodyBytes) * 100).toFixed(1)
: "0.0";
return `body=${before.bodyBytes}B→${after.bodyBytes}B messages=${before.messageBytes}B→${after.messageBytes}B tools=${before.toolSchemaBytes || 0}B→${after.toolSchemaBytes || 0}B toolHistory=${before.toolHistoryBytes || 0}B→${after.toolHistoryBytes || 0}B effective=${effective}%`;
}
export function isHeadroomPhantomSavings(stats, diagnostics, minShrinkRatio = 0.05) {