@khanglvm/llm-router 2.0.0-beta.1 → 2.0.0
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/CHANGELOG.md +27 -0
- package/README.md +163 -426
- package/package.json +3 -3
- package/src/cli/router-module.js +2773 -2587
- package/src/cli-entry.js +32 -103
- package/src/node/activity-log.js +119 -0
- package/src/node/coding-tool-config.js +85 -11
- package/src/node/config-workflows.js +51 -12
- package/src/node/instance-state.js +1 -1
- package/src/node/litellm-context-catalog.js +184 -0
- package/src/node/local-server.js +23 -3
- package/src/node/port-reclaim.js +2 -2
- package/src/node/start-command.js +22 -22
- package/src/node/startup-manager.js +3 -3
- package/src/node/web-command.js +1 -1
- package/src/node/web-console-assets.js +1 -1
- package/src/node/web-console-client.js +34 -29
- package/src/node/web-console-server.js +420 -38
- package/src/node/web-console-styles.generated.js +1 -1
- package/src/node/web-console-ui/buffered-text-input.js +133 -0
- package/src/node/web-console-ui/config-editor-utils.js +57 -4
- package/src/node/web-console-ui/dropdown-placement.js +153 -0
- package/src/node/web-console-ui/select-search-utils.js +6 -0
- package/src/node/web-console-ui/transient-integer-input-utils.js +12 -0
- package/src/runtime/balancer.js +78 -1
- package/src/runtime/codex-request-transformer.js +16 -7
- package/src/runtime/config.js +448 -12
- package/src/runtime/handler/amp-response.js +5 -3
- package/src/runtime/handler/amp-web-search.js +2232 -0
- package/src/runtime/handler/fallback.js +30 -2
- package/src/runtime/handler/provider-call.js +353 -36
- package/src/runtime/handler/provider-translation.js +14 -0
- package/src/runtime/handler/request.js +128 -2
- package/src/runtime/handler/route-debug.js +36 -0
- package/src/runtime/handler.js +210 -20
- package/src/runtime/subscription-provider.js +1 -1
- package/src/shared/coding-tool-bindings.js +49 -0
- package/src/shared/local-router-defaults.js +62 -0
- package/src/translator/request/claude-to-openai.js +43 -0
|
@@ -3,6 +3,9 @@ export const LOCAL_ROUTER_PORT = 8376;
|
|
|
3
3
|
export const LOCAL_ROUTER_ORIGIN = `http://${LOCAL_ROUTER_HOST}:${LOCAL_ROUTER_PORT}`;
|
|
4
4
|
export const LOCAL_ROUTER_OPENAI_BASE_URL = `${LOCAL_ROUTER_ORIGIN}/openai/v1`;
|
|
5
5
|
export const LOCAL_ROUTER_ANTHROPIC_BASE_URL = `${LOCAL_ROUTER_ORIGIN}/anthropic`;
|
|
6
|
+
const DEFAULT_ACTIVITY_LOG_SETTINGS = Object.freeze({
|
|
7
|
+
enabled: true
|
|
8
|
+
});
|
|
6
9
|
|
|
7
10
|
function toBoolean(value, fallback = false) {
|
|
8
11
|
if (value === undefined || value === null || value === "") return fallback;
|
|
@@ -45,6 +48,57 @@ export function buildPersistedLocalServerMetadata(source = {}, fallback = {}) {
|
|
|
45
48
|
return metadata;
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
export function buildActivityLogSettings(source = {}, fallback = {}) {
|
|
52
|
+
const base = {
|
|
53
|
+
enabled: toBoolean(fallback?.enabled, DEFAULT_ACTIVITY_LOG_SETTINGS.enabled)
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
enabled: toBoolean(source?.enabled, base.enabled)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function buildPersistedActivityLogMetadata(source = {}, fallback = {}) {
|
|
62
|
+
const resolved = buildActivityLogSettings(source, fallback);
|
|
63
|
+
const defaults = buildActivityLogSettings();
|
|
64
|
+
const metadata = {};
|
|
65
|
+
|
|
66
|
+
if (resolved.enabled !== defaults.enabled) metadata.enabled = resolved.enabled;
|
|
67
|
+
|
|
68
|
+
return metadata;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function readActivityLogSettings(config, fallback = DEFAULT_ACTIVITY_LOG_SETTINGS) {
|
|
72
|
+
return buildActivityLogSettings(config?.metadata?.activityLog, fallback);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function applyActivityLogSettings(config, settings) {
|
|
76
|
+
const next = config && typeof config === "object" && !Array.isArray(config)
|
|
77
|
+
? JSON.parse(JSON.stringify(config))
|
|
78
|
+
: {};
|
|
79
|
+
const resolved = readActivityLogSettings({ metadata: { activityLog: settings } }, DEFAULT_ACTIVITY_LOG_SETTINGS);
|
|
80
|
+
const persisted = buildPersistedActivityLogMetadata(resolved);
|
|
81
|
+
const metadata = next.metadata && typeof next.metadata === "object" && !Array.isArray(next.metadata)
|
|
82
|
+
? { ...next.metadata }
|
|
83
|
+
: {};
|
|
84
|
+
|
|
85
|
+
if (Object.keys(persisted).length > 0) {
|
|
86
|
+
next.metadata = {
|
|
87
|
+
...metadata,
|
|
88
|
+
activityLog: persisted
|
|
89
|
+
};
|
|
90
|
+
} else if (Object.prototype.hasOwnProperty.call(metadata, "activityLog")) {
|
|
91
|
+
delete metadata.activityLog;
|
|
92
|
+
if (Object.keys(metadata).length > 0) {
|
|
93
|
+
next.metadata = metadata;
|
|
94
|
+
} else {
|
|
95
|
+
delete next.metadata;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return next;
|
|
100
|
+
}
|
|
101
|
+
|
|
48
102
|
export function sanitizeRuntimeMetadata(metadata) {
|
|
49
103
|
if (!isPlainObject(metadata)) return {};
|
|
50
104
|
|
|
@@ -57,6 +111,14 @@ export function sanitizeRuntimeMetadata(metadata) {
|
|
|
57
111
|
delete next.localServer;
|
|
58
112
|
}
|
|
59
113
|
}
|
|
114
|
+
if (Object.prototype.hasOwnProperty.call(next, "activityLog")) {
|
|
115
|
+
const activityLog = buildPersistedActivityLogMetadata(next.activityLog);
|
|
116
|
+
if (Object.keys(activityLog).length > 0) {
|
|
117
|
+
next.activityLog = activityLog;
|
|
118
|
+
} else {
|
|
119
|
+
delete next.activityLog;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
60
122
|
|
|
61
123
|
return next;
|
|
62
124
|
}
|
|
@@ -4,6 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
import { FORMATS } from "../formats.js";
|
|
6
6
|
|
|
7
|
+
const WEB_SEARCH_TOOL_NAME = "web_search";
|
|
8
|
+
const WEB_SEARCH_FUNCTION_PARAMETERS = {
|
|
9
|
+
type: "object",
|
|
10
|
+
properties: {
|
|
11
|
+
query: {
|
|
12
|
+
type: "string",
|
|
13
|
+
description: "The search query to run against the web."
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
required: ["query"],
|
|
17
|
+
additionalProperties: false
|
|
18
|
+
};
|
|
19
|
+
|
|
7
20
|
function cloneCacheControl(value) {
|
|
8
21
|
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
9
22
|
const type = typeof value.type === "string" ? value.type.trim() : "";
|
|
@@ -40,6 +53,33 @@ function convertClaudeSystemToOpenAIContent(system) {
|
|
|
40
53
|
return parts;
|
|
41
54
|
}
|
|
42
55
|
|
|
56
|
+
function normalizeWebSearchType(value) {
|
|
57
|
+
return String(value || "").trim().toLowerCase();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isClaudeNativeWebSearchTool(tool) {
|
|
61
|
+
if (!tool || typeof tool !== "object") return false;
|
|
62
|
+
const normalizedType = normalizeWebSearchType(tool.type);
|
|
63
|
+
return normalizedType === "web_search"
|
|
64
|
+
|| (normalizedType.startsWith("web_search_") && !normalizedType.startsWith("web_search_preview"));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function convertClaudeWebSearchTool(tool) {
|
|
68
|
+
const description = typeof tool?.description === "string" && tool.description.trim()
|
|
69
|
+
? tool.description.trim()
|
|
70
|
+
: "Search the web for current information, news, documentation, or real-time facts.";
|
|
71
|
+
const cacheControl = cloneCacheControl(tool?.cache_control);
|
|
72
|
+
return {
|
|
73
|
+
type: "function",
|
|
74
|
+
function: {
|
|
75
|
+
name: WEB_SEARCH_TOOL_NAME,
|
|
76
|
+
description,
|
|
77
|
+
parameters: WEB_SEARCH_FUNCTION_PARAMETERS
|
|
78
|
+
},
|
|
79
|
+
...(cacheControl ? { cache_control: cacheControl } : {})
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
43
83
|
/**
|
|
44
84
|
* Convert Claude request to OpenAI format
|
|
45
85
|
*/
|
|
@@ -89,6 +129,9 @@ export function claudeToOpenAIRequest(model, body, stream) {
|
|
|
89
129
|
// Tools
|
|
90
130
|
if (body.tools && Array.isArray(body.tools)) {
|
|
91
131
|
result.tools = body.tools.map(tool => {
|
|
132
|
+
if (isClaudeNativeWebSearchTool(tool)) {
|
|
133
|
+
return convertClaudeWebSearchTool(tool);
|
|
134
|
+
}
|
|
92
135
|
const cacheControl = cloneCacheControl(tool.cache_control);
|
|
93
136
|
return {
|
|
94
137
|
type: "function",
|