Metadata-Version: 2.4
Name: clx-cli
Version: 0.6.0
Summary: A minimal, backend-agnostic AI resolver with SQL adapters.
Author-email: "Roberto L. Delgado" <roberto@delgadodev.xyz>
License: MIT
Project-URL: Homepage, https://github.com/roskideluge/clx
Project-URL: Repository, https://github.com/roskideluge/clx
Project-URL: Issues, https://github.com/roskideluge/clx/issues
Keywords: ai,llm,sql,spark,duckdb,sqlite
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28.0
Requires-Dist: tomli>=2.0.1; python_version < "3.11"
Dynamic: license-file

# clx — Minimal AI Resolver

`clx` is a lightweight Python library that forwards prompts to your own AI backend. It exposes a single primitive, `clx_query`, plus a handful of thin task helpers and SQL adapters for Spark, DuckDB, and SQLite. The library never talks directly to OpenAI/Ollama/etc.—you point it at any backend that implements the simple `/v1/query` contract.

## Installation
```bash
pip install clx-cli
```

## Backend configuration
Provide a backend URL via (in order of precedence):
1) Function argument: `backend_url="https://your-worker.workers.dev"`
2) Environment variable: `CLX_BACKEND_URL`
3) Config file: `~/.clx/config.toml`

Example `~/.clx/config.toml`:
```toml
backend_url = "https://my-backend.company.com"
```

### Default `/v1/query` contract
```
POST /v1/query
{
  "model": "...",
  "prompt": "...",
  "params": { ... }
}

Response:
{ "output": ... }  # string or JSON-serializable
```

### Pod/actor worker-style backend
If your backend uses the `pods/{podName}/actors/{actorId}/run` route (see example below), set `CLX_POD_NAME` and `CLX_ACTOR_ID` (or pass `pod_name`/`actor_id` to `clx_query`). Payload will be sent as `{"messages": [{"role":"user","content": prompt}], "metadata": {...}}` when `use_messages_payload=True`.

Example endpoint: `https://your-worker.workers.dev/pods/{podName}/actors/{actorId}/run`

## Core usage
```python
from clx import Cache, clx_query

result = clx_query(
    model="meta-llama-3-3-70b-instruct",
    prompt="Summarize: " + text,
    params={"max_tokens": 120, "temperature": 0.3},
    backend_url="https://your-worker.workers.dev",  # optional if CLX_BACKEND_URL/config is set
)
print(result)
```

### Optional caching
Caching is off by default. Pass a `Cache` instance to enable it (keyed by backend URL + model + prompt + params).
```python
from clx import Cache, clx_query

with Cache() as cache:
    summary = clx_query("meta-llama", "Summarize: " + text, cache=cache)
```

### Worker-style backend usage
```python
from clx import clx_query

result = clx_query(
    model="meta-llama-3-3-70b-instruct",
    prompt="What is 2+2?",
    backend_url="https://your-worker.workers.dev",
    pod_name="ed98786f-0342-4785-9be4-f0b9988ecd27",
    actor_id="3ec7a4b1-2e63-4c05-9ba5-25df471ceefc",
    use_messages_payload=True,
    metadata={"user_id": "user_123"},
)
print(result)  # e.g., "2+2 equals 4."
```

## Paseo backend optimization
clx is optimized to work cleanly with the Paseo backend contract:
- Supports pod/actor routes at `https://<paseo-worker>/pods/{podName}/actors/{actorId}/run`.
- Uses `use_messages_payload=True` to send `{"messages": [{"role":"user","content": prompt}], "metadata": {...}}` which matches Paseo’s expected shape.
- Accepts `response` or `output` fields so Paseo responses map directly to return values.
- Environment-friendly defaults: set `CLX_BACKEND_URL`, `CLX_POD_NAME`, `CLX_ACTOR_ID`, and `CLX_MODEL` (e.g., `@cf/meta/llama-3.1-8b-instruct`) in `.env` and invoke `clx_query` or `demo_backend_call.py`.

## Task helpers
All helpers forward to `clx_query` with light prompt templates:
- `clx_gen(model, prompt, **params)`
- `clx_summarize(model, text, **params)`
- `clx_translate(model, text, target_lang, **params)`
- `clx_classify(model, text, labels, **params)`
- `clx_extract(model, text, schema, **params)`
- `clx_similarity(model, a, b, **params)`
- `clx_fix_grammar(model, text, **params)`

Example:
```python
from clx import clx_summarize
summary = clx_summarize("meta-llama3", text, max_tokens=100)
```

## SQL adapters
Each adapter registers `clx_query` as a SQL function and returns strings (JSON is returned as a stringified payload when `expect_json=True`).

### Spark
```python
from clx.adapters.spark import register_clx_query
register_clx_query(spark, expect_json=True)

df_out = df.selectExpr(
    "clx_query('meta-llama3', CONCAT('Summarize: ', text), named_struct('max_tokens', 120)) AS summary"
)
```

### DuckDB
```python
import duckdb
from clx.adapters.duckdb import register_clx_query

con = duckdb.connect()
register_clx_query(con)

con.execute(\"\"\"
SELECT clx_query(
  'meta-llama3',
  'Translate: ' || text,
  '{"max_tokens": 80}'
) AS translation
FROM docs;
\"\"\")
```

### SQLite
```python
import sqlite3
from clx.adapters.sqlite import register_clx_query

conn = sqlite3.connect(":memory:")
register_clx_query(conn)

conn.execute("SELECT clx_query('meta-llama3', 'Summarize: ' || text, NULL) FROM messages;")
```

## Notes
- `clx_query` returns either a string or JSON (when `expect_json=True`, invalid JSON raises `ValueError`).
- Backend errors surface as `RuntimeError` with the status code.
- Keep your backend responsible for model selection, routing, and authentication.
