Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 29 additions & 1 deletion src/components/models/ModelDetailPage.astro
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import DeepgramAura from "~/components/models/code/DeepgramAura.astro";
import BgeRerankerBase from "~/components/models/code/Bge-Reranker-Base.astro";
import Flux1Schnell from "~/components/models/code/Flux-1-Schnell.astro";
import ClefCode from "~/components/models/code/ClefCode.astro";
import ClairicCode from "~/components/models/code/ClairicCode.astro";

interface Props {
model: ModelView;
Expand Down Expand Up @@ -77,6 +78,9 @@ const isClef = [
"@cf/cloudflare/clef-omni",
].includes(model.name);

// Clairic extraction models take typed fields, not chat messages.
const isClairic = model.name === "@cf/cloudflare/clairic";

// Legacy code-example overrides (only used when there is no catalog usage).
let CodeExamples: any = null;
if (!hasCatalogUsage) {
Expand Down Expand Up @@ -114,6 +118,9 @@ if (!hasCatalogUsage) {
if (isClef) {
CodeExamples = ClefCode;
}
if (isClairic) {
CodeExamples = ClairicCode;
}
}

const hasCodeExamples = hasCatalogUsage || !!CodeExamples;
Expand Down Expand Up @@ -145,7 +152,7 @@ function bannerAsideType(
// exclusion (gpt-oss is not in our slice — kept as a documented dead branch).
let hasPlayground =
model.task === "Text Generation" && model.hosting === "hosted";
if (model.name.includes("@cf/openai/gpt-oss") || isClef) {
if (model.name.includes("@cf/openai/gpt-oss") || isClef || isClairic) {
hasPlayground = false;
}

Expand Down Expand Up @@ -240,6 +247,7 @@ const headings: MarkdownHeading[] = [
hasRemainingExamples && { depth: 2, slug: "examples", text: "Examples" },
hasSchema && { depth: 2, slug: "parameters", text: "Parameters" },
hasSchema && { depth: 2, slug: "api-schemas", text: "API Schemas (Raw)" },
isClairic && { depth: 2, slug: "terms", text: "Terms" },
].filter(Boolean) as MarkdownHeading[];
---

Expand Down Expand Up @@ -687,4 +695,24 @@ const headings: MarkdownHeading[] = [
</div>
</Fragment>
)}

{isClairic && (
<Fragment>
<AnchorHeading depth={2} title="Terms" slug="terms" />
<p>
You may access or use this model at your sole discretion. Cloudflare
does not make any representations, warranties, or guarantees regarding
the model, including, but not limited to, as to its continued
availability and integrity. If Cloudflare makes the model available to
you, it is on an “AS IS” and “AS AVAILABLE” basis, and Cloudflare has no
obligation to indemnify you for any claims arising from your use of the
model. Cloudflare may cease providing it without entitling you to any
refund, credit, or other compensation. Cloudflare will not be directly
or indirectly responsible or liable to you in any manner, for any harms,
damages, loss, lost profits, special or consequential damages, or
claims, arising out of or in connection with the use of, or reliance on
the performance of the model.
</p>
</Fragment>
)}
</DocsLayout>
10 changes: 7 additions & 3 deletions src/components/models/ModelFeatures.astro
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,13 @@ const reasoning = formatModelReasoning(
<tr>
<td>Terms and License</td>
<td>
<a href={properties.terms} target="_blank">
link<span class="external-link"> ↗</span>
</a>
{String(properties.terms).startsWith("#") ? (
<a href={properties.terms}>link</a>
) : (
<a href={properties.terms} target="_blank">
link<span class="external-link"> ↗</span>
</a>
)}
</td>
</tr>
)}
Expand Down
157 changes: 157 additions & 0 deletions src/components/models/code/ClairicCode.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
---
import { z } from "astro/zod";
import { Code } from "~/components/ui/code";
import { Tabs, TabItem } from "~/components/ui/tabs";

type Props = z.infer<typeof props>;

const props = z.object({
name: z.string(),
});

const { name } = props.parse(Astro.props);

// Request body `model` selector is the last segment of the model ID.
const selector = name.split("/").at(-1);

const state =
"Invoice INV-2291 from Northwind Supply, issued 2026-09-02, net 30. 4 pallets at $250.00 each, shipping $80.00. Contact: Dana Ruiz, Priya Shah.";

const worker = `
export interface Env {
AI: Ai;
}

export default {
async fetch(request, env): Promise<Response> {
const response = await env.AI.run("${name}", {
model: "${selector}",
state: "${state}",
fields: {
invoice_number: { type: "text" },
due_date: {
type: "date",
instructions: "Issue date plus the net payment days",
},
total: {
type: "number",
decimals: 2,
unit: "USD",
instructions: "Amount due including shipping",
},
contacts: { type: "list", items: "text" },
po_number: { type: "text", instructions: "Purchase order number" },
days_to_pay: {
type: "estimate",
min: 0,
max: 120,
step: 1,
instructions: "Days after issue until this invoice is paid",
},
},
});

// response.answers.invoice_number -> exact span of the state, with character offsets
// response.answers.due_date -> date worked out from the issue date and terms
// response.answers.po_number -> null: the invoice has no PO number
// response.answers.days_to_pay -> median and 80% interval (p10 to p90)
return Response.json(response);
},
} satisfies ExportedHandler<Env>;
`;

const python = `
import os
import requests

ACCOUNT_ID = "your-account-id"
AUTH_TOKEN = os.environ.get("CLOUDFLARE_AUTH_TOKEN")

response = requests.post(
f"https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/ai/run/${name}",
headers={"Authorization": f"Bearer {AUTH_TOKEN}"},
json={
"model": "${selector}",
"state": "${state}",
"fields": {
"invoice_number": {"type": "text"},
"due_date": {
"type": "date",
"instructions": "Issue date plus the net payment days",
},
"total": {
"type": "number",
"decimals": 2,
"unit": "USD",
"instructions": "Amount due including shipping",
},
"contacts": {"type": "list", "items": "text"},
"po_number": {"type": "text", "instructions": "Purchase order number"},
"days_to_pay": {
"type": "estimate",
"min": 0,
"max": 120,
"step": 1,
"instructions": "Days after issue until this invoice is paid",
},
},
},
)
print(response.json())
`;

const curl = `
curl https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/ai/run/${name} \\
-X POST \\
-H "Authorization: Bearer $CLOUDFLARE_AUTH_TOKEN" \\
-d '{
"model": "${selector}",
"state": "${state}",
"fields": {
"invoice_number": { "type": "text" },
"due_date": { "type": "date", "instructions": "Issue date plus the net payment days" },
"total": { "type": "number", "decimals": 2, "unit": "USD", "instructions": "Amount due including shipping" },
"contacts": { "type": "list", "items": "text" },
"po_number": { "type": "text", "instructions": "Purchase order number" },
"days_to_pay": {
"type": "estimate",
"min": 0,
"max": 120,
"step": 1,
"instructions": "Days after issue until this invoice is paid"
}
}
}'
`;

// Real response to the request above (Workers AI wraps it in `result`); probabilities rounded.
const response = `
{
"model": "clairic",
"answers": {
"invoice_number": {"type": "text", "p_null": 3.8e-10, "span": [8, 16], "guess": "INV-2291", "confidence": 1, "value": "INV-2291"},
"due_date": {"type": "date", "p_null": 1.9e-08, "guess": "2026-10-02", "confidence": 0.9999, "value": "2026-10-02"},
"total": {"type": "number", "p_null": 2.3e-08, "guess": 1080, "confidence": 0.9999, "value": 1080},
"contacts": {"type": "list", "value": ["Dana Ruiz", "Priya Shah"], "spans": [[120, 129], [131, 141]], "confidence": 0.9999},
"po_number": {"type": "text", "p_null": 1, "span": [8, 16], "guess": "INV-2291", "confidence": 0.9995, "value": null},
"days_to_pay": {"type": "estimate", "value": 30, "mean": 29.535, "median": 30, "p10": 21, "p90": 38, "p_below": 5.9e-06, "p_above": 1.2e-05}
},
"truncated": false,
"usage": {"state_tokens": 61, "image_tokens": 0, "schema_tokens": 472, "input_tokens": 655, "output_tokens": 0, "passes": 3}
}
`;
---

<Tabs syncKey="workersAiExamples">
<TabItem label="TypeScript">
<Code code={worker} lang="ts" title="" />
</TabItem>
<TabItem label="Python">
<Code code={python} lang="py" title="" />
</TabItem>
<TabItem label="curl">
<Code code={curl} lang="sh" />
</TabItem>
</Tabs>

<Code code={response} lang="json" title="Response" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
title: "Introducing Clairic: an extraction model for typed values, now on Workers AI"
description: Clairic is a preview of a new class of System One models, extraction models. It extracts, computes, estimates, and forecasts values from your input, and returns typed answers with confidence scores in a single pass.
products:
- workers-ai
date: 2026-10-19
---

import { TypeScriptExample } from "~/components";

[`@cf/cloudflare/clairic`](/workers-ai/models/clairic/) is now available on Workers AI as a preview. Clairic is the first [extraction model](/workers-ai/concepts/extraction-models/): a System One model that answers with values instead of text. We intend to release the model with open weights soon.

Decision models such as [Clef](/workers-ai/models/clef/) choose among options you define up front. Many structured-output tasks do not have answers you can list in advance: an invoice total, a delivery date, the number of days a shipment is late, or what a repair will cost. Clairic handles these. You send a state and a schema of typed fields, and it returns one typed value per field. A value is read from the input, computed from it, or predicted beyond it.

Clairic does not generate tokens. It reads your input in one pass and decodes each answer under the rules of its type, so every answer is a valid value of the type you asked for, or `null` when the input does not determine it.

## Typed fields

Define as many fields as you need. Fields that do not fit beside the input in one pass are split across parallel passes, so there is no upper limit.

| Type | Returns |
| -------------------------- | ----------------------------------------------------------------------------------------- |
| `text` | An exact span of the input, with its character offsets |
| `number` | A number read or worked out from the input, honoring `integer` and `decimals` |
| `date`, `time`, `datetime` | Real calendar dates and valid times, in ISO form |
| `duration` | A length of time in the unit you choose |
| `list` | Every matching text, number, or date, in input order |
| `estimate` | A distribution inside your `min` and `max`, on your `step` grid, on a linear or log scale |
| `forecast` | One estimate per future step of a series, up to 64 steps |

Every scalar answer includes `p_null`, the probability that the input does not determine the value, and a `confidence` score. Estimates and forecasts include a median and an 80% interval (`p10` to `p90`). Set `options.include_quantiles` to also get all 99 percentiles. Use these to decide what to automate and what to send to review.

You can also pass up to four images alongside the state.

<TypeScriptExample>

```ts
const response = await env.AI.run("@cf/cloudflare/clairic", {
model: "clairic",
state:
"Invoice INV-2291 from Northwind Supply, issued 2026-09-02, net 30. 4 pallets at $250.00 each, shipping $80.00. Contact: Dana Ruiz, Priya Shah.",
fields: {
invoice_number: { type: "text" },
issued: { type: "date" },
due_date: {
type: "date",
instructions: "Issue date plus the net payment days",
},
total: {
type: "number",
decimals: 2,
unit: "USD",
instructions: "Amount due including shipping",
},
contacts: { type: "list", items: "text" },
},
});

// response.answers.invoice_number.value -> "INV-2291", with its span in the state
// response.answers.total.value -> the amount due, with p_null and confidence
```

</TypeScriptExample>

## Preview

The current endpoint is an early checkpoint, and we expect it to improve:

- **Arithmetic**: One- and two-step calculations, such as totals, differences, conversions, and date arithmetic, are reliable. Long chains of dependent steps and aggregations across many table rows are not yet. Extract the inputs with Clairic and do the math in code.
- **Estimates**: Clairic estimates best when the input contains evidence such as comparables, history, or descriptive text. Without evidence, the interval is wide.
- **Forecasts**: Forecasting is basic. Next-step forecasts follow recent patterns, but forecasts several steps out flatten toward the average, and Clairic does not anticipate events the series does not show, such as holidays. It does not replace a time-series model trained on your data.
- **Calibration**: Intervals are trained to be calibrated and are on most held-out sets, but they can run narrow on unfamiliar data.
- **Confidence**: Every answer is well formed, not necessarily correct. Use `confidence`, `p_null`, and the intervals to decide what to automate and what to send to review.
- **Dates**: Dates without a year are resolved from context. If a field depends on the current date, set `reference_date` or add a line such as `Today: 2026-10-19` to the state.

## Pricing

Clairic costs $0.10 per million input tokens. Because it generates no tokens, there is no output token charge.

## Get started

Use Clairic through the [Workers AI binding](/workers-ai/configuration/bindings/) (`env.AI.run()`) or the REST API at `/ai/run`. You can also use [AI Gateway](/ai-gateway/) with these endpoints.

For more information, refer to the [Clairic model page](/workers-ai/models/clairic/), [extraction models](/workers-ai/concepts/extraction-models/), [pricing](/workers-ai/platform/pricing/), and the [blog post](https://blog.cloudflare.com/clairic-extraction-model/).
Loading
Loading