Metadata-Version: 2.4
Name: poma
Version: 0.7.0
Summary: Official Python SDK for the Poma document-processing API
Author-email: "POMA AI GmbH, Berlin" <sdk@poma-ai.com>
License-Expression: MPL-2.0
Keywords: chunking,structure,rag,poma,documents,ai
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.28.1
Requires-Dist: typing_extensions>=4.5.0
Provides-Extra: langchain
Requires-Dist: langchain>=0.3.27; extra == "langchain"
Requires-Dist: langchain-text-splitters>=0.3.9; extra == "langchain"
Requires-Dist: pydantic>=2; extra == "langchain"
Provides-Extra: llamaindex
Requires-Dist: llama-index>=0.13.0; extra == "llamaindex"
Requires-Dist: pydantic>=2; extra == "llamaindex"
Provides-Extra: qdrant
Requires-Dist: qdrant-client[fastembed]>=1.17.1; extra == "qdrant"
Provides-Extra: all
Requires-Dist: langchain>=0.3.27; extra == "all"
Requires-Dist: langchain-text-splitters>=0.3.9; extra == "all"
Requires-Dist: llama-index>=0.13.0; extra == "all"
Requires-Dist: llama-index-vector-stores-faiss>=0.5.0; extra == "all"
Requires-Dist: faiss-cpu>=1.10.0; extra == "all"
Requires-Dist: langchain_openai>=0.3.28; extra == "all"
Requires-Dist: langchain_community>=0.3.27; extra == "all"
Requires-Dist: llama-index-embeddings-langchain>=0.4.0; extra == "all"
Requires-Dist: qdrant-client[fastembed]>=1.17.1; extra == "all"
Requires-Dist: dotenv; extra == "all"
Dynamic: license-file

