From 5c39b2c14d154b4e1bcd64f51f6f7feac0beccff Mon Sep 17 00:00:00 2001 From: Sean Brar Date: Wed, 17 Jun 2026 02:28:14 -0700 Subject: [PATCH] docs: refresh the README for the v2 API The landing-page examples still used the v1 ResultEnvelope surface and would not run on v2. Align them with the shipped API: - run() returns Output (result.text); run_many()/collect_deferred() return OutputCollection (.answers); drop dict-subscript access. - Structured output kwarg is output=, not response_schema. - Drop the nonexistent defer_many() from the entry-point table. - Note local multimodal (text/image/audio) and optional model. Add a "Multi-Turn Threads and Agent Loops" section covering interact() and Session, plus links to the agent-loop and 2.0 migration guides. --- README.md | 51 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5a4a5f4a..fd913f33 100644 --- a/README.md +++ b/README.md @@ -36,19 +36,21 @@ result = asyncio.run( config=Config(provider="gemini", model="gemini-2.5-flash-lite"), ) ) -print(result["answers"][0]) +print(result.text) # Revenue grew 18% YoY to $4.2B, driven by cloud services. Operating # margins improved from 29% to 34%. Management's $2B buyback and raised # guidance signal confidence in sustained growth. ``` -`run()` returns a `ResultEnvelope`: `answers` holds one entry per prompt. +`run()` returns an `Output`: `result.text` is the answer, with `result.structured`, +`result.usage`, and other facets alongside it. To use OpenAI instead: `Config(provider="openai", model="gpt-5-nano")`.
For Anthropic: `Config(provider="anthropic", model="claude-haiku-4-5")`.
For OpenRouter: `Config(provider="openrouter", model="google/gemma-3-27b-it:free")`.
-For a self-hosted OpenAI-compatible server (text-only): +For a self-hosted OpenAI-compatible server (text, image, and audio): `Config(provider="local", model="gemma3:4b", base_url="http://localhost:11434/v1")`. +Single-model servers can omit `model`. For a full walkthrough (install, key setup, first result), see [Getting Started](https://polluxlib.dev/getting-started/). @@ -59,7 +61,8 @@ For a full walkthrough (install, key setup, first result), see |---|---| | Ask one prompt and get an answer now | `run()` | | Ask many prompts against shared source(s) | `run_many()` | -| Submit non-urgent work and collect it later | `defer()` / `defer_many()` | +| Hold a multi-turn thread or run a tool-using agent loop | `interact()` / `Session` | +| Submit non-urgent work and collect it later | `defer()` | Pollux keeps realtime and deferred work on separate entry points. If the result can wait, submit it once, persist the handle, and collect the same @@ -78,7 +81,7 @@ Gemini-specific video clipping and FPS controls are available via `Source.with_gemini_video_settings(...)`; see the sending-content docs for the intended scope. -Need structured output? Pass a Pydantic model as `response_schema` and get a +Need structured output? Pass a Pydantic model as `output` and get a validated instance alongside the raw text. Switching providers is a config change: `provider="gemini"` to `provider="openai"`. @@ -97,15 +100,47 @@ envelope = asyncio.run( config=Config(provider="gemini", model="gemini-2.5-flash-lite"), ) ) -for answer in envelope["answers"]: +for answer in envelope.answers: print(answer) ``` +`run_many()` returns an `OutputCollection`: `answers` is the per-prompt text in +input order, with `outputs`, `structured`, `usage`, and `status` alongside it. + Add more sources when each prompt should see the same shared context. For per-file collection work, wrap `run_many()` in your own outer loop over files; that gives you one result record per file while Pollux handles each file's prompt set. +## Multi-Turn Threads and Agent Loops + +When you need conversation history or tool calls, use `interact()` over an +`Environment` and `Input`. A `Session` reuses one provider across turns: + +```python +import asyncio +from pollux import Config, Environment, Input, Session + +async def main(): + env = Environment(instructions="Be concise.") + async with Session(Config(provider="anthropic", model="claude-haiku-4-5")) as session: + first = await session.interact(env, Input("Name a primary color.")) + print(first.text) + + # Continue the same thread from the prior turn's continuation: + second = await session.interact( + env, Input("Now name its complement.", continuation=first.continuation) + ) + print(second.text) + +asyncio.run(main()) +``` + +`Output.tool_calls` exposes any tools the model wants to run; return their +results on the next `Input` to drive an agent loop. For tool declarations, +dispatch, and streaming, see +[Building an Agent Loop](https://polluxlib.dev/agent-loop/). + ## When the Work Can Wait Deferred delivery is for long fan-out work, backfills, and scheduled analysis @@ -134,7 +169,7 @@ handle = asyncio.run( snapshot = asyncio.run(inspect_deferred(handle)) if snapshot.is_terminal: result = asyncio.run(collect_deferred(handle)) - print(result["answers"][0]) + print(result.answers[0]) ``` In production code, persist `handle.to_dict()` and restore it later with @@ -173,7 +208,9 @@ For `provider="local"`, no API key is required; point `base_url` (or - [Getting Started](https://polluxlib.dev/getting-started/): first result in 2 minutes - [Core Concepts](https://polluxlib.dev/concepts/): mental model and vocabulary +- [Building an Agent Loop](https://polluxlib.dev/agent-loop/): multi-turn threads, tool calls, and streaming - [Submitting Work for Later Collection](https://polluxlib.dev/submitting-work-for-later-collection/): deferred lifecycle API +- [Migrating to 2.0](https://polluxlib.dev/migrating-to-v2/): moving from the v1 API - [Building With Deferred Delivery](https://polluxlib.dev/building-with-deferred-delivery/): when deferred is worth it - [API Reference](https://polluxlib.dev/reference/api/): entry points and types - [Cookbook](https://polluxlib.dev/reference/cli/): runnable end-to-end recipes