FASCICLE
Why Primitives Providers Docs GITHUB
Composable agentic toolkit

Compose agents like plain values.

Compose LLM calls, tools, and plain functions into a Step<i, o>. Wire eighteen primitives together over one generate surface. No magic. Just logic.

$ pnpm add fascicle zod
{{ fileName }}
{{ tk.t }}
ONE GENERATE SURFACE · 8 PROVIDERS
{{ c.name }}
18 PRIMITIVES · EVERY COMP TAKES A STEP, RETURNS A STEP
{{ p.name }}
{{ selectionNote }}
01 · WHY FASCICLE

Not a framework. A toolkit.

Most agent libraries are frameworks — they own your control flow, hide it behind decorators, and tie you to one vendor. Fascicle is the opposite: a small set of plain, typed functions you compose yourself. You keep the architecture; it just makes the hard parts composable.

No vendor lock-in

Anthropic, OpenAI, Bedrock, Google, OpenRouter — or a local model on your laptop — all sit behind one generate. Swap providers by changing a string, not your code.

No magic

No decorators, no dependency injection, no ambient scheduler deciding when your code runs. A flow is plain functions you can read top to bottom, set a breakpoint in, and unit-test.

You own the architecture

A toolkit, not a framework. Fascicle never dictates your app's shape — drop a Step into the code you already have. No rewrite, no buy-in, no ORM telling you how to build.

Composition, all the way down

Every primitive takes a Step<i,o> and returns a Step<i,o>. Wrap one in retry, fallback, or ensemble and it's still just a step — behavior layers on, the type never changes.

02 · THE HARNESS

You hold the reins.

The harness is the product. The model is a service it calls. Your outer program is ordinary, deterministic code that you can read, test, and reason about — it reaches for a model only where real judgment is needed, and runs token-free everywhere else.

That's the opposite of handing the reins to a model and hoping it orchestrates the others. Real engineering lives in the harness — Fascicle just gives you the primitives to compose it.

✕ A MODEL IN THE DRIVER'S SEAT
Reins in the model's hands
model
model
model
tokens spent on every hop
A model decides every next move at runtime.
Every hop spends tokens — latency and cost you can't predict.
Nondeterministic. Hard to test, replay, or debug.
✓ AN ENGINEER IN THE DRIVER'S SEAT
Reins in your hands
fn
model_call
branch
fn
one model call, surrounded by token-free code
+Your script holds the control flow — plain, readable, testable.
+model_call only where real judgment is needed.
+Everything else is token-free: fast, cheap, deterministic.
This shape is the future: boring, predictable code that calls models sparingly — not models burning tokens to orchestrate other models.
03 · ADD A MODEL CALL

A model is just another step.

model_call turns a provider into a Step<i, o> — so every primitive you've already seen applies to it. Wrap it in retry, fan it out with map, race it with fallback. There's no second API for “calling a model carefully.”

It also threads the tedious parts for you — cancellation through ctx.abort, tracing through ctx.trajectory, and streaming chunks — so your flow stays pure composition and never imports a provider SDK.

Read the concepts →
brief-flow.ts
import { create_engine, model_call, run, sequence, step } from 'fascicle'

const engine = create_engine({
providers: { anthropic: { api_key: process.env.ANTHROPIC_API_KEY! } },
})

const flow = sequence([
step('brief', (topic) => `Write a 2-sentence brief on: ${topic}`),
model_call({ engine, model: 'claude-sonnet-4-6', system: 'No preamble.' }),
step('extract', (r) => r.content),
])

try {
console.log(await run(flow, 'Rust ownership'))
} finally {
await engine.dispose()
}
04 · COMPOSITION

Add capability by wrapping — never by rewriting.

Because every composer takes a Step and returns a Step, a composition is just another Step. There's no privileged “top level,” no interface that widens as a flow grows — a thousand nested layers still type as Step<i, o>.

So you harden a flow the way you'd wrap a function. Need retries, a fallback, or three-model voting? Wrap the step you already have. What's inside doesn't change; what calls it doesn't change. No refactor, no bespoke glue for every feature — just one more layer.

Step<i, o> → Step<i, o>
retry + survives flaky providers
adversarial + critiques & revises
ensemble + runs 3, keeps the best
step
step
step
your logic
Three capabilities added. The step in the middle was never touched.
05 · PROVIDERS

One generate surface, eight providers.

Your logic shouldn't care who serves the tokens. Write a flow once and point it at Anthropic, Bedrock, or a model running on your laptop — by changing a string. Develop against free local models, ship on a compliant cloud, fall back to another vendor mid-outage; the code never moves.

Aliases resolve, a provider-neutral effort knob translates per vendor, and supports(capability) lets you degrade gracefully instead of failing at runtime. Every SDK is an optional peer — install only the ones you call.

PROVIDER text tools schema streaming image reasoning
{{ row.name }} {{ row.text }} {{ row.tools }} {{ row.schema }} {{ row.streaming }} {{ row.image }} {{ row.reasoning }}
✅ supported · — not in v1 (ollama, lmstudio, claude_cli have no image input or reasoning effort)

Build your first flow.

Install, wire a sequence, run it as a value. The docs walk you from a one-liner to a full agentic harness.

$ pnpm add fascicle zod
Open the docs →
FASCICLE

Composable TypeScript toolkit for agentic workflows. Compose LLM calls, tools, and plain functions into a Step<i, o>.

Apache-2.0 · © 2026
MODULES
core engine observability stores