Magma is a framework that lets you create AI agents without the headache. No complex chains, no confusing abstractions - just write the logic you want your agent to have.
Want to try it out? Chat with Dialog, our user research agent built with Magma!
- Install Magma:
npm i @pompeii-labs/magma
- Create your first agent:
import { MagmaAgent } from "@pompeii-labs/magma";
// That's it! You've got a working agent
const agent = new MagmaAgent();
// Want to give it some personality? Add system prompts:
agent.fetchSystemPrompts = () => [{
role: "system",
content: "You are a friendly assistant who loves dad jokes"
}];
// Need the agent to do something? Add tools:
agent.fetchTools = () => [{
name: "tell_joke",
description: "Tell a dad joke",
target: async () => {
return "Why don't eggs tell jokes? They'd crack up! π₯";
}
}];
// Run it:
const reply = await agent.main();
console.log(reply.content);
- Simple: Build agents in minutes with minimal code
- Flexible: Use any AI provider (OpenAI, Anthropic, Groq)
- Hosted: Deploy your agents in seconds with the MagmaDeploy platform (coming soon)
- Powerful: Add tools and middleware when you need them
- Observable: See exactly what your agent is doing
Want even more power? MagmaFlow gives you instant access to:
- Voice input/output
- Streaming responses
- Tool execution
- Usage tracking
- And more!
const agent = new MagmaAgent({
apiKey: "mf_..." // Get your key at magmaflow.dev
});
π MagmaFlow is currently in private beta! Join the waitlist to get early access.
Tools give your agent the ability to perform actions. Any method decorated with @tool and @toolparam will be available for the agent to use.
Important Notes:
- Every tool method must return a string
- Every tool has
call
as a required parameter, which is theMagmaToolCall
object - Tools are executed in sequence
import { MagmaAgent } from "@pompeii-labs/magma";
import { tool, toolparam } from "@pompeii-labs/magma/decorators";
/** Decorate any agent class method with @toolparam or @tool.
* @tool is used to define the tool itself
* @toolparam is used to define the parameters of the tool (key, type, description, required)
*/
class MyAgent extends MagmaAgent {
@tool({ name: "search_database", description: "Search the database for records" })
@toolparam({
key: "query",
type: "string",
description: "Search query",
required: true
})
@toolparam({
key: "filters",
type: "object",
properties: [
{ key: "date", type: "string" },
{ key: "category", type: "string", enum: ["A", "B", "C"] }
]
})
async searchDatabase(call: MagmaToolCall) {
const { query, filters } = call.fn_args;
// Implementation
}
}
Middleware is a novel concept to Magma. It allows you to add custom logic to your agent before or after a tool is executed.
This is a great way to add custom logging, validation, data sanitization, etc.
Important Notes:
- You can have unlimited middleware methods
- Middleware methods must return a string
import { MagmaAgent } from "@pompeii-labs/magma";
import { middleware } from "@pompeii-labs/magma/decorators";
/**
* Decorate any agent class method with @middleware to add custom logging, validation, etc.
* Types: "preCompletion", "onCompletion", "preToolExecution", "onToolExecution"
*/
class MyAgent extends MagmaAgent {
@middleware("onCompletion")
async logBeforeCompletion(message) {
if (message.content.includes("bad word")) {
return "You just used a bad word, please try again.";
}
}
}
Jobs allow you to schedule functions within your agent. Jobs conform to the standard UNIX cron syntax (https://crontab.guru/).
import { MagmaAgent } from "@pompeii-labs/magma";
import { job } from "@pompeii-labs/magma/decorators";
class MyAgent extends MagmaAgent {
// Run every day at midnight
@job("0 0 * * *")
async dailyCleanup() {
await this.cleanDatabase();
}
// Run every hour with timezone
@job("0 * * * *", { timezone: "America/New_York" })
async hourlySync() {
await this.syncData();
}
}
Hooks allow you to expose your agent as an API. Any method decorated with @hook will be exposed as an endpoint.
Important Notes:
- Hooks are exposed at
/hooks/{hook_name}
in the Magma API - The only parameter to hook functions is the request object, which is an instance of
express.Request
import { MagmaAgent } from "@pompeii-labs/magma";
import { hook } from "@pompeii-labs/magma/decorators";
import { Request } from "express";
class MyAgent extends MagmaAgent {
@hook('notification')
async handleNotification(req: Request) {
await this.processNotification(req.body);
}
}
class Agent extends MagmaAgent {
constructor() {
// Use OpenAI (default)
super({
providerConfig: {
provider: "openai",
model: "gpt-4o"
}
});
// Use Anthropic
this.setProviderConfig({
provider: "anthropic",
model: "claude-3.5-sonnet-20240620"
});
// Use Groq
this.setProviderConfig({
provider: "groq",
model: "llama-3.1-70b-versatile"
});
}
}
Every agent has a state object that you can use to store data.
State does not get passed into LLM calls, so it's a good place to store data that you want to persist between calls / sensitive data.
class MyAgent extends MagmaAgent {
async setup() {
// Initialize state
this.state.set("counter", 0);
this.state.set("access_token", "1234567890");
}
@tool({ name: "increment" })
async increment() {
const counter = this.state.get("counter") || 0;
this.state.set("counter", counter + 1);
return `Counter is now ${counter + 1}`;
}
@tool({ name: "api_call" })
async apiCall() {
const access_token = this.state.get("access_token");
// Make API call
}
}
import { MagmaAgent } from "@pompeii-labs/magma";
class MyAgent extends MagmaAgent {
// Initialize your agent
async setup() {
// Load resources, connect to databases, etc.
await this.loadDatabase();
return "I'm ready to help!";
}
// Handle incoming messages
async receive(message: any) {
// Process user input before main() is called
if (message.type === 'image') {
await this.processImage(message.content);
}
}
// Clean up resources
async cleanup();
// Manually trigger a specific tool
async trigger({ name: "get_weather" });
// Stop the current execution
kill();
}
Event handlers are optional methods that allow you to tack on custom logic to various events in the agent lifecycle.
import { MagmaAgent } from "@pompeii-labs/magma";
class MyAgent extends MagmaAgent {
// Handle agent shutdown
async onCleanup() {
console.log("Agent shutting down...");
}
// Handle errors
async onError(error: Error) {
console.error("Something went wrong:", error);
await this.notifyAdmin(error);
}
// Track token usage
async onUsageUpdate(usage: MagmaUsage) {
await this.saveUsageMetrics(usage);
}
// Process streaming responses
async onStreamChunk(chunk: MagmaStreamChunk) {
console.log("Received chunk:", chunk.content);
}
// MagmaFlow Handlers
async onConnect() {
console.log("Connected to MagmaFlow!");
}
// Handle agent disconnection from MagmaFlow
async onDisconnect() {
console.log("Disconnected from MagmaFlow");
}
// Handle incoming audio chunks
async onAudioChunk(chunk: Buffer) {
// Process incoming audio
await this.processAudioChunk(chunk);
}
// Handle audio stream completion
async onAudioCommit() {
// Audio stream complete
await this.finalizeAudioProcessing();
}
// Handle request abortion
async onAbort() {
await this.cleanup();
}
}
- Join our Slack Community
- Star us on GitHub
Magma is Apache 2.0 licensed.