@nomyx/assistant
Version:
A powerful assistant library and cli for your AI projects. works with Vertex AI (Claude and Gemini)
132 lines (128 loc) • 4.47 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.chat = chat;
const axios_1 = __importDefault(require("axios"));
const tools_1 = require("./tools");
const modelConfig_1 = require("./modelConfig");
async function chat(config, messages, options, tools) {
const url = config.getApiUrl('chat/completions');
const model = config.model;
const logger = config.getLogger();
const requestBody = {
messages: (0, tools_1.convertMessages)(messages).map(msg => ({
...msg,
content: convertContentToOpenRouterFormat(msg.content)
})),
model: model,
max_tokens: options.maxTokens || (0, modelConfig_1.getMaxTokens)(model),
temperature: options.temperature,
stream: false
};
const modelSupportsTools = (0, modelConfig_1.supportsTools)(model);
if (tools && tools.length > 0) {
if (modelSupportsTools) {
requestBody.tools = tools.map(tool => (0, tools_1.convertToolSchema)(tool.getSchema()));
requestBody.tool_choice = 'auto';
}
else {
// Append instructions for using tools
const toolInstructions = `
You have access to the following tools:
${tools.map(tool => `- ${tool.name}: ${tool.description}`).join('\n')}
To use a tool, output a function call in a code block like this:
\`\`\`function_call
{
"name": "tool_name",
"arguments": {
"arg1": "value1",
"arg2": "value2"
}
}
\`\`\`
Replace "tool_name" with the actual tool name and provide the necessary arguments.
For example, to use the "tool_name" tool with arguments "arg1" and "arg2":
\`\`\`function_call
{
"name": "tool_name",
"arguments": {
"arg1": "value1",
"arg2": "value2"
}
}
\`\`\
`;
requestBody.messages[requestBody.messages.length - 1].content += '\n\n' + toolInstructions;
}
}
const headers = {
...config.getHeaders(),
'HTTP-Referer': config.httpReferer,
'X-Title': config.xTitle
};
logger.debug('OpenRouter API request', { url, model, toolsCount: tools?.length });
try {
const response = await axios_1.default.post(url, requestBody, { headers });
logger.debug('OpenRouter API response received', { status: response.status });
return createProviderResponse(response.data, modelSupportsTools, logger);
}
catch (error) {
if (axios_1.default.isAxiosError(error)) {
logger.error('OpenRouter API error', {
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data
});
}
else {
logger.error('OpenRouter API error', { error });
}
throw error;
}
}
function createProviderResponse(data, modelSupportsTools, logger) {
const choice = data.choices[0];
if (!choice) {
throw new Error('Unexpected response format from OpenRouter API');
}
const message = choice.message || { role: 'assistant', content: '' };
let content = message.content || '';
let toolCalls = modelSupportsTools ? (0, tools_1.extractToolCalls)(message) : [];
if (!modelSupportsTools) {
const functionCallRegex = /```function_call\n([\s\S]*?)```/g;
let match;
while ((match = functionCallRegex.exec(content)) !== null) {
try {
const functionCall = JSON.parse(match[1]);
toolCalls.push({
name: functionCall.name,
arguments: functionCall.arguments
});
}
catch (error) {
logger.error('Error parsing function call', { error });
}
}
content = content.replace(functionCallRegex, '');
}
return {
content: content.trim(),
toolCalls: toolCalls,
usage: {
promptTokens: data.usage?.prompt_tokens || 0,
completionTokens: data.usage?.completion_tokens || 0,
totalTokens: data.usage?.total_tokens || 0
},
};
}
function convertContentToOpenRouterFormat(content) {
if (typeof content === 'string') {
return content;
}
return content.map(part => ({
type: part.type,
content: part[part.type]
}));
}