fluid-graph-ai
v1.0.0
Published
Dynamic, self-compiling DAG execution framework for AI agents.
Maintainers
Readme
🚀 What is FluidGraph?
FluidGraph.js is a TypeScript framework that eliminates the need to manually wire agent workflows. Unlike rigid graph frameworks (e.g., LangGraph) where you define every node and edge by hand, FluidGraph uses an LLM to dynamically compile a Directed Acyclic Graph (DAG) from a natural language goal and a set of available tools.
You describe what you want. FluidGraph figures out how.
Traditional Approach (LangGraph):
Developer manually codes: A → B → C → D
FluidGraph Approach:
Developer provides: Tools [A, B, C, D] + Goal "Do X"
LLM compiles: A → C → D (skips B, it's not needed)✨ Key Features
| Feature | Description |
|---------|-------------|
| 🧠 LLM-Powered Compilation | An LLM analyzes your goal and available tools, then generates the optimal execution graph automatically. |
| 🔌 Multi-Provider | Works with Gemini, OpenAI, Anthropic, and any OpenAI-compatible API (Ollama, LM Studio, Together AI). |
| 🔀 Dynamic DAG Generation | No manual node/edge wiring. The graph is compiled fresh for every request based on the goal. |
| 📐 Topological Execution | Uses Kahn's algorithm to guarantee correct dependency ordering across the DAG. |
| 🔄 Shared State Context | Nodes pass data through a shared ExecutionContext, enabling seamless data flow between steps. |
| 🛡️ Cycle Detection | Built-in validation ensures the compiled graph is a true DAG (no infinite loops). |
| 📝 Execution History | Full audit trail of every node execution for debugging and observability. |
📦 Installation
npm install fluidgraphOr clone and install locally:
git clone https://github.com/your-org/fluidgraph.git
cd fluidgraph
npm install⚡ Quick Start
1. Set up your API key
cp .env.example .env
# Edit .env and add your Gemini API keyGet your API key from Google AI Studio.
2. Pick your LLM provider
FluidGraph is model-agnostic. Use whichever provider you prefer:
import {
GeminiProvider,
OpenAIProvider,
AnthropicProvider
} from 'fluidgraph';
// Google Gemini (default)
const provider = new GeminiProvider({ model: 'gemini-2.5-flash' });
// OpenAI
const provider = new OpenAIProvider({ model: 'gpt-4o' });
// Anthropic Claude
const provider = new AnthropicProvider({ model: 'claude-sonnet-4-20250514' });
// Any OpenAI-compatible API (Ollama, LM Studio, Together AI)
const provider = new OpenAIProvider({
baseUrl: 'http://localhost:11434/v1',
apiKey: 'ollama',
model: 'llama3',
});3. Define your tools
Each tool is a FluidNode with an id, name, description, and an async action function:
import { FluidNode } from 'fluidgraph';
const tools: FluidNode[] = [
{
id: 'fetch_data',
name: 'Fetch Data',
description: 'Fetches user data from the database. Outputs { userData: object }',
action: async (state) => {
const data = await db.getUser(state.userId);
return { userData: data };
}
},
{
id: 'analyze',
name: 'Analyze Engagement',
description: 'Scores user engagement. Requires userData in state.',
action: async (state) => {
const score = calculateScore(state.userData);
return { engagementScore: score };
}
},
{
id: 'notify',
name: 'Send Notification',
description: 'Sends a notification. Requires engagementScore in state.',
action: async (state) => {
await sendPush(state.engagementScore > 80 ? 'premium' : 'standard');
return { notified: true };
}
}
];4. Compile and execute
import { GraphCompiler, GraphExecutor } from 'fluidgraph';
const compiler = new GraphCompiler(provider); // pass any LLMProvider
const executor = new GraphExecutor(tools);
// Describe your goal in natural language
const goal = "Fetch user data, analyze their engagement, and send a notification.";
// The LLM dynamically compiles the optimal DAG
const graph = await compiler.compile(goal, tools);
// Execute the compiled graph
const result = await executor.execute(graph, { userId: '123' });
console.log(result.state); // { userData: {...}, engagementScore: 92, notified: true }
console.log(result.history); // ["Executing: Fetch Data", "Completed: Fetch Data", ...]🏗️ Architecture
┌─────────────────────────────────────────────────────┐
│ FluidGraph.js │
│ │
│ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ User Goal │───▶│ GraphCompiler │ │
│ │ (string) │ │ ┌──────────────────────┐ │ │
│ └──────────────┘ │ │ LLM (Any) │ │ │
│ │ │ Generates DAG JSON │ │ │
│ ┌──────────────┐ │ └──────────────────────┘ │ │
│ │ Tool List │───▶│ │ │
│ │ (FluidNode) │ └─────────┬────────────────┘ │
│ └──────────────┘ │ │
│ ▼ │
│ ┌──────────────────────────┐ │
│ │ FluidGraphDef (JSON) │ │
│ │ { nodes: [], edges: [] } │ │
│ └─────────┬────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────┐ │
│ │ GraphExecutor │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Topological Sort │ │ │
│ │ │ (Kahn's Algorithm) │ │ │
│ │ └──────────────────────┘ │ │
│ │ ┌──────────────────────┐ │ │
│ │ │ Sequential Execution │ │ │
│ │ │ with State Merging │ │ │
│ │ └──────────────────────┘ │ │
│ └─────────┬────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────┐ │
│ │ ExecutionContext │ │
│ │ { state, history } │ │
│ └──────────────────────────┘ │
└─────────────────────────────────────────────────────┘🌎 Real-World Use Cases
Here are three examples of how FluidGraph outshines rigid frameworks in production:
1. Customer Support AI
- Tools:
check_order_status,process_refund,escalate_to_human - Scenario: A customer says, "My package is broken, I want a refund."
- The FluidGraph Advantage: The LLM dynamically compiles
check_order_status→process_refund. If the refund API is down (the node fails), FluidGraph's self-healing kicks in, excludes the broken node, recompiles the graph, and safely routes toescalate_to_human.
2. Autonomous Coding Agents
- Tools:
read_file,run_tests,git_commit - Scenario: The agent needs to verify a bug fix.
- The FluidGraph Advantage: Because
run_testsandread_fileare independent, FluidGraph automatically runs them in parallel. It compiles the DAG to execute them concurrently, drastically speeding up the agent's workflow compared to sequential loops.
3. Financial Analysis Pipelines
- Tools:
fetch_stock_price,fetch_news_sentiment,generate_report - Scenario: User asks for an Apple stock report.
- The FluidGraph Advantage: FluidGraph executes
fetch_stock_priceandfetch_news_sentimentin parallel, waits for both to finish, and passes their combined state downstream intogenerate_report.
📁 Project Structure
fluidgraph/
├── src/
│ ├── types.ts # Core interfaces, ExecutorOptions
│ ├── compiler.ts # GraphCompiler — LLM-powered DAG generation
│ ├── executor.ts # GraphExecutor — parallel, self-healing, conditional
│ ├── events.ts # FluidGraphEmitter — typed streaming events
│ ├── visualizer.ts # GraphVisualizer — Mermaid & ASCII export
│ ├── index.ts # Public API exports
│ └── providers/
│ ├── provider.ts # LLMProvider interface
│ ├── gemini.ts # Google Gemini implementation
│ ├── openai.ts # OpenAI / Azure / Ollama implementation
│ ├── anthropic.ts # Anthropic Claude implementation
│ └── index.ts # Provider barrel exports
├── dist/ # Compiled JavaScript output
├── example.ts # Working demo with all features
├── .env.example # Environment variable template
├── .gitignore
├── package.json
├── tsconfig.json
└── README.md🔧 API Reference
GraphCompiler
// With any LLMProvider
const compiler = new GraphCompiler(provider: LLMProvider);
// Legacy: pass a Gemini API key string directly
const compiler = new GraphCompiler('your-gemini-api-key');
// Default: auto-creates GeminiProvider from GEMINI_API_KEY env var
const compiler = new GraphCompiler();| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| provider | LLMProvider \| string | GeminiProvider() | Any LLM provider, or a Gemini API key string for backward compatibility |
compiler.compile(goal, tools)
| Parameter | Type | Description |
|-----------|------|-------------|
| goal | string | Natural language description of the desired outcome |
| tools | FluidNode[] | Array of available tools the graph can use |
Returns: Promise<FluidGraphDef> — The compiled graph definition.
GraphExecutor
const executor = new GraphExecutor(tools: FluidNode[]);executor.execute(graphDef, initialState?)
| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| graphDef | FluidGraphDef | — | The compiled graph to execute |
| initialState | Record<string, any> | {} | Initial state to seed the execution context |
Returns: Promise<ExecutionContext> — Contains the final state and history.
Core Types
interface FluidNode {
id: string;
name: string;
description: string;
action: (context: Record<string, any>) => Promise<Record<string, any>>;
}
interface FluidGraphDef {
nodes: { id: string; name: string; description: string }[];
edges: { from: string; to: string; conditionExpr?: string }[];
}
interface ExecutionContext {
state: Record<string, any>;
history: string[];
}
interface ExecutorOptions {
selfHealing?: boolean; // default: false
maxHealingRetries?: number; // default: 2
parallel?: boolean; // default: true
evaluateConditions?: boolean; // default: true
}GraphExecutor (Advanced)
// Basic usage
const executor = new GraphExecutor(tools);
// With all features enabled
const executor = new GraphExecutor(
tools,
{
parallel: true, // Run independent branches concurrently
selfHealing: true, // Recompile on failure
maxHealingRetries: 2, // Max recompilation attempts
evaluateConditions: true, // Evaluate conditionExpr on edges
},
compiler, // Required for self-healing
);
// Execute (pass goal for self-healing context)
const result = await executor.execute(graphDef, initialState, goal);Streaming Events
Subscribe to real-time execution events via executor.events:
executor.events.on('node:start', (e) => {
console.log(`Started: ${e.nodeName}`);
});
executor.events.on('node:complete', (e) => {
console.log(`Done: ${e.nodeName} in ${e.durationMs}ms`);
});
executor.events.on('node:skipped', (e) => {
console.log(`Skipped: ${e.nodeName} — ${e.reason}`);
});
executor.events.on('node:error', (e) => {
console.log(`Error: ${e.nodeName} — ${e.error.message}`);
});
executor.events.on('graph:healing', (e) => {
console.log(`Healing attempt ${e.attempt}/${e.maxRetries}`);
});
executor.events.on('graph:complete', (e) => {
console.log(`Complete: ${e.executedNodes} run, ${e.skippedNodes} skipped`);
});GraphVisualizer
Export compiled graphs as diagrams:
import { GraphVisualizer } from 'fluidgraph';
// Mermaid flowchart (for markdown / docs)
console.log(GraphVisualizer.toMermaid(graphDef));
// graph TD
// fetch_data["Fetch Data"] --> analyze["Analyze"]
// analyze["Analyze"] -->|"score > 80"| send_email["Send Email"]
// Mermaid wrapped in markdown code block
console.log(GraphVisualizer.toMermaidMarkdown(graphDef));
// ASCII art for terminal output
console.log(GraphVisualizer.toAscii(graphDef));🗺️ Roadmap
- [x] LLM-powered graph compilation
- [x] Topological sort execution (Kahn's algorithm)
- [x] Shared state context between nodes
- [x] Cycle detection
- [x] Multi-provider support — Gemini, OpenAI, Anthropic, and any OpenAI-compatible API
- [x] Self-healing graphs — On node failure, re-invoke the compiler to rewire around the broken node
- [x] Parallel execution — Detect independent branches and execute them concurrently
- [x] Streaming events — Real-time typed event emitter for all execution lifecycle events
- [x] Conditional edges — Runtime condition evaluation with cascade skipping
- [x] Graph visualization — Mermaid diagrams + ASCII art export
- [x] Custom provider — Implement
LLMProviderinterface to plug in any model backend
🤝 Contributing
Contributions are welcome! Please open an issue or submit a pull request.
📄 License
MIT © FluidGraph Contributors
