npm package: @project65/storyframe
Description: Agent framework for building AI-powered applications with support for OpenAI, tool usage, and persistent storage
Latest version: 0.3.7

# @project65/storyframe

[![npm version](https://badge.fury.io/js/%40project65%2Fstoryframe.svg)](https://badge.fury.io/js/%40project65%2Fstoryframe)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)

**@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](#-features)
- [Architecture](#-architecture)
- [Installation](#-installation)
- [Quick Start](#-quick-start)
- [Advanced Usage](#-advanced-usage)
  - [Custom Response Formats](#custom-response-formats)
  - [Custom System Prompts](#custom-system-prompts)
  - [Persistent Storage](#persistent-storage)
  - [Tool Development](#tool-development)
  - [Monitoring & Callbacks](#monitoring--callbacks)
- [API Reference](#-api-reference)
- [Contributing](#-contributing)
- [License](#-license)

## ✨ 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.

```mermaid
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. `OpenAIAgent` is 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 `ToolRouter` manages and exposes these tools to the agent.
- **Storage**: The memory of your application. Chat storage adapters like `InMemoryChatStorage` or `SupabaseChatStorage` are 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

```bash
npm install @project65/storyframe
# or
yarn add @project65/storyframe
# or
pnpm add @project65/storyframe
```

### Basic Usage

```typescript
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:

```ts
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:

```typescript
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:

```typescript
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](https://posthog.com/docs/llm-analytics/installation/openai) without impacting existing users. Provide your PostHog project credentials when constructing the agent, then add analytics metadata on each request as needed.

```typescript
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:

```typescript
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:

```typescript
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:

```typescript
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](https://github.com/project65/storyframe/docs/api.md).

## 🤝 Contributing

We welcome contributions! Please see our [Contributing Guide](https://github.com/project65/storyframe/CONTRIBUTING.md) for details.

## 📄 License

This project is licensed under the MIT License.