Metadata-Version: 2.4
Name: wardhook-core
Version: 0.1.1
Summary: LangGraph agent runtime with tool calling, RAG with source citations, and a one-command FastAPI server.
Project-URL: Homepage, https://github.com/justicebajaj161/wardhook
Project-URL: Documentation, https://github.com/justicebajaj161/wardhook/blob/main/docs/packages/core.md
Project-URL: Repository, https://github.com/justicebajaj161/wardhook
Project-URL: Issues, https://github.com/justicebajaj161/wardhook/issues
Project-URL: Changelog, https://github.com/justicebajaj161/wardhook/blob/main/CHANGELOG.md
Author-email: Aditya Gupta <adityabajaj161@gmail.com>
Maintainer-email: Aditya Gupta <adityabajaj161@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Aditya Gupta
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: agent,ai,fastapi,langchain,langgraph,llm,rag
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: fastapi>=0.110
Requires-Dist: langchain-core<2,>=1.4
Requires-Dist: langgraph<2,>=1.2
Requires-Dist: numpy>=1.24
Requires-Dist: pydantic<3,>=2.7
Requires-Dist: pypdf>=4.0
Requires-Dist: typer>=0.12
Requires-Dist: typing-extensions>=4.9
Requires-Dist: uvicorn[standard]>=0.29
Provides-Extra: all
Requires-Dist: chromadb>=0.5; extra == 'all'
Requires-Dist: langchain-anthropic>=1.0; extra == 'all'
Requires-Dist: langchain-google-genai>=2.0; extra == 'all'
Requires-Dist: langchain-openai>=1.0; extra == 'all'
Provides-Extra: anthropic
Requires-Dist: langchain-anthropic>=1.0; extra == 'anthropic'
Provides-Extra: chroma
Requires-Dist: chromadb>=0.5; extra == 'chroma'
Provides-Extra: google
Requires-Dist: langchain-google-genai>=2.0; extra == 'google'
Provides-Extra: openai
Requires-Dist: langchain-openai>=1.0; extra == 'openai'
Description-Content-Type: text/markdown

# wardhook-core

[![CI](https://github.com/justicebajaj161/wardhook/actions/workflows/ci.yml/badge.svg)](https://github.com/justicebajaj161/wardhook/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/wardhook-core.svg)](https://pypi.org/project/wardhook-core/)
[![Python](https://img.shields.io/pypi/pyversions/wardhook-core.svg)](https://pypi.org/project/wardhook-core/)
[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](../../LICENSE)

A LangGraph agent runtime with tool calling, retrieval with real source
citations, and a one-command FastAPI server.

Part of [Wardhook](https://github.com/justicebajaj161/wardhook). Works
completely on its own — it never imports the other Wardhook packages.

## Install

```bash
pip install wardhook-core

# with a model provider
pip install "wardhook-core[anthropic]"   # or [openai], [all]
```

## Usage

```python
from wardhook.core import AgentGraph, InMemoryVectorStore, Retriever, chunk_text

store = InMemoryVectorStore()
store.add(chunk_text(open("policy.md").read(), "policy.md"))

agent = AgentGraph(model="claude-opus-5", retriever=Retriever(store))
result = agent.invoke("What excess applies to storm damage?")

print(result["output"])
print(result["citations"][0]["source"])  # -> 'policy.md'
```

## What you get

**Provider-agnostic.** `AgentGraph` accepts any object with `.invoke()`. Pass a
`ChatAnthropic`, a `ChatOpenAI`, or a test double. Nothing in the base install
depends on a provider SDK, so you are never locked in.

```python
agent = AgentGraph(model=ChatOpenAI(model="gpt-4o"))  # instance
agent = AgentGraph(model="anthropic:claude-opus-5")  # name
agent = AgentGraph(model=my_fake)  # test double, no API key
```

**Tools from plain functions.** The docstring becomes the description the model
reads, so it is required rather than optional.

```python
def lookup_claim(claim_id: str) -> str:
    """Look up the current status of a claim by its identifier."""
    return db.claims.status(claim_id)


agent = AgentGraph(model="claude-opus-5", tools=[lookup_claim])
```

**Citations are structural, not parsed.** Retrieval returns records carrying
source, chunk position and score. You render or verify them directly; the model
cannot invent a citation for a document it was never shown.

```python
for c in result["citations"]:
    print(f"{c['source']} chunk {c['chunk_index']} (score {c['score']:.3f})")
```

**RAG that runs with no API key.** Document loading (PDF, Markdown, text),
recursive chunking with overlap, and a NumPy vector store. The default
embeddings are a classical hashing vectoriser — no model weights, no network —
so the pipeline works the moment you install it. Swap in real embeddings for
production; the interface is identical.

```python
store = InMemoryVectorStore(embeddings=OpenAIEmbeddings())
store.save("index")  # index.npz + index.json
store = InMemoryVectorStore.load("index", embeddings=OpenAIEmbeddings())
```

**Serve it in one command.**

```bash
wardhook serve myapp.agents:support_agent --port 8000
```

Exposes `POST /invoke`, `GET /health`, `GET /info`, and OpenAPI docs at `/docs`.
A production `Dockerfile` ships with the package.

## Composing with the rest of Wardhook

`AgentGraph` takes `guardrails=[...]` and `telemetry=True`, but core does not
depend on the packages that provide them. Both attach through structural
contracts in `wardhook.core.protocols`, so **any object of the right shape
works** — from Wardhook, from your codebase, or from somewhere else entirely.

```python
from wardhook.core import AgentGraph
from wardhook.guardrails import PIIRedactor, RoleBasedToolPolicy  # optional install

agent = AgentGraph(
    model="claude-opus-5",
    tools=[lookup_claim],
    guardrails=[PIIRedactor(pack="insurance"), RoleBasedToolPolicy(...)],
    telemetry=True,  # requires wardhook-observability
)
```

Writing your own takes no dependency at all:

```python
class NoInternalCodenames:
    name = "no-codenames"

    def on_output(self, text, context):
        if "PROJECT_HALCYON" in text:
            return {
                "action": "redact",
                "text": text.replace("PROJECT_HALCYON", "[internal]"),
                "reason": "internal codename",
                "rule": "codename-list",
            }
        return {"action": "allow"}


agent = AgentGraph(model="claude-opus-5", guardrails=[NoInternalCodenames()])
```

The full contract is three optional hooks — `on_input`, `on_output`,
`on_tool_call` — each returning something with an `action` of `"allow"`,
`"redact"` or `"block"`. Implement only the ones you need.

## Notes

- **Guardrails fail closed.** If a guardrail raises, the run is blocked and the
  failure is recorded. Set `guardrail_error_policy="allow"` or `"raise"` to
  change that.
- **Denied tool calls never execute.** The model is told it was denied through a
  normal tool result, so it can recover instead of failing the run.
- **Tool errors go back to the model**, not to your caller — an agent that can
  see a failure can often work around it.

## Links

- [Design decisions](../../docs/packages/core.md)
- [Architecture overview](../../docs/architecture.md)
- [Main repository](https://github.com/justicebajaj161/wardhook)
