Card backJack of Spades
Freehold/Workshop
Back

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

Ready

Try 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

1

User sends a message

The user types a question or request in the chat input.

2

Model decides to call a tool

Instead of replying directly, the model recognizes it needs external data and emits a structured tool call.

3

Tool call with typed arguments

The model outputs a JSON object with the tool name and strongly-typed arguments matching the tool schema.

4

Server executes the tool

The server runs the tool function with the provided arguments and returns the structured result.

5

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

NameTypeRequiredDescription
topicenum: overview | features | pricing | stackYesThe 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 description strings — 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