Skip to content

Oneirocom/nitro-agent

Repository files navigation

Magick Agent Nitro Template

This repository serves as a template for deploying Agents created with Magick via the Nitro framework.

Nitro is is a framework for rapidly building serverless applications. It has a robust set of features, all of which are documented in the Nitro documentation.

Introduction

The Magick Agent Nitro Template is a template for deploying Agents created with Magick via the Nitro framework. Its purpose is to produce a standalone runtime bundle which includes your agent and which can be deployed to a variety of platforms.

We utilize the new nitro module system to extend the functionality of the nitro runtime to include the Magick agent class, and a set of structured folders to modify the behavior of the agent at runtime. This allows us to take advantage of the nitro runtime's robust set of features, such as websockets, routes, and more.

Requirements

A Magick Agent has a number of requirements.

  • Postgres
  • Redis
  • Keywords
  • Embedder service

The purpose of these is:

  • PostgreSQL database: The agent stores its requests, state, eventsd, and more in a PostgreSQL database.
  • Redis database: Redis is used for caching the agents graph state for faster access and read/write operations. For advanced use cases, there are a number of events which the agent will emit, and you can listen for these events in your own code.
  • Keywords service: Keywords is our current LLM proxy provider. They provide a simple API for interacting with a variety of LLMs. They have observability, cost management, and more.
  • Embedder URL and API key: Currently our embedder service is the only hard dependency that the Agent has on our infrastructure. This is because, currently, knowledge is uploaded via the IDE UI, which means it winds up on our embedder service. This will change in the future. Our intention is to provide a folder for knowledge files in your repository, and have the agent pull that knowledge in during runtime.

Getting Started

  1. Fill in all required environment variables in .env file (see Environment Variables)
  2. Run npm install (this will also run npm init as a postinstall script)

The postinstall script runs migrations against the configured database and generates your Prisma client for your system. This will set up your database and create the necessary tables.

Running Locally

npm run dev

Building for Production

npm run build

Configuring Your Agent

Place your spell files from the Magick IDE into the agent/spells folder. These spells are loaded into the runtime to become active.

We will also be working to add some support for public variables, and multiple agents.

Interacting with Your Agent

The Agent is available in the Nitro runtime, allowing interaction through various Nitro library abstractions like routes and websockets. The agent uses a channel-based system for handling conversations, where each channel represents a unique conversation context.

Here's an example of how to interact with your agent using channels:

export default defineEventHandler(async (event) => {
  const body = await readBody(event);
  const { prompt, userid } = body;
  const nitro = useNitroApp();

  // Function to communicate with agent through a channel
  const sendMessageToAgent = async (content, channelId) => {
    return new Promise((resolve, reject) => {
      // Create a channel for this conversation
      const channel = nitro.agent.channel(channelId);

      // Set up message handler
      channel.on("messageReceived", (response) => {
        channel.removeAllListeners();
        resolve({
          status: "success",
          data: response.data,
          event: response.event,
        });
      });

      // Handle errors
      channel.on("error", (error) => {
        channel.removeAllListeners();
        reject(error);
      });

      // Send message through channel
      channel.emitToAgent(
        "message",
        nitro.agent.formatEvent({
          content: content,
          sender: channelId,
          channel: channelId,
          eventName: "message",
          skipPersist: true,
          rawData: content,
          metadata: {
            sessionId: channelId,
            timestamp: new Date().toISOString(),
          },
        })
      );
    });
  };

  try {
    const result = await sendMessageToAgent(prompt, userid);
    return {
      message: "Data received successfully",
      prompt: prompt,
      generated: result,
    };
  } catch (error) {
    return {
      status: "error",
      error: error,
    };
  }
});

Channel-Based Communication

The agent now uses a channel system for managing conversations:

  • Each conversation gets its own channel, typically identified by a user ID
  • Channels provide isolated communication contexts
  • Messages and responses are scoped to their specific channels
  • Channels automatically clean up listeners after message handling

Key concepts:

  • nitro.agent.channel(channelId): Creates/gets a channel for a specific conversation
  • channel.emitToAgent(): Sends a message through the channel
  • channel.on("messageReceived"): Listens for responses on the channel
  • Each channel should handle its own cleanup by removing listeners after use

Message Format

When sending messages to the agent, use the following format:

nitro.agent.formatEvent({
  content: "Your message here",
  sender: "user_id",
  channel: "channel_id",
  eventName: "message",
  skipPersist: true, // Set to false if you want to persist messages
  rawData: "Your message here",
  metadata: {
    sessionId: "channel_id",
    timestamp: new Date().toISOString(),
  },
});

Agent Class

The Agent class acts as an event engine. You can send events (e.g., message) to the agent and listen for responses on the messageReceived event.

Example usage:

nitro.agent.emit(
  "message",
  nitro.agent.formatEvent({
    content: "Hello, Agent!",
    sender: "user123",
    channel: "session456",
    eventName: "message",
    skipPersist: false,
    rawData: "Hello, Agent!",
  })
);

nitro.agent.on("messageReceived", (response) => {
  console.log(response.data.content);
});

Key properties in formatEvent:

  • sender: Unique user ID
  • channel: Unique namespace for the interaction (e.g., user ID or game session)

Authentication

We do not handle authetication directly. Nitro has many authentication solutions which can be used, and users are expected to implement their own authentication solution.

Once you have a user, you can use the user's ID as the sender and the user's ID as the channel in the formatEvent function.

Environment Variables

You will need to fill in the following environment variables. Duplicate the .env.example file and rename it to .env. Fill in your values.

KEYWORDS_API_KEY=your_keywords_api_key
KEYWORDS_API_URL=http://localhost:3000
REDIS_URL=redis://localhost:6379
DATABASE_URL=postgresql://user:password@localhost:5432/mydb
AGENT_EMBEDDER_API_KEY=your_agent_embedder_api_key
NEXT_PUBLIC_EMBEDDER_SERVER_URL=https://embedder-prod-production.up.railway.app/api
  • KEYWORDS_API_KEY: API key for the keywords service
  • KEYWORDS_API_URL: URL for the keywords service
  • REDIS_URL: Redis connection string
  • DATABASE_URL: PostgreSQL connection string
  • AGENT_EMBEDDER_API_KEY: API key for accessing your agent's knowledge
  • NEXT_PUBLIC_EMBEDDER_SERVER_URL: URL for the embedder service

To obtain your AGENT_EMBEDDER_API_KEY:

  1. Go to the Knowledge tab in the Magick IDE
  2. Click the 'Generate Token' button
  3. Use the generated token as your AGENT_EMBEDDER_API_KEY

This key allows your agent to access its knowledge base.

Deployment

As with any Nitro application, you can deploy your application to a variety of platforms. You can find more information in the Nitro deployment documentation.

Nitro.js Documentation

For more information on Nitro.js, visit the official documentation.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published