kimi-proxy 0.1.2 → 0.1.3
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/dist/core/providers/openai.d.ts +244 -244
- package/dist/core/types.d.ts +3 -1
- package/dist/index.js +139 -19
- package/dist/index.js.map +10 -10
- package/dist/services/kimiFixer.d.ts +4 -2
- package/package.json +1 -1
package/dist/core/types.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ export interface JsonObject {
|
|
|
16
16
|
[key: string]: JsonValue | undefined;
|
|
17
17
|
}
|
|
18
18
|
export type JsonValue = JsonPrimitive | JsonObject | JsonArray;
|
|
19
|
-
export declare function isJsonObject(value: JsonValue): value is JsonObject;
|
|
19
|
+
export declare function isJsonObject(value: JsonValue | undefined): value is JsonObject;
|
|
20
20
|
export declare enum Operation {
|
|
21
21
|
Chat = "chat",
|
|
22
22
|
Messages = "messages",
|
|
@@ -2792,3 +2792,5 @@ export interface PipelineResult {
|
|
|
2792
2792
|
}
|
|
2793
2793
|
export type Json = JsonValue;
|
|
2794
2794
|
export declare const isMessage: (value: unknown) => value is Message;
|
|
2795
|
+
export declare function getToolSchemas(request: Request): Record<string, z.ZodSchema>;
|
|
2796
|
+
export declare function findMatchingTool(request: Request, args: unknown): string | null;
|
package/dist/index.js
CHANGED
|
@@ -1018,6 +1018,77 @@ var ResponseSchema = z3.object({
|
|
|
1018
1018
|
synthetic: z3.boolean().optional()
|
|
1019
1019
|
}).optional()
|
|
1020
1020
|
});
|
|
1021
|
+
function jsonSchemaToZod(schema) {
|
|
1022
|
+
if (!schema || typeof schema !== "object" || Array.isArray(schema))
|
|
1023
|
+
return z3.any();
|
|
1024
|
+
const s = schema;
|
|
1025
|
+
switch (s.type) {
|
|
1026
|
+
case "object": {
|
|
1027
|
+
const shape = {};
|
|
1028
|
+
const properties = s.properties || {};
|
|
1029
|
+
const required = s.required || [];
|
|
1030
|
+
for (const key in properties) {
|
|
1031
|
+
let propSchema = jsonSchemaToZod(properties[key]);
|
|
1032
|
+
if (!required.includes(key)) {
|
|
1033
|
+
propSchema = propSchema.optional();
|
|
1034
|
+
}
|
|
1035
|
+
shape[key] = propSchema;
|
|
1036
|
+
}
|
|
1037
|
+
const zodObj = z3.object(shape);
|
|
1038
|
+
if (s.additionalProperties === false) {
|
|
1039
|
+
return zodObj.strict();
|
|
1040
|
+
} else {
|
|
1041
|
+
return zodObj.passthrough();
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
case "array":
|
|
1045
|
+
return z3.array(jsonSchemaToZod(s.items || {}));
|
|
1046
|
+
case "string":
|
|
1047
|
+
if (Array.isArray(s.enum) && s.enum.length > 0 && s.enum.every((e) => typeof e === "string")) {
|
|
1048
|
+
return z3.enum(s.enum);
|
|
1049
|
+
}
|
|
1050
|
+
return z3.string();
|
|
1051
|
+
case "number":
|
|
1052
|
+
case "integer":
|
|
1053
|
+
return z3.number();
|
|
1054
|
+
case "boolean":
|
|
1055
|
+
return z3.boolean();
|
|
1056
|
+
case "null":
|
|
1057
|
+
return z3.null();
|
|
1058
|
+
default:
|
|
1059
|
+
return z3.any();
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
var toolSchemaCache = new WeakMap;
|
|
1063
|
+
function getToolSchemas(request) {
|
|
1064
|
+
let schemas = toolSchemaCache.get(request);
|
|
1065
|
+
if (schemas)
|
|
1066
|
+
return schemas;
|
|
1067
|
+
schemas = {};
|
|
1068
|
+
for (const tool of request.tools ?? []) {
|
|
1069
|
+
schemas[tool.name] = jsonSchemaToZod(tool.parameters);
|
|
1070
|
+
}
|
|
1071
|
+
toolSchemaCache.set(request, schemas);
|
|
1072
|
+
return schemas;
|
|
1073
|
+
}
|
|
1074
|
+
function safeParseJson(str) {
|
|
1075
|
+
try {
|
|
1076
|
+
return JSON.parse(str);
|
|
1077
|
+
} catch {
|
|
1078
|
+
return str;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
function findMatchingTool(request, args) {
|
|
1082
|
+
const schemas = getToolSchemas(request);
|
|
1083
|
+
const matches = [];
|
|
1084
|
+
const parsedArgs = typeof args === "string" ? safeParseJson(args) : args;
|
|
1085
|
+
for (const [name, schema] of Object.entries(schemas)) {
|
|
1086
|
+
if (schema.safeParse(parsedArgs).success) {
|
|
1087
|
+
matches.push(name);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
return matches.length === 1 ? matches[0] : null;
|
|
1091
|
+
}
|
|
1021
1092
|
|
|
1022
1093
|
// src/core/ensureToolCall.ts
|
|
1023
1094
|
var ENSURE_TOOL_CALL_STATE_KEY = "__ensureToolCall";
|
|
@@ -2635,13 +2706,39 @@ function parseToolCalls(section) {
|
|
|
2635
2706
|
}
|
|
2636
2707
|
return toolCalls;
|
|
2637
2708
|
}
|
|
2638
|
-
function
|
|
2709
|
+
function repairToolNames(toolCalls, request) {
|
|
2710
|
+
const schemas = getToolSchemas(request);
|
|
2711
|
+
const toolNames = new Set(Object.keys(schemas));
|
|
2712
|
+
let repairedCount = 0;
|
|
2713
|
+
for (const call of toolCalls) {
|
|
2714
|
+
if (call.type !== "function" || !isJsonObject(call.function))
|
|
2715
|
+
continue;
|
|
2716
|
+
const fn = call.function;
|
|
2717
|
+
if (typeof fn.name === "number") {
|
|
2718
|
+
fn.name = String(fn.name);
|
|
2719
|
+
}
|
|
2720
|
+
const name = fn.name;
|
|
2721
|
+
if (typeof name !== "string")
|
|
2722
|
+
continue;
|
|
2723
|
+
if (toolNames.has(name))
|
|
2724
|
+
continue;
|
|
2725
|
+
const match = findMatchingTool(request, fn.arguments);
|
|
2726
|
+
if (match) {
|
|
2727
|
+
logger.debug(`[KimiFixer] Repaired tool name: ${name} -> ${match}`);
|
|
2728
|
+
fn.name = match;
|
|
2729
|
+
repairedCount++;
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
return repairedCount;
|
|
2733
|
+
}
|
|
2734
|
+
function fixKimiResponse(response, request) {
|
|
2639
2735
|
const metadata = {
|
|
2640
2736
|
extractedToolCalls: 0,
|
|
2641
2737
|
extractedFromReasoning: 0,
|
|
2642
2738
|
extractedFromContent: 0,
|
|
2643
2739
|
cleanedReasoningContent: false,
|
|
2644
|
-
cleanedMessageContent: false
|
|
2740
|
+
cleanedMessageContent: false,
|
|
2741
|
+
repairedToolNames: 0
|
|
2645
2742
|
};
|
|
2646
2743
|
try {
|
|
2647
2744
|
const choices = response?.choices;
|
|
@@ -2698,6 +2795,10 @@ ${thinkingContent}` : thinkingContent;
|
|
|
2698
2795
|
}
|
|
2699
2796
|
}
|
|
2700
2797
|
if (aggregatedToolCalls.length) {
|
|
2798
|
+
const repaired = repairToolNames(aggregatedToolCalls, request);
|
|
2799
|
+
if (repaired > 0) {
|
|
2800
|
+
metadata.repairedToolNames = (metadata.repairedToolNames || 0) + repaired;
|
|
2801
|
+
}
|
|
2701
2802
|
message.tool_calls = aggregatedToolCalls;
|
|
2702
2803
|
choice.finish_reason = "tool_calls";
|
|
2703
2804
|
} else if ("tool_calls" in message) {
|
|
@@ -2741,7 +2842,7 @@ var OpenAIToolCallSchema = z7.object({
|
|
|
2741
2842
|
id: z7.string().optional(),
|
|
2742
2843
|
type: z7.literal("function").optional(),
|
|
2743
2844
|
function: z7.object({
|
|
2744
|
-
name: z7.string(),
|
|
2845
|
+
name: z7.union([z7.string(), z7.number()]).transform(String),
|
|
2745
2846
|
arguments: z7.union([z7.string(), z7.record(z7.unknown())]).optional()
|
|
2746
2847
|
}).passthrough()
|
|
2747
2848
|
}).passthrough();
|
|
@@ -2771,13 +2872,14 @@ function normalizeToolCalls(toolCalls) {
|
|
|
2771
2872
|
const normalized = [];
|
|
2772
2873
|
for (const call of toolCalls) {
|
|
2773
2874
|
const fn = call?.function;
|
|
2774
|
-
if (!fn || typeof fn.name !== "string")
|
|
2875
|
+
if (!fn || typeof fn.name !== "string" && typeof fn.name !== "number")
|
|
2775
2876
|
continue;
|
|
2776
|
-
const
|
|
2877
|
+
const name = String(fn.name);
|
|
2878
|
+
const id = typeof call.id === "string" && call.id.length ? call.id : `${name}_call_${Math.random().toString(36).substring(2, 10)}`;
|
|
2777
2879
|
normalized.push({
|
|
2778
2880
|
id,
|
|
2779
2881
|
type: "function",
|
|
2780
|
-
name
|
|
2882
|
+
name,
|
|
2781
2883
|
arguments: safeJsonString(fn.arguments)
|
|
2782
2884
|
});
|
|
2783
2885
|
}
|
|
@@ -2879,7 +2981,7 @@ function toOpenAIMessages(messages) {
|
|
|
2879
2981
|
};
|
|
2880
2982
|
});
|
|
2881
2983
|
}
|
|
2882
|
-
function normalizeOpenAIProviderResponse(payload) {
|
|
2984
|
+
function normalizeOpenAIProviderResponse(payload, request) {
|
|
2883
2985
|
const parsed = OpenAIResponseSchema.safeParse(payload.body);
|
|
2884
2986
|
if (!parsed.success) {
|
|
2885
2987
|
const issue = parsed.error.issues[0]?.message ?? "Invalid provider payload";
|
|
@@ -2887,7 +2989,7 @@ function normalizeOpenAIProviderResponse(payload) {
|
|
|
2887
2989
|
return { error: issue };
|
|
2888
2990
|
}
|
|
2889
2991
|
const cloned = structuredClone(parsed.data);
|
|
2890
|
-
const { response, metadata } = fixKimiResponse(cloned);
|
|
2992
|
+
const { response, metadata } = fixKimiResponse(cloned, request);
|
|
2891
2993
|
const normalized = OpenAIResponseSchema.safeParse(response);
|
|
2892
2994
|
if (!normalized.success) {
|
|
2893
2995
|
const issue = normalized.error.issues[0]?.message ?? "Provider payload invalid after normalization";
|
|
@@ -3002,7 +3104,7 @@ class OpenAIProviderAdapter {
|
|
|
3002
3104
|
if (payload.status >= 400) {
|
|
3003
3105
|
return toUlxErrorResponse(payload, request);
|
|
3004
3106
|
}
|
|
3005
|
-
const normalized = normalizeOpenAIProviderResponse(payload);
|
|
3107
|
+
const normalized = normalizeOpenAIProviderResponse(payload, request);
|
|
3006
3108
|
if ("error" in normalized) {
|
|
3007
3109
|
return {
|
|
3008
3110
|
id: request.id,
|
|
@@ -3090,7 +3192,7 @@ class OpenRouterProviderAdapter {
|
|
|
3090
3192
|
if (payload.status >= 400) {
|
|
3091
3193
|
return toUlxErrorResponse(payload, request);
|
|
3092
3194
|
}
|
|
3093
|
-
const normalized = normalizeOpenAIProviderResponse(payload);
|
|
3195
|
+
const normalized = normalizeOpenAIProviderResponse(payload, request);
|
|
3094
3196
|
if ("error" in normalized) {
|
|
3095
3197
|
return {
|
|
3096
3198
|
id: request.id,
|
|
@@ -3422,7 +3524,7 @@ class VertexProviderAdapter {
|
|
|
3422
3524
|
return toUlxErrorResponse(payload, request);
|
|
3423
3525
|
}
|
|
3424
3526
|
if (request.model && MAAS_MODEL_PATTERN.test(request.model)) {
|
|
3425
|
-
const normalized = normalizeOpenAIProviderResponse(payload);
|
|
3527
|
+
const normalized = normalizeOpenAIProviderResponse(payload, request);
|
|
3426
3528
|
if ("error" in normalized) {
|
|
3427
3529
|
return {
|
|
3428
3530
|
id: request.id,
|
|
@@ -3977,7 +4079,7 @@ ${content}`
|
|
|
3977
4079
|
logger.warn({ requestId: request.id }, "Detected prior assistant termination; requesting synthetic response");
|
|
3978
4080
|
return true;
|
|
3979
4081
|
}
|
|
3980
|
-
if (toolCalls.length === 1 && this.checkTerminationHeuristic(message, toolCalls[0])) {
|
|
4082
|
+
if (toolCalls.length === 1 && this.checkTerminationHeuristic(message, toolCalls[0], request)) {
|
|
3981
4083
|
requestSyntheticResponse(request.state);
|
|
3982
4084
|
logger.warn({ requestId: request.id }, "Detected TodoWrite termination heuristic; requesting synthetic response");
|
|
3983
4085
|
return true;
|
|
@@ -3985,7 +4087,10 @@ ${content}`
|
|
|
3985
4087
|
}
|
|
3986
4088
|
return false;
|
|
3987
4089
|
}
|
|
3988
|
-
checkTerminationHeuristic(message, toolCall) {
|
|
4090
|
+
checkTerminationHeuristic(message, toolCall, request) {
|
|
4091
|
+
if (!request.model.toLowerCase().includes("kimi")) {
|
|
4092
|
+
return false;
|
|
4093
|
+
}
|
|
3989
4094
|
if (!hasCaseInsensitiveTerminationKeywords(message.content)) {
|
|
3990
4095
|
return false;
|
|
3991
4096
|
}
|
|
@@ -4014,7 +4119,7 @@ class EnsureToolCallResponseTransform {
|
|
|
4014
4119
|
const response = context.response;
|
|
4015
4120
|
if (!ensureState || !response)
|
|
4016
4121
|
return;
|
|
4017
|
-
if (this.checkTerminationHeuristic(response)) {
|
|
4122
|
+
if (this.checkTerminationHeuristic(response, context.request)) {
|
|
4018
4123
|
ensureState.pendingReminder = false;
|
|
4019
4124
|
logger.info({ requestId: context.request.id }, "EnsureToolCall satisfied by TodoWrite + keyword heuristics");
|
|
4020
4125
|
return;
|
|
@@ -4088,7 +4193,10 @@ class EnsureToolCallResponseTransform {
|
|
|
4088
4193
|
}
|
|
4089
4194
|
return false;
|
|
4090
4195
|
}
|
|
4091
|
-
checkTerminationHeuristic(response) {
|
|
4196
|
+
checkTerminationHeuristic(response, request) {
|
|
4197
|
+
if (!request.model.toLowerCase().includes("kimi")) {
|
|
4198
|
+
return false;
|
|
4199
|
+
}
|
|
4092
4200
|
const messageBlock = response.output.find((block) => block.type === "message");
|
|
4093
4201
|
if (!messageBlock || messageBlock.type !== "message")
|
|
4094
4202
|
return false;
|
|
@@ -5699,13 +5807,15 @@ async function handleRequest(req, reply, body, modelRegistry, pipeline, logStore
|
|
|
5699
5807
|
method: req.method,
|
|
5700
5808
|
url: req.url,
|
|
5701
5809
|
statusCode: 400,
|
|
5810
|
+
provider: "schema_validation_failed",
|
|
5702
5811
|
startedAt,
|
|
5703
5812
|
finishedAt: Date.now(),
|
|
5704
5813
|
requestBody: req.body,
|
|
5705
5814
|
responseBody: errorBody,
|
|
5706
5815
|
operation: options.operation,
|
|
5707
5816
|
clientFormat: options.clientFormat,
|
|
5708
|
-
profile: options.profile
|
|
5817
|
+
profile: options.profile,
|
|
5818
|
+
summary: summarizeError(error, "schema_validation")
|
|
5709
5819
|
});
|
|
5710
5820
|
reply.status(400).send(errorBody);
|
|
5711
5821
|
return;
|
|
@@ -5730,13 +5840,15 @@ async function handleRequest(req, reply, body, modelRegistry, pipeline, logStore
|
|
|
5730
5840
|
url: req.url,
|
|
5731
5841
|
statusCode: 400,
|
|
5732
5842
|
model,
|
|
5843
|
+
provider: "resolution_failed",
|
|
5733
5844
|
startedAt,
|
|
5734
5845
|
finishedAt: Date.now(),
|
|
5735
5846
|
requestBody: parsedBody,
|
|
5736
5847
|
responseBody: errorBody,
|
|
5737
5848
|
operation: options.operation,
|
|
5738
5849
|
clientFormat: options.clientFormat,
|
|
5739
|
-
profile: options.profile
|
|
5850
|
+
profile: options.profile,
|
|
5851
|
+
summary: summarizeError(error, "model_resolution")
|
|
5740
5852
|
});
|
|
5741
5853
|
reply.status(400).send(errorBody);
|
|
5742
5854
|
return;
|
|
@@ -5813,7 +5925,8 @@ async function handleRequest(req, reply, body, modelRegistry, pipeline, logStore
|
|
|
5813
5925
|
requestBody: upstreamModel ? { ...parsedBody, model: upstreamModel } : parsedBody,
|
|
5814
5926
|
responseBody: { error: errorDetails },
|
|
5815
5927
|
providerRequestBody: null,
|
|
5816
|
-
providerResponseBody: null
|
|
5928
|
+
providerResponseBody: null,
|
|
5929
|
+
summary: summarizeError(error, "pipeline_execution")
|
|
5817
5930
|
});
|
|
5818
5931
|
reply.status(500).send({ error: { message: "Internal proxy error" } });
|
|
5819
5932
|
}
|
|
@@ -5902,6 +6015,13 @@ function summarize(response) {
|
|
|
5902
6015
|
preview
|
|
5903
6016
|
});
|
|
5904
6017
|
}
|
|
6018
|
+
function summarizeError(error, errorType) {
|
|
6019
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
6020
|
+
return JSON.stringify({
|
|
6021
|
+
error_type: errorType,
|
|
6022
|
+
error_message: errorMessage.slice(0, 500)
|
|
6023
|
+
});
|
|
6024
|
+
}
|
|
5905
6025
|
function isPrematureCloseError(error) {
|
|
5906
6026
|
if (!error || typeof error !== "object")
|
|
5907
6027
|
return false;
|
|
@@ -5925,5 +6045,5 @@ async function bootstrap() {
|
|
|
5925
6045
|
}
|
|
5926
6046
|
bootstrap();
|
|
5927
6047
|
|
|
5928
|
-
//# debugId=
|
|
6048
|
+
//# debugId=49E2D016D6E3465664756E2164756E21
|
|
5929
6049
|
//# sourceMappingURL=index.js.map
|