kimi-proxy 0.0.1
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/LICENSE +24 -0
- package/README.md +177 -0
- package/dist/adapters/anthropicAdapter.d.ts +138 -0
- package/dist/adapters/anthropicAdapter.js +184 -0
- package/dist/adapters/anthropicAdapter.js.map +1 -0
- package/dist/config.d.ts +27 -0
- package/dist/config.js +228 -0
- package/dist/config.js.map +1 -0
- package/dist/core/converters/anthropicToOpenAI.d.ts +11 -0
- package/dist/core/converters/anthropicToOpenAI.js +18 -0
- package/dist/core/converters/anthropicToOpenAI.js.map +1 -0
- package/dist/core/converters/openaiPassthrough.d.ts +18 -0
- package/dist/core/converters/openaiPassthrough.js +794 -0
- package/dist/core/converters/openaiPassthrough.js.map +1 -0
- package/dist/core/converters/types.d.ts +14 -0
- package/dist/core/converters/types.js +16 -0
- package/dist/core/converters/types.js.map +1 -0
- package/dist/core/ensureToolCall.d.ts +24 -0
- package/dist/core/ensureToolCall.js +93 -0
- package/dist/core/ensureToolCall.js.map +1 -0
- package/dist/core/modelRegistry.d.ts +46 -0
- package/dist/core/modelRegistry.js +92 -0
- package/dist/core/modelRegistry.js.map +1 -0
- package/dist/core/pipeline.d.ts +17 -0
- package/dist/core/pipeline.js +109 -0
- package/dist/core/pipeline.js.map +1 -0
- package/dist/core/pipelineControl.d.ts +12 -0
- package/dist/core/pipelineControl.js +51 -0
- package/dist/core/pipelineControl.js.map +1 -0
- package/dist/core/providers/openRouterProvider.d.ts +52 -0
- package/dist/core/providers/openRouterProvider.js +158 -0
- package/dist/core/providers/openRouterProvider.js.map +1 -0
- package/dist/core/providers/openaiProvider.d.ts +22 -0
- package/dist/core/providers/openaiProvider.js +79 -0
- package/dist/core/providers/openaiProvider.js.map +1 -0
- package/dist/core/providers/types.d.ts +24 -0
- package/dist/core/providers/types.js +14 -0
- package/dist/core/providers/types.js.map +1 -0
- package/dist/core/providers/vertexProvider.d.ts +37 -0
- package/dist/core/providers/vertexProvider.js +167 -0
- package/dist/core/providers/vertexProvider.js.map +1 -0
- package/dist/core/syntheticResponse.d.ts +6 -0
- package/dist/core/syntheticResponse.js +36 -0
- package/dist/core/syntheticResponse.js.map +1 -0
- package/dist/core/transforms/request/ClampMaxTokensTransform.d.ts +7 -0
- package/dist/core/transforms/request/ClampMaxTokensTransform.js +29 -0
- package/dist/core/transforms/request/ClampMaxTokensTransform.js.map +1 -0
- package/dist/core/transforms/request/DisableStreamingTransform.d.ts +7 -0
- package/dist/core/transforms/request/DisableStreamingTransform.js +15 -0
- package/dist/core/transforms/request/DisableStreamingTransform.js.map +1 -0
- package/dist/core/transforms/request/EnsureToolCallRequestTransform.d.ts +12 -0
- package/dist/core/transforms/request/EnsureToolCallRequestTransform.js +120 -0
- package/dist/core/transforms/request/EnsureToolCallRequestTransform.js.map +1 -0
- package/dist/core/transforms/response/EnsureToolCallResponseTransform.d.ts +9 -0
- package/dist/core/transforms/response/EnsureToolCallResponseTransform.js +223 -0
- package/dist/core/transforms/response/EnsureToolCallResponseTransform.js.map +1 -0
- package/dist/core/transforms/response/KimiResponseTransform.d.ts +7 -0
- package/dist/core/transforms/response/KimiResponseTransform.js +32 -0
- package/dist/core/transforms/response/KimiResponseTransform.js.map +1 -0
- package/dist/core/types.d.ts +80 -0
- package/dist/core/types.js +18 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/persistence/logStore.d.ts +43 -0
- package/dist/persistence/logStore.js +115 -0
- package/dist/persistence/logStore.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +385 -0
- package/dist/server.js.map +1 -0
- package/dist/services/kimiFixer.d.ts +13 -0
- package/dist/services/kimiFixer.js +160 -0
- package/dist/services/kimiFixer.js.map +1 -0
- package/dist/services/streaming.d.ts +9 -0
- package/dist/services/streaming.js +513 -0
- package/dist/services/streaming.js.map +1 -0
- package/dist/utils/envResolver.d.ts +26 -0
- package/dist/utils/envResolver.js +64 -0
- package/dist/utils/envResolver.js.map +1 -0
- package/dist/utils/ids.d.ts +1 -0
- package/dist/utils/ids.js +7 -0
- package/dist/utils/ids.js.map +1 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.js +12 -0
- package/dist/utils/logger.js.map +1 -0
- package/frontend/dist/assets/JSONViewer-97138fd7.js +3 -0
- package/frontend/dist/assets/index-0e7c091b.css +1 -0
- package/frontend/dist/assets/index-4b354ce2.js +40 -0
- package/frontend/dist/index.html +22 -0
- package/package.json +60 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DisableStreamingTransform.js","sourceRoot":"","sources":["../../../../src/core/transforms/request/DisableStreamingTransform.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,yBAAyB;IACpC,IAAI,GAAG,mBAAmB,CAAC;IAC3B,KAAK,GAA8B,QAAQ,CAAC;IAE5C,OAAO,CAAC,OAAgC;QACtC,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,IAAI,CAAC;IACzC,CAAC;IAED,SAAS,CAAC,OAAgC;QACxC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;QAC5C,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC;QAC/B,IAAI,OAAO,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrE,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACtC,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RequestTransform, RequestTransformContext } from "../../types.js";
|
|
2
|
+
export declare class EnsureToolCallRequestTransform implements RequestTransform {
|
|
3
|
+
name: string;
|
|
4
|
+
stage: RequestTransform["stage"];
|
|
5
|
+
priority: number;
|
|
6
|
+
applies(context: RequestTransformContext): boolean;
|
|
7
|
+
transform(context: RequestTransformContext): void;
|
|
8
|
+
private ensureToolRegistration;
|
|
9
|
+
private ensureInstructionMessages;
|
|
10
|
+
private attachInstruction;
|
|
11
|
+
private shouldSkipEnforcement;
|
|
12
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { buildBaseInstruction, buildReminderInstruction, buildFinalAnswerRequirementInstruction, createTerminationToolDefinition, getEnsureToolCallState, getMessagesSinceLastUser, } from "../../ensureToolCall.js";
|
|
2
|
+
import { logger } from "../../../utils/logger.js";
|
|
3
|
+
import { requestSyntheticResponse } from "../../pipelineControl.js";
|
|
4
|
+
export class EnsureToolCallRequestTransform {
|
|
5
|
+
name = "ensure-tool-call-request";
|
|
6
|
+
stage = "post-conversion";
|
|
7
|
+
priority = 100;
|
|
8
|
+
applies(context) {
|
|
9
|
+
return Boolean(context.providerRequest?.body &&
|
|
10
|
+
getEnsureToolCallState(context.request.state));
|
|
11
|
+
}
|
|
12
|
+
transform(context) {
|
|
13
|
+
const ensureState = getEnsureToolCallState(context.request.state);
|
|
14
|
+
if (!ensureState || !context.providerRequest) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
const body = asJsonObject(context.providerRequest.body);
|
|
18
|
+
// avoid infinite loops by detecting previous termination in the current session
|
|
19
|
+
if (this.shouldSkipEnforcement(body.messages, context.request.state, context.request.id)) {
|
|
20
|
+
logger.debug({ requestId: context.request.id }, "EnsureToolCall skipped: previous turn termination detected");
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
this.ensureToolRegistration(body, ensureState);
|
|
24
|
+
const reminderAttached = this.ensureInstructionMessages(body, ensureState);
|
|
25
|
+
context.providerRequest.body = body;
|
|
26
|
+
if (reminderAttached) {
|
|
27
|
+
logger.info({
|
|
28
|
+
requestId: context.request.id,
|
|
29
|
+
reminderCount: ensureState.reminderCount,
|
|
30
|
+
}, "EnsureToolCall reminder appended to provider request");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
ensureToolRegistration(body, state) {
|
|
34
|
+
const existing = Array.isArray(body.tools) ? [...body.tools] : [];
|
|
35
|
+
const hasTerminationTool = existing.some((tool) => extractToolName(tool) === state.terminationToolName);
|
|
36
|
+
if (!hasTerminationTool) {
|
|
37
|
+
existing.push(createTerminationToolDefinition(state.terminationToolName));
|
|
38
|
+
}
|
|
39
|
+
body.tools = existing;
|
|
40
|
+
}
|
|
41
|
+
ensureInstructionMessages(body, state) {
|
|
42
|
+
const messages = Array.isArray(body.messages)
|
|
43
|
+
? body.messages
|
|
44
|
+
: (body.messages = []);
|
|
45
|
+
const baseInstruction = buildBaseInstruction(state.terminationToolName);
|
|
46
|
+
this.attachInstruction(messages, baseInstruction);
|
|
47
|
+
let reminderAttached = false;
|
|
48
|
+
if (state.pendingReminder) {
|
|
49
|
+
let reminder;
|
|
50
|
+
const nextReminderIndex = state.reminderCount + 1;
|
|
51
|
+
if (state.finalAnswerRequired) {
|
|
52
|
+
reminder = buildFinalAnswerRequirementInstruction(state.terminationToolName);
|
|
53
|
+
state.finalAnswerRequired = false; // Reset after showing the reminder
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
reminder = buildReminderInstruction(state.terminationToolName);
|
|
57
|
+
}
|
|
58
|
+
state.reminderCount = nextReminderIndex;
|
|
59
|
+
this.attachInstruction(messages, reminder);
|
|
60
|
+
state.pendingReminder = false;
|
|
61
|
+
if (state.reminderHistory) {
|
|
62
|
+
state.reminderHistory.push(reminder);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
state.reminderHistory = [reminder];
|
|
66
|
+
}
|
|
67
|
+
reminderAttached = true;
|
|
68
|
+
}
|
|
69
|
+
return reminderAttached;
|
|
70
|
+
}
|
|
71
|
+
attachInstruction(messages, content) {
|
|
72
|
+
const systemMessage = messages.find((entry) => isJsonObject(entry) &&
|
|
73
|
+
entry.role === "system" &&
|
|
74
|
+
typeof entry.content === "string");
|
|
75
|
+
if (systemMessage && typeof systemMessage.content === "string") {
|
|
76
|
+
if (!systemMessage.content.includes(content)) {
|
|
77
|
+
systemMessage.content = `${systemMessage.content}\n\n${content}`;
|
|
78
|
+
}
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
messages.unshift({ role: "system", content });
|
|
82
|
+
}
|
|
83
|
+
shouldSkipEnforcement(messages, requestState, requestId) {
|
|
84
|
+
if (!Array.isArray(messages))
|
|
85
|
+
return false;
|
|
86
|
+
if (messages.length < 2)
|
|
87
|
+
return false;
|
|
88
|
+
const messagesSinceLastUser = getMessagesSinceLastUser(messages);
|
|
89
|
+
// Search for any assistant message without tool calls
|
|
90
|
+
for (const m of messagesSinceLastUser) {
|
|
91
|
+
if (isJsonObject(m) && m.role === "assistant") {
|
|
92
|
+
const hasToolCalls = Array.isArray(m.tool_calls) && m.tool_calls.length > 0;
|
|
93
|
+
if (!hasToolCalls) {
|
|
94
|
+
// Request synthetic response to prevent infinite loops
|
|
95
|
+
requestSyntheticResponse(requestState);
|
|
96
|
+
logger.warn({ requestId }, "Detected previous termination pattern, requesting synthetic response");
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function extractToolName(tool) {
|
|
105
|
+
if (!isJsonObject(tool)) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
const functionDef = isJsonObject(tool.function) ? tool.function : null;
|
|
109
|
+
if (!functionDef) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
return typeof functionDef.name === "string" ? functionDef.name : null;
|
|
113
|
+
}
|
|
114
|
+
function asJsonObject(value) {
|
|
115
|
+
return isJsonObject(value) ? value : {};
|
|
116
|
+
}
|
|
117
|
+
function isJsonObject(value) {
|
|
118
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=EnsureToolCallRequestTransform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EnsureToolCallRequestTransform.js","sourceRoot":"","sources":["../../../../src/core/transforms/request/EnsureToolCallRequestTransform.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ResponseTransform, ResponseTransformContext } from "../../types.js";
|
|
2
|
+
export declare class EnsureToolCallResponseTransform implements ResponseTransform {
|
|
3
|
+
name: string;
|
|
4
|
+
stage: ResponseTransform["stage"];
|
|
5
|
+
priority: number;
|
|
6
|
+
applies(context: ResponseTransformContext): boolean;
|
|
7
|
+
transform(context: ResponseTransformContext): void;
|
|
8
|
+
private hasMeaningfulAssistantContent;
|
|
9
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { getEnsureToolCallState, getMessagesSinceLastUser, } from "../../ensureToolCall.js";
|
|
2
|
+
import { clearRetryRequest, requestRetry } from "../../pipelineControl.js";
|
|
3
|
+
import { logger } from "../../../utils/logger.js";
|
|
4
|
+
export class EnsureToolCallResponseTransform {
|
|
5
|
+
name = "ensure-tool-call-response";
|
|
6
|
+
stage = "provider";
|
|
7
|
+
priority = 100;
|
|
8
|
+
applies(context) {
|
|
9
|
+
// Skip if synthetic response
|
|
10
|
+
const headers = context.providerResponse?.headers || {};
|
|
11
|
+
if (headers["x-synthetic-response"] === "true") {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
return Boolean(context.providerResponse?.body &&
|
|
15
|
+
getEnsureToolCallState(context.request.state));
|
|
16
|
+
}
|
|
17
|
+
transform(context) {
|
|
18
|
+
const ensureState = getEnsureToolCallState(context.request.state);
|
|
19
|
+
if (!ensureState || !context.providerResponse) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// Skip enforcement for synthetic responses to prevent loops
|
|
23
|
+
const headers = context.providerResponse.headers || {};
|
|
24
|
+
const isSyntheticResponse = headers["x-synthetic-response"] === "true";
|
|
25
|
+
if (isSyntheticResponse) {
|
|
26
|
+
logger.debug({ requestId: context.request.id }, "Synthetic response detected, skipping EnsureToolCall enforcement");
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const body = asJsonObject(context.providerResponse.body);
|
|
30
|
+
const choices = Array.isArray(body.choices)
|
|
31
|
+
? body.choices
|
|
32
|
+
: (body.choices = []);
|
|
33
|
+
let sawToolCall = false;
|
|
34
|
+
let totalToolCalls = 0;
|
|
35
|
+
for (const choice of choices) {
|
|
36
|
+
if (!isJsonObject(choice)) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const message = asJsonObject(choice.message);
|
|
40
|
+
const toolCalls = Array.isArray(message.tool_calls)
|
|
41
|
+
? message.tool_calls
|
|
42
|
+
: [];
|
|
43
|
+
totalToolCalls += toolCalls.length;
|
|
44
|
+
if (!toolCalls.length) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
sawToolCall = true;
|
|
48
|
+
let sawOnlyTermination = false;
|
|
49
|
+
const remaining = [];
|
|
50
|
+
for (const toolCall of toolCalls) {
|
|
51
|
+
const toolName = extractToolName(toolCall);
|
|
52
|
+
if (toolName?.toLowerCase() === ensureState.terminationToolName ||
|
|
53
|
+
toolName?.toLowerCase() === "final") {
|
|
54
|
+
console.log(`[EnsureToolCall] Termination tool "${toolName}" used.`);
|
|
55
|
+
// Extract final_answer parameter
|
|
56
|
+
const finalAnswer = extractFinalAnswer(toolCall);
|
|
57
|
+
// Check if any previous assistant messages have meaningful content
|
|
58
|
+
// or if the current response message has meaningful content
|
|
59
|
+
const stateMessages = context.request.state.messages;
|
|
60
|
+
const previousMessages = Array.isArray(stateMessages)
|
|
61
|
+
? stateMessages
|
|
62
|
+
: [];
|
|
63
|
+
const hasPreviousContent = this.hasMeaningfulAssistantContent(previousMessages);
|
|
64
|
+
const firstChoice = choices[0];
|
|
65
|
+
const currentMessage = isJsonObject(firstChoice) && isJsonObject(firstChoice.message)
|
|
66
|
+
? firstChoice.message
|
|
67
|
+
: null;
|
|
68
|
+
const hasCurrentContent = currentMessage
|
|
69
|
+
? hasMeaningfulContent(currentMessage.content)
|
|
70
|
+
: false;
|
|
71
|
+
const hasContent = hasPreviousContent || hasCurrentContent;
|
|
72
|
+
// Check if final_answer is required
|
|
73
|
+
if (!hasContent && !finalAnswer) {
|
|
74
|
+
console.log(`[EnsureToolCall] Termination tool used without meaningful content or final_answer. Requesting retry.`);
|
|
75
|
+
ensureState.pendingReminder = true;
|
|
76
|
+
ensureState.finalAnswerRequired = true;
|
|
77
|
+
requestRetry(context.request.state);
|
|
78
|
+
logger.warn({
|
|
79
|
+
requestId: context.request.id,
|
|
80
|
+
reminderCount: ensureState.reminderCount,
|
|
81
|
+
}, "Termination tool called without final_answer when required; scheduling retry");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// If we have no content but have final_answer, convert final_answer to message content
|
|
85
|
+
if (!hasContent && finalAnswer && currentMessage) {
|
|
86
|
+
console.log(`[EnsureToolCall] No meaningful content but final_answer provided. Converting to message content.`);
|
|
87
|
+
currentMessage.content = finalAnswer;
|
|
88
|
+
}
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
remaining.push(toolCall);
|
|
92
|
+
}
|
|
93
|
+
if (remaining.length) {
|
|
94
|
+
message.tool_calls = remaining;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
delete message.tool_calls;
|
|
98
|
+
}
|
|
99
|
+
// If the response only contained the termination tool and lacks meaningful
|
|
100
|
+
// assistant content, clear content and any reasoning artifacts.
|
|
101
|
+
sawOnlyTermination = toolCalls.length > 0 && remaining.length === 0;
|
|
102
|
+
if (sawOnlyTermination && !hasMeaningfulContent(message.content)) {
|
|
103
|
+
message.content = null;
|
|
104
|
+
message.reasoning_content = null;
|
|
105
|
+
delete message.reasoning_summary;
|
|
106
|
+
}
|
|
107
|
+
choice.message = message;
|
|
108
|
+
}
|
|
109
|
+
if (!sawToolCall) {
|
|
110
|
+
console.log(`[EnsureToolCall] No tool calls found in response. Requesting retry (Attempt ${ensureState.reminderCount + 1}).`);
|
|
111
|
+
ensureState.pendingReminder = true;
|
|
112
|
+
requestRetry(context.request.state);
|
|
113
|
+
logger.warn({
|
|
114
|
+
requestId: context.request.id,
|
|
115
|
+
reminderCount: ensureState.reminderCount,
|
|
116
|
+
}, "Provider response omitted tool calls; scheduling retry");
|
|
117
|
+
context.providerResponse.body = body;
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
clearRetryRequest(context.request.state);
|
|
121
|
+
ensureState.pendingReminder = false;
|
|
122
|
+
logger.info({
|
|
123
|
+
requestId: context.request.id,
|
|
124
|
+
observedToolCalls: totalToolCalls,
|
|
125
|
+
}, "EnsureToolCall satisfied by provider response");
|
|
126
|
+
context.providerResponse.body = body;
|
|
127
|
+
}
|
|
128
|
+
hasMeaningfulAssistantContent(messages) {
|
|
129
|
+
if (!Array.isArray(messages))
|
|
130
|
+
return false;
|
|
131
|
+
const messagesSinceLastUser = getMessagesSinceLastUser(messages);
|
|
132
|
+
for (const m of messagesSinceLastUser) {
|
|
133
|
+
if (isJsonObject(m) && m.role === "assistant") {
|
|
134
|
+
if (hasMeaningfulContent(m.content)) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return false;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function extractToolName(tool) {
|
|
143
|
+
if (!isJsonObject(tool)) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const fn = isJsonObject(tool.function) ? tool.function : null;
|
|
147
|
+
if (!fn) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
return typeof fn.name === "string" ? fn.name : null;
|
|
151
|
+
}
|
|
152
|
+
function extractFinalAnswer(tool) {
|
|
153
|
+
if (!isJsonObject(tool)) {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
const fn = isJsonObject(tool.function) ? tool.function : null;
|
|
157
|
+
if (!fn) {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
let args = null;
|
|
161
|
+
// Parse JSON string arguments
|
|
162
|
+
if (typeof fn.arguments === "string") {
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(fn.arguments);
|
|
165
|
+
if (isJsonObject(parsed)) {
|
|
166
|
+
args = parsed;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
// Invalid JSON, skip
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else if (isJsonObject(fn.arguments)) {
|
|
175
|
+
args = fn.arguments;
|
|
176
|
+
}
|
|
177
|
+
if (!args) {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
if (isJsonObject(args.raw)) {
|
|
181
|
+
args = args.raw;
|
|
182
|
+
}
|
|
183
|
+
const argName = /final[_-\s]?answer|final|answer|summary/i;
|
|
184
|
+
const argKeys = Object.keys(args);
|
|
185
|
+
for (const argKey of argKeys) {
|
|
186
|
+
if (argName.test(argKey) &&
|
|
187
|
+
typeof args[argKey] === "string" &&
|
|
188
|
+
args[argKey].trim()) {
|
|
189
|
+
return args[argKey];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
function asJsonObject(value) {
|
|
195
|
+
return isJsonObject(value) ? value : {};
|
|
196
|
+
}
|
|
197
|
+
function isJsonObject(value) {
|
|
198
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
199
|
+
}
|
|
200
|
+
function hasMeaningfulContent(content) {
|
|
201
|
+
if (content === undefined || content === null) {
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
if (typeof content === "string") {
|
|
205
|
+
return Boolean(content.trim());
|
|
206
|
+
}
|
|
207
|
+
if (Array.isArray(content)) {
|
|
208
|
+
return content.some((entry) => {
|
|
209
|
+
if (typeof entry === "string") {
|
|
210
|
+
return Boolean(entry.trim());
|
|
211
|
+
}
|
|
212
|
+
if (isJsonObject(entry) && typeof entry.text === "string") {
|
|
213
|
+
return Boolean(entry.text.trim());
|
|
214
|
+
}
|
|
215
|
+
return true;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
if (isJsonObject(content)) {
|
|
219
|
+
return Object.keys(content).length > 0;
|
|
220
|
+
}
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=EnsureToolCallResponseTransform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EnsureToolCallResponseTransform.js","sourceRoot":"","sources":["../../../../src/core/transforms/response/EnsureToolCallResponseTransform.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { ResponseTransform, ResponseTransformContext } from "../../types.js";
|
|
2
|
+
export declare class KimiResponseTransform implements ResponseTransform {
|
|
3
|
+
name: string;
|
|
4
|
+
stage: ResponseTransform["stage"];
|
|
5
|
+
applies(context: ResponseTransformContext): boolean;
|
|
6
|
+
transform(context: ResponseTransformContext): void;
|
|
7
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { fixKimiResponse } from "../../../services/kimiFixer.js";
|
|
2
|
+
import { logger } from "../../../utils/logger.js";
|
|
3
|
+
export class KimiResponseTransform {
|
|
4
|
+
name = "kimi-response-transform";
|
|
5
|
+
stage = "provider";
|
|
6
|
+
applies(context) {
|
|
7
|
+
const body = context.providerResponse?.body;
|
|
8
|
+
return isJsonObject(body) && Array.isArray(body.choices);
|
|
9
|
+
}
|
|
10
|
+
transform(context) {
|
|
11
|
+
const providerResponse = context.providerResponse;
|
|
12
|
+
if (!providerResponse) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const providerBody = providerResponse.body;
|
|
16
|
+
if (isJsonObject(providerBody)) {
|
|
17
|
+
const { response, metadata } = fixKimiResponse(providerBody);
|
|
18
|
+
providerResponse.body = response;
|
|
19
|
+
if (metadata.extractedToolCalls > 0) {
|
|
20
|
+
logger.info({
|
|
21
|
+
requestId: context.request.id,
|
|
22
|
+
extractedFromContent: metadata.extractedFromContent,
|
|
23
|
+
extractedFromReasoning: metadata.extractedFromReasoning,
|
|
24
|
+
}, "Kimi response transform recovered structured tool calls from text output");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function isJsonObject(value) {
|
|
30
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=KimiResponseTransform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"KimiResponseTransform.js","sourceRoot":"","sources":["../../../../src/core/transforms/response/KimiResponseTransform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAQlD,MAAM,OAAO,qBAAqB;IAChC,IAAI,GAAG,yBAAyB,CAAC;IACjC,KAAK,GAA+B,UAAU,CAAC;IAE/C,OAAO,CAAC,OAAiC;QACvC,MAAM,IAAI,GAAG,OAAO,CAAC,gBAAgB,EAAE,IAAI,CAAC;QAC5C,OAAO,YAAY,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC3D,CAAC;IAED,SAAS,CAAC,OAAiC;QACzC,MAAM,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,CAAC;QAClD,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,OAAO;QACT,CAAC;QACD,MAAM,YAAY,GAAG,gBAAgB,CAAC,IAAI,CAAC;QAC3C,IAAI,YAAY,CAAC,YAAY,CAAC,EAAE,CAAC;YAC/B,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,eAAe,CAAC,YAAY,CAAC,CAAC;YAC7D,gBAAgB,CAAC,IAAI,GAAG,QAAQ,CAAC;YAEjC,IAAI,QAAQ,CAAC,kBAAkB,GAAG,CAAC,EAAE,CAAC;gBACpC,MAAM,CAAC,IAAI,CACT;oBACE,SAAS,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE;oBAC7B,oBAAoB,EAAE,QAAQ,CAAC,oBAAoB;oBACnD,sBAAsB,EAAE,QAAQ,CAAC,sBAAsB;iBACxD,EACD,0EAA0E,CAC3E,CAAC;YACJ,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAED,SAAS,YAAY,CAAC,KAA4B;IAChD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export declare enum ClientFormat {
|
|
2
|
+
OpenAIChatCompletions = "openai.chat-completions",
|
|
3
|
+
AnthropicMessages = "anthropic.messages",
|
|
4
|
+
OpenAIResponses = "openai.responses"
|
|
5
|
+
}
|
|
6
|
+
export declare enum ProviderFormat {
|
|
7
|
+
OpenAIChatCompletions = "openai.chat-completions",
|
|
8
|
+
VertexChatCompletions = "vertex.chat-completions"
|
|
9
|
+
}
|
|
10
|
+
export declare enum ProxyOperation {
|
|
11
|
+
ChatCompletions = "chat.completions",
|
|
12
|
+
Messages = "messages",
|
|
13
|
+
Responses = "responses"
|
|
14
|
+
}
|
|
15
|
+
export type JsonPrimitive = string | number | boolean | null;
|
|
16
|
+
export type JsonArray = JsonValue[];
|
|
17
|
+
export interface JsonObject {
|
|
18
|
+
[key: string]: JsonValue | undefined;
|
|
19
|
+
}
|
|
20
|
+
export type JsonValue = JsonPrimitive | JsonObject | JsonArray;
|
|
21
|
+
export interface ProxyRequest<TBody extends JsonValue = JsonObject> {
|
|
22
|
+
id: string;
|
|
23
|
+
operation: ProxyOperation;
|
|
24
|
+
clientFormat: ClientFormat;
|
|
25
|
+
model: string;
|
|
26
|
+
body: TBody;
|
|
27
|
+
headers: Record<string, string>;
|
|
28
|
+
stream: boolean;
|
|
29
|
+
state: Record<string, JsonValue>;
|
|
30
|
+
}
|
|
31
|
+
export interface ConvertedRequest<TBody extends JsonValue = JsonValue> {
|
|
32
|
+
body: TBody;
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
}
|
|
35
|
+
export interface ConversionContext<TBody extends JsonValue = JsonValue> {
|
|
36
|
+
request: ProxyRequest<TBody>;
|
|
37
|
+
providerFormat: ProviderFormat;
|
|
38
|
+
}
|
|
39
|
+
export interface ProviderRequestPayload<TBody extends JsonValue = JsonValue> {
|
|
40
|
+
body: TBody;
|
|
41
|
+
headers: Record<string, string>;
|
|
42
|
+
}
|
|
43
|
+
export interface ProviderResponsePayload<TBody extends JsonValue = JsonValue> {
|
|
44
|
+
status: number;
|
|
45
|
+
body: TBody;
|
|
46
|
+
headers: Record<string, string>;
|
|
47
|
+
}
|
|
48
|
+
export interface PipelineResult {
|
|
49
|
+
statusCode: number;
|
|
50
|
+
responseBody: JsonValue;
|
|
51
|
+
providerResponse: ProviderResponsePayload;
|
|
52
|
+
providerRequestBody: JsonValue;
|
|
53
|
+
request: ProxyRequest;
|
|
54
|
+
isError: boolean;
|
|
55
|
+
}
|
|
56
|
+
export type RequestTransformStage = "source" | "post-conversion";
|
|
57
|
+
export type ResponseTransformStage = "provider" | "post-conversion";
|
|
58
|
+
export interface RequestTransformContext {
|
|
59
|
+
request: ProxyRequest;
|
|
60
|
+
providerRequest?: ProviderRequestPayload;
|
|
61
|
+
}
|
|
62
|
+
export interface ResponseTransformContext {
|
|
63
|
+
request: ProxyRequest;
|
|
64
|
+
providerResponse?: ProviderResponsePayload;
|
|
65
|
+
clientResponse?: JsonValue;
|
|
66
|
+
}
|
|
67
|
+
export interface RequestTransform {
|
|
68
|
+
name: string;
|
|
69
|
+
stage: RequestTransformStage;
|
|
70
|
+
priority?: number;
|
|
71
|
+
applies(context: RequestTransformContext): boolean;
|
|
72
|
+
transform(context: RequestTransformContext): Promise<void> | void;
|
|
73
|
+
}
|
|
74
|
+
export interface ResponseTransform {
|
|
75
|
+
name: string;
|
|
76
|
+
stage: ResponseTransformStage;
|
|
77
|
+
priority?: number;
|
|
78
|
+
applies(context: ResponseTransformContext): boolean;
|
|
79
|
+
transform(context: ResponseTransformContext): Promise<void> | void;
|
|
80
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export var ClientFormat;
|
|
2
|
+
(function (ClientFormat) {
|
|
3
|
+
ClientFormat["OpenAIChatCompletions"] = "openai.chat-completions";
|
|
4
|
+
ClientFormat["AnthropicMessages"] = "anthropic.messages";
|
|
5
|
+
ClientFormat["OpenAIResponses"] = "openai.responses";
|
|
6
|
+
})(ClientFormat || (ClientFormat = {}));
|
|
7
|
+
export var ProviderFormat;
|
|
8
|
+
(function (ProviderFormat) {
|
|
9
|
+
ProviderFormat["OpenAIChatCompletions"] = "openai.chat-completions";
|
|
10
|
+
ProviderFormat["VertexChatCompletions"] = "vertex.chat-completions";
|
|
11
|
+
})(ProviderFormat || (ProviderFormat = {}));
|
|
12
|
+
export var ProxyOperation;
|
|
13
|
+
(function (ProxyOperation) {
|
|
14
|
+
ProxyOperation["ChatCompletions"] = "chat.completions";
|
|
15
|
+
ProxyOperation["Messages"] = "messages";
|
|
16
|
+
ProxyOperation["Responses"] = "responses";
|
|
17
|
+
})(ProxyOperation || (ProxyOperation = {}));
|
|
18
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,YAIX;AAJD,WAAY,YAAY;IACtB,iEAAiD,CAAA;IACjD,wDAAwC,CAAA;IACxC,oDAAoC,CAAA;AACtC,CAAC,EAJW,YAAY,KAAZ,YAAY,QAIvB;AAED,MAAM,CAAN,IAAY,cAGX;AAHD,WAAY,cAAc;IACxB,mEAAiD,CAAA;IACjD,mEAAiD,CAAA;AACnD,CAAC,EAHW,cAAc,KAAd,cAAc,QAGzB;AAED,MAAM,CAAN,IAAY,cAIX;AAJD,WAAY,cAAc;IACxB,sDAAoC,CAAA;IACpC,uCAAqB,CAAA;IACrB,yCAAuB,CAAA;AACzB,CAAC,EAJW,cAAc,KAAd,cAAc,QAIzB"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { loadConfig } from "./config.js";
|
|
2
|
+
import { createServer } from "./server.js";
|
|
3
|
+
import { logger } from "./utils/logger.js";
|
|
4
|
+
async function bootstrap() {
|
|
5
|
+
try {
|
|
6
|
+
const config = loadConfig();
|
|
7
|
+
const server = await createServer(config);
|
|
8
|
+
await server.listen({ port: config.server.port, host: config.server.host });
|
|
9
|
+
logger.info(`LLM proxy listening on ${config.server.host}:${config.server.port}`);
|
|
10
|
+
}
|
|
11
|
+
catch (error) {
|
|
12
|
+
logger.error({ err: error }, "Failed to bootstrap server");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
bootstrap();
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAE3C,KAAK,UAAU,SAAS;IACtB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5E,MAAM,CAAC,IAAI,CACT,0BAA0B,MAAM,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CACrE,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,CAAC,KAAK,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,4BAA4B,CAAC,CAAC;QAC3D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,SAAS,EAAE,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { JsonValue } from "../core/types.js";
|
|
2
|
+
export interface AppendLogInput {
|
|
3
|
+
method: string;
|
|
4
|
+
url: string;
|
|
5
|
+
statusCode: number;
|
|
6
|
+
model?: string;
|
|
7
|
+
requestBody: JsonValue;
|
|
8
|
+
responseBody: JsonValue;
|
|
9
|
+
providerRequestBody?: JsonValue;
|
|
10
|
+
providerResponseBody?: JsonValue;
|
|
11
|
+
}
|
|
12
|
+
export interface ListLogsQuery {
|
|
13
|
+
page: number;
|
|
14
|
+
pageSize: number;
|
|
15
|
+
search?: string;
|
|
16
|
+
}
|
|
17
|
+
export interface LogRecord {
|
|
18
|
+
id: number;
|
|
19
|
+
timestamp: string;
|
|
20
|
+
method: string;
|
|
21
|
+
url: string;
|
|
22
|
+
status_code: number;
|
|
23
|
+
model: string | null;
|
|
24
|
+
request_body: JsonValue;
|
|
25
|
+
response_body: JsonValue;
|
|
26
|
+
provider_request_body: JsonValue;
|
|
27
|
+
provider_response_body: JsonValue;
|
|
28
|
+
}
|
|
29
|
+
export interface ListLogsResult {
|
|
30
|
+
items: LogRecord[];
|
|
31
|
+
total: number;
|
|
32
|
+
page: number;
|
|
33
|
+
pageSize: number;
|
|
34
|
+
}
|
|
35
|
+
export declare class LogStore {
|
|
36
|
+
private readonly dbPath;
|
|
37
|
+
private db;
|
|
38
|
+
private insertStmt;
|
|
39
|
+
constructor(dbPath: string);
|
|
40
|
+
append(entry: AppendLogInput): void;
|
|
41
|
+
list(query: ListLogsQuery): ListLogsResult;
|
|
42
|
+
private ensureColumn;
|
|
43
|
+
}
|