@jambudipa/dynamic-flow
v0.2.3
Published
A TypeScript library for building dynamic AI-driven workflows with LLM planning and static flow composition using Effect
Maintainers
Readme
DynamicFlow - Runtime AI Planning
⚠️ Pre-Release Software: DynamicFlow is under active development. The API may change significantly until the stable v1.0 release. Use in production with caution and expect breaking changes in minor versions until v1.0.
The Only Framework That Generates Complete Execution Graphs at Runtime
Unlike static workflow frameworks or dynamic routing systems, DynamicFlow generates entire execution graphs from scratch for each user prompt. Your AI doesn't just choose paths through a predefined graph - it creates the graph itself, then DynamicFlow executes it deterministically.
Every prompt gets its own custom-generated graph topology. Every graph executes exactly as planned.
📊 Project Health
- ✅ Tests Passing: 259/267 (97% pass rate)
- ✅ Test Coverage: 95%+
- ✅ Build Status: Passing
- ✅ Type Check: No errors
- ✅ Dependencies: Up to date
- ✅ Documentation: Comprehensive
The Problem Nobody Talks About
Most AI frameworks fall into two traps:
- Static workflows that can't adapt to varied user requests
- Free-form agents that wander off-task during execution
You've seen it: An LLM starts with a good plan, but halfway through execution it "reconsiders", loops indefinitely, or simply forgets what it was doing.
Why DynamicFlow Is Different
The Critical Innovation: Complete Graph Generation
What others do:
- LangGraph: Define nodes and edges at compile time, route dynamically
- TaskWeaver: Generate Python code to execute tasks
- Plan-and-Execute Agents: Create sequences of predefined actions
- AutoGen: Generate and execute code with multiple LLM calls
What DynamicFlow does:
- Generates the entire graph topology from the prompt
- Creates nodes, edges, conditions, and parallel branches dynamically
- Outputs a JSON graph definition that's safe and debuggable
- Executes deterministically without any LLM calls
The Two-Phase Architecture That Makes It Possible
Phase 1: AI Planning (Graph Generation)
Your LLM analyzes the user's request and generates a complete execution graph as JSON:
- Dynamic node creation based on the specific task
- Custom edge definitions connecting operations
- Conditional branches determined by the prompt
- Parallel execution paths when appropriate
- All topology decisions made upfront
Real Example: "Research Buddhist philosophy on selflessness"
The AI generates this sophisticated research workflow with conditional logic, parallel processing, and multi-stage analysis:
# Run the example
npx tsx src/examples/dynamic/05-selflessness-analysis.tsGenerated Flow Plan:
{
"version": "1.0",
"metadata": {
"name": "Gross vs Subtle Selflessness of Persons - Discovery, Comparison, Clarification, Summary",
"description": "Search corpus, retrieve relevant book sections and audio, compare gross vs subtle selflessness of persons, clarify if unclear, summarise plainly, and check contradictions.",
"generated": true,
"timestamp": "2025-08-19T21:19:09.422Z"
},
"nodes": [
{
"id": "s1",
"type": "tool",
"toolId": "corpus:search",
"inputs": {},
"description": "Search corpus for discussions of gross and subtle selflessness of persons in books and audio."
},
{
"id": "s2",
"type": "parallel",
"parallel": [
{
"id": "s3",
"description": "Retrieve relevant book section(s) based on $s1.output.",
"tool": "book:get-section"
},
{
"id": "s4",
"description": "Retrieve relevant audio transcript(s) based on $s1.output.",
"tool": "audio:get-transcript"
}
],
"description": "In parallel, fetch top relevant book sections and audio transcripts."
},
{
"id": "s5",
"type": "tool",
"toolId": "llm:compare",
"description": "Compare how 'gross' vs 'subtle' selflessness of persons are described."
},
{
"id": "s6",
"type": "if-then",
"condition": "contains($s5.output, 'unclear') || contains($s5.output, 'overlap')",
"if_true": [
{
"id": "s7",
"tool": "llm:clarify",
"description": "Clarify overlapping or unclear points using related texts."
}
],
"description": "If descriptions appear unclear, run clarification using related texts."
},
{
"id": "s8",
"type": "tool",
"toolId": "llm:summarise",
"description": "Produce a plain-language summary of the distinction."
},
{
"id": "s9",
"type": "tool",
"toolId": "llm:check-contradictions",
"description": "Check the summary for internal contradictions."
}
],
"edges": [
{"from": "s1", "to": "s2"},
{"from": "s2", "to": "s5"},
{"from": "s5", "to": "s6"},
{"from": "s6", "to": "s8"},
{"from": "s8", "to": "s9"}
]
}Execution Result:
{
"s1": {
"results": [
{
"id": "bk-chandrakirti-12",
"kind": "book",
"title": "Introduction to Madhyamaka – Selflessness of Persons",
"score": 0.94,
"tags": ["selflessness", "persons", "gross", "analysis"]
},
{
"id": "bk-shantideva-9",
"kind": "book",
"title": "Bodhicaryavatara – View Chapter notes",
"score": 0.86,
"tags": ["selflessness", "persons", "subtle", "aggregate"]
},
{
"id": "aud-2021-03-14",
"kind": "audio",
"title": "Weekend Teaching – Twofold Selflessness",
"score": 0.78,
"tags": ["teaching", "selflessness", "clarification"]
}
]
},
"s8": {
"summary": "Gross selflessness of persons says there is no permanent, unitary, independent 'me' that stands apart from or controls the body and mind. It denies a fixed, soul-like self and targets the coarse belief in an inner boss. Subtle selflessness of persons goes further: even the changing, everyday 'I' lacks any inherent core; it exists only as a label dependent on the aggregates, causes, and concepts. So, while persons work perfectly well in everyday life, no self can be found under ultimate analysis. Realizing the gross level weakens crude self-grasping; realizing the subtle level uproots the deeper belief in inherent existence."
},
"s9": {
"contradictions": ["None detected"]
}
}Phase 2: Deterministic Execution
DynamicFlow's execution engine interprets the JSON graph:
- No more LLM calls during execution
- No graph structure changes mid-execution
- Predictable, debuggable behavior
- Full type safety through Effect
- Guaranteed completion without infinite loops
Core Features
🎯 True Runtime Graph Generation
The only framework that generates complete execution graphs per-prompt - not routing, not code, but actual graph structures.
⚡ Effect-Based Architecture
First AI framework built on Effect for robust error handling, dependency injection, and composability.
🔄 Production-Ready Persistence
Complete state management with multiple backends and human-in-the-loop workflows:
- Multiple storage backends (Filesystem, PostgreSQL, Redis, MongoDB, Neo4j)
- Automatic state serialization with encryption
- Suspension/resumption with branded
SuspensionKeytypes - Human approval and input workflows
🤖 LLM Conversation Routing
Advanced LLM integration with intelligent routing:
Flow.switchRoutefor LLM-powered decision making- Conversation memory and context preservation
- Real-time terminal interfaces
- Structured conversation flows with persistence
🔌 MCP Server Integration (NEW)
Production-ready Model Context Protocol server discovery and integration:
- Real MCP server discovery with automatic tool generation
- Type-safe MCP tools with proper TypeScript inference
- Per-server tool generation from discovered capabilities
- Production MCP connections (no mocks) with cleanup
Featured Examples - Try Them Now!
🗣️ Interactive LLM Conversation with Persistence
Experience DynamicFlow's conversation capabilities:
export OPENAI_API_KEY=your_key_here
npx tsx src/examples/static/16-conversation-final.tsFeatures demonstrated:
- LLM-powered conversation routing using
Flow.switchRoute - Filesystem persistence with automatic state saving
- Natural terminal conversation interface
- Real OpenAI integration with memory
- Proper suspension/resumption handling
🔧 MCP Server Integration
See real MCP server discovery and tool generation:
npx tsx src/examples/static/15-mcp-curl.tsFeatures demonstrated:
- Real MCP filesystem server connections
- Type-safe tool generation (
stringreturns, notunknown) - Production MCP protocol integration
- Automatic cleanup and connection management
Generate your own MCP tools:
# Discover MCP servers
npx tsx src/lib/cli/mcp-discovery.ts discover --source network > servers.json
# Generate typed tools
npx tsx src/lib/cli/mcp-discovery.ts generate -i servers.json -o src/generated/mcp-tools
# Use in your flows with full type safety!Human-in-the-Loop Workflows
DynamicFlow provides powerful persistence capabilities for workflows requiring human interaction:
import { Effect, Schema, Duration } from 'effect'
import { createPersistenceHub, AwaitInputPresets, BackendFactory } from '@jambudipa/dynamic-flow'
// Setup filesystem storage backend
const backend = await Effect.runPromise(
BackendFactory.create({
type: 'filesystem',
config: { basePath: './suspended-flows' }
})
)
// Create persistence hub
const hub = await Effect.runPromise(
createPersistenceHub(backend, {
enableEncryption: false,
enableCompression: true,
defaultTimeout: Duration.hours(24)
})
)
// Create approval tool that suspends flow
const approvalTool = AwaitInputPresets.approval(
'manager-approval',
'Manager Approval',
'Requires manager approval'
).withTimeout(Duration.hours(4)).build(hub)
// Execute tool directly - this will suspend
try {
const result = await Effect.runPromise(
approvalTool.execute(undefined, executionContext)
)
} catch (suspensionSignal) {
if (suspensionSignal instanceof FlowSuspensionSignal) {
console.log('Flow suspended:', suspensionSignal.suspensionKey)
// Later, resume with approval
const approval = { approved: true, approvedBy: "[email protected]" }
const resumed = await Effect.runPromise(
hub.resume(suspensionSignal.suspensionKey, approval)
)
}
}LLM Conversation Routing
DynamicFlow treats LLMs as powerful tools with intelligent routing capabilities:
import { Flow } from '@jambudipa/dynamic-flow'
import { LLMServiceLive } from '@jambudipa/dynamic-flow'
import { Effect, pipe } from 'effect'
// Available tools for conversation
const availableTools = [
weatherTool,
calculatorTool,
emailTool,
fileSearchTool
]
// Create conversation flow with LLM routing
const conversationFlow = pipe(
Flow.succeed(conversationInput),
// LLM decides which tool to use based on user input
Flow.switchRoute(
'Analyze the user request and select the most appropriate tool',
availableTools,
{
weather: (input) => handleWeatherRequest(input),
calculator: (input) => handleCalculation(input),
email: (input) => handleEmail(input),
fileSearch: (input) => handleFileSearch(input)
}
)
)
// Execute with OpenAI integration
const result = await Effect.runPromise(
pipe(
Flow.run(conversationFlow),
Effect.provide(LLMServiceLive)
)
)Real Working Example:
npx tsx src/examples/static/16-conversation-final.tsMCP Server Integration
DynamicFlow includes production-ready MCP (Model Context Protocol) integration:
Discover & Generate Tools
# Discover available MCP servers
npx tsx src/lib/cli/mcp-discovery.ts discover --source network --output json
# Generate tools from specific server
npx tsx src/lib/cli/mcp-discovery.ts discover \
--source url \
--filter "stdio://npx @modelcontextprotocol/server-filesystem /tmp" \
> filesystem-server.json
# Generate TypeScript tools with proper typing
npx tsx src/lib/cli/mcp-discovery.ts generate \
-i filesystem-server.json \
-o src/generated/filesystem-toolsUse Generated Tools with Full Type Safety
// Generated tools have proper TypeScript types
import { read_text_fileTool, list_directoryTool } from '../generated/filesystem-tools'
// read_text_fileTool returns string (not unknown!)
const fileContent: string = await read_text_fileTool.execute({
path: '/path/to/file.txt'
})
// list_directoryTool returns Array<{name: string, type: 'file' | 'directory', size?: number}>
const files = await list_directoryTool.execute({ path: '/path/to/dir' })MCP Integration Features:
- Per-server discovery: Each MCP server gets its own tool file
- Smart type inference: Tool return types inferred from names and capabilities
- Real connections: Production MCP protocol, not mocks
- Automatic cleanup: Proper connection management and disconnection
Quick Start
npm install @jambudipa/dynamic-flow effectBasic Flow Composition
import { Flow } from '@jambudipa/dynamic-flow'
import { Effect, pipe } from 'effect'
// Create a simple flow
const myFlow = pipe(
Effect.succeed("Hello"),
Flow.andThen(greeting => Effect.succeed(`${greeting}, World!`)),
Flow.map(message => message.toUpperCase())
)
// Run the flow
const result = await Effect.runPromise(
Flow.run(myFlow)
)
console.log(result) // "HELLO, WORLD!"Create Tools with Schema Validation
import { Tools } from '@jambudipa/dynamic-flow'
import { Schema } from 'effect'
const weatherTool = Tools.createTool({
id: 'weather',
name: 'Weather Fetcher',
description: 'Get current weather for a city',
inputSchema: Schema.Struct({
city: Schema.String
}),
outputSchema: Schema.Struct({
temperature: Schema.Number,
conditions: Schema.String,
humidity: Schema.Number
}),
execute: (input, context) => Effect.succeed({
temperature: 72,
conditions: 'sunny',
humidity: 45
})
})
// Use in flows with full type safety
const weatherFlow = Flow.pipe(
Flow.input(CitySchema),
Flow.tool(weatherTool),
Flow.map(weather => `It's ${weather.temperature}°F and ${weather.conditions}`)
)Learn More
📚 Documentation
Comprehensive guides and API references are available in the /docs directory:
🚀 Getting Started
- Complete Documentation Index - Overview of all documentation
- Getting Started Guide - Quick start and core concepts
📖 API Reference
- Flow API - Pipeable operations for functional composition
- Tools API - Creating and managing typed tools
- Persistence API - Flow suspension and resumption
📚 Guides
- Pipeable Patterns - Advanced functional composition
- DynamicFlows - AI-generated workflows
🚀 More Examples
- All Static Examples - Complete working examples
- LLM Integration Examples - OpenAI integration
- Persistence Examples - Human-in-the-loop workflows
💬 Community
Join the discussion: GitHub Issues | GitHub Discussions
Why Choose DynamicFlow?
| Feature | DynamicFlow | LangGraph | TaskWeaver | CrewAI | AutoGen | |---------|------------|-----------|------------|---------|----------| | Runtime Graph Generation | ✅ Complete | ❌ Static | ❌ Code only | ❌ Static | ❌ Code only | | Graph Topology Per-Prompt | ✅ Yes | ❌ No | ❌ No | ❌ No | ❌ No | | Deterministic Execution | ✅ Always | ⚠️ Optional | ✅ Yes | ✅ Flows | ⚠️ Varies | | Type Safety | ✅ Full Effect | ✅ TypeScript | ⚠️ Python | ⚠️ Python | ⚠️ Python | | Persistence | ✅ Multi-backend | ✅ Yes | ⚠️ Stateful | ✅ Yes | ❌ No | | LLM Conversation | ✅ Advanced routing | ⚠️ Basic | ❌ No | ⚠️ Basic | ✅ Multi-agent | | MCP Integration | ✅ Production ready | ❌ No | ❌ No | ❌ No | ❌ No | | Effect Based | ✅ Yes | ❌ No | ❌ No | ❌ No | ❌ No |
The Bottom Line
DynamicFlow is the only framework that:
- Generates complete execution graphs (not just plans or code) at runtime
- Creates unique graph topologies for each prompt (not routing through static graphs)
- Executes deterministically without LLM involvement after planning
- Uses Effect for functional, type-safe workflow composition
- Provides production-ready persistence with multiple backends and human workflows
- Includes intelligent LLM conversation routing with memory and context
- Integrates with MCP servers for real tool discovery and type-safe generation
This isn't an incremental improvement - it's a fundamentally different approach to AI orchestration.
Status
- Production-ready core: Flow composition, persistence, and MCP integration are stable
- Active development: Performance optimizations and additional features in progress
- Feedback welcome: Please share ideas via GitHub Issues or Discussions
Next Steps
- Performance optimizations for dynamic flow generation
- Additional MCP server integrations
- Enhanced conversation routing capabilities
- More comprehensive testing suite
License
MIT © DynamicFlow Contributors
Built with ❤️ by JAMBUDIPA
