/** * OpenAI-compatible secure chat completion API * Provides a drop-in replacement for OpenAI's ChatCompletion API with end-to-end encryption */ import { SecureCompletionClient, generateUUID } from '../core/SecureCompletionClient'; import { ChatCompletionConfig } from '../types/client'; import { ChatCompletionRequest, ChatCompletionResponse } from '../types/api'; export class SecureChatCompletion { private client: SecureCompletionClient; private apiKey?: string; /** Stored config used to spin up a temporary per-request instance when base_url is overridden */ private readonly _config: ChatCompletionConfig; constructor(config: ChatCompletionConfig = {}) { const { baseUrl = 'https://api.nomyo.ai:12435', allowHttp = false, apiKey, secureMemory = true, timeout, debug, keyRotationInterval, keyRotationDir, keyRotationPassword, maxRetries, keyDir, } = config; this._config = config; this.apiKey = apiKey; this.client = new SecureCompletionClient({ routerUrl: baseUrl, allowHttp, secureMemory, ...(timeout !== undefined && { timeout }), ...(debug !== undefined && { debug }), ...(keyRotationInterval !== undefined && { keyRotationInterval }), ...(keyRotationDir !== undefined && { keyRotationDir }), ...(keyRotationPassword !== undefined && { keyRotationPassword }), ...(maxRetries !== undefined && { maxRetries }), ...(keyDir !== undefined && { keyDir }), }); } /** * Create a chat completion (matches OpenAI API). * * Supports additional NOMYO-specific fields: * - `security_tier`: "standard" | "high" | "maximum" — controls hardware routing * - `api_key`: per-request API key override (takes precedence over constructor key) * - `base_url`: per-request router URL override (creates a temporary client for * this single call, matching the Python SDK's `create(base_url=...)` behaviour) */ async create(request: ChatCompletionRequest): Promise { const payloadId = generateUUID(); // Extract NOMYO-specific fields that must not go into the encrypted payload const { security_tier, api_key, base_url, ...payload } = request as ChatCompletionRequest & { security_tier?: string; api_key?: string; base_url?: string; }; if (!payload.model) { throw new Error('Missing required field: model'); } if (!payload.messages || !Array.isArray(payload.messages)) { throw new Error('Missing or invalid required field: messages'); } const apiKey = api_key ?? this.apiKey; // Per-request base_url: spin up a temporary client for this one call, // inheriting all other config from the current instance. if (base_url !== undefined) { const tempInstance = new SecureChatCompletion({ ...this._config, baseUrl: base_url, apiKey: this.apiKey, }); try { const response = await tempInstance.client.sendSecureRequest( payload, payloadId, apiKey, security_tier ); return response as unknown as ChatCompletionResponse; } finally { tempInstance.dispose(); } } const response = await this.client.sendSecureRequest( payload, payloadId, apiKey, security_tier ); return response as unknown as ChatCompletionResponse; } /** * Async alias for create() (for compatibility with OpenAI SDK) */ async acreate(request: ChatCompletionRequest): Promise { return this.create(request); } /** * Release resources: stop key rotation timer and zero in-memory key material. */ dispose(): void { this.client.dispose(); } }