@coreexl/mentor
v0.1.1
Published
A fully customizable, reusable mentor/chat interface component built with React, Next.js, and TypeScript
Downloads
65
Readme
@coreexl/mentor
A fully customizable, reusable mentor/chat interface component built with React, Next.js, and TypeScript. Features real-time AI voice and text chat with WebRTC integration via Pipecat AI.
Features
- Voice & Text Chat: Seamlessly switch between voice and text interactions
- Tool/Function Calling: Execute custom functions via LLM tool calls with visual status indicators
- Auto-resize Input: Chat input automatically grows with content
- Fully Customizable: Configure agent name, title, description, and avatar
- Real-time Audio: WebRTC-powered voice communication with AI
- Responsive Design: Mobile-first design with smooth animations
- TypeScript Support: Full type safety and IntelliSense
- Easy Integration: Drop-in component with minimal setup
Installation
npm install @coreexl/mentorQuick Start
1. Set Environment Variables (Strongly Recommended)
Environment variables are the preferred method for configuration as they:
- Keep sensitive data like access keys secure
- Allow easy configuration across different environments (dev, staging, production)
- Reduce code duplication and hardcoded values
- Can be managed through your deployment platform
Create a .env.local file in your project root:
# Required: Your CoreEXL access key
NEXT_PUBLIC_ACCESS_KEY=your_access_key_here
# Required: Your server URL
NEXT_PUBLIC_COREEXL_MENTOR_SERVER=https://your-server-url.com2. Basic Usage (Using Environment Variables)
import { MentorPage } from '@coreexl/mentor'
import '@coreexl/mentor/mentor.css'
function App() {
return (
<MentorPage
// No need to pass accessKey or serverUrl - they're read from env vars!
agentName="Coach Sarah"
agentTitle="Learning Assistant"
systemPrompt="You are Sarah, a helpful learning coach who assists students with their studies. Be encouraging and provide clear explanations."
avatarSrc="/coach-sarah-avatar.jpg"
userId="user123"
onConnect={() => console.log('Connected to mentor')}
onMessage={(msg) => console.log('New message:', msg)}
onVoiceChange={(voice) => console.log('Voice changed to:', voice)}
/>
)
}🔒 Security Best Practice: Always use environment variables for sensitive configuration like access keys. Only use props for dynamic values that change at runtime.
Advanced Usage
Using Props Instead of Environment Variables
While environment variables are recommended, you can pass configuration as props when necessary (e.g., for dynamic values that change at runtime):
<MentorPage
accessKey="your-access-key" // ⚠️ Avoid hardcoding - use env vars instead
serverUrl="https://your-server-url.com" // ⚠️ Prefer env vars
agentName="Coach Sarah"
agentTitle="Learning Assistant"
systemPrompt="Your custom prompt here..."
// ... other props
/>Override Environment Variables
Props take precedence over environment variables when you need runtime flexibility:
<MentorPage
accessKey="runtime-access-key" // Overrides NEXT_PUBLIC_ACCESS_KEY
serverUrl="https://different-server.com" // Overrides NEXT_PUBLIC_COREEXL_MENTOR_SERVER
agentName="Dynamic Agent"
// ... other props
/>Event Handling
<MentorPage
agentName="Coach Timothy"
onConnect={() => console.log('Mentor connected!')}
onDisconnect={() => console.log('Mentor disconnected')}
onMessage={(message) => console.log('Message received:', message)}
onVoiceChange={(voice) => console.log('Voice changed to:', voice)}
/>Tool/Function Calling
Enable the AI mentor to execute custom functions:
import { MentorPage, Tool, ToolCall } from '@coreexl/mentor'
const tools: Tool[] = [
{
type: 'function',
function: {
name: 'get_weather',
description: 'Get current weather for a location',
parameters: {
type: 'object',
properties: {
location: { type: 'string', description: 'City name' }
},
required: ['location']
}
}
}
]
const handleToolCall = async (toolCall: ToolCall) => {
if (toolCall.name === 'get_weather') {
const { location } = toolCall.arguments as { location: string }
const weather = await fetchWeather(location)
return { temperature: weather.temp, conditions: weather.conditions }
}
}
<MentorPage
agentName="Weather Assistant"
tools={tools}
onToolCall={handleToolCall}
toolCallAutoDismiss={true} // Auto-hide tool status after 5 seconds (default: true)
/>Responsive Design
The component is fully responsive and adapts to different screen sizes:
- Mobile: Optimized touch interface with larger buttons
- Tablet: Balanced layout with appropriate spacing
- Desktop: Full feature set with optimal viewing experience
Responsive Breakpoints
// The component uses Tailwind's responsive prefixes internally:
// - sm: 640px
// - md: 768px
// - lg: 1024px
// - xl: 1280pxCommon Use Cases
1. Full-Page Learning Assistant
import { MentorPage } from '@coreexl/mentor'
import '@coreexl/mentor/mentor.css'
export default function LearningPage() {
return (
<div className="h-screen w-screen">
<MentorPage
agentName="Professor AI"
agentTitle="Computer Science Tutor"
systemPrompt="You are an expert computer science tutor..."
/>
</div>
)
}2. Embedded Chat Widget
<div className="fixed bottom-4 right-4 z-50">
<MentorPage
agentName="Support Agent"
styles={{
container: {
height: '500px',
width: '380px'
}
}}
/>
</div>3. Side-by-Side with Content
<div className="flex h-screen">
<div className="w-1/2 p-4">
{/* Your content here */}
</div>
<div className="w-1/2">
<MentorPage agentName="Study Buddy" />
</div>
</div>Props
| Prop | Type | Required | Description |
|------|------|----------|-------------|
| accessKey | string | Conditional* | Your CoreEXL access key (prefer NEXT_PUBLIC_ACCESS_KEY env var) |
| serverUrl | string | Conditional* | Your server URL (prefer NEXT_PUBLIC_COREEXL_MENTOR_SERVER env var) |
| agentName | string | No | Name of the AI mentor (default: "Coach Timothy") |
| agentTitle | string | No | Title/role of the mentor (default: "Concept Coach") |
| systemPrompt | string | No | Custom instructions for the AI mentor |
| avatarSrc | string | No | URL for mentor's avatar image |
| userId | string | No | Unique identifier for the user |
| styles | MentorPageStyles | No | Style customization for layout and components |
| className | string | No | Additional CSS classes for root container |
| debug | boolean | No | Enable debug logging (default: false) |
| tools | Tool[] | No | Tool/function definitions for LLM to call |
| onToolCall | (toolCall: ToolCall) => Promise<unknown> | No | Handler for tool calls from LLM |
| toolCallAutoDismiss | boolean | No | Auto-hide tool status in chat (default: true) |
| onConnect | () => void | No | Callback when voice connection establishes |
| onDisconnect | () => void | No | Callback when voice connection ends |
| onMessage | (message: MentorMessage) => void | No | Callback for new messages |
| onVoiceChange | (voice: VoiceName) => void | No | Callback when voice changes |
Customization
Container Styling
By default, the component uses 100% width and height to fill its parent container. You can customize the layout and appearance using the styles prop:
<MentorPage
agentName="Coach Sarah"
styles={{
container: {
height: '600px', // Default: '100%'
width: '800px', // Default: '100%'
maxWidth: '1200px', // Optional max width
minHeight: '400px', // Default: '100%'
padding: '20px', // Optional padding
className: 'custom-container' // Additional CSS classes
},
chatPanel: {
height: '500px', // Optional fixed height
className: 'custom-chat-panel'
},
voicePanel: {
height: '500px', // Optional fixed height
className: 'custom-voice-panel'
},
inputArea: {
className: 'custom-input' // Style the input area
},
messageList: {
maxHeight: '400px', // Control message list scroll height
className: 'custom-messages'
}
}}
/>Full-Screen Example
// Full viewport height and width (default behavior)
<div style={{ height: '100vh', width: '100vw' }}>
<MentorPage agentName="Coach Sarah" />
</div>Embedded Widget Example
// Fixed size embedded widget
<MentorPage
agentName="Coach Sarah"
styles={{
container: {
height: '500px',
width: '400px',
maxWidth: '100%'
}
}}
/>Custom CSS Classes
You can apply custom CSS classes at multiple levels:
<MentorPage
agentName="Coach Sarah"
className="my-root-class" // Root container
styles={{
container: { className: 'my-container' },
chatPanel: { className: 'my-chat' },
voicePanel: { className: 'my-voice' },
inputArea: { className: 'my-input' },
messageList: { className: 'my-messages' }
}}
/>TypeScript Types
interface MentorMessage {
id: string;
text: string;
timestamp: number;
isUser: boolean;
}
type VoiceName = 'Arjun' | 'Priya' | 'Raj' | 'Ananya';
interface Tool {
type: 'function';
function: {
name: string;
description?: string;
parameters?: {
type: 'object';
properties?: Record<string, ToolParameter>;
required?: string[];
};
};
}
interface ToolCall {
id: string;
name: string;
arguments: Record<string, unknown>;
}
interface MentorPageStyles {
container?: {
height?: string;
width?: string;
maxWidth?: string;
minHeight?: string;
padding?: string;
className?: string;
};
chatPanel?: {
height?: string;
className?: string;
};
voicePanel?: {
height?: string;
className?: string;
};
inputArea?: {
className?: string;
};
messageList?: {
maxHeight?: string;
className?: string;
};
}* Required if not set via environment variables. The component prioritizes environment variables (
NEXT_PUBLIC_ACCESS_KEYandNEXT_PUBLIC_COREEXL_MENTOR_SERVER) over props. We strongly recommend using environment variables for security and maintainability.
Development
# Build the package
npm run build
# Test locally
npm run preview