A Python library for parallel LLM inference across providers, built on Polars DataFrames.
Polar Llama is a Python library that enables parallel inference calls to multiple Large Language Model providers through Polars dataframes. It streamlines batch processing of AI queries without serial request delays, making it ideal for data-intensive AI applications. 0.5.3 is a fix release: tag_taxonomy() works on OpenAI’s strict structured-output mode again, which changes the shape of the per-field thinking reasoning.
Send multiple inference requests in parallel without waiting for individual completions
OpenAI, Anthropic, Gemini, Groq, AWS Bedrock, and on-device MLX (Apple Silicon)
Dataframe-native tool calling: emission, batch-parallel execution, and synthesis are all ordinary columns
A DSPy-style engine (Signature, Predict, BootstrapFewShot, InstructionOptimizer) tunes prompts against labeled data
Pulls in mlx and mlx-lm; requires an Apple Silicon Mac and Python ≥ 3.10
Get started with a simple example
Two OpenAI Structured Outputs strict-mode violations, both fixed (#51)
tag_taxonomy() no longer fails on OpenAI (and other strict-schema providers) with invalid_request_error: 'required' ... Extra required key 'thinking' (#51). Two stacked OpenAI strict-mode schema violations were behind it, and both are fixed:
thinking reasoning is now a List[{value, reasoning}] instead of a Dict[str, str] keyed by value name, which strict mode rejects because a dynamic-key object has no fixed properties / required._pydantic_to_json_schema now strips sibling keywords from $ref nodes. Pydantic emits taxonomy fields as {"$ref": ..., "description": ...}, which strict mode rejects with "$ref cannot have keywords".Verified end-to-end with live OpenAI (gpt-4o-mini), Anthropic (claude-haiku-4-5), and Groq (llama-4-scout) calls.
One {value, reasoning} entry per candidate value
If you read thinking directly, update your code: it is now a list of fixed-shape structs, not a dict. Each entry’s value is the candidate being considered and reasoning explains why it does or does not apply. That is distinct from the field’s own value, which still holds the final selection. reflection, value, and confidence are unchanged. Taxonomy value names no longer need to be valid identifiers or unique dictionary keys.
_validate_strict_mode_schema now also warns when a user-supplied response_model has a Dict-typed field. Dict fields produce JSON schemas with dynamic keys (additionalProperties) that OpenAI Structured Outputs strict mode rejects. Use a nested model or a list of key/value items instead.
0.5.3 is cumulative: every feature shipped in 0.2.2 is still here, plus everything added across 0.3.0, 0.5.0, 0.5.1, 0.5.2.
Released 2026-06-10 to 2026-07-12
tools_to_response_model, mcp_tools, execute_tool_calls, tool_results_to_message: batch-parallel tool calling over an MCP server or a Python executor (0.3.0)
cache=True / CacheConfig shares a cached system prefix across rows via Anthropic cache_control, with 5-minute and 1-hour TTLs (0.3.0)
Signature, Predict, evaluate, BootstrapFewShot, InstructionOptimizer: DSPy-style instruction and few-shot tuning (0.3.0)
inference_local with server and in_process engines, collapsed prefix prefill, batched quantized KV cache (0.5.0), and an on-device prompt-tuning bridge (0.5.1)
Along the way: updated default models for every provider, OPENAI_BASE_URL / ANTHROPIC_BASE_URL overrides and POLAR_LLAMA_MAX_CONCURRENCY (0.3.0), and fixes for in-process Gemma 3n inference (0.5.2). See the 0.5.2 docs for the full details.
The agent loop unrolled into ordinary dataframe columns
Declare a task, then let an optimizer tune instructions or mine few-shot demos
Batched generation via mlx-lm, no API keys, no network
Process customer feedback at scale
Generate embeddings, then find nearest neighbors with HNSW
Classify documents with reasoning, reflection, and confidence scores
Six inference targets: five hosted providers plus on-device MLX
Default model: gpt-4o-mini
Default model: claude-opus-4-8; supports cache=True for prompt caching
Default model: us.anthropic.claude-haiku-4-5-20251001-v1:0; region resolved from AWS_REGION / AWS_DEFAULT_REGION
Default model: gemini-2.5-flash; native system_instruction and JSON-schema structured outputs
Default model: llama-3.3-70b-versatile
No API key, no network: the server engine points at a local OpenAI-compatible endpoint, and in_process drives mlx-lm directly
Core expressions exported from polar_llama
| Function | Purpose |
|---|---|
| inference_async(expr, *, provider, model, response_model, cache, system_prompt) | Parallel async inference; accepts cache=True/CacheConfig and system_prompt for provider-native prompt caching |
| inference(expr, *, provider, model, response_model) | Synchronous inference (deprecated in favor of inference_async) |
| inference_messages(expr, *, provider, model, response_model, cache) | Multi-turn conversation inference over JSON or List(Struct) message arrays |
| string_to_message(expr, *, message_type) | Convert text to a {role, content} message |
| combine_messages(*exprs) | Merge message columns/arrays into one ordered conversation |
| tag_taxonomy(expr, taxonomy, *, provider, model) | Classify text against a taxonomy with reasoning, reflection, and confidence |
| embedding_async(expr, *, provider, model) | Parallel embedding generation (OpenAI, Gemini, Bedrock) |
| cosine_similarity / dot_product / euclidean_distance(vec1, vec2) | Rust-powered vector similarity metrics |
| knn_hnsw(query_expr, reference_expr, *, k) | Stateless approximate nearest-neighbor search via HNSW |
| mcp_tools(transport, *, timeout_s) | Fetch tool definitions from an MCP server (tools/list) |
| tools_to_response_model(tools, *, model_name) | Build a Pydantic emission schema so the LLM emits structured tool calls |
| execute_tool_calls(expr, *, transport, executor, tools, concurrency, timeout_s) | Run every emitted call of every row in parallel; failures are data |
| tool_results_to_message(expr, *, role) | Render tool results as a message for the synthesis inference pass |
| Signature / Predict / evaluate / BootstrapFewShot / InstructionOptimizer | DSPy-style prompt optimization engine (polar_llama.optimize) |
| col(...).llama.inference_local(*, model, system, engine, base_url, max_tokens, temperature, top_p, stop) | On-device inference on Apple Silicon via mlx-lm |
| polar_llama.local.make_local_inference_fn(model, *, engine, collapse, max_tokens, ...) | Build an inference_fn that backs the optimizer with on-device Gemma 3n |
Every expression is also available on the fluent .llama namespace (pl.col("text").llama.inference_async(...), .llama.to_message(...), .llama.embedding(...), and so on).
Set up your API keys and overrides in a .env file:
Share a cached system prefix across rows (Anthropic cache_control):
Run tests with configured providers:
Process large datasets with AI insights: sentiment analysis, classification, entity extraction with validated structured outputs
Let the LLM call databases, internal APIs, or MCP servers at scale, with every call, result, and retry as an ordinary dataframe column
Run classification, extraction, or tuning on local models with no API keys and no data leaving the machine
Bootstrap few-shot demos or search for better instructions against a labeled dataset, entirely offline or on-device
Licensed under MIT.
Questions or issues? Open one on GitHub.