@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,164 lines (1,154 loc) • 42 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
createFriendli: () => createFriendli,
friendli: () => friendli
});
module.exports = __toCommonJS(index_exports);
// src/friendli-provider.ts
var import_openai_compatible = require("@ai-sdk/openai-compatible");
var import_provider4 = require("@ai-sdk/provider");
var import_provider_utils5 = require("@ai-sdk/provider-utils");
// src/friendli-chat-language-model.ts
var import_internal = require("@ai-sdk/openai-compatible/internal");
var import_provider3 = require("@ai-sdk/provider");
var import_provider_utils2 = require("@ai-sdk/provider-utils");
var import_v4 = require("zod/v4");
// src/friendli-error.ts
var import_provider = require("@ai-sdk/provider");
var import_provider_utils = require("@ai-sdk/provider-utils");
var import_zod = require("zod");
var friendliErrorResponseSchema = import_zod.z.object({
message: import_zod.z.string(),
error: import_zod.z.record(import_zod.z.string(), import_zod.z.any()).optional()
});
var openAIStyleErrorResponseSchema = import_zod.z.object({
error: import_zod.z.object({
message: import_zod.z.string()
}).loose()
}).loose();
var friendliaiErrorSchema = import_zod.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 import_provider.APICallError({
message: fallback2,
...baseErrorOptions
})
};
}
const parsedError = await (0, import_provider_utils.safeParseJSON)({
text: responseBody,
schema: friendliaiErrorSchema
});
if (parsedError.success) {
return {
responseHeaders,
value: new import_provider.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 import_provider.APICallError({
message: fallback,
cause: parsedError.error,
...baseErrorOptions
})
};
};
var tryWrapFriendliJsonEnvelopeError = async (error) => {
if (!import_provider.APICallError.isInstance(error)) {
return void 0;
}
const responseBody = error.responseBody;
if (typeof responseBody !== "string" || responseBody.trim() === "") {
return void 0;
}
const parsedError = await (0, import_provider_utils.safeParseJSON)({
text: responseBody,
schema: friendliaiErrorSchema
});
if (!parsedError.success) {
return void 0;
}
return new import_provider.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
var import_provider2 = require("@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 import_provider2.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 (0, import_provider_utils2.parseProviderOptions)({
provider: "friendliai",
providerOptions,
schema: friendliProviderOptionsSchema
});
const legacyFriendliOptions = await (0, import_provider_utils2.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, (0, import_internal.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 (0, import_provider_utils2.postJsonToApi)({
url: this.config.url({
path: "/chat/completions",
modelId: this.modelId
}),
headers: (0, import_provider_utils2.combineHeaders)(this.config.headers(), options.headers),
body: args,
failedResponseHandler: this.failedResponseHandler,
successfulResponseHandler: (0, import_provider_utils2.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 : (0, import_provider_utils2.generateId)(),
toolName: toolCall.function.name,
input: toolCall.function.arguments
});
}
}
return {
content,
finishReason: {
unified: (0, import_internal.mapOpenAICompatibleFinishReason)(choice.finish_reason),
raw: (_b = choice.finish_reason) != null ? _b : void 0
},
usage: (0, import_internal.convertOpenAICompatibleChatUsage)(responseBody.usage),
// providerMetadata,
request: { body },
response: {
...(0, import_internal.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 (0, import_provider_utils2.postJsonToApi)({
url: this.config.url({
path: "/chat/completions",
modelId: this.modelId
}),
headers: (0, import_provider_utils2.combineHeaders)(this.config.headers(), options.headers),
body,
failedResponseHandler: this.failedResponseHandler,
successfulResponseHandler: (0, import_provider_utils2.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 : (0, import_provider_utils2.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",
...(0, import_internal.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: (0, import_internal.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 = (0, import_provider_utils2.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 = (0, import_provider_utils2.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 import_provider3.InvalidResponseDataError({
data: toolCallDelta,
message: `Expected 'function' type.`
});
}
if (toolCallDelta.id == null) {
throw new import_provider3.InvalidResponseDataError({
data: toolCallDelta,
message: `Expected 'id' to be a string.`
});
}
if (((_e = toolCallDelta.function) == null ? void 0 : _e.name) == null) {
throw new import_provider3.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 ((0, import_provider_utils2.isParsableJson)(toolCall2.function.arguments)) {
controller.enqueue({
type: "tool-input-end",
id: toolCall2.id
});
controller.enqueue({
type: "tool-call",
toolCallId: (_i = toolCall2.id) != null ? _i : (0, import_provider_utils2.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 && (0, import_provider_utils2.isParsableJson)(toolCall.function.arguments)) {
controller.enqueue({
type: "tool-input-end",
id: toolCall.id
});
controller.enqueue({
type: "tool-call",
toolCallId: (_q = toolCall.id) != null ? _q : (0, import_provider_utils2.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 : (0, import_provider_utils2.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: (0, import_internal.convertOpenAICompatibleChatUsage)(usage),
providerMetadata
});
}
})
),
request: { body },
response: { headers: responseHeaders }
};
}
};
var openaiCompatibleTokenUsageSchema = import_v4.z.object({
prompt_tokens: import_v4.z.number().nullish(),
completion_tokens: import_v4.z.number().nullish(),
total_tokens: import_v4.z.number().nullish(),
prompt_tokens_details: import_v4.z.object({
cached_tokens: import_v4.z.number().nullish()
}).nullish(),
completion_tokens_details: import_v4.z.object({
reasoning_tokens: import_v4.z.number().nullish(),
accepted_prediction_tokens: import_v4.z.number().nullish(),
rejected_prediction_tokens: import_v4.z.number().nullish()
}).nullish()
}).nullish();
var OpenAICompatibleChatResponseSchema = import_v4.z.object({
id: import_v4.z.string().nullish(),
created: import_v4.z.number().nullish(),
model: import_v4.z.string().nullish(),
choices: import_v4.z.array(
import_v4.z.object({
message: import_v4.z.object({
role: import_v4.z.literal("assistant").nullish(),
content: import_v4.z.string().nullish(),
reasoning_content: import_v4.z.string().nullish(),
tool_calls: import_v4.z.array(
import_v4.z.object({
id: import_v4.z.string().nullish(),
type: import_v4.z.literal("function"),
function: import_v4.z.object({
name: import_v4.z.string(),
arguments: import_v4.z.string()
})
})
).nullish()
}),
finish_reason: import_v4.z.string().nullish()
})
),
usage: openaiCompatibleTokenUsageSchema
});
var createOpenAICompatibleChatChunkSchema = (errorSchema) => import_v4.z.union([
import_v4.z.object({
id: import_v4.z.string().nullish(),
created: import_v4.z.number().nullish(),
model: import_v4.z.string().nullish(),
choices: import_v4.z.array(
import_v4.z.object({
delta: import_v4.z.object({
role: import_v4.z.enum(["assistant"]).nullish(),
content: import_v4.z.string().nullish(),
reasoning_content: import_v4.z.string().nullish(),
tool_calls: import_v4.z.array(
import_v4.z.object({
index: import_v4.z.number(),
id: import_v4.z.string().nullish(),
type: import_v4.z.literal("function").nullish(),
function: import_v4.z.object({
name: import_v4.z.string().nullish(),
arguments: import_v4.z.string().nullish()
})
})
).nullish()
}).nullish(),
finish_reason: import_v4.z.string().nullish()
})
),
usage: openaiCompatibleTokenUsageSchema
}),
import_v4.z.object({
name: import_v4.z.string(),
status: import_v4.z.enum(["ENDED", "STARTED", "ERRORED", "UPDATING"]),
message: import_v4.z.null(),
parameters: import_v4.z.array(
import_v4.z.object({
name: import_v4.z.string(),
value: import_v4.z.string()
})
),
result: import_v4.z.string().nullable(),
error: import_v4.z.object({
type: import_v4.z.enum(["INVALID_PARAMETER", "UNKNOWN"]),
msg: import_v4.z.string()
}).nullable(),
timestamp: import_v4.z.number(),
usage: import_v4.z.null(),
tool_call_id: import_v4.z.string().nullable()
}),
errorSchema
]);
var friendliProviderOptionsSchema = import_v4.z.object({
/**
* Whether to enable parallel function calling during tool use. Default to true.
*/
parallelToolCalls: import_v4.z.boolean().nullish(),
/**
* BETA FEATURE: You can write a regular expression to force output that satisfies that regular expression.
*/
// regex: z.instanceof(RegExp).nullish(),
regex: import_v4.z.string().nullish(),
chat_template_kwargs: import_v4.z.record(import_v4.z.string(), import_v4.z.any()).nullish(),
/**
* A scaling factor used to determine the minimum token probability threshold.
*/
minP: import_v4.z.number().nullish(),
/**
* Penalizes tokens that have already appeared in the generated result.
*/
repetitionPenalty: import_v4.z.number().nullish(),
/**
* A probability threshold used to identify “top choice” tokens for exclusion in XTC sampling.
*/
xtcThreshold: import_v4.z.number().nullish(),
/**
* The probability that XTC (Exclude Top Choices) filtering will be applied for each sampling decision.
*/
xtcProbability: import_v4.z.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
var import_provider_utils3 = require("@ai-sdk/provider-utils");
var import_zod2 = require("zod");
var inputSchema = import_zod2.z.object({}).loose();
var outputSchema = import_zod2.z.unknown();
var webSearchTool = (0, import_provider_utils3.createProviderToolFactoryWithOutputSchema)({
id: "friendli.web:search",
inputSchema,
outputSchema
});
var webUrlTool = (0, import_provider_utils3.createProviderToolFactoryWithOutputSchema)({
id: "friendli.web:url",
inputSchema,
outputSchema
});
var mathCalendarTool = (0, import_provider_utils3.createProviderToolFactoryWithOutputSchema)({
id: "friendli.math:calendar",
inputSchema,
outputSchema
});
var mathStatisticsTool = (0, import_provider_utils3.createProviderToolFactoryWithOutputSchema)({
id: "friendli.math:statistics",
inputSchema,
outputSchema
});
var mathCalculatorTool = (0, import_provider_utils3.createProviderToolFactoryWithOutputSchema)({
id: "friendli.math:calculator",
inputSchema,
outputSchema
});
var codePythonInterpreterTool = (0, import_provider_utils3.createProviderToolFactoryWithOutputSchema)({
id: "friendli.code:python-interpreter",
inputSchema,
outputSchema
});
var linkupSearchTool = (0, import_provider_utils3.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
var import_provider_utils4 = require("@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 : (0, import_provider_utils4.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 ${(0, import_provider_utils5.loadApiKey)({
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 = (0, import_provider_utils5.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 import_openai_compatible.OpenAICompatibleCompletionLanguageModel(modelId, {
provider: `friendliai.${type}.completion`,
url: ({ path }) => `${baseURL}${path}`,
headers: getHeaders,
fetch: options.fetch,
errorStructure: friendliaiErrorStructure
});
};
const createTextEmbeddingModel = (modelId) => {
throw new import_provider4.NoSuchModelError({ modelId, modelType: "embeddingModel" });
};
const createImageModel = (modelId) => {
throw new import_provider4.NoSuchModelError({ modelId, modelType: "imageModel" });
};
const createTranscriptionModel = (modelId) => {
throw new import_provider4.NoSuchModelError({ modelId, modelType: "languageModel" });
};
const createSpeechModel = (modelId) => {
throw new import_provider4.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();
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
createFriendli,
friendli
});
//# sourceMappingURL=index.js.map