Open source conversational AI training platform for building immersive learning experiences.
DownPat provides a complete toolkit for building AI-powered training applications where users practice conversations with AI coaches. It supports multiple message types (conversation, commentary, summaries), real-time updates via Socket.io, and comprehensive admin tools.
The fastest way to see DownPat in action is to run the example app.
- Node.js 18+
- At least one AI provider API key (OpenAI, Anthropic, or Google Gemini). OpenAI key required for moderation.
# Clone the repo
git clone https://github.com/robhunter/oss-DownPat.git
cd oss-DownPat
# Install dependencies
npm install
# Copy the example environment file and add your API key(s)
cp .env.example .env
# Edit .env — at minimum, set one AI provider key (e.g. OPENAI_API_KEY)
# Build all packages
npm run build
# Start the dev server and client
npm run devOpen http://localhost:5173 in your browser. The example app uses in-memory storage and mock authentication, so no Firebase setup is needed to try it out.
- Login as Admin to create and manage exercises
- Login as User to practice conversations
To integrate DownPat into your own application:
# Server
npm install @downpat/express @downpat/firebase-storage
# Client
npm install @downpat/reactAll other packages (@downpat/core, @downpat/ai-adapters, @downpat/ui-components, @downpat/admin-ui, @downpat/api-client) are included as transitive dependencies.
By default, DownPat uses Firebase Firestore for storage of exercises, conversations, and user state. Our rationale is that it's easier for you to set up a dedicated datastore for DownPat rather than try to integrate our data model into your existing database, and it's easier for us to maintain a single storage adapter. The happy path is for you to set up your own Firebase project and provide credentials, though you're welcome to replace the storage adapter to use whatever datastore you'd like.
- Create a Firebase project at https://console.firebase.google.com
- Create a Firestore database in your project (start in production mode)
- Generate a service account key: Project Settings → Service Accounts → Generate New Private Key
- Save the JSON file to your project (e.g.
./service-account.json) and add it to.gitignore - Set environment variables in your
.env:
FIREBASE_PROJECT_ID=your-project-id
# Option 1: Path to service account JSON (local development)
GOOGLE_APPLICATION_CREDENTIALS=./service-account.json
# Option 2: Base64-encoded service account (Docker/CI)
# FIREBASE_SERVICE_ACCOUNT_BASE64=$(cat service-account.json | base64)Then in your server code:
import { createFirebaseStorage } from '@downpat/firebase-storage';
const { exerciseStorage, conversationStorage, userStateStorage } = createFirebaseStorage();createFirebaseStorage() reads credentials from environment variables automatically. In test environments (NODE_ENV=test), it falls back to in-memory storage so your tests don't need Firebase access.
DownPat assumes that you already have some kind of user authentication set up, and that you want to control which users have access to your exercises. You'll need to implement two methods — validateToken and getDemoUser — by providing an object that satisfies the ServerAuthProvider interface:
import type { ServerAuthProvider, User } from '@downpat/core';
const myAuthProvider: ServerAuthProvider = {
// Validate a Bearer token from the request and return user info.
// Throw an error if the token is invalid.
async validateToken(token: string): Promise<User> {
const session = await yourSessionStore.verify(token);
return {
userId: session.userId,
displayName: session.name,
isAdmin: session.role === 'admin',
isSubscriber: session.hasSubscription,
};
},
// Return a fallback user for unauthenticated/demo access.
getDemoUser(): User {
return {
userId: 'demo',
displayName: 'Guest',
isAdmin: false,
isSubscriber: false,
};
},
};Pass your auth provider when creating the server:
const { httpServer } = createDownpatServer(app, {
serverAuth: myAuthProvider,
// ...other options
});For development and testing, @downpat/express exports a createMockAuthProvider() that accepts hardcoded tokens (demo-token and admin-token).
import express from 'express';
import { createDownpatServer, createMockAuthProvider } from '@downpat/express';
import { createFirebaseStorage } from '@downpat/firebase-storage';
import { createAdapterRegistryFromEnv } from '@downpat/ai-adapters';
const app = express();
app.use(express.json());
// Create storage
const { exerciseStorage, conversationStorage, userStateStorage } = createFirebaseStorage();
// Create AI adapter registry (auto-detects API keys from environment)
const aiAdapters = await createAdapterRegistryFromEnv();
// Initialize DownPat with all dependencies
const { httpServer } = createDownpatServer(app, {
serverAuth: createMockAuthProvider(), // Replace with real auth in production
exerciseStorage,
conversationStorage,
userStateStorage,
aiAdapter: aiAdapters.getDefaultAdapter(),
moderationAdapter: aiAdapters.getModerationAdapter() ?? undefined,
availableModels: aiAdapters.getAllModels(),
});
httpServer.listen(3001);import { BrowserRouter, Routes, Route } from 'react-router-dom';
import { DownpatRoutes } from '@downpat/react';
import '@downpat/ui-components/styles';
import '@downpat/admin-ui/styles';
function App() {
return (
<BrowserRouter>
<Routes>
{/* Your app routes */}
<Route path="/" element={<Home />} />
{/* DownPat routes */}
<Route
path="/downpat/*"
element={
<DownpatRoutes
authWrapper={YourProtectedRoute}
adminAuthWrapper={YourAdminRoute}
basePath="/downpat"
getAuthToken={async () => localStorage.getItem('token')}
/>
}
/>
</Routes>
</BrowserRouter>
);
}import { createAdapterRegistry } from '@downpat/ai-adapters';
const registry = await createAdapterRegistry({
openai: { apiKey: process.env.OPENAI_API_KEY },
anthropic: { apiKey: process.env.ANTHROPIC_API_KEY },
gemini: { apiKey: process.env.GEMINI_API_KEY },
});
// Use any configured provider
const adapter = registry.getAdapterForModel('gpt-4');
const result = await adapter.complete({
model: 'gpt-4',
messages: [{ role: 'user', content: 'Hello!' }],
onChunk: (chunk) => console.log(chunk), // Streaming
});- USER - User messages
- CONVERSATION - AI responses visible to the user
- COMMENTARY - Coach feedback (can be hidden/shown)
- SUMMARY - Conversation summaries
- SIMPLE - Simple coach messages (Talk to Coach feature)
- MODERATION - Content moderation results
- STARTER - Conversation starters
- CONTEXT - Contextual information
Exercises define training scenarios with guidelines, welcome messages, conversation starters, and AI tasks.
Exercises support a draft/published workflow - changes are saved to drafts, then published to make live.
git clone https://github.com/robhunter/oss-DownPat.git
cd oss-DownPat
npm install
npm run build
npm testOPENAI_API_KEY- OpenAI API key for GPT models and moderationANTHROPIC_API_KEY- Anthropic API key for Claude modelsGEMINI_API_KEY- Google API key for Gemini models
| Package | Description |
|---|---|
@downpat/express |
Express router, Socket.io, and AI adapters (server) |
@downpat/react |
React components, hooks, and routing (client) |
@downpat/firebase-storage |
Firebase Firestore storage implementation |
@downpat/core |
Core types and interfaces (included automatically) |
@downpat/ai-adapters |
OpenAI, Anthropic, and Gemini adapters (included in express) |
@downpat/ui-components |
React UI components (included in react) |
@downpat/admin-ui |
Admin interface components (included in react) |
@downpat/api-client |
API client for server communication (included in react) |
packages/
├── core/ # Types, interfaces, controllers
├── firebase-storage/ # Firestore storage implementation
├── express/ # Express router & Socket.io
├── ui-components/ # React components & theme generator
├── admin-ui/ # Admin components
└── ai-adapters/ # AI provider adapters
example-app/
├── server/ # Express API server
└── client/ # React + Vite client
See VERIFICATION.md for testing and browser verification instructions.
MIT

