feat(日志): 重构日志系统并添加系统提示管理功能
- 将logPrompt重命名为logConversation以支持输入输出日志 - 添加manageSystemPrompt函数来管理系统提示文本文件 - 在流式和非流式请求中记录完整响应文本 - 改进提示文本提取逻辑以获取最新用户输入
This commit is contained in:
parent
1f24706f3f
commit
ea0e49f568
3 changed files with 87 additions and 36 deletions
|
|
@ -94,10 +94,11 @@ import {
|
|||
GeminiApiService,
|
||||
API_ACTIONS,
|
||||
formatExpiryTime,
|
||||
logPrompt,
|
||||
logConversation, // Changed from logPrompt
|
||||
extractPromptText,
|
||||
extractResponseText,
|
||||
getRequestBody
|
||||
getRequestBody,
|
||||
manageSystemPrompt,
|
||||
} from './gemini-core.js';
|
||||
|
||||
// --- Configuration Parsing ---
|
||||
|
|
@ -209,9 +210,13 @@ async function handleStreamRequest(res, service, model, requestBody) {
|
|||
const stream = service.generateContentStream(model, requestBody);
|
||||
console.log('[Server Response Stream]');
|
||||
process.stdout.write('> ');
|
||||
let fullResponseText = '';
|
||||
for await (const chunk of stream) {
|
||||
const chunkText = extractResponseText(chunk);
|
||||
if (chunkText) process.stdout.write(chunkText);
|
||||
if (chunkText) {
|
||||
process.stdout.write(chunkText);
|
||||
fullResponseText += chunkText;
|
||||
}
|
||||
const chunkString = JSON.stringify(chunk);
|
||||
res.write(`data: ${chunkString}\n\n`);
|
||||
}
|
||||
|
|
@ -219,6 +224,8 @@ async function handleStreamRequest(res, service, model, requestBody) {
|
|||
res.end();
|
||||
const expiryDate = service.authClient.credentials.expiry_date;
|
||||
console.log(`[Auth Token] Time until expiry: ${formatExpiryTime(expiryDate)}`);
|
||||
|
||||
await logConversation('output', fullResponseText, PROMPT_LOG_MODE, PROMPT_LOG_FILENAME);
|
||||
}
|
||||
async function handleUnaryRequest(res, service, model, requestBody) {
|
||||
const response = await service.generateContent(model, requestBody);
|
||||
|
|
@ -232,6 +239,8 @@ async function handleUnaryRequest(res, service, model, requestBody) {
|
|||
res.end(responseString);
|
||||
const expiryDate = service.authClient.credentials.expiry_date;
|
||||
console.log(`[Auth Token] Time until expiry: ${formatExpiryTime(expiryDate)}`);
|
||||
|
||||
await logConversation('output', responseText, PROMPT_LOG_MODE, PROMPT_LOG_FILENAME);
|
||||
}
|
||||
function handleError(res, error) {
|
||||
console.error('\n[Server] Request failed:', error.stack);
|
||||
|
|
@ -271,16 +280,16 @@ async function requestHandler(req, res) {
|
|||
const [, model, action] = urlMatch;
|
||||
const requestBody = await getRequestBody(req);
|
||||
|
||||
if (PROMPT_LOG_MODE !== 'none') {
|
||||
const promptText = extractPromptText(requestBody);
|
||||
await logPrompt(promptText, PROMPT_LOG_MODE, PROMPT_LOG_FILENAME);
|
||||
}
|
||||
await manageSystemPrompt(requestBody); // Call the new function here
|
||||
const promptText = extractPromptText(requestBody);
|
||||
await logConversation('input', promptText, PROMPT_LOG_MODE, PROMPT_LOG_FILENAME);
|
||||
|
||||
if (action === API_ACTIONS.STREAM_GENERATE_CONTENT) {
|
||||
await handleStreamRequest(res, service, model, requestBody);
|
||||
} else {
|
||||
await handleUnaryRequest(res, service, model, requestBody);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export const API_ACTIONS = {
|
|||
GENERATE_CONTENT: 'generateContent',
|
||||
STREAM_GENERATE_CONTENT: 'streamGenerateContent',
|
||||
};
|
||||
|
||||
const SYSTEM_PROMPT_FILE = path.join(process.cwd(), 'system_prompt.txt');
|
||||
// --- Utility Functions ---
|
||||
|
||||
export function ensureRolesInContents(requestBody) {
|
||||
|
|
@ -54,41 +54,77 @@ export function formatExpiryTime(expiryTimestamp) {
|
|||
return `${pad(hours)}h ${pad(minutes)}m ${pad(seconds)}s`;
|
||||
}
|
||||
|
||||
export async function logPrompt(promptText, logMode, logFilename) {
|
||||
export async function logConversation(type, content, logMode, logFilename) {
|
||||
if (logMode === 'none') return;
|
||||
const logContent = `--- Prompt Log @ ${new Date().toISOString()} ---\n${promptText}\n--------------------------------------\n\n`;
|
||||
if (logMode === 'console') {
|
||||
console.log(logContent);
|
||||
|
||||
const timestamp = new Date().toLocaleString();
|
||||
const logEntry = `${timestamp} [${type.toUpperCase()}]:\n${content}\n\n\n--------------------------------------\n\n\n`;
|
||||
|
||||
if (logMode === 'console' && type === 'input') {
|
||||
console.log(logEntry);
|
||||
} else if (logMode === 'file') {
|
||||
try {
|
||||
await fs.appendFile(logFilename, logContent);
|
||||
// Append to the file
|
||||
await fs.appendFile(logFilename, logEntry);
|
||||
} catch (err) {
|
||||
console.error(`[Error] Failed to write prompt to ${logFilename}:`, err);
|
||||
console.error(`[Error] Failed to write conversation log to ${logFilename}:`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function extractPromptText(requestBody) {
|
||||
if (!requestBody) return "[No request body found]";
|
||||
const logParts = [];
|
||||
|
||||
// ** FIX: Check for both snake_case and camelCase for system instruction **
|
||||
const systemInstruction = requestBody.system_instruction || requestBody.systemInstruction;
|
||||
if (!requestBody || !Array.isArray(requestBody.contents)) return "[No request body found]";
|
||||
|
||||
if (systemInstruction && Array.isArray(systemInstruction.parts)) {
|
||||
const systemText = systemInstruction.parts.filter(p => p && typeof p.text === 'string').map(p => p.text).join('\n');
|
||||
if (systemText) logParts.push(`[SYSTEM INSTRUCTION]\n${systemText}`);
|
||||
}
|
||||
let latestPrompt = "[No text prompt found]";
|
||||
|
||||
if (Array.isArray(requestBody.contents)) {
|
||||
const userText = requestBody.contents.flatMap(c => (c && Array.isArray(c.parts) ? c.parts : [])).filter(p => p && typeof p.text === 'string').map(p => p.text).join('\n');
|
||||
if (userText) {
|
||||
if (logParts.length > 0) logParts.push('----------------------');
|
||||
logParts.push(`[USER PROMPT]\n${userText}`);
|
||||
// Iterate through contents in reverse to find the latest user prompt
|
||||
for (let i = requestBody.contents.length - 1; i >= 0; i--) {
|
||||
const content = requestBody.contents[i];
|
||||
if (content && content.role === 'user' && Array.isArray(content.parts)) {
|
||||
const userParts = content.parts.filter(part => part && typeof part.text === 'string');
|
||||
if (userParts.length > 0) {
|
||||
latestPrompt = userParts.map(part => part.text).join('\n');
|
||||
break; // Found the latest user prompt, exit loop
|
||||
}
|
||||
}
|
||||
}
|
||||
if (logParts.length === 0) return "[No text prompt or system instruction found]";
|
||||
return logParts.join('\n');
|
||||
return latestPrompt;
|
||||
}
|
||||
|
||||
|
||||
export async function manageSystemPrompt(requestBody) {
|
||||
const incomingSystemInstruction = requestBody.system_instruction || requestBody.systemInstruction;
|
||||
let incomingSystemText = '';
|
||||
|
||||
if (incomingSystemInstruction && Array.isArray(incomingSystemInstruction.parts)) {
|
||||
incomingSystemText = incomingSystemInstruction.parts
|
||||
.filter(p => p && typeof p.text === 'string')
|
||||
.map(p => p.text)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
try {
|
||||
let currentSystemText = '';
|
||||
try {
|
||||
currentSystemText = await fs.readFile(SYSTEM_PROMPT_FILE, 'utf8');
|
||||
} catch (error) {
|
||||
if (error.code !== 'ENOENT') {
|
||||
console.error(`[System Prompt Manager] Error reading system prompt file: ${error.message}`);
|
||||
}
|
||||
// If file doesn't exist, currentSystemText remains empty, which is fine.
|
||||
}
|
||||
|
||||
if (incomingSystemText && incomingSystemText !== currentSystemText) {
|
||||
await fs.writeFile(SYSTEM_PROMPT_FILE, incomingSystemText);
|
||||
console.log('[System Prompt Manager] System prompt updated in file.');
|
||||
} else if (!incomingSystemText && currentSystemText) {
|
||||
// If incoming request has no system prompt but file has one, clear the file
|
||||
await fs.writeFile(SYSTEM_PROMPT_FILE, '');
|
||||
console.log('[System Prompt Manager] System prompt cleared from file.');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[System Prompt Manager] Failed to manage system prompt file: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export function extractResponseText(responseObject) {
|
||||
|
|
|
|||
|
|
@ -117,10 +117,11 @@ import {
|
|||
GeminiApiService,
|
||||
API_ACTIONS,
|
||||
formatExpiryTime,
|
||||
logPrompt,
|
||||
logConversation, // Changed from logPrompt
|
||||
extractPromptText,
|
||||
getRequestBody,
|
||||
extractResponseText
|
||||
extractResponseText,
|
||||
manageSystemPrompt, // New import
|
||||
} from './gemini-core.js';
|
||||
|
||||
// --- Configuration Parsing ---
|
||||
|
|
@ -424,12 +425,14 @@ async function handleStreamRequest(res, service, model, requestBody) {
|
|||
const stream = service.generateContentStream(model, requestBody);
|
||||
console.log('[Server Response Stream]');
|
||||
process.stdout.write('> ');
|
||||
let fullResponseText = ''; // Declare fullResponseText here
|
||||
try {
|
||||
for await (const chunk of stream) {
|
||||
const openAIChunk = toOpenAIStreamChunk(chunk, model);
|
||||
const chunkText = openAIChunk.choices[0].delta.content || "";
|
||||
if (chunkText) {
|
||||
process.stdout.write(chunkText);
|
||||
fullResponseText += chunkText; // Accumulate text here
|
||||
}
|
||||
res.write(`data: ${JSON.stringify(openAIChunk)}\n\n`);
|
||||
}
|
||||
|
|
@ -447,6 +450,8 @@ async function handleStreamRequest(res, service, model, requestBody) {
|
|||
if (!res.writableEnded) {
|
||||
res.end();
|
||||
}
|
||||
// Log the full conversation here
|
||||
await logConversation('output', fullResponseText, PROMPT_LOG_MODE, PROMPT_LOG_FILENAME);
|
||||
}
|
||||
const expiryDate = service.authClient.credentials.expiry_date;
|
||||
console.log(`[Auth Token] Time until expiry: ${formatExpiryTime(expiryDate)}`);
|
||||
|
|
@ -465,6 +470,8 @@ async function handleUnaryRequest(res, service, model, requestBody) {
|
|||
res.end(JSON.stringify(openAIResponse));
|
||||
const expiryDate = service.authClient.credentials.expiry_date;
|
||||
console.log(`[Auth Token] Time until expiry: ${formatExpiryTime(expiryDate)}`);
|
||||
|
||||
await logConversation('output', responseText, PROMPT_LOG_MODE, PROMPT_LOG_FILENAME);
|
||||
}
|
||||
|
||||
function handleError(res, error) {
|
||||
|
|
@ -504,10 +511,9 @@ async function requestHandler(req, res) {
|
|||
const model = openaiRequest.model;
|
||||
const geminiRequest = toGeminiRequest(openaiRequest);
|
||||
|
||||
if (PROMPT_LOG_MODE !== 'none') {
|
||||
const promptText = extractPromptText(geminiRequest); // Use geminiRequest for logging
|
||||
await logPrompt(promptText, PROMPT_LOG_MODE, PROMPT_LOG_FILENAME);
|
||||
}
|
||||
await manageSystemPrompt(geminiRequest); // Call the new function here
|
||||
const promptText = extractPromptText(geminiRequest); // Use geminiRequest for logging
|
||||
await logConversation('input', promptText, PROMPT_LOG_MODE, PROMPT_LOG_FILENAME);
|
||||
|
||||
if (openaiRequest.stream) {
|
||||
await handleStreamRequest(res, service, model, geminiRequest);
|
||||
|
|
|
|||
Loading…
Reference in a new issue