feat: add OpenAI types and AI caller utility
- Consolidate OpenAI types to types.ts - Create reusable callOpenAI() function - Add tool call parsing and result handling Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
127
src/utils/ai-caller.ts
Normal file
127
src/utils/ai-caller.ts
Normal file
@@ -0,0 +1,127 @@
|
||||
import { createLogger } from './logger';
|
||||
import { AI_CONFIG } from '../constants/agent-config';
|
||||
import type {
|
||||
Env,
|
||||
OpenAIAPIResponse,
|
||||
OpenAIToolCall,
|
||||
ToolDefinition,
|
||||
AICallerResult,
|
||||
ParsedToolCall
|
||||
} from '../types';
|
||||
|
||||
const logger = createLogger('ai-caller');
|
||||
|
||||
export interface AICallerConfig {
|
||||
model?: string;
|
||||
maxTokens: number;
|
||||
temperature: number;
|
||||
maxToolCalls?: number;
|
||||
responseFormat?: { type: 'json_object' } | { type: 'text' };
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
role: 'system' | 'user' | 'assistant' | 'tool';
|
||||
content: string;
|
||||
tool_call_id?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Call OpenAI API with optional function calling support
|
||||
*/
|
||||
export async function callOpenAI(
|
||||
messages: ChatMessage[],
|
||||
tools: ToolDefinition[] | undefined,
|
||||
config: AICallerConfig,
|
||||
env: Env
|
||||
): Promise<AICallerResult> {
|
||||
const model = config.model || AI_CONFIG.model;
|
||||
const maxToolCalls = config.maxToolCalls ?? AI_CONFIG.maxToolCalls;
|
||||
|
||||
try {
|
||||
const requestBody: Record<string, unknown> = {
|
||||
model,
|
||||
messages,
|
||||
max_tokens: config.maxTokens,
|
||||
temperature: config.temperature,
|
||||
};
|
||||
|
||||
if (tools && tools.length > 0) {
|
||||
requestBody.tools = tools;
|
||||
requestBody.tool_choice = 'auto';
|
||||
}
|
||||
|
||||
if (config.responseFormat) {
|
||||
requestBody.response_format = config.responseFormat;
|
||||
}
|
||||
|
||||
const response = await fetch(`${env.OPENAI_API_BASE}/chat/completions`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Authorization': `Bearer ${env.OPENAI_API_KEY}`,
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`OpenAI API error: ${response.status} - ${errorText}`);
|
||||
}
|
||||
|
||||
const data = await response.json() as OpenAIAPIResponse;
|
||||
const choice = data.choices[0];
|
||||
|
||||
if (!choice) {
|
||||
throw new Error('No response from OpenAI');
|
||||
}
|
||||
|
||||
const message = choice.message;
|
||||
const result: AICallerResult = {
|
||||
response: message.content || '',
|
||||
finishReason: choice.finish_reason,
|
||||
};
|
||||
|
||||
// Parse tool calls if present
|
||||
if (message.tool_calls && message.tool_calls.length > 0) {
|
||||
result.toolCalls = message.tool_calls
|
||||
.slice(0, maxToolCalls)
|
||||
.map(parseToolCall)
|
||||
.filter((tc): tc is ParsedToolCall => tc !== null);
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error('OpenAI API 호출 실패', error as Error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a single tool call from OpenAI response
|
||||
*/
|
||||
function parseToolCall(toolCall: OpenAIToolCall): ParsedToolCall | null {
|
||||
try {
|
||||
return {
|
||||
name: toolCall.function.name,
|
||||
arguments: JSON.parse(toolCall.function.arguments),
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('도구 호출 파싱 실패', error as Error, { toolCall });
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute tool calls and get results
|
||||
* Returns array of tool results for follow-up API call
|
||||
*/
|
||||
export function createToolResultMessages(
|
||||
toolCalls: OpenAIToolCall[],
|
||||
results: string[]
|
||||
): ChatMessage[] {
|
||||
return toolCalls.map((tc, index) => ({
|
||||
role: 'tool' as const,
|
||||
tool_call_id: tc.id,
|
||||
content: results[index] || 'Error executing tool',
|
||||
}));
|
||||
}
|
||||
15
src/utils/index.ts
Normal file
15
src/utils/index.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
export * from './logger';
|
||||
export * from './session-manager';
|
||||
export * from './ai-caller';
|
||||
export * from './formatters';
|
||||
export * from './patterns';
|
||||
export * from './retry';
|
||||
export * from './circuit-breaker';
|
||||
export * from './metrics';
|
||||
export * from './optimistic-lock';
|
||||
export * from './api-helper';
|
||||
export * from './api-urls';
|
||||
export * from './env-validation';
|
||||
export * from './error';
|
||||
export * from './email-decoder';
|
||||
export * from './reconciliation';
|
||||
Reference in New Issue
Block a user