feat(p7): /v1/ai REST + kernel-client AI methods + @insignia/iios-ai-web SDK
Task 7.5: AiController exposes POST /v1/ai/jobs, GET /v1/ai/artifacts(+/:id), POST accept/reject (session-auth). kernel-client RestClient gains runAiJob/ listArtifacts/getArtifact/acceptArtifact/rejectArtifact + AiArtifact/AiClaim/ AiEvidenceLink/AiModelRun types. New @insignia/iios-ai-web (AiProvider, useRunJob, useAiProposals, useArtifact). Boundary allowlist: ai-web -> contracts + kernel-client (fails on ai-web->service). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import type { IngestInteractionRequest } from '@insignia/iios-contracts';
|
||||
import type { Message, InboxItem, InboxState, Ticket, TicketState, CallbackRequest, RouteBinding, RouteDecision } from './types';
|
||||
import type { Message, InboxItem, InboxState, Ticket, TicketState, CallbackRequest, RouteBinding, RouteDecision, AiArtifact, AiJobResult } from './types';
|
||||
|
||||
export interface RestConfig {
|
||||
serviceUrl: string;
|
||||
@@ -124,6 +124,31 @@ export class RestClient {
|
||||
return this.post<RouteDecision>(`/v1/routes/decisions/${id}/deny`, {});
|
||||
}
|
||||
|
||||
// ─── AI proposal layer (P7) ───────────────────────────────────
|
||||
async runAiJob(input: { interactionId: string; jobType: 'CLASSIFY' | 'SUMMARIZE' | 'EXTRACT' }): Promise<AiJobResult> {
|
||||
return this.post<AiJobResult>('/v1/ai/jobs', input);
|
||||
}
|
||||
async listArtifacts(opts?: { interactionId?: string; status?: string }): Promise<AiArtifact[]> {
|
||||
const q = new URLSearchParams();
|
||||
if (opts?.interactionId) q.set('interactionId', opts.interactionId);
|
||||
if (opts?.status) q.set('status', opts.status);
|
||||
const qs = q.toString();
|
||||
const r = await fetch(this.url(`/v1/ai/artifacts${qs ? `?${qs}` : ''}`), { headers: this.headers() });
|
||||
if (!r.ok) throw new Error(`listArtifacts ${r.status}`);
|
||||
return (await r.json()) as AiArtifact[];
|
||||
}
|
||||
async getArtifact(id: string): Promise<AiArtifact> {
|
||||
const r = await fetch(this.url(`/v1/ai/artifacts/${id}`), { headers: this.headers() });
|
||||
if (!r.ok) throw new Error(`getArtifact ${r.status}`);
|
||||
return (await r.json()) as AiArtifact;
|
||||
}
|
||||
async acceptArtifact(id: string): Promise<AiArtifact> {
|
||||
return this.post<AiArtifact>(`/v1/ai/artifacts/${id}/accept`, {});
|
||||
}
|
||||
async rejectArtifact(id: string): Promise<AiArtifact> {
|
||||
return this.post<AiArtifact>(`/v1/ai/artifacts/${id}/reject`, {});
|
||||
}
|
||||
|
||||
private async post<T>(path: string, body: unknown): Promise<T> {
|
||||
const r = await fetch(this.url(path), { method: 'POST', headers: this.headers(), body: JSON.stringify(body) });
|
||||
if (!r.ok) throw new Error(`POST ${path} ${r.status}`);
|
||||
|
||||
@@ -102,6 +102,54 @@ export interface RouteDecision {
|
||||
routeBinding?: RouteBinding;
|
||||
}
|
||||
|
||||
export interface AiClaim {
|
||||
id: string;
|
||||
claimType: string;
|
||||
claimJson: Record<string, unknown> | unknown;
|
||||
confidence?: number | null;
|
||||
validationStatus: string;
|
||||
}
|
||||
|
||||
export interface AiEvidenceLink {
|
||||
id: string;
|
||||
sourceType: string;
|
||||
sourceId: string;
|
||||
spanRef?: string | null;
|
||||
relevanceScore?: number | null;
|
||||
}
|
||||
|
||||
export interface AiModelRun {
|
||||
provider: string;
|
||||
model: string;
|
||||
modelVersion: string;
|
||||
promptVersion: string;
|
||||
tokensIn: number;
|
||||
tokensOut: number;
|
||||
costUnits: number;
|
||||
latencyMs: number;
|
||||
}
|
||||
|
||||
export interface AiArtifact {
|
||||
id: string;
|
||||
jobId: string;
|
||||
artifactType: string;
|
||||
status: string;
|
||||
confidence?: number | null;
|
||||
contentRef: unknown;
|
||||
explanationRef?: string | null;
|
||||
abstentionReason?: string | null;
|
||||
createdAt: string;
|
||||
claims?: AiClaim[];
|
||||
evidence?: AiEvidenceLink[];
|
||||
modelRun?: AiModelRun;
|
||||
job?: { id: string; jobType: string; interactionId?: string | null; status: string };
|
||||
}
|
||||
|
||||
export interface AiJobResult {
|
||||
job: { id: string; jobType: string; status: string; abstentionReason?: string | null };
|
||||
artifact: AiArtifact | null;
|
||||
}
|
||||
|
||||
/** Minimal socket surface so the facade can be unit-tested with a fake. */
|
||||
export interface SocketLike {
|
||||
on(event: string, handler: (...args: unknown[]) => void): unknown;
|
||||
|
||||
Reference in New Issue
Block a user