Skip to main content

slotflow

Drive multi-turn LLM conversations from a Pydantic schema. Declare what to collect with Slot(), choose a flow mode (sequential, freeform, steps), and let slotflow handle question generation, extraction, response judging, and follow-ups — with immutable state that serializes to Redis out of the box.

  • Composition over modification — slots do not know about flows
  • Pydantic does all validation, including dynamic per-call wrapper models
  • Immutable state — every turn returns a new FlowState
  • LLM is injected, never constructed internally (provider-agnostic)
  • Core install is LLM-free; LangChain and OpenAI extras are optional

Installation

# Schema layer only (no LLM dependency)
pip install llm-slotflow

# With LangChain backend
pip install "slotflow[langchain]"

# With OpenAI backend (no LangChain)
pip install "slotflow[openai]"

Requires Python 3.10+.


Quickstart

1. Declare a schema

from datetime import date
from enum import Enum
from typing import Optional

from slotflow import OnboardingSchema, Slot


class DocumentType(str, Enum):
    DNI = "DNI"
    CE = "CE"
    PASSPORT = "Passport"


class UserOnboarding(OnboardingSchema):
    full_name: str = Slot(description="User's full name")
    document_type: DocumentType = Slot(description="Type of identity document")
    birth_date: date = Slot(description="Date of birth")
    phone: Optional[str] = Slot(default=None, description="Phone number (optional)")

Slots without a default are required; slots with one are optional.

2. Extract slots from free-form text

import asyncio
from langchain_openai import ChatOpenAI
from slotflow import extract_slots

llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)

result = asyncio.run(extract_slots(
    schema=UserOnboarding,
    field_names=["full_name", "document_type", "birth_date"],
    text="I'm John Smith, passport, born on 15/05/1990.",
    llm=llm,
))
print(result.value)
# {'full_name': 'John Smith', 'document_type': <DocumentType.PASSPORT>, 'birth_date': date(1990, 5, 15)}

extract_slot / extract_slots retry up to max_attempts (default 3), feeding the Pydantic ValidationError back to the LLM as feedback.

3. Drive a full conversation

import asyncio
from langchain_openai import ChatOpenAI
from slotflow import (
    FlowMode, OnboardingFlow, SlotPrompt,
    initial_state, next_message, process_response,
)

flow = OnboardingFlow(
    schema=UserOnboarding,
    mode=FlowMode.SEQUENTIAL,
    prompts={
        "phone": SlotPrompt(
            follow_up_hint="If the user hesitates, remind them it's optional."
        ),
    },
)
llm = ChatOpenAI(model="gpt-4o-mini", temperature=0)


async def main() -> None:
    state = initial_state(flow)
    state, turn = await next_message(flow=flow, state=state, llm=llm)

    while not turn.done:
        print("Bot >", turn.message)
        user_text = input("You > ")
        state, turn = await process_response(
            flow=flow, state=state, user_text=user_text, llm=llm
        )

    print("Captured:", dict(state.filled))
    print("Skipped:", list(state.skipped))


asyncio.run(main())

Flow modes

Mode What it does
SEQUENTIAL Asks one slot at a time in schema declaration order.
UNORDERED Asks one slot at a time but accepts answers for any pending slot in the same turn.
FREEFORM Opens with everything that is missing; extracts many slots from one response.
STEPS Groups slots into ordered Step(...)s; finishes a step before moving on.

Per-slot prompt overrides

Override question wording per slot, or hint the follow-up generator:

OnboardingFlow(
    schema=UserOnboarding,
    prompts={
        "full_name": SlotPrompt(question="What's your full name?"),
        "birth_date": SlotPrompt(
            follow_up_hint="Ask for an explicit day/month/year.",
        ),
    },
)

When SlotPrompt.question is set, the LLM question-generation call is skipped entirely — useful for tightly controlled wording or to save tokens.


How a turn works

  1. Extract — extract_slot / extract_slots is always called first.
  2. Judge — judge_response classifies the response as one of COMPLETE / PARTIAL / INSUFFICIENT / SKIP_INTENT / REFUSED, given the user's raw text and the extracted value.
  3. Decide — the runner fills the slot, marks an optional slot as skipped, asks a follow-up, or generates a nudge for a refused required slot.

Stateless runner

FlowState, Turn, and FlowTurn are all frozen dataclasses. Every call to next_message / process_response returns a new FlowState; the input is never mutated. This makes it trivial to:

  • run many conversations concurrently
  • pickle the state between turns (Redis, DB, queue)
  • snapshot/restore for testing or replay
import pickle
serialized = pickle.dumps(state)  # works out of the box

Examples

Runnable scripts live in examples/:

  • 01_extract_single_slot.py — minimal extraction example
  • 02_sequential_flow.py — SEQUENTIAL mode end to end
  • 03_freeform_flow.py — FREEFORM mode end to end
  • 04_steps_flow.py — STEPS mode with grouped slots

All read OPENAI_API_KEY from .env (see .env.example).


Development

python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"

# Run tests
.venv/bin/pytest tests/

# Lint + format
.venv/bin/ruff check .
.venv/bin/ruff format .

# Type check
.venv/bin/mypy

# Tests with coverage
.venv/bin/pytest --cov

License

MIT — see LICENSE.

Metadata

Release files for llm-slotflow 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for llm-slotflow 0.2.0
File Size Uploaded Provenance Actions
llm_slotflow-0.2.0.tar.gz 26.3 kB With provenance Details for llm_slotflow-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for llm-slotflow 0.2.0
File Interpreter ABI Platform Actions
llm_slotflow-0.2.0-py3-none-any.whl Python 3 none any Details for llm_slotflow-0.2.0-py3-none-any.whl

Total release size: 51.3 kB

Release files / llm_slotflow-0.2.0.tar.gz

Download URL llm_slotflow-0.2.0.tar.gz
Size 26.3 kB
Tags Source
SHA-256 checksum
How to use checksums
3172c5770b0962b411cafcca7b8d2923fcbf70e836ebb374ca6a514a20a01a5e
BLAKE2b-256 checksum
How to use checksums
3061ff0f1a890a00c340a0909ae1cb94da8b8af7db081279945939ac28bfaa1e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.

Transparency log

Release files / llm_slotflow-0.2.0-py3-none-any.whl

Download URL llm_slotflow-0.2.0-py3-none-any.whl
Size 25.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
46d79e2333f8c053197dc0fb8574310cb441c53bff2b42d3aa180488ccfe4425
BLAKE2b-256 checksum
How to use checksums
74f8865a22918090abffa96a2eaf1fc41569ffed66534022a5b3cda68dbe71c2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.

Transparency log

Security for llm-slotflow 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for llm_slotflow-0.2.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 1, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page