feat(CC-054): add ExternalDataService (legislation + AnythingLLM + capability)
This commit is contained in:
parent
3a39adb4bc
commit
aefd370d78
1 changed files with 81 additions and 0 deletions
81
src/external-data/external-data.service.ts
Normal file
81
src/external-data/external-data.service.ts
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
import { BadGatewayException, Injectable } from '@nestjs/common';
|
||||
|
||||
const TIMEOUT_MS = 10_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');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ExternalDataService {
|
||||
/** ceo-os-legislation-api: vector search over imported legislation corpus */
|
||||
private readonly legislationUrl =
|
||||
process.env.LEGISLATION_API_URL ?? 'http://91.98.39.120:9404';
|
||||
|
||||
/** AnythingLLM: AI-interpreted legislation answers via agent workspace */
|
||||
private readonly anythingLlmUrl =
|
||||
process.env.ANYTHINGLLM_URL ?? 'http://152.53.112.35:3010';
|
||||
|
||||
private readonly anythingLlmKey =
|
||||
process.env.ANYTHINGLLM_API_KEY ?? '';
|
||||
|
||||
private readonly legislationWorkspace =
|
||||
process.env.ANYTHINGLLM_LEGISLATION_WORKSPACE ?? 'legislation';
|
||||
|
||||
/**
|
||||
* Raw vector search: returns matching legislation sections (fast, no AI).
|
||||
* The 'scenario' field must be a full situation description, not just a 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 }),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* AI-interpreted legislation answer via AnythingLLM agent workspace.
|
||||
* Returns a conversational answer grounded in the legislation corpus.
|
||||
*/
|
||||
async askLegislationAgent(message: string, sessionId?: string) {
|
||||
if (!this.anythingLlmKey) {
|
||||
throw new BadGatewayException('ANYTHINGLLM_API_KEY not configured');
|
||||
}
|
||||
return jsonFetch(
|
||||
`${this.anythingLlmUrl}/api/v1/workspace/${this.legislationWorkspace}/chat`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { Authorization: `Bearer ${this.anythingLlmKey}` },
|
||||
body: JSON.stringify({
|
||||
message,
|
||||
mode: 'chat',
|
||||
sessionId: sessionId ?? 'ceo-os-legislation',
|
||||
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 }),
|
||||
});
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue