diff --git a/packages/drizzle-pkg/db.sqlite b/packages/drizzle-pkg/db.sqlite index a045b3a..d18dfae 100644 Binary files a/packages/drizzle-pkg/db.sqlite and b/packages/drizzle-pkg/db.sqlite differ diff --git a/server/api/llm/chat/index.post.ts b/server/api/llm/chat/index.post.ts index 13a4f65..8cba742 100644 --- a/server/api/llm/chat/index.post.ts +++ b/server/api/llm/chat/index.post.ts @@ -84,7 +84,7 @@ export default defineEventHandler(async (event) => { const languageModel = resolveModel(provider, model.modelId); - const { tools, approvalConfig } = enableTools ? await getEnabledToolsForLlm() : { tools: undefined, approvalConfig: {} }; + const { tools, approvalConfig } = enableTools ? await getEnabledToolsForLlm(user.id) : { tools: undefined, approvalConfig: {} }; const modelMessages = await convertToModelMessages(messages); diff --git a/server/service/agent-tool/executors/user-info/config.ts b/server/service/agent-tool/executors/user-info/config.ts new file mode 100644 index 0000000..19d05d5 --- /dev/null +++ b/server/service/agent-tool/executors/user-info/config.ts @@ -0,0 +1,27 @@ +import { z } from "zod"; + +export const userInfoConfigSchema = z.object({ + includeEmail: z.boolean().default(true), + includeAvatar: z.boolean().default(true), + includeRole: z.boolean().default(true), + includeStatus: z.boolean().default(false), + includeCreatedAt: z.boolean().default(false), +}); + +export type UserInfoToolConfig = z.infer; + +export const DEFAULT_USER_INFO_CONFIG: UserInfoToolConfig = { + includeEmail: true, + includeAvatar: true, + includeRole: true, + includeStatus: false, + includeCreatedAt: false, +}; + +export function parseUserInfoConfig(raw: unknown): UserInfoToolConfig { + const parsed = userInfoConfigSchema.safeParse(raw); + if (!parsed.success) { + throw new Error(`Invalid user-info config: ${parsed.error.message}`); + } + return parsed.data; +} diff --git a/server/service/agent-tool/executors/user-info/user-info.ts b/server/service/agent-tool/executors/user-info/user-info.ts new file mode 100644 index 0000000..7b5f6c8 --- /dev/null +++ b/server/service/agent-tool/executors/user-info/user-info.ts @@ -0,0 +1,114 @@ +import { z } from "zod"; +import type { JSONSchema7 } from "json-schema"; +import { dbGlobal } from "drizzle-pkg/lib/db"; +import { users } from "drizzle-pkg/lib/schema/auth"; +import { eq } from "drizzle-orm"; +import type { ToolExecutor, ToolContext, ToolResult } from "../../registry"; +import type { UserInfoToolConfig } from "./config"; + +export const userInfoInputSchema = z.object({}).describe("无需参数,自动获取当前登录用户信息"); + +type UserInfoInput = z.infer; + +export const userInfoExecutor: ToolExecutor = { + buildInputSchema(_config: UserInfoToolConfig): JSONSchema7 { + return { + type: "object", + properties: {}, + description: "无需参数,自动根据当前会话获取用户信息", + }; + }, + + buildDescription(config: UserInfoToolConfig): string { + const fields: string[] = []; + if (config.includeEmail) fields.push("邮箱"); + if (config.includeAvatar) fields.push("头像"); + if (config.includeRole) fields.push("角色"); + if (config.includeStatus) fields.push("状态"); + if (config.includeCreatedAt) fields.push("注册时间"); + const fieldDesc = fields.length > 0 ? fields.join("、") : "基本信息"; + return `获取当前登录用户信息(含${fieldDesc})。无需传入参数,自动从会话上下文中识别当前用户。`; + }, + + async execute( + input: unknown, + config: UserInfoToolConfig, + ctx: ToolContext, + ): Promise { + const start = Date.now(); + + const parsed = userInfoInputSchema.safeParse(input); + if (!parsed.success) { + return { + success: false, + data: null, + error: `输入参数校验失败: ${parsed.error.message}`, + metadata: { durationMs: Date.now() - start }, + }; + } + + if (ctx.userId === null || ctx.userId === undefined) { + return { + success: false, + data: null, + error: "无法识别当前用户:会话上下文中缺少 userId。该工具需要在登录会话中调用。", + metadata: { durationMs: Date.now() - start }, + }; + } + + try { + const rows = await dbGlobal + .select({ + id: users.id, + username: users.username, + email: users.email, + nickname: users.nickname, + avatar: users.avatar, + role: users.role, + status: users.status, + createdAt: users.createdAt, + }) + .from(users) + .where(eq(users.id, ctx.userId)) + .limit(1); + + const user = rows[0]; + if (!user) { + return { + success: false, + data: null, + error: `未找到用户 (id=${ctx.userId})`, + metadata: { durationMs: Date.now() - start }, + }; + } + + const data: Record = { + id: user.id, + username: user.username, + nickname: user.nickname ?? null, + }; + if (config.includeEmail) data.email = user.email ?? null; + if (config.includeAvatar) data.avatar = user.avatar ?? null; + if (config.includeRole) data.role = user.role; + if (config.includeStatus) data.status = user.status; + if (config.includeCreatedAt) { + data.createdAt = user.createdAt + ? new Date(user.createdAt).toISOString() + : null; + } + + return { + success: true, + data, + metadata: { durationMs: Date.now() - start }, + }; + } catch (e) { + return { + success: false, + data: null, + error: e instanceof Error ? e.message : String(e), + metadata: { durationMs: Date.now() - start }, + }; + } + }, +}; diff --git a/server/service/agent-tool/index.ts b/server/service/agent-tool/index.ts index de510da..2f37886 100644 --- a/server/service/agent-tool/index.ts +++ b/server/service/agent-tool/index.ts @@ -21,6 +21,8 @@ import { parseJsonFormatterConfig, DEFAULT_JSON_FORMATTER_CONFIG } from "./execu import { jsonFormatterExecutor, jsonFormatterInputSchema } from "./executors/json-formatter/json-formatter"; import { parseRegexTesterConfig, DEFAULT_REGEX_TESTER_CONFIG } from "./executors/regex-tester/config"; import { regexTesterExecutor, regexTesterInputSchema } from "./executors/regex-tester/regex-tester"; +import { parseUserInfoConfig, DEFAULT_USER_INFO_CONFIG } from "./executors/user-info/config"; +import { userInfoExecutor, userInfoInputSchema } from "./executors/user-info/user-info"; // ============ 工具类型注册表 ============ interface ToolTypeRegistration { @@ -73,6 +75,12 @@ const TOOL_TYPE_REGISTRY: Record = { defaultConfig: DEFAULT_REGEX_TESTER_CONFIG, zodSchema: regexTesterInputSchema, }, + "user-info": { + executor: userInfoExecutor, + parseConfig: parseUserInfoConfig, + defaultConfig: DEFAULT_USER_INFO_CONFIG, + zodSchema: userInfoInputSchema, + }, }; // 立即注册所有工具类型 @@ -315,7 +323,7 @@ export async function getEnabledToolInfos(): Promise { return result; } -export async function getEnabledToolsForLlm(): Promise<{ +export async function getEnabledToolsForLlm(userId: number | null = null): Promise<{ tools: Record>; approvalConfig: Record; }> { @@ -351,7 +359,7 @@ export async function getEnabledToolsForLlm(): Promise<{ }, }), execute: async (input: unknown) => { - const execResult = await executeAgentTool(agentTool.id, input, null); + const execResult = await executeAgentTool(agentTool.id, input, userId); if (!execResult.success) { return `工具执行失败: ${execResult.error ?? "未知错误"}。请停止调用此工具,基于已有信息回答用户或告知用户此工具不可用。`; } @@ -382,4 +390,6 @@ export { DEFAULT_JSON_FORMATTER_CONFIG } from "./executors/json-formatter/config export type { JsonFormatterToolConfig } from "./executors/json-formatter/config"; export { DEFAULT_REGEX_TESTER_CONFIG } from "./executors/regex-tester/config"; export type { RegexTesterToolConfig } from "./executors/regex-tester/config"; +export { DEFAULT_USER_INFO_CONFIG } from "./executors/user-info/config"; +export type { UserInfoToolConfig } from "./executors/user-info/config"; export type { ToolExecutor, ToolContext, ToolResult } from "./registry";