UNPKG

@openai/agents-openai

Version:

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

213 lines 8.19 kB
import OpenAI from 'openai'; import { getDefaultOpenAIClient, getDefaultOpenAIKey } from "../defaults.mjs"; import { convertToOutputItem, getInputItems } from "../openaiResponsesModel.mjs"; import { OPENAI_SESSION_API, } from "./openaiSessionApi.mjs"; export async function startOpenAIConversationsSession(client) { const resolvedClient = client ?? resolveClient({}); const response = await resolvedClient.conversations.create({ items: [] }); return response.id; } export class OpenAIConversationsSession { // Marks this session as backed by the Conversations API so Responses-only integrations can reject it. [OPENAI_SESSION_API] = 'conversations'; #client; #conversationId; constructor(options = {}) { this.#client = resolveClient(options); this.#conversationId = options.conversationId; } get sessionId() { return this.#conversationId; } async getSessionId() { if (!this.#conversationId) { this.#conversationId = await startOpenAIConversationsSession(this.#client); } return this.#conversationId; } async getItems(limit) { const conversationId = await this.getSessionId(); // Convert each API item into the Agent SDK's input shape. Some API payloads expand into multiple items. const toAgentItems = (item) => { if (item.type === 'message' && item.role === 'user') { const message = item; return [ { id: item.id, type: 'message', role: 'user', content: (message.content ?? []) .map((c) => { if (c.type === 'input_text') { return { type: 'input_text', text: c.text }; } else if (c.type === 'input_image') { if (c.image_url) { return { type: 'input_image', image: c.image_url }; } else if (c.file_id) { return { type: 'input_image', image: { id: c.file_id } }; } } else if (c.type === 'input_file') { if (c.file_data) { const fileItem = { type: 'input_file', file: c.file_data, }; if (c.filename) { fileItem.filename = c.filename; } return fileItem; } if (c.file_url) { const fileItem = { type: 'input_file', file: c.file_url, }; if (c.filename) { fileItem.filename = c.filename; } return fileItem; } else if (c.file_id) { const fileItem = { type: 'input_file', file: { id: c.file_id }, }; if (c.filename) { fileItem.filename = c.filename; } return fileItem; } } // Add more content types here when they're added return null; }) .filter((c) => c !== null), }, ]; } const outputItems = item .output; if (isResponseOutputItemArray(outputItems)) { return convertToOutputItem(outputItems); } return convertToOutputItem([item]); }; if (limit === undefined) { const items = []; const iterator = this.#client.conversations.items.list(conversationId, { order: 'asc', }); for await (const item of iterator) { items.push(...toAgentItems(item)); } return items; } if (limit <= 0) { return []; } const itemGroups = []; let total = 0; const iterator = this.#client.conversations.items.list(conversationId, { limit, order: 'desc', }); for await (const item of iterator) { const group = toAgentItems(item); if (!group.length) { continue; } itemGroups.push(group); total += group.length; if (total >= limit) { break; } } // Iterate in reverse because the API returned items in descending order. const orderedItems = []; for (let index = itemGroups.length - 1; index >= 0; index -= 1) { orderedItems.push(...itemGroups[index]); } if (orderedItems.length > limit) { orderedItems.splice(0, orderedItems.length - limit); } return orderedItems; } async addItems(items) { if (!items.length) { return; } const conversationId = await this.getSessionId(); await this.#client.conversations.items.create(conversationId, { items: getInputItems(items), }); } async popItem() { const conversationId = await this.getSessionId(); const [latest] = await this.getItems(1); if (!latest) { return undefined; } const itemId = latest.id; if (itemId) { await this.#client.conversations.items.delete(itemId, { conversation_id: conversationId, }); } return latest; } async clearSession() { if (!this.#conversationId) { return; } await this.#client.conversations.delete(this.#conversationId); this.#conversationId = undefined; } } // -------------------------------------------------------------- // Internals // -------------------------------------------------------------- const INPUT_CONTENT_TYPES = new Set([ 'input_text', 'input_image', 'input_file', 'input_audio', ]); function isObject(value) { return typeof value === 'object' && value !== null; } // Treats a value as ResponseOutputItem[] only when each entry resembles an output item rather than raw input content. function isResponseOutputItemArray(value) { if (!Array.isArray(value) || value.length === 0) { return false; } return value.every((entry) => { if (!isObject(entry)) { return false; } const type = entry.type; if (typeof type !== 'string') { return false; } if (INPUT_CONTENT_TYPES.has(type)) { return false; } // Fallback: pre-emptively exclude future input_* variants so they never masquerade as response outputs. return !type.startsWith('input_'); }); } function resolveClient(options) { if (options.client) { return options.client; } return (getDefaultOpenAIClient() ?? new OpenAI({ apiKey: options.apiKey ?? getDefaultOpenAIKey(), baseURL: options.baseURL, organization: options.organization, project: options.project, })); } //# sourceMappingURL=openaiConversationsSession.mjs.map