Skip to content

Commit 5142cb2

Browse files
feat(storage): add libsql file-based storage with network volume support (#9)
* fix(docs): correct agent endpoint, model references, add experimental notice - fix test-endpoint-agent.mjs to use /generate instead of /chat - update README.md with correct model name (qwen3-32b-awq) - update .runpod/README.md with experimental notice explaining: - cold start time (20-40s) and flash boot coming for cpu - load balancer is experimental for mastra api exposure - recommend active workers = 1 to avoid cold start - in-memory storage is file-based and lost when worker stops * feat(storage): add libsql file-based storage with network volume support - add libsql as storage backend when postgresql not configured - detect /runpod-volume at runtime for persistent storage - fallback to /tmp for ephemeral storage when no volume attached - update documentation with three storage modes
1 parent b81e564 commit 5142cb2

7 files changed

Lines changed: 93 additions & 86 deletions

File tree

.runpod/README.md

Lines changed: 42 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,54 @@
11
![Mastra AI Agent Server](https://mastra.ai/favicon.ico)
22

3-
Deploy AI agents powered by [Mastra](https://mastra.ai) with OpenAI-compatible endpoints
3+
Deploy AI agents powered by [Mastra](https://mastra.ai) on Runpod Serverless
44

55
---
66

77
[![Runpod](https://api.runpod.io/badge/runpod-workers/worker-mastra)](https://www.runpod.io/console/hub/runpod-workers/worker-mastra)
88

99
---
1010

11+
## Experimental
12+
13+
This worker is **experimental** and designed to explore running AI agents on Runpod Serverless CPU endpoints.
14+
15+
### Current Limitations
16+
17+
**Cold Start Time:** CPU pods currently have a cold start time of 20-40 seconds. We are actively working on bringing **Flash Boot** to CPU pods (currently available for GPU pods) to enable instant startups.
18+
19+
**Load Balancer:** This worker uses the experimental Load Balancer endpoint type to expose Mastra's HTTP API directly, without requiring the Runpod Python SDK.
20+
21+
### Recommended Configuration
22+
23+
To avoid cold start delays, we recommend setting **Active Workers to 1** in your endpoint configuration. This keeps one worker always running and ready to handle requests immediately.
24+
25+
## Storage and Memory
26+
27+
The worker supports three storage modes for agent memory:
28+
29+
**Ephemeral (Default):** Without database credentials and no network volume, the worker uses LibSQL (SQLite) file storage at `/tmp/mastra-storage.db`. This data is **lost when the worker stops**.
30+
31+
**Network Volume (Persistent):** Attach a network volume to the endpoint. The worker automatically detects `/runpod-volume` and stores data at `/runpod-volume/mastra-storage.db`, surviving worker restarts.
32+
33+
**PostgreSQL (Persistent):** Configure a PostgreSQL database with the `pgvector` extension for full-featured persistent agent memory with vector embeddings.
34+
1135
## Endpoint Configuration
1236

1337
All behaviour is controlled through environment variables:
1438

15-
| Environment Variable | Description | Default | Required |
16-
| -------------------- | ------------------------------------ | ------- | -------- |
17-
| `RUNPOD_API_KEY` | Runpod API key for AI model access | | Yes |
18-
| `DB_HOST` | PostgreSQL database host | | No |
19-
| `DB_USERNAME` | PostgreSQL database username | | No |
20-
| `DB_NAME` | PostgreSQL database name | | No |
21-
| `DB_PASSWORD` | PostgreSQL database password | | No |
22-
| `DB_PORT` | PostgreSQL database port | 6543 | No |
23-
| `PORT` | Server port | 80 | No |
24-
| `MASTRA_PORT` | Internal Mastra server port | 4111 | No |
39+
| Environment Variable | Description | Default | Required |
40+
| -------------------- | ---------------------------------- | ------- | -------- |
41+
| `RUNPOD_API_KEY` | Runpod API key for AI model access | | Yes |
42+
| `DB_HOST` | PostgreSQL database host | | No |
43+
| `DB_USERNAME` | PostgreSQL database username | | No |
44+
| `DB_NAME` | PostgreSQL database name | | No |
45+
| `DB_PASSWORD` | PostgreSQL database password | | No |
46+
| `DB_PORT` | PostgreSQL database port | 6543 | No |
47+
| `PORT` | Server port | 80 | No |
48+
| `MASTRA_PORT` | Internal Mastra server port | 4111 | No |
2549

2650
## Database Setup (Optional)
2751

28-
Database is optional. Without database credentials, the worker uses in-memory storage (no persistent memory between requests).
29-
3052
For persistent agent memory, configure a PostgreSQL database with the `pgvector` extension. We recommend **Supabase** for easy setup:
3153

3254
1. Create a new project at [Supabase Dashboard](https://supabase.com/dashboard)
@@ -43,8 +65,9 @@ curl https://YOUR_ENDPOINT_ID.api.runpod.ai/ping
4365
```
4466

4567
Response:
68+
4669
```json
47-
{"status": "healthy"}
70+
{ "status": "healthy" }
4871
```
4972

5073
### List Available Agents
@@ -70,7 +93,7 @@ curl -X POST https://YOUR_ENDPOINT_ID.api.runpod.ai/api/tools/get-weather/execut
7093
### Chat with Weather Agent
7194

7295
```bash
73-
curl -X POST https://YOUR_ENDPOINT_ID.api.runpod.ai/api/agents/weatherAgent/chat \
96+
curl -X POST https://YOUR_ENDPOINT_ID.api.runpod.ai/api/agents/weatherAgent/generate \
7497
-H "Content-Type: application/json" \
7598
-d '{
7699
"messages": [
@@ -82,20 +105,18 @@ curl -X POST https://YOUR_ENDPOINT_ID.api.runpod.ai/api/agents/weatherAgent/chat
82105
## Features
83106

84107
- Mastra AI framework with multi-agent support
85-
- Runpod AI SDK provider with OpenAI GPT-OSS-120B
86-
- PostgreSQL storage with PgVector for agent memory
108+
- Runpod AI SDK provider with Qwen3-32B model
109+
- PostgreSQL storage with PgVector for agent memory (optional)
87110
- `/ping` health check for load balancer monitoring
88-
- OpenAI-compatible chat API
89111

90112
## Architecture
91113

92114
- **Runtime:** CPU (serverless)
93-
- **Endpoint Type:** Load Balancer (LB)
115+
- **Endpoint Type:** Load Balancer (experimental)
94116
- **Framework:** Mastra + Hono server
95-
- **Storage:** PostgreSQL with pgvector extension
117+
- **Storage:** In-memory (default) or PostgreSQL with pgvector
96118

97119
## Documentation
98120

99121
- [Mastra Documentation](https://mastra.ai/docs)
100122
- [Runpod Serverless Documentation](https://docs.runpod.io/serverless)
101-

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Mastra production server running on Runpod Serverless CPU with Load Balancer sup
77
## Features
88

99
- Mastra Hono server with weather agent and tool (no API key required for weather)
10-
- Runpod AI SDK provider with OpenAI GPT-OSS-120B support
10+
- Runpod AI SDK provider with Qwen3-32B model support
1111
- `/ping` health check endpoint for Runpod serverless load balancer
1212
- PostgreSQL storage with PgVector for agent memory
1313
- Observability and telemetry enabled (Mastra Cloud)
@@ -160,7 +160,7 @@ For local development and testing, you can run Mastra directly without building
160160
- 🔌 **API Endpoints**: http://localhost:4111/api - REST API for agents
161161
- 📚 **API Documentation**: http://localhost:4111/swagger-ui - Interactive API explorer
162162

163-
**Note:** Make sure the `runpod-mcp` project is built and available at `../runpod-mcp/build/index.js` for the RunPod Infra Management agent to work. If you haven't built it yet:
163+
**Note:** Make sure the `runpod-mcp` project is built and available at `../runpod-mcp/build/index.js` for the Runpod Infra Management agent to work. If you haven't built it yet:
164164

165165
```bash
166166
cd ../runpod-mcp
@@ -315,7 +315,7 @@ Once deployed, access at: `https://YOUR_ENDPOINT_ID.api.runpod.ai/`
315315
- `GET /api/tools` - List available tools
316316
- `POST /api/tools/get-weather/execute` - Execute weather tool
317317
- `GET /api/agents` - List available agents
318-
- `POST /api/agents/weatherAgent/chat` - Chat with weather agent
318+
- `POST /api/agents/weatherAgent/generate` - Chat with weather agent
319319

320320
## Requirements Met
321321

docs/context.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This document outlines the key technical conventions and architectural decisions
99
- Core logic is implemented as Mastra Agents (e.g., `weatherAgent`, `runpodInfraAgent`).
1010
- External functionalities are integrated as Mastra Tools or via MCP (Model Context Protocol).
1111
- Multiple agents can coexist in a single Mastra instance.
12-
- **AI Provider:** RunPod AI SDK Provider (`@runpod/ai-sdk-provider` v0.9.0)
12+
- **AI Provider:** Runpod AI SDK Provider (`@runpod/ai-sdk-provider` v0.9.0)
1313
- Uses Qwen3-32B model (`qwen/qwen3-32b-awq`) for agent reasoning.
1414
- Supports streaming and non-streaming text generation.
1515
- **Server Framework:** Hono (via Mastra's built-in server)
@@ -18,7 +18,7 @@ This document outlines the key technical conventions and architectural decisions
1818
- Agent memory: `PgVector` from `@mastra/pg` for embeddings (when DB credentials provided)
1919
- Falls back to in-memory storage when no database credentials are configured
2020
- **External Tool Integration:** MCP (Model Context Protocol) via `@mastra/mcp`
21-
- MCP servers provide external tools to agents (e.g., RunPod API tools)
21+
- MCP servers provide external tools to agents (e.g., Runpod API tools)
2222
- MCP configuration in `src/mastra/mcp-config.ts` manages server connections
2323
- **Project Structure:**
2424
- `src/mastra/agents/` - Agent definitions
@@ -100,7 +100,7 @@ When all DB credentials are provided, PostgreSQL with PgVector is used. Otherwis
100100
- **Memory Initialization:** Agents import `createAgentMemory()` from `utils/db.ts` - they do not handle database logic themselves.
101101
- **Tool Integration:** Agents can use:
102102
- Mastra Tools: Direct tool implementations (e.g., `weatherTool`)
103-
- MCP Tools: External tools provided via MCP servers (e.g., RunPod API tools)
103+
- MCP Tools: External tools provided via MCP servers (e.g., Runpod API tools)
104104
- **MCP Integration:** MCP servers are configured in `src/mastra/mcp-config.ts`. Agents access MCP tools by importing the MCP client and filtering available tools as needed.
105105

106106
## Local Development
@@ -117,7 +117,7 @@ When all DB credentials are provided, PostgreSQL with PgVector is used. Otherwis
117117
- **Docker Hub:** Images pushed to `runpod/worker-mastra:<version>`
118118
- **Runpod Git Pipeline:** Configure to build and deploy on push to `main` branch
119119

120-
## RunPod Hub Integration
120+
## Runpod Hub Integration
121121

122122
- **Hub Metadata:** `.runpod/` folder contains Hub publishing configuration
123123
- `hub.json`: Worker metadata, environment variables, and deployment config

package-lock.json

Lines changed: 9 additions & 45 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"license": "ISC",
1414
"dependencies": {
1515
"@mastra/core": "^0.23.3",
16-
"@mastra/libsql": "^0.16.1",
16+
"@mastra/libsql": "^0.16.4",
1717
"@mastra/loggers": "^0.10.18",
1818
"@mastra/mcp": "^0.14.1",
1919
"@mastra/memory": "^0.15.10",

scripts/test-endpoint-agent.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ async function testAgent() {
2727

2828
try {
2929
const response = await fetch(
30-
`${ENDPOINT_URL}/api/agents/weatherAgent/chat`,
30+
`${ENDPOINT_URL}/api/agents/weatherAgent/generate`,
3131
{
3232
method: "POST",
3333
headers: {

src/mastra/utils/db.ts

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { existsSync } from "fs";
12
import { PostgresStore, PgVector } from "@mastra/pg";
3+
import { LibSQLStore } from "@mastra/libsql";
24
import { Memory } from "@mastra/memory";
35

46
// Check if all required DB credentials are provided
@@ -12,26 +14,47 @@ export const hasDbCredentials =
1214
export const getConnectionString = () =>
1315
`postgresql://${process.env.DB_USERNAME}:${process.env.DB_PASSWORD}@${process.env.DB_HOST}:${process.env.DB_PORT || "6543"}/${process.env.DB_NAME}`;
1416

17+
// Determine storage path: prefer /runpod-volume (network volume), fallback to /tmp
18+
const getStoragePath = () => {
19+
if (process.env.STORAGE_PATH) {
20+
return process.env.STORAGE_PATH;
21+
}
22+
// Use network volume if available, otherwise ephemeral /tmp
23+
if (existsSync("/runpod-volume")) {
24+
return "/runpod-volume/mastra-storage.db";
25+
}
26+
return "/tmp/mastra-storage.db";
27+
};
28+
1529
// Global storage for Mastra instance
1630
export const createStorage = () => {
17-
if (!hasDbCredentials) {
18-
console.log("[Mastra] No database credentials provided, using in-memory storage");
19-
return undefined;
31+
if (hasDbCredentials) {
32+
console.log("[Mastra] Using PostgreSQL storage");
33+
return new PostgresStore({
34+
host: process.env.DB_HOST!,
35+
port: parseInt(process.env.DB_PORT || "6543"),
36+
user: process.env.DB_USERNAME!,
37+
database: process.env.DB_NAME!,
38+
password: process.env.DB_PASSWORD!,
39+
});
2040
}
2141

22-
return new PostgresStore({
23-
host: process.env.DB_HOST!,
24-
port: parseInt(process.env.DB_PORT || "6543"),
25-
user: process.env.DB_USERNAME!,
26-
database: process.env.DB_NAME!,
27-
password: process.env.DB_PASSWORD!,
42+
const storagePath = getStoragePath();
43+
const isPersistent = storagePath.startsWith("/runpod-volume");
44+
console.log(
45+
`[Mastra] Using file-based storage at ${storagePath} (${isPersistent ? "persistent" : "ephemeral"})`
46+
);
47+
return new LibSQLStore({
48+
url: `file:${storagePath}`,
2849
});
2950
};
3051

3152
// Agent memory with PgVector for embeddings
3253
export const createAgentMemory = (agentName: string) => {
3354
if (!hasDbCredentials) {
34-
console.log(`[${agentName}] No database credentials provided, running without persistent memory`);
55+
console.log(
56+
`[${agentName}] No database credentials provided, running without persistent memory`
57+
);
3558
return undefined;
3659
}
3760

@@ -48,4 +71,3 @@ export const createAgentMemory = (agentName: string) => {
4871
},
4972
});
5073
};
51-

0 commit comments

Comments
 (0)