Files
ALwrity/frontend/src/components/BlogWriter/HallucinationChecker.tsx

74 lines
2.8 KiB
TypeScript

import React, { useState } from 'react';
import { useCopilotAction } from '@copilotkit/react-core';
import DiffPreview from './DiffPreview';
import { apiClient } from '../../api/client';
interface HallucinationCheckerProps {
buildFullMarkdown: () => string;
buildUpdatedMarkdownForClaim: (claimText: string, supportingUrl?: string) => {
original: string;
updated: string;
updatedMarkdown: string;
};
applyClaimFix: (claimText: string, supportingUrl?: string) => void;
}
const useCopilotActionTyped = useCopilotAction as any;
export const HallucinationChecker: React.FC<HallucinationCheckerProps> = ({
buildFullMarkdown,
buildUpdatedMarkdownForClaim,
applyClaimFix
}) => {
const [hallucinationResult, setHallucinationResult] = useState<any>(null);
useCopilotActionTyped({
name: 'runHallucinationCheck',
description: 'Run hallucination detector on full draft and view claims',
parameters: [],
handler: async () => {
const content = buildFullMarkdown();
const res = await apiClient.post('/api/blog/quality/hallucination-check', { text: content });
const data = res.data;
setHallucinationResult(data);
return { success: true, total_claims: data?.total_claims };
},
renderAndWaitForResponse: ({ respond, result }: any) => {
if (!result) return null;
const claims = hallucinationResult?.claims || [];
return (
<div style={{ padding: 12 }}>
<div style={{ fontWeight: 600, marginBottom: 8 }}>Hallucination Check</div>
<div>Total claims: {hallucinationResult?.total_claims ?? 0}</div>
<ul>
{claims.slice(0, 5).map((c: any, i: number) => {
const supporting = (c.supporting_sources && c.supporting_sources[0]?.url) || undefined;
const { original, updated } = buildUpdatedMarkdownForClaim(c.text, supporting);
return (
<li key={i} style={{ marginBottom: 10 }}>
<div style={{ marginBottom: 4 }}>[{c.assessment}] {c.text} (conf: {Math.round((c.confidence || 0)*100)/100})</div>
{original && updated ? (
<DiffPreview
original={original}
updated={updated}
onApply={() => { applyClaimFix(c.text, supporting); respond?.('applied'); }}
onDiscard={() => { respond?.('discarded'); }}
/>
) : (
<div style={{ fontStyle: 'italic', color: '#666' }}>No matching sentence found for preview.</div>
)}
</li>
);
})}
</ul>
<button onClick={() => respond?.('ack')}>Close</button>
</div>
);
}
});
return null; // This component only provides the copilot action
};
export default HallucinationChecker;