mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-15 07:31:19 +00:00
perf: variabel replace;Feat: prompt optimizer code (#5453)
* feat: add prompt optimizer (#5444) * feat: add prompt optimizer * fix * perf: variabel replace * perf: prompt optimizer code * feat: init charts shell * perf: user error remove --------- Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
@@ -34,15 +34,75 @@ export const valToStr = (val: any) => {
|
||||
};
|
||||
|
||||
// replace {{variable}} to value
|
||||
export function replaceVariable(text: any, obj: Record<string, string | number | undefined>) {
|
||||
export function replaceVariable(
|
||||
text: any,
|
||||
obj: Record<string, string | number | undefined>,
|
||||
depth = 0
|
||||
) {
|
||||
if (typeof text !== 'string') return text;
|
||||
|
||||
for (const key in obj) {
|
||||
const val = obj[key];
|
||||
const formatVal = valToStr(val);
|
||||
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), () => formatVal);
|
||||
const MAX_REPLACEMENT_DEPTH = 10;
|
||||
const processedVariables = new Set<string>();
|
||||
|
||||
// Prevent infinite recursion
|
||||
if (depth > MAX_REPLACEMENT_DEPTH) {
|
||||
return text;
|
||||
}
|
||||
return text || '';
|
||||
|
||||
// Check for circular references in variable values
|
||||
const hasCircularReference = (value: any, targetKey: string): boolean => {
|
||||
if (typeof value !== 'string') return false;
|
||||
|
||||
// Check if the value contains the target variable pattern (direct self-reference)
|
||||
const selfRefPattern = new RegExp(
|
||||
`\\{\\{${targetKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\}\\}`,
|
||||
'g'
|
||||
);
|
||||
return selfRefPattern.test(value);
|
||||
};
|
||||
|
||||
let result = text;
|
||||
let hasReplacements = false;
|
||||
|
||||
// Build replacement map first to avoid modifying string during iteration
|
||||
const replacements: { pattern: string; replacement: string }[] = [];
|
||||
|
||||
for (const key in obj) {
|
||||
// Skip if already processed to avoid immediate circular reference
|
||||
if (processedVariables.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const val = obj[key];
|
||||
|
||||
// Check for direct circular reference
|
||||
if (hasCircularReference(String(val), key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const formatVal = valToStr(val);
|
||||
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
replacements.push({
|
||||
pattern: `{{(${escapedKey})}}`,
|
||||
replacement: formatVal
|
||||
});
|
||||
|
||||
processedVariables.add(key);
|
||||
hasReplacements = true;
|
||||
}
|
||||
|
||||
// Apply all replacements
|
||||
replacements.forEach(({ pattern, replacement }) => {
|
||||
result = result.replace(new RegExp(pattern, 'g'), () => replacement);
|
||||
});
|
||||
|
||||
// If we made replacements and there might be nested variables, recursively process
|
||||
if (hasReplacements && /\{\{[^}]+\}\}/.test(result)) {
|
||||
result = replaceVariable(result, obj, depth + 1);
|
||||
}
|
||||
|
||||
return result || '';
|
||||
}
|
||||
|
||||
/* replace sensitive text */
|
||||
|
6
packages/global/core/chat/type.d.ts
vendored
6
packages/global/core/chat/type.d.ts
vendored
@@ -20,7 +20,7 @@ import type { WorkflowInteractiveResponseType } from '../workflow/template/syste
|
||||
import type { FlowNodeInputItemType } from '../workflow/type/io';
|
||||
import type { FlowNodeTemplateType } from '../workflow/type/node.d';
|
||||
|
||||
export type ChatSchema = {
|
||||
export type ChatSchemaType = {
|
||||
_id: string;
|
||||
chatId: string;
|
||||
userId: string;
|
||||
@@ -33,6 +33,8 @@ export type ChatSchema = {
|
||||
customTitle: string;
|
||||
top: boolean;
|
||||
source: `${ChatSourceEnum}`;
|
||||
sourceName?: string;
|
||||
|
||||
shareId?: string;
|
||||
outLinkUid?: string;
|
||||
|
||||
@@ -43,7 +45,7 @@ export type ChatSchema = {
|
||||
metadata?: Record<string, any>;
|
||||
};
|
||||
|
||||
export type ChatWithAppSchema = Omit<ChatSchema, 'appId'> & {
|
||||
export type ChatWithAppSchema = Omit<ChatSchemaType, 'appId'> & {
|
||||
appId: AppSchema;
|
||||
};
|
||||
|
||||
|
@@ -467,27 +467,63 @@ export const formatVariableValByType = (val: any, valueType?: WorkflowIOValueTyp
|
||||
|
||||
return val;
|
||||
};
|
||||
|
||||
// replace {{$xx.xx$}} variables for text
|
||||
export function replaceEditorVariable({
|
||||
text,
|
||||
nodes,
|
||||
variables
|
||||
variables,
|
||||
depth = 0
|
||||
}: {
|
||||
text: any;
|
||||
nodes: RuntimeNodeItemType[];
|
||||
variables: Record<string, any>; // global variables
|
||||
depth?: number;
|
||||
}) {
|
||||
if (typeof text !== 'string') return text;
|
||||
if (text === '') return text;
|
||||
|
||||
const MAX_REPLACEMENT_DEPTH = 10;
|
||||
const processedVariables = new Set<string>();
|
||||
|
||||
// Prevent infinite recursion
|
||||
if (depth > MAX_REPLACEMENT_DEPTH) {
|
||||
return text;
|
||||
}
|
||||
|
||||
text = replaceVariable(text, variables);
|
||||
|
||||
// Check for circular references in variable values
|
||||
const hasCircularReference = (value: any, targetKey: string): boolean => {
|
||||
if (typeof value !== 'string') return false;
|
||||
|
||||
// Check if the value contains the target variable pattern (direct self-reference)
|
||||
const selfRefPattern = new RegExp(
|
||||
`\\{\\{\\$${targetKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\$\\}\\}`,
|
||||
'g'
|
||||
);
|
||||
return selfRefPattern.test(value);
|
||||
};
|
||||
|
||||
const variablePattern = /\{\{\$([^.]+)\.([^$]+)\$\}\}/g;
|
||||
const matches = [...text.matchAll(variablePattern)];
|
||||
if (matches.length === 0) return text;
|
||||
|
||||
matches.forEach((match) => {
|
||||
let result = text;
|
||||
let hasReplacements = false;
|
||||
|
||||
// Build replacement map first to avoid modifying string during iteration
|
||||
const replacements: Array<{ pattern: string; replacement: string }> = [];
|
||||
|
||||
for (const match of matches) {
|
||||
const nodeId = match[1];
|
||||
const id = match[2];
|
||||
const variableKey = `${nodeId}.${id}`;
|
||||
|
||||
// Skip if already processed to avoid immediate circular reference
|
||||
if (processedVariables.has(variableKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const variableVal = (() => {
|
||||
if (nodeId === VARIABLE_NODE_ID) {
|
||||
@@ -505,13 +541,35 @@ export function replaceEditorVariable({
|
||||
if (input) return getReferenceVariableValue({ value: input.value, nodes, variables });
|
||||
})();
|
||||
|
||||
const formatVal = valToStr(variableVal);
|
||||
// Check for direct circular reference
|
||||
if (hasCircularReference(String(variableVal), variableKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const regex = new RegExp(`\\{\\{\\$(${nodeId}\\.${id})\\$\\}\\}`, 'g');
|
||||
text = text.replace(regex, () => formatVal);
|
||||
const formatVal = valToStr(variableVal);
|
||||
const escapedNodeId = nodeId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const escapedId = id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
replacements.push({
|
||||
pattern: `\\{\\{\\$(${escapedNodeId}\\.${escapedId})\\$\\}\\}`,
|
||||
replacement: formatVal
|
||||
});
|
||||
|
||||
processedVariables.add(variableKey);
|
||||
hasReplacements = true;
|
||||
}
|
||||
|
||||
// Apply all replacements
|
||||
replacements.forEach(({ pattern, replacement }) => {
|
||||
result = result.replace(new RegExp(pattern, 'g'), replacement);
|
||||
});
|
||||
|
||||
return text || '';
|
||||
// If we made replacements and there might be nested variables, recursively process
|
||||
if (hasReplacements && /\{\{\$[^.]+\.[^$]+\$\}\}/.test(result)) {
|
||||
result = replaceEditorVariable({ text: result, nodes, variables, depth: depth + 1 });
|
||||
}
|
||||
|
||||
return result || '';
|
||||
}
|
||||
|
||||
export const textAdaptGptResponse = ({
|
||||
|
@@ -13,7 +13,8 @@ export enum UsageSourceEnum {
|
||||
official_account = 'official_account',
|
||||
pdfParse = 'pdfParse',
|
||||
mcp = 'mcp',
|
||||
evaluation = 'evaluation'
|
||||
evaluation = 'evaluation',
|
||||
optimize_prompt = 'optimize_prompt'
|
||||
}
|
||||
|
||||
export const UsageSourceMap = {
|
||||
@@ -55,5 +56,8 @@ export const UsageSourceMap = {
|
||||
},
|
||||
[UsageSourceEnum.evaluation]: {
|
||||
label: i18nT('account_usage:evaluation')
|
||||
},
|
||||
[UsageSourceEnum.optimize_prompt]: {
|
||||
label: i18nT('common:support.wallet.usage.Optimize Prompt')
|
||||
}
|
||||
};
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { connectionMongo, getMongoModel } from '../../common/mongo';
|
||||
const { Schema } = connectionMongo;
|
||||
import { type ChatSchema as ChatType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { type ChatSchemaType } from '@fastgpt/global/core/chat/type.d';
|
||||
import { ChatSourceEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import {
|
||||
TeamCollectionName,
|
||||
@@ -83,10 +83,13 @@ const ChatSchema = new Schema({
|
||||
//For special storage
|
||||
type: Object,
|
||||
default: {}
|
||||
}
|
||||
},
|
||||
|
||||
initStatistics: Boolean
|
||||
});
|
||||
|
||||
try {
|
||||
ChatSchema.index({ initCharts: 1 });
|
||||
ChatSchema.index({ chatId: 1 });
|
||||
// get user history
|
||||
ChatSchema.index({ tmbId: 1, appId: 1, top: -1, updateTime: -1 });
|
||||
@@ -104,4 +107,4 @@ try {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoChat = getMongoModel<ChatType>(chatCollectionName, ChatSchema);
|
||||
export const MongoChat = getMongoModel<ChatSchemaType>(chatCollectionName, ChatSchema);
|
||||
|
@@ -165,7 +165,7 @@ export async function saveChat({
|
||||
});
|
||||
|
||||
try {
|
||||
const userId = outLinkUid || tmbId;
|
||||
const userId = String(outLinkUid || tmbId);
|
||||
const now = new Date();
|
||||
const fifteenMinutesAgo = new Date(now.getTime() - 15 * 60 * 1000);
|
||||
|
||||
|
@@ -6,19 +6,18 @@ import {
|
||||
VARIABLE_NODE_ID,
|
||||
WorkflowIOValueTypeEnum
|
||||
} from '@fastgpt/global/core/workflow/constants';
|
||||
import {
|
||||
DispatchNodeResponseKeyEnum,
|
||||
SseResponseEventEnum
|
||||
} from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import axios from 'axios';
|
||||
import { valueTypeFormat } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { type DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import type {
|
||||
ModuleDispatchProps,
|
||||
RuntimeNodeItemType
|
||||
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import {
|
||||
formatVariableValByType,
|
||||
getReferenceVariableValue,
|
||||
replaceEditorVariable,
|
||||
textAdaptGptResponse
|
||||
replaceEditorVariable
|
||||
} from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import json5 from 'json5';
|
||||
import { JSONPath } from 'jsonpath-plus';
|
||||
@@ -121,96 +120,6 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
variables: allVariables
|
||||
});
|
||||
};
|
||||
/* Replace the JSON string to reduce parsing errors
|
||||
1. Replace undefined values with null
|
||||
2. Replace newline strings
|
||||
*/
|
||||
const replaceJsonBodyString = (text: string) => {
|
||||
// Check if the variable is in quotes
|
||||
const isVariableInQuotes = (text: string, variable: string) => {
|
||||
const index = text.indexOf(variable);
|
||||
if (index === -1) return false;
|
||||
|
||||
// 计算变量前面的引号数量
|
||||
const textBeforeVar = text.substring(0, index);
|
||||
const matches = textBeforeVar.match(/"/g) || [];
|
||||
|
||||
// 如果引号数量为奇数,则变量在引号内
|
||||
return matches.length % 2 === 1;
|
||||
};
|
||||
const valToStr = (val: any, isQuoted = false) => {
|
||||
if (val === undefined) return 'null';
|
||||
if (val === null) return 'null';
|
||||
|
||||
if (typeof val === 'object') return JSON.stringify(val);
|
||||
|
||||
if (typeof val === 'string') {
|
||||
if (isQuoted) {
|
||||
// Replace newlines with escaped newlines
|
||||
return val.replace(/\n/g, '\\n').replace(/(?<!\\)"/g, '\\"');
|
||||
}
|
||||
try {
|
||||
JSON.parse(val);
|
||||
return val;
|
||||
} catch (error) {
|
||||
const str = JSON.stringify(val);
|
||||
|
||||
return str.startsWith('"') && str.endsWith('"') ? str.slice(1, -1) : str;
|
||||
}
|
||||
}
|
||||
|
||||
return String(val);
|
||||
};
|
||||
|
||||
// 1. Replace {{key.key}} variables
|
||||
const regex1 = /\{\{\$([^.]+)\.([^$]+)\$\}\}/g;
|
||||
const matches1 = [...text.matchAll(regex1)];
|
||||
matches1.forEach((match) => {
|
||||
const nodeId = match[1];
|
||||
const id = match[2];
|
||||
const fullMatch = match[0];
|
||||
|
||||
// 检查变量是否在引号内
|
||||
const isInQuotes = isVariableInQuotes(text, fullMatch);
|
||||
|
||||
const variableVal = (() => {
|
||||
if (nodeId === VARIABLE_NODE_ID) {
|
||||
return variables[id];
|
||||
}
|
||||
// Find upstream node input/output
|
||||
const node = runtimeNodes.find((node) => node.nodeId === nodeId);
|
||||
if (!node) return;
|
||||
|
||||
const output = node.outputs.find((output) => output.id === id);
|
||||
if (output) return formatVariableValByType(output.value, output.valueType);
|
||||
|
||||
const input = node.inputs.find((input) => input.key === id);
|
||||
if (input)
|
||||
return getReferenceVariableValue({ value: input.value, nodes: runtimeNodes, variables });
|
||||
})();
|
||||
|
||||
const formatVal = valToStr(variableVal, isInQuotes);
|
||||
|
||||
const regex = new RegExp(`\\{\\{\\$(${nodeId}\\.${id})\\$\\}\\}`, '');
|
||||
text = text.replace(regex, () => formatVal);
|
||||
});
|
||||
|
||||
// 2. Replace {{key}} variables
|
||||
const regex2 = /{{([^}]+)}}/g;
|
||||
const matches2 = text.match(regex2) || [];
|
||||
const uniqueKeys2 = [...new Set(matches2.map((match) => match.slice(2, -2)))];
|
||||
for (const key of uniqueKeys2) {
|
||||
const fullMatch = `{{${key}}}`;
|
||||
// 检查变量是否在引号内
|
||||
const isInQuotes = isVariableInQuotes(text, fullMatch);
|
||||
|
||||
text = text.replace(new RegExp(`{{(${key})}}`, ''), () =>
|
||||
valToStr(allVariables[key], isInQuotes)
|
||||
);
|
||||
}
|
||||
|
||||
return text.replace(/(".*?")\s*:\s*undefined\b/g, '$1:null');
|
||||
};
|
||||
|
||||
httpReqUrl = replaceStringVariables(httpReqUrl);
|
||||
|
||||
@@ -273,7 +182,10 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
}
|
||||
if (!httpJsonBody) return {};
|
||||
if (httpContentType === ContentTypes.json) {
|
||||
httpJsonBody = replaceJsonBodyString(httpJsonBody);
|
||||
httpJsonBody = replaceJsonBodyString(
|
||||
{ text: httpJsonBody },
|
||||
{ variables, allVariables, runtimeNodes }
|
||||
);
|
||||
return json5.parse(httpJsonBody);
|
||||
}
|
||||
|
||||
@@ -360,7 +272,7 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
Object.keys(results).length > 0 ? results : rawResponse
|
||||
};
|
||||
} catch (error) {
|
||||
addLog.error('Http request error', error);
|
||||
addLog.warn('Http request error', formatHttpError(error));
|
||||
|
||||
// @adapt
|
||||
if (node.catchError === undefined) {
|
||||
@@ -391,6 +303,187 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
|
||||
}
|
||||
};
|
||||
|
||||
/* Replace the JSON string to reduce parsing errors
|
||||
1. Replace undefined values with null
|
||||
2. Replace newline strings
|
||||
*/
|
||||
export const replaceJsonBodyString = (
|
||||
{ text, depth = 0 }: { text: string; depth?: number },
|
||||
props: {
|
||||
variables: Record<string, any>;
|
||||
allVariables: Record<string, any>;
|
||||
runtimeNodes: RuntimeNodeItemType[];
|
||||
}
|
||||
) => {
|
||||
const { variables, allVariables, runtimeNodes } = props;
|
||||
|
||||
const MAX_REPLACEMENT_DEPTH = 10;
|
||||
const processedVariables = new Set<string>();
|
||||
|
||||
// Prevent infinite recursion
|
||||
if (depth > MAX_REPLACEMENT_DEPTH) {
|
||||
return text;
|
||||
}
|
||||
|
||||
// Check if the variable is in quotes
|
||||
const isVariableInQuotes = (text: string, variable: string) => {
|
||||
const index = text.indexOf(variable);
|
||||
if (index === -1) return false;
|
||||
|
||||
// 计算变量前面的引号数量
|
||||
const textBeforeVar = text.substring(0, index);
|
||||
const matches = textBeforeVar.match(/"/g) || [];
|
||||
|
||||
// 如果引号数量为奇数,则变量在引号内
|
||||
return matches.length % 2 === 1;
|
||||
};
|
||||
|
||||
const valToStr = (val: any, isQuoted = false) => {
|
||||
if (val === undefined) return 'null';
|
||||
if (val === null) return 'null';
|
||||
|
||||
if (typeof val === 'object') {
|
||||
const jsonStr = JSON.stringify(val);
|
||||
if (isQuoted) {
|
||||
// Only escape quotes for JSON strings inside quotes (backslashes are already properly escaped by JSON.stringify)
|
||||
return jsonStr.replace(/"/g, '\\"');
|
||||
}
|
||||
return jsonStr;
|
||||
}
|
||||
|
||||
if (typeof val === 'string') {
|
||||
if (isQuoted) {
|
||||
const jsonStr = JSON.stringify(val);
|
||||
return jsonStr.slice(1, -1); // 移除首尾的双引号
|
||||
}
|
||||
try {
|
||||
JSON.parse(val);
|
||||
return val;
|
||||
} catch (error) {
|
||||
const str = JSON.stringify(val);
|
||||
return str.startsWith('"') && str.endsWith('"') ? str.slice(1, -1) : str;
|
||||
}
|
||||
}
|
||||
|
||||
return String(val);
|
||||
};
|
||||
|
||||
// Check for circular references in variable values
|
||||
const hasCircularReference = (value: any, targetKey: string): boolean => {
|
||||
if (typeof value !== 'string') return false;
|
||||
|
||||
// Check if the value contains the target variable pattern (direct self-reference)
|
||||
const selfRefPattern = new RegExp(
|
||||
`\\{\\{${targetKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\}\\}`,
|
||||
'g'
|
||||
);
|
||||
return selfRefPattern.test(value);
|
||||
};
|
||||
|
||||
let result = text;
|
||||
let hasReplacements = false;
|
||||
|
||||
// 1. Replace {{$nodeId.id$}} variables
|
||||
const regex1 = /\{\{\$([^.]+)\.([^$]+)\$\}\}/g;
|
||||
const matches1 = [...result.matchAll(regex1)];
|
||||
|
||||
// Build replacement map first to avoid modifying string during iteration
|
||||
const replacements1: Array<{ pattern: string; replacement: string }> = [];
|
||||
|
||||
for (const match of matches1) {
|
||||
const nodeId = match[1];
|
||||
const id = match[2];
|
||||
const fullMatch = match[0];
|
||||
const variableKey = `${nodeId}.${id}`;
|
||||
|
||||
// Skip if already processed to avoid immediate circular reference
|
||||
if (processedVariables.has(variableKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查变量是否在引号内
|
||||
const isInQuotes = isVariableInQuotes(result, fullMatch);
|
||||
|
||||
const variableVal = (() => {
|
||||
if (nodeId === VARIABLE_NODE_ID) {
|
||||
return variables[id];
|
||||
}
|
||||
// Find upstream node input/output
|
||||
const node = runtimeNodes.find((node) => node.nodeId === nodeId);
|
||||
if (!node) return;
|
||||
|
||||
const output = node.outputs.find((output) => output.id === id);
|
||||
if (output) return formatVariableValByType(output.value, output.valueType);
|
||||
|
||||
const input = node.inputs.find((input) => input.key === id);
|
||||
if (input)
|
||||
return getReferenceVariableValue({ value: input.value, nodes: runtimeNodes, variables });
|
||||
})();
|
||||
|
||||
const formatVal = valToStr(variableVal, isInQuotes);
|
||||
// Check for direct circular reference
|
||||
if (hasCircularReference(String(variableVal), variableKey)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const escapedPattern = `\\{\\{\\$(${nodeId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\.${id.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})\\$\\}\\}`;
|
||||
|
||||
replacements1.push({
|
||||
pattern: escapedPattern,
|
||||
replacement: formatVal
|
||||
});
|
||||
|
||||
processedVariables.add(variableKey);
|
||||
hasReplacements = true;
|
||||
}
|
||||
replacements1.forEach(({ pattern, replacement }) => {
|
||||
result = result.replace(new RegExp(pattern, 'g'), replacement);
|
||||
});
|
||||
|
||||
// 2. Replace {{key}} variables
|
||||
const regex2 = /{{([^}]+)}}/g;
|
||||
const matches2 = result.match(regex2) || [];
|
||||
const uniqueKeys2 = [...new Set(matches2.map((match) => match.slice(2, -2)))];
|
||||
// Build replacement map for simple variables
|
||||
const replacements2: Array<{ pattern: string; replacement: string }> = [];
|
||||
for (const key of uniqueKeys2) {
|
||||
if (processedVariables.has(key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fullMatch = `{{${key}}}`;
|
||||
const variableVal = allVariables[key];
|
||||
|
||||
// Check for direct circular reference
|
||||
if (hasCircularReference(variableVal, key)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查变量是否在引号内
|
||||
const isInQuotes = isVariableInQuotes(result, fullMatch);
|
||||
const formatVal = valToStr(variableVal, isInQuotes);
|
||||
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
|
||||
replacements2.push({
|
||||
pattern: `{{(${escapedKey})}}`,
|
||||
replacement: formatVal
|
||||
});
|
||||
|
||||
processedVariables.add(key);
|
||||
hasReplacements = true;
|
||||
}
|
||||
replacements2.forEach(({ pattern, replacement }) => {
|
||||
result = result.replace(new RegExp(pattern, 'g'), replacement);
|
||||
});
|
||||
|
||||
// If we made replacements and there might be nested variables, recursively process
|
||||
if (hasReplacements && /\{\{[^}]*\}\}/.test(result)) {
|
||||
result = replaceJsonBodyString({ text: result, depth: depth + 1 }, props);
|
||||
}
|
||||
|
||||
return result.replace(/(".*?")\s*:\s*undefined\b/g, '$1:null');
|
||||
};
|
||||
|
||||
async function fetchData({
|
||||
method,
|
||||
url,
|
||||
|
@@ -96,7 +96,7 @@ export const dispatchLafRequest = async (props: LafRequestProps): Promise<LafRes
|
||||
[DispatchNodeResponseKeyEnum.toolResponses]: rawResponse
|
||||
};
|
||||
} catch (error) {
|
||||
addLog.error('Http request error', error);
|
||||
addLog.warn('Http request error', formatHttpError(error));
|
||||
return {
|
||||
error: {
|
||||
[NodeOutputKeyEnum.errorText]: getErrText(error)
|
||||
|
@@ -450,6 +450,7 @@ export const iconPaths = {
|
||||
'model/yi': () => import('./icons/model/yi.svg'),
|
||||
more: () => import('./icons/more.svg'),
|
||||
moreLine: () => import('./icons/moreLine.svg'),
|
||||
optimizer: () => import('./icons/optimizer.svg'),
|
||||
out: () => import('./icons/out.svg'),
|
||||
paragraph: () => import('./icons/paragraph.svg'),
|
||||
'phoneTabbar/me': () => import('./icons/phoneTabbar/me.svg'),
|
||||
|
46
packages/web/components/common/Icon/icons/optimizer.svg
Normal file
46
packages/web/components/common/Icon/icons/optimizer.svg
Normal file
@@ -0,0 +1,46 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18" fill="none">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.02489 5.13636C6.02489 4.7096 6.37085 4.36364 6.79762 4.36364H11.434C11.8607 4.36364 12.2067 4.7096 12.2067 5.13636C12.2067 5.56313 11.8607 5.90909 11.434 5.90909H9.88853V12.0909H11.434C11.8607 12.0909 12.2067 12.4369 12.2067 12.8636C12.2067 13.2904 11.8607 13.6364 11.434 13.6364H6.79762C6.37085 13.6364 6.02489 13.2904 6.02489 12.8636C6.02489 12.4369 6.37085 12.0909 6.79762 12.0909H8.34307V5.90909H6.79762C6.37085 5.90909 6.02489 5.56313 6.02489 5.13636Z" fill="url(#paint0_linear_25014_10095)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.33398 12.669C2.01342 12.8153 1.63496 12.674 1.48865 12.3535C0.540947 10.2771 0.497952 7.89092 1.35999 5.78813C1.49365 5.4621 1.86631 5.30615 2.19234 5.43981C2.51837 5.57346 2.67433 5.94612 2.54067 6.27215C1.81207 8.04942 1.84846 10.0686 2.64949 11.8237C2.7958 12.1442 2.65454 12.5227 2.33398 12.669Z" fill="url(#paint1_linear_25014_10095)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.6702 12.6774C15.3495 12.5313 15.208 12.153 15.354 11.8323C16.1583 10.0664 16.1874 8.03144 15.4407 6.24392C15.3049 5.91878 15.4584 5.5451 15.7835 5.40929C16.1087 5.27347 16.4824 5.42695 16.6182 5.75209C17.5012 7.86613 17.4673 10.271 16.5153 12.3612C16.3692 12.6819 15.9909 12.8235 15.6702 12.6774Z" fill="url(#paint2_linear_25014_10095)"/>
|
||||
<path d="M3.77789 0.747892C3.85803 0.41737 4.32811 0.417369 4.40826 0.747892L4.62879 1.65735C4.6574 1.77535 4.74954 1.86749 4.86754 1.8961L5.777 2.11663C6.10752 2.19678 6.10752 2.66686 5.777 2.747L4.86754 2.96753C4.85279 2.97111 4.83845 2.97568 4.82458 2.98117C4.7275 3.01959 4.65382 3.10304 4.62879 3.20629L4.40826 4.11574C4.40325 4.1364 4.39672 4.15577 4.38885 4.17384C4.27087 4.44498 3.85302 4.42561 3.77789 4.11575L3.55736 3.20629C3.55557 3.19892 3.55353 3.19164 3.55126 3.18448C3.51712 3.07702 3.42923 2.99436 3.3186 2.96753L2.40914 2.747C2.38849 2.74199 2.36912 2.73546 2.35105 2.7276C2.07991 2.60962 2.09928 2.19177 2.40914 2.11663L3.3186 1.8961C3.4366 1.86749 3.52874 1.77535 3.55736 1.65735L3.77789 0.747892Z" fill="url(#paint3_linear_25014_10095)"/>
|
||||
<path d="M13.8233 13.8843C13.9035 13.5537 14.3736 13.5537 14.4537 13.8843L14.6742 14.7937C14.7029 14.9117 14.795 15.0039 14.913 15.0325L15.8225 15.253C16.153 15.3331 16.153 15.8032 15.8225 15.8834L14.913 16.1039C14.8982 16.1075 14.8839 16.112 14.87 16.1175C14.773 16.1559 14.6993 16.2394 14.6742 16.3427L14.4537 17.2521C14.4487 17.2728 14.4422 17.2921 14.4343 17.3102C14.3163 17.5813 13.8985 17.562 13.8233 17.2521L13.6028 16.3427C13.601 16.3353 13.599 16.328 13.5967 16.3208C13.5626 16.2134 13.4747 16.1307 13.3641 16.1039L12.4546 15.8834C12.4339 15.8784 12.4146 15.8718 12.3965 15.864C12.1254 15.746 12.1447 15.3281 12.4546 15.253L13.3641 15.0325C13.4821 15.0039 13.5742 14.9117 13.6028 14.7937L13.8233 13.8843Z" fill="url(#paint4_linear_25014_10095)"/>
|
||||
<path d="M14.5249 2.04545C14.5249 2.47222 14.1789 2.81818 13.7522 2.81818C13.3254 2.81818 12.9794 2.47222 12.9794 2.04545C12.9794 1.61869 13.3254 1.27273 13.7522 1.27273C14.1789 1.27273 14.5249 1.61869 14.5249 2.04545Z" fill="url(#paint5_linear_25014_10095)"/>
|
||||
<path d="M5.25216 15.9545C5.25216 16.3813 4.9062 16.7273 4.47943 16.7273C4.05267 16.7273 3.70671 16.3813 3.70671 15.9545C3.70671 15.5278 4.05267 15.1818 4.47943 15.1818C4.9062 15.1818 5.25216 15.5278 5.25216 15.9545Z" fill="url(#paint6_linear_25014_10095)"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_25014_10095" x1="8.99999" y1="1.05195" x2="8.99999" y2="16.9481" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#499DFF"/>
|
||||
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||
<stop offset="1" stop-color="#6E80FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint1_linear_25014_10095" x1="8.99999" y1="1.05195" x2="8.99999" y2="16.9481" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#499DFF"/>
|
||||
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||
<stop offset="1" stop-color="#6E80FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint2_linear_25014_10095" x1="8.99999" y1="1.05195" x2="8.99999" y2="16.9481" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#499DFF"/>
|
||||
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||
<stop offset="1" stop-color="#6E80FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint3_linear_25014_10095" x1="8.99999" y1="1.05195" x2="8.99999" y2="16.9481" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#499DFF"/>
|
||||
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||
<stop offset="1" stop-color="#6E80FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint4_linear_25014_10095" x1="8.99999" y1="1.05195" x2="8.99999" y2="16.9481" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#499DFF"/>
|
||||
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||
<stop offset="1" stop-color="#6E80FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint5_linear_25014_10095" x1="8.99999" y1="1.05195" x2="8.99999" y2="16.9481" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#499DFF"/>
|
||||
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||
<stop offset="1" stop-color="#6E80FF"/>
|
||||
</linearGradient>
|
||||
<linearGradient id="paint6_linear_25014_10095" x1="8.99999" y1="1.05195" x2="8.99999" y2="16.9481" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#499DFF"/>
|
||||
<stop offset="0.432292" stop-color="#2770FF"/>
|
||||
<stop offset="1" stop-color="#6E80FF"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 5.4 KiB |
@@ -57,7 +57,7 @@ const MyModal = ({
|
||||
closeOnOverlayClick={closeOnOverlayClick}
|
||||
returnFocusOnClose={false}
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalOverlay zIndex={props.zIndex} />
|
||||
<ModalContent
|
||||
w={w}
|
||||
minW={['90vw', '400px']}
|
||||
@@ -65,6 +65,9 @@ const MyModal = ({
|
||||
position={'relative'}
|
||||
maxH={'85vh'}
|
||||
boxShadow={'7'}
|
||||
containerProps={{
|
||||
zIndex: props.zIndex
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
{!title && onClose && showCloseButton && <ModalCloseButton zIndex={1} />}
|
||||
|
@@ -6,7 +6,9 @@ import {
|
||||
useDisclosure,
|
||||
type PlacementWithLogical,
|
||||
PopoverArrow,
|
||||
type PopoverContentProps
|
||||
type PopoverContentProps,
|
||||
Box,
|
||||
Portal
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
interface Props extends PopoverContentProps {
|
||||
@@ -15,6 +17,7 @@ interface Props extends PopoverContentProps {
|
||||
offset?: [number, number];
|
||||
trigger?: 'hover' | 'click';
|
||||
hasArrow?: boolean;
|
||||
onBackdropClick?: () => void;
|
||||
children: (e: { onClose: () => void }) => React.ReactNode;
|
||||
onCloseFunc?: () => void;
|
||||
onOpenFunc?: () => void;
|
||||
@@ -31,6 +34,7 @@ const MyPopover = ({
|
||||
onOpenFunc,
|
||||
onCloseFunc,
|
||||
closeOnBlur = false,
|
||||
onBackdropClick,
|
||||
...props
|
||||
}: Props) => {
|
||||
const firstFieldRef = React.useRef(null);
|
||||
@@ -60,10 +64,17 @@ const MyPopover = ({
|
||||
autoFocus={false}
|
||||
>
|
||||
<PopoverTrigger>{Trigger}</PopoverTrigger>
|
||||
<PopoverContent {...props}>
|
||||
{hasArrow && <PopoverArrow />}
|
||||
{children({ onClose })}
|
||||
</PopoverContent>
|
||||
{isOpen && onBackdropClick && (
|
||||
<Portal>
|
||||
<Box position="fixed" zIndex={1000} inset={0} onClick={() => onBackdropClick()} />
|
||||
</Portal>
|
||||
)}
|
||||
<Portal>
|
||||
<PopoverContent zIndex={1001} {...props}>
|
||||
{hasArrow && <PopoverArrow />}
|
||||
{children({ onClose })}
|
||||
</PopoverContent>
|
||||
</Portal>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
|
@@ -6,7 +6,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import { useState, useTransition } from 'react';
|
||||
import { useMemo, useState, useTransition } from 'react';
|
||||
import { LexicalComposer } from '@lexical/react/LexicalComposer';
|
||||
import { PlainTextPlugin } from '@lexical/react/LexicalPlainTextPlugin';
|
||||
import { ContentEditable } from '@lexical/react/LexicalContentEditable';
|
||||
@@ -14,7 +14,7 @@ import { HistoryPlugin } from '@lexical/react/LexicalHistoryPlugin';
|
||||
import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
|
||||
import LexicalErrorBoundary from '@lexical/react/LexicalErrorBoundary';
|
||||
import VariableLabelPickerPlugin from './plugins/VariableLabelPickerPlugin';
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { Box, Flex } from '@chakra-ui/react';
|
||||
import styles from './index.module.scss';
|
||||
import VariablePlugin from './plugins/VariablePlugin';
|
||||
import { VariableNode } from './plugins/VariablePlugin/node';
|
||||
@@ -32,36 +32,47 @@ import VariableLabelPlugin from './plugins/VariableLabelPlugin';
|
||||
import { useDeepCompareEffect } from 'ahooks';
|
||||
import VariablePickerPlugin from './plugins/VariablePickerPlugin';
|
||||
|
||||
export type EditorProps = {
|
||||
variables?: EditorVariablePickerType[];
|
||||
variableLabels?: EditorVariableLabelPickerType[];
|
||||
value?: string;
|
||||
showOpenModal?: boolean;
|
||||
minH?: number;
|
||||
maxH?: number;
|
||||
maxLength?: number;
|
||||
placeholder?: string;
|
||||
isInvalid?: boolean;
|
||||
|
||||
ExtensionPopover?: ((e: {
|
||||
onChangeText: (text: string) => void;
|
||||
iconButtonStyle: Record<string, any>;
|
||||
}) => React.ReactNode)[];
|
||||
};
|
||||
|
||||
export default function Editor({
|
||||
minH = 200,
|
||||
maxH = 400,
|
||||
maxLength,
|
||||
showOpenModal = true,
|
||||
onOpenModal,
|
||||
variables,
|
||||
variableLabels,
|
||||
variables = [],
|
||||
variableLabels = [],
|
||||
onChange,
|
||||
onChangeText,
|
||||
onBlur,
|
||||
value,
|
||||
placeholder = '',
|
||||
bg = 'white',
|
||||
isInvalid,
|
||||
|
||||
isInvalid
|
||||
}: {
|
||||
minH?: number;
|
||||
maxH?: number;
|
||||
maxLength?: number;
|
||||
showOpenModal?: boolean;
|
||||
onOpenModal?: () => void;
|
||||
variables: EditorVariablePickerType[];
|
||||
variableLabels: EditorVariableLabelPickerType[];
|
||||
onChange?: (editorState: EditorState, editor: LexicalEditor) => void;
|
||||
onBlur?: (editor: LexicalEditor) => void;
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
|
||||
isInvalid?: boolean;
|
||||
} & FormPropsType) {
|
||||
ExtensionPopover
|
||||
}: EditorProps &
|
||||
FormPropsType & {
|
||||
onOpenModal?: () => void;
|
||||
onChange: (editorState: EditorState, editor: LexicalEditor) => void;
|
||||
onChangeText?: ((text: string) => void) | undefined;
|
||||
onBlur: (editor: LexicalEditor) => void;
|
||||
}) {
|
||||
const [key, setKey] = useState(getNanoid(6));
|
||||
const [_, startSts] = useTransition();
|
||||
const [focus, setFocus] = useState(false);
|
||||
@@ -81,6 +92,28 @@ export default function Editor({
|
||||
setKey(getNanoid(6));
|
||||
}, [value, variables, variableLabels]);
|
||||
|
||||
const showFullScreenIcon = useMemo(() => {
|
||||
return showOpenModal && scrollHeight > maxH;
|
||||
}, [showOpenModal, scrollHeight, maxH]);
|
||||
|
||||
const iconButtonStyle = useMemo(
|
||||
() => ({
|
||||
position: 'absolute' as const,
|
||||
bottom: 1,
|
||||
right: showFullScreenIcon ? '34px' : 2,
|
||||
zIndex: 10,
|
||||
cursor: 'pointer',
|
||||
borderRadius: '6px',
|
||||
background: 'rgba(255, 255, 255, 0.01)',
|
||||
backdropFilter: 'blur(6.6666669845581055px)',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
w: 6,
|
||||
h: 6
|
||||
}),
|
||||
[showFullScreenIcon]
|
||||
);
|
||||
|
||||
return (
|
||||
<Box
|
||||
className="nowheel"
|
||||
@@ -146,17 +179,15 @@ export default function Editor({
|
||||
<VariablePickerPlugin variables={variableLabels.length > 0 ? [] : variables} />
|
||||
<OnBlurPlugin onBlur={onBlur} />
|
||||
</LexicalComposer>
|
||||
{showOpenModal && scrollHeight > maxH && (
|
||||
<Box
|
||||
zIndex={10}
|
||||
position={'absolute'}
|
||||
bottom={-1}
|
||||
right={2}
|
||||
cursor={'pointer'}
|
||||
onClick={onOpenModal}
|
||||
>
|
||||
<MyIcon name={'common/fullScreenLight'} w={'14px'} color={'myGray.500'} />
|
||||
</Box>
|
||||
|
||||
{onChangeText &&
|
||||
ExtensionPopover?.map((Item, index) => (
|
||||
<Item key={index} iconButtonStyle={iconButtonStyle} onChangeText={onChangeText} />
|
||||
))}
|
||||
{showFullScreenIcon && (
|
||||
<Flex onClick={onOpenModal} {...iconButtonStyle} right={2}>
|
||||
<MyIcon name={'common/fullScreenLight'} w={'1rem'} color={'myGray.500'} />
|
||||
</Flex>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
@@ -1,46 +1,29 @@
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
import { Box, Button, ModalBody, ModalFooter, useDisclosure } from '@chakra-ui/react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { editorStateToText } from './utils';
|
||||
import type { EditorProps } from './Editor';
|
||||
import Editor from './Editor';
|
||||
import MyModal from '../../MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import type { EditorState, LexicalEditor } from 'lexical';
|
||||
import type { FormPropsType } from './type.d';
|
||||
import { type EditorVariableLabelPickerType, type EditorVariablePickerType } from './type.d';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
const PromptEditor = ({
|
||||
showOpenModal = true,
|
||||
variables = [],
|
||||
variableLabels = [],
|
||||
value,
|
||||
onChange,
|
||||
onBlur,
|
||||
minH,
|
||||
maxH,
|
||||
maxLength,
|
||||
placeholder,
|
||||
title,
|
||||
isInvalid,
|
||||
isDisabled,
|
||||
...props
|
||||
}: {
|
||||
showOpenModal?: boolean;
|
||||
variables?: EditorVariablePickerType[];
|
||||
variableLabels?: EditorVariableLabelPickerType[];
|
||||
value?: string;
|
||||
onChange?: (text: string) => void;
|
||||
onBlur?: (text: string) => void;
|
||||
minH?: number;
|
||||
maxH?: number;
|
||||
maxLength?: number;
|
||||
placeholder?: string;
|
||||
title?: string;
|
||||
|
||||
isInvalid?: boolean;
|
||||
isDisabled?: boolean;
|
||||
} & FormPropsType) => {
|
||||
}: FormPropsType &
|
||||
EditorProps & {
|
||||
title?: string;
|
||||
isDisabled?: boolean;
|
||||
onChange?: (text: string) => void;
|
||||
onBlur?: (text: string) => void;
|
||||
}) => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -69,19 +52,13 @@ const PromptEditor = ({
|
||||
<>
|
||||
<Box position="relative">
|
||||
<Editor
|
||||
{...props}
|
||||
showOpenModal={showOpenModal}
|
||||
onOpenModal={onOpen}
|
||||
variables={variables}
|
||||
variableLabels={variableLabels}
|
||||
minH={minH}
|
||||
maxH={maxH}
|
||||
maxLength={maxLength}
|
||||
value={formattedValue}
|
||||
onChange={onChangeInput}
|
||||
onChangeText={onChange}
|
||||
onBlur={onBlurInput}
|
||||
placeholder={placeholder}
|
||||
isInvalid={isInvalid}
|
||||
{...props}
|
||||
/>
|
||||
{isDisabled && (
|
||||
<Box
|
||||
@@ -106,16 +83,14 @@ const PromptEditor = ({
|
||||
>
|
||||
<ModalBody>
|
||||
<Editor
|
||||
{...props}
|
||||
minH={400}
|
||||
maxH={400}
|
||||
maxLength={maxLength}
|
||||
showOpenModal={false}
|
||||
variables={variables}
|
||||
variableLabels={variableLabels}
|
||||
value={value}
|
||||
onChange={onChangeInput}
|
||||
onChangeText={onChange}
|
||||
onBlur={onBlurInput}
|
||||
placeholder={placeholder}
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
|
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"AutoOptimize": "Automatic optimization",
|
||||
"Click_to_delete_this_field": "Click to delete this field",
|
||||
"Filed_is_deprecated": "This field is deprecated",
|
||||
"Index": "Index",
|
||||
@@ -12,6 +13,13 @@
|
||||
"MCP_tools_url_is_empty": "The MCP address cannot be empty",
|
||||
"MCP_tools_url_placeholder": "After filling in the MCP address, click Analysis",
|
||||
"No_selected_dataset": "No selected dataset",
|
||||
"Optimizer_CloseConfirm": "Confirm to close",
|
||||
"Optimizer_CloseConfirmText": "Optimization results have been generated, confirming that closing will lose the current result. Will it continue?",
|
||||
"Optimizer_EmptyPrompt": "Please enter optimization requirements",
|
||||
"Optimizer_Generating": "Generating...",
|
||||
"Optimizer_Placeholder": "How do you want to write or optimize prompt words?",
|
||||
"Optimizer_Reoptimize": "Re-optimize",
|
||||
"Optimizer_Replace": "replace",
|
||||
"Role_setting": "Permission",
|
||||
"Run": "Execute",
|
||||
"Search_dataset": "Search dataset",
|
||||
|
@@ -1213,6 +1213,7 @@
|
||||
"support.wallet.usage.Bill Module": "Billing Module",
|
||||
"support.wallet.usage.Duration": "Duration (seconds)",
|
||||
"support.wallet.usage.Module name": "Module Name",
|
||||
"support.wallet.usage.Optimize Prompt": "Prompt word optimization",
|
||||
"support.wallet.usage.Source": "Source",
|
||||
"support.wallet.usage.Text Length": "Text Length",
|
||||
"support.wallet.usage.Time": "Generation Time",
|
||||
|
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"AutoOptimize": "自动优化",
|
||||
"Click_to_delete_this_field": "点击删除该字段",
|
||||
"Filed_is_deprecated": "该字段已弃用",
|
||||
"Index": "索引",
|
||||
@@ -12,6 +13,13 @@
|
||||
"MCP_tools_url_is_empty": "MCP 地址不能为空",
|
||||
"MCP_tools_url_placeholder": "填入 MCP 地址后,点击解析",
|
||||
"No_selected_dataset": "未选择知识库",
|
||||
"Optimizer_CloseConfirm": "确认关闭",
|
||||
"Optimizer_CloseConfirmText": "已经生成了优化结果,确认关闭将丢失当前结果,是否继续?",
|
||||
"Optimizer_EmptyPrompt": "请输入优化要求",
|
||||
"Optimizer_Generating": "生成中…",
|
||||
"Optimizer_Placeholder": "你希望如何编写或优化提示词?",
|
||||
"Optimizer_Reoptimize": "重新优化",
|
||||
"Optimizer_Replace": "替换",
|
||||
"Role_setting": "权限设置",
|
||||
"Run": "运行",
|
||||
"Search_dataset": "搜索知识库",
|
||||
|
@@ -1214,6 +1214,7 @@
|
||||
"support.wallet.usage.Bill Module": "扣费模块",
|
||||
"support.wallet.usage.Duration": "时长(秒)",
|
||||
"support.wallet.usage.Module name": "模块名",
|
||||
"support.wallet.usage.Optimize Prompt": "提示词优化",
|
||||
"support.wallet.usage.Source": "来源",
|
||||
"support.wallet.usage.Text Length": "文本长度",
|
||||
"support.wallet.usage.Time": "生成时间",
|
||||
|
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"AutoOptimize": "自動優化",
|
||||
"Click_to_delete_this_field": "點擊刪除該字段",
|
||||
"Filed_is_deprecated": "該字段已棄用",
|
||||
"Index": "索引",
|
||||
@@ -12,6 +13,13 @@
|
||||
"MCP_tools_url_is_empty": "MCP 地址不能為空",
|
||||
"MCP_tools_url_placeholder": "填入 MCP 地址後,點擊解析",
|
||||
"No_selected_dataset": "未選擇知識庫",
|
||||
"Optimizer_CloseConfirm": "確認關閉",
|
||||
"Optimizer_CloseConfirmText": "已經生成了優化結果,確認關閉將丟失當前結果,是否繼續?",
|
||||
"Optimizer_EmptyPrompt": "請輸入優化要求",
|
||||
"Optimizer_Generating": "生成中…",
|
||||
"Optimizer_Placeholder": "你希望如何編寫或優化提示詞?",
|
||||
"Optimizer_Reoptimize": "重新優化",
|
||||
"Optimizer_Replace": "替換",
|
||||
"Role_setting": "權限設定",
|
||||
"Run": "執行",
|
||||
"Search_dataset": "搜尋知識庫",
|
||||
|
@@ -1212,6 +1212,7 @@
|
||||
"support.wallet.usage.Bill Module": "計費模組",
|
||||
"support.wallet.usage.Duration": "時長(秒)",
|
||||
"support.wallet.usage.Module name": "模組名稱",
|
||||
"support.wallet.usage.Optimize Prompt": "提示詞優化",
|
||||
"support.wallet.usage.Source": "來源",
|
||||
"support.wallet.usage.Text Length": "文字長度",
|
||||
"support.wallet.usage.Time": "產生時間",
|
||||
|
Reference in New Issue
Block a user