picoflow.io Docs

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-exampleMOONSHOT_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.

Warning

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.

Note

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_STORE is forced to SQLITE;
  • SQLITE_PATH points at test/.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.

Warning

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.