Files
joel/src/services/ai/tools.ts
2026-02-26 14:45:57 +01:00

287 lines
8.9 KiB
TypeScript

/**
* AI Tool definitions for function calling
* These tools allow the AI to interact with the bot's systems
*/
import type { ChatCompletionTool } from "openai/resources/chat/completions";
/**
* Tool call result from execution
*/
export interface ToolResult {
name: string;
result: string;
}
/**
* Tool call request from the AI
*/
export interface ToolCall {
id: string;
name: string;
arguments: Record<string, unknown>;
}
/**
* Context provided to tool handlers
*/
export interface ToolContext {
userId: string;
guildId: string;
channelId: string;
authorName: string;
/** Optional: enable GIF search for this context */
gifSearchEnabled?: boolean;
/** Optional: enable image generation for this context */
imageGenEnabled?: boolean;
/** Optional: allow NSFW image generation in this context */
nsfwImageEnabled?: boolean;
}
/**
* Tool handler function type
*/
export type ToolHandler = (
args: Record<string, unknown>,
context: ToolContext
) => Promise<string>;
/**
* Available tools for the AI to use
*/
export const JOEL_TOOLS: ChatCompletionTool[] = [
{
type: "function",
function: {
name: "lookup_user_memories",
description: "Look up what you remember about a specific user. Use this when you want to personalize your response with things you know about them, or when they ask if you remember something.",
parameters: {
type: "object",
properties: {
user_id: {
type: "string",
description: "The Discord user ID to look up memories for. Use the current user's ID if looking up the person you're talking to.",
},
category: {
type: "string",
enum: ["personal", "opinion", "fact", "preference", "event", "relationship", "general"],
description: "Filter by category (optional)",
},
limit: {
type: "number",
description: "Maximum number of memories to retrieve (default: 10)",
},
},
required: ["user_id"],
},
},
},
{
type: "function",
function: {
name: "save_memory",
description: "Save something important or interesting about a user for later. Use this when someone reveals personal information, preferences, embarrassing facts, or anything you can use against them later.",
parameters: {
type: "object",
properties: {
user_id: {
type: "string",
description: "The Discord user ID this memory is about",
},
content: {
type: "string",
description: "The fact or information to remember. Be concise but include context.",
},
category: {
type: "string",
enum: ["personal", "opinion", "fact", "preference", "event", "relationship", "general"],
description: "Category of the memory for organization",
},
importance: {
type: "number",
description: "How important this memory is from 1-10. Only memories 5+ will be saved.",
},
},
required: ["user_id", "content"],
},
},
},
{
type: "function",
function: {
name: "search_memories",
description: "Search through all memories for specific topics or keywords. Use this when you want to find if you know anything about a particular subject.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "Search query - keywords or topics to search for",
},
guild_id: {
type: "string",
description: "Limit search to a specific server (optional)",
},
category: {
type: "string",
enum: ["personal", "opinion", "fact", "preference", "event", "relationship", "general"],
description: "Filter by category (optional)",
},
min_importance: {
type: "number",
description: "Minimum importance score to include (1-10)",
},
},
required: ["query"],
},
},
},
{
type: "function",
function: {
name: "get_memory_stats",
description: "Get statistics about memories stored for a user - total count, breakdown by category, average importance.",
parameters: {
type: "object",
properties: {
user_id: {
type: "string",
description: "The Discord user ID to get stats for",
},
},
required: ["user_id"],
},
},
},
{
type: "function",
function: {
name: "forget_user",
description: "Delete all memories about a user. Only use if explicitly asked to forget someone.",
parameters: {
type: "object",
properties: {
user_id: {
type: "string",
description: "The Discord user ID to forget",
},
},
required: ["user_id"],
},
},
},
];
/**
* GIF search tool - only enabled when gif_search_enabled is true for the guild
*/
export const GIF_SEARCH_TOOL: ChatCompletionTool = {
type: "function",
function: {
name: "search_gif",
description: "Search for a funny GIF to send in the chat. Use this when you want to express yourself with a GIF, react to something funny, or just be chaotic. The GIF URL will be included in your response.",
parameters: {
type: "object",
properties: {
query: {
type: "string",
description: "Search query for the GIF. Be creative and funny with your searches!",
},
limit: {
type: "number",
description: "Number of GIFs to get back (1-10). Default is 5, then a random one is picked.",
},
},
required: ["query"],
},
},
};
/**
* Image generation tool - creates images using AI
*/
export const IMAGE_GEN_TOOL: ChatCompletionTool = {
type: "function",
function: {
name: "generate_image",
description: "Generate an image based on a text description. Use this when someone asks you to draw, create, or generate an image/picture. By default, generate SFW images. Only generate NSFW content if the user explicitly requests it using words like 'nsfw', 'nude', 'naked', 'porn', 'hentai', 'xxx', or similar. The generated image URL will be included in your response.",
parameters: {
type: "object",
properties: {
prompt: {
type: "string",
description: "Detailed description of the image to generate. Be specific about subject, pose, environment, lighting, and style. Only include adult content if explicitly requested by the user.",
},
style: {
type: "string",
enum: ["photorealistic", "anime", "hentai", "digital art", "painting", "3d render"],
description: "Art style for the image.",
},
aspect_ratio: {
type: "string",
enum: ["1:1", "16:9", "9:16", "4:3", "3:4"],
description: "Aspect ratio. Use 9:16 or 3:4 for portraits/full body, 16:9 for wide scenes.",
},
quality: {
type: "string",
enum: ["fast", "quality", "anime"],
description: "Model selection. 'fast' = quick generation, 'quality' = higher detail, 'anime' = anime style.",
},
},
required: ["prompt"],
},
},
};
/**
* Get tools based on context settings
* Returns the base tools plus any optional tools that are enabled
*/
export function getToolsForContext(context: ToolContext): ChatCompletionTool[] {
const tools = [...JOEL_TOOLS];
// Add GIF search tool if enabled for this guild
if (context.gifSearchEnabled) {
tools.push(GIF_SEARCH_TOOL);
}
// Add image generation tool if enabled for this guild
if (context.imageGenEnabled) {
tools.push(IMAGE_GEN_TOOL);
}
return tools;
}
/**
* Subset of tools for memory extraction (lightweight)
*/
export const MEMORY_EXTRACTION_TOOLS: ChatCompletionTool[] = [
{
type: "function",
function: {
name: "extract_memory",
description: "Extract and save a memorable fact from the conversation. Only call this if there's something genuinely worth remembering.",
parameters: {
type: "object",
properties: {
content: {
type: "string",
description: "The fact to remember, written in third person (e.g., 'Loves pineapple on pizza')",
},
category: {
type: "string",
enum: ["personal", "opinion", "fact", "preference", "event", "relationship", "general"],
description: "What type of information this is",
},
importance: {
type: "number",
description: "How important/memorable this is from 1-10. Be honest - mundane chat is 1-3, interesting facts are 4-6, juicy secrets are 7-10.",
},
},
required: ["content", "category", "importance"],
},
},
},
];