opencode-translate 1.0.1 → 1.0.2

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
@@ -35,7 +35,8 @@ Add to `~/.config/opencode/opencode.jsonc`:
35
35
  "plugin": [
36
36
  ["opencode-translate", {
37
37
  "model": "openai/gpt-5.4-mini", // model to use for translation
38
- "lang": "Korean" // language you speak
38
+ "variant": "minimal", // optional model variant / thinking effort
39
+ "lang": "Korean" // language you speak
39
40
  }]
40
41
  ]
41
42
  }
@@ -56,6 +57,7 @@ All subsequent messages in the same session are translated automatically — no
56
57
  | Option | Type | Default | Description |
57
58
  | --- | --- | --- | --- |
58
59
  | `model` | string | required | Translator model in `provider/model-id` form |
60
+ | `variant` | string | optional | Translator model variant / thinking effort (for example, `"minimal"`, `"high"`, or `"max"`) |
59
61
  | `lang` | string | required | Language you speak (e.g. `"Korean"`, `"Japanese"`) |
60
62
  | `trigger` | string[] | `["$en"]` | Keywords that activate translation |
61
63
  | `verbose` | boolean | `false` | Print translation logs |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-translate",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "OpenCode plugin that lets the user chat in a configured language while the main chat loop only sees English.",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -21,6 +21,7 @@ export function resolveOptions(options: Record<string, unknown>): ResolvedTransl
21
21
  `[${PLUGIN_NAME}:INVALID_OPTIONS] options.lang is required. Set it to the user's language, e.g. "Korean" or "Japanese".`,
22
22
  )
23
23
  }
24
+ const variant = typeof options.variant === "string" ? options.variant.trim() : ""
24
25
 
25
26
  const rawTrigger = Array.isArray(options.trigger)
26
27
  ? options.trigger
@@ -31,6 +32,7 @@ export function resolveOptions(options: Record<string, unknown>): ResolvedTransl
31
32
 
