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:
@@ -0,0 +1,2 @@
|
||||
export { AiProvider, useRunJob, useAiProposals, useArtifact } from './react';
|
||||
export type { AiArtifact, AiClaim, AiEvidenceLink, AiModelRun, AiJobResult } from '@insignia/iios-kernel-client';
|
||||
@@ -0,0 +1,83 @@
|
||||
import React, { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
||||
import { RestClient, type AiArtifact, type AiJobResult } from '@insignia/iios-kernel-client';
|
||||
|
||||
const AiContext = createContext<RestClient | null>(null);
|
||||
|
||||
export function AiProvider({
|
||||
serviceUrl,
|
||||
token,
|
||||
children,
|
||||
}: {
|
||||
serviceUrl: string;
|
||||
token: string;
|
||||
children: React.ReactNode;
|
||||
}): React.ReactElement {
|
||||
const client = useMemo(() => new RestClient({ serviceUrl, token }), [serviceUrl, token]);
|
||||
return <AiContext.Provider value={client}>{children}</AiContext.Provider>;
|
||||
}
|
||||
|
||||
function useClient(): RestClient {
|
||||
const c = useContext(AiContext);
|
||||
if (!c) throw new Error('AI hooks must be used within <AiProvider>');
|
||||
return c;
|
||||
}
|
||||
|
||||
/** Run an AI worker (proposal only). Returns the job + proposed artifact (or null on abstain/fail). */
|
||||
export function useRunJob(): (interactionId: string, jobType: 'CLASSIFY' | 'SUMMARIZE' | 'EXTRACT') => Promise<AiJobResult> {
|
||||
const client = useClient();
|
||||
return (interactionId, jobType) => client.runAiJob({ interactionId, jobType });
|
||||
}
|
||||
|
||||
/** List AI proposals + human accept/reject feedback. */
|
||||
export function useAiProposals(opts?: { interactionId?: string; status?: string; pollMs?: number }): {
|
||||
artifacts: AiArtifact[];
|
||||
refresh: () => Promise<void>;
|
||||
accept: (id: string) => Promise<void>;
|
||||
reject: (id: string) => Promise<void>;
|
||||
} {
|
||||
const client = useClient();
|
||||
const [artifacts, setArtifacts] = useState<AiArtifact[]>([]);
|
||||
const refresh = useCallback(async () => {
|
||||
try {
|
||||
setArtifacts(await client.listArtifacts({ interactionId: opts?.interactionId, status: opts?.status }));
|
||||
} catch {
|
||||
/* transient */
|
||||
}
|
||||
}, [client, opts?.interactionId, opts?.status]);
|
||||
useEffect(() => {
|
||||
void refresh();
|
||||
if (!opts?.pollMs) return;
|
||||
const t = setInterval(() => void refresh(), opts.pollMs);
|
||||
return () => clearInterval(t);
|
||||
}, [refresh, opts?.pollMs]);
|
||||
const accept = async (id: string) => {
|
||||
await client.acceptArtifact(id);
|
||||
await refresh();
|
||||
};
|
||||
const reject = async (id: string) => {
|
||||
await client.rejectArtifact(id);
|
||||
await refresh();
|
||||
};
|
||||
return { artifacts, refresh, accept, reject };
|
||||
}
|
||||
|
||||
/** Full artifact detail (claims + evidence + model run) for explainability. */
|
||||
export function useArtifact(id: string | null): { artifact: AiArtifact | null; refresh: () => Promise<void> } {
|
||||
const client = useClient();
|
||||
const [artifact, setArtifact] = useState<AiArtifact | null>(null);
|
||||
const refresh = useCallback(async () => {
|
||||
if (!id) {
|
||||
setArtifact(null);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
setArtifact(await client.getArtifact(id));
|
||||
} catch {
|
||||
/* transient */
|
||||
}
|
||||
}, [client, id]);
|
||||
useEffect(() => {
|
||||
void refresh();
|
||||
}, [refresh]);
|
||||
return { artifact, refresh };
|
||||
}
|
||||
Reference in New Issue
Block a user