/**
* Fact-Check Cache Layer
*
* Handles caching and retrieval of fact-checks from Neo4j.
*/
import { apolloClient } from '../apollo-client';
import { gql } from '@apollo/client';
import type { FactCheck, FactCheckCitation } from './types';
/**
* GraphQL query to get a fact-check by its claim hash
*/
const GET_FACT_CHECK_BY_HASH = gql`
query GetFactCheckByClaimHash($claimHash: String!) {
getFactCheckByClaimHash(claimHash: $claimHash) {
id
claim_text
claim_text_hash
verdict
confidence
rationale
rationale_short
citations
model_used
processing_time_ms
verification_mode
checked_at
created_at
source_statement_id
}
}
`;
/**
* GraphQL query to get fact-checks for a statement
*/
const GET_FACT_CHECKS_FOR_STATEMENT = gql`
query GetFactChecksForStatement($statementId: ID!) {
getFactChecksForStatement(statementId: $statementId) {
id
claim_text
claim_text_hash
verdict
confidence
rationale
rationale_short
citations
model_used
processing_time_ms
verification_mode
checked_at
created_at
source_statement_id
}
}
`;
/**
* GraphQL mutation to create a fact-check
*/
const CREATE_FACT_CHECK = gql`
mutation CreateFactCheck($input: FactCheckCreateInput!) {
createFactChecks(input: [$input]) {
factChecks {
id
}
}
}
`;
/**
* GraphQL mutation to link fact-check to statement
*/
const LINK_FACT_CHECK_TO_STATEMENT = gql`
mutation LinkFactCheckToStatement($statementId: ID!, $factCheckId: ID!) {
updateStatements(
where: { id: $statementId }
connect: { factChecks: { where: { node: { id: $factCheckId } } } }
) {
statements {
id
}
}
}
`;
/**
* Parse citations from JSON string if needed
*/
function parseCitations(citations: string | FactCheckCitation[] | null): FactCheckCitation[] {
if (!citations) return [];
if (Array.isArray(citations)) return citations;
try {
return JSON.parse(citations);
} catch {
return [];
}
}
/**
* Transform GraphQL response to FactCheck type
*/
function transformFactCheck(data: any): FactCheck | null {
if (!data) return null;
return {
...data,
citations: parseCitations(data.citations),
};
}
/**
* Check cache for an existing fact-check by claim hash
*/
export async function getCachedFactCheck(claimHash: string): Promise<FactCheck | null> {
try {
const { data } = await apolloClient.query({
query: GET_FACT_CHECK_BY_HASH,
variables: { claimHash },
fetchPolicy: 'network-only', // Always check the database
});
return transformFactCheck(data?.getFactCheckByClaimHash);
} catch (error) {
console.error('[FactCheck Cache] Error fetching cached fact-check:', error);
return null;
}
}
/**
* Get all fact-checks for a statement
*/
export async function getFactChecksForStatement(statementId: string): Promise<FactCheck[]> {
try {
const { data } = await apolloClient.query({
query: GET_FACT_CHECKS_FOR_STATEMENT,
variables: { statementId },
fetchPolicy: 'network-only',
});
const factChecks = data?.getFactChecksForStatement || [];
return factChecks.map(transformFactCheck).filter(Boolean) as FactCheck[];
} catch (error) {
console.error('[FactCheck Cache] Error fetching fact-checks for statement:', error);
return [];
}
}
/**
* Store a new fact-check in Neo4j
*/
export async function storeFactCheck(
factCheck: FactCheck,
statementId?: string
): Promise<FactCheck | null> {
try {
// Create the fact-check node
const input = {
id: factCheck.id,
claim_text: factCheck.claim_text,
claim_text_hash: factCheck.claim_text_hash,
verdict: factCheck.verdict,
confidence: factCheck.confidence,
rationale: factCheck.rationale,
rationale_short: factCheck.rationale_short || null,
citations: JSON.stringify(factCheck.citations),
model_used: factCheck.model_used || null,
processing_time_ms: factCheck.processing_time_ms || null,
verification_mode: factCheck.verification_mode,
checked_at: factCheck.checked_at,
created_at: factCheck.created_at || new Date().toISOString(),
source_statement_id: statementId || null,
};
await apolloClient.mutate({
mutation: CREATE_FACT_CHECK,
variables: { input },
});
// If there's a statement ID, create the relationship
if (statementId) {
await apolloClient.mutate({
mutation: LINK_FACT_CHECK_TO_STATEMENT,
variables: { statementId, factCheckId: factCheck.id },
});
}
return factCheck;
} catch (error) {
console.error('[FactCheck Cache] Error storing fact-check:', error);
return null;
}
}
/**
* Check if a fact-check exists for the given claim hash
*/
export async function hasFactCheck(claimHash: string): Promise<boolean> {
const cached = await getCachedFactCheck(claimHash);
return cached !== null;
}