32
33
  return {
33
34
  model,
35
+ ...(variant ? { variant } : {}),
34
36
  trigger: trigger.length > 0 ? trigger : [...DEFAULT_TRIGGER],
35
37
  lang,
36
38
  verbose: options.verbose === true,
@@ -6,6 +6,7 @@ type ProviderSource = "env" | "config" | "custom" | "api"
6
6
 
7
7
  export interface ResolvedTranslateOptions {
8
8
  model: string
9
+ variant?: string
9
10
  trigger: string[]
10
11
  lang: string
11
12
  verbose: boolean
@@ -67,6 +68,7 @@ export interface ProviderModelInfo {
67
68
  }
68
69
  headers?: Record<string, string>
69
70
  options?: Record<string, unknown>
71
+ variants?: Record<string, Record<string, unknown>>
70
72
  capabilities?: {
71
73
  temperature?: boolean
72
74
  }
@@ -13,6 +13,7 @@ import { buildSystemPrompt, buildUserPrompt, unwrapEchoedTextEnvelope } from "..
13
13
  import { __resetSyntheticPartIDForTest } from "./part-id"
14
14
  import {
15
15
  __resetProviderFactoryCacheForTest,
16
+ buildVariantProviderOptions,
16
17
  instantiateModel,
17
18
  instantiateProvider,
18
19
  loadFactory,
@@ -73,6 +74,7 @@ export function createTranslator(
73
74
  const { providerID, modelID } = parseTranslatorModel(options.model)
74
75
  const credentials = await credentialResolver.resolve(options.model)
75
76
  const modelInfo = resolveModelInfo(credentials.provider, modelID)
77
+ const variantProviderOptions = buildVariantProviderOptions(providerID, modelID, modelInfo, options.variant)
76
78
  const factory = await loadFactory(providerID, modelInfo)
77
79
  const provider = instantiateProvider(factory, providerID, credentials, modelInfo)
78
80
  const providerOptions = { ...(credentials.provider?.options ?? {}), ...(modelInfo.options ?? {}) }
@@ -85,6 +87,7 @@ export function createTranslator(
85
87
  model,
86
88
  system: buildSystemPrompt(input),
87
89
  ...(supportsTemperature(providerID, modelID, modelInfo) ? { temperature: 0 } : {}),
90
+ ...(variantProviderOptions ? { providerOptions: variantProviderOptions } : {}),
88
91
  prompt: buildUserPrompt(input),
89
92
  }),
90
93
  timeoutMs,
@@ -1,4 +1,4 @@
1
- import type { AuthInfo, FetchLike, ProviderInfo, ProviderModelInfo } from "../constants"
1
+ import { type AuthInfo, type FetchLike, PLUGIN_NAME, type ProviderInfo, type ProviderModelInfo } from "../constants"
2
2
 
3
3
  const providerFactoryCache = new Map<string, unknown>()
4
4
 
@@ -23,6 +23,26 @@ const CREATE_EXPORT_FALLBACK: Record<string, string[]> = {
23
23
  "@openrouter/ai-sdk-provider": ["createOpenRouter", "openrouter"],
24
24
  }
25
25
 
26
+ const PROVIDER_OPTIONS_KEY: Record<string, string> = {
27
+ "@ai-sdk/amazon-bedrock": "bedrock",
28
+ "@ai-sdk/amazon-bedrock/mantle": "openai",
29
+ "@ai-sdk/anthropic": "anthropic",
30
+ "@ai-sdk/azure": "openai",
31
+ "@ai-sdk/gateway": "gateway",
32
+ "@ai-sdk/github-copilot": "openai",
33
+ "@ai-sdk/google": "google",
34
+ "@ai-sdk/google-vertex": "vertex",
35
+ "@ai-sdk/google-vertex/anthropic": "anthropic",
36
+ "@ai-sdk/openai": "openai",
37
+ "@openrouter/ai-sdk-provider": "openrouter",
38
+ "ai-gateway-provider": "openaiCompatible",
39
+ }
40
+
41
+ type JsonValue = null | string | number | boolean | JsonObject | JsonArray
42
+ type JsonObject = { [key: string]: JsonValue | undefined }
43
+ type JsonArray = JsonValue[]
44
+ type VariantProviderOptions = Record<string, JsonObject>
45
+
26
46
  interface ProviderCredentials {
27
47
  provider?: ProviderInfo
28
48
  authInfo?: AuthInfo
@@ -73,6 +93,40 @@ export function resolveModelInfo(provider: ProviderInfo | undefined, modelID: st
73
93
  return provider?.models?.[modelID] ?? { id: modelID, api: { id: modelID } }
74
94
  }
75
95
 
96
+ function sdkProviderOptionsKey(providerID: string, model?: ProviderModelInfo): string {
97
+ const packageName = model?.api?.npm
98
+ if (packageName && PROVIDER_OPTIONS_KEY[packageName]) return PROVIDER_OPTIONS_KEY[packageName]
99
+ if (packageName === "@ai-sdk/openai-compatible" || packageName === "@ai-sdk/openai") return providerID.split(".")[0]
100
+ return providerID
101
+ }
102
+
103
+ function invalidVariantError(providerID: string, modelID: string, model: ProviderModelInfo, variant: string) {
104
+ const variants = Object.keys(model.variants ?? {}).sort()
105
+ const modelName = `${providerID}/${modelID}`
106
+ if (variants.length === 0) {
107
+ return new Error(
108
+ `[${PLUGIN_NAME}:INVALID_VARIANT] options.variant "${variant}" is not available for "${modelName}". This model has no configurable variants.`,
109
+ )
110
+ }
111
+ return new Error(
112
+ `[${PLUGIN_NAME}:INVALID_VARIANT] options.variant "${variant}" is not available for "${modelName}". Available variants: ${variants.join(", ")}.`,
113
+ )
114
+ }
115
+
116
+ export function buildVariantProviderOptions(
117
+ providerID: string,
118
+ modelID: string,
119
+ model: ProviderModelInfo,
120
+ variant?: string,
121
+ ): VariantProviderOptions | undefined {
122
+ if (!variant) return undefined
123
+ const selected = model.variants?.[variant]
124
+ if (!selected) throw invalidVariantError(providerID, modelID, model, variant)
125
+ const providerOptions = selected as JsonObject
126
+ if (model.api?.npm === "@ai-sdk/azure") return { openai: providerOptions, azure: providerOptions }
127
+ return { [sdkProviderOptionsKey(providerID, model)]: providerOptions }
128
+ }
129
+
76
130
  function headerRecord(value: unknown): Record<string, string> {
77
131
  if (!value || typeof value !== "object" || Array.isArray(value)) return {}
78
132
  return Object.fromEntries(