contextual-agent-sdk
Version:
SDK for building AI agents with seamless voice-text context switching
281 lines • 12.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.OpenAIProvider = void 0;
const BaseLLMProvider_1 = require("./BaseLLMProvider");
const ToolConverter_1 = require("../tools/ToolConverter");
const openai_1 = __importDefault(require("openai"));
class OpenAIProvider extends BaseLLMProvider_1.BaseLLMProvider {
type = 'openai';
name = 'OpenAI';
config;
client;
constructor(config) {
super();
this.config = config;
if (config.apiKey) {
this.client = new openai_1.default({
apiKey: config.apiKey,
organization: config.organization,
baseURL: config.baseURL
});
console.log('🎯 OpenAI Provider initialized with official SDK');
}
}
supportsTools() {
return true;
}
supportsStreaming() {
return true;
}
async generateResponse(options) {
if (!this.client) {
throw new Error('OpenAI provider not configured. Please provide an API key.');
}
try {
const completion = await this.client.chat.completions.create({
model: options.model || this.config.model || 'gpt-4',
messages: options.messages
.filter(msg => msg.role !== 'tool')
.map(msg => ({
role: msg.role,
content: msg.content
})),
temperature: options.temperature ?? this.config.defaultOptions?.temperature ?? 0.7,
max_tokens: options.maxTokens ?? this.config.defaultOptions?.maxTokens ?? 500,
presence_penalty: options.presencePenalty ?? this.config.defaultOptions?.presencePenalty,
frequency_penalty: options.frequencyPenalty ?? this.config.defaultOptions?.frequencyPenalty,
stop: options.stop ?? this.config.defaultOptions?.stop
});
const choice = completion.choices[0];
if (!choice?.message?.content) {
throw new Error('No response content from OpenAI');
}
return {
content: choice.message.content,
finishReason: choice.finish_reason,
usage: completion.usage ? {
promptTokens: completion.usage.prompt_tokens,
completionTokens: completion.usage.completion_tokens,
totalTokens: completion.usage.total_tokens
} : undefined
};
}
catch (error) {
throw new Error(`OpenAI API error: ${error.message}`);
}
}
async generateWithTools(options) {
if (!this.client) {
throw new Error('OpenAI provider not configured. Please provide an API key.');
}
if (!options.tools || options.tools.length === 0) {
const response = await this.generateResponse(options);
return {
...response,
toolCalls: [],
stopReason: 'stop'
};
}
try {
console.log('🔧 OpenAI Function Calling Request:', {
model: this.config.model || 'gpt-4',
messages: options.messages.length,
functions: options.tools.map(t => ({ name: t.function.name, description: t.function.description }))
});
const completion = await this.client.chat.completions.create({
model: options.model || this.config.model || 'gpt-4',
messages: options.messages
.filter(msg => msg.role !== 'tool')
.map(msg => ({
role: msg.role,
content: msg.content
})),
functions: options.tools.map(tool => ({
name: tool.function.name,
description: tool.function.description,
parameters: tool.function.parameters
})),
function_call: options.toolChoice === 'none' ? 'none' :
options.toolChoice && typeof options.toolChoice === 'object' ?
{ name: options.toolChoice.function.name } : 'auto',
temperature: options.temperature ?? this.config.defaultOptions?.temperature ?? 0.7,
max_tokens: options.maxTokens ?? this.config.defaultOptions?.maxTokens ?? 1000
});
console.log('🔧 OpenAI Function Calling Response:', {
content_length: completion.choices[0]?.message?.content?.length || 0,
finish_reason: completion.choices[0]?.finish_reason,
function_call: !!completion.choices[0]?.message?.function_call,
usage: completion.usage
});
const choice = completion.choices[0];
if (!choice) {
throw new Error('No response from OpenAI');
}
const content = choice.message.content || '';
const toolCalls = choice.message.function_call ? [{
id: `call_${Date.now()}`,
type: 'function',
function: {
name: choice.message.function_call.name,
arguments: choice.message.function_call.arguments || '{}'
}
}] : [];
const stopReason = choice.finish_reason === 'function_call' ? 'tool_calls' :
choice.finish_reason === 'length' ? 'length' :
'stop';
console.log('🎯 Extracted Function Calls:', toolCalls.map(tc => ({
id: tc.id,
name: tc.function.name,
args: tc.function.arguments
})));
return {
content,
usage: completion.usage ? {
promptTokens: completion.usage.prompt_tokens,
completionTokens: completion.usage.completion_tokens,
totalTokens: completion.usage.total_tokens
} : undefined,
finishReason: choice.finish_reason,
toolCalls,
stopReason,
conversation: options.conversation
};
}
catch (error) {
console.error('❌ OpenAI Function Calling Error:', error);
throw new Error(`Failed to generate response with functions: ${error.message}`);
}
}
async handleToolLoop(conversation, tools) {
const maxIterations = 10;
let iteration = 0;
console.log(`🔄 Starting OpenAI Function Loop with ${tools.length} tools`);
while (iteration < maxIterations) {
iteration++;
console.log(`🔄 Function Loop Iteration ${iteration}/${maxIterations}`);
const messages = this.conversationManager.getConversationHistory(conversation.id);
const response = await this.generateWithTools({
messages,
tools: ToolConverter_1.ToolConverter.toOpenAIFunctions(tools),
conversation,
maxTokens: this.config.defaultOptions?.maxTokens || 1000
});
if (!response.toolCalls || response.toolCalls.length === 0) {
console.log('✅ Function loop complete - no more function calls');
return response;
}
console.log(`🔧 Executing ${response.toolCalls.length} function call(s):`, response.toolCalls.map(tc => tc.function.name));
const toolResults = await ToolConverter_1.ToolConverter.executeToolCalls(response.toolCalls, tools, ToolConverter_1.ToolConverter.createToolContext(conversation.metadata?.agentId || 'unknown', conversation.id, conversation.metadata?.userId));
this.conversationManager.addMessage(conversation.id, {
role: 'assistant',
content: response.content || '',
metadata: { toolCalls: response.toolCalls }
});
for (let i = 0; i < response.toolCalls.length; i++) {
const toolCall = response.toolCalls[i];
const result = toolResults[i];
const resultContent = result.success
? (typeof result.data === 'string' ? result.data : JSON.stringify(result.data))
: `Error: ${result.error}`;
this.conversationManager.addMessage(conversation.id, {
role: 'user',
content: `Function ${toolCall.function.name} result: ${resultContent}`,
metadata: {
isFunctionResult: true,
functionCallId: toolCall.id,
functionResult: result
}
});
console.log(`🎯 Function ${toolCall.function.name} result:`, {
success: result.success,
hasData: !!result.data,
error: result.error
});
}
const criticalFailure = toolResults.find(result => !result.success && result.metadata?.critical);
if (criticalFailure) {
console.error('❌ Critical function failure:', criticalFailure);
return {
...response,
content: `Function execution failed: ${criticalFailure.error}`,
stopReason: 'stop'
};
}
}
throw new Error(`Function execution loop exceeded maximum iterations (${maxIterations})`);
}
async *streamResponse(options) {
if (!this.client) {
throw new Error('OpenAI provider not configured. Please provide an API key.');
}
try {
const stream = await this.client.chat.completions.create({
model: options.model || this.config.model || 'gpt-4',
messages: options.messages
.filter(msg => msg.role !== 'tool')
.map(msg => ({
role: msg.role,
content: msg.content
})),
temperature: options.temperature ?? this.config.defaultOptions?.temperature ?? 0.7,
max_tokens: options.maxTokens ?? this.config.defaultOptions?.maxTokens ?? 1000,
stream: true
});
for await (const chunk of stream) {
const choice = chunk.choices[0];
if (choice?.delta?.content) {
yield {
type: 'content',
content: choice.delta.content
};
}
}
yield {
type: 'done',
done: true
};
}
catch (error) {
yield {
type: 'error',
error: error.message
};
}
}
getMaxTokens() {
const model = this.config.model || 'gpt-4';
if (model.includes('gpt-4o'))
return 128000;
if (model.includes('gpt-4-turbo'))
return 128000;
if (model.includes('gpt-4'))
return 8192;
if (model.includes('gpt-3.5-turbo-16k'))
return 16384;
if (model.includes('gpt-3.5-turbo'))
return 4096;
return 4096;
}
async isAvailable() {
return !!this.client && !!this.config.apiKey;
}
isConfigured() {
return !!this.client;
}
getSupportedModels() {
return [
'gpt-4o',
'gpt-4o-mini',
'gpt-4-turbo',
'gpt-4-turbo-preview',
'gpt-4',
'gpt-3.5-turbo',
'gpt-3.5-turbo-16k'
];
}
}
exports.OpenAIProvider = OpenAIProvider;
//# sourceMappingURL=OpenAIProvider.js.map