UNPKG

@friendliai/ai-provider

Version:

<!-- header start --> <p align="center"> <img src="https://huggingface.co/datasets/FriendliAI/documentation-images/resolve/main/model-card-assets/friendliai.png" width="100%" alt="FriendliAI Logo"> </p> <!-- header end -->

1,155 lines (1,147 loc) 39.3 kB
// src/friendli-provider.ts import { OpenAICompatibleCompletionLanguageModel } from "@ai-sdk/openai-compatible"; import { NoSuchModelError } from "@ai-sdk/provider"; import { loadApiKey as loadApiKey2, withoutTrailingSlash } from "@ai-sdk/provider-utils"; // src/friendli-chat-language-model.ts import { convertOpenAICompatibleChatUsage, convertToOpenAICompatibleChatMessages, getResponseMetadata, mapOpenAICompatibleFinishReason } from "@ai-sdk/openai-compatible/internal"; import { InvalidResponseDataError } from "@ai-sdk/provider"; import { combineHeaders, createEventSourceResponseHandler, createJsonResponseHandler, generateId, isParsableJson, parseProviderOptions, postJsonToApi } from "@ai-sdk/provider-utils"; import { z as z2 } from "zod/v4"; // src/friendli-error.ts import { APICallError } from "@ai-sdk/provider"; import { safeParseJSON } from "@ai-sdk/provider-utils"; import { z } from "zod"; var friendliErrorResponseSchema = z.object({ message: z.string(), error: z.record(z.string(), z.any()).optional() }); var openAIStyleErrorResponseSchema = z.object({ error: z.object({ message: z.string() }).loose() }).loose(); var friendliaiErrorSchema = z.union([ // OpenAI/OpenRouter style error: { "error": { "message": "..." } } openAIStyleErrorResponseSchema, // Friendli style error: { "message": "...", "error": { ... } } friendliErrorResponseSchema ]); var friendliaiErrorStructure = { errorSchema: friendliaiErrorSchema, errorToMessage: (data) => { if (typeof data === "object" && data != null && "error" in data && typeof data.error === "object" && data.error != null && "message" in data.error && typeof data.error.message === "string") { return data.error.message; } if (typeof data === "object" && data != null && "message" in data && typeof data.message === "string") { return data.message; } return "Unknown error"; } }; var friendliaiFailedResponseHandler = async ({ response, url, requestBodyValues }) => { const responseBody = await response.text(); const responseHeaders = {}; response.headers.forEach((value, key) => { responseHeaders[key] = value; }); const baseErrorOptions = { url, requestBodyValues, statusCode: response.status, responseHeaders, responseBody }; const trimmedBody = responseBody.trim(); if (trimmedBody === "") { const fallback2 = response.statusText || `Request failed with status ${response.status}`; return { responseHeaders, value: new APICallError({ message: fallback2, ...baseErrorOptions }) }; } const parsedError = await safeParseJSON({ text: responseBody, schema: friendliaiErrorSchema }); if (parsedError.success) { return { responseHeaders, value: new APICallError({ message: friendliaiErrorStructure.errorToMessage(parsedError.value), data: parsedError.value, ...baseErrorOptions }) }; } const fallback = trimmedBody || response.statusText || `Request failed with status ${response.status}`; return { responseHeaders, value: new APICallError({ message: fallback, cause: parsedError.error, ...baseErrorOptions }) }; }; var tryWrapFriendliJsonEnvelopeError = async (error) => { if (!APICallError.isInstance(error)) { return void 0; } const responseBody = error.responseBody; if (typeof responseBody !== "string" || responseBody.trim() === "") { return void 0; } const parsedError = await safeParseJSON({ text: responseBody, schema: friendliaiErrorSchema }); if (!parsedError.success) { return void 0; } return new APICallError({ message: friendliaiErrorStructure.errorToMessage(parsedError.value), url: error.url, requestBodyValues: error.requestBodyValues, statusCode: error.statusCode, responseHeaders: error.responseHeaders, responseBody: error.responseBody, cause: error, isRetryable: error.isRetryable, data: parsedError.value }); }; // src/friendli-prepare-tools.ts import { UnsupportedFunctionalityError } from "@ai-sdk/provider"; function prepareTools({ tools, toolChoice }) { var _a; tools = (tools == null ? void 0 : tools.length) ? tools : void 0; const toolWarnings = []; if (tools == null) { return { tools: void 0, toolChoice: void 0, toolWarnings }; } const openaiCompatTools = []; for (const tool of tools) { if (tool.type === "provider") { openaiCompatTools.push({ // NOTE: Friendli tool-assisted API expects provider tool types like "web:search". // We derive it from the provider tool id (e.g. "friendli.web:search" -> "web:search") // instead of tool.name (often "web_search"). type: (_a = tool.id.split(".")[1]) != null ? _a : "unknown" }); } else { openaiCompatTools.push({ type: "function", function: { name: tool.name, description: tool.description, parameters: tool.inputSchema } }); } } if (toolChoice == null) { return { tools: openaiCompatTools, toolChoice: void 0, toolWarnings }; } const type = toolChoice.type; switch (type) { case "auto": case "none": case "required": return { tools: openaiCompatTools, toolChoice: type, toolWarnings }; case "tool": return { tools: openaiCompatTools, toolChoice: { type: "function", function: { name: toolChoice.toolName } }, toolWarnings }; default: { const _exhaustiveCheck = type; throw new UnsupportedFunctionalityError({ functionality: `tool choice type: ${_exhaustiveCheck}` }); } } } // src/friendli-chat-language-model.ts function isRecord(value) { return typeof value === "object" && value != null; } function isHostedToolExecutionChunk(value) { if (!isRecord(value)) return false; return typeof value.status === "string" && typeof value.name === "string" && Array.isArray(value.parameters); } function getChunkErrorMessage(value) { if (!isRecord(value)) return void 0; if (typeof value.message === "string") { return value.message; } const nestedError = value.error; if (isRecord(nestedError) && typeof nestedError.message === "string") { return nestedError.message; } return void 0; } function isOpenAIChatChunk(value) { if (!isRecord(value)) return false; return Array.isArray(value.choices); } function addReasoningToMessages(prompt, messages) { let promptAssistantIndex = 0; for (const promptMessage of prompt) { if (promptMessage.role === "assistant") { const reasoningText = promptMessage.content.filter((part) => part.type === "reasoning").map((part) => part.text).join("\n"); if (reasoningText) { let messagesAssistantIndex = 0; for (let i = 0; i < messages.length; i++) { if (messages[i].role === "assistant") { if (messagesAssistantIndex === promptAssistantIndex) { messages[i].reasoning_content = reasoningText; break; } messagesAssistantIndex++; } } } promptAssistantIndex++; } } return messages; } var FriendliAIChatLanguageModel = class { // type inferred via constructor constructor(modelId, config) { this.specificationVersion = "v3"; var _a; this.modelId = modelId; this.config = config; const errorStructure = friendliaiErrorStructure; this.chunkSchema = createOpenAICompatibleChatChunkSchema(errorStructure.errorSchema); this.failedResponseHandler = friendliaiFailedResponseHandler; this.supportsStructuredOutputs = (_a = config.supportsStructuredOutputs) != null ? _a : true; } get provider() { return this.config.provider; } get supportedUrls() { var _a, _b, _c; return (_c = (_b = (_a = this.config).supportedUrls) == null ? void 0 : _b.call(_a)) != null ? _c : {}; } async getArgs({ prompt, maxOutputTokens, temperature, topP, topK, frequencyPenalty, presencePenalty, providerOptions, stopSequences, responseFormat, seed, toolChoice, tools, stream }) { var _a; const warnings = []; const friendliOptions = await parseProviderOptions({ provider: "friendliai", providerOptions, schema: friendliProviderOptionsSchema }); const legacyFriendliOptions = await parseProviderOptions({ provider: "friendli", providerOptions, schema: friendliProviderOptionsSchema }); const options = { ...legacyFriendliOptions, ...friendliOptions }; if ((responseFormat == null ? void 0 : responseFormat.type) === "json" && responseFormat.schema != null && !this.supportsStructuredOutputs) { warnings.push({ type: "unsupported", feature: "responseFormat", details: "JSON response format schema is only supported with structuredOutputs" }); } const { tools: openaiTools, toolChoice: openaiToolChoice, toolWarnings } = prepareTools({ tools, toolChoice }); const isToolsPresent = openaiTools != null && openaiTools.length > 0; if (isToolsPresent && (responseFormat != null || (options == null ? void 0 : options.regex) != null)) { warnings.push({ type: "unsupported", feature: "responseFormat", details: "response_format is not supported when tools are present." }); } return { args: { // >>> hard-coded default options >>> parse_reasoning: true, // <<< hard-coded default options <<< model: this.modelId, // standardized settings: stream, max_tokens: maxOutputTokens, temperature, top_p: topP, top_k: topK, frequency_penalty: frequencyPenalty, presence_penalty: presencePenalty, response_format: isToolsPresent === false ? (responseFormat == null ? void 0 : responseFormat.type) === "json" ? this.supportsStructuredOutputs === true && responseFormat.schema != null ? { type: "json_schema", json_schema: { schema: responseFormat.schema, name: (_a = responseFormat.name) != null ? _a : "response", description: responseFormat.description } } : { type: "json_object" } : (options == null ? void 0 : options.regex) != null ? { type: "regex", schema: options.regex } : void 0 : void 0, stop: stopSequences, seed, min_p: options == null ? void 0 : options.minP, repetition_penalty: options == null ? void 0 : options.repetitionPenalty, xtc_threshold: options == null ? void 0 : options.xtcThreshold, xtc_probability: options == null ? void 0 : options.xtcProbability, ...(options == null ? void 0 : options.chat_template_kwargs) ? { chat_template_kwargs: options.chat_template_kwargs } : {}, // messages: // Use addReasoningToMessages to include reasoning_content in assistant messages // for interleaved thinking support messages: addReasoningToMessages(prompt, convertToOpenAICompatibleChatMessages(prompt)), // tools: tools: openaiTools, tool_choice: openaiToolChoice, parallel_tool_calls: options == null ? void 0 : options.parallelToolCalls }, warnings: [...warnings, ...toolWarnings] }; } async doGenerate(options) { var _a, _b; const { args, warnings } = await this.getArgs({ ...options, stream: false }); const body = JSON.stringify(args); const response = await (async () => { try { return await postJsonToApi({ url: this.config.url({ path: "/chat/completions", modelId: this.modelId }), headers: combineHeaders(this.config.headers(), options.headers), body: args, failedResponseHandler: this.failedResponseHandler, successfulResponseHandler: createJsonResponseHandler(OpenAICompatibleChatResponseSchema), abortSignal: options.abortSignal, fetch: this.config.fetch }); } catch (error) { const wrappedError = await tryWrapFriendliJsonEnvelopeError(error); if (wrappedError != null) { throw wrappedError; } throw error; } })(); const { responseHeaders, value: responseBody, rawValue: rawResponse } = response; const choice = responseBody.choices[0]; const content = []; const text = choice.message.content; if (text != null && text.length > 0) { content.push({ type: "text", text }); } const reasoning = choice.message.reasoning_content; if (reasoning != null && reasoning.length > 0) { content.push({ type: "reasoning", text: reasoning }); } if (choice.message.tool_calls != null) { for (const toolCall of choice.message.tool_calls) { content.push({ type: "tool-call", toolCallId: (_a = toolCall.id) != null ? _a : generateId(), toolName: toolCall.function.name, input: toolCall.function.arguments }); } } return { content, finishReason: { unified: mapOpenAICompatibleFinishReason(choice.finish_reason), raw: (_b = choice.finish_reason) != null ? _b : void 0 }, usage: convertOpenAICompatibleChatUsage(responseBody.usage), // providerMetadata, request: { body }, response: { ...getResponseMetadata(responseBody), headers: responseHeaders, body: rawResponse }, warnings }; } async doStream(options) { var _a; const { args, warnings } = await this.getArgs({ ...options, stream: true }); const body = { ...args, stream: true, // only include stream_options when in strict compatibility mode: stream_options: this.config.includeUsage ? { include_usage: true } : void 0 }; const metadataExtractor = (_a = this.config.metadataExtractor) == null ? void 0 : _a.createStreamExtractor(); const { responseHeaders, value: response } = await postJsonToApi({ url: this.config.url({ path: "/chat/completions", modelId: this.modelId }), headers: combineHeaders(this.config.headers(), options.headers), body, failedResponseHandler: this.failedResponseHandler, successfulResponseHandler: createEventSourceResponseHandler(this.chunkSchema), abortSignal: options.abortSignal, fetch: this.config.fetch }); const toolCalls = []; let finishReason = { unified: "other", raw: void 0 }; let usage = void 0; let isFirstChunk = true; const providerOptionsName = "friendliai"; let currentTextId = null; let currentReasoningId = null; return { stream: response.pipeThrough( new TransformStream({ start(controller) { controller.enqueue({ type: "stream-start", warnings }); }, // NOTE: Chunk values can contain OpenAI-compatible deltas, hosted tool events, and error events. // We narrow with type guards for safe handling. transform(chunk, controller) { var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q; if (options.includeRawChunks) { controller.enqueue({ type: "raw", rawValue: chunk.rawValue }); } if (!chunk.success) { finishReason = { unified: "error", raw: void 0 }; controller.enqueue({ type: "error", error: chunk.error }); return; } const value = chunk.value; metadataExtractor == null ? void 0 : metadataExtractor.processChunk(chunk.rawValue); if (isHostedToolExecutionChunk(value)) { const toolCallId = (_a2 = value.tool_call_id) != null ? _a2 : generateId(); switch (value.status) { case "STARTED": controller.enqueue({ type: "tool-call", toolCallId, toolName: value.name, input: JSON.stringify( Object.fromEntries(value.parameters.map((p) => [p.name, p.value])) ), providerExecuted: true }); break; case "UPDATING": break; case "ENDED": controller.enqueue({ type: "tool-result", toolCallId, toolName: value.name, result: (_b = value.result) != null ? _b : "" }); break; case "ERRORED": finishReason = { unified: "error", raw: void 0 }; controller.enqueue({ type: "tool-result", toolCallId, toolName: value.name, result: (_d = (_c = value.error) == null ? void 0 : _c.msg) != null ? _d : "Unknown error", isError: true }); break; default: finishReason = { unified: "error", raw: void 0 }; controller.enqueue({ type: "error", error: new Error(`Unsupported tool call status: ${value.status}`) }); } return; } const chunkErrorMessage = getChunkErrorMessage(value); if (chunkErrorMessage != null) { finishReason = { unified: "error", raw: void 0 }; controller.enqueue({ type: "error", error: chunkErrorMessage }); return; } if (!isOpenAIChatChunk(value)) { finishReason = { unified: "error", raw: void 0 }; controller.enqueue({ type: "error", error: new Error("Unsupported chunk shape") }); return; } const chunkValue = value; if (isFirstChunk) { isFirstChunk = false; controller.enqueue({ type: "response-metadata", ...getResponseMetadata(chunkValue) }); } if (chunkValue.usage != null) { usage = chunkValue.usage; } const choice = chunkValue.choices[0]; if ((choice == null ? void 0 : choice.finish_reason) != null) { finishReason = { unified: mapOpenAICompatibleFinishReason(choice.finish_reason), raw: choice.finish_reason }; } if ((choice == null ? void 0 : choice.delta) == null) { return; } const delta = choice.delta; if (delta.reasoning_content != null) { if (currentReasoningId == null) { currentReasoningId = generateId(); controller.enqueue({ type: "reasoning-start", id: currentReasoningId }); } controller.enqueue({ type: "reasoning-delta", id: currentReasoningId, delta: delta.reasoning_content }); } if (delta.content != null) { if (currentTextId == null) { currentTextId = generateId(); controller.enqueue({ type: "text-start", id: currentTextId }); } controller.enqueue({ type: "text-delta", id: currentTextId, delta: delta.content }); } if (delta.tool_calls != null) { for (const toolCallDelta of delta.tool_calls) { const index = toolCallDelta.index; if (toolCalls[index] == null) { if (toolCallDelta.type !== "function") { throw new InvalidResponseDataError({ data: toolCallDelta, message: `Expected 'function' type.` }); } if (toolCallDelta.id == null) { throw new InvalidResponseDataError({ data: toolCallDelta, message: `Expected 'id' to be a string.` }); } if (((_e = toolCallDelta.function) == null ? void 0 : _e.name) == null) { throw new InvalidResponseDataError({ data: toolCallDelta, message: `Expected 'function.name' to be a string.` }); } toolCalls[index] = { id: toolCallDelta.id, type: "function", function: { name: toolCallDelta.function.name, arguments: (_f = toolCallDelta.function.arguments) != null ? _f : "" }, hasFinished: false }; controller.enqueue({ type: "tool-input-start", id: toolCallDelta.id, toolName: toolCallDelta.function.name }); const toolCall2 = toolCalls[index]; if (((_g = toolCall2.function) == null ? void 0 : _g.name) != null && ((_h = toolCall2.function) == null ? void 0 : _h.arguments) != null) { if (toolCall2.function.arguments.length > 0) { controller.enqueue({ type: "tool-input-delta", id: toolCall2.id, delta: toolCall2.function.arguments }); } if (isParsableJson(toolCall2.function.arguments)) { controller.enqueue({ type: "tool-input-end", id: toolCall2.id }); controller.enqueue({ type: "tool-call", toolCallId: (_i = toolCall2.id) != null ? _i : generateId(), toolName: toolCall2.function.name, input: toolCall2.function.arguments }); toolCall2.hasFinished = true; } } continue; } const toolCall = toolCalls[index]; if (toolCall.hasFinished) { continue; } if (((_j = toolCallDelta.function) == null ? void 0 : _j.arguments) != null) { toolCall.function.arguments += (_l = (_k = toolCallDelta.function) == null ? void 0 : _k.arguments) != null ? _l : ""; } controller.enqueue({ type: "tool-input-delta", id: toolCall.id, delta: (_n = (_m = toolCallDelta.function) == null ? void 0 : _m.arguments) != null ? _n : "" }); if (((_o = toolCall.function) == null ? void 0 : _o.name) != null && ((_p = toolCall.function) == null ? void 0 : _p.arguments) != null && isParsableJson(toolCall.function.arguments)) { controller.enqueue({ type: "tool-input-end", id: toolCall.id }); controller.enqueue({ type: "tool-call", toolCallId: (_q = toolCall.id) != null ? _q : generateId(), toolName: toolCall.function.name, input: toolCall.function.arguments }); toolCall.hasFinished = true; } } } }, flush(controller) { var _a2, _b, _c; if (currentReasoningId != null) { controller.enqueue({ type: "reasoning-end", id: currentReasoningId }); } if (currentTextId != null) { controller.enqueue({ type: "text-end", id: currentTextId }); } for (const toolCall of toolCalls.filter( (pendingToolCall) => !pendingToolCall.hasFinished )) { controller.enqueue({ type: "tool-input-end", id: toolCall.id }); controller.enqueue({ type: "tool-call", toolCallId: (_a2 = toolCall.id) != null ? _a2 : generateId(), toolName: toolCall.function.name, input: toolCall.function.arguments }); } const providerMetadata = { [providerOptionsName]: {}, ...metadataExtractor == null ? void 0 : metadataExtractor.buildMetadata() }; if (((_b = usage == null ? void 0 : usage.completion_tokens_details) == null ? void 0 : _b.accepted_prediction_tokens) != null) { providerMetadata[providerOptionsName].acceptedPredictionTokens = usage.completion_tokens_details.accepted_prediction_tokens; } if (((_c = usage == null ? void 0 : usage.completion_tokens_details) == null ? void 0 : _c.rejected_prediction_tokens) != null) { providerMetadata[providerOptionsName].rejectedPredictionTokens = usage.completion_tokens_details.rejected_prediction_tokens; } controller.enqueue({ type: "finish", finishReason, usage: convertOpenAICompatibleChatUsage(usage), providerMetadata }); } }) ), request: { body }, response: { headers: responseHeaders } }; } }; var openaiCompatibleTokenUsageSchema = z2.object({ prompt_tokens: z2.number().nullish(), completion_tokens: z2.number().nullish(), total_tokens: z2.number().nullish(), prompt_tokens_details: z2.object({ cached_tokens: z2.number().nullish() }).nullish(), completion_tokens_details: z2.object({ reasoning_tokens: z2.number().nullish(), accepted_prediction_tokens: z2.number().nullish(), rejected_prediction_tokens: z2.number().nullish() }).nullish() }).nullish(); var OpenAICompatibleChatResponseSchema = z2.object({ id: z2.string().nullish(), created: z2.number().nullish(), model: z2.string().nullish(), choices: z2.array( z2.object({ message: z2.object({ role: z2.literal("assistant").nullish(), content: z2.string().nullish(), reasoning_content: z2.string().nullish(), tool_calls: z2.array( z2.object({ id: z2.string().nullish(), type: z2.literal("function"), function: z2.object({ name: z2.string(), arguments: z2.string() }) }) ).nullish() }), finish_reason: z2.string().nullish() }) ), usage: openaiCompatibleTokenUsageSchema }); var createOpenAICompatibleChatChunkSchema = (errorSchema) => z2.union([ z2.object({ id: z2.string().nullish(), created: z2.number().nullish(), model: z2.string().nullish(), choices: z2.array( z2.object({ delta: z2.object({ role: z2.enum(["assistant"]).nullish(), content: z2.string().nullish(), reasoning_content: z2.string().nullish(), tool_calls: z2.array( z2.object({ index: z2.number(), id: z2.string().nullish(), type: z2.literal("function").nullish(), function: z2.object({ name: z2.string().nullish(), arguments: z2.string().nullish() }) }) ).nullish() }).nullish(), finish_reason: z2.string().nullish() }) ), usage: openaiCompatibleTokenUsageSchema }), z2.object({ name: z2.string(), status: z2.enum(["ENDED", "STARTED", "ERRORED", "UPDATING"]), message: z2.null(), parameters: z2.array( z2.object({ name: z2.string(), value: z2.string() }) ), result: z2.string().nullable(), error: z2.object({ type: z2.enum(["INVALID_PARAMETER", "UNKNOWN"]), msg: z2.string() }).nullable(), timestamp: z2.number(), usage: z2.null(), tool_call_id: z2.string().nullable() }), errorSchema ]); var friendliProviderOptionsSchema = z2.object({ /** * Whether to enable parallel function calling during tool use. Default to true. */ parallelToolCalls: z2.boolean().nullish(), /** * BETA FEATURE: You can write a regular expression to force output that satisfies that regular expression. */ // regex: z.instanceof(RegExp).nullish(), regex: z2.string().nullish(), chat_template_kwargs: z2.record(z2.string(), z2.any()).nullish(), /** * A scaling factor used to determine the minimum token probability threshold. */ minP: z2.number().nullish(), /** * Penalizes tokens that have already appeared in the generated result. */ repetitionPenalty: z2.number().nullish(), /** * A probability threshold used to identify “top choice” tokens for exclusion in XTC sampling. */ xtcThreshold: z2.number().nullish(), /** * The probability that XTC (Exclude Top Choices) filtering will be applied for each sampling decision. */ xtcProbability: z2.number().nullish() }); // src/friendli-settings.ts var FriendliAIServerlessModelIds = [ "google/gemma-4-31B-it", "zai-org/GLM-5.1", "zai-org/GLM-5", "meta-llama/Llama-3.3-70B-Instruct", "meta-llama-3.3-70b-instruct", "meta-llama/Llama-3.1-8B-Instruct", "meta-llama-3.1-8b-instruct", "Qwen/Qwen3-235B-A22B-Instruct-2507", "deepseek-ai/DeepSeek-V3.2", "openai/whisper-large-v3", "MiniMaxAI/MiniMax-M2.5", "LGAI-EXAONE/K-EXAONE-236B-A23B" ]; // src/friendli-tools.ts import { createProviderToolFactoryWithOutputSchema } from "@ai-sdk/provider-utils"; import { z as z3 } from "zod"; var inputSchema = z3.object({}).loose(); var outputSchema = z3.unknown(); var webSearchTool = createProviderToolFactoryWithOutputSchema({ id: "friendli.web:search", inputSchema, outputSchema }); var webUrlTool = createProviderToolFactoryWithOutputSchema({ id: "friendli.web:url", inputSchema, outputSchema }); var mathCalendarTool = createProviderToolFactoryWithOutputSchema({ id: "friendli.math:calendar", inputSchema, outputSchema }); var mathStatisticsTool = createProviderToolFactoryWithOutputSchema({ id: "friendli.math:statistics", inputSchema, outputSchema }); var mathCalculatorTool = createProviderToolFactoryWithOutputSchema({ id: "friendli.math:calculator", inputSchema, outputSchema }); var codePythonInterpreterTool = createProviderToolFactoryWithOutputSchema({ id: "friendli.code:python-interpreter", inputSchema, outputSchema }); var linkupSearchTool = createProviderToolFactoryWithOutputSchema({ id: "friendli.linkup:search", inputSchema, outputSchema }); function webSearch() { return webSearchTool({}); } function webUrl() { return webUrlTool({}); } function mathCalendar() { return mathCalendarTool({}); } function mathStatistics() { return mathStatisticsTool({}); } function mathCalculator() { return mathCalculatorTool({}); } function codePythonInterpreter() { return codePythonInterpreterTool({}); } function linkupSearch() { return linkupSearchTool({}); } var friendliTools = { webSearch, webUrl, mathCalendar, mathStatistics, mathCalculator, codePythonInterpreter, linkupSearch }; // src/get-available-models.ts import { loadApiKey } from "@ai-sdk/provider-utils"; var DEFAULT_GRAPHQL_URL = "https://api-internal.friendli.ai/api/graphql"; async function postGraphQL(url, body, headers) { const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", ...headers }, body: JSON.stringify(body) }); let json; try { json = await res.json(); } catch (err) { console.error( "Failed to parse JSON response from Friendli API:", err, "Status:", res.status, res.statusText ); throw new Error( `Failed to parse JSON response from Friendli API: ${err instanceof Error ? err.message : String(err)}` ); } return json; } function normalizePriceUnit(unit) { if (!unit) return void 0; return unit; } async function getAvailableModelsImpl(options) { var _a, _b, _c, _d, _e, _f; let token; try { token = (_a = options.apiKey) != null ? _a : loadApiKey({ apiKey: void 0, environmentVariableName: "FRIENDLI_TOKEN", description: "FRIENDLI_TOKEN" }); } catch (e) { token = void 0; } const headers = { ...token ? { Authorization: `Bearer ${token}` } : {}, ...options.teamId ? { "X-Friendli-Team": options.teamId } : {}, ...(_b = options.headers) != null ? _b : {} }; const url = (_c = options.graphqlURL) != null ? _c : DEFAULT_GRAPHQL_URL; const query = ` query Edges { serverlessEndpoints { edges { ... on ServerlessChatEndpointCatalog { id name status price { inputPrice cachedInputPrice outputPrice audioMinutePrice unit responseTimePrice priceUnitType } contextLength } } } } `; const resp = await postGraphQL( url, { query, variables: {}, operationName: "Edges" }, headers ); if (resp.errors && resp.errors.length > 0) { throw new Error( `getAvailableModels: GraphQL error: ${resp.errors.map((e) => e.message).join("; ")}` ); } const edges = (_f = (_e = (_d = resp.data) == null ? void 0 : _d.serverlessEndpoints) == null ? void 0 : _e.edges) != null ? _f : []; const models = edges.map((e) => { var _a2, _b2, _c2, _d2, _e2, _f2, _g, _h; const warm = e.status === "WARM"; const pricing = e.price ? { inputToken: (_a2 = e.price.inputPrice) != null ? _a2 : void 0, cachedInputToken: (_b2 = e.price.cachedInputPrice) != null ? _b2 : void 0, outputToken: (_c2 = e.price.outputPrice) != null ? _c2 : void 0, responseTime: (_d2 = e.price.responseTimePrice) != null ? _d2 : void 0, audioMinute: (_e2 = e.price.audioMinutePrice) != null ? _e2 : void 0, unitType: (_f2 = e.price.priceUnitType) != null ? _f2 : void 0, unit: normalizePriceUnit(e.price.unit), currency: "USD" } : void 0; return { id: e.id, name: (_g = e.name) != null ? _g : void 0, description: void 0, pricing, warm, cold: warm === false, contextLength: (_h = e.contextLength) != null ? _h : void 0 }; }); return { models }; } // src/friendli-provider.ts function createFriendli(options = {}) { const getHeaders = () => ({ Authorization: `Bearer ${loadApiKey2({ apiKey: options.apiKey, environmentVariableName: "FRIENDLI_TOKEN", description: "FRIENDLI_TOKEN" })}`, "X-Friendli-Team": options.teamId, ...options.headers }); const baseURLAutoSelect = (modelId, baseURL) => { const FriendliBaseURL = { serverless: "https://api.friendli.ai/serverless/v1", serverless_tools: "https://api.friendli.ai/serverless/tools/v1", dedicated: "https://api.friendli.ai/dedicated/v1" }; const customBaseURL = withoutTrailingSlash(baseURL); if (typeof customBaseURL === "string" && customBaseURL !== "dedicated" && customBaseURL !== "serverless" && customBaseURL !== "serverless-tools") { return { baseURL: customBaseURL, type: "custom" }; } switch (baseURL) { case "dedicated": return { baseURL: FriendliBaseURL.dedicated, type: "dedicated" }; case "serverless": return { baseURL: FriendliBaseURL.serverless, type: "serverless" }; case "serverless-tools": return { baseURL: FriendliBaseURL.serverless_tools, type: "serverless-tools" }; default: if (FriendliAIServerlessModelIds.includes(modelId)) { return { baseURL: FriendliBaseURL.serverless, type: "serverless" }; } else { return { baseURL: FriendliBaseURL.dedicated, type: "dedicated" }; } } }; const createLanguageModel = (modelId) => { const { baseURL, type } = baseURLAutoSelect(modelId, options.baseURL); return new FriendliAIChatLanguageModel(modelId, { provider: `friendliai.${type}.chat`, url: ({ path }) => `${baseURL}${path}`, headers: getHeaders, fetch: options.fetch, includeUsage: options.includeUsage }); }; const createCompletionModel = (modelId) => { const { baseURL, type } = baseURLAutoSelect(modelId, options.baseURL); return new OpenAICompatibleCompletionLanguageModel(modelId, { provider: `friendliai.${type}.completion`, url: ({ path }) => `${baseURL}${path}`, headers: getHeaders, fetch: options.fetch, errorStructure: friendliaiErrorStructure }); }; const createTextEmbeddingModel = (modelId) => { throw new NoSuchModelError({ modelId, modelType: "embeddingModel" }); }; const createImageModel = (modelId) => { throw new NoSuchModelError({ modelId, modelType: "imageModel" }); }; const createTranscriptionModel = (modelId) => { throw new NoSuchModelError({ modelId, modelType: "languageModel" }); }; const createSpeechModel = (modelId) => { throw new NoSuchModelError({ modelId, modelType: "languageModel" }); }; const provider = (modelId) => createLanguageModel(modelId); provider.languageModel = createLanguageModel; provider.chat = createLanguageModel; provider.completion = createCompletionModel; provider.embedding = createTextEmbeddingModel; provider.embeddingModel = createTextEmbeddingModel; provider.getAvailableModels = async (opts) => { var _a; const defaultURL = "https://api-internal.friendli.ai/api/graphql"; const graphqlURL = (_a = opts == null ? void 0 : opts.graphqlURL) != null ? _a : defaultURL; const apiKey = options.apiKey; const teamId = options.teamId; const headers = options.headers; return getAvailableModelsImpl({ apiKey, teamId, headers, graphqlURL }); }; provider.imageModel = createImageModel; provider.transcription = createTranscriptionModel; provider.speech = createSpeechModel; provider.tools = friendliTools; return provider; } var friendli = createFriendli(); export { createFriendli, friendli }; //# sourceMappingURL=index.mjs.map