feat: save chat

This commit is contained in:
archer 2025-12-05 10:39:58 +08:00
parent d63311d67a
commit b458c3447c
No known key found for this signature in database
GPG Key ID: 4446499B846D4A9E
8 changed files with 166 additions and 11 deletions

View File

@ -21,12 +21,13 @@ export const HelperBotChatSchema = z.object({
type: HelperBotTypeEnum,
userId: z.string(),
createTime: z.date(),
updateTime: z.date()
updateTime: z.date(),
metadata: z.record(z.string(), z.any()).optional()
});
export type HelperBotChatType = z.infer<typeof HelperBotChatSchema>;
// AI schema
const AIChatItemValueItemSchema = z.union([
export const AIChatItemValueItemSchema = z.union([
z.object({
text: z.object({
content: z.string()
@ -41,10 +42,12 @@ const AIChatItemValueItemSchema = z.union([
tool: ToolModuleResponseItemSchema
})
]);
export type AIChatItemValueItemType = z.infer<typeof AIChatItemValueItemSchema>;
const AIChatItemSchema = z.object({
obj: z.literal(ChatRoleEnum.AI),
value: z.array(AIChatItemValueItemSchema)
});
export type AIChatItemType = z.infer<typeof AIChatItemSchema>;
const HelperBotChatRoleSchema = z.union([
UserChatItemSchema,

View File

@ -67,7 +67,7 @@ export type ChatWithAppSchema = Omit<ChatSchemaType, 'appId'> & {
/* --------- chat item ---------- */
// User
export const UserChatItemFileItemSchema = z.object({
type: z.enum(Object.values(ChatFileTypeEnum)),
type: z.enum(ChatFileTypeEnum),
name: z.string().optional(),
key: z.string().optional(),
url: z.string()

View File

@ -1,11 +1,13 @@
import { PaginationPropsSchema, PaginationResponseSchema } from '../../../type';
import {
type HelperBotChatItemSiteType,
HelperBotTypeEnum,
HelperBotTypeEnumSchema,
topAgentParamsSchema
} from '../../../../core/chat/helperBot/type';
import { z } from 'zod';
import type { PaginationResponse } from '../../../../../web/common/fetch/type';
import { ChatFileTypeEnum } from '../../../../core/chat/constants';
// 分页获取记录
export const GetHelperBotChatRecordsParamsSchema = z
@ -46,7 +48,7 @@ export const HelperBotCompletionsParamsSchema = z.object({
query: z.string(),
files: z.array(
z.object({
type: z.enum(['image', 'file']),
type: z.enum(ChatFileTypeEnum),
key: z.string(),
url: z.string().optional(),
name: z.string()
@ -54,7 +56,7 @@ export const HelperBotCompletionsParamsSchema = z.object({
),
metadata: z.discriminatedUnion('type', [
z.object({
type: z.literal('topAgent'),
type: z.literal(HelperBotTypeEnum.topAgent),
data: topAgentParamsSchema
})
])

View File

@ -25,7 +25,8 @@ const HelperBotChatSchema = new Schema({
updateTime: {
type: Date,
default: () => new Date()
}
},
metadata: Object
});
HelperBotChatSchema.index({ type: 1, userId: 1, chatId: 1 }, { unique: true });

View File

@ -1,10 +1,35 @@
import type { HelperBotDispatchParamsType } from '../type';
import type { HelperBotDispatchParamsType, HelperBotDispatchResponseType } from '../type';
import { helperChats2GPTMessages } from '@fastgpt/global/core/chat/helperBot/adaptor';
export const dispatchTopAgent = async (props: HelperBotDispatchParamsType) => {
export const dispatchTopAgent = async (
props: HelperBotDispatchParamsType
): Promise<HelperBotDispatchResponseType> => {
const { query, files, metadata, histories } = props;
const messages = helperChats2GPTMessages({
messages: histories,
reserveTool: false
});
// 拿工具资源参考 FastGPT/projects/app/src/pages/api/core/app/tool/getSystemToolTemplates.ts
/*
onReasoning({ text }) {
if (!aiChatReasoning) return;
workflowStreamResponse?.({
event: SseResponseEventEnum.answer,
data: textAdaptGptResponse({
reasoning_content: text
})
});
},
onStreaming({ text }) {
if (!isResponseAnswerText) return;
workflowStreamResponse?.({
event: SseResponseEventEnum.answer,
data: textAdaptGptResponse({
text
})
});
}
*/
};

View File

@ -1,6 +1,9 @@
import { z } from 'zod';
import { HelperBotCompletionsParamsSchema } from '../../../../../global/openapi/core/chat/helperBot/api';
import { HelperBotChatItemSchema } from '@fastgpt/global/core/chat/helperBot/type';
import {
AIChatItemValueItemSchema,
HelperBotChatItemSchema
} from '@fastgpt/global/core/chat/helperBot/type';
import { WorkflowResponseFnSchema } from '../../../workflow/dispatch/type';
export const HelperBotDispatchParamsSchema = z.object({
@ -12,5 +15,7 @@ export const HelperBotDispatchParamsSchema = z.object({
});
export type HelperBotDispatchParamsType = z.infer<typeof HelperBotDispatchParamsSchema>;
export const HelperBotDispatchResponseSchema = z.object({});
export const HelperBotDispatchResponseSchema = z.object({
aiResponse: z.array(AIChatItemValueItemSchema)
});
export type HelperBotDispatchResponseType = z.infer<typeof HelperBotDispatchResponseSchema>;

View File

@ -0,0 +1,103 @@
import type { HelperBotTypeEnum } from '@fastgpt/global/core/chat/helperBot/type';
import type { HelperBotCompletionsParamsType } from '@fastgpt/global/openapi/core/chat/helperBot/api';
import { MongoHelperBotChat } from './chatSchema';
import type {
AIChatItemValueItemType,
UserChatItemValueItemType
} from '@fastgpt/global/core/chat/type';
import { MongoHelperBotChatItem } from './chatItemSchema';
import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { mongoSessionRun } from 'common/mongo/sessionRun';
export const pushChatRecords = async ({
type,
userId,
chatId,
chatItemId,
query,
files,
aiResponse,
memories,
metadata
}: {
type: HelperBotTypeEnum;
userId: string;
chatId: string;
chatItemId: string;
query: string;
files: HelperBotCompletionsParamsType['files'];
aiResponse: AIChatItemValueItemType[];
memories?: Record<string, any>;
metadata?: Record<string, any>;
}) => {
const chat = await MongoHelperBotChat.findOne(
{
type,
userId,
chatId
},
'_id metadata'
).lean();
const metadataUpdate = {
...chat?.metadata,
...metadata
};
const userValue: UserChatItemValueItemType[] = [
...files.map((file) => ({
file: {
type: file.type,
name: file.name,
url: '',
key: file.key || ''
}
})),
...(query
? [
{
text: {
content: query
}
}
]
: [])
];
await mongoSessionRun(async (session) => {
await MongoHelperBotChatItem.create(
[
{
userId,
chatId,
dataId: chatItemId,
obj: ChatRoleEnum.Human,
value: userValue
},
{
userId,
chatId,
dataId: chatItemId,
obj: ChatRoleEnum.AI,
value: aiResponse,
memories
}
],
{ session, ordered: true }
);
await MongoHelperBotChat.updateOne(
{
type,
userId,
chatId
},
{
updateTime: new Date(),
metadata: metadataUpdate
},
{
session
}
);
});
};

View File

@ -8,6 +8,7 @@ import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoHelperBotChatItem } from '@fastgpt/service/core/chat/HelperBot/chatItemSchema';
import { getWorkflowResponseWrite } from '@fastgpt/service/core/workflow/dispatch/utils';
import { dispatchMap } from '@fastgpt/service/core/chat/HelperBot/dispatch/index';
import { pushChatRecords } from '@fastgpt/service/core/chat/HelperBot/utils';
export type completionsBody = HelperBotCompletionsParamsType;
@ -37,9 +38,24 @@ async function handler(req: ApiRequestProps<completionsBody>, res: ApiResponseTy
// 执行不同逻辑
const fn = dispatchMap[metadata.type];
const result = await fn({});
const result = await fn({
query,
files,
metadata,
histories,
workflowResponseWrite
});
// Save chat
await pushChatRecords({
type: metadata.type,
userId,
chatId,
chatItemId,
query,
files,
aiResponse: result.aiResponse
});
// Push usage
}