in src/lib/server/endpoints/openai/endpointOai.ts [123:323]
export async function endpointOai(
input: z.input<typeof endpointOAIParametersSchema>
): Promise<Endpoint> {
const {
baseURL,
apiKey,
completion,
model,
defaultHeaders,
defaultQuery,
multimodal,
extraBody,
useCompletionTokens,
streamingSupported,
} = endpointOAIParametersSchema.parse(input);
let OpenAI;
try {
OpenAI = (await import("openai")).OpenAI;
} catch (e) {
throw new Error("Failed to import OpenAI", { cause: e });
}
const openai = new OpenAI({
apiKey: apiKey || "sk-",
baseURL,
defaultHeaders,
defaultQuery,
});
const imageProcessor = makeImageProcessor(multimodal.image);
if (completion === "completions") {
if (model.tools) {
throw new Error(
"Tools are not supported for 'completions' mode, switch to 'chat_completions' instead"
);
}
return async ({ messages, preprompt, continueMessage, generateSettings, conversationId }) => {
const prompt = await buildPrompt({
messages,
continueMessage,
preprompt,
model,
});
const parameters = { ...model.parameters, ...generateSettings };
const body: CompletionCreateParamsStreaming = {
model: model.id ?? model.name,
prompt,
stream: true,
max_tokens: parameters?.max_new_tokens,
stop: parameters?.stop,
temperature: parameters?.temperature,
top_p: parameters?.top_p,
frequency_penalty: parameters?.repetition_penalty,
presence_penalty: parameters?.presence_penalty,
};
const openAICompletion = await openai.completions.create(body, {
body: { ...body, ...extraBody },
headers: {
"ChatUI-Conversation-ID": conversationId?.toString() ?? "",
"X-use-cache": "false",
},
});
return openAICompletionToTextGenerationStream(openAICompletion);
};
} else if (completion === "chat_completions") {
return async ({
messages,
preprompt,
generateSettings,
tools,
toolResults,
conversationId,
}) => {
// Format messages for the chat API, handling multimodal content if supported
let messagesOpenAI: OpenAI.Chat.Completions.ChatCompletionMessageParam[] =
await prepareMessages(messages, imageProcessor, !model.tools && model.multimodal);
// Check if a system message already exists as the first message
const hasSystemMessage = messagesOpenAI.length > 0 && messagesOpenAI[0]?.role === "system";
if (hasSystemMessage) {
// System message exists - preserve user configuration
if (preprompt !== undefined) {
// Prepend preprompt to existing system message if preprompt exists
const userSystemPrompt = messagesOpenAI[0].content || "";
messagesOpenAI[0].content =
preprompt + (userSystemPrompt ? "\n\n" + userSystemPrompt : "");
}
// If no preprompt, user's system message remains unchanged
} else {
// No system message exists - create a new one with preprompt or empty string
messagesOpenAI = [{ role: "system", content: preprompt ?? "" }, ...messagesOpenAI];
}
// Handle models that don't support system role by converting to user message
// This maintains compatibility with older or non-standard models
if (
!model.systemRoleSupported &&
messagesOpenAI.length > 0 &&
messagesOpenAI[0]?.role === "system"
) {
messagesOpenAI[0] = {
...messagesOpenAI[0],
role: "user",
};
}
// Format tool results for the API to provide context for follow-up tool calls
// This creates the full conversation flow needed for multi-step tool interactions
if (toolResults && toolResults.length > 0) {
const toolCallRequests: OpenAI.Chat.Completions.ChatCompletionAssistantMessageParam = {
role: "assistant",
content: null,
tool_calls: [],
};
const responses: Array<OpenAI.Chat.Completions.ChatCompletionToolMessageParam> = [];
for (const result of toolResults) {
const id = result?.call?.toolId || uuidv4();
const toolCallResult: OpenAI.Chat.Completions.ChatCompletionMessageToolCall = {
type: "function",
function: {
name: result.call.name,
arguments: JSON.stringify(result.call.parameters),
},
id,
};
toolCallRequests.tool_calls?.push(toolCallResult);
const toolCallResponse: OpenAI.Chat.Completions.ChatCompletionToolMessageParam = {
role: "tool",
content: "",
tool_call_id: id,
};
if ("outputs" in result) {
toolCallResponse.content = JSON.stringify(result.outputs);
}
responses.push(toolCallResponse);
}
messagesOpenAI.push(toolCallRequests);
messagesOpenAI.push(...responses);
}
// Combine model defaults with request-specific parameters
const parameters = { ...model.parameters, ...generateSettings };
const toolCallChoices = createChatCompletionToolsArray(tools);
const body = {
model: model.id ?? model.name,
messages: messagesOpenAI,
stream: streamingSupported,
// Support two different ways of specifying token limits depending on the model
...(useCompletionTokens
? { max_completion_tokens: parameters?.max_new_tokens }
: { max_tokens: parameters?.max_new_tokens }),
stop: parameters?.stop,
temperature: parameters?.temperature,
top_p: parameters?.top_p,
frequency_penalty: parameters?.repetition_penalty,
presence_penalty: parameters?.presence_penalty,
// Only include tool configuration if tools are provided
...(toolCallChoices.length > 0 ? { tools: toolCallChoices, tool_choice: "auto" } : {}),
};
// Handle both streaming and non-streaming responses with appropriate processors
if (streamingSupported) {
const openChatAICompletion = await openai.chat.completions.create(
body as ChatCompletionCreateParamsStreaming,
{
body: { ...body, ...extraBody },
headers: {
"ChatUI-Conversation-ID": conversationId?.toString() ?? "",
"X-use-cache": "false",
},
}
);
return openAIChatToTextGenerationStream(openChatAICompletion);
} else {
const openChatAICompletion = await openai.chat.completions.create(
body as ChatCompletionCreateParamsNonStreaming,
{
body: { ...body, ...extraBody },
headers: {
"ChatUI-Conversation-ID": conversationId?.toString() ?? "",
"X-use-cache": "false",
},
}
);
return openAIChatToTextGenerationSingle(openChatAICompletion);
}
};
} else {
throw new Error("Invalid completion type");
}
}