feat: 新增系统提示词内容替换功能并重构常量定义
- 新增 SYSTEM_PROMPT_REPLACEMENTS 配置项,支持在系统提示词中执行顺序内容替换 - 将 MODEL_PROVIDER 等常量从 common.js 迁移到独立的 constants.js 文件 - 为所有提供商策略(OpenAI、Claude、Gemini、Grok、Forward、Codex Responses)添加系统提示词替换支持 - 更新 UI 配置界面,添加替换规则管理功能 - 更新 Grok 提供商模型列表至 4.20 版本
This commit is contained in:
parent
22ce2440da
commit
a069feea71
22 changed files with 296 additions and 68 deletions
|
|
@ -5,6 +5,10 @@
|
|||
"MODEL_PROVIDER": "gemini-cli-oauth",
|
||||
"SYSTEM_PROMPT_FILE_PATH": "configs/input_system_prompt.txt",
|
||||
"SYSTEM_PROMPT_MODE": "overwrite",
|
||||
"SYSTEM_PROMPT_REPLACEMENTS": [
|
||||
{ "old": "Old text to replace", "new": "New replacement text" },
|
||||
{ "old": "AI", "new": "Smart Bot" }
|
||||
],
|
||||
"PROMPT_LOG_BASE_NAME": "prompt_log",
|
||||
"PROMPT_LOG_MODE": "none",
|
||||
"REQUEST_MAX_RETRIES": 3,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import { v4 as uuidv4 } from 'uuid';
|
|||
import logger from '../utils/logger.js';
|
||||
import { MODEL_PROTOCOL_PREFIX, getProtocolPrefix } from '../utils/common.js';
|
||||
import { ConverterFactory } from '../converters/ConverterFactory.js';
|
||||
import { CONFIG } from '../core/config-manager.js';
|
||||
import {
|
||||
generateResponseCreated,
|
||||
generateResponseInProgress,
|
||||
|
|
@ -237,8 +238,8 @@ export function toOpenAIStreamChunkFromOpenAIResponses(responsesChunk, model) {
|
|||
|
||||
// 辅助函数导出
|
||||
export async function extractAndProcessSystemMessages(messages) {
|
||||
const { Utils } = await import('../converters/utils.js');
|
||||
return Utils.extractSystemMessages(messages);
|
||||
const { extractAndProcessSystemMessages: extract } = await import('../converters/utils.js');
|
||||
return extract(messages, CONFIG?.SYSTEM_PROMPT_REPLACEMENTS);
|
||||
}
|
||||
|
||||
export async function extractTextFromMessageContent(content) {
|
||||
|
|
|
|||
|
|
@ -119,18 +119,49 @@ export function extractTextFromMessageContent(content) {
|
|||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用系统提示词内容替换
|
||||
* @param {string} content - 原始内容
|
||||
* @param {Array} replacements - 替换规则数组
|
||||
* @returns {string} 替换后的内容
|
||||
*/
|
||||
export function applySystemPromptReplacements(content, replacements = []) {
|
||||
if (!content || !replacements || !Array.isArray(replacements) || replacements.length === 0) {
|
||||
return content;
|
||||
}
|
||||
let newContent = content;
|
||||
for (const replacement of replacements) {
|
||||
if (replacement.old !== undefined && replacement.new !== undefined) {
|
||||
if (typeof replacement.old === 'string') {
|
||||
// 简单字符串全量替换
|
||||
newContent = newContent.split(replacement.old).join(replacement.new);
|
||||
} else if (replacement.old instanceof RegExp || (typeof replacement.old === 'object' && replacement.old !== null)) {
|
||||
// 正则表达式替换
|
||||
newContent = newContent.replace(replacement.old, replacement.new);
|
||||
}
|
||||
}
|
||||
}
|
||||
return newContent;
|
||||
}
|
||||
|
||||
/**
|
||||
* 提取并处理系统消息
|
||||
* @param {Array} messages - 消息数组
|
||||
* @param {Array} replacements - 替换规则数组,可选
|
||||
* @returns {{systemInstruction: Object|null, nonSystemMessages: Array}}
|
||||
*/
|
||||
export function extractAndProcessSystemMessages(messages) {
|
||||
export function extractAndProcessSystemMessages(messages, replacements = []) {
|
||||
const systemContents = [];
|
||||
const nonSystemMessages = [];
|
||||
|
||||
for (const message of messages) {
|
||||
if (message.role === 'system') {
|
||||
systemContents.push(extractTextFromMessageContent(message.content));
|
||||
let content = extractTextFromMessageContent(message.content);
|
||||
|
||||
// 应用系统提示词内容替换
|
||||
content = applySystemPromptReplacements(content, replacements);
|
||||
|
||||
systemContents.push(content);
|
||||
} else {
|
||||
nonSystemMessages.push(message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import * as fs from 'fs';
|
||||
import { promises as pfs } from 'fs';
|
||||
import { INPUT_SYSTEM_PROMPT_FILE, MODEL_PROVIDER } from '../utils/common.js';
|
||||
import { INPUT_SYSTEM_PROMPT_FILE } from '../utils/common.js';
|
||||
import { MODEL_PROVIDER } from '../utils/constants.js';
|
||||
import logger from '../utils/logger.js';
|
||||
|
||||
export let CONFIG = {}; // Make CONFIG exportable
|
||||
|
|
@ -76,6 +77,7 @@ export async function initializeConfig(args = process.argv.slice(2), configFileP
|
|||
LOGIN_MIN_INTERVAL: 5000, // 两次尝试之间的最小间隔(毫秒),默认1秒
|
||||
PROVIDER_POOLS_FILE_PATH: null, // 新增号池配置文件路径
|
||||
MAX_ERROR_COUNT: 10, // 提供商最大错误次数
|
||||
SYSTEM_PROMPT_REPLACEMENTS: [], // 系统提示词内容替换规则,例如: [{"old": "AI", "new": "Bot"}, {"old": "OpenAI", "new": "Gemini"}]
|
||||
SCHEDULED_HEALTH_CHECK: {
|
||||
enabled: false,
|
||||
interval: 600000,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { handleUIApiRequests, serveStaticFiles } from '../services/ui-manager.js
|
|||
import { handleAPIRequests } from '../services/api-manager.js';
|
||||
import { getApiService, getProviderStatus } from '../services/service-manager.js';
|
||||
import { getProviderPoolManager } from '../services/service-manager.js';
|
||||
import { MODEL_PROVIDER } from '../utils/common.js';
|
||||
import { MODEL_PROVIDER } from '../utils/constants.js';
|
||||
import { getRegisteredProviders, isRegisteredProvider } from '../providers/adapter.js';
|
||||
import { countTokensAnthropic } from '../utils/token-utils.js';
|
||||
import { PROMPT_LOG_FILENAME } from '../core/config-manager.js';
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { IFlowApiService } from './openai/iflow-core.js';
|
|||
import { CodexApiService } from './openai/codex-core.js';
|
||||
import { ForwardApiService } from './forward/forward-core.js';
|
||||
import { GrokApiService } from './grok/grok-core.js';
|
||||
import { MODEL_PROVIDER } from '../utils/common.js';
|
||||
import { MODEL_PROVIDER } from '../utils/constants.js';
|
||||
import logger from '../utils/logger.js';
|
||||
|
||||
// 适配器注册表
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { ProviderStrategy } from '../../utils/provider-strategy.js';
|
||||
import logger from '../../utils/logger.js';
|
||||
import { extractSystemPromptFromRequestBody, MODEL_PROTOCOL_PREFIX } from '../../utils/common.js';
|
||||
import { applySystemPromptReplacements } from '../../converters/utils.js';
|
||||
|
||||
/**
|
||||
* Claude provider strategy implementation.
|
||||
|
|
@ -59,7 +60,8 @@ class ClaudeStrategy extends ProviderStrategy {
|
|||
? `${existingSystemText}\n${filePromptContent}`
|
||||
: filePromptContent;
|
||||
|
||||
requestBody.system = newSystemText;
|
||||
// Apply system prompt replacements
|
||||
requestBody.system = applySystemPromptReplacements(newSystemText, config.SYSTEM_PROMPT_REPLACEMENTS);
|
||||
logger.info(`[System Prompt] Applied system prompt from ${config.SYSTEM_PROMPT_FILE_PATH} in '${config.SYSTEM_PROMPT_MODE}' mode for provider 'claude'.`);
|
||||
|
||||
return requestBody;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
import { ProviderStrategy } from '../../utils/provider-strategy.js';
|
||||
import logger from '../../utils/logger.js';
|
||||
import { extractSystemPromptFromRequestBody, MODEL_PROTOCOL_PREFIX } from '../../utils/common.js';
|
||||
import { applySystemPromptReplacements } from '../../converters/utils.js';
|
||||
|
||||
/**
|
||||
* Forward provider strategy implementation.
|
||||
|
|
@ -40,8 +43,35 @@ class ForwardStrategy extends ProviderStrategy {
|
|||
}
|
||||
|
||||
async applySystemPromptFromFile(config, requestBody) {
|
||||
// For forwarder, we might want to skip automatic system prompt application
|
||||
// to keep it transparent, but let's follow the base implementation just in case.
|
||||
if (!config.SYSTEM_PROMPT_FILE_PATH) {
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
const filePromptContent = config.SYSTEM_PROMPT_CONTENT;
|
||||
if (filePromptContent === null) {
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
const existingSystemText = extractSystemPromptFromRequestBody(requestBody, MODEL_PROTOCOL_PREFIX.OPENAI);
|
||||
|
||||
const newSystemText = config.SYSTEM_PROMPT_MODE === 'append' && existingSystemText
|
||||
? `${existingSystemText}\n${filePromptContent}`
|
||||
: filePromptContent;
|
||||
|
||||
// Apply system prompt replacements
|
||||
const finalSystemText = applySystemPromptReplacements(newSystemText, config.SYSTEM_PROMPT_REPLACEMENTS);
|
||||
|
||||
if (!requestBody.messages) {
|
||||
requestBody.messages = [];
|
||||
}
|
||||
const systemMessageIndex = requestBody.messages.findIndex(m => m.role === 'system');
|
||||
if (systemMessageIndex !== -1) {
|
||||
requestBody.messages[systemMessageIndex].content = finalSystemText;
|
||||
} else {
|
||||
requestBody.messages.unshift({ role: 'system', content: finalSystemText });
|
||||
}
|
||||
logger.info(`[System Prompt] Applied system prompt from ${config.SYSTEM_PROMPT_FILE_PATH} in '${config.SYSTEM_PROMPT_MODE}' mode for provider 'forward'.`);
|
||||
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { API_ACTIONS, extractSystemPromptFromRequestBody, MODEL_PROTOCOL_PREFIX } from '../../utils/common.js';
|
||||
import logger from '../../utils/logger.js';
|
||||
import { ProviderStrategy } from '../../utils/provider-strategy.js';
|
||||
import { applySystemPromptReplacements } from '../../converters/utils.js';
|
||||
|
||||
/**
|
||||
* Gemini provider strategy implementation.
|
||||
|
|
@ -52,7 +53,10 @@ class GeminiStrategy extends ProviderStrategy {
|
|||
? `${existingSystemText}\n${filePromptContent}`
|
||||
: filePromptContent;
|
||||
|
||||
requestBody.systemInstruction = { parts: [{ text: newSystemText }] };
|
||||
// Apply system prompt replacements
|
||||
const finalSystemText = applySystemPromptReplacements(newSystemText, config.SYSTEM_PROMPT_REPLACEMENTS);
|
||||
|
||||
requestBody.systemInstruction = { parts: [{ text: finalSystemText }] };
|
||||
if (requestBody.system_instruction) {
|
||||
delete requestBody.system_instruction;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { API_ACTIONS, MODEL_PROTOCOL_PREFIX } from '../../utils/common.js';
|
||||
import logger from '../../utils/logger.js';
|
||||
import { ProviderStrategy } from '../../utils/provider-strategy.js';
|
||||
import { applySystemPromptReplacements } from '../../converters/utils.js';
|
||||
|
||||
/**
|
||||
* Grok provider strategy implementation.
|
||||
|
|
@ -37,10 +38,13 @@ class GrokStrategy extends ProviderStrategy {
|
|||
// Here we can prepend it if needed, or handle it during request conversion.
|
||||
// Since requestBody already contains the converted message, we might need to prepend it here.
|
||||
|
||||
// Apply system prompt replacements to file prompt content
|
||||
const finalFilePrompt = applySystemPromptReplacements(filePromptContent, config.SYSTEM_PROMPT_REPLACEMENTS);
|
||||
|
||||
const existingMessage = requestBody.message || "";
|
||||
const newSystemText = config.SYSTEM_PROMPT_MODE === 'append'
|
||||
? `${existingMessage}\n\nSystem: ${filePromptContent}`
|
||||
: `System: ${filePromptContent}\n\n${existingMessage}`;
|
||||
? `${existingMessage}\n\nSystem: ${finalFilePrompt}`
|
||||
: `System: ${finalFilePrompt}\n\n${existingMessage}`;
|
||||
|
||||
requestBody.message = newSystemText;
|
||||
logger.info(`[System Prompt] Applied system prompt for Grok in '${config.SYSTEM_PROMPT_MODE}' mode.`);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { ProviderStrategy } from '../../utils/provider-strategy.js';
|
||||
import logger from '../../utils/logger.js';
|
||||
import { extractSystemPromptFromRequestBody, MODEL_PROTOCOL_PREFIX } from '../../utils/common.js';
|
||||
import { applySystemPromptReplacements } from '../../converters/utils.js';
|
||||
|
||||
/**
|
||||
* OpenAI Responses API strategy implementation.
|
||||
|
|
@ -65,16 +66,19 @@ class CodexResponsesAPIStrategy extends ProviderStrategy {
|
|||
return requestBody;
|
||||
}
|
||||
|
||||
// Apply system prompt replacements
|
||||
const finalSystemText = applySystemPromptReplacements(filePromptContent, config.SYSTEM_PROMPT_REPLACEMENTS);
|
||||
|
||||
// In Responses API, system instructions are typically passed in 'instructions' field
|
||||
// or in the input array with role: 'developer'
|
||||
requestBody.instructions = requestBody.instructions || filePromptContent;
|
||||
requestBody.instructions = requestBody.instructions || finalSystemText;
|
||||
|
||||
// If using instructions field is not desired, append to input array instead
|
||||
if (!requestBody.instructions || config.SYSTEM_PROMPT_MODE === 'append') {
|
||||
if (typeof requestBody.input === 'string') {
|
||||
// Convert to array format to add system message
|
||||
requestBody.input = [
|
||||
{ type: 'message', role: 'developer', content: filePromptContent },
|
||||
{ type: 'message', role: 'developer', content: finalSystemText },
|
||||
{ type: 'message', role: 'user', content: requestBody.input }
|
||||
];
|
||||
} else if (Array.isArray(requestBody.input)) {
|
||||
|
|
@ -84,17 +88,17 @@ class CodexResponsesAPIStrategy extends ProviderStrategy {
|
|||
);
|
||||
|
||||
if (systemMessageIndex !== -1) {
|
||||
requestBody.input[systemMessageIndex].content = filePromptContent;
|
||||
requestBody.input[systemMessageIndex].content = finalSystemText;
|
||||
} else {
|
||||
requestBody.input.unshift({ type: 'message', role: 'developer', content: filePromptContent });
|
||||
requestBody.input.unshift({ type: 'message', role: 'developer', content: finalSystemText });
|
||||
}
|
||||
} else {
|
||||
// If input is not defined, initialize with system message
|
||||
requestBody.input = [{ type: 'message', role: 'developer', content: filePromptContent }];
|
||||
requestBody.input = [{ type: 'message', role: 'developer', content: finalSystemText }];
|
||||
}
|
||||
} else if (requestBody.instructions) {
|
||||
// If system prompt mode is not append, then replace the instructions
|
||||
requestBody.instructions = filePromptContent;
|
||||
requestBody.instructions = finalSystemText;
|
||||
}
|
||||
|
||||
logger.info(`[System Prompt] Applied system prompt from ${config.SYSTEM_PROMPT_FILE_PATH} in '${config.SYSTEM_PROMPT_MODE}' mode for provider 'responses'.`);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { ProviderStrategy } from '../../utils/provider-strategy.js';
|
||||
import logger from '../../utils/logger.js';
|
||||
import { extractSystemPromptFromRequestBody, MODEL_PROTOCOL_PREFIX } from '../../utils/common.js';
|
||||
import { applySystemPromptReplacements } from '../../converters/utils.js';
|
||||
|
||||
/**
|
||||
* OpenAI Responses API strategy implementation.
|
||||
|
|
@ -65,16 +66,23 @@ class ResponsesAPIStrategy extends ProviderStrategy {
|
|||
return requestBody;
|
||||
}
|
||||
|
||||
const newSystemText = config.SYSTEM_PROMPT_MODE === 'append' && existingSystemText
|
||||
? `${existingSystemText}\n${filePromptContent}`
|
||||
: filePromptContent;
|
||||
|
||||
// Apply system prompt replacements
|
||||
const finalSystemText = applySystemPromptReplacements(newSystemText, config.SYSTEM_PROMPT_REPLACEMENTS);
|
||||
|
||||
// In Responses API, system instructions are typically passed in 'instructions' field
|
||||
// or in the input array with role: 'system'
|
||||
requestBody.instructions = requestBody.instructions || filePromptContent;
|
||||
requestBody.instructions = requestBody.instructions || finalSystemText;
|
||||
|
||||
// If using instructions field is not desired, append to input array instead
|
||||
if (!requestBody.instructions || config.SYSTEM_PROMPT_MODE === 'append') {
|
||||
if (typeof requestBody.input === 'string') {
|
||||
// Convert to array format to add system message
|
||||
requestBody.input = [
|
||||
{ role: 'system', content: filePromptContent },
|
||||
{ role: 'system', content: finalSystemText },
|
||||
{ role: 'user', content: requestBody.input }
|
||||
];
|
||||
} else if (Array.isArray(requestBody.input)) {
|
||||
|
|
@ -84,17 +92,17 @@ class ResponsesAPIStrategy extends ProviderStrategy {
|
|||
);
|
||||
|
||||
if (systemMessageIndex !== -1) {
|
||||
requestBody.input[systemMessageIndex].content = filePromptContent;
|
||||
requestBody.input[systemMessageIndex].content = finalSystemText;
|
||||
} else {
|
||||
requestBody.input.unshift({ role: 'system', content: filePromptContent });
|
||||
requestBody.input.unshift({ role: 'system', content: finalSystemText });
|
||||
}
|
||||
} else {
|
||||
// If input is not defined, initialize with system message
|
||||
requestBody.input = [{ role: 'system', content: filePromptContent }];
|
||||
requestBody.input = [{ role: 'system', content: finalSystemText }];
|
||||
}
|
||||
} else if (requestBody.instructions) {
|
||||
// If system prompt mode is not append, then replace the instructions
|
||||
requestBody.instructions = filePromptContent;
|
||||
requestBody.instructions = finalSystemText;
|
||||
}
|
||||
|
||||
logger.info(`[System Prompt] Applied system prompt from ${config.SYSTEM_PROMPT_FILE_PATH} in '${config.SYSTEM_PROMPT_MODE}' mode for provider 'responses'.`);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { ProviderStrategy } from '../../utils/provider-strategy.js';
|
||||
import logger from '../../utils/logger.js';
|
||||
import { extractSystemPromptFromRequestBody, MODEL_PROTOCOL_PREFIX } from '../../utils/common.js';
|
||||
import { applySystemPromptReplacements } from '../../converters/utils.js';
|
||||
|
||||
/**
|
||||
* OpenAI provider strategy implementation.
|
||||
|
|
@ -61,14 +62,17 @@ class OpenAIStrategy extends ProviderStrategy {
|
|||
? `${existingSystemText}\n${filePromptContent}`
|
||||
: filePromptContent;
|
||||
|
||||
// Apply system prompt replacements
|
||||
const finalSystemText = applySystemPromptReplacements(newSystemText, config.SYSTEM_PROMPT_REPLACEMENTS);
|
||||
|
||||
if (!requestBody.messages) {
|
||||
requestBody.messages = [];
|
||||
}
|
||||
const systemMessageIndex = requestBody.messages.findIndex(m => m.role === 'system');
|
||||
if (systemMessageIndex !== -1) {
|
||||
requestBody.messages[systemMessageIndex].content = newSystemText;
|
||||
requestBody.messages[systemMessageIndex].content = finalSystemText;
|
||||
} else {
|
||||
requestBody.messages.unshift({ role: 'system', content: newSystemText });
|
||||
requestBody.messages.unshift({ role: 'system', content: finalSystemText });
|
||||
}
|
||||
logger.info(`[System Prompt] Applied system prompt from ${config.SYSTEM_PROMPT_FILE_PATH} in '${config.SYSTEM_PROMPT_MODE}' mode for provider 'openai'.`);
|
||||
|
||||
|
|
|
|||
|
|
@ -94,21 +94,20 @@ export const PROVIDER_MODELS = {
|
|||
],
|
||||
'forward-api': [],
|
||||
'grok-custom': [
|
||||
'grok-3',
|
||||
'grok-3-mini',
|
||||
'grok-3-thinking',
|
||||
'grok-4',
|
||||
'grok-4-mini',
|
||||
'grok-4-thinking',
|
||||
'grok-4-heavy',
|
||||
'grok-4.1-mini',
|
||||
'grok-4.1-fast',
|
||||
'grok-4.1-expert',
|
||||
'grok-4.1-thinking',
|
||||
'grok-4.20-beta',
|
||||
'grok-4.20',
|
||||
'grok-4.20-auto',
|
||||
'grok-4.20-fast',
|
||||
'grok-4.20-expert',
|
||||
'grok-4.20-heavy',
|
||||
'grok-imagine-1.0',
|
||||
'grok-imagine-1.0-edit',
|
||||
'grok-imagine-1.0-video'
|
||||
'grok-4.20-nsfw',
|
||||
'grok-4.20-auto-nsfw',
|
||||
'grok-4.20-fast-nsfw',
|
||||
'grok-4.20-expert-nsfw',
|
||||
'grok-4.20-heavy-nsfw',
|
||||
'grok-imagine-1.0-nsfw',
|
||||
'grok-imagine-1.0-edit-nsfw'
|
||||
]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import {
|
|||
getFileName,
|
||||
formatSystemPath
|
||||
} from '../utils/provider-utils.js';
|
||||
import { MODEL_PROVIDER } from '../utils/common.js';
|
||||
import { MODEL_PROVIDER } from '../utils/constants.js';
|
||||
|
||||
// 存储 ProviderPoolManager 实例
|
||||
let providerPoolManager = null;
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ export async function handleGetConfig(req, res, currentConfig) {
|
|||
LOGIN_EXPIRY: currentConfig.LOGIN_EXPIRY,
|
||||
PROVIDER_POOLS_FILE_PATH: currentConfig.PROVIDER_POOLS_FILE_PATH,
|
||||
MAX_ERROR_COUNT: currentConfig.MAX_ERROR_COUNT,
|
||||
SYSTEM_PROMPT_REPLACEMENTS: currentConfig.SYSTEM_PROMPT_REPLACEMENTS,
|
||||
WARMUP_TARGET: currentConfig.WARMUP_TARGET,
|
||||
REFRESH_CONCURRENCY_PER_PROVIDER: currentConfig.REFRESH_CONCURRENCY_PER_PROVIDER,
|
||||
providerFallbackChain: currentConfig.providerFallbackChain,
|
||||
|
|
@ -153,6 +154,11 @@ export async function handleUpdateConfig(req, res, currentConfig) {
|
|||
}
|
||||
}
|
||||
if (newConfig.SYSTEM_PROMPT_MODE !== undefined) currentConfig.SYSTEM_PROMPT_MODE = newConfig.SYSTEM_PROMPT_MODE;
|
||||
if (newConfig.SYSTEM_PROMPT_REPLACEMENTS !== undefined) {
|
||||
if (Array.isArray(newConfig.SYSTEM_PROMPT_REPLACEMENTS)) {
|
||||
currentConfig.SYSTEM_PROMPT_REPLACEMENTS = newConfig.SYSTEM_PROMPT_REPLACEMENTS;
|
||||
}
|
||||
}
|
||||
if (newConfig.PROMPT_LOG_BASE_NAME !== undefined) currentConfig.PROMPT_LOG_BASE_NAME = newConfig.PROMPT_LOG_BASE_NAME;
|
||||
if (newConfig.PROMPT_LOG_MODE !== undefined) currentConfig.PROMPT_LOG_MODE = newConfig.PROMPT_LOG_MODE;
|
||||
if (newConfig.REQUEST_MAX_RETRIES !== undefined) {
|
||||
|
|
@ -288,6 +294,7 @@ export async function handleUpdateConfig(req, res, currentConfig) {
|
|||
MODEL_PROVIDER: currentConfig.MODEL_PROVIDER,
|
||||
SYSTEM_PROMPT_FILE_PATH: currentConfig.SYSTEM_PROMPT_FILE_PATH,
|
||||
SYSTEM_PROMPT_MODE: currentConfig.SYSTEM_PROMPT_MODE,
|
||||
SYSTEM_PROMPT_REPLACEMENTS: currentConfig.SYSTEM_PROMPT_REPLACEMENTS,
|
||||
PROMPT_LOG_BASE_NAME: currentConfig.PROMPT_LOG_BASE_NAME,
|
||||
PROMPT_LOG_MODE: currentConfig.PROMPT_LOG_MODE,
|
||||
REQUEST_MAX_RETRIES: currentConfig.REQUEST_MAX_RETRIES,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
export { MODEL_PROTOCOL_PREFIX, MODEL_PROVIDER } from './constants.js';
|
||||
import { promises as fs } from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as http from 'http'; // Add http for IncomingMessage and ServerResponse types
|
||||
|
|
@ -6,6 +7,7 @@ import logger from './logger.js';
|
|||
import { convertData, getOpenAIStreamChunkStop } from '../convert/convert.js';
|
||||
import { ProviderStrategyFactory } from './provider-strategies.js';
|
||||
import { getPluginManager } from '../core/plugin-manager.js';
|
||||
import { MODEL_PROTOCOL_PREFIX, MODEL_PROVIDER } from './constants.js';
|
||||
|
||||
// ==================== 网络错误处理 ====================
|
||||
|
||||
|
|
@ -49,33 +51,6 @@ export const API_ACTIONS = {
|
|||
STREAM_GENERATE_CONTENT: 'streamGenerateContent',
|
||||
};
|
||||
|
||||
export const MODEL_PROTOCOL_PREFIX = {
|
||||
// Model provider constants
|
||||
GEMINI: 'gemini',
|
||||
OPENAI: 'openai',
|
||||
OPENAI_RESPONSES: 'openaiResponses',
|
||||
CLAUDE: 'claude',
|
||||
CODEX: 'codex',
|
||||
FORWARD: 'forward',
|
||||
GROK: 'grok',
|
||||
}
|
||||
|
||||
export const MODEL_PROVIDER = {
|
||||
// Model provider constants
|
||||
GEMINI_CLI: 'gemini-cli-oauth',
|
||||
ANTIGRAVITY: 'gemini-antigravity',
|
||||
OPENAI_CUSTOM: 'openai-custom',
|
||||
OPENAI_CUSTOM_RESPONSES: 'openaiResponses-custom',
|
||||
CLAUDE_CUSTOM: 'claude-custom',
|
||||
KIRO_API: 'claude-kiro-oauth',
|
||||
QWEN_API: 'openai-qwen-oauth',
|
||||
IFLOW_API: 'openai-iflow',
|
||||
CODEX_API: 'openai-codex-oauth',
|
||||
FORWARD_API: 'forward-api',
|
||||
GROK_CUSTOM: 'grok-custom',
|
||||
AUTO: 'auto',
|
||||
}
|
||||
|
||||
import {
|
||||
usesManagedModelList,
|
||||
getConfiguredSupportedModels
|
||||
|
|
|
|||
|
|
@ -40,3 +40,30 @@ export const RETRY = {
|
|||
// 最大重试次数
|
||||
MAX_RETRIES: 100
|
||||
};
|
||||
|
||||
// 协议前缀常量
|
||||
export const MODEL_PROTOCOL_PREFIX = {
|
||||
GEMINI: 'gemini',
|
||||
OPENAI: 'openai',
|
||||
OPENAI_RESPONSES: 'openaiResponses',
|
||||
CLAUDE: 'claude',
|
||||
CODEX: 'codex',
|
||||
FORWARD: 'forward',
|
||||
GROK: 'grok',
|
||||
};
|
||||
|
||||
// 提供商标识符常量
|
||||
export const MODEL_PROVIDER = {
|
||||
GEMINI_CLI: 'gemini-cli-oauth',
|
||||
ANTIGRAVITY: 'gemini-antigravity',
|
||||
OPENAI_CUSTOM: 'openai-custom',
|
||||
OPENAI_CUSTOM_RESPONSES: 'openaiResponses-custom',
|
||||
CLAUDE_CUSTOM: 'claude-custom',
|
||||
KIRO_API: 'claude-kiro-oauth',
|
||||
QWEN_API: 'openai-qwen-oauth',
|
||||
IFLOW_API: 'openai-iflow',
|
||||
CODEX_API: 'openai-codex-oauth',
|
||||
FORWARD_API: 'forward-api',
|
||||
GROK_CUSTOM: 'grok-custom',
|
||||
AUTO: 'auto',
|
||||
};
|
||||
|
|
|
|||
|
|
@ -126,6 +126,47 @@ function updatePinnedStatus(container) {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化系统提示词替换规则 UI
|
||||
*/
|
||||
function initReplacementsUI() {
|
||||
const addBtn = document.getElementById('addReplacementBtn');
|
||||
if (addBtn && !addBtn.dataset.listenerAttached) {
|
||||
addBtn.addEventListener('click', () => {
|
||||
addReplacementRow('', '');
|
||||
});
|
||||
addBtn.dataset.listenerAttached = 'true';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加一条替换规则行
|
||||
* @param {string} oldVal - 查找内容
|
||||
* @param {string} newVal - 替换内容
|
||||
*/
|
||||
function addReplacementRow(oldVal = '', newVal = '') {
|
||||
const container = document.getElementById('systemPromptReplacementsContainer');
|
||||
if (!container) return;
|
||||
|
||||
const row = document.createElement('div');
|
||||
row.className = 'replacement-row';
|
||||
row.innerHTML = `
|
||||
<input type="text" class="form-control replacement-old" placeholder="${t('config.advanced.replacement.old')}" value="${oldVal}">
|
||||
<input type="text" class="form-control replacement-new" placeholder="${t('config.advanced.replacement.new')}" value="${newVal}">
|
||||
<button type="button" class="remove-replacement-btn" title="${t('config.advanced.replacement.remove')}">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
</button>
|
||||
`;
|
||||
|
||||
// 绑定删除按钮事件
|
||||
const removeBtn = row.querySelector('.remove-replacement-btn');
|
||||
removeBtn.addEventListener('click', () => {
|
||||
row.remove();
|
||||
});
|
||||
|
||||
container.appendChild(row);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载配置
|
||||
*/
|
||||
|
|
@ -133,6 +174,18 @@ async function loadConfiguration() {
|
|||
try {
|
||||
const data = await window.apiClient.get('/config');
|
||||
|
||||
// 初始化替换规则 UI
|
||||
initReplacementsUI();
|
||||
const replacementsContainer = document.getElementById('systemPromptReplacementsContainer');
|
||||
if (replacementsContainer) {
|
||||
replacementsContainer.innerHTML = '';
|
||||
if (data.SYSTEM_PROMPT_REPLACEMENTS && Array.isArray(data.SYSTEM_PROMPT_REPLACEMENTS)) {
|
||||
data.SYSTEM_PROMPT_REPLACEMENTS.forEach(r => {
|
||||
addReplacementRow(r.old || '', r.new || '');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 基础配置
|
||||
const apiKeyEl = document.getElementById('apiKey');
|
||||
const hostEl = document.getElementById('host');
|
||||
|
|
@ -380,6 +433,19 @@ async function saveConfiguration() {
|
|||
// 保存高级配置参数
|
||||
config.SYSTEM_PROMPT_FILE_PATH = document.getElementById('systemPromptFilePath')?.value || 'configs/input_system_prompt.txt';
|
||||
config.SYSTEM_PROMPT_MODE = document.getElementById('systemPromptMode')?.value || 'append';
|
||||
|
||||
// 收集系统提示词内容替换规则
|
||||
const replacements = [];
|
||||
const replacementRows = document.querySelectorAll('.replacement-row');
|
||||
replacementRows.forEach(row => {
|
||||
const oldVal = row.querySelector('.replacement-old')?.value || '';
|
||||
const newVal = row.querySelector('.replacement-new')?.value || '';
|
||||
if (oldVal) {
|
||||
replacements.push({ old: oldVal, new: newVal });
|
||||
}
|
||||
});
|
||||
config.SYSTEM_PROMPT_REPLACEMENTS = replacements;
|
||||
|
||||
config.PROMPT_LOG_BASE_NAME = document.getElementById('promptLogBaseName')?.value || '';
|
||||
config.PROMPT_LOG_MODE = document.getElementById('promptLogMode')?.value || '';
|
||||
config.REQUEST_MAX_RETRIES = parseInt(document.getElementById('requestMaxRetries')?.value || 3);
|
||||
|
|
|
|||
|
|
@ -332,6 +332,12 @@ const translations = {
|
|||
'config.advanced.modelFallbackMappingInvalid': 'Model Fallback 映射配置格式无效,请输入有效的 JSON',
|
||||
'config.advanced.systemPrompt': '系统提示',
|
||||
'config.advanced.systemPromptPlaceholder': '输入系统提示...',
|
||||
'config.advanced.systemPromptReplacements': '系统提示词内容替换',
|
||||
'config.advanced.systemPromptReplacementsNote': '按顺序逐条对系统提示词(包括请求中的和文件中的)执行内容替换。',
|
||||
'config.advanced.replacement.old': '查找内容 (旧)',
|
||||
'config.advanced.replacement.new': '替换内容 (新)',
|
||||
'config.advanced.replacement.add': '添加替换规则',
|
||||
'config.advanced.replacement.remove': '删除',
|
||||
'config.advanced.adminPassword': '后台登录密码',
|
||||
'config.advanced.adminPasswordPlaceholder': '设置后台登录密码(留空则不修改)',
|
||||
'config.advanced.adminPasswordNote': '用于保护管理控制台的访问,修改后需要重新登录',
|
||||
|
|
@ -1220,6 +1226,12 @@ const translations = {
|
|||
'config.advanced.modelFallbackMappingInvalid': 'Invalid Model Fallback mapping config format, please enter valid JSON',
|
||||
'config.advanced.systemPrompt': 'System Prompt',
|
||||
'config.advanced.systemPromptPlaceholder': 'Enter system prompt...',
|
||||
'config.advanced.systemPromptReplacements': 'System Prompt Replacements',
|
||||
'config.advanced.systemPromptReplacementsNote': 'Executes sequential content replacements for the system prompt (both from requests and files).',
|
||||
'config.advanced.replacement.old': 'Find (Old)',
|
||||
'config.advanced.replacement.new': 'Replace (New)',
|
||||
'config.advanced.replacement.add': 'Add Rule',
|
||||
'config.advanced.replacement.remove': 'Remove',
|
||||
'config.advanced.adminPassword': 'Admin Password',
|
||||
'config.advanced.adminPasswordPlaceholder': 'Set admin password (leave empty to keep unchanged)',
|
||||
'config.advanced.adminPasswordNote': 'Used to protect management console access, requires re-login after modification',
|
||||
|
|
|
|||
|
|
@ -300,6 +300,43 @@ input:checked + .toggle-slider:before {
|
|||
box-shadow: 0 4px 12px var(--primary-40);
|
||||
}
|
||||
|
||||
/* 系统提示词替换规则样式 */
|
||||
.replacements-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.replacement-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr auto;
|
||||
gap: 0.75rem;
|
||||
align-items: center;
|
||||
background: var(--bg-tertiary);
|
||||
padding: 0.75rem;
|
||||
border-radius: var(--radius-md);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.replacement-row .form-control {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.remove-replacement-btn {
|
||||
color: var(--danger-color);
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0.5rem;
|
||||
transition: var(--transition);
|
||||
}
|
||||
|
||||
.remove-replacement-btn:hover {
|
||||
color: #dc2626;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* 提供商标签选择器样式 */
|
||||
.provider-tags {
|
||||
display: flex;
|
||||
|
|
|
|||
|
|
@ -365,6 +365,17 @@
|
|||
<label for="systemPrompt" data-i18n="config.advanced.systemPrompt">系统提示内容</label>
|
||||
<textarea id="systemPrompt" class="form-control" rows="4"></textarea>
|
||||
</div>
|
||||
<!-- 系统提示词替换规则 -->
|
||||
<div class="form-group">
|
||||
<label data-i18n="config.advanced.systemPromptReplacements">系统提示词内容替换</label>
|
||||
<div id="systemPromptReplacementsContainer" class="replacements-container">
|
||||
<!-- 动态添加替换规则行 -->
|
||||
</div>
|
||||
<button type="button" id="addReplacementBtn" class="btn btn-sm btn-outline-primary mt-2">
|
||||
<i class="fas fa-plus"></i> <span data-i18n="config.advanced.replacement.add">添加替换规则</span>
|
||||
</button>
|
||||
<small class="form-text" data-i18n="config.advanced.systemPromptReplacementsNote">按顺序逐条对系统提示词(包括请求中的和文件中的)执行内容替换。</small>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="adminPassword" data-i18n="config.advanced.adminPassword">后台登录密码</label>
|
||||
<div class="password-input-wrapper">
|
||||
|
|
|
|||
Loading…
Reference in a new issue