Metadata-Version: 2.4
Name: engini
Version: 0.7.1
Summary: Engini SDK — agent-first ergonomic layer over the Engini Public API
Project-URL: Homepage, https://github.com/engini/engini-sdk
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.9
Requires-Dist: engini-client<0.5.0,>=0.4.0
Requires-Dist: httpx>=0.27
Requires-Dist: pydantic>=2.6
Provides-Extra: all
Requires-Dist: tomli-w>=1.0; extra == 'all'
Requires-Dist: tomli>=2.0; (python_version < '3.11') and extra == 'all'
Requires-Dist: typer<1.0,>=0.12; extra == 'all'
Provides-Extra: cli
Requires-Dist: tomli-w>=1.0; extra == 'cli'
Requires-Dist: tomli>=2.0; (python_version < '3.11') and extra == 'cli'
Requires-Dist: typer<1.0,>=0.12; extra == 'cli'
Description-Content-Type: text/markdown

# engini

Agent-first Python SDK for the [Engini](https://engini.io) Public API — discover tools,
execute them against your connected apps, and wrap them as LLM tool definitions.

```bash
pip install engini            # SDK
pip install 'engini[cli]'     # SDK + the `engini` CLI
```

## Quickstart

```python
from engini import Engini

client = Engini(api_key="eng_…")          # or set ENGINI_API_KEY

# Discover canonical tool schemas
tools = client.tools.get(applications=["salesforce"], search="accounts", limit=5)

# Execute a tool against a connection
conn_id = next(c.connection_id for c in client.connections.list(application="salesforce"))
result = client.tools.execute(
    "salesforce_getrecords", {"sobject": "Account"}, connection_id=conn_id
)
print(result.output)
```

JWT auth is the fallback: `Engini(token="<jwt>", company_token="<id>")`, or set
`ENGINI_API_TOKEN` / `ENGINI_COMPANY_TOKEN`. With an API key the company is bound to the key,
so no company token is needed. Point at another host with `Engini(..., base_url=…)`.

## Use with an LLM

`Provider` adapters wrap canonical schemas into vendor tool definitions **client-side, with no
vendor SDK dependency**. OpenAI is the default; Anthropic is also available.

```python
# Bind applications → connections once, then drive a tool-calling loop
toolset = client.toolset(tools=["salesforce_getrecords"], connections={"salesforce": "Prod"})

openai_tools = client.provider.wrap_tools(toolset.tools())   # plain OpenAI tool-JSON dicts
# … send openai_tools to the model, get a response …
results = toolset.handle_tool_calls(llm_response)            # runs the calls, returns results
```

`client.toolset(...)` builds a local toolset (no I/O until used) or loads a server one via
`toolset_id=…`.

## Files

Tools whose `input_schema` marks a field `"format": "engini/file"` accept files. Wrap a file
with `engini.File` and pass it as the field value — the SDK base64-encodes it into the
`{base64_content, mime_type, filename}` wire shape. A field can take a single file or a list,
per the tool's schema.

```python
from engini import Engini, File

client = Engini(api_key="eng_…")
client.tools.execute(
    "doc_summarize",
    {
        "document": File.from_path("report.pdf"),                  # single file
        "attachments": [File.from_path("a.png"), File.from_path("b.png")],  # list of files
    },
    connection_id=conn_id,
)
```

`File.from_path` infers the filename and mime type; `File.from_bytes(data, filename=…,
mime_type=…)` and `File.from_base64(…)` cover in-memory content.

In the LLM loop an agent can't produce base64, so file fields are presented to it as string
fields. Register the files you'll allow and let the model reference one by key:

```python
results = toolset.handle_tool_calls(
    llm_response, files={"report": File.from_path("report.pdf")}
)
```

## What this adds over the raw REST client

Built on the autogenerated [`engini-client`](https://pypi.org/project/engini-client/), the SDK
adds what the generated client deliberately lacks: typed errors (the `EnginiError` family),
retry/backoff, auto-pagination, pluggable auth (`ApiKeyAuth` / `BearerAuth`), `Provider`
adapters for OpenAI/Anthropic, and the ergonomic `Toolset` object.

## Command-line interface

The `engini[cli]` extra ships an `engini` command — a machine-first CLI over the same surface.

```bash
engini --help     # login · logout · whoami · tools · applications · connections · connect
```

```bash
engini login --api-key eng_…                 # or run interactively on a TTY
engini whoami                                 # who am I / which company
engini applications list --available          # discover connector apps
engini tools list --application salesforce    # discover tools
engini tools call salesforce_getrecords --args '{"sobject":"Account"}' --connection <id>
engini tools call doc_summarize --args '{}' --file document=@report.pdf   # attach a file
engini tools result <handle> --select records.Id   # drill into a large result
engini connect                                # open the connections page in your browser
```

Output is machine-first: human-readable on a TTY, **compact JSON when piped**, pretty JSON with
`--json`. Every command also accepts `--quiet` and `--schema` (a machine-readable arg schema),
and mutating commands accept `--dry-run`. Exit codes are a stable contract
(`0` ok · `2` usage · `3` auth · `4` not found · `5` validation · `124` timeout).

**Large results.** Tool outputs can be huge, and an agent shouldn't pay tokens for data it
hasn't asked for. When a `tools call` result exceeds `--max-bytes` (default `4096`), it is
spilled to a local sandbox (`$XDG_CACHE_HOME/engini/results/`) and the command prints a compact
envelope — `{handle, data_size, shape, preview, hint}` — instead of the full payload. Drill in
on demand with `engini tools result <handle>`: `--select <dot.path>` (maps over lists),
`--fields a,b` (project keys), `--offset/--limit` (page a list), or `--full`; `--list` / `--clear`
manage the store. `--inline` (or `--max-bytes 0`) forces the full inline result; `--raw` / `--llm`
emit full output and bypass the sandbox. The store auto-prunes to the most-recent 20 results,
dropping anything older than 24h, so a handle is short-lived — drill in during the same session.

Credentials and host config live in `$XDG_CONFIG_HOME/engini/config.toml`
(`%APPDATA%\engini\config.toml` on Windows), written `0600`. Resolution order is
**flags > environment > config**; the env vars are `ENGINI_API_KEY` (preferred) or
`ENGINI_API_TOKEN`, plus `ENGINI_COMPANY_TOKEN`.

Source & docs: <https://github.com/engini/engini-sdk>
