import { BadGatewayException, Injectable } from '@nestjs/common'; const TIMEOUT_MS = 15_000; async function jsonFetch(url: string, opts: RequestInit = {}): Promise { const ctrl = new AbortController(); const id = setTimeout(() => ctrl.abort(), TIMEOUT_MS); try { const res = await fetch(url, { ...opts, headers: { 'Content-Type': 'application/json', ...opts.headers }, signal: ctrl.signal, }); clearTimeout(id); if (!res.ok) throw new BadGatewayException(`upstream returned ${res.status}`); return (await res.json()) as T; } catch (err) { clearTimeout(id); if (err instanceof BadGatewayException) throw err; throw new BadGatewayException('upstream service unreachable'); } } /** * Mapare țară → workspace AnythingLLM. * Fiecare workspace conține corpusul legal/fiscal al jurisdicției respective. */ const COUNTRY_WORKSPACE: Record = { RO: 'agent-juridic-romania', DE: 'national-germania', 'DE-fiscal': 'fiscal-germania', 'RO-fiscal': 'fiscal-romania', FR: 'national-franta', 'FR-fiscal': 'fiscal-franta', UK: 'national-uk', 'UK-fiscal': 'fiscal-uk', EU: 'agent-juridic-romania', // fallback general }; @Injectable() export class ExternalDataService { /** ceo-os-legislation-api: RAG vector search direct în corpus legislativ */ private readonly legislationUrl = process.env.LEGISLATION_API_URL ?? 'http://91.98.39.120:9404'; private readonly anythingLlmUrl = process.env.ANYTHINGLLM_URL ?? 'http://anythingllm-n3n3xi75sj0xkh3oey17n962.91.98.39.120.sslip.io'; private readonly anythingLlmKey = process.env.ANYTHINGLLM_API_KEY ?? ''; /** * Vector search în corpusul legislativ — returnează secțiuni text brut. * 'scenario' trebuie să fie o descriere completă a situației (nu keyword). */ async searchLegislation(scenario: string, country?: string, topK = 8) { return jsonFetch(`${this.legislationUrl}/search_legislation`, { method: 'POST', body: JSON.stringify({ scenario, country: country ?? null, top_k: topK }), }); } /** * Răspuns AI interpretat via agent AnythingLLM. * Rutare automată pe workspace-ul potrivit după țară și tip. */ async askLegislationAgent( message: string, country = 'RO', type: 'general' | 'fiscal' | 'national' = 'general', sessionId?: string, ) { if (!this.anythingLlmKey) { throw new BadGatewayException('ANYTHINGLLM_API_KEY not configured'); } const key = type === 'fiscal' ? `${country}-fiscal` : country; const workspace = COUNTRY_WORKSPACE[key] ?? COUNTRY_WORKSPACE[country] ?? 'agent-juridic-romania'; return jsonFetch( `${this.anythingLlmUrl}/api/v1/workspace/${workspace}/chat`, { method: 'POST', headers: { Authorization: `Bearer ${this.anythingLlmKey}` }, body: JSON.stringify({ message, mode: 'chat', sessionId: sessionId ?? `ceo-os-${country}-${Date.now()}`, attachments: [], }), }, ); } async checkCapability(capability: string, context?: string) { const capUrl = process.env.CAPABILITY_API_URL ?? 'http://91.98.39.120:9402'; return jsonFetch(`${capUrl}/check_capability`, { method: 'POST', body: JSON.stringify({ capability, context }), }); } }