@project65/storyframe
v0.3.7
Published
Agent framework for building AI-powered applications with support for OpenAI, tool usage, and persistent storage
Downloads
73
Maintainers
Readme
@project65/storyframe
@project65/storyframe is a powerful and flexible TypeScript framework designed to accelerate the development of sophisticated AI-powered applications. It provides a structured, extensible architecture for building and managing AI agents that can interact with users, leverage external tools, and maintain conversational context.
Whether you're building a simple chatbot, a complex multi-agent system, or an AI-driven data analysis tool, Storyframe provides the essential components to get you started quickly.
📚 Table of Contents
✨ Features
- 🤖 Flexible Agent System: Abstract base classes and a ready-to-use
OpenAIAgent. - 🔧 Extensible Tool Framework: Define custom tools to give your agents new capabilities.
- 💾 Persistent Storage: Includes in-memory and Supabase adapters.
- 🔄 First-Class Streaming Support: Built-in support for streaming responses.
- 📝 TypeScript-First: Fully written in TypeScript for robust, type-safe code.
- 🛠️ Comprehensive Callback System: For monitoring, logging, and debugging.
- 🎯 Custom Response Formats: Define your own response schemas using Zod.
- 📋 Templated System Prompts: Dynamic system prompts with variable support.
🏗️ Architecture
Storyframe's architecture is designed to be modular and intuitive. The diagram below illustrates how the core components work together.
graph TD
subgraph Legend
direction LR
box1[ ] -.- UserInput[User Input]
box2[ ] -.- YourCode[Your Code]
box3[ ] -.- Framework[Storyframe]
box4[ ] -.- External[External]
end
subgraph "Your Application"
UserInput -- "sends" --> YourCode
YourCode -- "receives" --> ResponseStream
end
subgraph "Storyframe Framework"
Agent -- "manages" --> ToolRouter
Agent -- "uses" --> ChatStorage
ToolRouter -- "notifies" --> Callbacks
end
subgraph "External Services"
LLM[LLM e.g. OpenAI]
DB[(Database e.g. Supabase)]
CustomTool[Custom Tool]
end
YourCode -- "instantiates and calls" --> Agent
Agent -- "communicates with" --> LLM
LLM -- "responds to" --> Agent
Agent -- "streams to" --> ResponseStream
ToolRouter -- "executes" --> CustomTool
ChatStorage -- "persists to" --> DB
classDef default fill:#fff,stroke:#333,stroke-width:2px;
classDef userInput fill:#f9f,stroke:#333,stroke-width:1px;
classDef yourCode fill:#ccf,stroke:#333,stroke-width:1px;
classDef framework fill:#cfc,stroke:#333,stroke-width:1px;
classDef external fill:#ffc,stroke:#333,stroke-width:1px;
class box1,UserInput userInput;
class box2,YourCode,ResponseStream yourCode;
class box3,Agent,ToolRouter,ChatStorage,Callbacks framework;
class box4,LLM,DB,CustomTool external;Core Concepts
- Agents: The brain of your application. An agent orchestrates the conversation, calls tools, and interacts with the language model.
OpenAIAgentis the primary implementation. - Tools: Functions that an agent can execute. Tools are how you connect your agent to external data sources, APIs, or custom logic. The
ToolRoutermanages and exposes these tools to the agent. - Storage: The memory of your application. Chat storage adapters like
InMemoryChatStorageorSupabaseChatStorageare used to load and save conversation histories. - Callbacks: Hooks into the lifecycle of tool execution. Use callbacks to implement logging, performance monitoring, or custom error handling.
🚀 Quick Start
Installation
npm install @project65/storyframe
# or
yarn add @project65/storyframe
# or
pnpm add @project65/storyframeBasic Usage
import { OpenAIAgent, InMemoryChatStorage } from '@project65/storyframe';
const agent = new OpenAIAgent({
name: 'my-assistant',
id: 'agent-1',
description: 'A helpful assistant',
apiKey: process.env.OPENAI_API_KEY,
model: 'gpt-4',
streaming: true
});
const storage = new InMemoryChatStorage();
const response = await agent.process('Hello!', 'user-1', 'session-1', []);To run the same agent against Groq’s OpenAI-compatible endpoint, switch the provider and key—Storyframe updates the base URL automatically:
const agent = new OpenAIAgent({
name: 'groq-assistant',
id: 'agent-groq',
description: 'Runs on Groq',
inferenceProvider: 'groq',
apiKey: process.env.GROQ_API_KEY,
model: 'mixtral-8x7b-32768'
});📖 Advanced Usage
Custom Response Formats
Storyframe v0.2.0 introduces custom response formats using Zod schemas. This allows you to define exactly how you want your agent's responses to be structured:
import { z } from 'zod';
import { OpenAIAgent } from '@project65/storyframe';
// Define a custom response schema
const CustomResponseSchema = z.object({
messages: z.array(z.object({
type: z.enum(['text', 'code', 'image']),
content: z.string(),
metadata: z.record(z.string()).optional()
})),
confidence: z.number().min(0).max(1).optional()
});
// Create an agent with the custom format
const agent = new OpenAIAgent({
name: 'structured-agent',
id: 'agent-1',
description: 'An agent with structured outputs',
apiKey: process.env.OPENAI_API_KEY,
model: 'gpt-4',
textFormat: {
schema: CustomResponseSchema,
field: 'messages' // Root field in your schema
}
});Custom System Prompts
Use dynamic system prompts with variable substitution:
const agent = new OpenAIAgent({
// ... other options ...
customSystemPrompt: {
template: `You are {{role}}, specialized in {{domain}}.
Your task is to {{mainTask}}.
Follow these guidelines:
{{#each guidelines}}
- {{this}}
{{/each}}`,
variables: {
role: "an AI coding assistant",
domain: "TypeScript development",
mainTask: "help users write clean, maintainable code",
guidelines: [
"Always explain your reasoning",
"Suggest best practices",
"Consider performance implications"
]
}
}
});Optional LLM Analytics with PostHog
Storyframe can emit PostHog LLM analytics without impacting existing users. Provide your PostHog project credentials when constructing the agent, then add analytics metadata on each request as needed.
import { OpenAIAgent } from '@project65/storyframe';
const agent = new OpenAIAgent({
name: 'instrumented-agent',
id: 'agent-analytics',
description: 'An agent with optional analytics',
apiKey: process.env.OPENAI_API_KEY!,
posthog: {
apiKey: process.env.POSTHOG_API_KEY!,
host: 'https://us.i.posthog.com' // or your EU/self-hosted endpoint
}
});
const response = await agent.process(
'Summarize this conversation.',
'user-123',
'session-456',
chatHistory,
undefined,
{
posthogDistinctId: 'user-123',
posthogTraceId: 'conversation-456',
posthogProperties: { plan: 'pro', workspaceId: 'acme-co' },
posthogPrivacyMode: false
}
);
// On shutdown, flush PostHog if Storyframe created the client
await agent.shutdown();If you supply your own OpenAI or PostHog clients, simply pass them through the constructor and Storyframe will reuse them.
Heads up: PostHog’s official AI instrumentation requires Node.js 20+. If you are on an older runtime, Storyframe will automatically fall back to the standard OpenAI client and skip analytics.
Persistent Storage with Supabase
Set up Supabase storage for production use:
import { SupabaseChatStorage } from '@project65/storyframe';
// First, create your Supabase table
/*
CREATE TABLE public.chat_history (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL,
user_id TEXT NOT NULL,
session_id TEXT NOT NULL,
agent_id TEXT NOT NULL,
message JSONB NOT NULL,
message_key TEXT,
message_index BIGINT
);
*/
const storage = new SupabaseChatStorage(
process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_KEY!
);
// Use the storage with your agent
const history = await storage.fetchChat(userId, sessionId, agentId);
await storage.saveChat(userId, sessionId, agentId, newMessage);Tool Development
Create custom tools with type-safe parameters:
import { Tool, ToolRouter } from '@project65/storyframe';
const weatherTool = new Tool({
name: 'getWeather',
description: 'Get weather information for a location',
properties: {
location: {
type: 'string',
description: 'City name or coordinates'
},
units: {
type: 'string',
enum: ['celsius', 'fahrenheit']
}
},
required: ['location'],
handler: async ({ location, units = 'celsius' }) => {
// Implement weather lookup logic
return { temperature: 22, units, location };
}
});
const router = new ToolRouter();
router.register(weatherTool);Monitoring & Callbacks
Implement comprehensive monitoring:
import {
CompositeCallbacks,
LoggingCallbacks,
PerformanceCallbacks
} from '@project65/storyframe';
class CustomCallbacks extends LoggingCallbacks {
onToolStart(toolName: string, input: any) {
console.log(`[${new Date().toISOString()}] Starting ${toolName}`);
// Send metrics to your monitoring system
}
}
const callbacks = new CompositeCallbacks(
new CustomCallbacks(),
new PerformanceCallbacks()
);
const router = new ToolRouter(callbacks);📘 API Reference
For detailed API documentation, see our API Reference.
🤝 Contributing
We welcome contributions! Please see our Contributing Guide for details.
📄 License
This project is licensed under the MIT License.
