feat: add unified @manacore/shared-llm package and migrate all backends

Create a shared LLM client package that provides a unified interface
to the mana-llm service, replacing 9 individual fetch-based integrations
with consistent error handling, retry logic, and JSON extraction.

Package (@manacore/shared-llm):
- LlmModule with forRoot/forRootAsync (NestJS dynamic module)
- LlmClientService: chat, json, vision, visionJson, embed, stream
- LlmClient standalone class for non-NestJS consumers
- extractJson utility (consolidates 3 markdown-stripping implementations)
- retryFetch with exponential backoff (429, 5xx, network errors)
- 44 unit tests (json-extractor, retry, llm-client)

Migrated backends:
- mana-core-auth: raw fetch → llm.json()
- planta: raw fetch + vision → llm.visionJson()
- nutriphi: raw fetch + regex → llm.visionJson() + llm.json()
- chat: custom OllamaService (175 LOC) → llm.chatMessages()
- context: raw fetch → llm.chat() (keeps token tracking)
- traces: 2x raw fetch → llm.chat()
- manadeck: @google/genai SDK → llm.json() + llm.visionJson()
- bot-services: raw Ollama API → LlmClient standalone
- matrix-ollama-bot: raw fetch → llm.chatMessages() + llm.vision()

New credit operations:
- AI_PLANT_ANALYSIS (2 credits, planta)
- AI_GUIDE_GENERATION (5 credits, traces)
- AI_CONTEXT_GENERATION (2 credits, context)
- AI_BOT_CHAT (0.1 credits, matrix)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Till JS 2026-03-23 22:06:30 +01:00
parent e7bf58c5b6
commit e2f144962c
48 changed files with 2476 additions and 1297 deletions

View file

@ -0,0 +1,8 @@
export type {
LlmModuleOptions,
LlmModuleAsyncOptions,
LlmOptionsFactory,
ResolvedLlmOptions,
} from './llm-options.interface';
export { resolveOptions } from './llm-options.interface';

View file

@ -0,0 +1,47 @@
import type { ModuleMetadata, Type } from '@nestjs/common';
export interface LlmModuleOptions {
/** mana-llm service URL (default: http://localhost:3025) */
manaLlmUrl?: string;
/** Default text model (default: ollama/gemma3:4b) */
defaultModel?: string;
/** Default vision model (default: ollama/llava:7b) */
defaultVisionModel?: string;
/** Request timeout in ms (default: 120000) */
timeout?: number;
/** Max retries on transient failures (default: 2) */
maxRetries?: number;
/** Enable debug logging (default: false) */
debug?: boolean;
}
export interface LlmModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
useExisting?: Type<LlmOptionsFactory>;
useClass?: Type<LlmOptionsFactory>;
useFactory?: (...args: any[]) => Promise<LlmModuleOptions> | LlmModuleOptions;
inject?: any[];
}
export interface LlmOptionsFactory {
createLlmOptions(): Promise<LlmModuleOptions> | LlmModuleOptions;
}
export interface ResolvedLlmOptions {
manaLlmUrl: string;
defaultModel: string;
defaultVisionModel: string;
timeout: number;
maxRetries: number;
debug: boolean;
}
export function resolveOptions(options: LlmModuleOptions): ResolvedLlmOptions {
return {
manaLlmUrl: options.manaLlmUrl ?? 'http://localhost:3025',
defaultModel: options.defaultModel ?? 'ollama/gemma3:4b',
defaultVisionModel: options.defaultVisionModel ?? 'ollama/llava:7b',
timeout: options.timeout ?? 120_000,
maxRetries: options.maxRetries ?? 2,
debug: options.debug ?? false,
};
}