Metadata-Version: 2.4
Name: unshadow
Version: 0.2.2
Summary: Python client for Unshadow Engine and agent banks.
Author-email: Unshadow <support@unshadow.dev>
License-Expression: MIT
Project-URL: Homepage, https://unshadow.dev
Project-URL: Documentation, https://unshadow.dev/docs/api/sdk
Project-URL: Repository, https://github.com/unshadow-ai/unshadow
Project-URL: Issues, https://github.com/unshadow-ai/unshadow/issues
Keywords: unshadow,memory,langchain,llamaindex,rag
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
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 :: Software Development :: Libraries
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.3; extra == "langchain"
Provides-Extra: llamaindex
Requires-Dist: llama-index-core>=0.11; extra == "llamaindex"
Dynamic: license-file

<p align="center">
  <a href="https://unshadow.dev">
    <img src="https://unshadow.dev/unshadow-mark-on-white.png" alt="Unshadow" width="96" height="96">
  </a>
</p>

<p align="center">
  <a href="https://pypi.org/project/unshadow/"><img src="https://img.shields.io/pypi/v/unshadow" alt="PyPI"></a>
  <a href="https://pypi.org/project/unshadow/"><img src="https://img.shields.io/pypi/pyversions/unshadow" alt="Python"></a>
  <a href="https://unshadow.dev/docs/api/sdk"><img src="https://img.shields.io/badge/docs-unshadow.dev-111111" alt="Docs"></a>
  <a href="https://github.com/unshadow-ai/unshadow/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-111111" alt="MIT"></a>
</p>

# Unshadow

Python client for [Unshadow](https://unshadow.dev) Engine and agent banks.

```bash
pip install unshadow
```

| Extra | Install | What you get |
|-------|---------|----------------|
| LangChain | `pip install "unshadow[langchain]"` | `langchain_unshadow.UnshadowRetriever` |
| LlamaIndex | `pip install "unshadow[llamaindex]"` | `llama_index_unshadow.UnshadowRetriever` |

TypeScript: [`npm i @unshadow/sdk`](https://www.npmjs.com/package/@unshadow/sdk). n8n: [`@unshadow/n8n-nodes-unshadow`](https://www.npmjs.com/package/@unshadow/n8n-nodes-unshadow). Docs: [unshadow.dev/docs/api/sdk](https://unshadow.dev/docs/api/sdk). API keys: [unshadow.dev/install/api](https://unshadow.dev/install/api).

## Engine

`Unshadow` matches the TypeScript SDK: `context`, `search`, `extract`, `forget`, `profile`.

```python
from unshadow import Unshadow

unshadow = Unshadow(api_key="unshadow_…")
packed = unshadow.inject_context("What am I working on?", token_budget=1500)
hits = unshadow.search("pnpm", limit=8, category="preference")
unshadow.extract(
    "User prefers pnpm",
    source_type="ai_conversation",
    idempotency_key="turn-1",
)
unshadow.forget(["11111111-1111-1111-1111-111111111111"])
```

`extract(..., idempotency_key=)` sends `Idempotency-Key`. Reads retry once on 429/5xx. `extract` retries only when that key is set. Search rows include `supersedes` and `superseded_by`. Filter with `unshadow.search("editor", category="preference")`.

### LangChain

```python
from langchain_unshadow import UnshadowRetriever
from unshadow import Unshadow

unshadow = Unshadow(api_key="unshadow_…")
retriever = UnshadowRetriever(client=unshadow, k=8, prefer_context=True)
docs = retriever.invoke("What did we decide about pnpm?")
```

`prefer_context` uses `POST /context` and splits `[mem:uuid]` lines. This is not `ConversationBufferMemory`.

### LlamaIndex

```python
from llama_index_unshadow import UnshadowRetriever
from unshadow import Unshadow

unshadow = Unshadow(api_key="unshadow_…")
retriever = UnshadowRetriever(unshadow, k=8, prefer_context=True)
nodes = retriever.retrieve("What did we decide about pnpm?")
```

## Agent bank

`UnshadowBank` is retain / recall / authorize. Those calls use `/bank/*`.

```python
from unshadow import UnshadowBank

bank = UnshadowBank(api_key="unshadow_…")
bank.remember("Ship on Fridays is forbidden.", conversation_key="agent-main")
packed = bank.context("release policy")
receipt = bank.authorize(["memory-id"], query="deploy production")
bank.explain(packed["operation_id"])
```

| Name | HTTP |
|------|------|
| `remember` | `POST /bank/retain` (delta, 32k cap, 409 retry) |
| `explain` | `POST /bank/explain` |
| `authorize` | `POST /bank/recall` with `purpose: tool_arg` |

`retain` is an alias of `remember`.

The bank client does not ingest files. Capture stores pages and repo notes. Link that project into the agent bank. `remember(..., source_url=...)` cites a URL.
