AI Workshop
Connect your own AI provider and test the chat system live. See how tool calling works with different models and providers.
Provider Configuration
ReadyTry the chat with preprogrammed responses and real tool calls — no API key needed.
Your keys stay private
API keys are sent directly to the provider over HTTPS and are never stored on our servers. They exist only in memory for the duration of your request.
Workshop Chat
Demo Mode
Ask me anything about Freehold!
How Tool Calling Works
User sends a message
The user types a question or request in the chat input.
Model decides to call a tool
Instead of replying directly, the model recognizes it needs external data and emits a structured tool call.
Tool call with typed arguments
The model outputs a JSON object with the tool name and strongly-typed arguments matching the tool schema.
Server executes the tool
The server runs the tool function with the provided arguments and returns the structured result.
Model formulates a response
The model receives the tool result and uses it to compose a natural-language answer for the user.
Available Tools
Retrieves information about the Freehold platform. The model calls this tool when the user asks about what Freehold is, what it offers, pricing, or the tech stack.
Parameters
| Name | Type | Required | Description |
|---|---|---|---|
| topic | enum: overview | features | pricing | stack | Yes | The topic to get info about |
Example Call
{
"topic": "features"
}Example Result
{
"info": "Key features include: Payroll Dashboard, Client Management (CRM), Pipeline Tracking, AI Chat Assistants, Rich Text Editing, Data Visualization (charts), and Billing."
}Adding Your Own Tools
Tools let the AI model call functions during a conversation. Here's how to create and register a new tool.
1. Define the tool
import { defineTool } from '@freehold/ai'
import { z } from 'zod'
export const weatherTool = defineTool({
name: 'getWeather',
description: 'Get current weather for a city',
parameters: z.object({
city: z.string().describe('City name, e.g. "San Francisco"'),
units: z.enum(['celsius', 'fahrenheit'])
.optional()
.describe('Temperature units, defaults to fahrenheit'),
}),
execute: async ({ city, units = 'fahrenheit' }) => {
// Your implementation here — call an API, query a DB, etc.
const temp = units === 'celsius' ? 18 : 64
return { city, temperature: temp, units, condition: 'Partly cloudy' }
},
})2. Register the tool
// tools.ts
import { weatherTool } from './weather-tool'
import { companyInfoTool, payrollCalculatorTool } from './tools'
export const workshopTools = [
companyInfoTool,
payrollCalculatorTool,
weatherTool, // ← add your new tool here
]3. Pass to the API route
// api/workshop/chat/route.ts
import { toAISDKTools } from '@freehold/ai/server'
import { workshopTools } from '../../../workshop/tools'
// In your POST handler:
const result = streamText({
model: languageModel,
messages,
tools: toAISDKTools(workshopTools), // ← tools auto-converted
maxSteps: 5,
})Best practices
- -Write clear, specific
descriptionstrings — the model uses them to decide when to call the tool - -Use
.describe()on every Zod parameter so the model knows what values to pass - -Return structured data (objects) rather than plain strings for richer model responses
- -Keep tool execution fast — long-running tools delay the user experience