https://github.com/ensemble-edge/conductor
Edge-native orchestration runtime for AI components. Executes ensembles on Cloudflare's global network with sub-50ms latency.
https://github.com/ensemble-edge/conductor
agents ai cloudflare edge orchestration typescript workers
Last synced: about 2 months ago
JSON representation
Edge-native orchestration runtime for AI components. Executes ensembles on Cloudflare's global network with sub-50ms latency.
- Host: GitHub
- URL: https://github.com/ensemble-edge/conductor
- Owner: ensemble-edge
- License: apache-2.0
- Created: 2025-10-30T13:39:53.000Z (8 months ago)
- Default Branch: master
- Last Pushed: 2026-03-28T16:21:18.000Z (3 months ago)
- Last Synced: 2026-03-28T18:26:28.064Z (3 months ago)
- Topics: agents, ai, cloudflare, edge, orchestration, typescript, workers
- Language: TypeScript
- Homepage: https://docs.ensemble.ai/conductor/
- Size: 13.8 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
- Security: SECURITY.md
- Support: SUPPORT.md
- Notice: NOTICE
- Copyright: COPYRIGHT
Awesome Lists containing this project
README
# @ensemble-edge/conductor
> Edge-native AI orchestration โ built exclusively for Cloudflare Workers, not ported from Lambda.
[](https://www.npmjs.com/package/@ensemble-edge/conductor)
[](LICENSE)
## Overview
**Conductor** is an orchestration runtime that executes YAML-defined workflows at the edge using Cloudflare Workers. Purpose-built for V8 isolatesโzero cold starts, 300+ global locations, native access to Workers AI, KV, D1, R2, Durable Objects, and Vectorize.
Think of it as the runtime engine for your AI workflowsโagents are musicians, ensembles are sheet music, and Conductor performs the symphony.
### Key Features
- ๐ **Edge-Native** - Runs on Cloudflare Workers for sub-50ms latency globally
- ๐ **YAML-Driven** - Define workflows as simple, readable YAML files
- ๐ฏ **Type-Safe** - Full TypeScript support with strong typing
- ๐งช **Built-in Testing** - 1500+ tests passing, comprehensive mocks, custom matchers
- ๐ **State Management** - Built-in state sharing across agent executions
- ๐พ **Integrated Caching** - KV-based caching for performance and cost optimization
- ๐งฉ **Composable Operations** - 13 operation types: think, code, storage, data, http, tools, email, sms, html, pdf, form, queue, docs
- ๐ ๏ธ **CLI Tools** - Project scaffolding, agent generation, and upgrades
- ๐ฆ **SDK** - Client library, testing utilities, and agent factories
- ๐ **Durable Objects** - Stateful workflows with strong consistency (ExecutionState, HITL)
- โฐ **Scheduled Execution** - Cron-based ensemble triggers for automated workflows
- ๐ช **Webhooks** - HTTP triggers for ensemble execution
- ๐ค **Human-in-the-Loop** - Approval workflows with resumption support
- ๐ **Async Execution Tracking** - Real-time status tracking for long-running workflows
- ๐ฏ **Scoring System** - Quality evaluation with automatic retry logic
- ๐๏ธ **Build Triggers** - Static generation at build time
- ๐ฅ๏ธ **CLI Triggers** - Custom developer commands
- ๐ **Multi-path HTTP** - Multiple routes per ensemble
## Getting Started
### Quick Start
```bash
# Create a new project (no installation needed)
npx @ensemble-edge/ensemble conductor init my-project
cd my-project
# Install and start dev server
pnpm install
ensemble conductor start
```
> **Tip**: For CI/CD pipelines, use the `-y` flag to skip all interactive prompts:
> ```bash
> npx @ensemble-edge/conductor init my-project -y
> ```
The init command creates:
- โ
Complete project structure
- โ
10 example pages (static, dynamic, forms, SSR)
- โ
Working hello-world ensemble
- โ
Example agents and tests
- โ
Ready to run immediately
### Your Project Structure
After running `conductor init`, here's what you'll have:
```
my-project/
โโโ src/
โ โโโ index.ts # ๐ง Worker entry point (Choose: Built-in API or custom)
โ โโโ lib/ # ๐ YOUR UTILITIES - Shared helpers, utilities
โ โโโ helpers.ts # Reusable functions across agents
โ
โโโ agents/ # ๐ YOUR AGENTS - Business logic implementations
โ โโโ examples/ # Learning examples (delete when ready)
โ โโโ (your agents here) # Each agent is a folder with:
โ # - agent.yaml (configuration)
โ # - index.ts (implementation code)
โ
โโโ docs/ # ๐ API DOCUMENTATION - First-class component
โ โโโ docs.yaml # Configuration (route, UI, theme)
โ โโโ *.md # Markdown pages with Handlebars
โ
โโโ ensembles/ # ๐ YOUR WORKFLOWS - Orchestration definitions
โ โโโ hello-world.yaml # YAML files defining:
โ # - flow (execution steps)
โ # - schedules (cron triggers)
โ # - webhooks (HTTP triggers)
โ # - state (shared data)
โ
โโโ prompts/ # ๐ SHARED PROMPTS - Versioned with Edgit
โ โโโ extraction.md # Prompt templates that can be:
โ โโโ company-analysis.md # - Referenced by multiple agents
โ # - Versioned independently (v1.0.0, v2.0.0)
โ # - Reused across ensembles
โ
โโโ queries/ # ๐ SHARED SQL - Versioned with Edgit
โ โโโ company-lookup.sql # SQL queries that can be:
โ โโโ competitor-search.sql # - Referenced by multiple agents
โ # - Versioned independently
โ # - Optimized over time
โ
โโโ configs/ # ๐ SHARED CONFIGS - Versioned with Edgit
โ โโโ model-settings.yaml # Configuration files for:
โ # - Model parameters
โ # - Feature flags
โ # - Environment-specific settings
โ
โโโ schemas/ # ๐ SHARED SCHEMAS - Versioned with Edgit
โ โโโ invoice.json # JSON Schema definitions for:
โ โโโ contact.json # - Structured AI outputs
โ # - Data validation
โ # - Versioned independently
โ
โโโ wrangler.toml # ๐ง Cloudflare configuration
โโโ package.json # ๐ฆ Dependencies (@ensemble-edge/conductor, @ensemble-edge/edgit)
โโโ tsconfig.json # TypeScript config
โโโ README.md # Project documentation
```
**Where to add your components:**
| Component | Location | Created With | Purpose |
|-----------|----------|--------------|---------|
| **Agents** | `agents//` | Create folder manually | Business logic: AI, functions, API calls, data operations |
| **Ensembles** | `ensembles/.yaml` | Create YAML file manually | Workflow orchestration: define flow, schedules, webhooks |
| **Prompts** | `prompts/.md` | Create file, register with `edgit tag` | Shared prompt templates - reusable across agents/ensembles |
| **Queries** | `queries/.sql` | Create file, register with `edgit tag` | Shared SQL queries - reusable, versioned, optimized |
| **Configs** | `configs/.yaml` | Create file, register with `edgit tag` | Shared configuration - model settings, feature flags |
| **Schemas** | `schemas/.json` | Create file, register with `edgit tag` | JSON Schema for structured AI outputs - versioned, validated |
| **Utilities** | `src/lib/.ts` | Create file | Shared helper functions across your codebase |
| **Schedules** | Inside ensemble YAML | Add `schedules:` array | Cron-based automation (daily reports, monitoring) |
| **Webhooks** | Inside ensemble YAML | Add `webhooks:` array | HTTP triggers (Stripe, GitHub, external events) |
| **API Config** | `src/index.ts` | Edit file | Choose built-in API or custom endpoints |
| **Cron Triggers** | `wrangler.toml` | Copy from ensemble schedules | Register cron expressions with Cloudflare |
| **Environment Vars** | `wrangler.toml` | Edit `[vars]` section | API keys, settings, feature flags |
**Key Concepts:**
1. **Two types of components**:
- **Built-in** (inside Conductor) - Scoring agents, validators, etc. - updated when you upgrade Conductor
- **Your components** (your project) - Agents, ensembles, prompts, queries - never touched by Conductor
2. **Shared, versioned components** (๐):
- `prompts/`, `queries/`, `configs/` are **Edgit components**
- Can be referenced by multiple agents or ensembles
- Versioned independently (e.g., `extraction-prompt@v1.0.0`, `extraction-prompt@v2.0.0`)
- **Example**: 5 different agents can all use `company-analysis-prompt@v2.1.0`
3. **Agent implementations** (๐):
- `agents/` contains your business logic code
- Each agent can reference shared prompts/queries at specific versions
- **Example**: `agent.yaml` can specify `prompt: company-analysis@v2.1.0`
4. **Workflow orchestration**:
- `ensembles/` defines how agents work together
- Can reference components directly: `component: extraction-prompt@v0.1.0`
- **Example**: Mix versions - use v0.1.0 of prompt (ancient but perfect) with v3.0.0 of agent (latest)
5. **Multiple projects share Conductor**:
- Use `npx @ensemble-edge/conductor` (no installation needed)
- Use in many projects: `my-app-1/`, `my-app-2/`, `my-app-3/`
- Each project has its own agents, ensembles, prompts, queries
### Add to Existing Project
Already have a Cloudflare Worker? Add Conductor to it:
```bash
# 1. Install conductor
npm install @ensemble-edge/conductor
# 2. Create directories
mkdir -p agents ensembles
# 3. Add your first agent
ensemble conductor add agent greet --operation code
# 4. Create an ensemble
# Create ensembles/hello-world.yaml manually or use ensemble conductor add ensemble
# 5. Update your worker
```
Then in your worker (`src/index.ts`):
```typescript
import { Executor, MemberLoader } from '@ensemble-edge/conductor';
import greetConfig from '../agents/greet/agent.yaml';
import greetImpl from '../agents/greet';
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const executor = new Executor({ env, ctx });
const loader = new MemberLoader({ env, ctx });
const greet = loader.registerMember(greetConfig, greetImpl);
executor.registerMember(greet);
// Your logic here
return Response.json({ status: 'ok' });
}
};
```
**That's it!** Conductor doesn't require a specific project structure - just install and use.
## Architecture
Conductor is a single npm package with three parts:
```
@ensemble-edge/conductor
โโโ Runtime - Core orchestration engine (Executor, Parser, StateManager, Durable Objects)
โโโ CLI - Project management tools (init, add member, validate, upgrade)
โโโ SDK - Development utilities (client, testing, member factories)
```
## Runtime API
### Basic Usage
```typescript
import { Executor, MemberLoader } from '@ensemble-edge/conductor';
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
// Create executor
const executor = new Executor({ env, ctx });
// Register your agents
const loader = new MemberLoader({ env, ctx });
const greet = loader.registerMember(greetConfig, greetFunction);
executor.registerMember(greet);
// Execute ensemble
const input = await request.json();
const result = await executor.executeFromYAML(yamlContent, input);
return Response.json(result);
}
};
```
### Using Durable Objects
Export Durable Objects in your worker:
```typescript
import { Executor, ExecutionState, HITLState } from '@ensemble-edge/conductor';
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const executor = new Executor({ env, ctx });
// ... your logic
}
};
// Export Durable Objects
export { ExecutionState, HITLState };
```
## Testing
Conductor provides **first-class testing support** built into the core package. No separate testing package needed - comprehensive testing utilities are included.
### Test Infrastructure
**1500+ tests passing** covering:
- ๐งช **Unit Tests** - Core runtime, agents, state management, pages
- ๐ **Integration Tests** - End-to-end workflows, catalog loading, dynamic routing
**Test Coverage:**
- Lines: 40%+ | Functions: 40%+ | Branches: 35%+ | Statements: 40%+
- Comprehensive coverage of critical paths with mock implementations
### Quick Start
```bash
# Run all tests
npm test
# Run with coverage
npm run test:coverage
# Watch mode
npm run test:watch
```
### Writing Tests
Import testing utilities from `@ensemble-edge/conductor/testing`:
```typescript
import { describe, it, expect } from 'vitest';
import { TestConductor, registerMatchers } from '@ensemble-edge/conductor/testing';
// Register custom matchers
registerMatchers();
describe('My Ensemble', () => {
it('should execute successfully', async () => {
// Create test conductor
const conductor = await TestConductor.create({
projectPath: '.'
});
// Execute ensemble
const result = await conductor.executeEnsemble('hello-world', {
name: 'World'
});
// Use custom matchers
expect(result).toBeSuccessful();
expect(result).toHaveOutput({ message: 'Hello, World!' });
});
});
```
### Custom Matchers
Conductor provides specialized matchers for testing workflows:
```typescript
// Success/failure assertions
expect(result).toBeSuccessful();
expect(result).toBeFailed();
// Output assertions
expect(result).toHaveOutput({ key: 'value' });
expect(result).toHaveOutputContaining({ key: 'value' });
// State assertions
expect(result).toHaveState({ counter: 5 });
// Member execution
expect(result).toHaveMemberExecuted('member-name');
expect(result).toHaveMemberFailed('member-name');
// Timing assertions
expect(result).toHaveCompletedWithin(1000); // ms
```
### Mock Providers
Test without external dependencies using built-in mocks:
```typescript
import {
mockAIProvider,
mockDatabase,
mockHTTP,
mockVectorize
} from '@ensemble-edge/conductor/testing';
describe('Think Member', () => {
it('should call AI provider', async () => {
const aiMock = mockAIProvider({
response: { message: 'AI response' }
});
const conductor = await TestConductor.create({
projectPath: '.',
mocks: { ai: aiMock }
});
const result = await conductor.executeMember('analyze', {
text: 'Sample text'
});
expect(aiMock.calls).toHaveLength(1);
expect(result.output.message).toBe('AI response');
});
});
```
**Available Mocks:**
- `MockAIProvider` - Mock AI/LLM responses (Think agents)
- `MockDatabase` - Mock KV/D1/R2 operations (Data agents)
- `MockHTTPClient` - Mock HTTP requests (HTTP agents)
- `MockVectorize` - Mock vector search (RAG agents)
- `MockDurableObject` - Mock Durable Object state
### Test Configuration
Configure testing with `vitest.config.mts`:
```typescript
import { defineWorkersConfig } from '@cloudflare/vitest-pool-workers/config';
export default defineWorkersConfig({
test: {
poolOptions: {
workers: {
wrangler: { configPath: './wrangler.jsonc' }
}
},
testTimeout: 15000, // 15 seconds for Worker operations
coverage: {
provider: 'v8',
reporter: ['text', 'json', 'html', 'lcov'],
thresholds: {
lines: 40,
functions: 40,
branches: 35,
statements: 40
}
}
}
});
```
### Testing Patterns
#### Unit Testing Members
```typescript
import { APIMember } from '@ensemble-edge/conductor';
describe('API Member', () => {
it('should make HTTP request', async () => {
const mockFetch = vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ data: 'response' })
});
global.fetch = mockFetch;
const member = new APIMember({
name: 'test-api',
type: 'API',
config: { url: 'https://api.example.com/data' }
});
const result = await member.execute({
input: {},
env: {} as any,
ctx: {} as ExecutionContext
});
expect(result.success).toBe(true);
expect(mockFetch).toHaveBeenCalledWith(
'https://api.example.com/data',
expect.any(Object)
);
});
});
```
#### Integration Testing Workflows
```typescript
describe('Company Intelligence Workflow', () => {
it('should fetch and analyze company data', async () => {
const conductor = await TestConductor.create({
projectPath: '.',
mocks: {
http: mockHTTP({
'https://api.example.com/company': {
data: { name: 'Acme Corp' }
}
}),
ai: mockAIProvider({
response: { analysis: 'Strong company' }
})
}
});
const result = await conductor.executeEnsemble('company-intelligence', {
domain: 'acme.com'
});
expect(result).toBeSuccessful();
expect(result).toHaveOutput({
analysis: 'Strong company'
});
expect(result).toHaveMemberExecuted('fetch-company-data');
expect(result).toHaveMemberExecuted('analyze-company');
});
});
```
#### State Management Testing
```typescript
describe('StateManager', () => {
it('should track state access', () => {
const manager = new StateManager({
initial: { counter: 0, name: 'test' }
});
const { getPendingUpdates } = manager.getStateForMember('member1', {
use: ['counter'],
set: ['counter']
});
const { newLog } = getPendingUpdates();
const manager2 = manager.applyPendingUpdates({}, newLog);
const report = manager2.getAccessReport();
expect(report.accessPatterns['member1']).toBeDefined();
expect(report.unusedKeys).toContain('name');
});
});
```
### Test Structure
Recommended project test structure:
```
my-project/
โโโ tests/
โ โโโ unit/ # Unit tests for individual agents
โ โ โโโ agents/
โ โ โ โโโ greet.test.ts
โ โ โ โโโ analyze.test.ts
โ โ โ โโโ fetch-data.test.ts
โ โ โโโ runtime/
โ โ โโโ parser.test.ts
โ โ โโโ interpolation.test.ts
โ โ โโโ state-manager.test.ts
โ โ
โ โโโ integration/ # Integration tests for workflows
โ โโโ hello-world.test.ts
โ โโโ company-intel.test.ts
โ โโโ approval-workflow.test.ts
โ
โโโ vitest.config.mts # Test configuration
โโโ package.json
```
### Continuous Integration
Add to your CI pipeline:
```yaml
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm install
- run: npm test
- run: npm run test:coverage
```
### Debugging Tests
```bash
# Run specific test file
npx vitest run tests/unit/members/greet.test.ts
# Run tests matching pattern
npx vitest run -t "should execute"
# Debug mode
npx vitest --inspect-brk
# UI mode
npx vitest --ui
```
### Best Practices
1. **Test at the right level**
- Unit tests: Individual agents, utilities, parsing
- Integration tests: Complete workflows, state flow
2. **Use mocks for external dependencies**
- Mock AI providers to avoid API costs
- Mock databases to avoid state pollution
- Mock HTTP to avoid network flakiness
3. **Test critical paths first**
- Agent execution
- State management
- Error handling
- Configuration parsing
4. **Keep tests fast**
- Use mocks instead of real services
- Run in Workers pool for realistic execution
- Set appropriate timeouts (15s default)
5. **Maintain test coverage**
- Aim for 40%+ coverage on critical code
- Focus on business logic, not boilerplate
- Use coverage reports to find gaps
## SDK Usage
### Agent Development
```typescript
import { createFunctionAgent } from '@ensemble-edge/conductor/sdk';
export default createFunctionAgent({
async handler({ input }) {
return {
message: `Hello, ${input.name}!`
};
}
});
```
### Client Library (Call Deployed Conductors)
```typescript
import { ConductorClient } from '@ensemble-edge/conductor/sdk';
// Connect to your deployed conductor
const client = new ConductorClient({
baseUrl: 'https://my-project.example.com',
apiKey: process.env.API_KEY
});
// Execute an ensemble
const result = await client.executeEnsemble('company-intelligence', {
domain: 'acme.com'
});
// Stream results
for await (const chunk of client.streamEnsemble('analysis', input)) {
console.log(chunk);
}
```
### Testing Utilities
See the [Testing](#testing) section above for comprehensive testing examples using TestConductor, custom matchers, and mock providers.
```typescript
import { TestConductor } from '@ensemble-edge/conductor/testing';
describe('greet agent', () => {
it('should greet user', async () => {
const conductor = await TestConductor.create({ projectPath: '.' });
const result = await conductor.executeAgent('greet', {
name: 'World'
});
expect(result.success).toBe(true);
expect(result.output.message).toBe('Hello, World!');
});
});
```
### Schema Validation
Use JSON Schema in your agent.yaml for input/output validation:
```yaml
# agents/my-agent/agent.yaml
name: my-agent
operation: code
schema:
input:
type: object
properties:
domain:
type: string
required:
type: boolean
required: [domain]
output:
type: object
properties:
result:
type: string
```
Conductor automatically validates inputs/outputs against your schema at runtime.
## CLI Commands
All CLI commands are available through the unified Ensemble CLI:
### `ensemble conductor init `
Create a new Conductor project
```bash
ensemble conductor init my-project
cd my-project
pnpm install
ensemble conductor start
```
> **Tip**: For CI/automated environments, use `--yes` to skip all interactive prompts:
> ```bash
> ensemble conductor init my-project --yes
> ```
Creates a complete project with:
- 10 example pages (static, dynamic, forms)
- Working hello-world ensemble
- Example agents with implementations
- Test suite with passing examples
### `ensemble conductor add agent `
Scaffold a new agent (works in any project with Conductor installed)
```bash
ensemble conductor add agent analyze-company --operation think
ensemble conductor add agent fetch-data --operation http
ensemble conductor add agent calculate --operation code
# Create think agent with Edgit-ready prompt
ensemble conductor add agent analyze-company --operation think --with-prompt
```
**Options:**
- `-o, --operation ` - Agent operation type (code, think, http, data, storage, etc.)
- `-d, --description ` - Agent description
- `--with-prompt` - Create prompt.md file for think agents (Edgit integration)
Creates:
- `agents//agent.yaml` - Configuration
- `agents//index.ts` - Implementation template
- `agents//prompt.md` - Prompt template (with --with-prompt)
### `ensemble conductor validate`
Validate YAML syntax and agent references
```bash
ensemble conductor validate
```
Checks:
- โ
All agent.yaml files are valid YAML
- โ
Required fields present (name, operation)
- โ
Ensemble agent references exist
- โ
Schema compliance
### `ensemble conductor upgrade`
Upgrade Conductor and run migrations
```bash
ensemble conductor upgrade
# or skip confirmation
ensemble conductor upgrade --yes
```
What it does:
- ๐ก Checks for latest version
- ๐ฆ Updates package
- ๐ง Runs migration scripts
- โ
Verifies configuration
## Agent Types
### Code Agent
Execute JavaScript/TypeScript functions
```yaml
# agents/user/calculate/agent.yaml
name: calculate
operation: code
handler: ./index.ts
description: Calculate score
schema:
input:
value: number
output:
score: number
```
### Think Agent
AI reasoning with LLMs (OpenAI, Anthropic, Cloudflare AI)
```yaml
# agents/user/analyze/agent.yaml
name: analyze
operation: think
handler: ./index.ts
description: Analyze data with AI
config:
model: gpt-4
provider: openai
temperature: 0.7
```
### Storage Agent
Storage operations with KV, D1, or R2
```yaml
# agents/user/cache-lookup/agent.yaml
name: cache-lookup
operation: storage
handler: ./index.ts
description: Look up cached data
config:
storage: kv
operation: get
binding: CACHE
```
### HTTP Agent
HTTP requests to external services
```yaml
# agents/user/fetch-data/agent.yaml
name: fetch-data
operation: http
handler: ./index.ts
description: Fetch external data
config:
url: https://api.example.com/data
method: GET
headers:
Authorization: Bearer ${env.API_KEY}
```
## Ensembles
Define workflows as YAML:
```yaml
name: company-intelligence
description: Analyze company data
trigger:
- type: http
path: /api/intelligence
methods: [POST]
public: true
flow:
- name: fetch-company-data
agent: fetch-company-data
input:
domain: ${input.domain}
cache:
ttl: 3600
- name: analyze-company
agent: analyze-company
input:
companyData: ${fetch-company-data.output}
instructions: Analyze this company
output:
analysis: ${analyze-company.output.analysis}
```
## Scheduled Execution
Schedule ensembles to run automatically using cron expressions. Perfect for periodic data processing, monitoring, reports, and automated workflows.
### Configuration
Add schedules to your ensemble YAML:
```yaml
name: daily-report
description: Generate daily analytics report
schedules:
- cron: "0 9 * * *" # Every day at 9 AM UTC
timezone: "America/New_York" # Optional: timezone for cron
enabled: true
input:
reportType: "daily"
recipients: ["team@example.com"]
- cron: "0 */4 * * *" # Every 4 hours
enabled: true
input:
reportType: "hourly"
flow:
- name: generate-report
agent: generate-report
input:
type: ${input.reportType}
```
### Cron Expression Format
Standard Unix cron syntax:
```
โโโโโโโโ minute (0 - 59)
โ โโโโโโโ hour (0 - 23)
โ โ โโโโโโ day of month (1 - 31)
โ โ โ โโโโโ month (1 - 12)
โ โ โ โ โโโโ day of week (0 - 7) (Sunday = 0 or 7)
โ โ โ โ โ
* * * * *
```
**Examples:**
- `"0 9 * * *"` - Daily at 9 AM UTC
- `"*/15 * * * *"` - Every 15 minutes
- `"0 0 * * 0"` - Weekly on Sunday at midnight
- `"0 0 1 * *"` - Monthly on the 1st at midnight
- `"0 */6 * * *"` - Every 6 hours
### Worker Configuration
Add cron triggers to your `wrangler.toml`:
```toml
[triggers]
crons = [
"0 9 * * *", # Daily at 9 AM UTC
"0 */4 * * *", # Every 4 hours
"*/15 * * * *" # Every 15 minutes
]
```
**Automatic generation**: Get all cron expressions from your ensembles:
```bash
curl https://your-worker.dev/api/v1/schedules/crons/list
```
### Runtime Behavior
When a cron trigger fires:
1. ScheduleManager loads all ensembles with matching cron expressions
2. Each matching schedule executes with its configured input
3. Execution includes `_schedule` metadata:
```json
{
"reportType": "daily",
"_schedule": {
"cron": "0 9 * * *",
"timezone": "America/New_York",
"scheduledTime": 1699524000000,
"triggeredAt": 1699524001234
}
}
```
### Testing Schedules
Use the API to test schedules without waiting for cron:
```bash
# Trigger a specific ensemble's schedule manually
curl -X POST https://your-worker.dev/api/v1/schedules/daily-report/trigger \
-H "Content-Type: application/json" \
-d '{"scheduleIndex": 0}'
# Test a cron expression
curl -X POST https://your-worker.dev/api/v1/schedules/test \
-H "Content-Type: application/json" \
-d '{"cron": "0 9 * * *", "timezone": "America/New_York"}'
```
## Webhooks
Trigger ensemble execution via HTTP webhooks. Perfect for integrations, event-driven workflows, and external system notifications.
### Configuration
Add webhooks to your ensemble YAML:
```yaml
name: process-payment
description: Process payment webhook from Stripe
webhooks:
- path: "/stripe-payment"
method: POST
auth:
type: signature
secret: ${env.STRIPE_WEBHOOK_SECRET}
async: true # Return immediately, execute in background
timeout: 30000 # 30 second timeout
- path: "/github-push"
method: POST
auth:
type: bearer
secret: ${env.GITHUB_TOKEN}
mode: trigger # 'trigger' (default) or 'resume' (HITL)
flow:
- name: validate-payment
agent: validate-payment
input:
paymentData: ${input.data}
```
### Authentication Types
**Bearer Token:**
```yaml
auth:
type: bearer
secret: ${env.API_SECRET}
```
Request requires: `Authorization: Bearer `
**Signature Verification:**
```yaml
auth:
type: signature
secret: ${env.WEBHOOK_SECRET}
```
Validates `X-Signature` header (Stripe-style HMAC).
**Basic Auth:**
```yaml
auth:
type: basic
secret: ${env.BASIC_AUTH_CREDENTIALS}
```
Request requires: `Authorization: Basic `
### Webhook Modes
**Trigger Mode** (default): Start new execution
```yaml
mode: trigger
```
**Resume Mode**: Resume HITL workflow
```yaml
mode: resume
```
Used with Human-in-the-Loop workflows to resume after approval.
### Webhook URLs
Webhooks are available at:
```
https://your-worker.dev/webhooks/{path}
```
Example:
```bash
curl -X POST https://your-worker.dev/webhooks/stripe-payment \
-H "Authorization: Bearer your-secret" \
-H "Content-Type: application/json" \
-d '{"amount": 1000, "currency": "usd"}'
```
### Async Execution
Set `async: true` for long-running workflows:
```yaml
webhooks:
- path: "/long-process"
async: true
```
Returns immediately with execution ID:
```json
{
"status": "accepted",
"executionId": "exec_abc123",
"statusUrl": "/api/v1/executions/exec_abc123"
}
```
## Durable Objects & Stateful Workflows
Conductor uses Cloudflare Durable Objects for strongly consistent, stateful workflow tracking. Two Durable Object types provide different state management patterns.
### ExecutionState
Tracks async execution state with real-time status queries and optional WebSocket streaming.
**Use cases:**
- Long-running workflow monitoring
- Real-time progress updates
- Execution history and metrics
- Status dashboards
**Configuration:**
```toml
# wrangler.toml
[[durable_objects.bindings]]
name = "EXECUTION_STATE"
class_name = "ExecutionState"
script_name = "conductor"
[[migrations]]
tag = "v1"
new_classes = ["ExecutionState"]
```
**Usage:**
```typescript
import { Executor } from '@ensemble-edge/conductor';
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const executor = new Executor({ env, ctx });
// Start async execution with tracking
const executionId = crypto.randomUUID();
const result = await executor.executeEnsemble(ensemble, input, {
async: true,
executionId
});
// Return execution ID for status tracking
return Response.json({
executionId,
statusUrl: `/api/v1/executions/${executionId}`
});
}
};
```
**Query execution status:**
```bash
# Get current status
curl https://your-worker.dev/api/v1/executions/exec_abc123
# Stream live updates via WebSocket
wscat -c wss://your-worker.dev/api/v1/executions/exec_abc123/stream
```
**Status response:**
```json
{
"executionId": "exec_abc123",
"ensembleName": "process-payment",
"status": "running",
"startedAt": 1699524000000,
"currentStep": "validate-payment",
"stepIndex": 2,
"totalSteps": 5,
"outputs": {
"fetch-data": { "result": "..." }
},
"metrics": {
"duration": 1234,
"stepsCompleted": 2
}
}
```
### HITLState (Human-in-the-Loop)
Manages approval workflows and human intervention points with resumption support.
**Use cases:**
- Approval workflows
- Human review gates
- Manual intervention points
- Compliance checkpoints
**Configuration:**
```toml
# wrangler.toml
[[durable_objects.bindings]]
name = "HITL_STATE"
class_name = "HITLState"
script_name = "conductor"
[[migrations]]
tag = "v1"
new_classes = ["ExecutionState", "HITLState"]
```
**Ensemble configuration:**
```yaml
name: expense-approval
description: Expense approval workflow with human review
flow:
- name: validate-expense
agent: validate-expense
input:
expense: ${input.expense}
- name: request-approval
agent: request-approval
operation: hitl
input:
requester: ${input.userId}
amount: ${input.expense.amount}
reason: ${input.expense.reason}
approvers: ["manager@example.com"]
timeout: 86400000 # 24 hours
- name: process-approved-expense
agent: process-approved-expense
input:
expense: ${input.expense}
```
**HITL workflow:**
1. Execution pauses at HITL step
2. Approval request sent to designated approvers
3. Execution waits for approval/rejection
4. Resume with decision
**Resume execution:**
```bash
# Approve
curl -X POST https://your-worker.dev/api/v1/executions/exec_abc123/resume \
-H "Content-Type: application/json" \
-d '{"approved": true, "comment": "Looks good"}'
# Reject
curl -X POST https://your-worker.dev/api/v1/executions/exec_abc123/resume \
-H "Content-Type: application/json" \
-d '{"approved": false, "comment": "Needs more detail"}'
```
**HITL state:**
```json
{
"executionId": "exec_abc123",
"status": "pending_approval",
"requestedAt": 1699524000000,
"approvers": ["manager@example.com"],
"timeout": 86400000,
"context": {
"amount": 1000,
"reason": "Conference travel"
}
}
```
### Storage Backend
Durable Objects use Cloudflare's strongly consistent storage:
- **Single-threaded execution** - No race conditions
- **Transactional storage** - Atomic state updates
- **Global uniqueness** - One instance per execution ID
- **Automatic failover** - Cloudflare handles migration
- **Low latency** - Co-located with execution
## API Endpoints
Conductor provides a comprehensive REST API for workflow management.
### Base Configuration
```toml
# wrangler.toml
[vars]
API_KEYS = "key1,key2,key3" # Optional: API key authentication
ALLOW_ANONYMOUS = "false" # Require authentication
DISABLE_LOGGING = "false" # Enable request logging
```
### Execution API
**POST /api/v1/execute**
Execute an ensemble:
```bash
curl -X POST https://your-worker.dev/api/v1/execute \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"ensemble": "company-intelligence",
"input": {
"domain": "acme.com"
},
"async": true
}'
```
Response:
```json
{
"executionId": "exec_abc123",
"status": "accepted",
"statusUrl": "/api/v1/executions/exec_abc123"
}
```
**GET /api/v1/executions/:id**
Get execution status:
```bash
curl https://your-worker.dev/api/v1/executions/exec_abc123 \
-H "X-API-Key: your-api-key"
```
**POST /api/v1/executions/:id/resume**
Resume HITL execution:
```bash
curl -X POST https://your-worker.dev/api/v1/executions/exec_abc123/resume \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"approved": true, "comment": "Approved"}'
```
**DELETE /api/v1/executions/:id**
Cancel execution:
```bash
curl -X DELETE https://your-worker.dev/api/v1/executions/exec_abc123 \
-H "X-API-Key: your-api-key"
```
### Schedule API
**GET /api/v1/schedules**
List all scheduled ensembles:
```bash
curl https://your-worker.dev/api/v1/schedules \
-H "X-API-Key: your-api-key"
```
**GET /api/v1/schedules/:ensembleName**
Get schedules for specific ensemble:
```bash
curl https://your-worker.dev/api/v1/schedules/daily-report \
-H "X-API-Key: your-api-key"
```
**POST /api/v1/schedules/:ensembleName/trigger**
Manually trigger a schedule:
```bash
curl -X POST https://your-worker.dev/api/v1/schedules/daily-report/trigger \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{"scheduleIndex": 0}'
```
**GET /api/v1/schedules/crons/list**
Get all cron expressions for wrangler.toml:
```bash
curl https://your-worker.dev/api/v1/schedules/crons/list \
-H "X-API-Key: your-api-key"
```
### Member API
**GET /api/v1/agents**
List available agents:
```bash
curl https://your-worker.dev/api/v1/agents \
-H "X-API-Key: your-api-key"
```
**GET /api/v1/agents/:name**
Get agent details:
```bash
curl https://your-worker.dev/api/v1/agents/analyze-company \
-H "X-API-Key: your-api-key"
```
### Stream API
**POST /api/v1/stream**
Stream ensemble execution with Server-Sent Events:
```bash
curl -N -X POST https://your-worker.dev/api/v1/stream \
-H "Content-Type: application/json" \
-H "X-API-Key: your-api-key" \
-d '{
"ensemble": "analysis",
"input": {"data": "..."}
}'
```
### Health API
**GET /health**
Health check (no authentication required):
```bash
curl https://your-worker.dev/health
```
Response:
```json
{
"status": "healthy",
"timestamp": 1699524000000,
"version": "1.0.0"
}
```
### Webhook API
**POST /webhooks/:path**
Trigger webhook (authentication per webhook config):
```bash
curl -X POST https://your-worker.dev/webhooks/stripe-payment \
-H "Authorization: Bearer your-webhook-secret" \
-H "Content-Type: application/json" \
-d '{"event": "payment.succeeded"}'
```
## Platform Architecture
Conductor uses a **three-layer architecture** that cleanly separates AI providers, cloud platforms, and core interfaces. This design allows you to use any AI model from any provider while leveraging platform-specific features like Cloudflare's AI Gateway.
### Three Layers
```
catalog/ # Reference data (packaged with npm)
โโโ ai/ # Layer 1: AI provider catalogs
โ โโโ manifest.json
โ โโโ workers-ai.json
โ โโโ openai.json
โ โโโ anthropic.json
โ โโโ groq.json
โโโ cloud/ # Layer 2: Cloud platform configs
โโโ cloudflare/
โโโ ai-gateway.json
โโโ capabilities.json
โโโ bindings.json
src/platforms/ # TypeScript source (compiles to dist)
โโโ base/ # Layer 3: Core interfaces
โ โโโ platform.ts
โ โโโ types.ts
โ โโโ index.ts
โโโ cloudflare/ # Cloudflare adapter
โโโ index.ts
```
#### Layer 1: AI Providers (`catalog/ai/`)
**AI provider catalogs** containing model lists, capabilities, and deprecation tracking. Each provider is a separate JSON file:
- **workers-ai.json** - Cloudflare's edge-hosted models (Llama, Mistral, Qwen, etc.)
- **openai.json** - GPT-4, GPT-4o, GPT-3.5, o1
- **anthropic.json** - Claude 3.5, Claude 3
- **groq.json** - Ultra-fast inference (Llama, Mixtral, Gemma)
- **manifest.json** - Registry of all providers with default routing
**Key insight**: Workers AI is just another AI provider, not a special platform feature. All providers are treated equally in configuration.
#### Layer 2: Cloud Platforms (`catalog/cloud/`)
**Infrastructure platform configurations** like Cloudflare Workers, including:
- AI Gateway configuration (routing, caching, analytics)
- Platform capabilities (bindings, storage, compute)
- Platform-specific features
#### Layer 3: Base Interfaces (`src/platforms/base/`)
**TypeScript interfaces and types** that all platform adapters must implement. Source code that compiles to `dist/platforms/base/`:
- **platform.ts** - BasePlatform abstract class
- **types.ts** - PlatformModel, PlatformProvider, ValidationResult types
- **index.ts** - Public exports
### AI Provider Configuration
Each AI provider file contains:
```json
{
"provider": "openai",
"name": "OpenAI",
"defaultRouting": "cloudflare-gateway",
"endpoints": {
"direct": {
"baseUrl": "https://api.openai.com/v1"
},
"cloudflare-gateway": {
"baseUrl": "https://gateway.ai.cloudflare.com/v1/{account}/{gateway}/openai",
"features": ["caching", "rate-limiting", "analytics"]
}
},
"models": [
{
"id": "gpt-4o",
"name": "GPT-4o",
"status": "active",
"capabilities": ["chat", "vision", "function-calling"],
"contextWindow": 128000,
"recommended": true
}
]
}
```
### Routing Modes
Conductor supports **three routing modes** for accessing AI models:
#### 1. `cloudflare` - Platform-Native (Workers AI only)
Direct access to Cloudflare's edge-hosted models via Workers AI binding.
```yaml
# agents/user/analyze/agent.yaml
operation: think
config:
provider: workers-ai
model: "@cf/meta/llama-3.1-8b-instruct"
routing: cloudflare # Platform-native
```
**Benefits**: Ultra-low latency, no API keys needed, edge execution
#### 2. `cloudflare-gateway` - AI Gateway (Recommended for external providers)
Route external provider requests through Cloudflare AI Gateway for caching, analytics, and cost controls.
```yaml
# agents/user/analyze/agent.yaml
operation: think
config:
provider: openai
model: gpt-4o
routing: cloudflare-gateway # Default for external providers
```
**Benefits**:
- Persistent caching (reduce costs & latency)
- Real-time analytics and logging
- Rate limiting and cost controls
- Single API for multiple providers
- Automatic failover and retries
#### 3. `direct` - Direct API Calls
Make direct API calls to the provider, bypassing the gateway.
```yaml
# agents/user/analyze/agent.yaml
operation: think
config:
provider: anthropic
model: claude-3-5-sonnet-20241022
routing: direct # Bypass gateway
```
**Use when**: You need provider-specific features not available through the gateway, or you're not on Cloudflare.
### Smart Routing Defaults
Conductor uses intelligent defaults based on provider and platform:
| Provider | Default Routing | Reason |
|----------|----------------|---------|
| workers-ai | `cloudflare` | Platform-native, edge-hosted |
| openai | `cloudflare-gateway` | Leverage caching & analytics |
| anthropic | `cloudflare-gateway` | Leverage caching & analytics |
| groq | `cloudflare-gateway` | Leverage analytics |
**You can override these defaults** by specifying `routing:` in your agent config.
### Cloudflare AI Gateway
The AI Gateway acts as a **universal API gateway** for AI providers:
```
Your Worker โ AI Gateway โ AI Provider (OpenAI/Anthropic/Groq/etc.)
โ
[Cache/Analytics/Logs]
```
**Features**:
- โ
Persistent caching (same request = cached response)
- โ
Real-time analytics (tokens, costs, latency)
- โ
Rate limiting per user/endpoint
- โ
Cost controls and spending alerts
- โ
Automatic retry on failures
- โ
Fallback between providers
- โ
Request/response logging
**Configuration**: Set environment variables for gateway access:
```toml
# wrangler.toml
[vars]
CLOUDFLARE_ACCOUNT_ID = "your-account-id"
CLOUDFLARE_GATEWAY_NAME = "your-gateway-name"
```
### Model Deprecation Tracking
Conductor tracks model lifecycle and warns you about deprecated models:
```bash
$ conductor check-config
โ agents/user/analyze/agent.yaml
Model "gpt-4-turbo-preview" is deprecated
Reason: Replaced by stable gpt-4-turbo release
End of life: 2025-04-09 (120 days)
โ Recommended: "gpt-4-turbo"
```
Each model in the catalog includes:
- `status`: "active" | "deprecated"
- `deprecatedAt`: When deprecation was announced
- `deprecatedReason`: Why it was deprecated
- `replacementModel`: Recommended replacement
- `endOfLife`: When the model will stop working
**Stay up to date**: Run `conductor upgrade` to get the latest model catalogs.
### Using Multiple Providers
You can mix and match providers in a single ensemble:
```yaml
name: multi-provider-analysis
flow:
# Fast, cheap analysis with Workers AI
- name: quick-scan
agent: quick-scan
input:
provider: workers-ai
model: "@cf/meta/llama-3.1-8b-instruct"
# Deep analysis with Claude
- name: deep-analysis
agent: deep-analysis
input:
provider: anthropic
model: claude-3-5-sonnet-20241022
routing: cloudflare-gateway
# Reasoning with OpenAI o1
- name: strategic-thinking
agent: strategic-thinking
input:
provider: openai
model: o1-preview
routing: cloudflare-gateway
```
### Platform-Specific Features
While AI providers are universal, platforms can provide additional features:
**Cloudflare Workers**:
- Workers AI binding for edge models
- AI Gateway for external providers
- KV/D1/R2 for Data agents
- Durable Objects for state
- Automatic global distribution
**Future platforms** (Vercel, AWS Lambda, etc.) will have their own cloud platform configurations while sharing the same AI provider catalogs.
### Best Practices
1. **Use cloudflare-gateway by default** for external providers (OpenAI, Anthropic, Groq)
- Get caching, analytics, and cost controls for free
- No code changes needed
2. **Use workers-ai for speed** when sub-50ms latency matters
- Edge-hosted models run closest to users
- No API key management needed
3. **Check for deprecations regularly**
```bash
conductor check-config
conductor upgrade
```
4. **Pin model versions in production**
```yaml
model: claude-3-5-sonnet-20241022 # Good: Specific version
# vs
model: claude-3-5-sonnet # Risky: Auto-updates
```
5. **Monitor costs with AI Gateway**
- View analytics in Cloudflare dashboard
- Set spending limits and alerts
- Track per-user or per-endpoint usage
## Edgit Integration
Conductor works seamlessly with Edgit for versioning prompts, configs, and member configurations. This enables powerful versioning chains where ensembles reference versioned members, which in turn reference versioned prompts.
**Key Principle: Edgit creates and manages git tags. That's it.** GitHub Actions handles deployment. Edgit is a tag manager, not a deployment tool.
### Quick Example
```bash
# Version a prompt
edgit tag create company-analysis-prompt v1.0.0
# Deploy to staging
edgit tag set company-analysis-prompt staging v1.0.0
edgit push --tags --force
# Promote to production
edgit tag set company-analysis-prompt production v1.0.0
edgit push --tags --force
```
### What Gets Versioned with Edgit?
**Edgit Components** (versioned artifacts in shared folders):
- โ
**Prompts** (`prompts/*.md`) - Shared prompt templates, reusable across agents/ensembles
- โ
**Queries** (`queries/*.sql`) - Shared SQL queries, reusable and optimized
- โ
**Configs** (`configs/*.yaml`) - Shared configuration files, model settings
- โ
**Agent configurations** (`agents/*/agent.yaml`) - Agent config files
**NOT Edgit Components** (code in your repo):
- โ **Agent implementations** (`agents/*/index.ts`) - Code is git-versioned and bundled with worker
- โ **Ensembles** (`ensembles/*.yaml`) - Workflow definitions live in git
- โ **Worker code** (`src/*`) - Application code lives in git
**Key Insight:** Prompts, queries, and configs live in **their own folders** at the project root, not inside individual agents. This enables **reuse** - multiple agents can reference the same prompt at different versions.
**Example: Shared Prompt Reuse**
```
my-project/
โโโ prompts/
โ โโโ company-analysis.md # Shared prompt, versioned v1.0.0, v2.0.0, v2.1.0
โโโ agents/
โ โโโ analyze-tech-company/
โ โ โโโ agent.yaml # Uses: company-analysis-prompt@v2.1.0
โ โโโ analyze-startup/
โ โ โโโ agent.yaml # Uses: company-analysis-prompt@v2.0.0
โ โโโ quick-company-check/
โ โโโ agent.yaml # Uses: company-analysis-prompt@v1.0.0
```
All three agents share the same prompt source file, but reference different versions based on what works best for their use case.
### The Versioning Chain
Conductor supports a complete versioning chain from ensemble โ agent config โ prompts:
```
Ensemble (git-versioned)
โ references
Agent Config@v1.0.0 (Edgit component)
โ references
Prompt@v2.1.0 (Edgit component)
```
**Example:**
```yaml
# ensembles/company-intel.yaml (git)
flow:
- name: analyze-company-step
agent: analyze-company@production
input:
domain: ${input.domain}
```
```yaml
# agents/user/analyze-company/agent.yaml (Edgit component)
name: analyze-company
operation: think
config:
model: gpt-4
temperature: 0.7
prompt: company-analysis-prompt@v2.1.0 # References versioned prompt
```
```markdown
# prompts/company-analysis.md (Edgit component)
You are an expert at analyzing companies...
```
**Runtime Resolution:**
1. Executor reads ensemble: `analyze-company@production`
2. Loads agent.yaml from Edgit: `analyze-company@production` โ v1.0.0
3. Agent.yaml references: `company-analysis-prompt@v2.1.0`
4. Loads prompt from Edgit: `company-analysis-prompt@v2.1.0`
5. Executes bundled code (index.ts) with resolved config + prompt
### Three Integration Patterns
#### Pattern 1: Inline (Simple)
Config lives directly in agent.yaml - no versioning needed.
```yaml
# agents/user/simple-analysis/agent.yaml
name: simple-analysis
operation: think
config:
model: gpt-4
temperature: 0.7
```
**Use when**: Configuration is simple and doesn't change often.
#### Pattern 2: Edgit Reference (Planned)
> **Note**: Edgit runtime integration is planned for a future release. The API below shows the intended interface.
Agent loads versioned prompt from Edgit:
```typescript
// agents/user/company-analysis/index.ts
import { createThinkAgent } from '@ensemble-edge/conductor/sdk';
// import { loadComponent } from '@ensemble-edge/edgit'; // Future
export default createThinkAgent({
async handler({ input, env }) {
// Future: Load versioned prompt from Edgit
// const prompt = await loadComponent('company-analysis-prompt@v1.2.0', env);
// For now: Load from local file or inline
const prompt = "You are an expert at analyzing companies...";
// Use with AI
const response = await callAI(prompt, input);
return response;
}
});
```
**Use when**: Prompts need versioning, testing, and independent deployment.
#### Pattern 3: Co-located Development (Current Approach)
During development, keep prompts with agents or in shared prompts directory.
```bash
# 1. Create agent with prompt file
ensemble conductor add agent analyze-company --operation think --with-prompt
# 2. Develop and test locally
# Edit agents/user/analyze-company/prompt.md
# Edit agents/user/analyze-company/index.ts
# 3. Load prompt in your agent implementation
# Read prompt.md from filesystem or include inline in agent config
# 4. Future: When Edgit integration is complete
# Version prompt: edgit tag create analyze-company-prompt v1.0.0
# Load at runtime: loadComponent('analyze-company-prompt@v1.0.0', env)
```
**Use when**: Starting new think agents - currently the recommended approach until Edgit runtime integration is complete.
### Example: Think Agent with Edgit (Planned)
> **Note**: Full Edgit runtime integration coming soon. Currently use inline prompts or load from local files.
```typescript
import { createThinkAgent } from '@ensemble-edge/conductor/sdk';
// import { loadComponent } from '@ensemble-edge/edgit'; // Future
export default createThinkAgent({
async handler({ input, state, env }) {
// Future: Load versioned prompt from Edgit
// const systemPrompt = await loadComponent('analysis-system-prompt@v2.1.0', env);
// Current: Inline prompt or load from agent config
const systemPrompt = "You are an expert analyst...";
// Combine with dynamic context
const messages = [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: `Analyze: ${input.data}` }
];
// Use with AI provider
const response = await env.AI.run('@cf/meta/llama-2-7b-chat-int8', {
messages,
temperature: 0.7
});
return {
content: response.result
};
}
});
```
### Versioning Scenarios
#### Scenario 1: Deploy New Config Without Code Changes
```bash
# Update agent config with new model settings
edgit tag create analyze-company v2.0.0
# agent.yaml v2.0.0
config:
model: gpt-4-turbo # โ Changed
temperature: 0.5 # โ Changed
prompt: company-analysis-prompt@v2.1.0
# Deploy to preview
edgit tag set analyze-company preview v2.0.0
edgit push --tags --force
# Test, then promote to production
edgit tag set analyze-company production v2.0.0
edgit push --tags --force
```
**No code deploy needed!** Same bundled code, different config.
#### Scenario 2: A/B Test Different Configurations
```yaml
# ensembles/company-intel.yaml
flow:
# 90% use stable config
- name: analyze-stable
agent: analyze-company@v1.0.0
weight: 90
input:
domain: ${input.domain}
# 10% test new config
- name: analyze-new
agent: analyze-company@v2.0.0
weight: 10
input:
domain: ${input.domain}
```
**Test in production** with gradual rollout.
#### Scenario 3: Environment-Specific Configs
```bash
# Production: Stable model, proven prompt
edgit tag set analyze-company production v1.0.0
edgit push --tags --force
# v1.0.0 โ model: gpt-4, prompt@v1.0.0
# Staging: Latest model, new prompt
edgit tag set analyze-company staging v2.0.0
edgit push --tags --force
# v2.0.0 โ model: gpt-4-turbo, prompt@v2.0.0
# Preview: Experimental settings
edgit tag set analyze-company preview v3.0.0-beta
edgit push --tags --force
# v3.0.0-beta โ model: claude-3-opus, prompt@v3.0.0-beta
```
**Same ensemble, different configs per environment:**
```yaml
flow:
- name: analyze-prod
agent: analyze-company@production # Uses v1.0.0
- name: analyze-staging
agent: analyze-company@staging # Uses v2.0.0
- name: analyze-preview
agent: analyze-company@preview # Uses v3.0.0-beta
```
#### Scenario 4: Independent Rollbacks
```bash
# Rollback just the prompt (keep agent config)
edgit tag create company-analysis-prompt v2.0.1
edgit tag set company-analysis-prompt production v2.0.1
edgit push --tags --force
# agent.yaml stays at v1.0.0, uses new prompt
# Rollback entire agent config
edgit tag set analyze-company production v0.9.0
edgit push --tags --force
# Rolls back model, temperature, AND prompt reference
# Emergency: rollback prompt instantly
edgit tag set company-analysis-prompt production v1.0.0
edgit push --tags --force
```
### Versioning Workflow
**Development to Production:**
```bash
# 1. Create agent locally
ensemble conductor add agent analyze-company --operation think --with-prompt
# 2. Develop and test with local files
# Edit agents/user/analyze-company/agent.yaml
# Edit agents/user/analyze-company/prompt.md
# 3. Version the prompt
edgit tag create company-analysis-prompt v1.0.0
# 4. Update agent.yaml to reference versioned prompt
# config:
# prompt: company-analysis-prompt@v1.0.0
# 5. Version the agent config
edgit tag create analyze-company v1.0.0
# 6. Deploy to staging
edgit tag set analyze-company staging v1.0.0
edgit push --tags --force
edgit tag set company-analysis-prompt staging v1.0.0
edgit push --tags --force
# 7. Update ensemble to use versioned agent
# flow:
# - name: analyze
# agent: analyze-company@staging
# 8. Test, then promote to production
edgit tag set analyze-company production v1.0.0
edgit push --tags --force
edgit tag set company-analysis-prompt production v1.0.0
edgit push --tags --force
```
## Multi-Project Workflow
Build multiple projects on Conductor:
```bash
ensemble conductor init owner-oiq
ensemble conductor init owner-internal
ensemble conductor init customer-portal
```
Each project is independent with its own agents and ensembles. Conductor is just the engine.
## Development
```bash
# Install dependencies
npm install
# Build runtime + SDK
npm run build
# Test
npm test
# Generate Cloudflare types
npm run cf-typegen
```
## Philosophy
- **Conductor** = The runtime engine (this package)
- **Agents** = Your code (your repository)
- **Ensembles** = Your workflows (your YAML)
- **CLI** = Your development workflow
- **SDK** = Your development utilities
We provide the tools, you provide the creativity.
## Examples
See [examples/](./examples/) for:
- Complete starter project
- Agent implementations (all types)
- Ensemble workflows
- Testing examples
## Links
- [Full Documentation](https://docs.ensemble.ai/conductor/overview)
- [Getting Started](https://docs.ensemble.ai/conductor/getting-started/your-first-project)
- [Operations Reference](https://docs.ensemble.ai/conductor/operations/overview)
- [Starter Kit](https://docs.ensemble.ai/conductor/starter-kit/overview)
- [Examples](./examples/)
- [Issues](https://github.com/ensemble-edge/conductor/issues)
- [Edgit Integration](https://github.com/ensemble-edge/edgit)
## License
Apache 2.0 - see [LICENSE](LICENSE)
## Key Architectural Decisions
### Versioning Strategy
**Code (Git):**
- Agent implementations (index.ts)
- Ensemble workflows (YAML)
- Worker entry points
**Configuration (Edgit):**
- Agent configs (agent.yaml) - Version independently
- Prompts (prompt.md) - Version independently
- SQL queries, templates - Version independently
**Benefits:**
- ๐ Deploy config changes without code deploy
- ๐งช A/B test different configurations
- ๐ Environment-specific settings
- โก Instant rollbacks (configs OR code)
- ๐ Mix optimal versions from any timeline
### The Power of Separation
**Traditional:**
```
v2.0.0 deployment
โโโ All code at v2.0.0
โโโ All configs at v2.0.0 โ Locked together
```
**With Conductor + Edgit:**
```
Your deployment
โโโ Code (bundled with worker)
โโโ Agent config@v1.0.0 โ
Independent
โโโ Agent config@v2.0.0 โ
Independent
โโโ Prompt@v0.1.0 โ
Ancient but perfect
โโโ Prompt@v3.0.0 โ
Latest
```
**Access any version, any time, in any combination.**
## Trademark
Ensembleยฎ is a registered trademark of Higinio O. Maycotte.