daimon.email
ExamplesFrameworks

LangChain Integration

Using daimon.email as a LangChain tool

Overview

Integrate daimon.email into LangChain agents as a tool, enabling your agents to create inboxes, check for new emails, and send messages as part of multi-step reasoning chains.

Info

LangChain Tools: Wrap daimon.email API endpoints as LangChain tools that your agent can invoke during execution.

Installation

npm install langchain @langchain/openai daimon-email
# or
pip install langchain langchain-openai daimon-email

Complete Implementation

import { DynamicStructuredTool } from '@langchain/core/tools';
import { z } from 'zod';
import { DaimonClient } from 'daimon-email';
import { ChatOpenAI } from '@langchain/openai';
import { AgentExecutor, createOpenAIFunctionsAgent } from 'langchain/agents';
import { ChatPromptTemplate } from '@langchain/core/prompts';

// Initialize daimon.email client
const daimonClient = new DaimonClient({ apiKey: process.env.DAIMON_API_KEY });

// Tool 1: Create Inbox
const createInboxTool = new DynamicStructuredTool({
  name: 'create_inbox',
  description: 'Create a new email inbox. Returns the email address and API key.',
  schema: z.object({
    username: z.string().describe('Username for the inbox (e.g., "my-agent")'),
  }),
  func: async ({ username }) => {
    const inbox = await daimonClient.inboxes.create({
      username,
      clientId: `langchain-${Date.now()}`
    });

    return JSON.stringify({
      email: inbox.result.address,
      inboxId: inbox.result.id,
      apiKey: inbox.result.apiKey
    });
  },
});

// Tool 2: Check for New Messages
const checkMessagesTool = new DynamicStructuredTool({
  name: 'check_messages',
  description: 'Check for new messages in an inbox. Returns list of messages with subject, sender, and body.',
  schema: z.object({
    inboxId: z.string().describe('The inbox ID to check'),
  }),
  func: async ({ inboxId }) => {
    const messages = await daimonClient.inboxes.messages.list(inboxId);

    return JSON.stringify(
      messages.map(m => ({
        from: m.from,
        subject: m.subject,
        body: m.replyBody || m.body,
        receivedAt: m.receivedAt,
        ctaLinks: m.ctaLinks
      }))
    );
  },
});

// Tool 3: Send Email
const sendEmailTool = new DynamicStructuredTool({
  name: 'send_email',
  description: 'Send an email from an inbox. Requires paid tier.',
  schema: z.object({
    inboxId: z.string().describe('The inbox ID to send from'),
    to: z.string().describe('Recipient email address'),
    subject: z.string().describe('Email subject'),
    body: z.string().describe('Email body'),
  }),
  func: async ({ inboxId, to, subject, body }) => {
    try {
      const result = await daimonClient.inboxes.send(inboxId, {
        to,
        subject,
        body
      });

      return JSON.stringify({
        success: true,
        messageId: result.messageId
      });
    } catch (error: any) {
      return JSON.stringify({
        success: false,
        error: error.code,
        upgradeContext: error.upgradeContext
      });
    }
  },
});

// Create the agent
const llm = new ChatOpenAI({
  modelName: 'gpt-4',
  temperature: 0,
});

const tools = [createInboxTool, checkMessagesTool, sendEmailTool];

const prompt = ChatPromptTemplate.fromMessages([
  ['system', 'You are an AI assistant with email capabilities. You can create inboxes, check messages, and send emails using the provided tools.'],
  ['human', '{input}'],
  ['placeholder', '{agent_scratchpad}'],
]);

const agent = await createOpenAIFunctionsAgent({
  llm,
  tools,
  prompt,
});

const agentExecutor = new AgentExecutor({
  agent,
  tools,
});

// Example usage
const result = await agentExecutor.invoke({
  input: 'Create an inbox with username "test-agent", then check if there are any messages in it'
});

console.log(result.output);
from langchain.agents import initialize_agent, Tool
from langchain.agents import AgentType
from langchain.chat_models import ChatOpenAI
from daimon_email import DaimonClient
import json

# Initialize daimon.email client
daimon_client = DaimonClient(api_key=os.getenv('DAIMON_API_KEY'))

# Tool 1: Create Inbox
def create_inbox(username: str) -> str:
    """Create a new email inbox. Returns the email address and API key."""
    inbox = daimon_client.inboxes.create(
        username=username,
        client_id=f'langchain-{int(time.time())}'
    )

    return json.dumps({
        'email': inbox['result']['address'],
        'inbox_id': inbox['result']['id'],
        'api_key': inbox['result']['api_key']
    })

# Tool 2: Check for New Messages
def check_messages(inbox_id: str) -> str:
    """Check for new messages in an inbox."""
    messages = daimon_client.inboxes.messages.list(inbox_id)

    return json.dumps([
        {
            'from': m['from'],
            'subject': m['subject'],
            'body': m.get('reply_body') or m['body'],
            'received_at': m['received_at'],
            'cta_links': m.get('cta_links', [])
        }
        for m in messages
    ])

# Tool 3: Send Email
def send_email(inbox_id: str, to: str, subject: str, body: str) -> str:
    """Send an email from an inbox."""
    try:
        result = daimon_client.inboxes.send(inbox_id, {
            'to': to,
            'subject': subject,
            'body': body
        })

        return json.dumps({
            'success': True,
            'message_id': result['message_id']
        })
    except Exception as error:
        return json.dumps({
            'success': False,
            'error': str(error)
        })

