DaydreamsAI

repository·main·Indexed 20 days ago

https://github.com/daydreamsai/daydreams

A memory system for AI agents featuring persistent vector storage via @daydreamsai/chroma, an interactive CLI via @daydreamsai/cli, and deployment tools via @daydreamsai/deploy. Includes integrations for Coinbase Server Wallet v2 for EVM wallet management and x402 micropayment support for paid AI nano services.

Tokens
167.7K
Snippets
510
Records
758
Agent score
68%

What's inside daydreams

  1. Key features of the Daydreams Router

    main

    The Daydreams Router provides several core capabilities for AI application development:

    • Unified Interface: A single API for OpenAI, Anthropic, Google, and more.
    • Model Routing: Automatic selection and fallback between different providers.
    • Dual Authentication: Supports both standard API keys and x402 USDC micropayments.
    • OpenAI Compatibility: Can be used with existing OpenAI SDK clients.
    • Cost Tracking: Ability to monitor usage across all integrated providers.
  2. What is the Daydreams Router?

    main
    The Daydreams Router is an intelligent gateway that provides a unified API for accessing multiple AI providers (such as OpenAI, Anthropic, and Google) through a single interface. It standardizes interactions by accepting OpenAI-format requests, translating them to provider-specific formats, and returning normalized OpenAI-format responses. It also handles automatic retries and provider fallbacks.
  3. What is a Context in Daydreams?

    main
    A Context acts as a separate workspace for your agent, similar to having different browser tabs open. Each context maintains its own isolated state, memory, and behavior. This prevents data leakage between different users (e.g., User A seeing User B's private info) or different sessions (e.g., game states from different matches mixing together).
  4. Route Inputs using Context Targeting

    main

    Inputs can be routed to specific context instances by passing unique arguments to the send function. This allows a single input (like a Discord listener) to manage multiple independent agent sessions (e.g., one per user) where each session maintains its own memory.

    const discordInput = input({
      type: "discord:message",
      schema: z.object({
        content: z.string(),
        userId: z.string(),
        channelId: z.string(),
      }),
      subscribe: (send, agent) => {
        discord.on("messageCreate", (message) => {
          // Route to user-specific chat context
          send(
            chatContext,
            { userId: message.author.id }, // Context args
            {
              content: message.content,
              userId: message.author.id,
              channelId: message.channel.id,
            }
          );
        });
    
        return () => discord.removeAllListeners("messageCreate");
      },
    });
  5. Compose multiple contexts using `.use()`

    main

    You can compose multiple contexts into a single agent execution using the use property. This allows the LLM to access data and actions from multiple specialized workspaces simultaneously. For example, an agent can use both a customerContext and an accountContext by passing the relevant args to each.

    // Example of composing contexts in an agent run
    // The LLM will see data and actions from both contexts
    await agent.run({
      context: mainContext,
      args: { customerId: "CUST001" },
      use: (state) => [
        {
          context: accountContext,
          args: { customerId: state.args.customerId },
        },
      ],
    });
  6. How Actions work in Daydreams

    main

    Actions are type-safe functions that an agent can execute. They are defined using the action() function, which requires a name, a description, a Zod schema for input validation, and a handler function. The handler receives a call object and access to memory.

    const action = action({
      name: "sendEmail",
      schema: z.object({
        to: z.string().email(),
        subject: z.string(),
        body: z.string(),
      }),
      handler: async ({ call, memory }) => {
        // Implementation
        return { sent: true };
      },
    });
  7. How Contexts work in @daydreamsai/core

    main

    Contexts are isolated, stateful environments used to manage specific conversations or tasks. Each context maintains its own unique memory and state. You define a context using the context() function, providing a type, a Zod schema for state validation, and optional lifecycle hooks like create to initialize state.

    const context = context({
      type: 'support',
      schema: z.object({ ticketId: z.string() }),
      create: async ({ args }) => ({
        status: 'open',
        messages: []
      })
    });
  8. How Actions work in @daydreamsai/core

    main

    Actions are type-safe functions that agents can execute. They are defined using the action() function, which requires a name, a description, and a Zod schema for input validation. The handler function receives a context (including call and memory) to perform the task.

    const action = action({
      name: 'sendEmail',
      schema: z.object({
        to: z.string().email(),
        subject: z.string(),
        body: z.string()
      }),
      handler: async ({ call, memory }) => {
        // Implementation
        return { sent: true };
      }
    });
  9. How building blocks work together in an agent flow

    main

    A Daydreams agent is composed of several core building blocks that work in a continuous loop:

    1. Inputs: Listen for external events (e.g., a Discord message) and trigger the agent.
    2. Contexts: Provide the persistent memory and state (e.g., chat history) that the agent uses to maintain continuity.
    3. Actions: Perform specific tasks or fetch data (e.g., getting weather) based on the agent's reasoning.
    4. Outputs: Send information or perform actions back to the external world (e.g., sending a Discord message).

    These are all tied together using createDreams, which accepts a model, contexts, inputs, outputs, and actions.

    import { createDreams, context, action, input, output } from "@daydreamsai/core";
    import { openai } from "@ai-sdk/openai";
    import * as z from "zod";
    
    // 1. INPUT
    const discordInput = input({
      type: "discord:message",
      schema: z.object({ content: z.string(), userId: z.string(), channelId: z.string() }),
      subscribe: (send, agent) => {
        // ... subscription logic
        return () => discord.removeAllListeners("messageCreate");
      },
    });
    
    // 2. ACTION
    const getWeather = action({
      name: "get-weather",
      description: "Gets current weather for a city",
      schema: z.object({ city: z.string().describe("City name") }),
      handler: async ({ city }) => {
        // ... logic
        return { temperature: 72, condition: "sunny", city };
      },
    });
    
    // 3. OUTPUT
    const discordOutput = output({
      type: "discord:message",
      description: "Sends a message to Discord",
      schema: z.string(),
      attributes: z.object({ channelId: z.string() }),
      handler: async (message, ctx) => {
        const { channelId } = ctx.outputRef.params;
        await discord.send(channelId, message);
        return { sent: true };
      },
    });
    
    // 4. CONTEXT
    const chatContext = context({
      type: "chat",
      schema: z.object({ userId: z.string() }),
      create: () => ({ messages: [] }),
    });
    
    // 5. AGENT
    const agent = createDreams({
      model: openai("gpt-4o"),
      contexts: [chatContext],
      inputs: [discordInput],
      outputs: [discordOutput],
      actions: [getWeather],
    });
  10. How Extensions and Services differ in Daydreams

    main

    In Daydreams, the architecture is split between Services (Infrastructure) and Extensions (Features). Understanding this distinction is critical for designing scalable agent capabilities.

    Services (The "How to Connect")

    Services manage the underlying infrastructure. They are responsible for managing connections to external systems like databases or APIs and handling their lifecycle (startup/shutdown).

    • Purpose: Manage infrastructure and shared utilities.
    • Contents: API clients, database connections, and environment-based configuration.
    • Lifecycle: register() $\rightarrow$ boot().
    • Usage: Typically used by multiple extensions to avoid duplicating connection logic.

    Extensions (The "What Agent Can Do")

    Extensions bundle complete, cohesive feature sets for a specific domain. They represent the actual capabilities an agent possesses.

    • Purpose: Provide complete features and platform support (e.g., Discord, Twitter).
    • Contents: Actions, contexts, inputs, and outputs.
    • Lifecycle: install() when added to an agent.
    • Usage: Agents consume extensions directly to gain functionality.

    Comparison Summary

    FeatureServiceExtension
    RoleInfrastructureFeatures
    AnalogyPower supply, motherboardComplete software package
    Primary GoalManage connectionsProvide agent capabilities
    CompositionUsed by ExtensionsUsed by Agents
  11. Composed Contexts Pattern (The Power Pattern)

    main

    The Composed Contexts pattern allows contexts to work together using the .use() method. This is the most powerful pattern, enabling you to build modular, scalable systems where a primary context can dynamically include other contexts based on the current state or arguments.

    Key capabilities:

    • Share common functionality: Include analytics, auth, or logging contexts in every interaction.
    • Conditional features: Use .use() to include specific contexts (like a premiumContext) only when certain conditions are met (e.g., state.args.customerTier === 'vip').
    • Modular design: Build complex behaviors by composing small, specialized contexts.
    import { context, action } from "@daydreamsai/core";
    import * as z from "zod";
    
    // ... (analyticsContext, profileContext, premiumContext definitions) ...
    
    // Smart chat context that composes all the above
    const smartChatContext = context({
      type: "chat",
      schema: z.object({ userId: z.string() }),
      create: () => ({ messages: [] }),
    })
      .use((state) => [
        // Always include analytics for every user
        { context: analyticsContext, args: { userId: state.args.userId } },
        
        // Always include profile  
        { context: profileContext, args: { userId: state.args.userId } },
        
        // Include premium features only for premium users
        state.memory.userTier === "premium" 
          ? { context: premiumContext, args: { userId: state.args.userId } }
          : null,
      ].filter(Boolean));
  12. Customize Context keys and instructions

    main

    Custom Keys

    By default, context instances use a type:key format. You can override this by providing a key function in the context definition that generates a unique ID based on the args.

    Dynamic Instructions

    Instead of a static string, instructions can be a function that receives the current state (including args and memory), allowing the LLM's persona or constraints to change based on the context state.

    // Custom Key
    const customContext = context({
      type: "user-session",
      schema: z.object({ userId: z.string(), sessionType: z.string() }),
      key: (args) => `${args.userId}-${args.sessionType}`,
      create: () => ({ data: {} })
    });
    
    // Dynamic Instructions
    const adaptiveContext = context({
      type: "adaptive",
      schema: z.object({ userTier: z.string() }),
      create: () => ({ features: [] }),
      instructions: (state) => {
        return state.args.userTier === "premium" 
          ? "You are a premium assistant." 
          : "You are a standard assistant.";
      }
    });