Skip to content

Perf-Technology/perf-langgraph

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

perf-langgraph

Catch and fix hallucinations in any LangGraph workflow.

perf-langgraph drops Perf into your LangGraph in three lines: add a node, add an edge, get auto-corrected LLM output before downstream nodes see the mistake.

Install

pip install perf-langgraph
export PERF_API_KEY=pk_...

Quickstart

from langgraph.graph import END, StateGraph
from perf_langgraph import PerfRepair

graph = StateGraph(MyState)
graph.add_node("llm", call_llm)
graph.add_node("verify", PerfRepair())     # <-- new
graph.add_edge("llm", "verify")            # <-- new
graph.add_edge("verify", END)              # <-- new (was llm -> END)

That's it. The LLM output flows through Perf's correction engine. Hallucinations are detected and rewritten before downstream nodes see the broken text. The corrected version is left in state["messages"] (or state["output"], or wherever the source text lives).

What it does

  • Verifies LLM output against trusted sources via Perf's CER — web search, citation databases, NLI, internal consistency checks.
  • Repairs errors automatically — temporal mistakes, fabricated citations, numerical errors, schema violations. Returns the corrected text plus a list of changes.
  • Routes the result through standard LangGraph conditional edges. pass / corrected / rejected / error.

PerfVerify vs PerfRepair

Both nodes wrap the same engine. They differ in what they do with the result:

  • PerfVerify — runs verification and writes claim-level evidence to state["perf"]. Does not modify the source text. Use when you want a routing decision without auto-correction.
  • PerfRepair — runs verification and applies corrections. Writes the corrected text back to the source path in state by default. Use when you want the fix applied. Most users want this.

Routing

perf_router reads state["perf"]["status"] and returns one of "pass", "corrected", "rejected", or "error". Drop it into add_conditional_edges:

from perf_langgraph import perf_router

graph.add_conditional_edges(
    "verify",
    perf_router,
    {
        "pass": "respond",
        "corrected": "respond",       # often the same as pass
        "rejected": "regenerate",     # or route to human review
        "error": "fallback_handler",
    },
)

For stricter routing where any modification should follow the recovery path, use strict_perf_router (it folds correctedrejected).

State contract

After a Perf node runs, state looks like:

state["perf"] = {
    "node": "verify" | "repair",
    "status": "approved" | "corrected" | "rejected" | "error",
    "verified_text": str,           # original (verify) or corrected (repair)
    "original_text": str,
    "was_corrected": bool,
    "confidence": float,
    "evidence": [...],              # claim-level (verify) or change-level (repair)
    "error": None | str,
    "latency_ms": int,
    "mode": "fast" | "standard" | "thorough",
    # repair-only:
    "error_types_detected": list[str] | None,
}

The node reads input from state["messages"][-1].content by default, falling back to state["output"]. Override with text_path="my_field" on the node constructor.

The router (PerfRepair only, with mutate_source=True, default): writes corrected_text back to whichever path the input came from. For messages, it appends a new AIMessage with the corrected content (preserving the original in the message history). For string fields, it replaces in place.

Configuration

Both nodes share most options:

Parameter Type Default Description
api_key str | None PERF_API_KEY env Perf API key.
base_url str | None PERF_API_BASE env or prod API base URL.
text_path str | None auto State path to read input from.
output_key str "perf" Where to write the result.
on_failure "raise" | "return" | "fallback" "fallback" What to do on Perf API errors.
timeout float 30.0 Per-call timeout in seconds.
max_retries int 2 Retry count for retryable errors.

PerfVerify-specific: | mode | "fast" \| "standard" \| "thorough" | "standard" | CER mode. |

PerfRepair-specific: | correction_budget | "fast" \| "thorough" | "fast" | Correction depth. (Note: no "standard"/v1/correct doesn't accept it.) | | original_prompt_path | str \| None | None | State path with the prompt that produced the content. Improves CER context. | | target_schema | dict \| None | None | JSON Schema for schema-based correction. | | mutate_source | bool | True | If True, write corrected text back to the source path. |

on_failure semantics

  • "raise" — re-raise the SDK's PerfError. Crashes the graph. Good for dev.
  • "return" — set status="error" and let the router decide. verified_text is None.
  • "fallback" (default) — set status="error" AND populate verified_text with the original (un-verified) text. Production-safe — graph keeps running even if Perf is unreachable.

Async

Both nodes are LangChain Runnables, so await graph.ainvoke(state) works:

result = await graph.ainvoke({"messages": [...]})

FAQ

How is this different from LangChain output parsers? Output parsers validate format (does this match a schema?). Perf validates truth (is this claim actually correct?) and corrects errors against trusted sources.

Does Perf replace LangGraph? No. LangGraph orchestrates where, when, how LLM calls happen. Perf operates on the output of any individual call. Different layer entirely. Use them together.

What modalities are supported? This package handles text. Perf itself supports text, images, audio, and video — the broader SDK provides multi-modal endpoints.

What about cost? Per-call pricing on the Perf side. See withperf.pro for current rates.

Links

License

MIT

About

Catch and fix hallucinations in any LangGraph workflow. Drop in PerfVerify or PerfRepair as a node; get verified or auto-corrected LLM output.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages