UNPKG

watsonx-ai-provider

Version:

IBM watsonx.ai provider for the Vercel AI SDK - access Granite, OpenAI, Llama, Mistral, and other foundation models

993 lines (980 loc) 31.8 kB
// src/watsonx-provider.ts import { NoSuchModelError } from "@ai-sdk/provider"; import { withUserAgentSuffix, withoutTrailingSlash } from "@ai-sdk/provider-utils"; // src/watsonx-chat-language-model.ts import { APICallError } from "@ai-sdk/provider"; import { postJsonToApi, createJsonResponseHandler, createEventSourceResponseHandler, generateId as defaultGenerateId, parseProviderOptions, combineHeaders } from "@ai-sdk/provider-utils"; // src/watsonx-chat-settings.ts import { z } from "zod"; var watsonxLanguageModelOptions = z.object({ /** * Maximum wall-clock time in milliseconds the server will spend on this * request before aborting. Unique to watsonx.ai. */ timeLimit: z.number().optional(), /** * Whether the model may call multiple tools in parallel within one response. * Forwarded to wx.ai as `parallel_tool_calls`. Defaults to the model's native * behavior (usually true) when unset. */ parallelToolCalls: z.boolean().optional(), /** * Reasoning effort for reasoning-capable models. Forwarded to wx.ai as * `reasoning_effort`. Verified to scale chain-of-thought length on * `openai/gpt-oss-120b` (HIGH produces ~3× the reasoning of LOW). * * Models without reasoning capability (e.g. `ibm/granite-4-h-small`) * accept the field but produce no `reasoning_content` regardless. */ reasoningEffort: z.enum(["low", "medium", "high"]).optional() }); // src/watsonx-config.ts var WATSONX_API_VERSION = "2026-04-20"; // src/watsonx-iam.ts import { LoadAPIKeyError } from "@ai-sdk/provider"; var tokenCache = /* @__PURE__ */ new Map(); var pendingRequests = /* @__PURE__ */ new Map(); async function getIAMToken(apiKey) { const cached = tokenCache.get(apiKey); if (cached && cached.expiresAt > Date.now() + 5 * 60 * 1e3) { return cached.token; } const pending = pendingRequests.get(apiKey); if (pending) { return pending; } const fetchPromise = fetchIAMToken(apiKey); pendingRequests.set(apiKey, fetchPromise); try { return await fetchPromise; } finally { pendingRequests.delete(apiKey); } } async function fetchIAMToken(apiKey) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 15e3); let response; try { response = await fetch("https://iam.cloud.ibm.com/identity/token", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: new URLSearchParams({ grant_type: "urn:ibm:params:oauth:grant-type:apikey", apikey: apiKey }), signal: controller.signal }); } finally { clearTimeout(timeoutId); } if (!response.ok) { const text = await response.text(); throw new LoadAPIKeyError({ message: `Failed to get IBM IAM token: ${response.status} - ${text}` }); } const data = await response.json(); tokenCache.set(apiKey, { token: data.access_token, expiresAt: Date.now() + data.expires_in * 1e3 }); return data.access_token; } function invalidateIAMToken(apiKey) { tokenCache.delete(apiKey); } // src/watsonx-schemas.ts import { z as z2 } from "zod"; var watsonxErrorSchema = z2.object({ errors: z2.array( z2.object({ code: z2.string(), message: z2.string(), more_info: z2.string().optional() }).loose() ).optional(), error: z2.string().optional(), message: z2.string().optional(), // wx.ai variants return either; coerce to one via preprocess. statusCode: z2.number().optional() }).loose().transform((val) => { const raw = val; return { ...val, statusCode: val.statusCode ?? raw.status_code }; }); var watsonxChatResponseSchema = z2.object({ id: z2.string().nullish(), model_id: z2.string().nullish(), created: z2.number().nullish(), choices: z2.array( z2.object({ index: z2.number(), message: z2.object({ role: z2.literal("assistant"), content: z2.string().nullish(), reasoning_content: z2.string().nullish(), tool_calls: z2.array( z2.object({ id: z2.string(), type: z2.literal("function"), function: z2.object({ name: z2.string(), arguments: z2.string() }).loose() }).loose() ).optional() }).loose(), finish_reason: z2.string().nullish() }).loose() ), usage: z2.object({ prompt_tokens: z2.number(), completion_tokens: z2.number(), total_tokens: z2.number(), completion_tokens_details: z2.object({ reasoning_tokens: z2.number().optional() }).loose().optional() }).loose() }); var watsonxChatChunkSchema = z2.object({ id: z2.string().nullish(), model_id: z2.string().nullish(), created: z2.number().nullish(), choices: z2.array( z2.object({ index: z2.number().optional(), delta: z2.object({ role: z2.string().optional(), content: z2.string().nullish(), reasoning_content: z2.string().nullish(), tool_calls: z2.array( z2.object({ index: z2.number().optional(), id: z2.string().optional(), type: z2.string().optional(), function: z2.object({ name: z2.string().optional(), arguments: z2.string().optional() }).loose().optional() }).loose() ).optional() }).loose().optional(), finish_reason: z2.string().nullish() }).loose() ).optional(), usage: z2.object({ prompt_tokens: z2.number(), completion_tokens: z2.number(), total_tokens: z2.number().optional(), completion_tokens_details: z2.object({ reasoning_tokens: z2.number().optional() }).loose().optional() }).loose().optional() }); var watsonxEmbeddingResponseSchema = z2.object({ model_id: z2.string().nullish(), results: z2.array( z2.object({ embedding: z2.array(z2.number()), input_token_count: z2.number().optional() }).loose() ), input_token_count: z2.number().optional() }); // src/watsonx-error.ts import { createJsonErrorResponseHandler } from "@ai-sdk/provider-utils"; var watsonxErrorHandler = createJsonErrorResponseHandler({ errorSchema: watsonxErrorSchema, errorToMessage: (error) => { if (error.errors?.[0]?.message) { return error.errors[0].message; } return error.message ?? error.error ?? "Unknown watsonx.ai error"; }, isRetryable: (response, error) => { if (response.status === 429) return true; if (response.status >= 500) return true; const code = error?.errors?.[0]?.code; if (code === "rate_limit_exceeded" || code === "service_unavailable") return true; return false; } }); // src/watsonx-chat-messages.ts import { UnsupportedFunctionalityError } from "@ai-sdk/provider"; import { convertUint8ArrayToBase64 } from "@ai-sdk/provider-utils"; function convertToWatsonxMessages(options) { const messages = []; for (const message of options.prompt) { switch (message.role) { case "system": messages.push({ role: "system", content: message.content }); break; case "user": { const parts = []; for (const part of message.content) { if (part.type === "text") { parts.push({ type: "text", text: part.text }); } else if (part.type === "file") { if (!part.mediaType.startsWith("image/")) { throw new UnsupportedFunctionalityError({ functionality: `file parts with media type ${part.mediaType}`, message: "watsonx.ai chat only supports image file parts; other media types are not supported." }); } let imageUrl; if (part.data instanceof URL) { imageUrl = part.data.toString(); } else if (typeof part.data === "string") { imageUrl = part.data.startsWith("data:") || part.data.startsWith("http") ? part.data : `data:${part.mediaType};base64,${part.data}`; } else if (part.data instanceof Uint8Array) { const base64 = convertUint8ArrayToBase64(part.data); imageUrl = `data:${part.mediaType};base64,${base64}`; } else { throw new UnsupportedFunctionalityError({ functionality: "file part with unknown data shape" }); } parts.push({ type: "image_url", image_url: { url: imageUrl } }); } } if (parts.length === 1 && parts[0].type === "text") { messages.push({ role: "user", content: parts[0].text ?? "" }); } else { messages.push({ role: "user", content: parts }); } break; } case "assistant": { const toolCalls = []; let textContent = ""; for (const part of message.content) { if (part.type === "text") { textContent += part.text; } else if (part.type === "tool-call") { toolCalls.push({ id: part.toolCallId, type: "function", function: { name: part.toolName, arguments: typeof part.input === "string" ? part.input : JSON.stringify(part.input) } }); } } const msg = { role: "assistant" }; if (textContent) msg.content = textContent; if (toolCalls.length > 0) msg.tool_calls = toolCalls; messages.push(msg); break; } case "tool": for (const part of message.content) { if (part.type === "tool-result") { let resultContent; if (part.output.type === "text") { resultContent = part.output.value; } else if (part.output.type === "json") { resultContent = JSON.stringify(part.output.value); } else if (part.output.type === "error-text") { resultContent = part.output.value; } else if (part.output.type === "error-json") { resultContent = JSON.stringify(part.output.value); } else { resultContent = JSON.stringify(part.output); } messages.push({ role: "tool", tool_call_id: part.toolCallId, content: resultContent }); } } break; } } return messages; } // src/watsonx-prepare-tools.ts function prepareWatsonxTools(options) { const toolWarnings = []; if (!options.tools || options.tools.length === 0) { return { toolWarnings }; } for (const tool of options.tools) { if (tool.type !== "function") { toolWarnings.push({ type: "unsupported", feature: `tool type '${tool.type}'`, details: "watsonx.ai only supports function tools" }); } } const tools = options.tools.filter((tool) => tool.type === "function").map((tool) => { const fn = { name: tool.name, description: tool.description, parameters: tool.inputSchema }; const strict = tool.strict; if (strict != null) fn.strict = strict; return { type: "function", function: fn }; }); let toolChoice; if (options.toolChoice) { switch (options.toolChoice.type) { case "auto": case "none": case "required": toolChoice = { tool_choice_option: options.toolChoice.type }; break; case "tool": toolChoice = { tool_choice: { type: "function", function: { name: options.toolChoice.toolName } } }; break; } } return { tools, toolChoice, toolWarnings }; } // src/watsonx-chat-helpers.ts function getResponseMetadata(value) { return { id: value.id ?? void 0, modelId: value.model_id ?? void 0, timestamp: typeof value.created === "number" ? new Date(value.created * 1e3) : void 0 }; } function convertFinishReason(reason, hasToolCalls = false) { let unified; switch (reason) { case "stop": case "eos_token": unified = "stop"; break; case "length": case "max_tokens": unified = "length"; break; case "tool_calls": unified = "tool-calls"; break; case "content_filter": unified = "content-filter"; break; default: unified = "other"; } if (unified === "other" && hasToolCalls) { unified = "tool-calls"; } return { unified, raw: reason }; } function parseToolCallArgs(args, modelId) { if (modelId == null || !modelId.startsWith("ibm/granite")) { return args; } try { const parsed = JSON.parse(args); if (typeof parsed === "string") { return parsed; } } catch { } return args; } function convertUsage(usage) { if (usage == null) { return { inputTokens: { total: 0, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: 0, text: void 0, reasoning: void 0 } }; } const reasoningTokens = usage.completion_tokens_details?.reasoning_tokens; const textTokens = reasoningTokens != null ? Math.max(0, usage.completion_tokens - reasoningTokens) : usage.completion_tokens; return { inputTokens: { total: usage.prompt_tokens, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: usage.completion_tokens, text: textTokens, reasoning: reasoningTokens }, raw: { prompt_tokens: usage.prompt_tokens, completion_tokens: usage.completion_tokens } }; } // src/watsonx-chat-language-model.ts var WatsonxChatLanguageModel = class { specificationVersion = "v3"; modelId; config; // Vision-capable wx models accept https image URLs directly; non-vision // models will surface an error from the backend when sent one. supportedUrls = { "image/*": [/^https?:\/\/.*$/] }; constructor(modelId, config) { this.modelId = modelId; this.config = config; } get provider() { return this.config.provider; } get generateId() { return this.config.generateId ?? defaultGenerateId; } async getArgs(options) { const warnings = []; if (options.frequencyPenalty != null) { warnings.push({ type: "unsupported", feature: "frequencyPenalty" }); } if (options.presencePenalty != null) { warnings.push({ type: "unsupported", feature: "presencePenalty" }); } if (options.seed != null) { warnings.push({ type: "unsupported", feature: "seed" }); } const watsonxOptions = await parseProviderOptions({ provider: "watsonx", providerOptions: options.providerOptions, schema: watsonxLanguageModelOptions }); const messages = convertToWatsonxMessages(options); const body = { model_id: this.modelId, project_id: this.config.projectId(), messages, max_tokens: options.maxOutputTokens }; if (options.temperature != null) { body.temperature = options.temperature; } if (options.topP != null) { body.top_p = options.topP; } if (options.topK != null) { body.top_k = options.topK; } if (options.stopSequences && options.stopSequences.length > 0) { const WX_STOP_SEQUENCE_SOFT_CAP = 4; if (options.stopSequences.length > WX_STOP_SEQUENCE_SOFT_CAP) { warnings.push({ type: "other", message: `stopSequences has ${options.stopSequences.length} entries; wx.ai may reject more than ${WX_STOP_SEQUENCE_SOFT_CAP} depending on the model family.` }); } body.stop_sequences = options.stopSequences; } if (watsonxOptions?.timeLimit != null) { body.time_limit = watsonxOptions.timeLimit; } if (watsonxOptions?.reasoningEffort != null) { body.reasoning_effort = watsonxOptions.reasoningEffort; } if (options.responseFormat?.type === "json") { if (options.responseFormat.schema != null) { body.response_format = { type: "json_schema", json_schema: { name: options.responseFormat.name ?? "response", description: options.responseFormat.description, schema: options.responseFormat.schema } }; } else { body.response_format = { type: "json_object" }; } } const { tools, toolChoice, toolWarnings } = prepareWatsonxTools(options); warnings.push(...toolWarnings); if (tools && tools.length > 0) { body.tools = tools; if (toolChoice) Object.assign(body, toolChoice); if (watsonxOptions?.parallelToolCalls != null) { body.parallel_tool_calls = watsonxOptions.parallelToolCalls; } } return { body, warnings }; } async doGenerate(options) { const { body, warnings } = await this.getArgs(options); const apiKey = this.config.apiKey(); const call = async () => { const token = await getIAMToken(apiKey); return postJsonToApi({ url: `${this.config.baseURL}/ml/v1/text/chat?version=${WATSONX_API_VERSION}`, headers: combineHeaders( { Authorization: `Bearer ${token}` }, this.config.headers(), options.headers ), body, failedResponseHandler: watsonxErrorHandler, successfulResponseHandler: createJsonResponseHandler(watsonxChatResponseSchema), abortSignal: options.abortSignal, fetch: this.config.fetch }); }; let value; let rawValue; let responseHeaders; try { ({ value, rawValue, responseHeaders } = await call()); } catch (error) { if (APICallError.isInstance(error) && error.statusCode === 401) { invalidateIAMToken(apiKey); ({ value, rawValue, responseHeaders } = await call()); } else { throw error; } } const response = value; const choice = response.choices[0]; if (!choice) { throw new APICallError({ message: "No choices returned from watsonx.ai", url: `${this.config.baseURL}/ml/v1/text/chat?version=${WATSONX_API_VERSION}`, requestBodyValues: body, isRetryable: false }); } const content = []; if (choice.message.reasoning_content) { content.push({ type: "reasoning", text: choice.message.reasoning_content }); } if (choice.message.content) { content.push({ type: "text", text: choice.message.content }); } if (choice.message.tool_calls) { for (const tc of choice.message.tool_calls) { content.push({ type: "tool-call", toolCallId: tc.id, toolName: tc.function.name, input: parseToolCallArgs(tc.function.arguments, this.modelId) }); } } const hasToolCalls = (choice.message.tool_calls?.length ?? 0) > 0; return { content, finishReason: convertFinishReason(choice.finish_reason ?? "stop", hasToolCalls), usage: convertUsage(response.usage), request: { body }, response: { ...getResponseMetadata(response), headers: responseHeaders, body: rawValue ?? response }, warnings }; } async doStream(options) { const { body, warnings } = await this.getArgs(options); const apiKey = this.config.apiKey(); const generateId = this.generateId; const INITIAL_STALL_MS = 12e4; const STREAMING_STALL_MS = 3e4; const stallController = new AbortController(); const composedSignal = options.abortSignal ? AbortSignal.any([options.abortSignal, stallController.signal]) : stallController.signal; const call = async () => { const token = await getIAMToken(apiKey); return postJsonToApi({ url: `${this.config.baseURL}/ml/v1/text/chat_stream?version=${WATSONX_API_VERSION}`, headers: combineHeaders( { Authorization: `Bearer ${token}` }, this.config.headers(), options.headers ), body, failedResponseHandler: watsonxErrorHandler, successfulResponseHandler: createEventSourceResponseHandler( watsonxChatChunkSchema ), abortSignal: composedSignal, fetch: this.config.fetch }); }; let result; try { result = await call(); } catch (error) { if (APICallError.isInstance(error) && error.statusCode === 401) { invalidateIAMToken(apiKey); result = await call(); } else { throw error; } } const { value: source, responseHeaders } = result; let usage = { inputTokens: { total: 0, noCache: void 0, cacheRead: void 0, cacheWrite: void 0 }, outputTokens: { total: 0, text: void 0, reasoning: void 0 } }; let finishReason = { unified: "other", raw: void 0 }; const toolCallsInProgress = /* @__PURE__ */ new Map(); let textId = null; let reasoningId = null; let sentResponseMetadata = false; let receivedContent = false; let finalized = false; let responseId; let receivedFinishReason = false; let stallTimer; const armStall = () => { if (stallTimer) clearTimeout(stallTimer); const ms = receivedContent ? STREAMING_STALL_MS : INITIAL_STALL_MS; stallTimer = setTimeout(() => { stallController.abort( new Error( `watsonx stream stalled: no data for ${ms / 1e3}s (contentStarted=${receivedContent})` ) ); }, ms); }; const clearStall = () => { if (stallTimer) { clearTimeout(stallTimer); stallTimer = void 0; } }; const finalize = (controller) => { if (finalized) return; finalized = true; clearStall(); if (reasoningId !== null) { controller.enqueue({ type: "reasoning-end", id: reasoningId }); } if (textId !== null) { controller.enqueue({ type: "text-end", id: textId }); } for (const [, tc] of toolCallsInProgress) { controller.enqueue({ type: "tool-input-end", id: tc.id }); controller.enqueue({ type: "tool-call", toolCallId: tc.id, toolName: tc.name, input: parseToolCallArgs(tc.args, this.modelId) }); } const finalFinishReason = toolCallsInProgress.size > 0 && finishReason.unified !== "tool-calls" ? { unified: "tool-calls", raw: finishReason.raw } : finishReason; const completionTokens = usage.outputTokens?.total ?? 0; if (!receivedContent && completionTokens > 0) { console.warn( `[watsonx] stream anomaly: model=${this.modelId} response_id=${responseId ?? "unknown"} reported completion_tokens=${completionTokens} but streamed no content/tool_calls. Likely a wx.ai streaming bug. Finish reason received: ${receivedFinishReason}.` ); } controller.enqueue({ type: "finish", finishReason: finalFinishReason, usage }); }; const stream = source.pipeThrough( new TransformStream({ start(controller) { controller.enqueue({ type: "stream-start", warnings }); armStall(); }, transform(chunk, controller) { if (finalized) return; armStall(); if (process.env.WATSONX_DEBUG_STREAM) { if (chunk.success) { console.log("[watsonx chunk]", JSON.stringify(chunk.rawValue)); } else { console.log( "[watsonx chunk parse-fail]", chunk.error, "raw:", JSON.stringify(chunk.rawValue) ); } } if (options.includeRawChunks) { controller.enqueue({ type: "raw", rawValue: chunk.rawValue }); } if (!chunk.success) { controller.enqueue({ type: "error", error: chunk.error }); return; } const value = chunk.value; if (!sentResponseMetadata) { sentResponseMetadata = true; const meta = getResponseMetadata(value); responseId = meta.id; controller.enqueue({ type: "response-metadata", ...meta, timestamp: meta.timestamp ?? /* @__PURE__ */ new Date() }); } if (value.usage != null) { usage = convertUsage(value.usage); } const choice = value.choices?.[0]; if (!choice) return; if (choice.delta?.reasoning_content) { receivedContent = true; if (reasoningId === null) { reasoningId = `reasoning-${generateId()}`; controller.enqueue({ type: "reasoning-start", id: reasoningId }); } controller.enqueue({ type: "reasoning-delta", id: reasoningId, delta: choice.delta.reasoning_content }); } if (choice.delta?.content) { receivedContent = true; if (textId === null) { textId = `text-${generateId()}`; controller.enqueue({ type: "text-start", id: textId }); } controller.enqueue({ type: "text-delta", id: textId, delta: choice.delta.content }); } if (choice.delta?.tool_calls) { receivedContent = true; for (const tc of choice.delta.tool_calls) { const toolIndex = tc.index ?? 0; let existing = toolCallsInProgress.get(toolIndex); if (!existing) { existing = { id: tc.id ?? `tool-${toolIndex}`, name: "", args: "" }; toolCallsInProgress.set(toolIndex, existing); } if (tc.id) existing.id = tc.id; if (tc.function?.name && existing.name === "") { existing.name = tc.function.name; controller.enqueue({ type: "tool-input-start", id: existing.id, toolName: existing.name }); } if (tc.function?.arguments) { existing.args += tc.function.arguments; controller.enqueue({ type: "tool-input-delta", id: existing.id, delta: tc.function.arguments }); } } } if (choice.finish_reason) { finishReason = convertFinishReason(choice.finish_reason); receivedFinishReason = true; finalize(controller); controller.terminate(); } }, flush(controller) { finalize(controller); } }) ); return { stream, request: { body }, response: { headers: responseHeaders } }; } }; // src/watsonx-embedding-model.ts import { TooManyEmbeddingValuesForCallError } from "@ai-sdk/provider"; import { postJsonToApi as postJsonToApi2, createJsonResponseHandler as createJsonResponseHandler2, combineHeaders as combineHeaders2 } from "@ai-sdk/provider-utils"; var WatsonxEmbeddingModel = class { specificationVersion = "v3"; modelId; settings; config; /** * Maximum number of embeddings per API call. * watsonx.ai supports batching multiple texts in a single request. */ maxEmbeddingsPerCall = 100; /** * Whether the model supports parallel embedding calls. */ supportsParallelCalls = true; constructor(modelId, settings, config) { this.modelId = modelId; this.settings = settings; this.config = config; } get provider() { return this.config.provider; } async doEmbed(options) { if (options.values.length > this.maxEmbeddingsPerCall) { throw new TooManyEmbeddingValuesForCallError({ provider: this.provider, modelId: this.modelId, maxEmbeddingsPerCall: this.maxEmbeddingsPerCall, values: options.values }); } const token = await getIAMToken(this.config.apiKey()); const body = { model_id: this.modelId, project_id: this.config.projectId(), inputs: options.values }; if (this.settings.truncateInputTokens) { body.parameters = { truncate_input_tokens: true }; } const { value: response, rawValue, responseHeaders } = await postJsonToApi2({ url: `${this.config.baseURL}/ml/v1/text/embeddings?version=${WATSONX_API_VERSION}`, headers: combineHeaders2( { Authorization: `Bearer ${token}` }, this.config.headers(), options.headers ), body, failedResponseHandler: watsonxErrorHandler, successfulResponseHandler: createJsonResponseHandler2( watsonxEmbeddingResponseSchema ), abortSignal: options.abortSignal, fetch: this.config.fetch }); let totalTokens; if (response.input_token_count != null) { totalTokens = response.input_token_count; } else { totalTokens = 0; for (const result of response.results) { totalTokens += result.input_token_count ?? 0; } } return { embeddings: response.results.map((r) => r.embedding), usage: { tokens: totalTokens }, response: { headers: responseHeaders, body: rawValue ?? response }, warnings: [] }; } }; // src/version.ts var VERSION = true ? "2.0.0" : "0.0.0-test"; // src/watsonx-provider.ts function createWatsonx(options = {}) { const baseURL = withoutTrailingSlash(options.baseURL) ?? "https://us-south.ml.cloud.ibm.com"; const getApiKey = () => { const apiKey = options.apiKey ?? process.env.WATSONX_AI_APIKEY; if (!apiKey) { throw new Error( "IBM Cloud API key is required. Set WATSONX_AI_APIKEY env variable or pass apiKey option." ); } return apiKey; }; const getProjectId = () => { const projectId = options.projectId ?? process.env.WATSONX_AI_PROJECT_ID; if (!projectId) { throw new Error( "watsonx.ai project ID is required. Set WATSONX_AI_PROJECT_ID env variable or pass projectId option." ); } return projectId; }; const getHeaders = () => withUserAgentSuffix( { ...options.headers }, `ai-sdk/watsonx/${VERSION}` ); const createChatModel = (modelId) => { return new WatsonxChatLanguageModel(modelId, { provider: "watsonx.chat", baseURL, apiKey: getApiKey, projectId: getProjectId, headers: getHeaders, fetch: options.fetch, generateId: options.generateId }); }; const createEmbeddingModel = (modelId, settings = {}) => { return new WatsonxEmbeddingModel(modelId, settings, { provider: "watsonx.embedding", baseURL, apiKey: getApiKey, projectId: getProjectId, headers: getHeaders, fetch: options.fetch }); }; const provider = function(modelId) { if (new.target) { throw new Error( "The watsonx model function cannot be called with the `new` keyword." ); } return createChatModel(modelId); }; provider.specificationVersion = "v3"; provider.chat = createChatModel; provider.languageModel = createChatModel; provider.embeddingModel = createEmbeddingModel; provider.textEmbeddingModel = createEmbeddingModel; provider.imageModel = (modelId) => { throw new NoSuchModelError({ modelId, modelType: "imageModel" }); }; return provider; } var watsonx = createWatsonx(); export { WATSONX_API_VERSION, WatsonxChatLanguageModel, WatsonxEmbeddingModel, createWatsonx, createWatsonx as createWatsonxProvider, watsonx, watsonxLanguageModelOptions }; //# sourceMappingURL=index.mjs.map