UNPKG

@openai/agents-openai

Version:

The OpenAI Agents SDK is a lightweight yet powerful framework for building multi-agent workflows.

300 lines 11.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OpenAIChatCompletionsModel = exports.FAKE_ID = void 0; const agents_core_1 = require("@openai/agents-core"); const logger_1 = __importDefault(require("./logger.js")); const defaults_1 = require("./defaults.js"); const openaiChatCompletionsStreaming_1 = require("./openaiChatCompletionsStreaming.js"); const openaiChatCompletionsConverter_1 = require("./openaiChatCompletionsConverter.js"); exports.FAKE_ID = 'FAKE_ID'; function hasReasoningContent(message) { return ('reasoning' in message && typeof message.reasoning === 'string' && message.reasoning !== ''); } /** * A model that uses (or is compatible with) OpenAI's Chat Completions API. */ class OpenAIChatCompletionsModel { #client; #model; constructor(client, model) { this.#client = client; this.#model = model; } async getResponse(request) { const response = await (0, agents_core_1.withGenerationSpan)(async (span) => { span.spanData.model = this.#model; span.spanData.model_config = request.modelSettings ? { temperature: request.modelSettings.temperature, top_p: request.modelSettings.topP, frequency_penalty: request.modelSettings.frequencyPenalty, presence_penalty: request.modelSettings.presencePenalty, reasoning_effort: request.modelSettings.reasoning?.effort, verbosity: request.modelSettings.text?.verbosity, } : { base_url: this.#client.baseURL }; const response = await this.#fetchResponse(request, span, false); if (span && request.tracing === true) { span.spanData.output = [response]; } return response; }); const output = []; if (response.choices && response.choices[0]) { const message = response.choices[0].message; if (hasReasoningContent(message)) { output.push({ type: 'reasoning', content: [], rawContent: [ { type: 'reasoning_text', text: message.reasoning, }, ], }); } if (message.content !== undefined && message.content !== null && // Azure OpenAI returns empty string instead of null for tool calls, causing parser rejection !(message.tool_calls && message.content === '')) { const { content, ...rest } = message; output.push({ id: response.id, type: 'message', role: 'assistant', content: [ { type: 'output_text', text: content || '', providerData: rest, }, ], status: 'completed', }); } else if (message.refusal) { const { refusal, ...rest } = message; output.push({ id: response.id, type: 'message', role: 'assistant', content: [ { type: 'refusal', refusal: refusal || '', providerData: rest, }, ], status: 'completed', }); } else if (message.audio) { const { data, ...remainingAudioData } = message.audio; output.push({ id: response.id, type: 'message', role: 'assistant', content: [ { type: 'audio', audio: data, providerData: remainingAudioData, }, ], status: 'completed', }); } else if (message.tool_calls) { for (const tool_call of message.tool_calls) { if (tool_call.type === 'function') { // Note: custom tools are not supported for now const { id: callId, ...remainingToolCallData } = tool_call; const { arguments: args, name, ...remainingFunctionData } = tool_call.function; output.push({ id: response.id, type: 'function_call', arguments: args, name: name, callId: callId, status: 'completed', providerData: { ...remainingToolCallData, ...remainingFunctionData, }, }); } } } } const modelResponse = { usage: response.usage ? new agents_core_1.Usage(toResponseUsage(response.usage)) : new agents_core_1.Usage(), output, responseId: response.id, providerData: response, }; return modelResponse; } async *getStreamedResponse(request) { const span = request.tracing ? (0, agents_core_1.createGenerationSpan)() : undefined; try { if (span) { span.start(); (0, agents_core_1.setCurrentSpan)(span); } const stream = await this.#fetchResponse(request, span, true); const response = { id: exports.FAKE_ID, created: Math.floor(Date.now() / 1000), model: this.#model, object: 'chat.completion', choices: [], usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, }, }; for await (const event of (0, openaiChatCompletionsStreaming_1.convertChatCompletionsStreamToResponses)(response, stream)) { yield event; } if (span && response && request.tracing === true) { span.spanData.output = [response]; } } catch (error) { if (span) { span.setError({ message: 'Error streaming response', data: { error: request.tracing === true ? String(error) : error instanceof Error ? error.name : undefined, }, }); } throw error; } finally { if (span) { span.end(); (0, agents_core_1.resetCurrentSpan)(); } } } async #fetchResponse(request, span, stream) { const tools = []; if (request.tools) { for (const tool of request.tools) { tools.push((0, openaiChatCompletionsConverter_1.toolToOpenAI)(tool)); } } if (request.handoffs) { for (const handoff of request.handoffs) { tools.push((0, openaiChatCompletionsConverter_1.convertHandoffTool)(handoff)); } } const responseFormat = getResponseFormat(request.outputType); let parallelToolCalls = undefined; if (typeof request.modelSettings.parallelToolCalls === 'boolean') { if (request.modelSettings.parallelToolCalls && tools.length === 0) { throw new Error('Parallel tool calls are not supported without tools'); } parallelToolCalls = request.modelSettings.parallelToolCalls; } const messages = (0, openaiChatCompletionsConverter_1.itemsToMessages)(request.input); if (request.systemInstructions) { messages.unshift({ content: request.systemInstructions, role: 'system', }); } if (span && request.tracing === true) { span.spanData.input = messages; } const providerData = request.modelSettings.providerData ?? {}; if (request.modelSettings.reasoning && request.modelSettings.reasoning.effort) { // merge the top-level reasoning.effort into provider data providerData.reasoning_effort = request.modelSettings.reasoning.effort; } if (request.modelSettings.text && request.modelSettings.text.verbosity) { // merge the top-level text.verbosity into provider data providerData.verbosity = request.modelSettings.text.verbosity; } const requestData = { model: this.#model, messages, tools: tools.length ? tools : undefined, temperature: request.modelSettings.temperature, top_p: request.modelSettings.topP, frequency_penalty: request.modelSettings.frequencyPenalty, presence_penalty: request.modelSettings.presencePenalty, max_tokens: request.modelSettings.maxTokens, tool_choice: (0, openaiChatCompletionsConverter_1.convertToolChoice)(request.modelSettings.toolChoice), response_format: responseFormat, parallel_tool_calls: parallelToolCalls, stream, store: request.modelSettings.store, ...providerData, }; if (logger_1.default.dontLogModelData) { logger_1.default.debug('Calling LLM'); } else { logger_1.default.debug(`Calling LLM. Request data: ${JSON.stringify(requestData, null, 2)}`); } const completion = await this.#client.chat.completions.create(requestData, { headers: defaults_1.HEADERS, signal: request.signal, }); if (logger_1.default.dontLogModelData) { logger_1.default.debug('Response received'); } else { logger_1.default.debug(`Response received: ${JSON.stringify(completion, null, 2)}`); } return completion; } } exports.OpenAIChatCompletionsModel = OpenAIChatCompletionsModel; function getResponseFormat(outputType) { if (outputType === 'text') { return { type: 'text' }; } if (outputType.type === 'json_schema') { return { type: 'json_schema', json_schema: { name: outputType.name, strict: outputType.strict, schema: outputType.schema, }, }; } return { type: 'json_object' }; } function toResponseUsage(usage) { return { requests: 1, input_tokens: usage.prompt_tokens, output_tokens: usage.completion_tokens, total_tokens: usage.total_tokens, input_tokens_details: { cached_tokens: usage.prompt_tokens_details?.cached_tokens || 0, }, output_tokens_details: { reasoning_tokens: usage.completion_tokens_details?.reasoning_tokens || 0, }, }; } //# sourceMappingURL=openaiChatCompletionsModel.js.map