@staticeng/opencode-litellm 0.1.7 → 0.1.9

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/README.md CHANGED
@@ -15,7 +15,7 @@ npm install @staticeng/opencode-litellm
15
15
  "$schema": "https://opencode.ai/config.json",
16
16
  "plugin": [
17
17
  [
18
- "@staticeng/opencode-litellm",
18
+ "@staticeng/opencode-litellm@0.1.9",
19
19
  {
20
20
  "baseURL": "https://litellm.example.com/v1",
21
21
  "apiKeyEnv": "LITELLM_API_KEY"
@@ -33,6 +33,8 @@ export LITELLM_API_KEY="..."
33
33
 
34
34
  Do not put real API keys in `opencode.json`. If you pass `apiKey` directly, the plugin uses it only in memory and never logs it, but environment variables are recommended.
35
35
 
36
+ Shared or synchronized OpenCode configuration must use the published, pinned npm package as shown above. Do not use a local path or `file://` plugin reference for shared configuration.
37
+
36
38
  ## Options
37
39
 
38
40
  | Option | Default | Purpose |
@@ -56,6 +58,10 @@ The LiteLLM key must have permission to call `GET /model/info` and root `GET /mo
56
58
 
57
59
  LiteLLM metadata does not expose every OpenCode model field. Use `overrides` to add variants, thinking controls, cache costs, interleaved reasoning settings, limits, modalities, timeouts, or provider-specific options.
58
60
 
61
+ When LiteLLM publishes `model_info.supported_reasoning_efforts`, the plugin maps
62
+ that list to OpenCode variants automatically. It does not guess effort levels
63
+ from model names; the LiteLLM catalog remains the source of truth.
64
+
59
65
  ```json
60
66
  {
61
67
  "plugin": [
@@ -68,7 +74,7 @@ LiteLLM metadata does not expose every OpenCode model field. Use `overrides` to
68
74
  "chatgpt/gpt-5.5": {
69
75
  "limit": { "context": 262144, "output": 128000 },
70
76
  "variants": {
71
- "high": { "options": { "reasoning_effort": "high" } }
77
+ "high": { "reasoningEffort": "high" }
72
78
  },
73
79
  "interleaved": { "field": "reasoning_details" }
74
80
  }
@@ -86,6 +92,10 @@ LiteLLM metadata does not expose every OpenCode model field. Use `overrides` to
86
92
 
87
93
  Overrides win over generated values through a recursive deep merge. Arrays and scalar values are replaced rather than concatenated.
88
94
 
95
+ The exact model groups `deepseek-v4-flash-fp8-mtp` and `deepseek-v4-flash-fp8-mtp-norefusal` are an exception: the plugin exposes only `off`, `low`, `high`, and `max`. Semantic `off` emits canonical `none`; the other values pass through unchanged. At the OpenCode config boundary, disabled `medium` and `xhigh` tombstones remove generic generated variants, while the model-scoped `variantDefault: false` capability hides the UI's default sentinel on compatible OpenCode builds. The plugin strips that UI-only marker in `chat.params` only when both the LiteLLM provider and an exact target alias match. Target model and provider overrides cannot change these controls. No manual model entry in `opencode.json` is needed.
96
+
97
+ The exact model group `qwen3.8-27b-refusal-dial` similarly exposes only `off`, `low`, `medium`, and `xhigh`. Its semantic `off` sends `chat_template_kwargs.enable_thinking=false` without a `reasoning_effort`; the native effort modes pass through unchanged. Disabled `none`, `minimal`, `high`, and `max` tombstones suppress OpenCode's unsupported generated variants. Official OpenCode 1.18.23 still owns an intrinsic default selection (`Predeterminado`) outside model variants; plugin configuration cannot remove it, and leaving it selected omits reasoning controls and preserves the model's deployed default.
98
+
89
99
  ## Runtime Behavior
90
100
 
91
101
  - Fetches `${baseURL}/model/info`, then root `/model_group/info`, with separate authenticated and timed requests.
@@ -93,8 +103,11 @@ Overrides win over generated values through a recursive deep merge. Arrays and s
93
103
  - Adds or replaces both legacy `cfg.provider[providerKey]` and V2 `cfg.providers[providerKey]` during the config hook.
94
104
  - Keeps namespaced deployment IDs and augments them with exact bare model-group IDs; exact collisions are enriched deterministically.
95
105
  - Skips malformed group rows without a nonblank `model_group` and exposes reasoning capability on legacy and V2 model metadata.
106
+ - Generates exact DeepSeek V4 and Qwen3.8 reasoning variants for the supported aliases and sanitizes conflicting target overrides after merging.
96
107
  - Warns safely and keeps deployment models when group discovery fails in non-strict mode; strict mode mutates neither config surface on failure.
97
108
  - Does not write `opencode.json`, cache model metadata, or persist generated provider data.
109
+ - Emits the required `limit.output` as `0` when LiteLLM reports a context/input limit but no output
110
+ limit. The zero sentinel retains known metadata without inventing a positive output allowance.
98
111
  - Includes `chat` and `responses` modes by default; non-chat modes such as `embedding` and `rerank` are excluded unless explicitly included.
99
112
  - No HuggingFace fallback is included in v1.
100
113
 
@@ -110,3 +123,8 @@ npm pack --dry-run
110
123
  ```
111
124
 
112
125
  Confirm package contents before publishing and keep secrets out of fixtures, docs, logs, and config examples.
126
+
127
+ Publishing uses npm trusted publishing from the `npm-production` GitHub environment. Provenance
128
+ requires this GitHub repository to remain public. Before a release, configure npm's trusted
129
+ publisher for package `@staticeng/opencode-litellm` with this repository, workflow
130
+ `publish.yml`, and environment `npm-production`; no npm token is used by the workflow.
package/dist/helpers.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { buildProvider, buildProviderV2, mapModels, mapModelsV2, mergeModelInfo, normalizeModelListResponse, modelListResponseShape, } from "./mapping.js";
1
+ export { addOpenCodeVariantTombstones, buildProvider, buildProviderV2, isDeepSeekV4ReasoningModel, mapModels, mapModelsV2, mergeModelInfo, normalizeModelListResponse, modelListResponseShape, } from "./mapping.js";
2
2
  export { fetchModelGroupInfo, fetchModelInfo, normalizeModelGroupResponse } from "./litellm.js";
3
3
  export { modelGroupInfoURL, modelInfoURL } from "./options.js";
4
4
  export { deepMerge } from "./merge.js";
package/dist/helpers.js CHANGED
@@ -1,4 +1,4 @@
1
- export { buildProvider, buildProviderV2, mapModels, mapModelsV2, mergeModelInfo, normalizeModelListResponse, modelListResponseShape, } from "./mapping.js";
1
+ export { addOpenCodeVariantTombstones, buildProvider, buildProviderV2, isDeepSeekV4ReasoningModel, mapModels, mapModelsV2, mergeModelInfo, normalizeModelListResponse, modelListResponseShape, } from "./mapping.js";
2
2
  export { fetchModelGroupInfo, fetchModelInfo, normalizeModelGroupResponse } from "./litellm.js";
3
3
  export { modelGroupInfoURL, modelInfoURL } from "./options.js";
4
4
  export { deepMerge } from "./merge.js";
package/dist/index.d.ts CHANGED
@@ -2,5 +2,18 @@ import type { Config, PluginInput } from "@opencode-ai/plugin";
2
2
  import type { LiteLLMPluginOptions } from "./types.js";
3
3
  declare const LiteLLMPlugin: (ctx: PluginInput, options?: LiteLLMPluginOptions) => Promise<{
4
4
  config: (cfg: Config) => Promise<void>;
5
+ "chat.params": (input: {
6
+ sessionID: string;
7
+ agent: string;
8
+ model: import("@opencode-ai/sdk").Model;
9
+ provider: import("@opencode-ai/plugin").ProviderContext;
10
+ message: import("@opencode-ai/sdk").UserMessage;
11
+ }, output: {
12
+ temperature: number;
13
+ topP: number;
14
+ topK: number;
15
+ maxOutputTokens: number | undefined;
16
+ options: Record<string, any>;
17
+ }) => Promise<void>;
5
18
  }>;
6
19
  export default LiteLLMPlugin;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { fetchModelGroupInfo, fetchModelInfo } from "./litellm.js";
2
- import { buildProvider, buildProviderV2, mergeModelInfo } from "./mapping.js";
2
+ import { addOpenCodeVariantTombstones, buildProvider, buildProviderV2, isContractReasoningModel, mergeModelInfo, } 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 } });
@@ -25,7 +25,7 @@ const LiteLLMPlugin = async (ctx, options) => {
25
25
  throw error;
26
26
  }
27
27
  const models = mergeModelInfo(deployments, groups);
28
- const provider = buildProvider(models, resolved);
28
+ const provider = addOpenCodeVariantTombstones(buildProvider(models, resolved));
29
29
  const config = cfg;
30
30
  const providerV2 = buildProviderV2(models, resolved);
31
31
  cfg.provider = {
@@ -53,6 +53,14 @@ const LiteLLMPlugin = async (ctx, options) => {
53
53
  throw error;
54
54
  }
55
55
  },
56
+ "chat.params": (async (input, output) => {
57
+ const provider = input.provider;
58
+ if ((provider.info?.id ?? provider.id) !== resolved.providerKey)
59
+ return;
60
+ if (!isContractReasoningModel(input.model.id))
61
+ return;
62
+ delete output.options.variantDefault;
63
+ }),
56
64
  };
57
65
  };
58
66
  export default LiteLLMPlugin;
package/dist/mapping.d.ts CHANGED
@@ -10,6 +10,9 @@ export type ModelListResponseShape = {
10
10
  export declare function modelListResponseShape(value: unknown): ModelListResponseShape;
11
11
  export declare function mergeModelInfo(deployments: LiteLLMModelInfo[], groups: LiteLLMModelInfo[]): LiteLLMModelInfo[];
12
12
  export declare function normalizeModelListResponse(response: unknown): LiteLLMModelInfo[];
13
+ export declare function isDeepSeekV4ReasoningModel(name: string | undefined): boolean;
14
+ export declare function isContractReasoningModel(name: string | undefined): boolean;
15
+ export declare function addOpenCodeVariantTombstones(provider: OpenCodeProvider): OpenCodeProvider;
13
16
  export declare function mapModels(response: unknown, options?: LiteLLMPluginOptions): {
14
17
  [k: string]: OpenCodeModel;
15
18
  };
package/dist/mapping.js CHANGED
@@ -2,7 +2,39 @@ import { deepMerge } from "./merge.js";
2
2
  const DEFAULT_MODES = new Set(["chat", "responses"]);
3
3
  const NON_CHAT_MODES = new Set(["embedding", "rerank"]);
4
4
  const TEXT_MODALITY = "text";
5
+ const OPENCODE_MODALITIES = new Set(["text", "audio", "image", "video", "pdf"]);
5
6
  const TEXT_ONLY_MODELS = new Set(["minimax-m2.7", "minimax_m2.7"]);
7
+ const DEEPSEEK_V4_REASONING_MODELS = new Set([
8
+ "deepseek-v4-flash-fp8-mtp",
9
+ "deepseek-v4-flash-fp8-mtp-norefusal",
10
+ ]);
11
+ const QWEN_38_REASONING_MODELS = new Set(["qwen3.8-27b-refusal-dial"]);
12
+ const DEEPSEEK_V4_VARIANTS = {
13
+ off: { reasoningEffort: "none" },
14
+ low: { reasoningEffort: "low" },
15
+ high: { reasoningEffort: "high" },
16
+ max: { reasoningEffort: "max" },
17
+ };
18
+ const DEEPSEEK_V4_VARIANTS_V2 = [
19
+ { id: "off", body: { reasoning_effort: "none" } },
20
+ { id: "low", body: { reasoning_effort: "low" } },
21
+ { id: "high", body: { reasoning_effort: "high" } },
22
+ { id: "max", body: { reasoning_effort: "max" } },
23
+ ];
24
+ const QWEN_38_VARIANTS = {
25
+ off: { chat_template_kwargs: { enable_thinking: false } },
26
+ low: { reasoningEffort: "low" },
27
+ medium: { reasoningEffort: "medium" },
28
+ xhigh: { reasoningEffort: "xhigh" },
29
+ };
30
+ const QWEN_38_VARIANTS_V2 = [
31
+ { id: "off", body: { chat_template_kwargs: { enable_thinking: false } } },
32
+ { id: "low", body: { reasoning_effort: "low" } },
33
+ { id: "medium", body: { reasoning_effort: "medium" } },
34
+ { id: "xhigh", body: { reasoning_effort: "xhigh" } },
35
+ ];
36
+ const REASONING_EFFORTS = ["none", "minimal", "low", "medium", "high", "xhigh", "max", "ultra"];
37
+ const REASONING_EFFORT_SET = new Set(REASONING_EFFORTS);
6
38
  const NUMERIC_MODEL_INFO_FIELDS = new Set([
7
39
  "input_cost_per_token",
8
40
  "output_cost_per_token",
@@ -32,6 +64,9 @@ const MODEL_INFO_FIELDS = new Set([
32
64
  "output_modalities",
33
65
  "supported_params",
34
66
  ]);
67
+ function supportedModalities(values) {
68
+ return unique(values).filter((modality) => OPENCODE_MODALITIES.has(modality));
69
+ }
35
70
  function isRecord(value) {
36
71
  return typeof value === "object" && value !== null && !Array.isArray(value);
37
72
  }
@@ -92,6 +127,9 @@ function enrichMetadata(existing, incoming, key) {
92
127
  }
93
128
  return merged;
94
129
  }
130
+ if (key === "supports_reasoning" && typeof existing === "boolean" && typeof incoming === "boolean") {
131
+ return existing && incoming;
132
+ }
95
133
  if (typeof existing === "boolean" && typeof incoming === "boolean")
96
134
  return existing || incoming;
97
135
  if (isValidMetadata(existing, key))
@@ -201,12 +239,12 @@ function v2Cost(model) {
201
239
  function limit(model) {
202
240
  const context = asNumber(model.model_info?.max_input_tokens) ?? asNumber(model.model_info?.max_tokens);
203
241
  const output = asNumber(model.model_info?.max_output_tokens) ?? asNumber(model.model_info?.max_tokens);
204
- if (context === undefined || output === undefined)
242
+ if (context === undefined)
205
243
  return undefined;
206
244
  return {
207
245
  context,
208
246
  ...(asNumber(model.model_info?.max_input_tokens) === undefined ? {} : { input: asNumber(model.model_info?.max_input_tokens) }),
209
- output,
247
+ output: output ?? 0,
210
248
  };
211
249
  }
212
250
  function canonicalModelName(model) {
@@ -226,20 +264,80 @@ function modalities(model) {
226
264
  };
227
265
  }
228
266
  return {
229
- input: unique([
267
+ input: supportedModalities([
230
268
  TEXT_MODALITY,
231
269
  ...(model.model_info?.supported_modalities ?? []),
232
270
  ...(model.model_info?.supports_vision ? ["image"] : []),
233
271
  ...(model.model_info?.supports_pdf_input ? ["pdf"] : []),
234
272
  ]),
235
- output: unique([TEXT_MODALITY, ...(model.model_info?.output_modalities ?? [])]),
273
+ output: supportedModalities([TEXT_MODALITY, ...(model.model_info?.output_modalities ?? [])]),
236
274
  };
237
275
  }
238
276
  function supportsReasoning(model) {
239
- return Boolean(model.model_info?.supports_reasoning ||
240
- model.model_info?.supported_params?.includes("reasoning_effort") ||
277
+ if (typeof model.model_info?.supports_reasoning === "boolean")
278
+ return model.model_info.supports_reasoning;
279
+ return Boolean(model.model_info?.supported_params?.includes("reasoning_effort") ||
241
280
  model.model_info?.supported_params?.includes("thinking"));
242
281
  }
282
+ export function isDeepSeekV4ReasoningModel(name) {
283
+ return name !== undefined && DEEPSEEK_V4_REASONING_MODELS.has(name);
284
+ }
285
+ export function isContractReasoningModel(name) {
286
+ return isDeepSeekV4ReasoningModel(name) || (name !== undefined && QWEN_38_REASONING_MODELS.has(name));
287
+ }
288
+ function enforceReasoningVariants(model) {
289
+ const name = asString(model.id) ?? asString(model.name);
290
+ if (!isContractReasoningModel(name))
291
+ return model;
292
+ return {
293
+ ...model,
294
+ reasoning: true,
295
+ options: { ...(isRecord(model.options) ? model.options : {}), variantDefault: false },
296
+ variants: isDeepSeekV4ReasoningModel(name) ? DEEPSEEK_V4_VARIANTS : QWEN_38_VARIANTS,
297
+ };
298
+ }
299
+ function enforceReasoningProviderModels(provider) {
300
+ return {
301
+ ...provider,
302
+ models: Object.fromEntries(Object.entries(provider.models).map(([name, model]) => [
303
+ name,
304
+ isContractReasoningModel(name) ? enforceReasoningVariants({ ...model, id: name }) : model,
305
+ ])),
306
+ };
307
+ }
308
+ export function addOpenCodeVariantTombstones(provider) {
309
+ return {
310
+ ...provider,
311
+ models: Object.fromEntries(Object.entries(provider.models).map(([name, model]) => [
312
+ name,
313
+ isContractReasoningModel(name)
314
+ ? {
315
+ ...model,
316
+ variants: {
317
+ ...(isDeepSeekV4ReasoningModel(name) ? DEEPSEEK_V4_VARIANTS : QWEN_38_VARIANTS),
318
+ ...(isDeepSeekV4ReasoningModel(name)
319
+ ? { medium: { disabled: true }, xhigh: { disabled: true } }
320
+ : {
321
+ none: { disabled: true },
322
+ minimal: { disabled: true },
323
+ high: { disabled: true },
324
+ max: { disabled: true },
325
+ }),
326
+ },
327
+ }
328
+ : model,
329
+ ])),
330
+ };
331
+ }
332
+ function reasoningVariants(model) {
333
+ const reported = new Set((model.model_info?.supported_reasoning_efforts ?? [])
334
+ .map((effort) => asString(effort)?.toLowerCase())
335
+ .filter((effort) => effort !== undefined && REASONING_EFFORT_SET.has(effort)));
336
+ const efforts = REASONING_EFFORTS.filter((effort) => reported.has(effort));
337
+ if (!efforts.length)
338
+ return undefined;
339
+ return Object.fromEntries(efforts.map((effort) => [effort, { reasoningEffort: effort }]));
340
+ }
243
341
  function generatedModel(model) {
244
342
  const name = modelName(model);
245
343
  if (!name)
@@ -254,9 +352,20 @@ function generatedModel(model) {
254
352
  modalities: mappedModalities,
255
353
  ...(mappedModalities.input.includes("image") || mappedModalities.input.includes("pdf") ? { attachment: true } : {}),
256
354
  ...(supportsReasoning(model) ? { reasoning: true } : {}),
355
+ ...(supportsReasoning(model) && reasoningVariants(model) ? { variants: reasoningVariants(model) } : {}),
257
356
  ...(model.model_info?.supports_function_calling || model.model_info?.supports_tool_choice ? { tool_call: true } : {}),
258
357
  };
259
358
  }
359
+ function finalizedModel(model, override) {
360
+ const input = supportedModalities(model.modalities?.input ?? [TEXT_MODALITY]);
361
+ const output = supportedModalities(model.modalities?.output ?? [TEXT_MODALITY]);
362
+ const attachmentOverride = typeof override?.attachment === "boolean" ? override.attachment : undefined;
363
+ return {
364
+ ...model,
365
+ modalities: { input, output },
366
+ attachment: attachmentOverride ?? input.some((modality) => modality === "image" || modality === "pdf"),
367
+ };
368
+ }
260
369
  function v2Variants(model) {
261
370
  const variants = model.variants;
262
371
  if (!isRecord(variants))
@@ -288,9 +397,11 @@ function v2Model(model, baseURL) {
288
397
  capabilities: {
289
398
  tools: model.tool_call ?? false,
290
399
  reasoning: model.reasoning ?? false,
291
- input: model.modalities?.input ?? [TEXT_MODALITY],
292
- output: model.modalities?.output ?? [TEXT_MODALITY],
400
+ attachment: model.attachment ?? false,
401
+ input: supportedModalities(model.modalities?.input ?? [TEXT_MODALITY]),
402
+ output: supportedModalities(model.modalities?.output ?? [TEXT_MODALITY]),
293
403
  },
404
+ ...(isRecord(model.options) ? { options: { ...model.options } } : {}),
294
405
  ...(v2Cost(model) ? { cost: v2Cost(model) } : {}),
295
406
  ...(model.limit ? { limit: { ...model.limit } } : {}),
296
407
  ...(v2Variants(model) ? { variants: v2Variants(model) } : {}),
@@ -303,12 +414,17 @@ export function mapModels(response, options = {}) {
303
414
  .map((model) => {
304
415
  const name = modelName(model);
305
416
  const generated = generatedModel(model);
306
- return name && generated ? [name, deepMerge(generated, options.overrides?.[name])] : undefined;
417
+ return name && generated
418
+ ? [
419
+ name,
420
+ enforceReasoningVariants(finalizedModel(deepMerge(generated, options.overrides?.[name]), options.overrides?.[name])),
421
+ ]
422
+ : undefined;
307
423
  })
308
424
  .filter((entry) => Boolean(entry)));
309
425
  }
310
426
  export function buildProvider(models, options = {}) {
311
- return deepMerge({
427
+ return enforceReasoningProviderModels(deepMerge({
312
428
  id: options.providerKey ?? "LiteLLM",
313
429
  name: options.providerName ?? "LiteLLM",
314
430
  npm: "@ai-sdk/openai-compatible",
@@ -319,14 +435,14 @@ export function buildProvider(models, options = {}) {
319
435
  ...(options.apiKey ? { body: { apiKey: options.apiKey } } : {}),
320
436
  },
321
437
  models: mapModels(models, options),
322
- }, options.providerOverrides);
438
+ }, options.providerOverrides));
323
439
  }
324
440
  export function mapModelsV2(models, options = {}) {
325
441
  const legacy = mapModels(models, options);
326
442
  return Object.fromEntries(Object.entries(legacy).map(([name, model]) => [name, v2Model(model, options.baseURL)]));
327
443
  }
328
444
  export function buildProviderV2(models, options = {}) {
329
- return deepMerge({
445
+ const provider = deepMerge({
330
446
  name: options.providerName ?? "LiteLLM",
331
447
  api: {
332
448
  type: "aisdk",
@@ -341,4 +457,24 @@ export function buildProviderV2(models, options = {}) {
341
457
  },
342
458
  models: mapModelsV2(models, options),
343
459
  }, options.providerOverrides);
460
+ return {
461
+ ...provider,
462
+ models: Object.fromEntries(Object.entries(provider.models).map(([name, model]) => [
463
+ name,
464
+ isContractReasoningModel(name)
465
+ ? {
466
+ ...model,
467
+ capabilities: {
468
+ tools: model.capabilities?.tools ?? false,
469
+ reasoning: true,
470
+ attachment: model.capabilities?.attachment ?? false,
471
+ input: model.capabilities?.input ?? [TEXT_MODALITY],
472
+ output: model.capabilities?.output ?? [TEXT_MODALITY],
473
+ },
474
+ options: { ...(isRecord(model.options) ? model.options : {}), variantDefault: false },
475
+ variants: isDeepSeekV4ReasoningModel(name) ? DEEPSEEK_V4_VARIANTS_V2 : QWEN_38_VARIANTS_V2,
476
+ }
477
+ : model,
478
+ ])),
479
+ };
344
480
  }
package/dist/types.d.ts CHANGED
@@ -20,6 +20,7 @@ export type OpenCodeModel = JsonObject & {
20
20
  attachment?: boolean;
21
21
  reasoning?: boolean;
22
22
  tool_call?: boolean;
23
+ variants?: Record<string, JsonObject>;
23
24
  };
24
25
  export type OpenCodeModelV2 = JsonObject & {
25
26
  family?: string;
@@ -34,6 +35,7 @@ export type OpenCodeModelV2 = JsonObject & {
34
35
  capabilities?: {
35
36
  tools: boolean;
36
37
  reasoning: boolean;
38
+ attachment: boolean;
37
39
  input: string[];
38
40
  output: string[];
39
41
  };
@@ -119,6 +121,10 @@ export type LiteLLMModelInfo = JsonObject & {
119
121
  supports_vision?: boolean;
120
122
  supports_pdf_input?: boolean;
121
123
  supports_reasoning?: boolean;
124
+ supported_reasoning_efforts?: string[];
125
+ supports_low_reasoning_effort?: boolean;
126
+ supports_xhigh_reasoning_effort?: boolean;
127
+ supports_max_reasoning_effort?: boolean;
122
128
  supported_modalities?: string[];
123
129
  output_modalities?: string[];
124
130
  supported_params?: string[];
@@ -144,6 +150,7 @@ export type LiteLLMModelGroup = JsonObject & {
144
150
  supports_vision?: boolean;
145
151
  supports_pdf_input?: boolean;
146
152
  supports_reasoning?: boolean;
153
+ supported_reasoning_efforts?: string[];
147
154
  supported_modalities?: string[];
148
155
  output_modalities?: string[];
149
156
  supported_openai_params?: string[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staticeng/opencode-litellm",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "OpenCode plugin that discovers LiteLLM models at runtime.",
5
5
  "type": "module",
6
6
  "license": "MIT",