file: ./content/docs/core/contributing.mdx
meta: {
"title": "Contributing",
"description": "Contributing to Daydreams."
}
## Contributing
To contribute to Daydreams, please review our development guidelines and
submission process.
If you are a developer and would like to contribute with code, please check out
our [GitHub repository](https://github.com/daydreamsai/daydreams) and open an
issue to discuss before opening a Pull Request.
## Star History
file: ./content/docs/core/first-agent.mdx
meta: {
"title": "Your first agent",
"description": "Build your first Daydreams agent."
}
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
## Overview
Daydreams is a framework for building autonomous AI agents. At its core, an
agent operates through a continuous cycle:
1. **Analyzes** incoming information (inputs)
2. **Reasons** about it using a Large Language Model (LLM)
3. **Decides** on the next steps - either generating a response (output) or
performing a task (action)
4. **Feeds results** back into the agent's awareness, creating a continuous loop
orchestrated by the LLM
This enables you to build agents that can interact with various systems like
blockchains, social media platforms, APIs, and more, all based on predefined
goals and contextual understanding.
## Installation
Install the core Daydreams packages:
pnpm add @daydreamsai/core @daydreamsai/clinpm install @daydreamsai/core @daydreamsai/clibun add @daydreamsai/core @daydreamsai/cliyarn add @daydreamsai/core @daydreamsai/cli
You'll also need an LLM provider SDK. For this guide, we'll use OpenAI:
pnpm add @ai-sdk/openainpm install @ai-sdk/openaibun add @ai-sdk/openaiyarn add @ai-sdk/openai
**Important:** Make sure you have an `OPENAI_API_KEY` environment variable set
before proceeding.
## Core Concepts
Daydreams is built around several key components that work together:
### Essential Components
* **[Agent Lifecycle](/docs/core/concepts/agent-lifecycle)** - The central
orchestrator that runs the main loop
* **[Contexts](/docs/core/concepts/contexts)** - Manages state and memory for
specific tasks or interactions (e.g., a chat session)
* **[Inputs](/docs/core/concepts/inputs)** - How agents receive information
(e.g., CLI messages, API events)
* **[Outputs](/docs/core/concepts/outputs)** - How agents respond or send
information (e.g., CLI responses, tweets)
* **[Actions](/docs/core/concepts/actions)** - Tasks agents can perform (e.g.,
calling APIs, executing transactions)
* **[Memory](/docs/core/concepts/memory)** - How agents store and recall
information (working memory, episodic memory)
For detailed information about these concepts, visit the
[Core Concepts](/docs/core/concepts/core) section.
## Your First Agent (CLI Echo Bot)
Let's build a simple agent that echoes back whatever you type in the command
line. This example demonstrates the basic structure and workflow of a Daydreams
agent.
### Step 1: Set up your project
```bash title="create-project.sh"
mkdir my-first-agent && cd my-first-agent
```
pnpm add @daydreamsai/core @daydreamsai/cli @ai-sdk/openai zod
npm install @daydreamsai/core @daydreamsai/cli @ai-sdk/openai zod
bun add @daydreamsai/core @daydreamsai/cli @ai-sdk/openai
yarn add @daydreamsai/core @daydreamsai/cli @ai-sdk/openai zod
### Step 2: Create your agent
Create a file named `agent.ts`:
```typescript title="agent.ts"
import { createDreams, context, input, output } from "@daydreamsai/core";
import { cliExtension } from "@daydreamsai/cli";
import { openai } from "@ai-sdk/openai";
import { z } from "zod";
// 1. Define the main context for our agent
const echoContext = context({
type: "echo",
// No specific arguments needed for this simple context
schema: z.object({}),
// Instructions that guide the LLM's behavior
instructions:
"You are a simple echo bot. Repeat the user's message back to them.",
});
// 2. Create the agent instance
const agent = createDreams({
// Configure the LLM model to use
model: openai("gpt-4o-mini"),
// Include the CLI extension for input/output handling
extensions: [cliExtension],
// Register our custom context
contexts: [echoContext],
});
// 3. Start the agent and run the context
async function main() {
// Initialize the agent (sets up services like readline)
await agent.start();
console.log("Echo agent started. Type 'exit' to quit.");
// Run our echo context
// The cliExtension automatically handles console input/output
await agent.run({
context: echoContext,
args: {}, // Empty object since our schema requires no arguments
});
// Agent stops when the input loop breaks (e.g., user types "exit")
console.log("Agent stopped.");
}
// Start the application
main();
```
### Step 3: Run your agent
Ensure your `OPENAI_API_KEY` environment variable is set, then run:
```bash title="run-agent.sh"
node agent.ts
```
Your agent will start listening for input. Type any message and watch as the
agent echoes it back using the LLM and CLI handlers provided by the
`cliExtension`.
***
## Next Steps
Continue learning about Daydreams with these resources:
* **[Core Concepts](/docs/core/concepts/core)** - Deep dive into Daydreams
architecture
* **[Advanced Features](/docs/core/advanced/introduction)** - More complex
examples and advanced usage patterns
file: ./content/docs/core/index.mdx
meta: {
"title": "About Daydreams",
"description": "TypeScript framework for building autonomous AI agents with unified LLM provider support."
}
> ⚠️ **Warning**: This is alpha software under active development. Expect
> frequent breaking changes and bugs. The API is not yet stable.
## What is an AI Agent?
Think of an AI agent as a smart assistant that can:
* **Listen** for events (Discord messages, API calls, timers)
* **Think** about what to do (using AI models like GPT)
* **Take action** (call APIs, send responses, update databases)
* **Remember** what happened for future interactions
## Real Examples
Here are some agents you could build with Daydreams:
### Discord Weather Bot
```typescript title="weather-bot.ts"
// When someone says "what's the weather?"
// → Agent calls weather API
// → Agent responds: "It's 72°F and sunny in San Francisco"
```
### Trading Assistant
```typescript title="trading-bot.ts"
// When market conditions change
// → Agent analyzes data
// → Agent executes trades or sends alerts
```
### Customer Support Bot
```typescript title="support-bot.ts"
// When customer sends message
// → Agent checks knowledge base
// → Agent provides help or escalates to human
```
## How Daydreams Works
Daydreams provides the building blocks to create these agents:
* **Contexts** - Manage different conversation sessions or tasks
* **Inputs** - Listen for Discord messages, API calls, timers, etc.
* **Outputs** - Send responses via Discord, email, webhooks, etc.
* **Actions** - Call APIs, process data, perform calculations
* **Memory** - Remember conversations and learn from interactions
## Why Choose Daydreams?
* **TypeScript-first** - Full type safety and IntelliSense support
* **Model-agnostic** - Works with OpenAI, Anthropic, Groq, and any AI SDK
provider
* **Production-ready** - Built-in memory management, error handling, and
concurrency
* **Extensible** - Pre-built extensions for Discord, Twitter, Telegram, and more
## Get Started
Choose your path based on your experience:
### New to AI Agents?
1. **[Building Blocks](/docs/core/concepts/building-blocks)** - Understand the
core components with simple examples
2. **[Your First Agent](/docs/core/first-agent)** - Build a working CLI echo bot
3. **[Agent Lifecycle](/docs/core/concepts/agent-lifecycle)** - Learn how agents
think and act
### Ready to Build?
1. **[Installation](/docs/core/installation)** - Set up your development
environment
2. **[Examples](/docs/tutorials/examples)** - See complete working agents
3. **[API Reference](/docs/api)** - Detailed documentation for all features
### Want to Understand the Architecture?
1. **[Core Concepts](/docs/core/concepts/core)** - Deep dive into the framework
design
2. **[Advanced Features](/docs/core/advanced/introduction)** - Extensions,
services, and customization
file: ./content/docs/core/installation.mdx
meta: {
"title": "Installation",
"description": "Set up Daydreams and configure your development environment."
}
import { Tab, Tabs } from "fumadocs-ui/components/tabs";
import { Step, Steps } from "fumadocs-ui/components/steps";
## Get an API key
* [Google Gemini](https://console.cloud.google.com/gemini) -> `GEMINI_API_KEY`
* [OpenAI](https://platform.openai.com/docs/api-reference) -> `OPENAI_API_KEY`
* [Anthropic](https://docs.anthropic.com/en/api/getting-started) ->
`ANTHROPIC_API_KEY`
* [Groq](https://docs.groq.com/docs/api-reference) -> `GROQ_API_KEY`
* [OpenRouter](https://openrouter.ai/docs/api-reference) -> `OPENROUTER_API_KEY`
* Other providers are supported by
[ai-sdk.dev](https://ai-sdk.dev/docs/foundations/providers-and-models)
## Installation
There are two ways to get started with Daydreams:
The quickest way to get started is using the create-agent command:
Run the create-agent command:
```bash title="create-agent.sh"
npx @daydreamsai/create-agent my-agent
```
This will:
* Create a new directory for your agent
* Set up package.json with necessary dependencies
* Create an index.ts file with your selected extensions
* Generate a .env.example file with required environment variables
* Install all dependencies
Choose your extensions when prompted (or use flags):
```bash title="create-agent-with-extensions.sh"
# With specific extensions
npx @daydreamsai/create-agent my-agent --twitter --discord --cli
# With all extensions
npx @daydreamsai/create-agent my-agent --all
```
Available extensions:
* `--cli`: Include CLI extension
* `--twitter`: Include Twitter extension
* `--discord`: Include Discord extension
* `--telegram`: Include Telegram extension
* `--all`: Include all extensions
Configure your environment variables in the generated `.env` file and start
building!
For more control over your setup, you can install manually:
Initialize your project and install core packages:
```bash title="package-install.sh"
pnpm init -y
pnpm add typescript tsx @types/node @daydreamsai/core @daydreamsai/cli
```
```bash title="package-install.sh"
npm init -y
npm install typescript tsx @types/node @daydreamsai/core @daydreamsai/cli
```
```bash title="package-install.sh"
bun init -y
bun add typescript tsx @types/node @daydreamsai/core @daydreamsai/cli
```
```bash title="package-install.sh"
yarn init -y
yarn add typescript tsx @types/node @daydreamsai/core @daydreamsai/cli
```
Install an LLM provider SDK:
`bash title="package-install.sh" pnpm add @ai-sdk/openai `
`bash title="package-install.sh" npm install @ai-sdk/openai `
`bash title="package-install.sh" bun add @ai-sdk/openai `
`bash title="package-install.sh" yarn add @ai-sdk/openai `
Other supported providers from [ai-sdk.dev](https://ai-sdk.dev/):
* `@ai-sdk/anthropic` for Claude
* `@ai-sdk/google` for Gemini
* And many more...
Create your environment file:
```bash title="bash"
# Create .env file
cp .env.example .env
```
Add your API keys:
```env title=".env"
OPENAI_API_KEY=your_openai_api_key_here
# ANTHROPIC_API_KEY=your_anthropic_api_key_here
# GEMINI_API_KEY=your_gemini_api_key_here
```
Create your first agent file (`index.ts`):
```typescript title="index.ts"
import { createDreams, LogLevel } from "@daydreamsai/core";
import { cliExtension } from "@daydreamsai/cli";
import { openai } from "@ai-sdk/openai";
const agent = createDreams({
logLevel: LogLevel.DEBUG,
model: openai("gpt-4o"),
extensions: [cliExtension],
});
// Start the agent
await agent.start();
```
Add run scripts to your `package.json`:
```json title="package.json"
{
"scripts": {
"dev": "tsx index.ts",
"start": "node index.js"
}
}
```
Run your agent:
`bash title="run-dev.sh" pnpm dev ``bash title="run-dev.sh" npm run dev ``bash title="run-dev.sh" bun dev ``bash title="run-dev.sh" yarn dev `
## Next Steps
Now you can start building your first agent! Check out the
[concepts](/docs/core/concepts/core) section to learn about the core building
blocks.
file: ./content/docs/tutorials/index.mdx
meta: {
"title": "Daydreams Tutorials",
"description": "Tutorials for Daydreams"
}
***
title: Introduction description: Bootstrap your first Daydreams agent.
***
## Bootstrapping a New Agent
The easiest way to get started with Daydreams is to use the CLI tool:
### Install the CLI
```bash
npm install -g @daydreamsai/create-agent
```
### Create a new agent
```bash
npx @daydreamsai/create-agent dreaming-agent
```
This will scaffold a new agent project with all the necessary files and
dependencies.
### Navigate to your project
```bash
cd dreaming-agent
```
### Configure Environment
Copy the `.env.example` file to `.env` and fill in your API keys.
```bash
cp .env.example .env
```
### Start your agent
```bash
npm start
# or use bun
# bun run index.ts
```
## Complete Example
Here's a minimal example of a Daydreams agent using the CLI extension:
```ts
import { createDreams } from "@daydreamsai/core";
import { cli } from "@daydreamsai/core/extensions";
import { groq } from "@daydreamsai/core/models";
const agent = createDreams({
model: groq("deepseek-r1-distill-llama-70b"),
extensions: [cli],
}).start();
```
This will run the agent in the terminal. Talk to it!
For more detailed usage, check out the other guides or explore the
[Concepts](/docs/concepts/core) section.
file: ./content/docs/core/advanced/deep.mdx
meta: {
"title": "Deep Research",
"description": "This guide will walk you through creating an AI agent that can perform deep research using Daydreams."
}
You can find a deep-research example in the
[examples](https://github.com/daydreamsai/daydreams/tree/main/examples/deep-research)
directory.
Detailed tutorial coming soon!
file: ./content/docs/core/advanced/extensions-vs-services.mdx
meta: {
"title": "Extensions vs Services",
"description": "Understanding the difference between extensions and services in Daydreams."
}
## What Are Extensions and Services?
Think of building an agent like assembling a computer:
* **Services** are like **individual components** (hard drive, graphics card,
RAM)
* **Extensions** are like **complete packages** (gaming bundle, productivity
suite)
## Real Examples
### Services: Individual Components
```typescript title="database-service.ts"
// A service manages ONE specific thing
const databaseService = service({
name: "database",
// How to create the database connection
register: (container) => {
container.singleton("db", () => new Database(process.env.DB_URL));
},
// How to initialize it when agent starts
boot: async (container) => {
const db = container.resolve("db");
await db.connect();
console.log("Database connected!");
},
});
```
### Extensions: Complete Packages
```typescript title="discord-extension.ts"
// An extension bundles EVERYTHING for a feature
const discordExtension = extension({
name: "discord",
// Services this extension needs
services: [discordService], // Manages Discord client
// All the Discord-related features
contexts: { discord: discordContext },
actions: [sendMessageAction, createChannelAction],
inputs: { "discord:message": messageInput },
outputs: { "discord:reply": replyOutput },
});
```
## The Problem: Managing Complexity
Without this separation, you'd have to set up everything manually:
```typescript title="manual-setup.ts"
// ❌ Without extensions/services - manual setup nightmare
const agent = createDreams({
model: openai("gpt-4o"),
// You'd have to manually configure EVERYTHING
contexts: {
discord: discordContext,
twitter: twitterContext,
database: databaseContext,
// ... 50+ more contexts
},
actions: [
sendDiscordMessage,
createDiscordChannel,
sendTweet,
followUser,
saveToDatabase,
queryDatabase,
// ... 100+ more actions
],
// Plus manually manage all the connections, API clients, etc.
// This becomes unmanageable quickly!
});
```
## The Solution: Organized Architecture
With extensions and services, it's clean and simple:
```typescript title="organized-setup.ts"
// ✅ With extensions/services - clean and simple
const agent = createDreams({
model: openai("gpt-4o"),
// Just add the features you want
extensions: [
discord, // Adds Discord support + client management
twitter, // Adds Twitter support + API management
mongoMemory, // Adds database memory + connection management
],
// That's it! Each extension handles its own complexity
});
```
## How They Work Together
### Services Handle the "How"
Services manage the technical details of connecting to external systems:
```typescript title="discord-service.ts"
const discordService = service({
name: "discord",
// HOW to create the Discord client
register: (container) => {
container.singleton(
"discordClient",
() =>
new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMessages],
token: process.env.DISCORD_TOKEN,
})
);
},
// HOW to initialize it
boot: async (container) => {
const client = container.resolve("discordClient");
await client.login();
console.log("Discord client ready!");
},
});
```
### Extensions Handle the "What"
Extensions bundle all the features users actually want:
```typescript title="discord-extension-complete.ts"
const discord = extension({
name: "discord",
// Use the service for client management
services: [discordService],
// WHAT the agent can do with Discord
contexts: {
discord: context({
type: "discord",
schema: z.object({ guildId: z.string(), channelId: z.string() }),
create: () => ({
messageHistory: [],
memberCount: 0,
}),
render: (state) => `
Discord Server: ${state.args.guildId}
Channel: ${state.args.channelId}
Members: ${state.memory.memberCount}
Recent messages: ${state.memory.messageHistory.slice(-3).join("\n")}
`,
}),
},
actions: [
action({
name: "send-discord-message",
description: "Send a message to a Discord channel",
schema: z.object({
channelId: z.string(),
content: z.string(),
}),
handler: async ({ channelId, content }, ctx) => {
const client = ctx.container.resolve("discordClient");
const channel = await client.channels.fetch(channelId);
await channel.send(content);
return { sent: true, messageId: result.id };
},
}),
],
inputs: {
"discord:message": input({
subscribe: (send, agent) => {
const client = agent.container.resolve("discordClient");
client.on("messageCreate", (message) => {
if (message.author.bot) return;
send({
type: "discord:message",
data: {
content: message.content,
author: message.author.username,
channelId: message.channel.id,
guildId: message.guild?.id,
},
});
});
},
}),
},
outputs: {
"discord:reply": output({
schema: z.object({
content: z.string(),
channelId: z.string(),
}),
handler: async ({ content, channelId }, ctx) => {
const client = ctx.container.resolve("discordClient");
const channel = await client.channels.fetch(channelId);
await channel.send(content);
},
}),
},
});
```
## When to Use Each
### Create a Service When:
* Managing an external connection (database, API client)
* Sharing utilities across multiple features
* Handling lifecycle management (startup, shutdown)
```typescript title="when-to-use-service.ts"
// ✅ Good service examples
const redisService = service({
/* manage Redis connection */
});
const loggerService = service({
/* configure logging */
});
const webhookService = service({
/* handle webhook server */
});
```
### Create an Extension When:
* Bundling a complete feature set
* Adding support for a new platform (Discord, Twitter, etc.)
* Packaging related actions/contexts/inputs/outputs
```typescript title="when-to-use-extension.ts"
// ✅ Good extension examples
const twitter = extension({
/* everything for Twitter integration */
});
const tradingBot = extension({
/* everything for trading features */
});
const gameEngine = extension({
/* everything for game mechanics */
});
```
## Practical Example: Building a Trading Extension
Let's see how they work together in practice:
### 1. First, Create Services for External APIs
```typescript title="trading-services.ts"
const alpacaService = service({
name: "alpaca",
register: (container) => {
container.singleton(
"alpacaClient",
() =>
new AlpacaApi({
key: process.env.ALPACA_KEY,
secret: process.env.ALPACA_SECRET,
paper: process.env.NODE_ENV !== "production",
})
);
},
boot: async (container) => {
const client = container.resolve("alpacaClient");
await client.authenticate();
},
});
const marketDataService = service({
name: "marketData",
register: (container) => {
container.singleton(
"marketClient",
() => new MarketDataClient(process.env.MARKET_DATA_KEY)
);
},
});
```
### 2. Then, Create Extension Using Those Services
```typescript title="trading-extension.ts"
const trading = extension({
name: "trading",
// Use the services we created
services: [alpacaService, marketDataService],
// Bundle all trading features
contexts: {
portfolio: portfolioContext,
watchlist: watchlistContext,
},
actions: [buyStockAction, sellStockAction, getQuoteAction, setStopLossAction],
inputs: {
"market:price-alert": priceAlertInput,
"market:news": newsInput,
},
outputs: {
"trading:order-confirmation": orderOutput,
"trading:alert": alertOutput,
},
});
```
### 3. Use the Extension in Your Agent
```typescript title="trading-agent.ts"
const agent = createDreams({
model: openai("gpt-4o"),
// Just add the extension - everything works automatically!
extensions: [trading],
// Now your agent can trade stocks with full context awareness
});
// Agent can now:
// - Listen for price alerts (inputs)
// - Check portfolio status (contexts)
// - Execute trades (actions)
// - Send confirmations (outputs)
// - All using properly managed API connections (services)
```
## Architecture Summary
```text title="architecture-flow.txt"
Extension (trading)
├── Services (how to connect)
│ ├── alpacaService → manages trading API client
│ └── marketDataService → manages market data client
│
└── Features (what agent can do)
├── Contexts → portfolio, watchlist state
├── Actions → buy, sell, get quotes
├── Inputs → listen for price alerts
└── Outputs → send trade confirmations
When you add the extension to your agent:
1. Services get registered and initialized automatically
2. All features become available to the LLM
3. API clients are properly managed and shared
4. Everything works together seamlessly
```
## Key Differences
| Aspect | Service | Extension |
| --------------- | --------------------------- | ---------------------------------- |
| **Purpose** | Manages infrastructure | Provides features |
| **Contains** | Connection logic, utilities | Actions, contexts, inputs, outputs |
| **Lifecycle** | `register()` → `boot()` | `install()` when added |
| **Reusability** | Used by multiple extensions | Used by agents |
| **Analogy** | Computer component | Software package |
## Best Practices
### Service Design
```typescript title="good-service.ts"
// ✅ Good - focused on one responsibility
const databaseService = service({
name: "database",
register: (container) => {
// Just database connection management
container.singleton("db", () => new Database(process.env.DB_URL));
},
});
// ❌ Bad - doing too many things
const everythingService = service({
name: "everything",
register: (container) => {
// Don't mix database, API clients, loggers, etc.
container.singleton("db", () => new Database(/* ... */));
container.singleton("api", () => new ApiClient(/* ... */));
container.singleton("logger", () => new Logger(/* ... */));
},
});
```
### Extension Design
```typescript title="good-extension.ts"
// ✅ Good - cohesive feature set
const discord = extension({
name: "discord",
services: [discordService], // Only Discord-related services
// All features work together for Discord integration
});
// ❌ Bad - unrelated features mixed together
const mixedExtension = extension({
name: "mixed",
services: [discordService, twitterService, databaseService],
// Discord actions mixed with Twitter contexts - confusing!
});
```
## Next Steps
* **[Extensions Guide](/docs/core/advanced/extensions)** - Learn to build your
own extensions
* **[Built-in Extensions](/docs/tutorials/examples)** - See real extension
examples
* **[Service Patterns](/docs/core/advanced/services)** - Advanced service
techniques
## Key Takeaways
* **Services manage "how"** - Connection setup, lifecycle, dependencies
* **Extensions manage "what"** - Features users actually want
* **Clean separation** - Infrastructure vs functionality
* **Easy composition** - Add extensions like LEGO blocks
* **Automatic management** - Framework handles the wiring
This architecture lets you build complex agents by combining simple, focused
pieces.
file: ./content/docs/core/advanced/extensions.mdx
meta: {
"title": "Extensions",
"description": "Building your own modular Daydreams extensions."
}
## What Are Extensions?
Extensions are **feature packages** for your agent. Think of them like apps on
your phone - each one adds specific capabilities without you having to build
everything from scratch.
## Real Examples
Here's what extensions look like in practice:
### Using Built-in Extensions
```typescript title="using-extensions.ts"
import { createDreams } from "@daydreamsai/core";
import { discord } from "@daydreamsai/discord";
import { twitter } from "@daydreamsai/twitter";
const agent = createDreams({
model: openai("gpt-4o"),
// Add extensions like installing apps
extensions: [
discord, // Now agent can read/send Discord messages
twitter, // Now agent can read/send tweets
],
});
// That's it! Your agent now speaks Discord and Twitter
```
### What Each Extension Gives You
```typescript title="extension-features.ts"
// The Discord extension adds:
// ✅ Automatic Discord client connection
// ✅ Listen for Discord messages (inputs)
// ✅ Send Discord replies (outputs)
// ✅ Track conversation context (contexts)
// ✅ Discord-specific actions (ban, kick, etc.)
// The Twitter extension adds:
// ✅ Twitter API client management
// ✅ Listen for mentions/DMs (inputs)
// ✅ Send tweets/replies (outputs)
// ✅ Track follower context (contexts)
// ✅ Twitter actions (follow, like, retweet)
```
## The Problem: Building Everything From Scratch
Without extensions, you'd have to build every feature manually:
```typescript title="manual-agent-building.ts"
// ❌ Without extensions - hundreds of lines of setup code
const agent = createDreams({
model: openai("gpt-4o"),
// Manually define every context
contexts: {
discordGuild: context({
/* Discord server context */
}),
discordChannel: context({
/* Discord channel context */
}),
discordUser: context({
/* Discord user context */
}),
twitterUser: context({
/* Twitter user context */
}),
twitterThread: context({
/* Twitter thread context */
}),
// ... 50+ more contexts
},
// Manually define every action
actions: [
action({ name: "send-discord-message" /* ... lots of code ... */ }),
action({ name: "ban-discord-user" /* ... lots of code ... */ }),
action({ name: "create-discord-channel" /* ... lots of code ... */ }),
action({ name: "send-tweet" /* ... lots of code ... */ }),
action({ name: "follow-twitter-user" /* ... lots of code ... */ }),
// ... 100+ more actions
],
// Manually set up all the inputs/outputs
inputs: {
"discord:message": input({
/* Complex Discord API setup */
}),
"discord:reaction": input({
/* More Discord API code */
}),
"twitter:mention": input({
/* Complex Twitter API setup */
}),
// ... dozens more
},
// Plus manage all the API clients, authentication, etc.
// This would be thousands of lines of code!
});
```
## The Solution: Extensions Package Everything
With extensions, complex features become simple one-liners:
```typescript title="simple-agent-building.ts"
// ✅ With extensions - clean and simple
const agent = createDreams({
model: openai("gpt-4o"),
extensions: [
discord, // Hundreds of lines of Discord integration
twitter, // Hundreds of lines of Twitter integration
],
// Done! Everything just works
});
```
## Building Your First Extension
Let's build a simple weather extension step by step:
### 1. Define What Your Extension Does
```typescript title="weather-extension-plan.ts"
// Weather extension should provide:
// - Action to get current weather
// - Action to get weather forecast
// - Context to remember user's preferred location
// - Service to manage weather API client
```
### 2. Create the Service (API Management)
```typescript title="weather-service.ts"
import { service } from "@daydreamsai/core";
const weatherService = service({
name: "weather",
// How to create the weather API client
register: (container) => {
container.singleton("weatherClient", () => ({
apiKey: process.env.WEATHER_API_KEY,
baseUrl: "https://api.openweathermap.org/data/2.5",
async getCurrentWeather(location: string) {
const response = await fetch(
`${this.baseUrl}/weather?q=${location}&appid=${this.apiKey}&units=metric`
);
return response.json();
},
async getForecast(location: string) {
const response = await fetch(
`${this.baseUrl}/forecast?q=${location}&appid=${this.apiKey}&units=metric`
);
return response.json();
},
}));
},
// Initialize when agent starts
boot: async (container) => {
const client = container.resolve("weatherClient");
console.log("Weather service ready!");
},
});
```
### 3. Create the Context (User Preferences)
```typescript title="weather-context.ts"
import { context } from "@daydreamsai/core";
import { z } from "zod";
const weatherContext = context({
type: "weather-preferences",
schema: z.object({ userId: z.string() }),
create: () => ({
defaultLocation: null,
units: "metric", // celsius by default
lastChecked: null,
favoriteLocations: [],
}),
render: (state) => `
User Weather Preferences:
- Default location: ${state.memory.defaultLocation || "Not set"}
- Units: ${state.memory.units}
- Favorite locations: ${state.memory.favoriteLocations.join(", ") || "None"}
- Last checked: ${state.memory.lastChecked || "Never"}
`,
});
```
### 4. Create the Actions (What Agent Can Do)
```typescript title="weather-actions.ts"
import { action } from "@daydreamsai/core";
import { z } from "zod";
const getCurrentWeatherAction = action({
name: "get-current-weather",
description: "Get the current weather for a location",
schema: z.object({
location: z.string().describe("City name, e.g., 'San Francisco, CA'"),
}),
handler: async ({ location }, ctx) => {
const weatherClient = ctx.container.resolve("weatherClient");
try {
const weather = await weatherClient.getCurrentWeather(location);
// Update user's context
ctx.memory.lastChecked = new Date().toISOString();
if (!ctx.memory.defaultLocation) {
ctx.memory.defaultLocation = location;
}
return {
success: true,
location: weather.name,
temperature: `${weather.main.temp}°C`,
description: weather.weather[0].description,
humidity: `${weather.main.humidity}%`,
windSpeed: `${weather.wind.speed} m/s`,
};
} catch (error) {
return {
success: false,
error: "Could not fetch weather data",
message: error.message,
};
}
},
});
const setDefaultLocationAction = action({
name: "set-default-weather-location",
description: "Set user's default location for weather",
schema: z.object({
location: z.string(),
}),
handler: async ({ location }, ctx) => {
ctx.memory.defaultLocation = location;
// Add to favorites if not already there
if (!ctx.memory.favoriteLocations.includes(location)) {
ctx.memory.favoriteLocations.push(location);
}
return {
success: true,
message: `Default location set to ${location}`,
};
},
});
```
### 5. Bundle Everything Into an Extension
```typescript title="weather-extension.ts"
import { extension } from "@daydreamsai/core";
export const weather = extension({
name: "weather",
// Services this extension needs
services: [weatherService],
// Contexts this extension provides
contexts: {
"weather-preferences": weatherContext,
},
// Actions this extension provides
actions: [getCurrentWeatherAction, setDefaultLocationAction],
// Optional: Run setup when extension is added
install: async (agent) => {
console.log("Weather extension installed!");
// Could do additional setup here if needed
},
});
```
### 6. Use Your Extension
```typescript title="weather-agent.ts"
import { createDreams } from "@daydreamsai/core";
import { weather } from "./weather-extension";
const agent = createDreams({
model: openai("gpt-4o"),
extensions: [weather],
});
await agent.start();
// Now your agent can:
// - Check weather for any location
// - Remember user's preferred locations
// - Set default locations for users
// - All with proper API management
```
## Extension Lifecycle
Here's what happens when your agent starts:
```text title="extension-lifecycle.txt"
1. Agent Creation
└── Extensions added to agent.extensions[]
2. agent.start() called
├── For each extension:
│ ├── Register all services
│ ├── Merge contexts into agent
│ ├── Merge actions into agent
│ ├── Merge inputs into agent
│ └── Merge outputs into agent
│
├── Boot all services (connect to APIs, databases, etc.)
│
├── Call extension.install() for each extension
│
└── Start all inputs (begin listening for events)
3. Agent Ready
└── All extension features available to LLM
```
## Advanced Extension Features
### Inputs and Outputs
Extensions can also define how agents listen and respond:
```typescript title="weather-inputs-outputs.ts"
const weatherExtension = extension({
name: "weather",
// ... services, contexts, actions ...
// Listen for weather-related events
inputs: {
"weather:alert": input({
subscribe: (send, agent) => {
// Listen for severe weather alerts
const weatherClient = agent.container.resolve("weatherClient");
setInterval(async () => {
const alerts = await weatherClient.getAlerts();
for (const alert of alerts) {
send({
type: "weather:alert",
data: {
type: alert.type,
severity: alert.severity,
location: alert.location,
message: alert.message,
},
});
}
}, 60000); // Check every minute
},
}),
},
// Send weather notifications
outputs: {
"weather:notification": output({
schema: z.object({
message: z.string(),
location: z.string(),
urgency: z.enum(["low", "medium", "high"]),
}),
handler: async ({ message, location, urgency }, ctx) => {
// Could send via email, SMS, Discord, etc.
console.log(
`[${urgency.toUpperCase()}] Weather alert for ${location}: ${message}`
);
// Could also trigger other actions based on urgency
if (urgency === "high") {
// Maybe send emergency notifications
}
},
}),
},
});
```
### Extension Dependencies
Extensions can depend on other extensions:
```typescript title="weather-discord-extension.ts"
import { discord } from "@daydreamsai/discord";
import { weather } from "./weather-extension";
const weatherDiscordBot = extension({
name: "weather-discord-bot",
// This extension requires both Discord and Weather
services: [], // No additional services needed
// Add a Discord-specific weather command
actions: [
action({
name: "send-weather-to-discord",
description: "Send weather info to a Discord channel",
schema: z.object({
channelId: z.string(),
location: z.string(),
}),
handler: async ({ channelId, location }, ctx) => {
// Use weather extension's client
const weatherClient = ctx.container.resolve("weatherClient");
const weather = await weatherClient.getCurrentWeather(location);
// Use Discord extension's client
const discordClient = ctx.container.resolve("discordClient");
const channel = await discordClient.channels.fetch(channelId);
await channel.send(
`🌤️ Weather in ${location}: ${weather.main.temp}°C, ${weather.weather[0].description}`
);
return { success: true };
},
}),
],
});
// Use all three extensions together
const agent = createDreams({
model: openai("gpt-4o"),
extensions: [
discord, // Provides Discord functionality
weather, // Provides weather functionality
weatherDiscordBot, // Combines both for Discord weather bot
],
});
```
## Best Practices
### 1. Focus on One Domain
```typescript title="focused-extension.ts"
// ✅ Good - focused on weather
const weather = extension({
name: "weather",
// All features related to weather
});
// ❌ Bad - mixing unrelated features
const everything = extension({
name: "everything",
// Weather + Discord + Trading + Gaming features mixed together
});
```
### 2. Provide Complete Functionality
```typescript title="complete-extension.ts"
// ✅ Good - provides everything needed for weather
const weather = extension({
name: "weather",
services: [weatherService], // API management
contexts: { preferences: weatherContext }, // User preferences
actions: [getCurrentWeather, setLocation], // Core functionality
inputs: { alerts: weatherAlerts }, // Listen for alerts
outputs: { notify: weatherNotify }, // Send notifications
});
// ❌ Bad - incomplete, missing key features
const incompleteWeather = extension({
name: "weather",
actions: [getCurrentWeather], // Only one action, no context or API management
});
```
### 3. Clear Service Dependencies
```typescript title="clear-dependencies.ts"
// ✅ Good - clear what services this extension needs
const trading = extension({
name: "trading",
services: [
alpacaService, // For executing trades
marketDataService, // For getting quotes
riskService, // For risk management
],
// ... rest of extension
});
// ❌ Bad - unclear dependencies
const trading = extension({
name: "trading",
services: [
everythingService, // What does this provide?
],
});
```
### 4. Meaningful Names
```typescript title="meaningful-names.ts"
// ✅ Good - clear what each extension does
const discord = extension({ name: "discord" });
const weather = extension({ name: "weather" });
const tradingBot = extension({ name: "trading-bot" });
// ❌ Bad - unclear names
const ext1 = extension({ name: "ext1" });
const myStuff = extension({ name: "my-stuff" });
const helper = extension({ name: "helper" });
```
## Publishing Extensions
Once you've built an extension, you can share it:
### 1. Package Structure
```text title="extension-package.txt"
my-weather-extension/
├── src/
│ ├── services/
│ │ └── weather-service.ts
│ ├── contexts/
│ │ └── weather-context.ts
│ ├── actions/
│ │ ├── get-weather.ts
│ │ └── set-location.ts
│ ├── index.ts # Export the extension
│ └── types.ts # TypeScript types
├── package.json
├── tsconfig.json
└── README.md
```
### 2. Package.json
```json title="package.json"
{
"name": "@yourorg/daydreams-weather",
"version": "1.0.0",
"description": "Weather extension for Daydreams agents",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"peerDependencies": {
"@daydreamsai/core": "^1.0.0"
},
"dependencies": {
"zod": "^3.0.0"
}
}
```
### 3. Export Your Extension
```typescript title="src/index.ts"
export { weather } from "./weather-extension";
export type { WeatherData, WeatherAlert } from "./types";
```
### 4. Usage by Others
```typescript title="using-published-extension.ts"
import { createDreams } from "@daydreamsai/core";
import { weather } from "@yourorg/daydreams-weather";
const agent = createDreams({
model: openai("gpt-4o"),
extensions: [weather],
});
```
## Next Steps
* **[Extensions vs Services](/docs/core/advanced/extensions-vs-services)** -
Understand when to use each
* **[Built-in Extensions](/docs/tutorials/examples)** - Explore existing
extensions
* **[Discord Extension](/docs/tutorials/discord)** - See a complete extension
example
## Key Takeaways
* **Extensions are feature packages** - Like apps for your agent
* **Bundle related functionality** - Contexts, actions, inputs, outputs together
* **Automatic lifecycle management** - Services boot, features register
automatically
* **Reusable and shareable** - Build once, use everywhere
* **Clean agent configuration** - Add complex features with one line
Extensions let you build powerful agents by combining focused, reusable feature
packages.
file: ./content/docs/core/advanced/grpo-training-export.mdx
meta: {
"title": "Training Data Export for GRPO",
"description": "This guide explains how to export episodic memories as training data for Group Relative Policy Optimization (GRPO) using the Daydreams AI core package."
}
## What is GRPO Training?
GRPO (Group Relative Policy Optimization) is a reinforcement learning algorithm
designed to enhance reasoning capabilities in large language models. It
optimizes memory usage and is particularly effective for tasks requiring complex
problem-solving, such as:
* Mathematical reasoning
* Decision-making scenarios
* Step-by-step problem solving
* Game-based learning environments
**Key Benefits of GRPO:**
* Improves reasoning capabilities beyond standard fine-tuning
* Optimizes memory usage compared to traditional PPO
* Particularly effective for complex problem-solving tasks
## Workflow Overview
Your Daydreams agent can build reasoning traces for GRPO training by following
this structured workflow:
1. **Define Prompt Sources** - Use static datasets or interactive environments
2. **Generate Reasoning Traces** - Create completions that include thought
processes
3. **Store and Save Data** - Export in JSONL format compatible with training
tools
## Enabling Automatic Export
You can configure Daydreams to automatically export training data after each
episode:
```typescript
import { createDreams } from "@daydreamsai/core";
const agent = createDreams({
model: openai("gpt-4-turbo"),
exportTrainingData: true,
trainingDataPath: "./grpo-training-data.jsonl", // Optional, defaults to "./training-data.jsonl"
// ... other configuration options
});
```
**Note:** If you don't specify `trainingDataPath`, Daydreams will save the data
to `./training-data.jsonl` in your project root.
## Manual Export
You can manually export all episodes as training data:
```typescript
// Export using the default path from your agent configuration
await agent.exportAllTrainingData();
// Or specify a custom path
await agent.exportAllTrainingData("./custom-path/grpo-training-data.jsonl");
```
## Understanding the Data Format for GRPO
Daydreams exports training data in JSONL (JSON Lines) format, optimized for GRPO
training. Each line contains a JSON object with:
```json
{
"prompt": "You are in a dark room with a door to the north.",
"completion": "I need to find a way out. I should check if the door is locked.\n\nI found the door was unlocked and was able to exit the room."
}
```
The format includes:
* **prompt**: The observation or context provided to the agent
* **completion**: The agent's reasoning process and action results
For interactive environments, ensure completions include both reasoning and an
explicit action statement:
```json
{
"prompt": "You are in a dark room with a door to the north.",
"completion": "I need to find a way out. I should check if the door is locked.\n\nAction: try opening the door"
}
```
## Creating Custom Training Pairs for GRPO
For advanced use cases, you can create custom training data pairs specifically
designed for GRPO:
## Optimizing Data for GRPO Training
To maximize the effectiveness of your GRPO training data:
1. **Include diverse scenarios** - Ensure your agent encounters a variety of
situations
2. **Capture step-by-step reasoning** - The completion should show the agent's
thought process
3. **Format actions consistently** - Use patterns like "Action: \[action]" for
easy parsing
4. **Balance task difficulty** - Include both simple and complex reasoning
challenges
## Customizing the Export Format
If you need a different format for your specific GRPO training framework:
1. Create your own formatter function based on the Daydreams utilities
2. Process the episodic memories to match your required format
3. Save the data using your preferred file structure
**Example use case:** You might need to add additional metadata fields like task
difficulty or domain type to help with training organization.
file: ./content/docs/core/advanced/services.mdx
meta: {
"title": "Services",
"description": "Dependency Injection & Lifecycle Management."
}
## What Are Services?
Services are **infrastructure managers** for your agent. Think of them like the
utilities in a city - you don't think about electricity or water pipes, but
everything depends on them working properly.
## Real Examples
Here's what services handle in your agent:
### Database Connections
```typescript title="database-service.ts"
// Service manages database connection lifecycle
const databaseService = service({
name: "database",
register: (container) => {
// HOW to create database connection
container.singleton("db", () => new MongoDB(process.env.MONGODB_URI));
},
boot: async (container) => {
// WHEN to connect (agent startup)
const db = container.resolve("db");
await db.connect();
console.log("✅ Database connected!");
},
});
// Now any action can use the database:
// const db = ctx.container.resolve("db");
// await db.collection("users").findOne({ id: userId });
```
### API Client Management
```typescript title="discord-service.ts"
// Service manages Discord client lifecycle
const discordService = service({
name: "discord",
register: (container) => {
// HOW to create Discord client
container.singleton(
"discordClient",
() =>
new Discord.Client({
intents: [Discord.GatewayIntentBits.Guilds],
token: process.env.DISCORD_TOKEN,
})
);
},
boot: async (container) => {
// WHEN to connect (agent startup)
const client = container.resolve("discordClient");
await client.login();
console.log("✅ Discord bot online!");
},
});
// Now any action can send Discord messages:
// const client = ctx.container.resolve("discordClient");
// await client.channels.get(channelId).send("Hello!");
```
## The Problem: Manual Connection Management
Without services, you'd have to manage connections manually in every action:
```typescript title="manual-connection-nightmare.ts"
// ❌ Without services - repeated connection code everywhere
const sendDiscordMessageAction = action({
name: "send-discord-message",
handler: async ({ channelId, message }) => {
// Create new Discord client every time (slow!)
const client = new Discord.Client({
intents: [Discord.GatewayIntentBits.Guilds],
token: process.env.DISCORD_TOKEN,
});
// Connect every time (slow!)
await client.login();
// Send message
await client.channels.get(channelId).send(message);
// Close connection
await client.destroy();
},
});
const banUserAction = action({
name: "ban-user",
handler: async ({ userId, guildId }) => {
// Same connection code repeated (DRY violation!)
const client = new Discord.Client({
intents: [Discord.GatewayIntentBits.Guilds],
token: process.env.DISCORD_TOKEN,
});
await client.login(); // Slow reconnection every time!
const guild = await client.guilds.fetch(guildId);
await guild.members.ban(userId);
await client.destroy();
},
});
// Problems:
// 🐌 Slow - reconnecting for every action
// 🔄 Repetitive - same connection code everywhere
// 💔 Unreliable - connection failures not handled
// 📈 Expensive - multiple connections to same service
```
## The Solution: Services Manage Infrastructure
With services, connections are created once and shared:
```typescript title="clean-service-solution.ts"
// ✅ With services - clean, fast, reliable
// 1. Service handles connection once
const discordService = service({
name: "discord",
register: (container) => {
container.singleton(
"discordClient",
() =>
new Discord.Client({
intents: [Discord.GatewayIntentBits.Guilds],
token: process.env.DISCORD_TOKEN,
})
);
},
boot: async (container) => {
const client = container.resolve("discordClient");
await client.login(); // Connect once at startup
},
});
// 2. Actions just use the shared client
const sendDiscordMessageAction = action({
name: "send-discord-message",
handler: async ({ channelId, message }, ctx) => {
// Get already-connected client (fast!)
const client = ctx.container.resolve("discordClient");
// Send message immediately (no connection delay)
await client.channels.get(channelId).send(message);
},
});
const banUserAction = action({
name: "ban-user",
handler: async ({ userId, guildId }, ctx) => {
// Same client, no duplication (DRY!)
const client = ctx.container.resolve("discordClient");
const guild = await client.guilds.fetch(guildId);
await guild.members.ban(userId);
},
});
// Benefits:
// ⚡ Fast - client connected once at startup
// 🔄 DRY - no repeated connection code
// 💪 Reliable - connection managed centrally
// 💰 Efficient - one connection shared across actions
```
## How Services Work: The Container
Services use a **dependency injection container** - think of it like a storage
system for shared resources:
### Container Basics
```typescript title="container-example.ts"
import { createContainer } from "@daydreamsai/core";
const container = createContainer();
// Store things in the container
container.singleton("databaseUrl", () => process.env.DATABASE_URL);
container.singleton("database", (c) => new MongoDB(c.resolve("databaseUrl")));
// Get things from the container
const db = container.resolve("database");
await db.connect();
```
### Container Methods
```typescript title="container-methods.ts"
const container = createContainer();
// singleton() - Create once, reuse everywhere
container.singleton("apiClient", () => new ApiClient());
const client1 = container.resolve("apiClient");
const client2 = container.resolve("apiClient");
// client1 === client2 (same instance)
// register() - Create new instance each time
container.register("requestId", () => Math.random().toString());
const id1 = container.resolve("requestId");
const id2 = container.resolve("requestId");
// id1 !== id2 (different instances)
// instance() - Store pre-created object
const config = { apiKey: "secret123" };
container.instance("config", config);
const retrievedConfig = container.resolve("config");
// retrievedConfig === config (exact same object)
// alias() - Create alternative name
container.alias("db", "database");
const db = container.resolve("db"); // Same as resolve("database")
```
## Service Lifecycle
Services have two phases: **register** (setup) and **boot** (initialize):
```typescript title="service-lifecycle.ts"
const redisService = service({
name: "redis",
// Phase 1: REGISTER - Define how to create things
register: (container) => {
// Just define the factory functions
container.singleton("redisConfig", () => ({
host: process.env.REDIS_HOST || "localhost",
port: process.env.REDIS_PORT || 6379,
password: process.env.REDIS_PASSWORD,
}));
container.singleton(
"redisClient",
(c) => new Redis(c.resolve("redisConfig"))
);
console.log("📝 Redis service registered");
},
// Phase 2: BOOT - Actually connect/initialize
boot: async (container) => {
// Now connect to Redis
const client = container.resolve("redisClient");
await client.connect();
// Test the connection
await client.ping();
console.log("🚀 Redis service booted and connected!");
},
});
// Lifecycle order:
// 1. All services run register() first
// 2. Then all services run boot()
// 3. This ensures dependencies are available when needed
```
## Real-World Service Examples
### Trading Service
```typescript title="trading-service.ts"
const tradingService = service({
name: "trading",
register: (container) => {
// Register trading client
container.singleton(
"alpacaClient",
() =>
new Alpaca({
key: process.env.ALPACA_KEY,
secret: process.env.ALPACA_SECRET,
paper: process.env.NODE_ENV !== "production",
})
);
// Register portfolio tracker
container.singleton(
"portfolio",
(c) => new PortfolioTracker(c.resolve("alpacaClient"))
);
// Register risk manager
container.singleton(
"riskManager",
() =>
new RiskManager({
maxPositionSize: 0.1, // 10% of portfolio
stopLossPercent: 0.05, // 5% stop loss
})
);
},
boot: async (container) => {
// Initialize connections
const client = container.resolve("alpacaClient");
await client.authenticate();
const portfolio = container.resolve("portfolio");
await portfolio.sync(); // Load current positions
console.log("💰 Trading service ready!");
},
});
// Now trading actions can use these:
const buyStockAction = action({
name: "buy-stock",
handler: async ({ symbol, quantity }, ctx) => {
const client = ctx.container.resolve("alpacaClient");
const riskManager = ctx.container.resolve("riskManager");
// Check risk before buying
if (riskManager.canBuy(symbol, quantity)) {
return await client.createOrder({
symbol,
qty: quantity,
side: "buy",
type: "market",
});
} else {
throw new Error("Risk limits exceeded");
}
},
});
```
### Logging Service
```typescript title="logging-service.ts"
const loggingService = service({
name: "logging",
register: (container) => {
// Different loggers for different purposes
container.singleton(
"appLogger",
() =>
new Logger({
level: process.env.LOG_LEVEL || "info",
format: "json",
transports: [new FileTransport("app.log"), new ConsoleTransport()],
})
);
container.singleton(
"actionLogger",
() =>
new Logger({
level: "debug",
prefix: "[ACTION]",
transports: [new FileTransport("actions.log")],
})
);
container.singleton(
"errorLogger",
() =>
new Logger({
level: "error",
format: "detailed",
transports: [
new FileTransport("errors.log"),
new SlackTransport(process.env.SLACK_WEBHOOK),
],
})
);
},
boot: async (container) => {
const appLogger = container.resolve("appLogger");
appLogger.info("🚀 Application starting up");
},
});
// Actions can use appropriate logger:
const dangerousAction = action({
name: "delete-user",
handler: async ({ userId }, ctx) => {
const actionLogger = ctx.container.resolve("actionLogger");
const errorLogger = ctx.container.resolve("errorLogger");
try {
actionLogger.info(`Attempting to delete user ${userId}`);
// ... deletion logic ...
actionLogger.info(`Successfully deleted user ${userId}`);
} catch (error) {
errorLogger.error(`Failed to delete user ${userId}`, error);
throw error;
}
},
});
```
## Service Dependencies
Services can depend on other services:
```typescript title="service-dependencies.ts"
// Base database service
const databaseService = service({
name: "database",
register: (container) => {
container.singleton("db", () => new MongoDB(process.env.DB_URI));
},
boot: async (container) => {
const db = container.resolve("db");
await db.connect();
},
});
// Cache service that depends on database
const cacheService = service({
name: "cache",
register: (container) => {
// Redis for fast cache
container.singleton("redis", () => new Redis(process.env.REDIS_URL));
// Cache manager that uses both Redis and MongoDB
container.singleton(
"cacheManager",
(c) =>
new CacheManager({
fastCache: c.resolve("redis"), // From this service
slowCache: c.resolve("db"), // From database service
ttl: 3600, // 1 hour
})
);
},
boot: async (container) => {
const redis = container.resolve("redis");
await redis.connect();
const cacheManager = container.resolve("cacheManager");
await cacheManager.initialize();
console.log("💾 Cache service ready!");
},
});
// Extensions can use both services
const dataExtension = extension({
name: "data",
services: [databaseService, cacheService], // Order doesn't matter
actions: [
action({
name: "get-user",
handler: async ({ userId }, ctx) => {
const cache = ctx.container.resolve("cacheManager");
// Try cache first
let user = await cache.get(`user:${userId}`);
if (!user) {
// Cache miss - get from database
const db = ctx.container.resolve("db");
user = await db.collection("users").findOne({ _id: userId });
// Store in cache for next time
await cache.set(`user:${userId}`, user);
}
return user;
},
}),
],
});
```
## Advanced Patterns
### Environment-Based Services
```typescript title="environment-services.ts"
const storageService = service({
name: "storage",
register: (container) => {
if (process.env.NODE_ENV === "production") {
// Production: Use S3
container.singleton(
"storage",
() =>
new S3Storage({
bucket: process.env.S3_BUCKET,
region: process.env.AWS_REGION,
})
);
} else {
// Development: Use local filesystem
container.singleton(
"storage",
() => new LocalStorage({ path: "./uploads" })
);
}
},
boot: async (container) => {
const storage = container.resolve("storage");
await storage.initialize();
if (process.env.NODE_ENV === "production") {
console.log("☁️ S3 storage ready");
} else {
console.log("📁 Local storage ready");
}
},
});
```
### Service Configuration
```typescript title="service-configuration.ts"
const notificationService = service({
name: "notifications",
register: (container) => {
// Configuration
container.singleton("notificationConfig", () => ({
slack: {
webhook: process.env.SLACK_WEBHOOK,
channel: process.env.SLACK_CHANNEL || "#alerts",
},
email: {
smtpHost: process.env.SMTP_HOST,
smtpPort: process.env.SMTP_PORT,
from: process.env.EMAIL_FROM,
},
discord: {
webhookUrl: process.env.DISCORD_WEBHOOK,
},
}));
// Notification clients
container.singleton("slackNotifier", (c) => {
const config = c.resolve("notificationConfig");
return new SlackNotifier(config.slack);
});
container.singleton("emailNotifier", (c) => {
const config = c.resolve("notificationConfig");
return new EmailNotifier(config.email);
});
container.singleton("discordNotifier", (c) => {
const config = c.resolve("notificationConfig");
return new DiscordNotifier(config.discord);
});
// Unified notification manager
container.singleton(
"notificationManager",
(c) =>
new NotificationManager({
slack: c.resolve("slackNotifier"),
email: c.resolve("emailNotifier"),
discord: c.resolve("discordNotifier"),
})
);
},
boot: async (container) => {
const manager = container.resolve("notificationManager");
await manager.testConnections();
console.log("📢 Notification service ready!");
},
});
```
## Best Practices
### 1. Single Responsibility
```typescript title="single-responsibility.ts"
// ✅ Good - each service handles one thing
const databaseService = service({
name: "database",
// Only database connection management
});
const cacheService = service({
name: "cache",
// Only caching functionality
});
const loggingService = service({
name: "logging",
// Only logging configuration
});
// ❌ Bad - one service doing everything
const everythingService = service({
name: "everything",
register: (container) => {
// Database + cache + logging + API clients + notifications...
// Too many responsibilities!
},
});
```
### 2. Clear Dependencies
```typescript title="clear-dependencies.ts"
// ✅ Good - clear what this service provides
const authService = service({
name: "auth",
register: (container) => {
container.singleton("jwtSecret", () => process.env.JWT_SECRET);
container.singleton(
"tokenManager",
(c) => new TokenManager(c.resolve("jwtSecret"))
);
container.singleton(
"userAuthenticator",
(c) =>
new UserAuthenticator({
tokenManager: c.resolve("tokenManager"),
database: c.resolve("db"), // Depends on database service
})
);
},
});
// ❌ Bad - unclear what's provided
const helperService = service({
name: "helper",
register: (container) => {
container.singleton("stuff", () => new Thing());
container.singleton("helper", () => new Helper());
// What do these do? How do they relate?
},
});
```
### 3. Graceful Error Handling
```typescript title="error-handling.ts"
const apiService = service({
name: "external-api",
register: (container) => {
container.singleton(
"apiClient",
() =>
new ApiClient({
baseUrl: process.env.API_URL,
apiKey: process.env.API_KEY,
timeout: 10000,
retries: 3,
})
);
},
boot: async (container) => {
try {
const client = container.resolve("apiClient");
await client.healthCheck();
console.log("✅ External API connection verified");
} catch (error) {
console.error("❌ External API connection failed:", error.message);
// Don't crash the agent - just log the error
// Actions can handle API unavailability gracefully
console.warn(
"⚠️ Agent will start but external API features may be limited"
);
}
},
});
```
### 4. Resource Cleanup
```typescript title="resource-cleanup.ts"
const databaseService = service({
name: "database",
register: (container) => {
container.singleton("db", () => {
const db = new MongoDB(process.env.DB_URI);
// Register cleanup when process exits
process.on("SIGINT", async () => {
console.log("🔄 Closing database connection...");
await db.close();
console.log("✅ Database connection closed");
process.exit(0);
});
return db;
});
},
boot: async (container) => {
const db = container.resolve("db");
await db.connect();
},
});
```
## Troubleshooting
### Missing Dependencies
```typescript title="missing-dependency-error.ts"
// Error: "Token 'databaseClient' not found in container"
// ❌ Problem - trying to resolve unregistered token
const action = action({
handler: async (args, ctx) => {
const db = ctx.container.resolve("databaseClient"); // Not registered!
},
});
// ✅ Solution - ensure service registers the token
const databaseService = service({
register: (container) => {
container.singleton("databaseClient", () => new Database());
// ^^^^^^^^^^^^^^ Must match resolve token
},
});
```
### Circular Dependencies
```typescript title="circular-dependency-fix.ts"
// ❌ Problem - services depending on each other
const serviceA = service({
register: (container) => {
container.singleton("a", (c) => new A(c.resolve("b"))); // Needs B
},
});
const serviceB = service({
register: (container) => {
container.singleton("b", (c) => new B(c.resolve("a"))); // Needs A
},
});
// ✅ Solution - introduce a coordinator service
const coordinatorService = service({
register: (container) => {
container.singleton("a", () => new A());
container.singleton("b", () => new B());
container.singleton(
"coordinator",
(c) => new Coordinator(c.resolve("a"), c.resolve("b"))
);
},
boot: async (container) => {
const coordinator = container.resolve("coordinator");
coordinator.wireComponents(); // Set up relationships after creation
},
});
```
## Next Steps
* **[Extensions vs Services](/docs/core/advanced/extensions-vs-services)** -
When to use services vs extensions
* **[Extensions Guide](/docs/core/advanced/extensions)** - Build complete
feature packages
* **[Built-in Services](/docs/tutorials/examples)** - See real service examples
## Key Takeaways
* **Services manage infrastructure** - Database connections, API clients,
utilities
* **Container provides shared access** - One connection used by all actions
* **Two-phase lifecycle** - Register (setup) then boot (initialize)
* **Dependency injection** - Components don't create their own dependencies
* **Clean separation** - Infrastructure separate from business logic
Services let you build reliable agents with proper resource management and clean
architecture.
file: ./content/docs/core/advanced/supabase.mdx
meta: {
"title": "Supabase",
"description": "This guide will walk you through creating an AI agent that utilizes supabase as the memory store."
}
## Using Supabase with Daydreams
Setup Info:
* Vector Model Provider: `gpt-4-turbo` via `@ai-sdk/openai`
* Model Provider: `google/gemini-2.0-flash-001` via
`@openrouter/ai-sdk-provider`
* Memory Store: Supabase via `@daydreamsai/supabase`
* Communication method: Command Line via `@daydreamsai/cli`
Initialize a project and add our setup packages
```bash
bun init
bun add @daydreamsai/core @daydreamsai/supabase @daydreamsai/cli @ai-sdk/openai @openrouter/ai-sdk-provider
```
After installing the packages, go to
[https://supabase.com/](https://supabase.com/) and create a new project. Once
your project is created, you'll need to add the two environment variables
necessary for this package to your environment.
```bash
# Supabase
SUPABASE_URL=YOUR_SUPABASE_URL
SUPABASE_SERVICE_KEY=YOUR_SUPABASE_SERVICE_KEY
# Other variables used in this example
OPENROUTER_API_KEY=YOUR_SUPABASE_SERVICE_KEY
OPENAI_API_KEY=YOUR_OPENAI_KEY
OPENROUTER_API_KEY=YOUR_OPENROUTER_KEY
```
These variables are provided by Supabase when you create the project and can be
found in your project settings:Data API.
Next, you need to set up the necessary database structure for the agent's
memory. Copy the following SQL code block and paste in the Supabase SQL Editor
(found in the sidebar):
```sql
-- Enable the pgvector extension if it's not already enabled
-- This is crucial for vector similarity search used by SupabaseVectorStore
CREATE EXTENSION IF NOT EXISTS vector;
-- Function to enable pgvector extension (might be used internally by SupabaseVectorStore)
CREATE OR REPLACE FUNCTION enable_pgvector_extension()
RETURNS void
LANGUAGE plpgsql
AS $$
BEGIN
CREATE EXTENSION IF NOT EXISTS vector;
END;
$$;
-- Function to execute arbitrary SQL (potentially used by SupabaseVectorStore for initialization)
-- SECURITY DEFINER allows the function to run with the privileges of the user who defines it,
-- necessary for operations like creating tables or extensions if the calling user doesn't have direct permissions.
-- Ensure you understand the security implications before using SECURITY DEFINER.
CREATE OR REPLACE FUNCTION execute_sql(query text)
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
EXECUTE query;
END;
$$;
```
Afterards you should see a success message like "Success. No rows returned".
With the Supabase setup complete, let's create the agent in our `index.ts`:
```ts
// This example shows how to use Supabase with DaydreamsAI.
// Vector Model Provider: gpt-4-turbo via @ai-sdk/openai
// Model Provider: google/gemini-2.0-flash-001 via @openrouter/ai-sdk-provider
// Memory Store: @daydreamsai/supabase
// CLI Extension: @daydreamsai/cli
import { openai } from "@ai-sdk/openai";
import {
createContainer,
createDreams,
Logger,
LogLevel,
validateEnv,
} from "@daydreamsai/core";
import { createSupabaseBaseMemory } from "@daydreamsai/supabase";
import { z } from "zod";
import { cliExtension } from "@daydreamsai/cli";
import { openrouter } from "@openrouter/ai-sdk-provider";
validateEnv(
z.object({
OPENAI_API_KEY: z.string().min(1, "OPENAI_API_KEY is required"),
SUPABASE_URL: z.string().min(1, "SUPABASE_URL is required"),
SUPABASE_SERVICE_KEY: z.string().min(1, "SUPABASE_SERVICE_KEY is required"),
})
);
const agent = createDreams({
container: createContainer(),
logger: new Logger({ level: LogLevel.DEBUG }),
model: openrouter("google/gemini-2.0-flash-001"),
extensions: [cliExtension],
memory: createSupabaseBaseMemory({
url: process.env.SUPABASE_URL!,
key: process.env.SUPABASE_SERVICE_KEY!,
memoryTableName: "agent",
vectorTableName: "agentVectors",
vectorModel: openai("gpt-4-turbo"),
}),
});
// Agent starts
await agent.start();
```
Run the agent and chat via the command line interface!
```
bun run index.ts
```
file: ./content/docs/core/concepts/actions.mdx
meta: {
"title": "Actions",
"description": "Define capabilities and interactions for your Daydreams agent."
}
## What is an Action?
An action is something your agent can **do** - like calling an API, saving data,
or performing calculations. Think of actions as giving your agent superpowers.
## Real Examples
Here are actions that make agents useful:
### Weather Action
```typescript title="weather-action.ts"
// Agent can check weather
const getWeather = action({
name: "get-weather",
description: "Gets current weather for a city",
schema: z.object({
city: z.string(),
}),
handler: async ({ city }) => {
const response = await fetch(`https://api.weather.com/${city}`);
return await response.json(); // { temperature: "72°F", condition: "sunny" }
},
});
```
### Database Action
```typescript title="database-action.ts"
// Agent can save user preferences
const savePreference = action({
name: "save-preference",
description: "Saves a user preference",
schema: z.object({
userId: z.string(),
key: z.string(),
value: z.string(),
}),
handler: async ({ userId, key, value }) => {
await database.save(userId, key, value);
return { success: true, message: "Preference saved!" };
},
});
```
### Email Action
```typescript title="email-action.ts"
// Agent can send emails
const sendEmail = action({
name: "send-email",
description: "Sends an email to a user",
schema: z.object({
to: z.string(),
subject: z.string(),
body: z.string(),
}),
handler: async ({ to, subject, body }) => {
await emailService.send({ to, subject, body });
return { sent: true, timestamp: new Date().toISOString() };
},
});
```
## The Problem Without Actions
Without actions, your agent can only **talk**:
```text title="limited-agent.txt"
User: "What's the weather in Boston?"
Agent: "I don't have access to weather data, but I imagine it might be nice!"
// ❌ Can't actually check weather
// ❌ Just makes stuff up
// ❌ Not helpful
```
## The Solution: Actions Give Agents Capabilities
With actions, your agent can **do things**:
```text title="capable-agent.txt"
User: "What's the weather in Boston?"
Agent: *calls getWeather action*
Agent: "It's 65°F and cloudy in Boston right now!"
// ✅ Actually checks real weather API
// ✅ Provides accurate information
// ✅ Actually useful
```
## How Actions Work in Your Agent
### 1. You Define What the Agent Can Do
```typescript title="define-actions.ts"
const agent = createDreams({
model: openai("gpt-4o"),
actions: [
getWeather, // Agent can check weather
savePreference, // Agent can save user data
sendEmail, // Agent can send emails
],
});
```
### 2. The LLM Decides When to Use Them
When the agent thinks, it sees:
```text title="llm-prompt.txt"
Available actions:
- get-weather: Gets current weather for a city
- save-preference: Saves a user preference
- send-email: Sends an email to a user
User message: "Check weather in NYC and email it to john@example.com"
```
### 3. The LLM Calls Actions
The LLM responds with structured calls:
```xml title="llm-response.xml"
I need to check weather first, then email the results{"city": "NYC"}{
"to": "john@example.com",
"subject": "NYC Weather",
"body": "Current weather: {{calls[0].temperature}}, {{calls[0].condition}}"
}
```
### 4. Daydreams Executes Your Code
Daydreams automatically:
* Validates the arguments against your schema
* Runs your handler function
* Returns results to the LLM
* Handles errors gracefully
## Creating Your First Action
Here's a simple action that adds two numbers:
```typescript title="calculator-action.ts"
import { action } from "@daydreamsai/core";
import { z } from "zod";
export const addNumbers = action({
// Name the LLM uses to call this action
name: "add-numbers",
// Description helps LLM know when to use it
description: "Adds two numbers together",
// Schema defines what arguments are required
schema: z.object({
a: z.number().describe("First number"),
b: z.number().describe("Second number"),
}),
// Handler is your actual code that runs
handler: async ({ a, b }) => {
const result = a + b;
return {
sum: result,
message: `${a} + ${b} = ${result}`,
};
},
});
```
Use it in your agent:
```typescript title="agent-with-calculator.ts"
const agent = createDreams({
model: openai("gpt-4o"),
actions: [addNumbers],
});
// Now when user asks "What's 5 + 3?", the agent will:
// 1. Call addNumbers action with {a: 5, b: 3}
// 2. Get back {sum: 8, message: "5 + 3 = 8"}
// 3. Respond: "5 + 3 = 8"
```
## Working with State and Memory
Actions can read and modify your agent's memory:
```typescript title="todo-actions.ts"
// Define what your context remembers
interface TodoMemory {
tasks: { id: string; title: string; done: boolean }[];
}
const addTask = action({
name: "add-task",
description: "Adds a new task to the todo list",
schema: z.object({
title: z.string().describe("What the task is"),
}),
handler: async ({ title }, ctx) => {
// Access context memory (automatically typed!)
const memory = ctx.memory as TodoMemory;
// Initialize if needed
if (!memory.tasks) {
memory.tasks = [];
}
// Add new task
const newTask = {
id: crypto.randomUUID(),
title,
done: false,
};
memory.tasks.push(newTask);
// Changes are automatically saved
return {
success: true,
taskId: newTask.id,
message: `Added "${title}" to your todo list`,
};
},
});
const completeTask = action({
name: "complete-task",
description: "Marks a task as completed",
schema: z.object({
taskId: z.string().describe("ID of task to complete"),
}),
handler: async ({ taskId }, ctx) => {
const memory = ctx.memory as TodoMemory;
const task = memory.tasks?.find((t) => t.id === taskId);
if (!task) {
return { success: false, message: "Task not found" };
}
task.done = true;
return {
success: true,
message: `Completed "${task.title}"`,
};
},
});
```
Now your agent can manage todo lists across conversations!
## External API Integration
Actions are perfect for calling external APIs:
```typescript title="external-api-action.ts"
const searchWikipedia = action({
name: "search-wikipedia",
description: "Searches Wikipedia for information",
schema: z.object({
query: z.string().describe("What to search for"),
limit: z.number().optional().default(3).describe("Max results"),
}),
handler: async ({ query, limit }) => {
try {
const response = await fetch(
`https://en.wikipedia.org/api/rest_v1/page/search?q=${encodeURIComponent(
query
)}&limit=${limit}`
);
if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}
const data = await response.json();
return {
success: true,
results: data.pages.map((page) => ({
title: page.title,
description: page.description,
url: `https://en.wikipedia.org/wiki/${page.key}`,
})),
message: `Found ${data.pages.length} results for "${query}"`,
};
} catch (error) {
return {
success: false,
error: error.message,
message: `Failed to search Wikipedia for "${query}"`,
};
}
},
});
```
## Best Practices
### 1. Use Clear Names and Descriptions
```typescript title="good-naming.ts"
// ✅ Good - clear what it does
const getUserProfile = action({
name: "get-user-profile",
description: "Gets detailed profile information for a user by their ID",
// ...
});
// ❌ Bad - unclear purpose
const doStuff = action({
name: "do-stuff",
description: "Does some user stuff",
// ...
});
```
### 2. Validate Input with Schemas
```typescript title="good-validation.ts"
// ✅ Good - specific validation
schema: z.object({
email: z.string().email().describe("User's email address"),
age: z.number().min(0).max(150).describe("User's age in years"),
preferences: z
.array(z.string())
.optional()
.describe("List of user preferences"),
});
// ❌ Bad - too loose
schema: z.object({
data: z.any(),
});
```
### 3. Handle Errors Gracefully
```typescript title="error-handling.ts"
handler: async ({ userId }) => {
try {
const user = await database.getUser(userId);
return { success: true, user };
} catch (error) {
// Log the error for debugging
console.error("Failed to get user:", error);
// Return structured error for the LLM
return {
success: false,
error: "User not found",
message: `Could not find user with ID ${userId}`,
};
}
};
```
### 4. Use async/await for I/O Operations
```typescript title="async-best-practice.ts"
// ✅ Good - properly handles async
handler: async ({ url }) => {
const response = await fetch(url);
const data = await response.json();
return { data };
};
// ❌ Bad - doesn't wait for async operations
handler: ({ url }) => {
fetch(url); // This returns a Promise that's ignored!
return { status: "done" }; // Completes before fetch finishes
};
```
### 5. Check for Cancellation in Long Operations
```typescript title="cancellation-handling.ts"
handler: async ({ items }, ctx) => {
for (let i = 0; i < items.length; i++) {
// Check if the agent wants to cancel
if (ctx.abortSignal?.aborted) {
throw new Error("Operation cancelled");
}
await processItem(items[i]);
}
return { processed: items.length };
};
```
## Advanced: Context-Specific Actions
You can attach actions to specific contexts so they're only available in certain
situations:
```typescript title="context-specific.ts"
const chatContext = context({
type: "chat",
schema: z.object({ userId: z.string() }),
create: () => ({ messages: [] }),
}).setActions([
// These actions only available during chat
action({
name: "save-chat-preference",
description: "Saves a preference for this chat user",
schema: z.object({
key: z.string(),
value: z.string(),
}),
handler: async ({ key, value }, ctx) => {
// ctx.memory is automatically typed as chat memory
if (!ctx.memory.userPreferences) {
ctx.memory.userPreferences = {};
}
ctx.memory.userPreferences[key] = value;
return { saved: true };
},
}),
]);
```
## Key Takeaways
* **Actions give agents capabilities** - They can do things, not just talk
* **LLM chooses when to use them** - Based on names and descriptions you provide
* **Arguments are validated** - Zod schemas ensure type safety
* **State persists automatically** - Changes to memory are saved
* **Error handling is crucial** - Return structured success/error responses
* **async/await required** - For any I/O operations like API calls
Actions transform your agent from a chatbot into a capable assistant that can
actually get things done.
file: ./content/docs/core/concepts/agent-lifecycle.mdx
meta: {
"title": "Agent Lifecycle",
"description": "How Daydreams agents process information and execute tasks."
}
## Simple Overview
Think of an agent as following a simple loop:
1. **Something happens** (input arrives)
2. **Agent thinks** (uses LLM to decide what to do)
3. **Agent acts** (performs actions or sends responses)
4. **Agent remembers** (saves what happened)
5. **Repeat**
This loop continues as long as the agent is running, handling new inputs and
responding intelligently based on its context and memory.
## The Basic Flow
Here's what happens when your agent receives a Discord message:
```
Discord Message Arrives
↓
Agent loads chat context & memory
↓
Agent thinks: "What should I do?"
↓
Agent decides: "I'll check the weather and respond"
↓
Agent calls weather API (action)
↓
Agent sends Discord reply (output)
↓
Agent saves conversation to memory
```
***
## Detailed Technical Explanation
The core of the Daydreams framework is the agent's execution lifecycle. This
loop manages how an agent receives input, reasons with an LLM, performs actions,
and handles results. Understanding this flow is crucial for building and
debugging agents.
Let's trace the lifecycle of a typical request:
## 1. Input Reception
* **Source:** An external system (like Discord, Telegram, CLI, or an API) sends
information to the agent. This is usually configured via an `extension`.
* **Listener:** An `input` definition within the agent or an extension listens
for these events (e.g., a new message arrives).
* **Trigger:** When the external event occurs, the input listener is triggered.
* **Invocation:** The listener typically calls `agent.send(...)`, providing:
* The target `context` definition (which part of the agent should handle
this?).
* `args` to identify the specific context instance (e.g., which chat
session?).
* The input `data` itself (e.g., the message content).
## 2. `agent.send` - Starting the Process
* **Log Input:** The framework logs the incoming information as an `InputRef` (a
record of the input).
* **Initiate Run:** It then calls the internal `agent.run` method to start or
continue the processing cycle for the specified context instance, passing the
new `InputRef` along.
## 3. `agent.run` - Managing the Execution Cycle
* **Load/Create Context:** The framework finds the specific `ContextState` for
the target instance (e.g., the state for chat session #123). If it's the first
time interacting with this instance, it creates the state and its associated
persistent memory (`ContextState.memory`). It also retrieves or creates the
temporary `WorkingMemory` for this specific run.
* **Handle Concurrency:** It checks if this context instance is already
processing another request. If so, the new input is usually added to the
ongoing run. If not, it sets up a new run.
* **Setup Run Environment:** It prepares the environment for the LLM
interaction, gathering all available `actions`, `outputs`, and relevant
context information.
* **Start Step Loop:** It begins the main processing loop, which iterates
through one or more reasoning steps until the interaction is complete.
## 4. Inside the Step Loop - Perception, Reasoning, Action
Each iteration (step) within the `agent.run` loop represents one turn of the
agent's core reasoning cycle:
* **Prepare State:** The agent gathers the latest information, including:
* The current persistent state of the active `Context`(s) (via their `render`
functions).
* The history of the current interaction from `WorkingMemory` (processed
inputs, outputs, action results from previous steps).
- Any *new* unprocessed information (like the initial `InputRef` or results
from actions completed in the previous step).
- The list of currently available `actions` and `outputs`.
* **Generate Prompt:** This information is formatted into a structured prompt
(using XML) for the LLM. The prompt clearly tells the LLM its instructions,
what tools (actions/outputs) it has, the current state, and what new
information needs attention. (See [Prompting](/docs/core/concepts/prompting)).
* **LLM Call:** The agent sends the complete prompt to the configured LLM.
* **Process LLM Response Stream:** As the LLM generates its response token by
token:
* The framework **streams** the response.
* It **parses** the stream, looking for specific XML tags defined in the
expected response structure (``, ``, `