Files
FastGPT/packages/global/core/chat/adapt.ts
T
Finley Ge 939282b7c8 V4.14.8 dev (#6517)
* doc

* wip(next): upgrade next16 with next-rspack to build (#6501)

* wip(next): upgrade next16 with next-rspack to build

* wip: fix tsconfig path alias, bump various deps

* fix: test action pnpm version, immer dep

* fix: only use Rspack for develop environment

* lock

* fix: dataset choice hint (#6514)

* fix: dataset choice hint

* fix: regex replaceVarible remove useless match group

* fix: type check (#6515)

* test: perfect test cases for replaceVarible function in  like case (#6516)

---------

Co-authored-by: archer <545436317@qq.com>
Co-authored-by: Ryo <whoeverimf5@gmail.com>
2026-03-06 19:02:04 +08:00

481 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type {
AIChatItemValueItemType,
ChatItemType,
ChatItemValueItemType,
RuntimeUserPromptType,
SystemChatItemValueItemType,
ToolModuleResponseItemType,
UserChatItemFileItemType,
UserChatItemType,
UserChatItemValueItemType
} from './type';
import { ChatFileTypeEnum, ChatRoleEnum } from '../../core/chat/constants';
import type {
ChatCompletionContentPart,
ChatCompletionFunctionMessageParam,
ChatCompletionMessageFunctionCall,
ChatCompletionMessageParam,
ChatCompletionMessageToolCall,
ChatCompletionToolMessageParam
} from '../../core/ai/type';
import { ChatCompletionRequestMessageRoleEnum } from '../../core/ai/constants';
import { getNanoid } from '../../common/string/tools';
export const GPT2Chat = {
[ChatCompletionRequestMessageRoleEnum.System]: ChatRoleEnum.System,
[ChatCompletionRequestMessageRoleEnum.Developer]: ChatRoleEnum.System,
[ChatCompletionRequestMessageRoleEnum.User]: ChatRoleEnum.Human,
[ChatCompletionRequestMessageRoleEnum.Assistant]: ChatRoleEnum.AI,
[ChatCompletionRequestMessageRoleEnum.Function]: ChatRoleEnum.AI,
[ChatCompletionRequestMessageRoleEnum.Tool]: ChatRoleEnum.AI
};
export function adaptRole_Message2Chat(role: `${ChatCompletionRequestMessageRoleEnum}`) {
return GPT2Chat[role];
}
export const simpleUserContentPart = (content: ChatCompletionContentPart[]) => {
if (content.length === 1 && content[0].type === 'text') {
return content[0].text;
}
return content;
};
export const chats2GPTMessages = ({
messages,
reserveId,
reserveTool = false
}: {
messages: ChatItemType[];
reserveId: boolean;
reserveTool?: boolean;
}): ChatCompletionMessageParam[] => {
let results: ChatCompletionMessageParam[] = [];
messages.forEach((item) => {
const dataId = reserveId ? item.dataId : undefined;
if (item.obj === ChatRoleEnum.System) {
const content = item.value?.[0]?.text?.content;
if (content) {
results.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.System,
content
});
}
} else if (item.obj === ChatRoleEnum.Human) {
const value = item.value
.map((item) => {
if (item.text) {
return {
type: 'text',
text: item.text?.content || ''
};
}
if (item.file) {
if (item.file?.type === ChatFileTypeEnum.image) {
return {
type: 'image_url',
key: item.file.key,
image_url: {
url: item.file.url
}
};
} else if (item.file?.type === ChatFileTypeEnum.file) {
return {
type: 'file_url',
name: item.file?.name || '',
url: item.file.url,
key: item.file.key
};
}
}
})
.filter(Boolean) as ChatCompletionContentPart[];
results.push({
dataId,
hideInUI: item.hideInUI,
role: ChatCompletionRequestMessageRoleEnum.User,
content: simpleUserContentPart(value)
});
} else {
const aiResults: ChatCompletionMessageParam[] = [];
const existsPlanId = new Set<string>();
item.value.forEach((value, i) => {
// 只需要把根节点转化即可
if (value.stepId) return;
if ((value.tools || value.tool) && reserveTool) {
const tools = value.tools || [value.tool!];
const tool_calls: ChatCompletionMessageToolCall[] = [];
const toolResponse: ChatCompletionToolMessageParam[] = [];
tools.forEach((tool) => {
tool_calls.push({
id: tool.id,
type: 'function',
function: {
name: tool.functionName,
arguments: tool.params
}
});
toolResponse.push({
tool_call_id: tool.id,
role: ChatCompletionRequestMessageRoleEnum.Tool,
content: tool.response || ''
});
});
aiResults.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
tool_calls
});
aiResults.push(...toolResponse);
} else if (typeof value.text?.content === 'string') {
if (!value.text.content && item.value.length > 1) {
return;
}
// Concat text
const lastResult = aiResults[aiResults.length - 1];
if (
lastResult?.role === ChatCompletionRequestMessageRoleEnum.Assistant &&
typeof lastResult?.content === 'string'
) {
lastResult.content += value.text.content;
} else {
aiResults.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: value.text.content
});
}
} else if (value.plan && reserveTool) {
const planId = value.plan.planId;
if (existsPlanId.has(planId)) {
return;
}
existsPlanId.add(planId);
const steps = item.value
.filter((item) => item.plan?.planId === planId)
.flatMap((item) => item.plan?.steps || [])
.map((step) => {
const stepResponse = item.value
.filter((item) => item.stepId === step.id)
?.map((item) => item.text?.content)
.join('\n');
return {
title: step.title,
response: stepResponse
};
});
const toolId = getNanoid(6);
aiResults.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
tool_calls: [
{
id: toolId,
type: 'function',
function: {
name: 'plan_agent',
arguments: JSON.stringify({
task: value.plan.task,
description: value.plan.description,
background: value.plan.background
})
}
}
]
});
aiResults.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.Tool,
tool_call_id: toolId,
content: JSON.stringify(steps)
});
} else if (value.interactive) {
if (value.interactive.type === 'agentPlanAskQuery') {
aiResults.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: value.interactive.params.content
});
} else if (value.interactive.type === 'agentPlanAskUserForm') {
aiResults.push({
dataId,
role: ChatCompletionRequestMessageRoleEnum.Assistant,
content: `${value.interactive.params.description}
Answer: ${value.interactive.params.inputForm.map((item) => `- ${item.label}: ${item.value}`).join('\n')}`
});
}
}
});
// Auto add empty assistant message
results = results.concat(aiResults);
}
});
return results;
};
export const GPTMessages2Chats = ({
messages,
reserveTool = true,
reserveReason = true,
getToolInfo
}: {
messages: ChatCompletionMessageParam[];
reserveTool?: boolean;
reserveReason?: boolean;
getToolInfo?: (name: string) => { name: string; avatar: string };
}): ChatItemType[] => {
const chatMessages = messages
.map((item) => {
const obj = GPT2Chat[item.role];
if (
obj === ChatRoleEnum.System &&
item.role === ChatCompletionRequestMessageRoleEnum.System
) {
const value: SystemChatItemValueItemType[] = [];
if (Array.isArray(item.content)) {
item.content.forEach((item) => [
value.push({
text: {
content: item.text
}
})
]);
} else {
value.push({
text: {
content: item.content
}
});
}
return {
dataId: item.dataId,
obj,
hideInUI: item.hideInUI,
value
};
} else if (
obj === ChatRoleEnum.Human &&
item.role === ChatCompletionRequestMessageRoleEnum.User
) {
const value: UserChatItemValueItemType[] = [];
if (typeof item.content === 'string') {
value.push({
text: {
content: item.content
}
});
} else if (Array.isArray(item.content)) {
item.content.forEach((item) => {
if (item.type === 'text') {
value.push({
text: {
content: item.text
}
});
} else if (item.type === 'image_url') {
value.push({
file: {
type: ChatFileTypeEnum.image,
name: '',
url: item.image_url.url,
key: item.key
}
});
} else if (item.type === 'file_url') {
value.push({
file: {
type: ChatFileTypeEnum.file,
name: item.name,
url: item.url,
key: item.key
}
});
}
});
}
return {
dataId: item.dataId,
obj,
hideInUI: item.hideInUI,
value
};
} else if (
obj === ChatRoleEnum.AI &&
item.role === ChatCompletionRequestMessageRoleEnum.Assistant
) {
const value: AIChatItemValueItemType[] = [];
if (typeof item.reasoning_content === 'string' && item.reasoning_content && reserveReason) {
value.push({
reasoning: {
content: item.reasoning_content
}
});
}
if (typeof item.content === 'string' && item.content) {
const lastValue = value[value.length - 1];
if (lastValue && lastValue.text) {
lastValue.text.content += item.content;
} else {
value.push({
text: {
content: item.content
}
});
}
}
if (item.tool_calls && reserveTool) {
// save tool calls
const toolCalls = item.tool_calls as ChatCompletionMessageToolCall[];
const tools = toolCalls.flatMap<ToolModuleResponseItemType>((tool) => {
// Skil plan tool
if (tool.function.name === 'plan_agent') {
return [];
}
let toolResponse =
messages.find(
(msg) =>
msg.role === ChatCompletionRequestMessageRoleEnum.Tool &&
msg.tool_call_id === tool.id
)?.content || '';
toolResponse =
typeof toolResponse === 'string' ? toolResponse : JSON.stringify(toolResponse);
const toolInfo = getToolInfo?.(tool.function.name);
return [
{
id: tool.id,
toolName: toolInfo?.name || '',
toolAvatar: toolInfo?.avatar || '',
functionName: tool.function.name,
params: tool.function.arguments,
response: toolResponse as string
}
];
});
value.push({
tools
});
}
if (item.function_call && reserveTool) {
const functionCall = item.function_call as ChatCompletionMessageFunctionCall;
const functionResponse = messages.find(
(msg) =>
msg.role === ChatCompletionRequestMessageRoleEnum.Function &&
msg.name === item.function_call?.name
) as ChatCompletionFunctionMessageParam;
if (functionResponse) {
value.push({
tool: {
id: functionCall.id || '',
toolName: functionCall.toolName || '',
toolAvatar: functionCall.toolAvatar || '',
functionName: functionCall.name,
params: functionCall.arguments,
response: functionResponse.content || ''
}
});
}
}
if (item.interactive) {
value.push({
interactive: item.interactive
});
}
return {
dataId: item.dataId,
obj,
hideInUI: item.hideInUI,
value
};
}
return {
dataId: item.dataId,
obj,
hideInUI: item.hideInUI,
value: []
};
})
.filter((item) => item.value.length > 0);
// Merge data with the same dataIdSequential obj merging
const result = chatMessages.reduce((result: ChatItemType[], currentItem) => {
const lastItem = result[result.length - 1];
if (lastItem && lastItem.dataId === currentItem.dataId && lastItem.obj === currentItem.obj) {
// @ts-ignore
lastItem.value = lastItem.value.concat(currentItem.value);
} else {
result.push(currentItem);
}
return result;
}, []);
return result;
};
export const chatValue2RuntimePrompt = (value: ChatItemValueItemType[]): RuntimeUserPromptType => {
const prompt: RuntimeUserPromptType = {
files: [],
text: ''
};
value.forEach((item) => {
if ('file' in item && item.file) {
prompt.files.push(item.file);
} else if (item.text) {
prompt.text += item.text.content;
}
});
return prompt;
};
export const runtimePrompt2ChatsValue = (prompt: {
files?: UserChatItemFileItemType[];
text?: string;
}): UserChatItemType['value'] => {
const value: UserChatItemType['value'] = [];
if (prompt.files) {
prompt.files.forEach((file) => {
value.push({
file
});
});
}
if (prompt.text) {
value.push({
text: {
content: prompt.text
}
});
}
return value;
};
export const getSystemPrompt_ChatItemType = (prompt?: string): ChatItemType[] => {
if (!prompt) return [];
return [
{
obj: ChatRoleEnum.System,
value: [{ text: { content: prompt } }]
}
];
};