![POMA AI Logo](https://raw.githubusercontent.com/poma-ai/.github/main/assets/POMA_AI_Logo_Pink.svg)
# POMA: Preserving Optimal Markdown Architecture

## Quick-Start Guide

### Installation

Requires Python 3.10+. Install the core package:
```bash
pip install poma
```

For different integrations:
```bash
pip install 'poma[langchain]'
pip install 'poma[llamaindex]'
pip install 'poma[qdrant]'

# Or LangChain/LlamaIndex/Qdrant including example extras:
pip install 'poma[all]'
```

- You may also want: `pip install python-dotenv` to load API keys from a .env file.
- API keys required (POMA_API_KEY) for the POMA AI client via environment variables.
- **To request a POMA_API_KEY, please contact us at sdk@poma-ai.com**


### Usage

```python
from poma import PrimeCut, generate_cheatsheets

pc = PrimeCut(api_key="your_key")

# Ingest a document — submits the file, polls, and returns typed results
result = pc.ingest("document.pdf")

print(result.chunksets[0])
print(result.chunks[0].content)

# Eco ingestion uses the same flow against the eco endpoints
eco_result = pc.ingest_eco("document.pdf")
```

To test this flow from the command line (requires `POMA_API_KEY` in the environment):

```bash
python -m poma document.pdf          # run both ingest and ingest_eco
python -m poma path/to/file.pdf      # custom file
python -m poma document.pdf --no-eco   # standard ingest only
python -m poma document.pdf --eco     # eco ingest only
```

Generate cheatsheets as a top-level utility:
```python
cheatsheets = generate_cheatsheets(
    relevant_chunksets=result.chunksets,
    all_chunks=result.chunks,
)
print(cheatsheets[0]["content"])
```

If you already have a `.poma` archive, unpack it directly:
```python
from poma import unpack

archived_result = unpack("document.poma")
print(archived_result.chunks[0].content)
```

Async clients use the same API shape:
```python
import asyncio

from poma import AsyncPrimeCut


async def main() -> None:
    async with AsyncPrimeCut(api_key="your_key") as pc:
        result = await pc.ingest("document.pdf")
        print(result.chunksets[0])


asyncio.run(main())
```

### Grill (RAG / hybrid search)

Grill ingests documents into a per-project namespace and serves prompt-ready retrieval context for RAG.

> **Auth note**: Grill endpoints require a **project-level** key (prefix `poma_proj_gr_...`), **not** the account-level `POMA_API_KEY`. Set `POMA_GRILL_API_KEY` in your environment. The SDK validates the prefix at construction and raises `InvalidGrillApiKeyError` if you accidentally use the wrong one.

```python
from poma import Grill

g = Grill()  # reads POMA_GRILL_API_KEY

# 0. List the project's typed attributes BEFORE declaring any.
#    Attribute names (and their types) are permanent per project and capped at
#    `max_names`, so reuse an existing name instead of minting a near-duplicate.
known = {a.name: a.type for a in g.list_attributes().attributes}

# 1. Ingest a document. Grill indexes it into your project namespace
#    (no archive download). `result.status == "done"` when ready.
#    Attach typed attributes for query-time filtering. string/int/float/bool
#    are inferred; datetime and encrypted_text must be declared.
result = g.ingest(
    "document.pdf",
    attributes={"source": "treasury", "published_at": "2023-01-01T00:00:00Z"},
    attribute_schema={"published_at": {"type": "datetime"}},
)
print(result.job_id, result.status, result.usage)

# 1b. Or let Grill fetch the file itself from a public URL — no upload.
#     Optionally POST a webhook when the job finishes; the value is
#     "<url>|<header>:<value>|..." (server caps it at 1024 chars).
result = g.ingest(
    remote_url="https://example.com/document.pdf",
    completion="https://example.com/hook|x-api-key:secret",
)

# 2. Run hybrid search across all documents in the namespace.
#    Returns a prompt-ready XML+Markdown block — drop it into an LLM prompt.
#    Optionally narrow by typed attributes with `attribute_filters`.
ctx = g.search(
    "How do I configure retries?",
    target_tokens=2048,
    attribute_filters=[{"attr": "source", "op": "eq", "value": "treasury"}],
)
print(ctx.context)

# 3. List, inspect, and delete documents in the namespace.
#    To find the doc you just ingested, match source_job_id to the job_id.
for doc in g.list_docs():
    print(doc.doc_id, doc.filename, doc.pages)

deleted = g.delete_doc(doc.doc_id)
print(deleted.vectors_deleted, deleted.storage_deleted)
```

#### Listing attributes: list before you declare

`list_attributes()` (`GET /v3/grill/attributes`) returns an `AttributeList`:
`attributes` (a list of `AttributeInfo(name, type)`), `max_names` (the
project's cap on distinct names) and `note` (the server's guidance). It uses
the same project key as `list_docs()`.

```python
attrs = g.list_attributes()
for a in attrs.attributes:
    print(a.name, a.type)
print(f"{len(attrs.attributes)}/{attrs.max_names} names used")
```

**Attribute names are permanent per project** — the first write of a name fixes
its type and spends one of the `max_names` slots, and clearing a value does not
free the name. Agents and integrations should call `list_attributes()` before
declaring attributes and reuse an existing name (with its existing type).

A `503` means the attribute schema is temporarily unreadable. It is raised as
`ApiError` with `status_code == 503` — retry later; **never treat it as an
empty list**, or you will mint duplicate names. A response without an
`attributes` list raises `InvalidResponseError` for the same reason.

#### Deprecated: labels and integer metadata

`labels` and `meta_int_1` / `meta_int_2` on ingest, and `labels_any`,
`labels_all` and `meta_int_{1,2}_{gte,lte}` on search, are **deprecated** in
favour of typed attributes (`attributes` / `attribute_schema` on ingest,
`attribute_filters` on search). They are still sent and accepted by the API for
now and will be removed later, with notice. Passing them emits a
`DeprecationWarning`.

The async client mirrors the same surface:

```python
import asyncio
from poma import AsyncGrill


async def main() -> None:
    async with AsyncGrill() as g:
        ctx = await g.search_in_doc("summarize section 3", "doc_abc123")
        print(ctx.context)


asyncio.run(main())
```


### Example Implementations

All examples, integrations, and additional information can be found in our GitHub repository: [poma-ai/poma](https://github.com/poma-ai/)

We provide example implementations to help you get started with POMA AI:
- example.py — A standalone implementation for documents, showing the basic POMA AI workflow with simple keyword-based retrieval
- example_langchain.py — Integration with LangChain, demonstrating how easy it is to use POMA AI with LangChain
- example_llamaindex.py — Integration with LlamaIndex, showing how simple it is to use POMA AI with LlamaIndex

*Note: The integration examples use OpenAI embeddings. Make sure to set your OPENAI_API_KEY environment variable, or replace the embeddings with your preferred ones.*

All examples follow the same two-phase process (ingest → retrieve) but demonstrate different integration options for your RAG pipeline.

! Please do NOT send any sensitive and/or personal information to POMA AI endpoints without having a signed contract & DPA !
