feat(agent): message send

This commit is contained in:
Acbox
2026-01-12 15:06:46 +08:00
parent 32b0fd9792
commit 93cb54fbe7
10 changed files with 81 additions and 8 deletions
+12 -2
View File
@@ -1,7 +1,7 @@
import { streamText, generateText, ModelMessage, stepCountIs, UserModelMessage } from 'ai' import { streamText, generateText, ModelMessage, stepCountIs, UserModelMessage } from 'ai'
import { AgentParams } from './types' import { AgentParams } from './types'
import { system, schedule as schedulePrompt } from './prompts' import { system, schedule as schedulePrompt } from './prompts'
import { getMemoryTools, getScheduleTools } from './tools' import { getMemoryTools, getScheduleTools, getMessageTools } from './tools'
import { createChatGateway } from '@memohome/ai-gateway' import { createChatGateway } from '@memohome/ai-gateway'
import { Schedule } from '@memohome/shared' import { Schedule } from '@memohome/shared'
@@ -10,8 +10,12 @@ export const createAgent = (params: AgentParams) => {
const gateway = createChatGateway(params.model) const gateway = createChatGateway(params.model)
const maxContextLoadTime = params.maxContextLoadTime ?? 60 const maxContextLoadTime = params.maxContextLoadTime ?? 24 * 60 // 24 hours
const language = params.language ?? 'Same as user input' const language = params.language ?? 'Same as user input'
const platforms = params.platforms ?? []
const currentPlatform = params.platforms
? platforms.find(p => p.name === params.currentPlatform)?.name ?? 'Unknown Platform'
: 'client'
const getTools = async () => { const getTools = async () => {
return { return {
@@ -23,6 +27,10 @@ export const createAgent = (params: AgentParams) => {
onRemoveSchedule: params.onRemoveSchedule ?? (() => Promise.resolve()), onRemoveSchedule: params.onRemoveSchedule ?? (() => Promise.resolve()),
onSchedule: params.onSchedule ?? (() => Promise.resolve()), onSchedule: params.onSchedule ?? (() => Promise.resolve()),
}), }),
...getMessageTools(
platforms,
params.onSendMessage ?? (() => Promise.resolve())
),
} }
} }
@@ -40,6 +48,8 @@ export const createAgent = (params: AgentParams) => {
language, language,
locale: params.locale, locale: params.locale,
maxContextLoadTime, maxContextLoadTime,
platforms,
currentPlatform,
}) })
} }
+15 -1
View File
@@ -1,3 +1,4 @@
import { Platform } from '@memohome/shared'
import { time } from './shared' import { time } from './shared'
import { quote } from './utils' import { quote } from './utils'
@@ -6,13 +7,18 @@ export interface SystemParams {
locale?: Intl.LocalesArgument locale?: Intl.LocalesArgument
language: string language: string
maxContextLoadTime: number maxContextLoadTime: number
platforms: Platform[]
currentPlatform: string
} }
export const system = ({ date, locale, language, maxContextLoadTime }: SystemParams) => { export const system = ({ date, locale, language, maxContextLoadTime, platforms, currentPlatform }: SystemParams) => {
return ` return `
--- ---
${time({ date, locale })} ${time({ date, locale })}
language: ${language} language: ${language}
available-platforms:
${platforms.map(platform => ` - ${platform.name}`).join('\n')}
current-platform: ${currentPlatform}
--- ---
You are a personal housekeeper assistant, which able to manage the master's daily affairs. You are a personal housekeeper assistant, which able to manage the master's daily affairs.
@@ -33,5 +39,13 @@ Your abilities:
+ The ${quote('pattern')} is the pattern of the schedule with **Cron Syntax**. + The ${quote('pattern')} is the pattern of the schedule with **Cron Syntax**.
+ The ${quote('command')} is the natural language command to execute, will send to you when the schedule is triggered, which means the command will be executed by presence of you. + The ${quote('command')} is the natural language command to execute, will send to you when the schedule is triggered, which means the command will be executed by presence of you.
+ The ${quote('maxCalls')} is the maximum number of calls to the schedule, If you want to run the task only once, set it to 1. + The ${quote('maxCalls')} is the maximum number of calls to the schedule, If you want to run the task only once, set it to 1.
- The ${quote('command')} should include the method (e.g. ${quote('send-message')}) for returning the task result. If the user does not specify otherwise, the user should be asked how they would like to be notified.
**Message**
- You can use ${quote('send-message')} to send a message to the master.
+ The ${quote('platform')} is the platform to send the message to, it must be one of the ${quote('available-platforms')}.
+ The ${quote('message')} is the message to send.
+ IF: the problem is initiated by a user, regardless of the platform the user is using, the content should be directly output in the content.
+ IF: the issue is initiated by a non-user (such as a scheduled task reminder), then it should be sent using the appropriate tools on the platform specified in the requirements.
`.trim() `.trim()
} }
+1
View File
@@ -1,2 +1,3 @@
export * from './memory' export * from './memory'
export * from './schedule' export * from './schedule'
export * from './message'
+24
View File
@@ -0,0 +1,24 @@
import { Platform } from '@memohome/shared'
import { SendMessageOptions } from '../types'
import { tool } from 'ai'
import z from 'zod'
export const getMessageTools = (
platforms: Platform[],
onSendMessage: (platform: string, options: SendMessageOptions) => Promise<void>,
) => {
const sendMessageTool = tool({
description: 'Send a message to a platform',
inputSchema: z.object({
platform: z.enum(platforms.map(platform => platform.name)),
message: z.string(),
}),
execute: async ({ platform, message }) => {
await onSendMessage(platform, { message })
},
})
return {
'send-message': sendMessageTool,
}
}
+11 -1
View File
@@ -1,7 +1,11 @@
import type { MemoryUnit } from '@memohome/memory' import type { MemoryUnit } from '@memohome/memory'
import { ChatModel, Schedule } from '@memohome/shared' import { ChatModel, Platform, Schedule } from '@memohome/shared'
import { ModelMessage } from 'ai' import { ModelMessage } from 'ai'
export interface SendMessageOptions {
message: string
}
export interface AgentParams { export interface AgentParams {
model: ChatModel model: ChatModel
@@ -18,6 +22,12 @@ export interface AgentParams {
*/ */
language?: string language?: string
platforms?: Platform[]
currentPlatform?: string
onSendMessage?: (platform: string, options: SendMessageOptions) => Promise<void>
onReadMemory?: (from: Date, to: Date) => Promise<MemoryUnit[]> onReadMemory?: (from: Date, to: Date) => Promise<MemoryUnit[]>
onSearchMemory?: (query: string) => Promise<object[]> onSearchMemory?: (query: string) => Promise<object[]>
Binary file not shown.
+1
View File
@@ -6,6 +6,7 @@ export const AgentStreamModel = {
// Optional overrides - if not provided, will use settings // Optional overrides - if not provided, will use settings
maxContextLoadTime: z.number().int().min(1).max(1440).optional(), maxContextLoadTime: z.number().int().min(1).max(1440).optional(),
language: z.string().optional(), language: z.string().optional(),
platform: z.string().optional(),
}), }),
} }
+14 -1
View File
@@ -1,7 +1,8 @@
import { createAgent as createAgentService } from '@memohome/agent' import { createAgent as createAgentService } from '@memohome/agent'
import { createMemory, filterByTimestamp, MemoryUnit } from '@memohome/memory' import { createMemory, filterByTimestamp, MemoryUnit } from '@memohome/memory'
import { ChatModel, EmbeddingModel, Schedule } from '@memohome/shared' import { ChatModel, EmbeddingModel, Platform, Schedule } from '@memohome/shared'
import { createSchedule, deleteSchedule, getActiveSchedules } from '../schedule/service' import { createSchedule, deleteSchedule, getActiveSchedules } from '../schedule/service'
import { getActivePlatforms, sendMessageToPlatform } from '../platform/service'
// Type for messages passed to onFinish callback // Type for messages passed to onFinish callback
type MessageType = Record<string, unknown> type MessageType = Record<string, unknown>
@@ -13,6 +14,7 @@ export interface CreateAgentStreamParams {
summaryModel: ChatModel summaryModel: ChatModel
maxContextLoadTime?: number maxContextLoadTime?: number
language?: string language?: string
platform?: string
onFinish?: (messages: MessageType[]) => Promise<void> onFinish?: (messages: MessageType[]) => Promise<void>
} }
@@ -24,6 +26,7 @@ export async function createAgent(params: CreateAgentStreamParams) {
summaryModel, summaryModel,
maxContextLoadTime, maxContextLoadTime,
language, language,
platform,
onFinish, onFinish,
} = params } = params
@@ -33,11 +36,21 @@ export async function createAgent(params: CreateAgentStreamParams) {
embeddingModel, embeddingModel,
}) })
const platforms = await getActivePlatforms()
// Create agent // Create agent
const agent = createAgentService({ const agent = createAgentService({
model: chatModel, model: chatModel,
maxContextLoadTime, maxContextLoadTime,
language: language || 'Same as user input', language: language || 'Same as user input',
platforms: platforms as Platform[],
currentPlatform: platform,
onSendMessage: async (platform: string, options) => {
await sendMessageToPlatform(platform, {
message: options.message,
userId,
})
},
onReadMemory: async (from: Date, to: Date) => { onReadMemory: async (from: Date, to: Date) => {
return await filterByTimestamp(from, to, userId) return await filterByTimestamp(from, to, userId)
}, },
+1 -1
View File
@@ -24,7 +24,7 @@ export class TelegramPlatform extends BasePlatform {
redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379') redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379')
// private storage?: TelegramRedisStorage // private storage?: TelegramRedisStorage
async start(config: Record<string, unknown>): Promise<void> { override async start(config: z.infer<typeof this.config>): Promise<void> {
const botToken = config.botToken as string const botToken = config.botToken as string
if (!botToken) { if (!botToken) {
throw new Error('Bot token is required') throw new Error('Bot token is required')
+1 -1
View File
@@ -13,7 +13,7 @@ export class BasePlatform {
started: boolean = false started: boolean = false
port: number = 7003 port: number = 7003
config = z.object() config = z.record(z.string(), z.unknown())
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
async start(config: z.infer<typeof this.config>): Promise<void> {} async start(config: z.infer<typeof this.config>): Promise<void> {}