Get started
Run the demo app
Install and start the NestJS demo application, set only the environment variables you actually need, and run the end-to-end flow scenarios.
The demo is a NestJS + Fastify service that registers the example flows, exposes them over HTTP, and carries the end-to-end scenarios used to validate them. It is the fastest way to read real PicoFlow code that runs.
Get the source
git clone https://github.com/picoflowio/pico-demo
cd pico-demo
yarn install
npm install
The demo depends on @picoflow/core. In this repository the dependency is wired to a local
staging build of the library rather than to the published package:
"@picoflow/core": "file:../picoflow/npmlib/staging/lib"
If you are working from the monorepo layout, build the library before installing the demo:
npm --prefix ../picoflow run build:locallib
The demo also exposes that as a script, npm run build:picoflow. If you are working from a
standalone clone, replace the file: dependency with the published version.
Environment variables
Copy .env-example to .env and fill in only what your target flow needs.
Always required
| Variable | Notes |
|---|---|
PICOFLOW_KEY |
Runtime license token. Every flow fails on its first model response without it. |
Per flow
| Flow | Default model | Key required |
|---|---|---|
BasicFlow |
openai:gpt-4o-mini, with gpt-5 and gpt-5.1 step overrides |
OPENAI_API_KEY |
HotelFlow |
openai:gpt-4o, with gpt-5.1 step overrides |
OPENAI_API_KEY |
InvoiceFlow |
google:gemini-2.5-flash, with a gemini-3.1-pro-preview step override |
GEMINI_API_KEY |
SupportFlow |
openai:gpt-4o |
OPENAI_API_KEY |
HomeInsuranceQuoteFlow |
openai:gpt-4o |
OPENAI_API_KEY |
EmployeeBenefitsFlow |
openai:gpt-4o, with gpt-5.1 overrides for plan selection, elections, and review |
OPENAI_API_KEY |
app.module.ts also constructs Anthropic and NVIDIA adapters. Registering an adapter with
an undefined API key is harmless; the credential is only used when a flow or step actually
selects that provider. The other keys in .env-example — MOONSHOT_API_KEY, ZAI_API_KEY,
OPENROUTER_API_KEY, OLLAMA_BASE_URL — correspond to
built-in adapters that are commented out in app.module.ts.
Session storage
SESSION_STORE=SQLITE
SQLITE_PATH=ignore/session/session.sqlite
SESSION_STORE selects the backend and accepts MEMORY (the default), SQLITE, MONGO,
COSMO or COSMOS. Session-idle policy belongs to the Flow’s
onRestoreSessionDoc() hook; it is not an environment setting.
The shipped .env-example sets DOCUMENT_DB=COSMO. The library reads SESSION_STORE, not DOCUMENT_DB. If you only set DOCUMENT_DB you will silently get the in-process MEMORY store, and every session will disappear on restart. Set SESSION_STORE.
SQLite is the recommended local durable store. Relative SQLITE_PATH values resolve from
the project root. For MongoDB or Cosmos DB, fill in the corresponding block:
MONGODB_NAME=picoflow
MONGODB_COLLECTION=sessions
MONGODB_URL=mongodb://localhost:27017/?directConnection=true
COSMODB_URL=http://localhost:8081/
COSMODB_KEY=...
COSMODB_ID=picoflow
COSMODB_SESSION_ID=sessions
Batch mode only
SELF_URL is not in .env-example, but concurrentSteps(...) needs it: the coordinator
fans work out by making HTTP calls back into this same application.
SELF_URL=http://localhost:8000/ai/run
Build and start
| Script | What it does |
|---|---|
npm run build |
Delegates to build:app, which runs nest build, then copies json, md, png and pdf assets into dist/. |
npm run start:dev |
nest start --watch. The normal development loop. |
npm run start |
Builds, then runs start:prod. |
npm run start:prod |
node --enable-source-maps dist/main.js. |
npm run typecheck |
tsc --project tsconfig.contract.json. |
npm run start:dev
The service listens on port 8000 and binds 0.0.0.0.
The postbuild asset copy matters. HotelFlow, InvoiceFlow, HomeInsuranceQuoteFlow, and EmployeeBenefitsFlow load prompt files, configuration JSON, catalogs, or sample documents from disk at runtime, so running dist/main.js after a bare nest build will fail to find them.
Which flows are registered
src/app.module.ts is the application bootstrap contract. It builds the engine in a NestJS
factory:
FlowEngine.create({
flows: [
BasicFlow,
HotelFlow,
InvoiceFlow,
SupportFlow,
HomeInsuranceQuoteFlow,
EmployeeBenefitsFlow,
],
providers: [
...ModelProvider.createBuiltinAdapters({
openai: { apiKey: config.get<string>("OPENAI_API_KEY") },
google: { apiKey: config.get<string>("GEMINI_API_KEY") },
anthropic: { apiKey: config.get<string>("ANTHROPIC_API_KEY") },
}),
ModelProvider.createCustomAdapter({
provider: "nvidia",
runtimeProvider: "openai",
config: {
apiKey: config.get<string>("NVIDIA_API_KEY"),
configuration: { baseURL: "https://integrate.api.nvidia.com/v1" },
},
}),
],
});
The NVIDIA entry is worth reading twice: it uses an OpenAI-compatible endpoint but stays an
application-owned integration rather than a PicoFlow built-in, which is exactly what
createCustomAdapter(...) is for. Its selections are deliberately dynamic:
new RecommendationStep(this).useModel({
provider: "nvidia",
name: "meta/llama-3.1-70b-instruct",
params: { temperature: 0.2, maxTokens: 800 },
});
Unlike openai:gpt-5, PicoFlow cannot compile-check NVIDIA’s parameter contract because that
contract belongs to NVIDIA, not the PicoFlow catalog. Register validate(selection) and/or
capabilities(selection) on the custom adapter when your application needs to enforce a
runtime policy. See Providers for the full custom-adapter contract.
Confirm the registered names once the service is up:
curl http://localhost:8000/ai/flows
["BasicFlow","HotelFlow","InvoiceFlow","SupportFlow","HomeInsuranceQuoteFlow","EmployeeBenefitsFlow"]
What each flow demonstrates
| Flow | Shape | Read it for |
|---|---|---|
BasicFlow |
Multi-stage conversation | The broadest lifecycle coverage: context-dependent initialStep(), per-step model overrides, shared and separate memory namespaces, logic steps, nested and concurrent execution, batch coordination. |
HotelFlow |
Multi-turn search, compare, book | onEnter() with eraseMemory(), onCrossing(), memory compaction configured in the flow constructor, large prompt files, direct(...) responses. |
InvoiceFlow |
One-shot document extraction | A step with no tools, multimodal file input, structured output, HttpContentType.Json, and document fan-out via spawnSteps(). |
SupportFlow |
Durable support case | Deterministic policy, approval boundaries, isolated specialist memory, and session restoration. |
HomeInsuranceQuoteFlow |
Twenty-turn quote journey | Shared intake memory, isolated coverage/contact stages, deterministic rating, exact quote tables, correction, re-rating, and consent. |
EmployeeBenefitsFlow |
Twenty-two-turn enrollment journey | Directory-backed eligibility, household validation, exact plan and network tools, HSA and dependent-care limits, ancillary pricing, beneficiary validation, explicit review, and deterministic submission. |
Run the flow tests
npm run test:basic-flow
npm run test:hotel-flow
npm run test:invoice-flow
npm run test:support-flow
npm run test:home-insurance-flow
npm run test:employee-benefits-flow
npm test runs the standard flow suite in sequence via test:flows.
Each spec boots the real NestJS application with a Fastify adapter and drives a scripted
multi-turn scenario through the HTTP contract. Every flow test loads PicoDemo’s .env.
By default it replaces session persistence with a flow-specific SQLite database under
test/.tmp, keeping normal test runs isolated.
Test environment
The default local store is isolated per flow:
SESSION_STOREis forced toSQLITE;SQLITE_PATHpoints attest/.tmp/<flow>-session.sqlite.
Set USE_ENV=1 to retain the session-store settings already loaded from .env instead. This
is useful when intentionally exercising MongoDB, Cosmos DB, or a configured SQLite path:
USE_ENV=1 npm run test:basic-flow
USE_ENV=1 is a test-harness switch. It does not supply provider credentials or by itself
turn a skipped live scenario into a runnable one.
Skipping and required keys
A live scenario is skipped, not failed, when its provider keys are absent:
| Spec | Required to run live |
|---|---|
test:basic-flow |
OPENAI_API_KEY, PICOFLOW_KEY |
test:hotel-flow |
OPENAI_API_KEY, PICOFLOW_KEY |
test:invoice-flow |
GEMINI_API_KEY, PICOFLOW_KEY |
test:home-insurance-flow |
OPENAI_API_KEY, PICOFLOW_KEY |
test:employee-benefits-flow |
OPENAI_API_KEY, PICOFLOW_KEY |
Semantic judges use the API-key openai provider. Their model is configured by the scenario
or the flow-specific *_JUDGE_MODEL environment variable; the checked-in scenarios use
gpt-4o. Flow models remain independently selected and still need their listed credentials.
BasicFlow additionally supports a deterministic mode that replaces the provider with a
scripted model, so it exercises the same transitions and SQLite assertions without spending
tokens:
BASIC_FLOW_USE_SCRIPTED_MODEL=1 npm run test:basic-flow
In that mode only PICOFLOW_KEY is required.
The demo README.md refers to a test:basic-flow:contract script for the deterministic run. No such script exists in package.json. Set BASIC_FLOW_USE_SCRIPTED_MODEL=1 on the normal script instead.
HotelFlow’s scenario is graded by its API-key semantic judge. Pair live scenarios with
deterministic contract assertions so a fluent answer cannot disguise missing state or a wrong
transition.
HomeInsuranceQuoteFlow applies the same principle over twenty live turns. Its spec also
tests rating and referral decisions without a model, and the final session assertions prove
that the roof correction, deductible re-rate, selected option, and contact consent persisted.
EmployeeBenefitsFlow extends that pattern to twenty-two live turns. Its
deterministic tests own eligibility, plan prices, provider-network status, HSA and
dependent-care limits, ancillary pricing, pending evidence of insurability, and the final
enrollment record. The live scenario adds semantic grading and persisted-state assertions.
Next
With the service running, work through the real HTTP contract in Your first request, or start reading the flows themselves in the tutorials.