opencode-translate 1.0.6 → 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/README.md +77 -6
- package/dist/index.js +873 -0
- package/index.d.ts +5 -0
- package/package.json +15 -16
- package/src/activation/chat-message.ts +0 -189
- package/src/activation/index.ts +0 -38
- package/src/activation/logging.ts +0 -11
- package/src/activation/messages-transform.ts +0 -44
- package/src/activation/metadata.ts +0 -41
- package/src/activation/parts.ts +0 -46
- package/src/activation/question-hooks.ts +0 -126
- package/src/activation/state.ts +0 -97
- package/src/activation/text-complete.ts +0 -50
- package/src/activation/trigger.ts +0 -57
- package/src/activation/types.ts +0 -47
- package/src/activation.ts +0 -1
- package/src/anthropic-oauth.ts +0 -148
- package/src/auth/codex-request.ts +0 -108
- package/src/auth/codex-response.ts +0 -78
- package/src/auth/codex-shared.ts +0 -3
- package/src/auth/headers.ts +0 -18
- package/src/auth/index.ts +0 -177
- package/src/auth/oauth-fetch.ts +0 -100
- package/src/auth/refresh.ts +0 -102
- package/src/auth/retry.ts +0 -70
- package/src/auth/store.ts +0 -98
- package/src/auth/types.ts +0 -27
- package/src/auth.ts +0 -1
- package/src/constants/errors.ts +0 -24
- package/src/constants/guards.ts +0 -33
- package/src/constants/options.ts +0 -55
- package/src/constants/plugin.ts +0 -9
- package/src/constants/types.ts +0 -159
- package/src/constants.ts +0 -5
- package/src/formatting.ts +0 -157
- package/src/index.ts +0 -7
- package/src/labels.ts +0 -3
- package/src/prompts.ts +0 -123
- package/src/question-tool.ts +0 -234
- package/src/translator/index.ts +0 -172
- package/src/translator/part-id.ts +0 -43
- package/src/translator/provider.ts +0 -411
- package/src/translator/retry.ts +0 -62
- package/src/translator/types.ts +0 -24
- package/src/translator.ts +0 -1
|
@@ -1,411 +0,0 @@
|
|
|
1
|
-
import { type AuthInfo, type FetchLike, PLUGIN_NAME, type ProviderInfo, type ProviderModelInfo } from "../constants"
|
|
2
|
-
|
|
3
|
-
const providerFactoryCache = new Map<string, unknown>()
|
|
4
|
-
|
|
5
|
-
const PROVIDER_PACKAGE_FALLBACK: Record<string, string> = {
|
|
6
|
-
anthropic: "@ai-sdk/anthropic",
|
|
7
|
-
openai: "@ai-sdk/openai",
|
|
8
|
-
google: "@ai-sdk/google",
|
|
9
|
-
"google-vertex": "@ai-sdk/google-vertex",
|
|
10
|
-
"amazon-bedrock": "@ai-sdk/amazon-bedrock",
|
|
11
|
-
"github-copilot": "@ai-sdk/openai-compatible",
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const CREATE_EXPORT_FALLBACK: Record<string, string[]> = {
|
|
15
|
-
"@ai-sdk/amazon-bedrock": ["createAmazonBedrock", "bedrock"],
|
|
16
|
-
"@ai-sdk/anthropic": ["createAnthropic", "anthropic"],
|
|
17
|
-
"@ai-sdk/azure": ["createAzure", "azure"],
|
|
18
|
-
"@ai-sdk/gateway": ["createGateway", "gateway"],
|
|
19
|
-
"@ai-sdk/google": ["createGoogleGenerativeAI", "google"],
|
|
20
|
-
"@ai-sdk/google-vertex": ["createVertex", "vertex"],
|
|
21
|
-
"@ai-sdk/openai": ["createOpenAI", "openai"],
|
|
22
|
-
"@ai-sdk/openai-compatible": ["createOpenAICompatible"],
|
|
23
|
-
"@openrouter/ai-sdk-provider": ["createOpenRouter", "openrouter"],
|
|
24
|
-
}
|
|
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
|
-
|
|
46
|
-
interface ProviderCredentials {
|
|
47
|
-
provider?: ProviderInfo
|
|
48
|
-
authInfo?: AuthInfo
|
|
49
|
-
apiKey?: string
|
|
50
|
-
fetch?: FetchLike
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function __resetProviderFactoryCacheForTest() {
|
|
54
|
-
providerFactoryCache.clear()
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function providerPackage(providerID: string, model?: ProviderModelInfo): string {
|
|
58
|
-
const packageName = model?.api?.npm || PROVIDER_PACKAGE_FALLBACK[providerID]
|
|
59
|
-
if (!packageName) throw new Error(`Unsupported translator provider "${providerID}"`)
|
|
60
|
-
return packageName
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function pickFactory(mod: Record<string, unknown>, packageName: string): unknown {
|
|
64
|
-
for (const key of CREATE_EXPORT_FALLBACK[packageName] ?? []) {
|
|
65
|
-
if (typeof mod[key] === "function") return mod[key]
|
|
66
|
-
}
|
|
67
|
-
const createKey = Object.keys(mod).find((key) => key.startsWith("create") && typeof mod[key] === "function")
|
|
68
|
-
return createKey ? mod[createKey] : undefined
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export async function loadFactory(providerID: string, model?: ProviderModelInfo): Promise<unknown> {
|
|
72
|
-
const packageName = providerPackage(providerID, model)
|
|
73
|
-
const cached = providerFactoryCache.get(packageName)
|
|
74
|
-
if (cached) return cached
|
|
75
|
-
|
|
76
|
-
let mod: Record<string, unknown>
|
|
77
|
-
try {
|
|
78
|
-
mod = (await import(packageName)) as Record<string, unknown>
|
|
79
|
-
} catch (error) {
|
|
80
|
-
throw new Error(`Unable to load provider package "${packageName}" for "${providerID}": ${String(error)}`)
|
|
81
|
-
}
|
|
82
|
-
const factory = pickFactory(mod, packageName)
|
|
83
|
-
|
|
84
|
-
if (typeof factory !== "function") {
|
|
85
|
-
throw new Error(`Unable to load provider factory from "${packageName}" for "${providerID}"`)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
providerFactoryCache.set(packageName, factory)
|
|
89
|
-
return factory
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function resolveModelInfo(provider: ProviderInfo | undefined, modelID: string): ProviderModelInfo {
|
|
93
|
-
return provider?.models?.[modelID] ?? { id: modelID, api: { id: modelID } }
|
|
94
|
-
}
|
|
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
|
-
|
|
130
|
-
function headerRecord(value: unknown): Record<string, string> {
|
|
131
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return {}
|
|
132
|
-
return Object.fromEntries(
|
|
133
|
-
Object.entries(value as Record<string, unknown>).filter((entry): entry is [string, string] => {
|
|
134
|
-
return typeof entry[1] === "string"
|
|
135
|
-
}),
|
|
136
|
-
)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function substitutionVars(options: Record<string, unknown>, authInfo?: AuthInfo): Record<string, string | undefined> {
|
|
140
|
-
const metadata = authInfo?.type === "api" ? authInfo.metadata : undefined
|
|
141
|
-
const location =
|
|
142
|
-
stringOption(options.location) ?? process.env.GOOGLE_VERTEX_LOCATION ?? process.env.GOOGLE_CLOUD_LOCATION
|
|
143
|
-
const vertexEndpoint =
|
|
144
|
-
location === "global" ? "aiplatform.googleapis.com" : location ? `${location}-aiplatform.googleapis.com` : undefined
|
|
145
|
-
return {
|
|
146
|
-
...process.env,
|
|
147
|
-
AZURE_RESOURCE_NAME:
|
|
148
|
-
stringOption(options.resourceName) ?? metadata?.resourceName ?? process.env.AZURE_RESOURCE_NAME,
|
|
149
|
-
GOOGLE_VERTEX_PROJECT:
|
|
150
|
-
stringOption(options.project) ??
|
|
151
|
-
process.env.GOOGLE_VERTEX_PROJECT ??
|
|
152
|
-
process.env.GOOGLE_CLOUD_PROJECT ??
|
|
153
|
-
process.env.GCP_PROJECT ??
|
|
154
|
-
process.env.GCLOUD_PROJECT,
|
|
155
|
-
GOOGLE_VERTEX_LOCATION: location,
|
|
156
|
-
GOOGLE_VERTEX_ENDPOINT: vertexEndpoint ?? process.env.GOOGLE_VERTEX_ENDPOINT,
|
|
157
|
-
CLOUDFLARE_ACCOUNT_ID: metadata?.accountId ?? process.env.CLOUDFLARE_ACCOUNT_ID,
|
|
158
|
-
CLOUDFLARE_GATEWAY_ID: metadata?.gatewayId ?? process.env.CLOUDFLARE_GATEWAY_ID,
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
function stringOption(value: unknown): string | undefined {
|
|
163
|
-
return typeof value === "string" && value.length > 0 ? value : undefined
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function resolveBaseURL(baseURL: unknown, apiURL: unknown, options: Record<string, unknown>, authInfo?: AuthInfo) {
|
|
167
|
-
let url = stringOption(baseURL) ?? stringOption(apiURL)
|
|
168
|
-
if (!url) return undefined
|
|
169
|
-
const vars = substitutionVars(options, authInfo)
|
|
170
|
-
url = url.replace(/\$\{([^}]+)\}/g, (match, key) => vars[String(key)] ?? match)
|
|
171
|
-
return url
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function wrapSSE(response: Response, ms: number, controller: AbortController) {
|
|
175
|
-
if (typeof ms !== "number" || ms <= 0) return response
|
|
176
|
-
if (!response.body) return response
|
|
177
|
-
if (!response.headers.get("content-type")?.includes("text/event-stream")) return response
|
|
178
|
-
|
|
179
|
-
const reader = response.body.getReader()
|
|
180
|
-
const body = new ReadableStream<Uint8Array>({
|
|
181
|
-
async pull(ctrl) {
|
|
182
|
-
const part = await new Promise<Awaited<ReturnType<typeof reader.read>>>((resolve, reject) => {
|
|
183
|
-
const id = setTimeout(() => {
|
|
184
|
-
const error = new Error("SSE read timed out")
|
|
185
|
-
controller.abort(error)
|
|
186
|
-
void reader.cancel(error)
|
|
187
|
-
reject(error)
|
|
188
|
-
}, ms)
|
|
189
|
-
|
|
190
|
-
reader.read().then(
|
|
191
|
-
(value) => {
|
|
192
|
-
clearTimeout(id)
|
|
193
|
-
resolve(value)
|
|
194
|
-
},
|
|
195
|
-
(error) => {
|
|
196
|
-
clearTimeout(id)
|
|
197
|
-
reject(error)
|
|
198
|
-
},
|
|
199
|
-
)
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
if (part.done) {
|
|
203
|
-
ctrl.close()
|
|
204
|
-
return
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
ctrl.enqueue(part.value)
|
|
208
|
-
},
|
|
209
|
-
async cancel(reason) {
|
|
210
|
-
controller.abort(reason)
|
|
211
|
-
await reader.cancel(reason)
|
|
212
|
-
},
|
|
213
|
-
})
|
|
214
|
-
|
|
215
|
-
return new Response(body, {
|
|
216
|
-
headers: new Headers(response.headers),
|
|
217
|
-
status: response.status,
|
|
218
|
-
statusText: response.statusText,
|
|
219
|
-
})
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function anySignal(signals: AbortSignal[]): AbortSignal | undefined {
|
|
223
|
-
if (signals.length === 0) return undefined
|
|
224
|
-
if (signals.length === 1) return signals[0]
|
|
225
|
-
const signalAny = (AbortSignal as typeof AbortSignal & { any?: (signals: AbortSignal[]) => AbortSignal }).any
|
|
226
|
-
return signalAny ? signalAny(signals) : signals[0]
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
function stripOpenAIItemIDs(packageName: string, init: RequestInit) {
|
|
230
|
-
if (packageName !== "@ai-sdk/openai" && packageName !== "@ai-sdk/azure") return
|
|
231
|
-
if (!init.body || init.method !== "POST" || typeof init.body !== "string") return
|
|
232
|
-
try {
|
|
233
|
-
const body = JSON.parse(init.body) as Record<string, unknown>
|
|
234
|
-
if (body.store === true || !Array.isArray(body.input)) return
|
|
235
|
-
for (const item of body.input) {
|
|
236
|
-
if (item && typeof item === "object" && !Array.isArray(item)) delete (item as Record<string, unknown>).id
|
|
237
|
-
}
|
|
238
|
-
init.body = JSON.stringify(body)
|
|
239
|
-
} catch {}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function withOpenCodeFetch(config: Record<string, unknown>, packageName: string) {
|
|
243
|
-
const configuredFetch = typeof config.fetch === "function" ? (config.fetch as FetchLike) : undefined
|
|
244
|
-
const chunkTimeout = typeof config.chunkTimeout === "number" ? config.chunkTimeout : undefined
|
|
245
|
-
delete config.chunkTimeout
|
|
246
|
-
|
|
247
|
-
config.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
248
|
-
const requestInit = { ...(init ?? {}) }
|
|
249
|
-
const signals: AbortSignal[] = []
|
|
250
|
-
const chunkController = chunkTimeout && chunkTimeout > 0 ? new AbortController() : undefined
|
|
251
|
-
if (requestInit.signal) signals.push(requestInit.signal)
|
|
252
|
-
if (chunkController) signals.push(chunkController.signal)
|
|
253
|
-
if (typeof config.timeout === "number" && config.timeout > 0) signals.push(AbortSignal.timeout(config.timeout))
|
|
254
|
-
const signal = anySignal(signals)
|
|
255
|
-
if (signal) requestInit.signal = signal
|
|
256
|
-
stripOpenAIItemIDs(packageName, requestInit)
|
|
257
|
-
|
|
258
|
-
const response = await (configuredFetch ?? fetch)(input, { ...requestInit, timeout: false } as RequestInit)
|
|
259
|
-
return chunkController && chunkTimeout ? wrapSSE(response, chunkTimeout, chunkController) : response
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function providerConfig(
|
|
264
|
-
providerID: string,
|
|
265
|
-
credentials: ProviderCredentials,
|
|
266
|
-
model?: ProviderModelInfo,
|
|
267
|
-
): Record<string, unknown> {
|
|
268
|
-
const provider = credentials.provider
|
|
269
|
-
const packageName = providerPackage(providerID, model)
|
|
270
|
-
const config: Record<string, unknown> = { ...(provider?.options ?? {}) }
|
|
271
|
-
|
|
272
|
-
if (providerID === "google-vertex" && !packageName.includes("@ai-sdk/openai-compatible")) delete config.fetch
|
|
273
|
-
if (packageName.includes("@ai-sdk/openai-compatible") && config.includeUsage !== false) config.includeUsage = true
|
|
274
|
-
|
|
275
|
-
const baseURL = resolveBaseURL(config.baseURL, model?.api?.url, config, credentials.authInfo)
|
|
276
|
-
if (baseURL !== undefined) config.baseURL = baseURL
|
|
277
|
-
if (credentials.apiKey !== undefined) config.apiKey = credentials.apiKey
|
|
278
|
-
if (credentials.fetch) config.fetch = credentials.fetch
|
|
279
|
-
if (model?.headers) config.headers = { ...headerRecord(config.headers), ...model.headers }
|
|
280
|
-
if (providerID === "github-copilot" && config.baseURL === undefined) config.baseURL = "https://api.githubcopilot.com"
|
|
281
|
-
if (
|
|
282
|
-
providerID === "amazon-bedrock" &&
|
|
283
|
-
credentials.authInfo?.type === "api" &&
|
|
284
|
-
!process.env.AWS_BEARER_TOKEN_BEDROCK
|
|
285
|
-
) {
|
|
286
|
-
process.env.AWS_BEARER_TOKEN_BEDROCK = credentials.authInfo.key
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
withOpenCodeFetch(config, packageName)
|
|
290
|
-
return { name: providerID, ...config }
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
export function instantiateProvider(
|
|
294
|
-
factory: unknown,
|
|
295
|
-
providerID: string,
|
|
296
|
-
credentials: ProviderCredentials,
|
|
297
|
-
model?: ProviderModelInfo,
|
|
298
|
-
): unknown {
|
|
299
|
-
if (typeof factory !== "function") throw new Error(`Invalid provider factory for "${providerID}"`)
|
|
300
|
-
return (factory as (config: Record<string, unknown>) => unknown)(providerConfig(providerID, credentials, model))
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
function shouldUseCopilotResponsesApi(modelID: string): boolean {
|
|
304
|
-
const match = /^gpt-(\d+)/.exec(modelID)
|
|
305
|
-
if (!match) return false
|
|
306
|
-
return Number(match[1]) >= 5 && !modelID.startsWith("gpt-5-mini")
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
function selectAzureLanguageModel(record: Record<string, unknown>, modelID: string, useChat: boolean): unknown {
|
|
310
|
-
if (useChat && typeof record.chat === "function") return (record.chat as (id: string) => unknown)(modelID)
|
|
311
|
-
if (typeof record.responses === "function") return (record.responses as (id: string) => unknown)(modelID)
|
|
312
|
-
if (typeof record.messages === "function") return (record.messages as (id: string) => unknown)(modelID)
|
|
313
|
-
if (typeof record.chat === "function") return (record.chat as (id: string) => unknown)(modelID)
|
|
314
|
-
if (typeof record.languageModel === "function") return (record.languageModel as (id: string) => unknown)(modelID)
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
function bedrockModelID(modelID: string, region: unknown): string {
|
|
318
|
-
const crossRegionPrefixes = ["global.", "us.", "eu.", "jp.", "apac.", "au."]
|
|
319
|
-
if (crossRegionPrefixes.some((prefix) => modelID.startsWith(prefix))) return modelID
|
|
320
|
-
if (typeof region !== "string") return modelID
|
|
321
|
-
|
|
322
|
-
let regionPrefix = region.split("-")[0]
|
|
323
|
-
if (regionPrefix === "us") {
|
|
324
|
-
const modelRequiresPrefix = [
|
|
325
|
-
"nova-micro",
|
|
326
|
-
"nova-lite",
|
|
327
|
-
"nova-pro",
|
|
328
|
-
"nova-premier",
|
|
329
|
-
"nova-2",
|
|
330
|
-
"claude",
|
|
331
|
-
"deepseek",
|
|
332
|
-
].some((value) => modelID.includes(value))
|
|
333
|
-
if (modelRequiresPrefix && !region.startsWith("us-gov")) return `${regionPrefix}.${modelID}`
|
|
334
|
-
}
|
|
335
|
-
if (regionPrefix === "eu") {
|
|
336
|
-
const regionRequiresPrefix = [
|
|
337
|
-
"eu-west-1",
|
|
338
|
-
"eu-west-2",
|
|
339
|
-
"eu-west-3",
|
|
340
|
-
"eu-north-1",
|
|
341
|
-
"eu-central-1",
|
|
342
|
-
"eu-south-1",
|
|
343
|
-
"eu-south-2",
|
|
344
|
-
].some((value) => region.includes(value))
|
|
345
|
-
const modelRequiresPrefix = ["claude", "nova-lite", "nova-micro", "llama3", "pixtral"].some((value) =>
|
|
346
|
-
modelID.includes(value),
|
|
347
|
-
)
|
|
348
|
-
if (regionRequiresPrefix && modelRequiresPrefix) return `${regionPrefix}.${modelID}`
|
|
349
|
-
}
|
|
350
|
-
if (regionPrefix === "ap") {
|
|
351
|
-
const isAustraliaRegion = ["ap-southeast-2", "ap-southeast-4"].includes(region)
|
|
352
|
-
const isTokyoRegion = region === "ap-northeast-1"
|
|
353
|
-
if (
|
|
354
|
-
isAustraliaRegion &&
|
|
355
|
-
["anthropic.claude-sonnet-4-5", "anthropic.claude-haiku"].some((value) => modelID.includes(value))
|
|
356
|
-
) {
|
|
357
|
-
regionPrefix = "au"
|
|
358
|
-
return `${regionPrefix}.${modelID}`
|
|
359
|
-
}
|
|
360
|
-
const modelRequiresPrefix = ["claude", "nova-lite", "nova-micro", "nova-pro"].some((value) =>
|
|
361
|
-
modelID.includes(value),
|
|
362
|
-
)
|
|
363
|
-
if (modelRequiresPrefix) return `${isTokyoRegion ? "jp" : "apac"}.${modelID}`
|
|
364
|
-
}
|
|
365
|
-
return modelID
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
export function instantiateModel(
|
|
369
|
-
provider: unknown,
|
|
370
|
-
modelID: string,
|
|
371
|
-
providerID?: string,
|
|
372
|
-
model?: ProviderModelInfo,
|
|
373
|
-
providerOptions?: Record<string, unknown>,
|
|
374
|
-
): unknown {
|
|
375
|
-
const apiID = model?.api?.id || model?.id || modelID
|
|
376
|
-
if (typeof provider === "function") return provider(modelID)
|
|
377
|
-
if (provider && typeof provider === "object") {
|
|
378
|
-
const record = provider as Record<string, unknown>
|
|
379
|
-
if ((providerID === "openai" || providerID === "xai") && typeof record.responses === "function") {
|
|
380
|
-
return (record.responses as (id: string) => unknown)(apiID)
|
|
381
|
-
}
|
|
382
|
-
if (
|
|
383
|
-
providerID === "github-copilot" &&
|
|
384
|
-
typeof record.responses === "function" &&
|
|
385
|
-
typeof record.chat === "function"
|
|
386
|
-
) {
|
|
387
|
-
return shouldUseCopilotResponsesApi(apiID)
|
|
388
|
-
? (record.responses as (id: string) => unknown)(apiID)
|
|
389
|
-
: (record.chat as (id: string) => unknown)(apiID)
|
|
390
|
-
}
|
|
391
|
-
if (providerID === "azure" || providerID === "azure-cognitive-services") {
|
|
392
|
-
const selected = selectAzureLanguageModel(record, apiID, providerOptions?.useCompletionUrls === true)
|
|
393
|
-
if (selected) return selected
|
|
394
|
-
}
|
|
395
|
-
if (providerID === "amazon-bedrock" && typeof record.languageModel === "function") {
|
|
396
|
-
return (record.languageModel as (id: string) => unknown)(bedrockModelID(apiID, providerOptions?.region))
|
|
397
|
-
}
|
|
398
|
-
if (typeof record.chatModel === "function") return (record.chatModel as (id: string) => unknown)(modelID)
|
|
399
|
-
if (typeof record.languageModel === "function") return (record.languageModel as (id: string) => unknown)(apiID)
|
|
400
|
-
if (typeof record.chat === "function") return (record.chat as (id: string) => unknown)(apiID)
|
|
401
|
-
if (typeof record.responses === "function") return (record.responses as (id: string) => unknown)(apiID)
|
|
402
|
-
}
|
|
403
|
-
throw new Error(`Unable to instantiate model "${modelID}"`)
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
export function supportsTemperature(providerID: string, modelID: string, model?: ProviderModelInfo): boolean {
|
|
407
|
-
if (typeof model?.capabilities?.temperature === "boolean") return model.capabilities.temperature
|
|
408
|
-
if (providerID !== "openai") return true
|
|
409
|
-
if (modelID.startsWith("o1") || modelID.startsWith("o3") || modelID.startsWith("o4-mini")) return false
|
|
410
|
-
return !(modelID.startsWith("gpt-5") && !modelID.startsWith("gpt-5-chat"))
|
|
411
|
-
}
|
package/src/translator/retry.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { normalizeReason } from "../constants"
|
|
2
|
-
|
|
3
|
-
function getStatus(error: unknown): number | undefined {
|
|
4
|
-
if (!error || typeof error !== "object") return undefined
|
|
5
|
-
const record = error as Record<string, unknown>
|
|
6
|
-
if (typeof record.status === "number") return record.status
|
|
7
|
-
if (typeof record.statusCode === "number") return record.statusCode
|
|
8
|
-
const response = record.response
|
|
9
|
-
if (response && typeof response === "object") {
|
|
10
|
-
const status = (response as Record<string, unknown>).status
|
|
11
|
-
if (typeof status === "number") return status
|
|
12
|
-
}
|
|
13
|
-
return undefined
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function getRetryAfterMs(error: unknown): number {
|
|
17
|
-
if (!error || typeof error !== "object") return 2000
|
|
18
|
-
const response = (error as Record<string, unknown>).response
|
|
19
|
-
if (!response || typeof response !== "object") return 2000
|
|
20
|
-
const headers = (response as { headers?: Headers }).headers
|
|
21
|
-
if (!(headers instanceof Headers)) return 2000
|
|
22
|
-
const retryAfter = headers.get("retry-after")
|
|
23
|
-
if (!retryAfter) return 2000
|
|
24
|
-
const seconds = Number(retryAfter)
|
|
25
|
-
if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000)
|
|
26
|
-
const date = Date.parse(retryAfter)
|
|
27
|
-
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : 2000
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function isRetryable(error: unknown): boolean {
|
|
31
|
-
const status = getStatus(error)
|
|
32
|
-
if (status === 429) return true
|
|
33
|
-
if (status !== undefined) return status >= 500
|
|
34
|
-
const message = normalizeReason(error).toLowerCase()
|
|
35
|
-
return (
|
|
36
|
-
message.includes("network") ||
|
|
37
|
-
message.includes("fetch") ||
|
|
38
|
-
message.includes("timeout") ||
|
|
39
|
-
message.includes("socket") ||
|
|
40
|
-
message.includes("econn")
|
|
41
|
-
)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export async function withRetry<T>(task: () => Promise<T>, sleepImpl: (ms: number) => Promise<void>): Promise<T> {
|
|
45
|
-
let lastError: unknown
|
|
46
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
47
|
-
try {
|
|
48
|
-
return await task()
|
|
49
|
-
} catch (error) {
|
|
50
|
-
lastError = error
|
|
51
|
-
if (!isRetryable(error)) throw error
|
|
52
|
-
if (getStatus(error) === 429) {
|
|
53
|
-
if (attempt >= 1) throw error
|
|
54
|
-
await sleepImpl(getRetryAfterMs(error))
|
|
55
|
-
continue
|
|
56
|
-
}
|
|
57
|
-
if (attempt >= 2) throw error
|
|
58
|
-
await sleepImpl(attempt === 0 ? 500 : 1500)
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
throw lastError
|
|
62
|
-
}
|
package/src/translator/types.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { generateText } from "ai"
|
|
2
|
-
import type { createCredentialResolver } from "../auth"
|
|
3
|
-
|
|
4
|
-
export interface TranslatorDependencies {
|
|
5
|
-
generateTextImpl?: typeof generateText
|
|
6
|
-
sleep?: (ms: number) => Promise<void>
|
|
7
|
-
now?: () => number
|
|
8
|
-
credentialResolver?: ReturnType<typeof createCredentialResolver>
|
|
9
|
-
timeoutMs?: number
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export interface TranslateTextInput {
|
|
13
|
-
text: string
|
|
14
|
-
sourceLanguage: string
|
|
15
|
-
targetLanguage: string
|
|
16
|
-
direction: "inbound" | "outbound"
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface TranslateTextsInput {
|
|
20
|
-
texts: readonly string[]
|
|
21
|
-
sourceLanguage: string
|
|
22
|
-
targetLanguage: string
|
|
23
|
-
direction: "inbound" | "outbound"
|
|
24
|
-
}
|
package/src/translator.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from "./translator/index"
|