ZaFlow is a blazing-fast, lightweight alternative to LangChain & LangGraph. Build powerful AI agents in minutes with multi-agent workflows, tool calling, and flow control — all in pure TypeScript with zero bloat.
🚀 Overview • 🪶 Features • 📦 Installation • ⚡ Quick Start • 🔌 Providers • 🎮 Execution Modes • 🛠️ Tools • 🤖 Agents • 🔄 Flow Control • 📷 Multimodal • 🎯 Structured Output • 🤝 Contributing
🚧 BETA VERSION — This library is currently in active development and not yet recommended for production use. APIs may change without notice. Use at your own risk and feel free to report issues or contribute!
ZaFlow solves the complexity of building AI agent systems by providing a high-level, opinionated API. It is built for developers who need to create chatbots, autonomous agents, or AI-powered workflows without getting bogged down in protocol details.
Targeting Node.js and TypeScript developers, ZaFlow integrates essential features like multi-provider support, tool calling, agent delegation, and flow control out of the box.
- 🪶 Lightweight - Minimal dependencies, fast startup, no bloat
- 🔌 Multi-Provider - OpenAI, Groq, Ollama, or custom provider
- 🤖 3 Execution Modes - Single, Agentic, Autonomous
- 🛠️ Tools & Agents - Define tools with Zod schema validation
- 🎯 Flow Control - Steps, conditions, loops, parallel execution
- 📷 Multimodal - Image, audio, file support with smart media reference system
- 📝 Output Format - Auto, JSON, WhatsApp formatting
- 🔄 Agent Delegation - Autonomous agent-to-agent communication
- 🧩 Structured Output - Type-safe responses with Zod schema validation
ZaFlow - Your orchestrator. Manages providers, tools, agents, and execution flows.
Provider - AI model connection (OpenAI, Groq, Ollama, or custom).
Tool - Functions that AI can call with validated parameters via Zod schemas.
Agent - Specialized AI with its own provider, prompt, and tools.
ExecutionContext - Rich context object with state, messages, and helpers.
Install zaflow using your preferred package manager:
npm install zaflow
# or
pnpm add zaflow
# or
bun add zaflowNote: Requires Node.js v20+ and TypeScript for best experience.
# For OpenAI provider
npm install openai
# For Groq provider
npm install groq-sdk
# For Ollama provider
npm install ollamaHere is a minimal example to get your AI agent running:
import { ZaFlow, groq } from 'zaflow';
const zaflow = new ZaFlow({
provider: groq({ apiKey: 'your-api-key' }),
});
const result = await zaflow.run('Explain quantum computing in one sentence');
console.log(result.output);The ZaFlow constructor accepts a configuration object:
| Option | Type | Description |
|---|---|---|
provider |
ProviderInterface |
Required. AI provider to use. |
cost |
CostConfig |
Optional. Token/cost tracking. |
You can define tools with Zod schema validation for type-safe AI function calling.
import { ZaFlow, groq, defineTool } from 'zaflow';
import { z } from 'zod';
const zaflow = new ZaFlow({
provider: groq({ apiKey: 'xxx' }),
});
const calculator = defineTool({
name: 'calculator',
description: 'Perform mathematical calculations',
schema: z.object({
expression: z.string().describe('Math expression like "2 + 2"'),
}),
handler: ({ expression }) => eval(expression),
});
zaflow.registerTools([calculator]);
const result = await zaflow.run('What is 15 * 7 + 23?', { mode: 'agentic' });import { openai } from 'zaflow';
const provider = openai({
apiKey: 'sk-xxx',
model: 'gpt-4o-mini',
});import { groq } from 'zaflow';
const provider = groq({
apiKey: 'gsk_xxx',
model: 'llama-3.3-70b-versatile',
});import { ollama } from 'zaflow';
const provider = ollama({
host: 'http://localhost:11434',
model: 'llama3.2',
});import { createProvider } from 'zaflow';
// with your external AI API
const customAI = createProvider({
name: 'my-ai',
handler: async ({ prompt, messages }) => {
const res = await fetch('https://my-api.com/chat', {
method: 'POST',
body: JSON.stringify({ prompt }),
});
const json = await res.json();
return json.response;
},
});
const zaflow = new ZaFlow({ provider: customAI });
⚠️ AI Model Quality Affects Tool Calling & Agent DelegationThe quality of your AI model significantly impacts whether tool calls and agent delegations work correctly. When using Custom Provider with external APIs:
- Low-quality models may ignore tool instructions and respond directly instead of returning the required JSON format
{"tool": "...", "params": {...}}- Inconsistent behavior - The same prompt may sometimes trigger tools and sometimes not
- Empty responses - Some APIs may return empty responses causing execution errors
Recommendations:
Use Case Recommended Models Tool Calling GPT-4, GPT-4o, Claude 3, Llama 3.1 70B+, Qwen 2.5 72B+ Agent Delegation GPT-4o, Claude 3 Opus, Llama 3.3 70B Simple Chat Any model works fine For best results with tool calling and agent features, use built-in providers (
openai,groq,ollama) with high-quality models that have native function calling support.
Quick Jump: Single · Agentic · Autonomous
One chat call without tools. Fastest and most token-efficient.
const result = await zaflow.run('Hello!', { mode: 'single' });AI can automatically call tools when needed. (Default)
const result = await zaflow.run('Search weather in Jakarta', {
mode: 'agentic',
maxToolCalls: 5,
});AI can call tools AND delegate to other agents.
const result = await zaflow.run('Analyze this image and generate a poem', {
mode: 'autonomous',
maxIterations: 10,
});Quick Jump: Define Tool · Tool Config · Media Handling
import { defineTool } from 'zaflow';
import { z } from 'zod';
const weatherTool = defineTool({
name: 'get_weather',
description: 'Get current weather for a city',
schema: z.object({
city: z.string().describe('City name'),
unit: z.enum(['celsius', 'fahrenheit']).optional(),
}),
handler: async ({ city, unit = 'celsius' }) => {
// const data = await fetchWeatherAPI...
return `Currently in ${city} is 20°`;
},
});const tool = defineTool({
...,
config: {
timeout: 10000,
cacheable: true,
cacheTTL: 300000,
retryable: true,
maxRetries: 3,
},
});const imageAnalyzer = defineTool({
name: 'analyze_image',
description: 'Analyze image content',
schema: z.object({ media: z.string() }),
handles: ['image', 'anything...'],
handler: async ({ media }) => {
// const data = await fetchAIImageAnalyzer...
return 'The picture shows the sun rising!';
},
});Quick Jump: Define Agent · Media Support · Register
import { defineAgent, groq } from 'zaflow';
const ai = groq({ apiKey: 'xxx' }); // you can use other
const codeReviewer = defineAgent({
name: 'Code Reviewer',
provider: ai,
model: 'llama-3.3-70b-versatile',
prompt: `You are an expert code reviewer. Review code for bugs, security issues, and best practices.`,
temperature: 0.3,
tools: [lintTool, securityScanTool],
});
const contentWriter = defineAgent({
name: 'Content Writer',
provider: ai,
prompt: 'You are a creative content writer.',
temperature: 0.8,
});const visionAgent = defineAgent({
name: 'Vision Analyzer',
provider: ai,
needsMedia: ['image'],
prompt: 'You analyze images and describe their content.',
});zaflow.registerAgents([codeReviewer, contentWriter, visionAgent]);Quick Jump: Steps · Conditional · Loop · Parallel
zaflow
.step('fetch', async (ctx) => {
return await fetch('https://api.example.com/data').then((r) => r.json());
})
.step('process', async (ctx) => {
const data = ctx.previous;
return processData(data);
})
.step('respond', async (ctx) => {
return await ctx.ai(`Summarize: ${JSON.stringify(ctx.previous)}`);
});zaflow
.step('classify', async (ctx) => {
return await ctx.ai('Classify this as positive or negative: ' + ctx.input);
})
.if((ctx) => ctx.previous.includes('positive'))
.then([ZaFlow.step('celebrate', async () => '🎉 Great feedback!')])
.else([
ZaFlow.step('improve', async (ctx) => {
return await ctx.ai('Suggest improvements for: ' + ctx.input);
}),
])
.endif();zaflow.loop({
condition: (ctx) => ctx.get('retryCount', 0) < 3,
maxIterations: 5,
steps: [
zaflow.step('attempt', async (ctx) => {
const count = ctx.get('retryCount', 0);
ctx.set('retryCount', count + 1);
return await tryOperation();
}),
],
});zaflow
.parallel([
ZaFlow.step('task1', async () => await fetchFromAPI1()),
ZaFlow.step('task2', async () => await fetchFromAPI2()),
ZaFlow.step('task3', async () => await fetchFromAPI3()),
])
.step('combine', async (ctx) => {
const [result1, result2, result3] = ctx.parallel;
return combineResults(result1, result2, result3);
});Quick Jump: Image URL · Image Base64 · Media Reference
import { msg, text, image } from 'zaflow';
const messages = [
...,
msg.user([
text('What is in this image?'),
image('https://example.com/photo.jpg')
])
];
const result = await zaflow.run(messages, { mode: 'autonomous' });import { msg, text, imageBase64 } from 'zaflow';
const messages = [
...,
msg.user([
text('Describe this:'),
imageBase64(base64Data, 'image/png')
])
];
const result = await zaflow.run(messages, { mode: 'autonomous' });ZaFlow automatically uses a reference system for large media files (token efficient!):
// 20MB image is NOT copied to every agent
// Only reference ID is passed around
image(massive20MBBase64);
// Agents that need the image: needsMedia: ['image']
// → Receives full resolved data
// Agents that don't need the image
// → Media is stripped, saving tokens!// Auto (default) - raw output
const result = await zaflow.run(input, { format: 'auto' });
// JSON - parse/wrap as JSON
const result = await zaflow.run(input, { format: 'json' });
// WhatsApp - convert markdown to WhatsApp format
const result = await zaflow.run(input, { format: 'whatsapp' });Force AI to respond in a specific JSON structure with Zod schema validation:
import { ZaFlow, groq } from 'zaflow';
import { z } from 'zod';
const zaflow = new ZaFlow({
provider: groq({ apiKey: 'xxx' }),
});
// Define your expected response schema
const personSchema = z.object({
name: z.string(),
age: z.number(),
occupation: z.string(),
skills: z.array(z.string()),
});
const result = await zaflow.run('Extract info: John Doe is a 28 year old software engineer skilled in TypeScript and React', { schema: personSchema });
// result.output → raw JSON string
// result.parsed → typed & validated object!
console.log(result.parsed?.name); // "John Doe" (string)
console.log(result.parsed?.age); // 28 (number)
console.log(result.parsed?.skills); // ["TypeScript", "React"]const articleSchema = z.object({
title: z.string(),
summary: z.string().max(200),
tags: z.array(z.string()).min(1).max(5),
sentiment: z.enum(['positive', 'negative', 'neutral']),
metadata: z.object({
wordCount: z.number(),
readingTime: z.string(),
}),
});
const result = await zaflow.run('Analyze this article: ...', {
schema: articleSchema,
});Note: If the AI response doesn't match the schema,
result.parsedwill beundefined. Always check before using.
zaflow.step('myStep', async (ctx) => {
// Input & Output
ctx.input; // Original input
ctx.previous; // Previous step output
ctx.parallel; // Array of parallel results
// State Management
ctx.set('key', value); // Store value
ctx.get('key', defaultValue); // Retrieve value
ctx.has('key'); // Check existence
// Messages
ctx.messages; // Full conversation history
ctx.addMessage('user', 'Hello');
// AI Helper
const response = await ctx.ai('Your prompt here');
const response = await ctx.ai({
prompt: 'Complex prompt',
model: 'gpt-4',
temperature: 0.7,
tools: [myTool],
});
// Stats
ctx.tokens; // Token count
ctx.cost; // Estimated cost
});const result = await zaflow.run(input, {
mode: 'autonomous',
format: 'whatsapp',
maxIterations: 10,
maxToolCalls: 3,
maxTokens: 4096,
timeout: 30000,
signal: abortController.signal,
schema: responseSchema,
systemPrompt: 'You are a helpful assistant.',
onAgentEvent: (event) => {
console.log(`[${event.type}] ${event.agent || event.tool}`);
},
});Customize AI behavior without affecting library's internal prompts:
const result = await zaflow.run('Halo!', {
systemPrompt: `Kamu adalah JawiBot, asisten AI berbahasa Jawa.
Jawab semua pertanyaan dalam bahasa Jawa yang sopan.
Gunakan emoji sesekali untuk membuat percakapan lebih hidup.`,
});
// Output: "Sugeng enjing! Aku JawiBot, asisten AI sing siap mbantu sampeyan! 😊"Note:
systemPromptis injected before library's internal prompts (tools, agents), ensuring your personalization takes priority.
const result = await zaflow.run(input);
result.output; // Final response string
result.thinking; // AI's thinking process (if <think> tags used)
result.messages; // Full conversation history
result.steps; // Step-by-step execution results
result.events; // Agent/tool call events
result.duration; // Total execution time (ms)
result.stats; // Usage statistics
result.stats.tokens; // Total tokens used
result.stats.cost; // Estimated cost
result.stats.agentCalls; // Agent call counts
result.stats.toolCalls; // Tool call countsimport type {
ZaFlowConfig,
ProviderInterface,
ToolDefinition,
AgentDefinition,
ExecutionContext,
ExecutionOptions,
ExecutionResult,
Message,
StepDefinition,
} from 'zaflow';Contributions are welcome! Please follow these steps:
- Fork the repository.
- Create new branch:
git checkout -b feature/my-feature. - Commit your changes:
git commit -m 'Add some feature'. - Push to the branch:
git push origin feature/my-feature. - Open Pull Request.
If you encounter any problems or have feature requests, please open an issue
- Buy me coffee ☕
- Ko-Fi
- Trakteer
- ⭐ Star the repo on GitHub
Distributed under the MIT License. See LICENSE for details.