ceo-api/src/external-data/external-data.service.ts

106 lines
3.3 KiB
TypeScript

import { BadGatewayException, Injectable } from '@nestjs/common';
const TIMEOUT_MS = 15_000;
async function jsonFetch<T>(url: string, opts: RequestInit = {}): Promise<T> {
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<string, string> = {
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 }),
});
}
}