AI Package Overview
Complete guide to the @repo/ai package for AI-powered features in the BISO Sites monorepo.
AI Package Overview
The @repo/ai package provides shared AI configuration, tools, and components for building intelligent features across the BISO Sites monorepo. Built on top of the Vercel AI SDK, it enables streaming AI assistants with navigation and form-filling capabilities.
When to use this package
- When building AI-powered chat interfaces or assistants
- When you need AI to navigate users through the application
- When implementing "autopilot" form-filling features
- For consistent AI configuration across apps
The AI package is designed for internal admin tools and public-facing chatbots. It provides tools that allow AI to take actions on behalf of users.
Installation
The package is already available as a workspace dependency:
{
"dependencies": {
"@repo/ai": "*"
}
}Package Structure
packages/ai/src/
├── config.ts # AI model configuration
├── types.ts # TypeScript type definitions
├── provider.tsx # React context provider
├── prompts.ts # System prompts for assistants
├── hooks/
│ ├── use-assistant.ts # Chat hook for client components
│ └── use-form-autopilot.ts # Form filling hook
└── tools/
├── navigation.ts # Navigation tool for AI
└── form-filler.ts # Form filling tool for AICore Concepts
AI Tools
Tools are functions that the AI can call to take actions. The package provides three main tools:
- Navigation Tool - Redirects users to specific pages
- Form Filler Tool - Fills form fields with AI-generated content
- Translate Tool - Translates content between Norwegian and English
Streaming Responses
All AI interactions use streaming for real-time responses. This provides:
- Immediate feedback as the AI "thinks"
- Typewriter effect for form filling
- Better perceived performance
Form Autopilot
The autopilot feature allows AI to:
- Detect which form the user is on
- Ask clarifying questions
- Stream responses directly into form fields
Quick Start
1. Create an API Route
import { openai } from "@ai-sdk/openai";
import { streamText, convertToModelMessages } from "ai";
import { createNavigationTool, defaultAdminRoutes } from "@repo/ai/tools/navigation";
import { createFormFillerTool, eventFormFields } from "@repo/ai/tools/form-filler";
import { ADMIN_ASSISTANT_PROMPT } from "@repo/ai/prompts";
export const maxDuration = 60;
export async function POST(req: Request) {
const { messages, formContext } = await req.json();
const result = streamText({
model: openai("gpt-5-mini"),
messages: convertToModelMessages(messages),
system: ADMIN_ASSISTANT_PROMPT,
tools: {
navigate: createNavigationTool(defaultAdminRoutes),
fillFormFields: createFormFillerTool(
formContext?.fields || eventFormFields
),
},
});
return result.toUIMessageStreamResponse();
}2. Build a Chat Component
'use client';
import { useState, useCallback } from 'react';
import { useRouter } from 'next/navigation';
export function AssistantChat() {
const router = useRouter();
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const handleNavigate = useCallback((path: string) => {
router.push(path);
}, [router]);
const sendMessage = async (content: string) => {
// Add user message
const userMessage = { role: 'user', content };
setMessages(prev => [...prev, userMessage]);
// Stream response from API
const response = await fetch('/api/assistant', {
method: 'POST',
body: JSON.stringify({ messages: [...messages, userMessage] }),
});
// Handle streaming response...
};
return (
<div>
{messages.map((m, i) => (
<div key={i}>{m.content}</div>
))}
<input
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && sendMessage(input)}
/>
</div>
);
}Available Exports
Configuration
import { createAIConfig, adminAssistantConfig, publicAssistantConfig } from '@repo/ai/config';
// Create custom config
const config = createAIConfig({
model: 'gpt-5-mini',
maxDuration: 60,
});Types
import type {
AssistantMessage,
NavigationAction,
FormFieldAction,
FormContext,
FormFieldInfo,
} from '@repo/ai/types';Prompts
import {
ADMIN_ASSISTANT_PROMPT,
PUBLIC_ASSISTANT_PROMPT,
getSystemPrompt,
} from '@repo/ai/prompts';
// Get prompt with additional context
const prompt = getSystemPrompt('admin', 'User is on the events page');Tools
import { createNavigationTool, defaultAdminRoutes } from '@repo/ai/tools/navigation';
import { createFormFillerTool, eventFormFields } from '@repo/ai/tools/form-filler';Hooks
import { useFormAutopilot } from '@repo/ai/hooks/use-form-autopilot';Environment Variables
# Required for OpenAI
OPENAI_API_KEY=sk-...Never expose OPENAI_API_KEY to the client. All AI calls should go through server-side API routes.
Best Practices
1. Use Server-Side API Routes
Always process AI requests on the server:
// ✅ Good: Server-side API route
export async function POST(req: Request) {
const result = streamText({ model: openai("gpt-5-mini"), ... });
return result.toUIMessageStreamResponse();
}
// ❌ Bad: Client-side API call
const response = await openai.chat.completions.create(...);2. Provide Context to the AI
Include relevant context in your prompts:
const system = `${ADMIN_ASSISTANT_PROMPT}
Current page: ${currentPath}
User role: ${userRole}
Available actions: ${availableActions.join(', ')}
`;3. Handle Errors Gracefully
try {
const response = await fetch('/api/assistant', { ... });
if (!response.ok) throw new Error('Request failed');
// Handle streaming...
} catch (error) {
// Show user-friendly error message
setMessages(prev => [...prev, {
role: 'assistant',
content: 'Sorry, I encountered an error. Please try again.',
}]);
}4. Validate Tool Inputs
The tools use Zod schemas for validation:
const navigationSchema = z.object({
path: z.string().describe("The path to navigate to"),
reason: z.string().optional().describe("Why navigating here"),
});Next Steps
- AI Tools Reference - Detailed tool documentation
- Admin Assistant Guide - Building the admin assistant
- Vercel AI SDK Docs - Official SDK documentation
