Metadata-Version: 2.4
Name: infolang
Version: 0.2.0
Summary: Official Python SDK for InfoLang semantic memory (il-runtime).
Project-URL: Homepage, https://infolang.ai
Project-URL: Documentation, https://infolang.ai/docs/sdk/python
Project-URL: Source, https://github.com/InfoLang-Inc/infolang-sdk-python
Project-URL: Issues, https://github.com/InfoLang-Inc/infolang-sdk-python/issues
Author-email: InfoLang <engineering@infolang.ai>
License-Expression: Apache-2.0
Keywords: context,infolang,llm,rag,sdk,semantic-memory
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.7
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Description-Content-Type: text/markdown

# InfoLang Python SDK

Official Python client for [InfoLang](https://infolang.ai) semantic memory. Wraps the
`il-runtime` REST API (Forge-compatible) with one-line construction, typed
errors, automatic retries, and ergonomic agent helpers.

> Repository: `__REPO__`. Package: `infolang` (PyPI).

## Install

While the package is private, install from the repo:

```bash
pip install "infolang @ git+ssh://git@github.com/InfoLang-Inc/__REPO__.git@v0.1.0"
```

Once published:

```bash
pip install infolang
```

## Quickstart

```python
from infolang import InfoLang

il = InfoLang.from_api_key("il_live_...")          # managed cloud
result = il.investigate("How does auth middleware work?")
for chunk in result.chunks:
    print(chunk.score, chunk.text)
```

Three ways to call, depending on your runtime:

```python
# 1. One-shot
chunks = InfoLang.from_api_key("il_live_...").investigate("query").chunks

# 2. Durable client (connection pooling)
with InfoLang.from_session_file() as il:        # OAuth via ~/.config/infolang/session.json
    il.memorize("a fact worth keeping", source="docs/auth.md")

# 3. Async
import asyncio
from infolang import AsyncInfoLang

async def main():
    async with AsyncInfoLang.from_api_key("il_live_...") as il:
        result = await il.recall("auth middleware", top_k=5)
        print(len(result.chunks))

asyncio.run(main())
```

## Authentication

| Mode | Constructor | Target |
|------|-------------|--------|
| Managed cloud (API key) | `InfoLang.from_api_key("il_live_...")` | `api.infolang.ai` |
| Managed cloud (OAuth) | `InfoLang.from_session_file()` | `api.infolang.ai` |
| Self-hosted dev | `InfoLang.from_dev_key("key:namespace")` | `127.0.0.1:8766` |
| Enterprise mTLS | `InfoLang.from_mtls("client.pem", "client-key.pem")` | self-hosted origin |

Credentials are also read from the environment: `INFOLANG_API_KEY`,
`INFOLANG_DEV_KEY`, `INFOLANG_BASE_URL`, `INFOLANG_NAMESPACE`.

## Core API

| Method | Purpose |
|--------|---------|
| `recall(query, *, namespace, top_k, filters, verbose)` | Semantic recall |
| `recall_hybrid(query, *, namespace, top_k, tag_filter, candidate_pool, use_hybrid)` | Recall over a candidate pool with tag-inclusion ordering |
| `investigate(query, *, namespace_hint, top_k=5)` | Agent-style recall |
| `remember(text, *, source, tags, namespace)` | Store a memory |
| `remember_batch(items, *, namespace, source)` | Store many memories in one call |
| `memorize(content, *, source, tags, namespace)` | Alias of `remember` |
| `forget(memory_id, *, namespace)` | Delete a memory |
| `reset_namespace(namespace)` | Bulk clear a namespace (list + forget) |
| `list_banks()` / `list_recent(*, namespace, n)` | Introspection |
| `context_pack(query, *, namespace, max_tokens, repo_root)` | One-shot context string |
| `ingest_repo(namespace, *, repo_root, ref)` | Index a repository |
| `execute(operations)` | Batch ops |
| `health.check()` | Liveness/readiness |

### Bulk ingest + hybrid recall (benchmarks / evals)

```python
il = InfoLang.from_dev_key("devsecret:default")   # self-hosted runtime

il.reset_namespace("eval_run")                      # clean slate
il.remember_batch(
    [
        {"text": "Alice: I moved to Berlin in March 2024.",
         "tags": ["alice", "march", "2024", "session_1"]},
        {"text": "Bob: My flight is on the 12th.",
         "tags": ["bob", "session_2"]},
    ],
    namespace="eval_run",
)

hits = il.recall_hybrid(
    "When did Alice move to Berlin?",
    namespace="eval_run",
    top_k=20,
    tag_filter=["march", "2024"],   # restrict to chunks carrying these tags
    candidate_pool=500,
)
for chunk in hits.chunks:
    print(chunk.score, chunk.tags, chunk.text)
```

`recall_hybrid` fetches up to `candidate_pool` IL-ranked candidates, then (when
`tag_filter` is given) re-orders so chunks sharing a tag come first and truncates
to `top_k`. This biases a wide recall sweep toward tagged evidence — handy for
evals/benchmarks — without requiring any server-side hybrid op.

## Errors

All failures raise a subclass of `InfoLangError`: `AuthenticationError`,
`RateLimitError` (with `retry_after`), `NotFoundError`, `ValidationError`,
`ServerError`, plus `InfoLangConnectionError` for transport failures. Every API
error carries `status`, `body`, and `request_id`.

## Resilience

`recall`/`remember` and friends retry `429` and `5xx` with exponential backoff
plus full jitter (configurable via `max_retries`), and honor `Retry-After`.
Timeouts default to connect 5s / read 30s.

## Development

```bash
pip install -e ".[dev]"
ruff check .
mypy
pytest
```

The REST contract is pinned in `openapi/` (see `openapi/IL_RUNTIME_VERSION`).
Regenerate the typed models with `scripts/codegen.sh` after bumping the pin.
