@staticeng/opencode-litellm 0.1.3 → 0.1.5
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 +0 -0
- package/README.md +0 -0
- package/dist/helpers.d.ts +2 -2
- package/dist/helpers.js +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.js +8 -3
- package/dist/litellm.d.ts +2 -1
- package/dist/litellm.js +9 -2
- package/dist/mapping.d.ts +7 -13
- package/dist/mapping.js +75 -86
- package/dist/merge.d.ts +0 -0
- package/dist/merge.js +0 -0
- package/dist/options.d.ts +0 -0
- package/dist/options.js +0 -0
- package/dist/types.d.ts +56 -3
- package/dist/types.js +0 -0
- package/package.json +1 -1
package/LICENSE
CHANGED
|
File without changes
|
package/README.md
CHANGED
|
File without changes
|
package/dist/helpers.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { buildProvider,
|
|
1
|
+
export { buildProvider, buildProviderV2, mapModels, mapModelsV2 } from "./mapping.js";
|
|
2
2
|
export { fetchModelInfo } from "./litellm.js";
|
|
3
3
|
export { deepMerge } from "./merge.js";
|
|
4
|
-
export type { LiteLLMPluginOptions,
|
|
4
|
+
export type { LiteLLMModelInfo, LiteLLMPluginOptions, OpenCodeModel, OpenCodeModelV2, OpenCodeProvider, OpenCodeProviderV2, } from "./types.js";
|
package/dist/helpers.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { buildProvider,
|
|
1
|
+
export { buildProvider, buildProviderV2, mapModels, mapModelsV2 } from "./mapping.js";
|
|
2
2
|
export { fetchModelInfo } from "./litellm.js";
|
|
3
3
|
export { deepMerge } from "./merge.js";
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fetchModelInfo } from "./litellm.js";
|
|
2
|
-
import { buildProvider } from "./mapping.js";
|
|
2
|
+
import { buildProvider, buildProviderV2 } from "./mapping.js";
|
|
3
3
|
import { resolveOptions } from "./options.js";
|
|
4
4
|
async function log(ctx, level, message, extra = {}) {
|
|
5
5
|
await ctx.client?.app?.log?.({ body: { service: "opencode-litellm", level, message, extra } });
|
|
@@ -11,21 +11,26 @@ const LiteLLMPlugin = async (ctx, options) => {
|
|
|
11
11
|
try {
|
|
12
12
|
const models = await fetchModelInfo(resolved);
|
|
13
13
|
const provider = buildProvider(models, resolved);
|
|
14
|
+
const config = cfg;
|
|
15
|
+
const providerV2 = buildProviderV2(models, resolved);
|
|
14
16
|
cfg.provider = {
|
|
15
17
|
...(cfg.provider ?? {}),
|
|
16
18
|
[resolved.providerKey]: provider,
|
|
17
19
|
};
|
|
20
|
+
config.providers = {
|
|
21
|
+
...(config.providers ?? {}),
|
|
22
|
+
[resolved.providerKey]: providerV2,
|
|
23
|
+
};
|
|
18
24
|
await log(ctx, "info", "loaded LiteLLM provider metadata", {
|
|
19
25
|
providerKey: resolved.providerKey,
|
|
20
26
|
modelCount: Object.keys(provider.models ?? {}).length,
|
|
27
|
+
configProviderV2: true,
|
|
21
28
|
});
|
|
22
29
|
}
|
|
23
30
|
catch (error) {
|
|
24
|
-
const errorShape = error instanceof Error && "responseShape" in error ? error.responseShape : undefined;
|
|
25
31
|
await log(ctx, resolved.strict ? "error" : "warn", "failed to load LiteLLM provider metadata", {
|
|
26
32
|
providerKey: resolved.providerKey,
|
|
27
33
|
error: error instanceof Error ? error.message : String(error),
|
|
28
|
-
...(errorShape ? { responseShape: errorShape } : {}),
|
|
29
34
|
});
|
|
30
35
|
if (resolved.strict)
|
|
31
36
|
throw error;
|
package/dist/litellm.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
1
|
import { type ResolvedOptions } from "./options.js";
|
|
2
|
-
|
|
2
|
+
import type { LiteLLMModelInfo } from "./types.js";
|
|
3
|
+
export declare function fetchModelInfo(options: ResolvedOptions, fetchImpl?: typeof fetch): Promise<LiteLLMModelInfo[]>;
|
package/dist/litellm.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { normalizeModelListResponse } from "./mapping.js";
|
|
2
1
|
import { modelInfoURL } from "./options.js";
|
|
2
|
+
function isModelInfoArray(value) {
|
|
3
|
+
return Array.isArray(value);
|
|
4
|
+
}
|
|
3
5
|
export async function fetchModelInfo(options, fetchImpl = fetch) {
|
|
4
6
|
if (!options.baseURL)
|
|
5
7
|
throw new Error("opencode-litellm requires options.baseURL or LITELLM_BASE_URL");
|
|
@@ -18,7 +20,12 @@ export async function fetchModelInfo(options, fetchImpl = fetch) {
|
|
|
18
20
|
});
|
|
19
21
|
if (!response.ok)
|
|
20
22
|
throw new Error(`LiteLLM /model/info returned HTTP ${response.status}`);
|
|
21
|
-
|
|
23
|
+
const body = (await response.json());
|
|
24
|
+
if (isModelInfoArray(body))
|
|
25
|
+
return body;
|
|
26
|
+
if (isModelInfoArray(body.data))
|
|
27
|
+
return body.data;
|
|
28
|
+
throw new Error("LiteLLM /model/info response did not contain a data array");
|
|
22
29
|
}
|
|
23
30
|
finally {
|
|
24
31
|
clearTimeout(timeout);
|
package/dist/mapping.d.ts
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import type { LiteLLMModelInfo, LiteLLMPluginOptions, OpenCodeModel, OpenCodeProvider } from "./types.js";
|
|
2
|
-
export
|
|
3
|
-
type: string;
|
|
4
|
-
length?: number;
|
|
5
|
-
keyCount?: number;
|
|
6
|
-
arrayFields?: string[];
|
|
7
|
-
objectValueCount?: number;
|
|
8
|
-
scalarValueCount?: number;
|
|
9
|
-
};
|
|
10
|
-
export declare function modelListResponseShape(value: unknown): ModelListResponseShape;
|
|
11
|
-
export declare function normalizeModelListResponse(response: unknown): LiteLLMModelInfo[];
|
|
12
|
-
export declare function mapModels(response: unknown, options?: LiteLLMPluginOptions): {
|
|
1
|
+
import type { LiteLLMModelInfo, LiteLLMPluginOptions, OpenCodeModel, OpenCodeModelV2, OpenCodeProvider, OpenCodeProviderV2 } from "./types.js";
|
|
2
|
+
export declare function mapModels(models: LiteLLMModelInfo[], options?: LiteLLMPluginOptions): {
|
|
13
3
|
[k: string]: OpenCodeModel;
|
|
14
4
|
};
|
|
15
|
-
export declare function buildProvider(models:
|
|
5
|
+
export declare function buildProvider(models: LiteLLMModelInfo[], options?: LiteLLMPluginOptions): OpenCodeProvider;
|
|
6
|
+
export declare function mapModelsV2(models: LiteLLMModelInfo[], options?: LiteLLMPluginOptions): {
|
|
7
|
+
[k: string]: OpenCodeModelV2;
|
|
8
|
+
};
|
|
9
|
+
export declare function buildProviderV2(models: LiteLLMModelInfo[], options?: LiteLLMPluginOptions): OpenCodeProviderV2;
|
package/dist/mapping.js
CHANGED
|
@@ -3,51 +3,6 @@ const DEFAULT_MODES = new Set(["chat", "responses"]);
|
|
|
3
3
|
const NON_CHAT_MODES = new Set(["embedding", "rerank"]);
|
|
4
4
|
const TEXT_MODALITY = "text";
|
|
5
5
|
const TEXT_ONLY_MODELS = new Set(["minimax-m2.7", "minimax_m2.7"]);
|
|
6
|
-
const MODEL_LIST_RESPONSE_ERROR_MESSAGE = "LiteLLM model list response did not contain a model array";
|
|
7
|
-
const MODEL_ARRAY_FIELDS = ["data", "models", "model_info"];
|
|
8
|
-
const MODEL_INFO_FIELDS = new Set([
|
|
9
|
-
"id",
|
|
10
|
-
"base_model",
|
|
11
|
-
"mode",
|
|
12
|
-
"input_cost_per_token",
|
|
13
|
-
"output_cost_per_token",
|
|
14
|
-
"max_tokens",
|
|
15
|
-
"max_input_tokens",
|
|
16
|
-
"max_output_tokens",
|
|
17
|
-
"supports_function_calling",
|
|
18
|
-
"supports_tool_choice",
|
|
19
|
-
"supports_vision",
|
|
20
|
-
"supports_pdf_input",
|
|
21
|
-
"supports_reasoning",
|
|
22
|
-
"supported_modalities",
|
|
23
|
-
"output_modalities",
|
|
24
|
-
"supported_params",
|
|
25
|
-
]);
|
|
26
|
-
function isRecord(value) {
|
|
27
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
28
|
-
}
|
|
29
|
-
export function modelListResponseShape(value) {
|
|
30
|
-
if (Array.isArray(value))
|
|
31
|
-
return { type: "array", length: value.length };
|
|
32
|
-
if (value === null)
|
|
33
|
-
return { type: "null" };
|
|
34
|
-
if (!isRecord(value))
|
|
35
|
-
return { type: typeof value };
|
|
36
|
-
const values = Object.values(value);
|
|
37
|
-
return {
|
|
38
|
-
type: "object",
|
|
39
|
-
keyCount: Object.keys(value).length,
|
|
40
|
-
arrayFields: MODEL_ARRAY_FIELDS.filter((field) => Array.isArray(value[field])),
|
|
41
|
-
objectValueCount: values.filter(isRecord).length,
|
|
42
|
-
scalarValueCount: values.filter((entry) => !isRecord(entry) && !Array.isArray(entry)).length,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
function modelListResponseError(response) {
|
|
46
|
-
return Object.assign(new Error(MODEL_LIST_RESPONSE_ERROR_MESSAGE), {
|
|
47
|
-
name: "LiteLLMModelListResponseError",
|
|
48
|
-
responseShape: modelListResponseShape(response),
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
6
|
function asString(value) {
|
|
52
7
|
return typeof value === "string" && value.trim() ? value : undefined;
|
|
53
8
|
}
|
|
@@ -65,44 +20,6 @@ function unique(values) {
|
|
|
65
20
|
function modelName(model) {
|
|
66
21
|
return asString(model.model_name) ?? asString(model.litellm_params?.model) ?? asString(model.model_info?.id);
|
|
67
22
|
}
|
|
68
|
-
function hasModelInfoFields(value) {
|
|
69
|
-
return Object.keys(value).some((key) => MODEL_INFO_FIELDS.has(key));
|
|
70
|
-
}
|
|
71
|
-
function isPlausibleMappedModel(value) {
|
|
72
|
-
if (isRecord(value.model_info) && hasModelInfoFields(value.model_info))
|
|
73
|
-
return true;
|
|
74
|
-
if (isRecord(value.litellm_params) && typeof value.litellm_params.model === "string")
|
|
75
|
-
return true;
|
|
76
|
-
return hasModelInfoFields(value);
|
|
77
|
-
}
|
|
78
|
-
function normalizeMappedModel(name, value) {
|
|
79
|
-
if (!isRecord(value))
|
|
80
|
-
return undefined;
|
|
81
|
-
const model = value;
|
|
82
|
-
if (modelName(model))
|
|
83
|
-
return model;
|
|
84
|
-
if (!isPlausibleMappedModel(value))
|
|
85
|
-
return undefined;
|
|
86
|
-
return { ...model, model_name: name };
|
|
87
|
-
}
|
|
88
|
-
export function normalizeModelListResponse(response) {
|
|
89
|
-
if (Array.isArray(response))
|
|
90
|
-
return response;
|
|
91
|
-
if (isRecord(response)) {
|
|
92
|
-
for (const field of MODEL_ARRAY_FIELDS) {
|
|
93
|
-
if (Array.isArray(response[field]))
|
|
94
|
-
return response[field];
|
|
95
|
-
}
|
|
96
|
-
if (MODEL_ARRAY_FIELDS.some((field) => field in response))
|
|
97
|
-
throw modelListResponseError(response);
|
|
98
|
-
const mappedModels = Object.entries(response)
|
|
99
|
-
.map(([name, value]) => normalizeMappedModel(name, value))
|
|
100
|
-
.filter((model) => Boolean(model));
|
|
101
|
-
if (mappedModels.length > 0)
|
|
102
|
-
return mappedModels;
|
|
103
|
-
}
|
|
104
|
-
throw modelListResponseError(response);
|
|
105
|
-
}
|
|
106
23
|
function mode(model) {
|
|
107
24
|
return asString(model.model_info?.mode) ?? "chat";
|
|
108
25
|
}
|
|
@@ -135,6 +52,22 @@ function cost(model) {
|
|
|
135
52
|
: { cache_read: perMillion(model.model_info?.cache_read_input_token_cost) }),
|
|
136
53
|
};
|
|
137
54
|
}
|
|
55
|
+
function v2Cost(model) {
|
|
56
|
+
if (!model.cost)
|
|
57
|
+
return undefined;
|
|
58
|
+
return {
|
|
59
|
+
input: model.cost.input,
|
|
60
|
+
output: model.cost.output,
|
|
61
|
+
...(!model.cost.cache_read && !model.cost.cache_write
|
|
62
|
+
? {}
|
|
63
|
+
: {
|
|
64
|
+
cache: {
|
|
65
|
+
...(model.cost.cache_read === undefined ? {} : { read: model.cost.cache_read }),
|
|
66
|
+
...(model.cost.cache_write === undefined ? {} : { write: model.cost.cache_write }),
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
138
71
|
function limit(model) {
|
|
139
72
|
const context = asNumber(model.model_info?.max_input_tokens) ?? asNumber(model.model_info?.max_tokens);
|
|
140
73
|
const output = asNumber(model.model_info?.max_output_tokens) ?? asNumber(model.model_info?.max_tokens);
|
|
@@ -183,7 +116,7 @@ function generatedModel(model) {
|
|
|
183
116
|
return undefined;
|
|
184
117
|
const mappedModalities = modalities(model);
|
|
185
118
|
return {
|
|
186
|
-
id:
|
|
119
|
+
id: name,
|
|
187
120
|
name,
|
|
188
121
|
...(asString(model.model_info?.base_model) ? { family: asString(model.model_info?.base_model) } : {}),
|
|
189
122
|
...(cost(model) ? { cost: cost(model) } : {}),
|
|
@@ -194,8 +127,43 @@ function generatedModel(model) {
|
|
|
194
127
|
...(model.model_info?.supports_function_calling || model.model_info?.supports_tool_choice ? { tool_call: true } : {}),
|
|
195
128
|
};
|
|
196
129
|
}
|
|
197
|
-
|
|
198
|
-
const
|
|
130
|
+
function v2Variants(model) {
|
|
131
|
+
const variants = model.variants;
|
|
132
|
+
if (!variants || typeof variants !== "object" || Array.isArray(variants))
|
|
133
|
+
return undefined;
|
|
134
|
+
return Object.entries(variants).map(([id, value]) => {
|
|
135
|
+
const raw = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
136
|
+
const body = raw.options && typeof raw.options === "object" && !Array.isArray(raw.options)
|
|
137
|
+
? raw.options
|
|
138
|
+
: raw;
|
|
139
|
+
return { id, body };
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
function v2Model(model, baseURL) {
|
|
143
|
+
const id = asString(model.id) ?? asString(model.name);
|
|
144
|
+
const name = asString(model.name);
|
|
145
|
+
const family = asString(model.family);
|
|
146
|
+
return {
|
|
147
|
+
...(family ? { family } : {}),
|
|
148
|
+
...(name ? { name } : {}),
|
|
149
|
+
api: {
|
|
150
|
+
...(id ? { id } : {}),
|
|
151
|
+
type: "aisdk",
|
|
152
|
+
package: "@ai-sdk/openai-compatible",
|
|
153
|
+
...(baseURL ? { url: baseURL } : {}),
|
|
154
|
+
settings: {},
|
|
155
|
+
},
|
|
156
|
+
capabilities: {
|
|
157
|
+
tools: model.tool_call ?? false,
|
|
158
|
+
input: model.modalities?.input ?? [TEXT_MODALITY],
|
|
159
|
+
output: model.modalities?.output ?? [TEXT_MODALITY],
|
|
160
|
+
},
|
|
161
|
+
...(v2Cost(model) ? { cost: v2Cost(model) } : {}),
|
|
162
|
+
...(model.limit ? { limit: { ...model.limit } } : {}),
|
|
163
|
+
...(v2Variants(model) ? { variants: v2Variants(model) } : {}),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
export function mapModels(models, options = {}) {
|
|
199
167
|
return Object.fromEntries(models
|
|
200
168
|
.filter((model) => allowedByFilters(model, options))
|
|
201
169
|
.map((model) => {
|
|
@@ -218,3 +186,24 @@ export function buildProvider(models, options = {}) {
|
|
|
218
186
|
models: mapModels(models, options),
|
|
219
187
|
}, options.providerOverrides);
|
|
220
188
|
}
|
|
189
|
+
export function mapModelsV2(models, options = {}) {
|
|
190
|
+
const legacy = mapModels(models, options);
|
|
191
|
+
return Object.fromEntries(Object.entries(legacy).map(([name, model]) => [name, v2Model(model, options.baseURL)]));
|
|
192
|
+
}
|
|
193
|
+
export function buildProviderV2(models, options = {}) {
|
|
194
|
+
return {
|
|
195
|
+
name: options.providerName ?? "LiteLLM",
|
|
196
|
+
api: {
|
|
197
|
+
type: "aisdk",
|
|
198
|
+
package: "@ai-sdk/openai-compatible",
|
|
199
|
+
...(options.baseURL ? { url: options.baseURL } : {}),
|
|
200
|
+
settings: {},
|
|
201
|
+
},
|
|
202
|
+
request: {
|
|
203
|
+
body: {
|
|
204
|
+
...(options.apiKey ? { apiKey: options.apiKey } : {}),
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
models: mapModelsV2(models, options),
|
|
208
|
+
};
|
|
209
|
+
}
|
package/dist/merge.d.ts
CHANGED
|
File without changes
|
package/dist/merge.js
CHANGED
|
File without changes
|
package/dist/options.d.ts
CHANGED
|
File without changes
|
package/dist/options.js
CHANGED
|
File without changes
|
package/dist/types.d.ts
CHANGED
|
@@ -21,6 +21,46 @@ export type OpenCodeModel = JsonObject & {
|
|
|
21
21
|
reasoning?: boolean;
|
|
22
22
|
tool_call?: boolean;
|
|
23
23
|
};
|
|
24
|
+
export type OpenCodeModelV2 = JsonObject & {
|
|
25
|
+
family?: string;
|
|
26
|
+
name?: string;
|
|
27
|
+
api?: {
|
|
28
|
+
id?: string;
|
|
29
|
+
type: "aisdk";
|
|
30
|
+
package: string;
|
|
31
|
+
url?: string;
|
|
32
|
+
settings?: JsonObject;
|
|
33
|
+
};
|
|
34
|
+
capabilities?: {
|
|
35
|
+
tools: boolean;
|
|
36
|
+
input: string[];
|
|
37
|
+
output: string[];
|
|
38
|
+
};
|
|
39
|
+
request?: {
|
|
40
|
+
headers?: Record<string, string>;
|
|
41
|
+
body?: JsonObject;
|
|
42
|
+
variant?: string;
|
|
43
|
+
};
|
|
44
|
+
variants?: Array<{
|
|
45
|
+
id: string;
|
|
46
|
+
headers?: Record<string, string>;
|
|
47
|
+
body?: JsonObject;
|
|
48
|
+
}>;
|
|
49
|
+
cost?: {
|
|
50
|
+
input: number;
|
|
51
|
+
output: number;
|
|
52
|
+
cache?: {
|
|
53
|
+
read?: number;
|
|
54
|
+
write?: number;
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
disabled?: boolean;
|
|
58
|
+
limit?: {
|
|
59
|
+
context?: number;
|
|
60
|
+
input?: number;
|
|
61
|
+
output?: number;
|
|
62
|
+
};
|
|
63
|
+
};
|
|
24
64
|
export type OpenCodeProvider = JsonObject & {
|
|
25
65
|
id?: string;
|
|
26
66
|
name?: string;
|
|
@@ -29,6 +69,20 @@ export type OpenCodeProvider = JsonObject & {
|
|
|
29
69
|
options?: JsonObject;
|
|
30
70
|
models: Record<string, OpenCodeModel>;
|
|
31
71
|
};
|
|
72
|
+
export type OpenCodeProviderV2 = JsonObject & {
|
|
73
|
+
name?: string;
|
|
74
|
+
api: {
|
|
75
|
+
type: "aisdk";
|
|
76
|
+
package: string;
|
|
77
|
+
url?: string;
|
|
78
|
+
settings?: JsonObject;
|
|
79
|
+
};
|
|
80
|
+
request: {
|
|
81
|
+
headers?: Record<string, string>;
|
|
82
|
+
body: JsonObject;
|
|
83
|
+
};
|
|
84
|
+
models: Record<string, OpenCodeModelV2>;
|
|
85
|
+
};
|
|
32
86
|
export type LiteLLMPluginOptions = {
|
|
33
87
|
baseURL?: string;
|
|
34
88
|
apiKey?: string;
|
|
@@ -69,13 +123,12 @@ export type LiteLLMModelInfo = JsonObject & {
|
|
|
69
123
|
supported_params?: string[];
|
|
70
124
|
};
|
|
71
125
|
};
|
|
72
|
-
export type LiteLLMModelInfoResponse =
|
|
126
|
+
export type LiteLLMModelInfoResponse = {
|
|
73
127
|
data?: LiteLLMModelInfo[];
|
|
74
|
-
models?: LiteLLMModelInfo[];
|
|
75
|
-
model_info?: LiteLLMModelInfo[];
|
|
76
128
|
};
|
|
77
129
|
export type OpenCodeConfig = {
|
|
78
130
|
provider?: Record<string, OpenCodeProvider>;
|
|
131
|
+
providers?: Record<string, OpenCodeProviderV2>;
|
|
79
132
|
};
|
|
80
133
|
export type PluginContext = {
|
|
81
134
|
client?: {
|
package/dist/types.js
CHANGED
|
File without changes
|