Metadata-Version: 2.5
Name: render-lab-tasks-llm
Version: 0.1.1
Summary: Provider-agnostic LLM tasks, embeddings, skills, and cost reporting for Render Workflows
Project-URL: Repository, https://github.com/render-lab/render-tasks-python
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: anthropic<1,>=0.125
Requires-Dist: httpx<0.29,>=0.28
Requires-Dist: openai<3,>=2.54
Requires-Dist: redis<7,>=6.4
Requires-Dist: render==1.0.1
Description-Content-Type: text/markdown

# render-lab-tasks-llm

Provider-agnostic LLM tasks for Render Workflows. Version 0.1.1 implements all
13 registered tasks. Version 0.1.0 contained only classification and summarization.

Install from [PyPI](https://pypi.org/project/render-lab-tasks-llm/) with Python 3.12+:

```sh
pip install render-lab-tasks-llm==0.1.1
```

Import `app`, `classify`, `classify_impl`, `summarize`, and `summarize_impl` from
`render_lab_tasks_llm.tasks`. Combine pack apps with `Workflows.from_workflows`.
Run wrapped tasks with `await ctx.run(classify, input)`; inject `LlmDeps(chat=...)`
into the raw implementations for tests or custom task wrappers.

## Contracts

`classify` accepts `text`, `labels` (objects with `name` and optional `description`),
optional `maxLabels` (default 3), `model`, and `ledger`. It returns `labels`,
optional `reasoning`, resolved `model`, and optional `usage`. Unknown/non-string
labels are removed, duplicates are removed before capping, and malformed model
JSON yields an empty label list, matching TypeScript. This is prompt-based
classification, not a guarantee of semantic correctness.

`summarize` accepts `text`, optional `instructions`, `maxWords` (default 120),
`model`, and `ledger`. It returns trimmed `summary`, resolved `model`, and optional
`usage`. The word limit is a prompt target, not hard truncation.

Missing or null optional defaults behave like the TS implementation. Python
additionally rejects negative, boolean, and nonintegral `maxLabels`/`maxWords`
before calling a provider. Zero is valid. Optional result keys are omitted when
unavailable. Unknown model prices do not become zero-dollar estimates.

## Providers and environment

Credentials and configuration are read when a task runs, never during imports.

| Variable | Purpose |
| --- | --- |
| LLM_MODEL | Default provider-prefixed model; otherwise `anthropic/claude-opus-4-8`, matching the TS snapshot |
| OPENAI_API_KEY | Direct `openai/` models, such as `openai/gpt-4o-mini` |
| ANTHROPIC_API_KEY | Direct `anthropic/` models |
| GEMINI_API_KEY | Direct `google/` or `gemini/` models through Google's OpenAI-compatible endpoint |
| GOOGLE_GENERATIVE_AI_API_KEY | Fallback Google key; OpenAI keys are never used for direct Google requests |
| LLM_BASE_URL | Explicit OpenAI-compatible gateway; preserves the full model identifier |
| LLM_API_KEY | Preferred key for gateways and direct OpenAI; falls back to OPENAI_API_KEY |
| REDIS_URL | Required only for a nonempty `ledger` without an injected cost-ledger port |
| LLM_PRICING | JSON price overrides keyed by provider-prefixed model; values contain inputUsdPerMTok and optional outputUsdPerMTok |
| LLM_COST_LEDGER_TTL_SECONDS | TTL of at least one second; default 172800, rounded down |

The default model and estimated pricing table are copied from the pinned TS
snapshot, not a claim of current pricing or availability. Override `LLM_MODEL`
for your account. The example explicitly uses `openai/gpt-4o-mini`.

## Retries, usage, and cost tracking

Provider calls stream internally and assemble their text before returning JSON.
Direct OpenAI uses max_completion_tokens; gateways/Google use max_tokens.
Official SDK and HTTP retries are disabled; Render owns five retries with a
2000 ms base delay and 2x backoff. Clients close after each call.

Usage includes available input/output token counts and an estimated dollar cost
when known. Output accounting preserves the TS max(completion, total-prompt)
rule. Estimates ignore caching discounts and price tiers; they are not invoices.

A nonempty `ledger` appends a JSON cost record to Redis list `llm:cost:<ledger>`
with TTL, compatible with the TS format. Invalid ledger configuration fails
before provider spend. Pricing failures without a ledger and append/cleanup
failures after spend warn without failing the task, avoiding rebilling solely
to repair observability. Inject `CostLedgerPort` to use another store. The
default Redis adapter also disables client retries.

## Complete task surface

The pack also exports `chat_turn` (`llm.chat`), `complete`, `extract`,
`extract_strict`, `translate`, `moderate`, `rerank`, `embed`, `load_skill`,
`open_cost_ledger`, and `cost_report`, with their injectable raw implementations.
`with_ledger(ctx, ledger)` binds the ledger while dispatching durable child runs.
Chat/completion preserve `stopReason` so callers can detect truncated output.

`LLM_EMBED_MODEL` overrides the embedding default. `SKILLS_PATH` changes installed
skill search directories. Skills load text only from GitHub, URLs, vendored files,
or installed names; they never execute scripts. `REDIS_URL` is required only for
cost-ledger IO; `LLM_COST_LEDGER_TTL_SECONDS` controls record expiry.
Structured output uses forced tools; OpenAI-compatible calls use a non-streaming
request so Gemini tool calls survive its different finish-reason behavior.
Google embeddings use Google credentials, fixing the TS adapter's accidental
OpenAI-key selection (tracked as a deliberate difference in ADR-0019).
