@staticeng/opencode-litellm 0.2.5 → 0.2.6

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 CHANGED
File without changes
package/README.md CHANGED
@@ -93,6 +93,31 @@ When LiteLLM publishes `model_info.supported_reasoning_efforts`, the plugin maps
93
93
 
94
94
  Overrides win over built-in contracts and generated values through a recursive deep merge. Model overrides are applied first and provider overrides last. Arrays and scalar values are replaced rather than concatenated.
95
95
 
96
+ ### GPT Transport
97
+
98
+ GPT models use `@ai-sdk/openai` and the Responses API through the configured
99
+ LiteLLM base URL (for example, `/v1/responses`). Other models retain
100
+ `@ai-sdk/openai-compatible` and Chat Completions. No OpenAI OAuth endpoint or
101
+ direct upstream URL is introduced.
102
+
103
+ Selection matches a final `gpt-` plus digit segment (case-insensitive), including
104
+ namespaced aliases, in the routable model name, `base_model`, or
105
+ `litellm_params.model`. Opaque deployment IDs do not override a named alias.
106
+ The routable ID is preserved. O-series and non-GPT models are not migrated merely
107
+ because discovery reports `mode: responses`. Explicit model `provider.npm`
108
+ overrides still win; V2 uses the corresponding `api.package`.
109
+
110
+ Legacy reasoning options remain `reasoningEffort` (OpenCode places them under
111
+ `providerOptions.openai` for this SDK). V2 GPT request defaults and variants use
112
+ Responses wire bodies: `reasoning: { effort }`, not `reasoning_effort`.
113
+ Use an OpenCode release with AI SDK 5 or newer, where the OpenAI SDK's
114
+ `languageModel()` defaults to Responses. Tests pin the SDK versions used by
115
+ OpenCode at qualification time; these are development dependencies only.
116
+
117
+ After upgrading, pin the exact plugin version and restart only when active
118
+ sessions can safely stop. Existing processes retain their loaded plugin; do not
119
+ clear global caches to upgrade one plugin.
120
+
96
121
  The built-in mode matrix is:
97
122
 
98
123
  | Contract | Modes in order | Default |
package/dist/helpers.d.ts CHANGED
File without changes
package/dist/helpers.js CHANGED
File without changes
package/dist/index.d.ts CHANGED
File without changes
package/dist/index.js CHANGED
File without changes
package/dist/litellm.d.ts CHANGED
File without changes
package/dist/litellm.js CHANGED
File without changes
package/dist/mapping.d.ts CHANGED
File without changes
package/dist/mapping.js CHANGED
@@ -111,6 +111,10 @@ function enrichMetadata(existing, incoming, key) {
111
111
  function modelName(model) {
112
112
  return asString(model.model_name) ?? asString(model.litellm_params?.model) ?? asString(model.model_info?.id);
113
113
  }
114
+ function usesGPTResponses(model) {
115
+ // Match a GPT family segment, not an incidental substring or an opaque deployment ID.
116
+ return [modelName(model), model.model_info?.base_model, model.litellm_params?.model].some((name) => typeof name === "string" && /(?:^|\/)gpt-\d[^/]*$/i.test(name));
117
+ }
114
118
  export function mergeModelInfo(deployments, groups) {
115
119
  const byName = new Map();
116
120
  for (const model of [...deployments, ...groups]) {
@@ -332,6 +336,7 @@ function generatedModel(model, tombstones = false) {
332
336
  const generated = {
333
337
  id: name,
334
338
  name,
339
+ ...(usesGPTResponses(model) ? { provider: { npm: "@ai-sdk/openai" } } : {}),
335
340
  ...(asString(model.model_info?.base_model) ? { family: asString(model.model_info?.base_model) } : {}),
336
341
  ...(cost(model) ? { cost: cost(model) } : {}),
337
342
  ...(limit(model) ? { limit: limit(model) } : {}),
@@ -403,8 +408,8 @@ function v2Model(model, baseURL) {
403
408
  api: {
404
409
  ...(id ? { id } : {}),
405
410
  type: "aisdk",
406
- package: "@ai-sdk/openai-compatible",
407
- ...(baseURL ? { url: baseURL } : {}),
411
+ package: model.provider?.npm ?? "@ai-sdk/openai-compatible",
412
+ ...(model.provider?.api ?? baseURL ? { url: model.provider?.api ?? baseURL } : {}),
408
413
  settings: {},
409
414
  },
410
415
  capabilities: {
@@ -458,9 +463,32 @@ export function mapModelsV2(models, options = {}) {
458
463
  return Object.fromEntries(Object.entries(legacy).map(([name, model]) => {
459
464
  const mapped = v2Model(model, options.baseURL);
460
465
  const variants = contractV2Variants(name, model, options.overrides?.[name]);
461
- return [name, variants ? { ...mapped, variants } : mapped];
466
+ const result = variants ? { ...mapped, variants } : mapped;
467
+ if (mapped.api?.package === "@ai-sdk/openai") {
468
+ const body = responsesBody(isRecord(model.options) ? model.options : {});
469
+ result.request = { body };
470
+ result.variants = result.variants?.map((variant) => ({ ...variant, body: responsesBody(variant.body ?? {}) }));
471
+ }
472
+ return [name, result];
462
473
  }));
463
474
  }
475
+ function responsesBody(options) {
476
+ const body = { ...options };
477
+ const effort = body.reasoningEffort ?? body.reasoning_effort;
478
+ const summary = body.reasoningSummary;
479
+ delete body.reasoningEffort;
480
+ delete body.reasoning_effort;
481
+ delete body.reasoningSummary;
482
+ delete body.variantDefault;
483
+ if (effort !== undefined || summary !== undefined) {
484
+ body.reasoning = {
485
+ ...(isRecord(body.reasoning) ? body.reasoning : {}),
486
+ ...(effort !== undefined ? { effort } : {}),
487
+ ...(summary !== undefined ? { summary } : {}),
488
+ };
489
+ }
490
+ return body;
491
+ }
464
492
  export function buildProviderV2(models, options = {}) {
465
493
  return deepMerge({
466
494
  name: options.providerName ?? "LiteLLM",
package/dist/merge.d.ts CHANGED
File without changes
package/dist/merge.js CHANGED
File without changes
File without changes
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
@@ -27,6 +27,10 @@ export type ModelContract = Readonly<{
27
27
  metadata?: ModelContractMetadata;
28
28
  }>;
29
29
  export type OpenCodeModel = JsonObject & {
30
+ provider?: {
31
+ npm?: string;
32
+ api?: string;
33
+ };
30
34
  id?: string;
31
35
  name?: string;
32
36
  cost?: {
package/dist/types.js CHANGED
File without changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staticeng/opencode-litellm",
3
- "version": "0.2.5",
3
+ "version": "0.2.6",
4
4
  "description": "OpenCode plugin that discovers LiteLLM models at runtime.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -30,6 +30,8 @@
30
30
  "@opencode-ai/plugin": "*"
31
31
  },
32
32
  "devDependencies": {
33
+ "@ai-sdk/openai": "3.0.84",
34
+ "@ai-sdk/openai-compatible": "2.0.41",
33
35
  "@opencode-ai/plugin": "latest",
34
36
  "@types/node": "^24.10.1",
35
37
  "typescript": "^5.9.3"