Skip to main content

langgraph-jev

langgraph-jev is an open-source LangGraph/LangChain integration for Jev, TypeSafe's typed probabilistic decision API. JevNode wraps it as a decision node you drop into an agent graph -- it's not an LLM and not an LLM wrapper.

Jev evaluates typed questions against state and returns typed answers with probabilities and confidence attached. There's no text generation and nothing to parse -- your code branches on, sorts by, and routes with the result directly.

For example, a work_ticket graph might run an "understand" step over a customer request, hand the structured output to a JevNode for typed decisions (work type, priority, whether it needs an engineer), and then route to a coding, support, or docs worker based on those decisions. See examples/work_ticket.py for the full version of this.

Installation

pip install langgraph-jev

Set your API key (never commit it):

export TYPESAFE_API_KEY=...

The examples in examples/ also support a project-local .env file (already covered by .gitignore) via optional python-dotenv support:

pip install "langgraph-jev[examples]"
echo "TYPESAFE_API_KEY=..." > .env
python examples/basic.py

Quick start

from langgraph_jev import JevNode, choice

jev = JevNode(
    questions={
        "route": choice(["coding", "support", "research"]),
    }
)

Running examples/basic.py against a real state produces typed decisions with confidence scores, not free text:

Terminal output of examples/basic.py

Resource cleanup

JevClient (and anything built on it, like JevNode/JevRunnable) opens underlying HTTP clients on construction. Close it when you're done, or use it as a context manager:

from langgraph_jev import JevClient, choice

with JevClient() as client:
    result = client.decide(state={"title": "..."}, questions={"route": choice(["a", "b"])})

# or, in async code:
async with JevClient() as client:
    result = await client.adecide(state={"title": "..."}, questions={"route": choice(["a", "b"])})

If you construct a long-lived JevNode/JevRunnable (e.g. one per graph, reused across requests), there's no need to close it per-call -- call client.close()/await client.aclose() once when your application shuts down.

LangGraph

JevNode is directly callable by LangGraph -- it receives graph state and returns a state update (not a replacement of the whole state):

graph.add_node("jev", jev)

By default the entire graph state is passed to Jev as state. Use state_key/output_key to scope input and output:

jev = JevNode(
    state_key="structured_request",
    output_key="decision",
    questions={...},
)

Confidence routing

Choice and Score answers include a confidence score derived from the response's probability distribution (see Confidence). Configure thresholds and a policy for what happens below them:

jev = JevNode(
    questions={
        "work_type": choice(["bug", "support", "configuration", "documentation"]),
        "priority": choice(["critical", "high", "normal", "low"]),
    },
    thresholds={"work_type": 0.85, "priority": 0.90},
    low_confidence="human_review",  # "allow" (default) | "human_review" | "error"
)


def route_after_jev(state):
    if state["decision"].requires_review:
        return "human_review"
    return "continue"

Low-confidence decisions are never silently discarded -- the full decision and its confidence are always available on the result.

Routing helper

from langgraph_jev import route_by_decision


def route_work(state):
    return route_by_decision(
        state["decision"],
        field="work_type",
        routes={
            "bug": "coding_agent",
            "support": "support_agent",
            "documentation": "documentation_agent",
        },
        fallback="human_review",
    )

Jev makes the probabilistic decision; your application code decides what to do with it. Routing stays deterministic.

WorkTicket example

Jev doesn't generate the WorkTicket itself -- it provides the decisions, and your application code builds the typed contract from them:

decision = state["decision"]

ticket = WorkTicket(
    id="WT-1234",
    type=decision["work_type"].value,
    priority=decision["priority"].value,
    needs_engineer=decision["needs_engineer"].value,
    title=request["title"],
    description=request["description"],
    context={"customerTier": request.get("customerTier")},
    acceptance_criteria=["Identify root cause", "Add regression coverage if appropriate"],
)

Terminal output of examples/work_ticket.py

See examples/work_ticket.py for the full example: an understand step feeds Jev, Jev's decisions build a WorkTicket, and the ticket routes to a coding, support, or docs worker.

LangChain Runnable

from langgraph_jev import JevRunnable, choice

jev = JevRunnable(questions={"route": choice(["coding", "support", "research"])})
result = jev.invoke({"title": "Customer cannot login"})

Supports both invoke() and ainvoke().

Examples

File Demonstrates
examples/basic.py Minimal JevClient usage
examples/langgraph_basic.py A minimal LangGraph graph with a JevNode
examples/confidence_routing.py Thresholds and low_confidence="human_review"
examples/work_ticket.py Building a WorkTicket from Jev's decisions
examples/multi_agent.py Full graph: understand -> Jev -> ticket -> routed workers

Public API

from langgraph_jev import (
    JevClient,
    JevNode,
    JevRunnable,
    choice,
    boolean,
    score,
    route_by_decision,
)

That's the full public API. Errors (JevError and its subclasses JevConfigurationError, JevAPIError, JevTimeoutError, JevValidationError) and the result types (JevDecision, JevResult) are also exported for type annotations and except clauses.

Development

pip install -e ".[dev]"
pytest
ruff check .
ruff format .
mypy src

Tests use a fake/mock Jev transport and never require a real TYPESAFE_API_KEY. CI runs ruff check, ruff format --check, mypy, and pytest on Python 3.11-3.13.

License

MIT

Metadata

Release files for langgraph-jev 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for langgraph-jev 0.1.0
File Size Uploaded
langgraph_jev-0.1.0.tar.gz 149.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for langgraph-jev 0.1.0
File Interpreter ABI Platform
langgraph_jev-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 164.6 kB

Release files / langgraph_jev-0.1.0.tar.gz

Download URL langgraph_jev-0.1.0.tar.gz
Size 149.1 kB
Tags Source
SHA-256 checksum
How to use checksums
2c56fd2083404aa33f496cfb02f732cc6d0da12899f2ce15d93928146cac02d7
BLAKE2b-256 checksum
How to use checksums
e58f93778f93b99523cbb01e956e6c8f2a4393a44ce6bef9bdb67a4250e00dcb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.7

Release files / langgraph_jev-0.1.0-py3-none-any.whl

Download URL langgraph_jev-0.1.0-py3-none-any.whl
Size 15.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9afb1192a0f389893173700c142c131181f153bc8e151c2c5ad7796df0100589
BLAKE2b-256 checksum
How to use checksums
3c52bb3d399b5d6474d877a6cccce697fd65d858b2b3ea5a4bed351cbfd790e5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.7

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page