A minimal, async-native, and unopinionated toolkit for modern LLM applications.
Project description
A minimal, async-native, and unopinionated toolkit for modern LLM applications.
Stop wiring graphs. Start modeling conversations.
Lingo is a lightweight, type-safe Python framework for building LLM-powered applications. It moves beyond generic "agents" and "chains" to focus on Conversational Modeling—the discipline of defining exactly how a system perceives, processes, and advances a dialogue state.
It unifies three powerful paradigms in a single, typed architecture:
- Procedural Skills (Linear, script-like flows)
- Symbolic States (Deterministic FSMs)
- Reflexive Patterns (Event-driven guardrails)
⚡ Features
- 💾 Stateful by Default: The Python stack is your state machine. Use
await engine.ask()to pause execution and wait for user input naturally. - 🧠 Cognitive Architecture: Mix rigid business rules (States) with flexible reasoning (Skills).
- 🛡️ Type-Safe: Built on Pydantic. All inputs, outputs, and tool calls are validated schemas.
- 🌊 Low-Level Flow Control: Direct access to the underlying
Flowgraph for complex orchestration (Fork/Join, Retry, Loops).
🚀 Quickstart
Installation
pip install lingo-ai
The "Hello World" (Stateful Wizard)
Lingo allows you to model conversations as linear scripts. You don't need to manage session IDs or database steps manually—variables persist in memory across turns.
import asyncio
from lingo import Lingo
# Initialize the application
app = Lingo("Wizard", description="A helpful setup wizard")
@app.skill
async def onboarding(ctx, eng):
# 1. Output a message
await eng.reply(ctx, "Welcome to the system.")
# 2. PAUSE execution and wait for user input
# Lingo automatically suspends the stack here.
# The variable 'name' is preserved in memory when the user replies!
name = await eng.ask(ctx, "What is your name?")
# 3. Resume and use context from previous turns
email = await eng.ask(ctx, f"Hi {name}, what is your email?")
# 4. Use structured decision making (LLM is forced to return bool)
if await eng.decide(ctx, f"Is {email} a valid corporate email address?"):
await eng.reply(ctx, "Registration complete.")
else:
await eng.reply(ctx, "Personal emails are not allowed.")
if __name__ == "__main__":
from lingo.cli import loop
loop(app)
🧠 The Three Modeling Paradigms
Lingo gives you the right abstraction for every type of logic.
1. Symbolic States (Finite State Machine)
Best for: Business Logic, Security Boundaries, Multi-Step Workflows.
Use the StateMachine to enforce strict rules about allowed transitions.
from lingo.fsm import StateMachine
# 1. Initialize the FSM with the bot's registry
fsm = StateMachine(app.registry)
@fsm.state
async def login(ctx, eng):
await eng.reply(ctx, "Please log in.")
# Deterministic transition to the next state
fsm.goto(dashboard, restart=True)
@fsm.state
async def dashboard(ctx, eng):
await eng.reply(ctx, "Welcome to your dashboard.")
# Logic restricted to this state...
# Register the FSM as a skill
@app.skill
async def run_workflow(ctx, eng):
await fsm.execute(ctx, eng)
2. Reflexive Patterns (Event-Driven)
Best for: Guardrails, Interruptions, Global Commands.
Use @app.when to define high-priority listeners that intercept messages before they reach skills.
@app.when("User wants to quit or cancel the operation")
async def emergency_stop(ctx, eng):
await eng.reply(ctx, "Stopping immediately.")
eng.stop() # Terminates the flow and clears the stack
3. Structured Flows (Low-Level Graph)
Best for: Parallel Processing, Retries, Complex Orchestration.
You can drop down to the Flow API to build complex execution graphs explicitly.
from lingo import Flow
# Define two sub-flows
research = Flow("Research").reply("Searching for info...")
draft = Flow("Draft").reply("Drafting content...")
# Build a flow that runs them in parallel (Fork)
# and summarizes the result
complex_flow = (
Flow("ParallelWorker")
.fork(
research,
draft,
aggregator="Combine the research and draft into a final report."
)
)
📦 Architecture
Context: Mutable ledger of the conversation history.Engine: The "actuator" that drives the LLM. It exposes methods like.ask(),.decide(),.choose(), and.create().Flow: The underlying graph representation of all skills.
🤝 Contribution
We welcome contributions! Please see CONTRIBUTING.md for details on how to set up your development environment and submit pull requests.
📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file lingo_ai-1.3.tar.gz.
File metadata
- Download URL: lingo_ai-1.3.tar.gz
- Upload date:
- Size: 83.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d1f07f7ea900588ab1387f14e716a0a008ab24e0d12746c5c29a219fc57a3841
|
|
| MD5 |
923a7ac84af6caa2f274961c59e85ed5
|
|
| BLAKE2b-256 |
58014999b2321dad576264e4b8861e88f6faf5dbc6da915d077e49d25829a85b
|
File details
Details for the file lingo_ai-1.3-py3-none-any.whl.
File metadata
- Download URL: lingo_ai-1.3-py3-none-any.whl
- Upload date:
- Size: 32.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.26 {"installer":{"name":"uv","version":"0.9.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2f72763feeb1df4204e9c943b7adb5e99111d9ecc8008ccf65e8c4648b75ca53
|
|
| MD5 |
bf033e18c2eeced4f19021119a7da93a
|
|
| BLAKE2b-256 |
6658ca2839e88bcce36ca4a4ff2a87c30300e897db978955fb8f1cf8fb9012d1
|