managarten/packages/shared-gpu/src/gpu-client.ts
Till JS c67ed0df14 feat(gpu-server): add API key auth, VRAM management, and Piper TTS voices
- Add API key authentication to all GPU services (X-API-Key header)
  - /health and /docs remain public (no key needed)
  - Shared key configured via GPU_API_KEY env variable
- Add VRAM auto-unload for mana-image-gen (5min) and mana-stt (10min)
  - FLUX.2 pipeline freed after idle, recovering ~13GB VRAM
  - WhisperX models freed after idle, recovering ~3GB VRAM
- Install Piper TTS voices (Thorsten + Kerstin) for local German TTS
- Update @manacore/shared-gpu client to support apiKey parameter
- Add GPU_API_KEY to .env.development
- Document API auth and VRAM management in setup guide

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 21:54:35 +01:00

58 lines
1.5 KiB
TypeScript

import type { GpuServiceConfig } from './types';
import { SttClient } from './stt-client';
import { TtsClient } from './tts-client';
import { ImageClient } from './image-client';
/**
* Unified client for all Mana GPU services.
*
* @example Public URLs (from anywhere):
* ```ts
* const gpu = new GpuClient({ baseUrl: 'https://gpu.mana.how' });
* ```
*
* @example LAN (direct):
* ```ts
* const gpu = new GpuClient({ baseUrl: 'http://192.168.178.11' });
* ```
*
* @example Custom URLs:
* ```ts
* const gpu = new GpuClient({
* baseUrl: '',
* urls: { stt: 'https://gpu-stt.mana.how', tts: 'https://gpu-tts.mana.how' },
* });
* ```
*/
export class GpuClient {
public readonly stt: SttClient;
public readonly tts: TtsClient;
public readonly image: ImageClient;
public readonly apiKey?: string;
constructor(config: GpuServiceConfig) {
this.apiKey = config.apiKey;
this.stt = new SttClient(config);
this.tts = new TtsClient(config);
this.image = new ImageClient(config);
}
/** Check health of all GPU services. */
async healthCheck(): Promise<{
stt: boolean;
tts: boolean;
image: boolean;
}> {
const [sttHealth, ttsHealth, imageHealth] = await Promise.allSettled([
this.stt.health(),
this.tts.health(),
this.image.health(),
]);
return {
stt: sttHealth.status === 'fulfilled' && sttHealth.value.status === 'healthy',
tts: ttsHealth.status === 'fulfilled' && ttsHealth.value.status === 'healthy',
image: imageHealth.status === 'fulfilled' && imageHealth.value.status === 'healthy',
};
}
}