- added retry logic with exponential backoff
- per request base_url setting
- configurable key_dir
- protocol downgrade protection
- public secure memory API
This commit is contained in:
Alpha Nerd 2026-04-16 15:36:20 +02:00
parent 3b1792e613
commit 76b2a284d5
Signed by: alpha-nerd
SSH key fingerprint: SHA256:QkkAgVoYi9TQ0UKPkiKSfnerZy2h4qhi3SVPXJmBN+M
5 changed files with 220 additions and 37 deletions

View file

@ -10,6 +10,8 @@ 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 {
@ -22,8 +24,11 @@ export class SecureChatCompletion {
keyRotationInterval,
keyRotationDir,
keyRotationPassword,
maxRetries,
keyDir,
} = config;
this._config = config;
this.apiKey = apiKey;
this.client = new SecureCompletionClient({
routerUrl: baseUrl,
@ -34,6 +39,8 @@ export class SecureChatCompletion {
...(keyRotationInterval !== undefined && { keyRotationInterval }),
...(keyRotationDir !== undefined && { keyRotationDir }),
...(keyRotationPassword !== undefined && { keyRotationPassword }),
...(maxRetries !== undefined && { maxRetries }),
...(keyDir !== undefined && { keyDir }),
});
}
@ -43,18 +50,19 @@ export class SecureChatCompletion {
* 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<ChatCompletionResponse> {
const payloadId = generateUUID();
// Extract NOMYO-specific fields that must not go into the encrypted payload
const { security_tier, api_key, ...payload } = request as ChatCompletionRequest & {
const { security_tier, api_key, base_url, ...payload } = request as ChatCompletionRequest & {
security_tier?: string;
api_key?: string;
base_url?: string;
};
const apiKey = api_key ?? this.apiKey;
if (!payload.model) {
throw new Error('Missing required field: model');
}
@ -62,6 +70,29 @@ export class SecureChatCompletion {
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,