# TypeSafe System One ## Overview `typesafe_eval` is a native inference layer for the [TypeSafe System One API](https://docs.typesafe.ai/api), written in Rust (`src/model_client/typesafe.rs` + `src/typesafe_expr.rs`) and exposed to Python as a Polars expression (`polar_llama/typesafe.py`). TypeSafe is deliberately **not** a chat-completions provider, so it does not implement the `ModelClient` trait the OpenAI/Anthropic/Gemini/Groq/Bedrock clients share. There is no prompt and no free-text completion. One request carries a single `state` plus a map of typed `questions`, and returns one typed `answer` per question: | Question | Asks | Answer | |---|---|---| | `noul` | a yes/no question | probability in `[0, 1]` | | `choice` | pick one of a closed set | the pick + a probability per option + confidence | | `score` | rate against ordered levels | a probability-weighted value + a probability per level + confidence | Because the answers are typed and calibrated rather than parsed out of prose, they land in a DataFrame as ordinary numeric and string columns you can filter, sort, threshold, and join on -- which is why this belongs in polar-llama at all. ``` df: one state per row | v typesafe_eval(pl.col("message"), questions={...}) | -- ONE POST /v1/systemone per row, carrying | every question; bounded by | POLAR_LLAMA_MAX_CONCURRENCY v Struct{ is_urgent: f64, department: str, department_confidence: f64, ..., _error: str } | v .unnest("ts") -- flat, typed columns | v df.filter(pl.col("department_confidence") > 0.9) -- your code decides ``` ## Why one request per row, not one per question Every question in the mapping rides in the *same* request. TypeSafe's own ["speculative fan-out"](https://docs.typesafe.ai/patterns/fan-out) guidance is to ask everything you might want up front and let your code decide afterwards what to read -- batching is dramatically cheaper and faster than one call per question, because the state is only paid for once. A per-question expression would re-send the state once per column, so the API is deliberately shaped so that you cannot accidentally do that. ## Usage ```python import polars as pl from polar_llama import typesafe_eval, noul, choice, score df = pl.DataFrame({"message": [ "Help! My payouts have been failing for 3 days.", "Hi, just wondering what your enterprise pricing looks like.", ]}) out = df.with_columns( ts=typesafe_eval( pl.col("message"), questions={ "is_urgent": noul( "Does this convey urgency?", true="Explicitly time-sensitive", false="No urgency expressed", ), "department": choice( "Which team should handle this?", { "billing": "Payments, invoicing, refunds", "technical": "Bugs, outages, integrations", "sales": "Pricing, upgrades, new accounts", }, ), "frustration": score( "How frustrated is the customer?", ["Calm", "Frustrated", "Very angry"], ), }, ) ).unnest("ts") ``` ``` ┌──────────────┬───────────┬────────────┬───────────────────────┬─────────────┬────────────────────────┬────────┐ │ message ┆ is_urgent ┆ department ┆ department_confidence ┆ frustration ┆ frustration_confidence ┆ _error │ │ str ┆ f64 ┆ str ┆ f64 ┆ f64 ┆ f64 ┆ str │ ╞══════════════╪═══════════╪════════════╪═══════════════════════╪═════════════╪════════════════════════╪════════╡ │ Help! My … ┆ 0.95 ┆ billing ┆ 0.79 ┆ 1.04 ┆ 0.93 ┆ null │ │ Hi, just … ┆ 0.06 ┆ sales ┆ 1.0 ┆ 0.0 ┆ 1.0 ┆ null │ └──────────────┴───────────┴────────────┴───────────────────────┴─────────────┴────────────────────────┴────────┘ ``` The fluent namespace works too: ```python df.with_columns(ts=pl.col("message").llama.typesafe_eval(questions={...})).unnest("ts") ``` ## Output schema Columns are emitted in the order the questions were declared. The dtype is derived from the question type *before* any request is made, so `.collect_schema()` on a LazyFrame resolves the full output shape without spending a token. | Question type | Columns | |---|---| | `noul` | ``: Float64 | | `choice` | ``: String, `_confidence`: Float64 | | `score` | ``: Float64, `_confidence`: Float64 | Plus: * `_error`: String -- always present, null on success. * `probabilities=True` -- adds `_p_