joel memories
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
import { createLogger } from "../../core/logger";
|
||||
import { OpenRouterProvider } from "./openrouter";
|
||||
import type { AiProvider, AiResponse, MessageStyle } from "./types";
|
||||
import type { ToolContext } from "./tools";
|
||||
|
||||
const logger = createLogger("AI:Service");
|
||||
|
||||
@@ -27,6 +28,23 @@ export class AiService {
|
||||
return this.provider.ask({ prompt, systemPrompt });
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a response with tool calling support
|
||||
* The AI can look up memories, save new ones, etc.
|
||||
*/
|
||||
async generateResponseWithTools(
|
||||
prompt: string,
|
||||
systemPrompt: string,
|
||||
context: ToolContext
|
||||
): Promise<AiResponse> {
|
||||
if (this.provider.askWithTools) {
|
||||
logger.debug("Generating response with tools", { promptLength: prompt.length });
|
||||
return this.provider.askWithTools({ prompt, systemPrompt, context });
|
||||
}
|
||||
// Fallback to regular response if tools not supported
|
||||
return this.generateResponse(prompt, systemPrompt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify a message to determine the appropriate response style
|
||||
*/
|
||||
@@ -37,6 +55,19 @@ export class AiService {
|
||||
// Default to snarky if provider doesn't support classification
|
||||
return "snarky";
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract and save memorable information from a message
|
||||
*/
|
||||
async extractMemories(
|
||||
message: string,
|
||||
authorName: string,
|
||||
context: ToolContext
|
||||
): Promise<void> {
|
||||
if (this.provider.extractMemories) {
|
||||
return this.provider.extractMemories(message, authorName, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Singleton instance
|
||||
@@ -50,3 +81,5 @@ export function getAiService(): AiService {
|
||||
}
|
||||
|
||||
export type { AiProvider, AiResponse, MessageStyle } from "./types";
|
||||
export type { ToolContext, ToolCall, ToolResult } from "./tools";
|
||||
export { JOEL_TOOLS, MEMORY_EXTRACTION_TOOLS } from "./tools";
|
||||
|
||||
@@ -3,15 +3,21 @@
|
||||
*/
|
||||
|
||||
import OpenAI from "openai";
|
||||
import type { ChatCompletionMessageParam, ChatCompletionTool } from "openai/resources/chat/completions";
|
||||
import { config } from "../../core/config";
|
||||
import { createLogger } from "../../core/logger";
|
||||
import type { AiProvider, AiResponse, AskOptions, MessageStyle } from "./types";
|
||||
import type { AiProvider, AiResponse, AskOptions, AskWithToolsOptions, MessageStyle } from "./types";
|
||||
import { JOEL_TOOLS, MEMORY_EXTRACTION_TOOLS, type ToolCall, type ToolContext } from "./tools";
|
||||
import { executeTools } from "./tool-handlers";
|
||||
|
||||
const logger = createLogger("AI:OpenRouter");
|
||||
|
||||
// Style classification options
|
||||
const STYLE_OPTIONS: MessageStyle[] = ["story", "snarky", "insult", "explicit", "helpful"];
|
||||
|
||||
// Maximum tool call iterations to prevent infinite loops
|
||||
const MAX_TOOL_ITERATIONS = 5;
|
||||
|
||||
export class OpenRouterProvider implements AiProvider {
|
||||
private client: OpenAI;
|
||||
|
||||
@@ -61,6 +67,148 @@ export class OpenRouterProvider implements AiProvider {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a response with tool calling support
|
||||
* The AI can call tools (like looking up memories) during response generation
|
||||
*/
|
||||
async askWithTools(options: AskWithToolsOptions): Promise<AiResponse> {
|
||||
const { prompt, systemPrompt, context, maxTokens, temperature } = options;
|
||||
|
||||
const messages: ChatCompletionMessageParam[] = [
|
||||
{ role: "system", content: systemPrompt },
|
||||
{ role: "user", content: prompt },
|
||||
];
|
||||
|
||||
let iterations = 0;
|
||||
|
||||
while (iterations < MAX_TOOL_ITERATIONS) {
|
||||
iterations++;
|
||||
|
||||
try {
|
||||
const completion = await this.client.chat.completions.create({
|
||||
model: config.ai.model,
|
||||
messages,
|
||||
tools: JOEL_TOOLS,
|
||||
tool_choice: "auto",
|
||||
max_tokens: maxTokens ?? config.ai.maxTokens,
|
||||
temperature: temperature ?? config.ai.temperature,
|
||||
});
|
||||
|
||||
const choice = completion.choices[0];
|
||||
const message = choice?.message;
|
||||
|
||||
if (!message) {
|
||||
logger.warn("No message in completion");
|
||||
return { text: "" };
|
||||
}
|
||||
|
||||
// Check if the AI wants to call tools
|
||||
if (message.tool_calls && message.tool_calls.length > 0) {
|
||||
logger.debug("AI requested tool calls", {
|
||||
count: message.tool_calls.length,
|
||||
tools: message.tool_calls.map(tc => tc.function.name)
|
||||
});
|
||||
|
||||
// Add the assistant's message with tool calls
|
||||
messages.push(message);
|
||||
|
||||
// Parse and execute tool calls
|
||||
const toolCalls: ToolCall[] = message.tool_calls.map((tc) => ({
|
||||
id: tc.id,
|
||||
name: tc.function.name,
|
||||
arguments: JSON.parse(tc.function.arguments || "{}"),
|
||||
}));
|
||||
|
||||
const results = await executeTools(toolCalls, context);
|
||||
|
||||
// Add tool results as messages
|
||||
for (let i = 0; i < toolCalls.length; i++) {
|
||||
messages.push({
|
||||
role: "tool",
|
||||
tool_call_id: toolCalls[i].id,
|
||||
content: results[i].result,
|
||||
});
|
||||
}
|
||||
|
||||
// Continue the loop to get the AI's response after tool execution
|
||||
continue;
|
||||
}
|
||||
|
||||
// No tool calls - we have a final response
|
||||
const text = message.content ?? "";
|
||||
logger.debug("AI response generated", {
|
||||
iterations,
|
||||
textLength: text.length
|
||||
});
|
||||
|
||||
return { text: text.slice(0, 1900) };
|
||||
} catch (error: unknown) {
|
||||
logger.error("Failed to generate response with tools", error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
logger.warn("Max tool iterations reached");
|
||||
return { text: "I got stuck in a loop thinking about that..." };
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyze a message to extract memorable information
|
||||
*/
|
||||
async extractMemories(
|
||||
message: string,
|
||||
authorName: string,
|
||||
context: ToolContext
|
||||
): Promise<void> {
|
||||
const systemPrompt = `You are analyzing a Discord message to determine if it contains any memorable or useful information about the user "${authorName}".
|
||||
|
||||
Look for:
|
||||
- Personal information (name, age, location, job, hobbies)
|
||||
- Preferences (likes, dislikes, favorites)
|
||||
- Embarrassing admissions or confessions
|
||||
- Strong opinions or hot takes
|
||||
- Achievements or accomplishments
|
||||
- Relationships or social information
|
||||
- Recurring patterns or habits
|
||||
|
||||
If you find something worth remembering, use the extract_memory tool. Only extract genuinely interesting or useful information - don't save trivial things.
|
||||
|
||||
The user's Discord ID is: ${context.userId}`;
|
||||
|
||||
try {
|
||||
const completion = await this.client.chat.completions.create({
|
||||
model: config.ai.classificationModel,
|
||||
messages: [
|
||||
{ role: "system", content: systemPrompt },
|
||||
{ role: "user", content: `Analyze this message for memorable content:\n\n"${message}"` },
|
||||
],
|
||||
tools: MEMORY_EXTRACTION_TOOLS,
|
||||
tool_choice: "auto",
|
||||
max_tokens: 200,
|
||||
temperature: 0.3,
|
||||
});
|
||||
|
||||
const toolCalls = completion.choices[0]?.message?.tool_calls;
|
||||
|
||||
if (toolCalls && toolCalls.length > 0) {
|
||||
const parsedCalls: ToolCall[] = toolCalls.map((tc) => ({
|
||||
id: tc.id,
|
||||
name: tc.function.name,
|
||||
arguments: JSON.parse(tc.function.arguments || "{}"),
|
||||
}));
|
||||
|
||||
await executeTools(parsedCalls, context);
|
||||
logger.debug("Memory extraction complete", {
|
||||
extracted: parsedCalls.length,
|
||||
authorName
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// Don't throw - memory extraction is non-critical
|
||||
logger.error("Memory extraction failed", error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify a message to determine the appropriate response style
|
||||
*/
|
||||
|
||||
233
src/services/ai/tool-handlers.ts
Normal file
233
src/services/ai/tool-handlers.ts
Normal file
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* Tool handler implementations
|
||||
* Executes the actual logic when the AI calls a tool
|
||||
*/
|
||||
|
||||
import { createLogger } from "../../core/logger";
|
||||
import { memoryRepository, type MemoryCategory } from "../../database";
|
||||
import type { ToolHandler, ToolContext, ToolCall, ToolResult } from "./tools";
|
||||
|
||||
const logger = createLogger("AI:ToolHandlers");
|
||||
|
||||
/**
|
||||
* Registry of tool handlers
|
||||
*/
|
||||
const toolHandlers: Record<string, ToolHandler> = {
|
||||
/**
|
||||
* Look up memories about a specific user
|
||||
*/
|
||||
async lookup_user_memories(args, context): Promise<string> {
|
||||
const userId = (args.user_id as string) || context.userId;
|
||||
const limit = (args.limit as number) || 10;
|
||||
const category = args.category as MemoryCategory | undefined;
|
||||
|
||||
logger.debug("Looking up memories", { userId, limit, category });
|
||||
|
||||
let userMemories;
|
||||
if (category) {
|
||||
userMemories = await memoryRepository.findByCategory(userId, category, limit);
|
||||
} else {
|
||||
userMemories = await memoryRepository.findByUserId(userId, limit);
|
||||
}
|
||||
|
||||
if (userMemories.length === 0) {
|
||||
return `No memories found for this user. You don't know anything about them yet.`;
|
||||
}
|
||||
|
||||
const memoryList = userMemories
|
||||
.map((m, i) => {
|
||||
const cat = m.category || "general";
|
||||
const imp = m.importance || 5;
|
||||
return `${i + 1}. [${cat}|★${imp}] ${m.content}`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
return `Found ${userMemories.length} memories about this user:\n${memoryList}`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Save a new memory about a user
|
||||
*/
|
||||
async save_memory(args, context): Promise<string> {
|
||||
const userId = (args.user_id as string) || context.userId;
|
||||
const content = args.content as string;
|
||||
const category = (args.category as MemoryCategory) || "general";
|
||||
const importance = (args.importance as number) || 5;
|
||||
|
||||
if (!content || content.trim().length === 0) {
|
||||
return "Error: No content provided to remember.";
|
||||
}
|
||||
|
||||
// Check for duplicate memories using new similarity check
|
||||
const similar = await memoryRepository.findSimilar(userId, content);
|
||||
if (similar.length > 0) {
|
||||
return "Already knew something similar. Memory not saved (duplicate).";
|
||||
}
|
||||
|
||||
logger.info("Saving new memory", {
|
||||
userId,
|
||||
category,
|
||||
importance,
|
||||
contentLength: content.length
|
||||
});
|
||||
|
||||
await memoryRepository.create({
|
||||
userId,
|
||||
guildId: context.guildId,
|
||||
content,
|
||||
category,
|
||||
importance,
|
||||
});
|
||||
|
||||
return `Memory saved [${category}|★${importance}]: "${content.slice(0, 100)}${content.length > 100 ? "..." : ""}"`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Search memories by keyword/topic
|
||||
*/
|
||||
async search_memories(args, context): Promise<string> {
|
||||
const query = args.query as string;
|
||||
const guildId = args.guild_id as string | undefined;
|
||||
const category = args.category as MemoryCategory | undefined;
|
||||
const minImportance = args.min_importance as number | undefined;
|
||||
|
||||
if (!query || query.trim().length === 0) {
|
||||
return "Error: No search query provided.";
|
||||
}
|
||||
|
||||
logger.debug("Searching memories", { query, guildId, category, minImportance });
|
||||
|
||||
const results = await memoryRepository.search({
|
||||
query,
|
||||
guildId,
|
||||
category,
|
||||
minImportance,
|
||||
limit: 15,
|
||||
});
|
||||
|
||||
if (results.length === 0) {
|
||||
return `No memories found matching "${query}".`;
|
||||
}
|
||||
|
||||
const memoryList = results
|
||||
.map((m, i) => {
|
||||
const cat = m.category || "general";
|
||||
const imp = m.importance || 5;
|
||||
return `${i + 1}. [User ${m.user_id?.slice(0, 8)}...] [${cat}|★${imp}] ${m.content}`;
|
||||
})
|
||||
.join("\n");
|
||||
|
||||
return `Found ${results.length} memories matching "${query}":\n${memoryList}`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Delete all memories about a user
|
||||
*/
|
||||
async forget_user(args, context): Promise<string> {
|
||||
const userId = (args.user_id as string) || context.userId;
|
||||
|
||||
logger.warn("Forgetting user memories", { userId, requestedBy: context.userId });
|
||||
|
||||
const deleted = await memoryRepository.deleteByUserId(userId);
|
||||
|
||||
return `Deleted ${deleted} memories about user ${userId}.`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Extract memory from conversation (used by memory extraction system)
|
||||
*/
|
||||
async extract_memory(args, context): Promise<string> {
|
||||
const content = args.content as string;
|
||||
const category = (args.category as MemoryCategory) || "general";
|
||||
const importance = (args.importance as number) || 5;
|
||||
|
||||
// Only save if importance is high enough
|
||||
if (importance < 5) {
|
||||
return `Memory not important enough (${importance}/10). Skipped.`;
|
||||
}
|
||||
|
||||
// Check for duplicates
|
||||
const similar = await memoryRepository.findSimilar(context.userId, content);
|
||||
if (similar.length > 0) {
|
||||
return "Similar memory already exists. Skipped.";
|
||||
}
|
||||
|
||||
logger.info("Extracting memory from conversation", {
|
||||
userId: context.userId,
|
||||
category,
|
||||
importance,
|
||||
});
|
||||
|
||||
await memoryRepository.create({
|
||||
userId: context.userId,
|
||||
guildId: context.guildId,
|
||||
content,
|
||||
category,
|
||||
importance,
|
||||
});
|
||||
|
||||
return `Memory extracted [${category}|★${importance}]: "${content.slice(0, 50)}..."`;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get statistics about a user's memories
|
||||
*/
|
||||
async get_memory_stats(args, context): Promise<string> {
|
||||
const userId = (args.user_id as string) || context.userId;
|
||||
|
||||
const stats = await memoryRepository.getStats(userId);
|
||||
|
||||
if (stats.total === 0) {
|
||||
return `No memories stored for this user.`;
|
||||
}
|
||||
|
||||
const categoryBreakdown = Object.entries(stats.byCategory)
|
||||
.map(([cat, count]) => ` - ${cat}: ${count}`)
|
||||
.join("\n");
|
||||
|
||||
return `Memory stats for user:\n` +
|
||||
`Total: ${stats.total} memories\n` +
|
||||
`Average importance: ${stats.avgImportance.toFixed(1)}/10\n` +
|
||||
`By category:\n${categoryBreakdown}`;
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute a tool call and return the result
|
||||
*/
|
||||
export async function executeTool(
|
||||
toolCall: ToolCall,
|
||||
context: ToolContext
|
||||
): Promise<ToolResult> {
|
||||
const handler = toolHandlers[toolCall.name];
|
||||
|
||||
if (!handler) {
|
||||
logger.warn("Unknown tool called", { name: toolCall.name });
|
||||
return {
|
||||
name: toolCall.name,
|
||||
result: `Error: Unknown tool "${toolCall.name}"`,
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await handler(toolCall.arguments, context);
|
||||
logger.debug("Tool executed", { name: toolCall.name, resultLength: result.length });
|
||||
return { name: toolCall.name, result };
|
||||
} catch (error) {
|
||||
logger.error("Tool execution failed", { name: toolCall.name, error });
|
||||
return {
|
||||
name: toolCall.name,
|
||||
result: `Error executing tool: ${(error as Error).message}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute multiple tool calls
|
||||
*/
|
||||
export async function executeTools(
|
||||
toolCalls: ToolCall[],
|
||||
context: ToolContext
|
||||
): Promise<ToolResult[]> {
|
||||
return Promise.all(toolCalls.map((tc) => executeTool(tc, context)));
|
||||
}
|
||||
199
src/services/ai/tools.ts
Normal file
199
src/services/ai/tools.ts
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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"],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
* 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"],
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -3,6 +3,8 @@
|
||||
* Allows swapping AI providers (Replicate, OpenAI, etc.) without changing business logic
|
||||
*/
|
||||
|
||||
import type { ToolContext } from "./tools";
|
||||
|
||||
export interface AiResponse {
|
||||
text: string;
|
||||
}
|
||||
@@ -18,6 +20,11 @@ export interface AiProvider {
|
||||
*/
|
||||
ask(options: AskOptions): Promise<AiResponse>;
|
||||
|
||||
/**
|
||||
* Generate a response with tool calling support
|
||||
*/
|
||||
askWithTools?(options: AskWithToolsOptions): Promise<AiResponse>;
|
||||
|
||||
/**
|
||||
* Check if the AI service is healthy
|
||||
*/
|
||||
@@ -27,6 +34,15 @@ export interface AiProvider {
|
||||
* Classify a message to determine response style
|
||||
*/
|
||||
classifyMessage?(message: string): Promise<MessageStyle>;
|
||||
|
||||
/**
|
||||
* Extract memorable information from a message
|
||||
*/
|
||||
extractMemories?(
|
||||
message: string,
|
||||
authorName: string,
|
||||
context: ToolContext
|
||||
): Promise<void>;
|
||||
}
|
||||
|
||||
export interface AskOptions {
|
||||
@@ -35,3 +51,7 @@ export interface AskOptions {
|
||||
maxTokens?: number;
|
||||
temperature?: number;
|
||||
}
|
||||
|
||||
export interface AskWithToolsOptions extends AskOptions {
|
||||
context: ToolContext;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user