# Create tools
tools = [
    Tool(
        name='create_inbox',
        func=create_inbox,
        description='Create a new email inbox. Input should be a username string.'
    ),
    Tool(
        name='check_messages',
        func=check_messages,
        description='Check for new messages in an inbox. Input should be an inbox ID.'
    ),
    Tool(
        name='send_email',
        func=lambda args: send_email(**json.loads(args)),
        description='Send an email. Input should be JSON with inbox_id, to, subject, and body.'
    )
]

# Initialize agent
llm = ChatOpenAI(model_name='gpt-4', temperature=0)

agent = initialize_agent(
    tools,
    llm,
    agent=AgentType.OPENAI_FUNCTIONS,
    verbose=True
)

# Example usage
result = agent.run(
    'Create an inbox with username "test-agent", then check if there are any messages in it'
)

print(result)

Example Workflows

1. Service Signup Flow

const result = await agentExecutor.invoke({
  input: `
    1. Create an inbox with username "signup-test"
    2. Sign up for a service at example.com using that email
    3. Wait 30 seconds
    4. Check for confirmation emails
    5. Extract the confirmation link and report it
  `
});

2. Email Monitoring Agent

# Agent that monitors inbox and responds to keywords
result = agent.run("""
    Check my inbox (inbox ID: inb_abc123).
    For any emails with subject containing "urgent",
    send a reply saying "I've received your message and will respond shortly."
""")

3. Multi-Service Integration

const result = await agentExecutor.invoke({
  input: `
    I need to sign up for three services:
    - CRM at crm.example.com
    - Analytics at analytics.example.com
    - Help desk at helpdesk.example.com

    Create one inbox, use it to sign up for all three,
    and confirm each account by clicking the verification links.
  `
});

Advanced: LangChain Chains with Email

Email + RAG Pipeline

import { RetrievalQAChain } from 'langchain/chains';
import { MemoryVectorStore } from 'langchain/vectorstores/memory';
import { OpenAIEmbeddings } from '@langchain/openai';

// Combine email checking with RAG
const emailRAGChain = async (inboxId: string, query: string) => {
  // 1. Check for new emails
  const messages = await daimonClient.inboxes.messages.list(inboxId);

  // 2. Create vector store from email bodies
  const docs = messages.map(m => ({
    pageContent: m.body,
    metadata: { from: m.from, subject: m.subject }
  }));

  const vectorStore = await MemoryVectorStore.fromDocuments(
    docs,
    new OpenAIEmbeddings()
  );

  // 3. Query the emails
  const chain = RetrievalQAChain.fromLLM(llm, vectorStore.asRetriever());
  const result = await chain.call({ query });

  return result.text;
};

// Usage
const answer = await emailRAGChain(
  'inb_abc123',
  'What are the action items from my recent emails?'
);

Email-Triggered Agent

from langchain.callbacks import StreamingStdOutCallbackHandler

# Agent that runs when new emails arrive
def email_triggered_agent(inbox_id: str):
    messages = daimon_client.inboxes.messages.list(inbox_id)

    for message in messages:
        if not message.get('processed'):
            # Run agent on this email
            result = agent.run(
                f"Analyze this email and suggest a response:\n\n"
                f"From: {message['from']}\n"
                f"Subject: {message['subject']}\n"
                f"Body: {message['body']}"
            )

            print(f"Suggested response: {result}")

LangChain + LangSmith Tracing

import { LangChainTracer } from 'langchain/callbacks';

const tracer = new LangChainTracer({
  projectName: 'daimon-email-agent',
});

const result = await agentExecutor.invoke(
  {
    input: 'Create an inbox and send a test email'
  },
  {
    callbacks: [tracer]
  }
);

// View full trace at smith.langchain.com

Best Practices

1. Store Inbox IDs in Memory

import { BufferMemory } from 'langchain/memory';

const memory = new BufferMemory({
  returnMessages: true,
  memoryKey: 'chat_history',
});

// Agent remembers inbox IDs across conversation
await agentExecutor.invoke({
  input: 'Create an inbox',
  memory
});

await agentExecutor.invoke({
  input: 'Check that inbox for messages', // Refers to previously created inbox
  memory
});

2. Handle Rate Limits

const checkMessagesWithRetry = new DynamicStructuredTool({
  name: 'check_messages',
  description: 'Check for new messages with retry logic',
  schema: z.object({
    inboxId: z.string(),
  }),
  func: async ({ inboxId }) => {
    let attempts = 0;
    while (attempts < 3) {
      try {
        return await daimonClient.inboxes.messages.list(inboxId);
      } catch (error: any) {
        if (error.status === 429) {
          await new Promise(r => setTimeout(r, 2000));
          attempts++;
        } else {
          throw error;
        }
      }
    }
  },
});

3. Use Structured Output

from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field

class EmailAction(BaseModel):
    action: str = Field(description="Action to take: 'send', 'wait', or 'check'")
    inbox_id: str = Field(description="Inbox ID to use")
    details: dict = Field(description="Additional details for the action")

parser = PydanticOutputParser(pydantic_object=EmailAction)

# Agent outputs structured actions

Next Steps