136 lines
3.0 KiB
TypeScript
136 lines
3.0 KiB
TypeScript
import { SSESession } from '../utils/sse-session.js';
|
|
import { EventEmitter } from '../utils/event-emitter.js';
|
|
import { GPTResponseIterator, type ToolCall, type ToolDefinition } from './gpt-response.js';
|
|
|
|
|
|
export type GPTEventMap = {
|
|
/**
|
|
* Emitted when a message is sent
|
|
*/
|
|
messageSent: { mesasge: string };
|
|
|
|
/**
|
|
* Emitted when a message chunk is received
|
|
*/
|
|
messageChunkReceived: { chunk: string };
|
|
|
|
/**
|
|
* Emitted when a response is received
|
|
*/
|
|
responseReceived: { response: string };
|
|
|
|
/**
|
|
* Emitted when a tool is called
|
|
*/
|
|
toolCalled: { toolName: string; arguments: Record<string, unknown>; result: unknown };
|
|
}
|
|
|
|
export type GPTConfig = {
|
|
/**
|
|
* The API key to use for the GPT API
|
|
*/
|
|
apiKey: string;
|
|
|
|
/**
|
|
* The API URL to use for the GPT API
|
|
*/
|
|
apiUrl: string;
|
|
|
|
/**
|
|
* The model to use for the GPT API
|
|
*/
|
|
model: string;
|
|
}
|
|
|
|
/**
|
|
* Message types that can be sent to the GPT API
|
|
*/
|
|
export type Message =
|
|
| {
|
|
role: 'user' | 'system';
|
|
content: string;
|
|
}
|
|
| {
|
|
role: 'assistant';
|
|
content: string | null;
|
|
tool_calls?: ToolCall[];
|
|
}
|
|
| {
|
|
role: 'tool';
|
|
tool_call_id: string;
|
|
name: string;
|
|
content: string;
|
|
};
|
|
|
|
/**
|
|
* Request configuration for GPT API calls
|
|
*/
|
|
export type GPTRequest = {
|
|
/**
|
|
* The messages to send to the GPT API
|
|
*/
|
|
messages: Message[];
|
|
|
|
/**
|
|
* Optional tool definitions for function calling
|
|
*/
|
|
tools?: ToolDefinition[];
|
|
|
|
/**
|
|
* Controls which (if any) tool is called by the model
|
|
* - 'auto' (default): model decides whether to call a tool
|
|
* - 'none': model will not call any tools
|
|
* - { type: 'function', function: { name: 'tool_name' } }: forces a specific tool call
|
|
*/
|
|
tool_choice?: 'auto' | 'none' | { type: 'function'; function: { name: string } };
|
|
}
|
|
|
|
export class GPT extends EventEmitter<GPTEventMap> {
|
|
constructor(public config: GPTConfig) {
|
|
super();
|
|
}
|
|
|
|
/**
|
|
* Sends a message to the GPT API
|
|
* @param message - The message to send
|
|
* @returns The response from the GPT API
|
|
*/
|
|
send(request: GPTRequest): GPTResponseIterator {
|
|
const config = this.config;
|
|
|
|
const lazyIterator = (async function* () {
|
|
// Build the API request body
|
|
const requestBody: Record<string, unknown> = {
|
|
model: config.model,
|
|
messages: request.messages,
|
|
stream: true,
|
|
};
|
|
|
|
// Add tools if provided
|
|
if (request.tools && request.tools.length > 0) {
|
|
requestBody.tools = request.tools;
|
|
}
|
|
|
|
// Add tool_choice if provided
|
|
if (request.tool_choice) {
|
|
requestBody.tool_choice = request.tool_choice;
|
|
}
|
|
|
|
const session = await SSESession.from(config.apiUrl, {
|
|
headers: {
|
|
Authorization: `Bearer ${config.apiKey}`,
|
|
},
|
|
method: 'POST',
|
|
body: JSON.stringify(requestBody),
|
|
});
|
|
|
|
if (!session.messages) {
|
|
throw new Error('Failed to create SSE session');
|
|
}
|
|
|
|
yield* session.messages;
|
|
})();
|
|
|
|
return new GPTResponseIterator(lazyIterator);
|
|
}
|
|
} |