nomyo-js/src/api/SecureChatCompletion.ts

120 lines
4.2 KiB
TypeScript
Raw Normal View History

2026-01-17 12:02:08 +01:00
/**
* 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';
2026-01-17 12:02:08 +01:00
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;
2026-01-17 12:02:08 +01:00
constructor(config: ChatCompletionConfig = {}) {
const {
baseUrl = 'https://api.nomyo.ai:12435',
2026-01-17 12:02:08 +01:00
allowHttp = false,
apiKey,
secureMemory = true,
timeout,
debug,
keyRotationInterval,
keyRotationDir,
keyRotationPassword,
maxRetries,
keyDir,
2026-01-17 12:02:08 +01:00
} = config;
this._config = config;
2026-01-17 12:02:08 +01:00
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 }),
2026-01-17 12:02:08 +01:00
});
}
/**
* 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)
2026-01-17 12:02:08 +01:00
*/
async create(request: ChatCompletionRequest): Promise<ChatCompletionResponse> {
const payloadId = generateUUID();
2026-01-17 12:02:08 +01:00
// 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;
};
2026-01-17 12:02:08 +01:00
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();
}
}
2026-01-17 12:02:08 +01:00
const response = await this.client.sendSecureRequest(
payload,
payloadId,
apiKey,
security_tier
2026-01-17 12:02:08 +01:00
);
return response as unknown as ChatCompletionResponse;
2026-01-17 12:02:08 +01:00
}
/**
* Async alias for create() (for compatibility with OpenAI SDK)
*/
async acreate(request: ChatCompletionRequest): Promise<ChatCompletionResponse> {
return this.create(request);
}
/**
* Release resources: stop key rotation timer and zero in-memory key material.
*/
dispose(): void {
this.client.dispose();
}
2026-01-17 12:02:08 +01:00
}