Integration guide
LangGraph + SourceScore VERITAS
A LangGraph StateGraph with two VERITAS nodes: one retrieves curated records and another finds a candidate for review. Similarity alone never marks the answer grounded.
Install
pip install langgraph langchain-openai requestsGraph state + the retrieve node
LangGraph nodes are plain functions that take the state and return a partial update. The retrieve node turns a question into curated VERITAS records via /search, which returns a results array of claim summaries.
import requests
from typing import TypedDict, List
from langgraph.graph import StateGraph, END
from langchain_openai import ChatOpenAI
VERITAS = "https://sourcescore.org/api/v1"
class State(TypedDict):
question: str
claims: List[dict]
answer: str
candidate_found: bool
def veritas_retrieve(state: State) -> dict:
r = requests.get(
f"{VERITAS}/search",
params={"q": state["question"], "limit": 5},
timeout=8,
)
r.raise_for_status()
# /search returns {"results": [...]} — each item is a catalog summary.
return {"claims": r.json().get("results", [])}
The generate node
Build the prompt from retrieved records and tell the model to cite one only after its exact statement supports the answer.
def generate(state: State) -> dict:
context = "\n".join(
f"[{c['id']}] {c['statement']} (confidence {c['confidence']})"
for c in state["claims"]
)
prompt = (
"The records below are candidates, not truth verdicts. Use a record only "
"when its exact statement supports the answer; otherwise say the supplied "
"evidence does not cover the question.\n\n"
f"Candidate records:\n{context}\n\nQuestion: {state['question']}\nAnswer:"
)
answer = ChatOpenAI(model="gpt-4o-mini").invoke(prompt).content
return {"answer": answer}
The candidate-lookup node
A retriever returns the closest claims; it does not confirm the generated answer is consistent with them. This node POSTs the answer to /verify, which returns a bestMatch when it finds a similar catalog record after method-specific retrieval and legacy record-confidence gates. That is still not an entailment or truth verdict.
def find_answer_candidate(state: State) -> dict:
r = requests.post(
f"{VERITAS}/verify",
json={"claim": state["answer"], "minConfidence": 0.85},
timeout=8,
).json()
# /verify returns {"matches": [...], "bestMatch": {...} | absent}.
return {"candidate_found": r.get("bestMatch") is not None}
Wire the graph
Retrieve first. If no claims match, short-circuit to END (don't let the model improvise). Otherwise generate, then expose the nearest catalog candidate for an explicit evidence-review step.
def has_claims(state: State) -> str:
return "generate" if state["claims"] else "no_claims"
g = StateGraph(State)
g.add_node("retrieve", veritas_retrieve)
g.add_node("generate", generate)
g.add_node("candidate_lookup", find_answer_candidate)
g.set_entry_point("retrieve")
g.add_conditional_edges("retrieve", has_claims, {"generate": "generate", "no_claims": END})
g.add_edge("generate", "candidate_lookup")
g.add_edge("candidate_lookup", END)
app = g.compile()
out = app.invoke({"question": "Who introduced the Transformer architecture?"})
print(out["answer"])
print("candidate found:", out["candidate_found"])
Why expose a candidate state
The two failure modes a retriever can't catch — out-of-corpus assertions and fabricated citations — both can be investigated here: if no similar catalog candidate is returned, bestMatch is absent and candidate_found is false. When a candidate exists, compare its primary sources before asserting truth; otherwise retry, hand off to a human, or label the answer as lacking a catalog match. The public API is free with no account, key, or signup.