kimi-proxy 0.1.1 → 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 +142 -20
- 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;
|
|
@@ -4058,6 +4163,8 @@ class EnsureToolCallResponseTransform {
|
|
|
4058
4163
|
}
|
|
4059
4164
|
if (!hasContent && finalAnswer) {
|
|
4060
4165
|
messageBlock.content = [{ type: "text", text: finalAnswer }];
|
|
4166
|
+
} else if (hasContent && finalAnswer) {
|
|
4167
|
+
messageBlock.content.push({ type: "text", text: finalAnswer });
|
|
4061
4168
|
}
|
|
4062
4169
|
continue;
|
|
4063
4170
|
}
|
|
@@ -4086,7 +4193,10 @@ class EnsureToolCallResponseTransform {
|
|
|
4086
4193
|
}
|
|
4087
4194
|
return false;
|
|
4088
4195
|
}
|
|
4089
|
-
checkTerminationHeuristic(response) {
|
|
4196
|
+
checkTerminationHeuristic(response, request) {
|
|
4197
|
+
if (!request.model.toLowerCase().includes("kimi")) {
|
|
4198
|
+
return false;
|
|
4199
|
+
}
|
|
4090
4200
|
const messageBlock = response.output.find((block) => block.type === "message");
|
|
4091
4201
|
if (!messageBlock || messageBlock.type !== "message")
|
|
4092
4202
|
return false;
|
|
@@ -4189,7 +4299,7 @@ class ModelRegistry {
|
|
|
4189
4299
|
}));
|
|
4190
4300
|
}
|
|
4191
4301
|
resolve(modelName, profile) {
|
|
4192
|
-
let variants = this.groups.get(modelName);
|
|
4302
|
+
let variants = this.groups.get(modelName)?.filter((v) => !v.profile || v.profile === profile);
|
|
4193
4303
|
if (variants && variants.length > 0 && profile) {
|
|
4194
4304
|
const filtered = variants.filter((v) => v.profile === profile);
|
|
4195
4305
|
if (filtered.length > 0) {
|
|
@@ -5697,13 +5807,15 @@ async function handleRequest(req, reply, body, modelRegistry, pipeline, logStore
|
|
|
5697
5807
|
method: req.method,
|
|
5698
5808
|
url: req.url,
|
|
5699
5809
|
statusCode: 400,
|
|
5810
|
+
provider: "schema_validation_failed",
|
|
5700
5811
|
startedAt,
|
|
5701
5812
|
finishedAt: Date.now(),
|
|
5702
5813
|
requestBody: req.body,
|
|
5703
5814
|
responseBody: errorBody,
|
|
5704
5815
|
operation: options.operation,
|
|
5705
5816
|
clientFormat: options.clientFormat,
|
|
5706
|
-
profile: options.profile
|
|
5817
|
+
profile: options.profile,
|
|
5818
|
+
summary: summarizeError(error, "schema_validation")
|
|
5707
5819
|
});
|
|
5708
5820
|
reply.status(400).send(errorBody);
|
|
5709
5821
|
return;
|
|
@@ -5728,13 +5840,15 @@ async function handleRequest(req, reply, body, modelRegistry, pipeline, logStore
|
|
|
5728
5840
|
url: req.url,
|
|
5729
5841
|
statusCode: 400,
|
|
5730
5842
|
model,
|
|
5843
|
+
provider: "resolution_failed",
|
|
5731
5844
|
startedAt,
|
|
5732
5845
|
finishedAt: Date.now(),
|
|
5733
5846
|
requestBody: parsedBody,
|
|
5734
5847
|
responseBody: errorBody,
|
|
5735
5848
|
operation: options.operation,
|
|
5736
5849
|
clientFormat: options.clientFormat,
|
|
5737
|
-
profile: options.profile
|
|
5850
|
+
profile: options.profile,
|
|
5851
|
+
summary: summarizeError(error, "model_resolution")
|
|
5738
5852
|
});
|
|
5739
5853
|
reply.status(400).send(errorBody);
|
|
5740
5854
|
return;
|
|
@@ -5811,7 +5925,8 @@ async function handleRequest(req, reply, body, modelRegistry, pipeline, logStore
|
|
|
5811
5925
|
requestBody: upstreamModel ? { ...parsedBody, model: upstreamModel } : parsedBody,
|
|
5812
5926
|
responseBody: { error: errorDetails },
|
|
5813
5927
|
providerRequestBody: null,
|
|
5814
|
-
providerResponseBody: null
|
|
5928
|
+
providerResponseBody: null,
|
|
5929
|
+
summary: summarizeError(error, "pipeline_execution")
|
|
5815
5930
|
});
|
|
5816
5931
|
reply.status(500).send({ error: { message: "Internal proxy error" } });
|
|
5817
5932
|
}
|
|
@@ -5900,6 +6015,13 @@ function summarize(response) {
|
|
|
5900
6015
|
preview
|
|
5901
6016
|
});
|
|
5902
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
|
+
}
|
|
5903
6025
|
function isPrematureCloseError(error) {
|
|
5904
6026
|
if (!error || typeof error !== "object")
|
|
5905
6027
|
return false;
|
|
@@ -5923,5 +6045,5 @@ async function bootstrap() {
|
|
|
5923
6045
|
}
|
|
5924
6046
|
bootstrap();
|
|
5925
6047
|
|
|
5926
|
-
//# debugId=
|
|
6048
|
+
//# debugId=49E2D016D6E3465664756E2164756E21
|
|
5927
6049
|
//# sourceMappingURL=index.js.map
|