|
|
import {LlamaText} from "node-llama-cpp";
|
|
|
import path from "path";
|
|
|
import fs from "fs/promises";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const OutputTypes = {
|
|
|
EXACT_PROMPT: 'exactPrompt',
|
|
|
CONTEXT_STATE: 'contextState',
|
|
|
STRUCTURED: 'structured'
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export class PromptDebugger {
|
|
|
constructor(options = {}) {
|
|
|
this.outputDir = options.outputDir || './';
|
|
|
this.filename = options.filename;
|
|
|
this.includeTimestamp = options.includeTimestamp ?? false;
|
|
|
this.appendMode = options.appendMode ?? false;
|
|
|
|
|
|
this.outputTypes = options.outputTypes || [OutputTypes.EXACT_PROMPT];
|
|
|
|
|
|
if (!Array.isArray(this.outputTypes)) {
|
|
|
this.outputTypes = [this.outputTypes];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
captureExactPrompt(params) {
|
|
|
const { session, prompt, systemPrompt, functions } = params;
|
|
|
|
|
|
const chatWrapper = session.chatWrapper;
|
|
|
|
|
|
|
|
|
const history = [{ type: 'user', text: prompt }];
|
|
|
|
|
|
if (systemPrompt) {
|
|
|
history.unshift({ type: 'system', text: systemPrompt });
|
|
|
}
|
|
|
|
|
|
|
|
|
const state = chatWrapper.generateContextState({
|
|
|
chatHistory: history,
|
|
|
availableFunctions: functions,
|
|
|
systemPrompt: systemPrompt
|
|
|
});
|
|
|
|
|
|
const formattedPrompt = state.contextText.toString();
|
|
|
|
|
|
return {
|
|
|
exactPrompt: formattedPrompt,
|
|
|
timestamp: new Date().toISOString(),
|
|
|
prompt,
|
|
|
systemPrompt,
|
|
|
functions: functions ? Object.keys(functions) : []
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
captureContextState(params) {
|
|
|
const { session, model } = params;
|
|
|
|
|
|
|
|
|
const contextState = model.detokenize(session.sequence.contextTokens, true);
|
|
|
|
|
|
return {
|
|
|
contextState,
|
|
|
timestamp: new Date().toISOString(),
|
|
|
tokenCount: session.sequence.contextTokens.length
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
captureStructured(params) {
|
|
|
const { session, model } = params;
|
|
|
|
|
|
const structured = LlamaText.fromTokens(model.tokenizer, session.sequence.contextTokens);
|
|
|
|
|
|
return {
|
|
|
structured,
|
|
|
timestamp: new Date().toISOString(),
|
|
|
tokenCount: session.sequence.contextTokens.length
|
|
|
};
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
captureAll(params) {
|
|
|
const result = {
|
|
|
timestamp: new Date().toISOString()
|
|
|
};
|
|
|
|
|
|
if (this.outputTypes.includes(OutputTypes.EXACT_PROMPT)) {
|
|
|
const exactData = this.captureExactPrompt(params);
|
|
|
result.exactPrompt = exactData.exactPrompt;
|
|
|
result.prompt = exactData.prompt;
|
|
|
result.systemPrompt = exactData.systemPrompt;
|
|
|
result.functions = exactData.functions;
|
|
|
}
|
|
|
|
|
|
if (this.outputTypes.includes(OutputTypes.CONTEXT_STATE)) {
|
|
|
const contextData = this.captureContextState(params);
|
|
|
result.contextState = contextData.contextState;
|
|
|
result.contextTokenCount = contextData.tokenCount;
|
|
|
}
|
|
|
|
|
|
if (this.outputTypes.includes(OutputTypes.STRUCTURED)) {
|
|
|
const structuredData = this.captureStructured(params);
|
|
|
result.structured = structuredData.structured;
|
|
|
result.structuredTokenCount = structuredData.tokenCount;
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
formatOutput(capturedData) {
|
|
|
let output = `\n========== PROMPT DEBUG OUTPUT ==========\n`;
|
|
|
output += `Timestamp: ${capturedData.timestamp}\n`;
|
|
|
|
|
|
if (capturedData.prompt) {
|
|
|
output += `Original Prompt: ${capturedData.prompt}\n`;
|
|
|
}
|
|
|
|
|
|
if (capturedData.systemPrompt) {
|
|
|
output += `System Prompt: ${capturedData.systemPrompt.substring(0, 50)}...\n`;
|
|
|
}
|
|
|
|
|
|
if (capturedData.functions && capturedData.functions.length > 0) {
|
|
|
output += `Functions: ${capturedData.functions.join(', ')}\n`;
|
|
|
}
|
|
|
|
|
|
if (capturedData.exactPrompt) {
|
|
|
output += `\n=== EXACT PROMPT ===\n`;
|
|
|
output += capturedData.exactPrompt;
|
|
|
output += `\n`;
|
|
|
}
|
|
|
|
|
|
if (capturedData.contextState) {
|
|
|
output += `Token Count: ${capturedData.contextTokenCount || 'N/A'}\n`;
|
|
|
|
|
|
output += `\n=== CONTEXT STATE ===\n`;
|
|
|
output += capturedData.contextState;
|
|
|
output += `\n`;
|
|
|
}
|
|
|
|
|
|
if (capturedData.structured) {
|
|
|
output += `\n=== STRUCTURED ===\n`;
|
|
|
output += `Token Count: ${capturedData.structuredTokenCount || 'N/A'}\n`;
|
|
|
output += JSON.stringify(capturedData.structured, null, 2);
|
|
|
output += `\n`;
|
|
|
}
|
|
|
|
|
|
output += `==========================================\n`;
|
|
|
return output;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async saveToFile(capturedData, customFilename = null) {
|
|
|
const content = this.formatOutput(capturedData);
|
|
|
|
|
|
let filename = customFilename || this.filename;
|
|
|
|
|
|
if (this.includeTimestamp) {
|
|
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
|
const ext = path.extname(filename);
|
|
|
const base = path.basename(filename, ext);
|
|
|
filename = `${base}_${timestamp}${ext}`;
|
|
|
}
|
|
|
|
|
|
const filepath = path.join(this.outputDir, filename);
|
|
|
|
|
|
if (this.appendMode) {
|
|
|
await fs.appendFile(filepath, content, 'utf8');
|
|
|
} else {
|
|
|
await fs.writeFile(filepath, content, 'utf8');
|
|
|
}
|
|
|
|
|
|
console.log(`Prompt debug output written to ${filepath}`);
|
|
|
return filepath;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async debugExactPrompt(params, customFilename = null) {
|
|
|
const oldOutputTypes = this.outputTypes;
|
|
|
this.outputTypes = [OutputTypes.EXACT_PROMPT];
|
|
|
const capturedData = this.captureAll(params);
|
|
|
const filepath = await this.saveToFile(capturedData, customFilename);
|
|
|
this.outputTypes = oldOutputTypes;
|
|
|
return { capturedData, filepath };
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async debugContextState(params, customFilename = null) {
|
|
|
const oldOutputTypes = this.outputTypes;
|
|
|
this.outputTypes = [OutputTypes.CONTEXT_STATE];
|
|
|
const capturedData = this.captureAll(params);
|
|
|
const filepath = await this.saveToFile(capturedData, customFilename);
|
|
|
this.outputTypes = oldOutputTypes;
|
|
|
return { capturedData, filepath };
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async debugStructured(params, customFilename = null) {
|
|
|
const oldOutputTypes = this.outputTypes;
|
|
|
this.outputTypes = [OutputTypes.STRUCTURED];
|
|
|
const capturedData = this.captureAll(params);
|
|
|
const filepath = await this.saveToFile(capturedData, customFilename);
|
|
|
this.outputTypes = oldOutputTypes;
|
|
|
return { capturedData, filepath };
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async debug(params, customFilename = null) {
|
|
|
const capturedData = this.captureAll(params);
|
|
|
|
|
|
return { capturedData };
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logToConsole(params) {
|
|
|
const capturedData = this.captureAll(params);
|
|
|
console.log(this.formatOutput(capturedData));
|
|
|
return capturedData;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logExactPrompt(params) {
|
|
|
const capturedData = this.captureExactPrompt(params);
|
|
|
console.log(this.formatOutput(capturedData));
|
|
|
return capturedData;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logContextState(params) {
|
|
|
const capturedData = this.captureContextState(params);
|
|
|
console.log(this.formatOutput(capturedData));
|
|
|
return capturedData;
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logStructured(params) {
|
|
|
const capturedData = this.captureStructured(params);
|
|
|
console.log(this.formatOutput(capturedData));
|
|
|
return capturedData;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function debugExactPrompt(params, options = {}) {
|
|
|
const promptDebugger = new PromptDebugger({
|
|
|
...options,
|
|
|
outputTypes: [OutputTypes.EXACT_PROMPT]
|
|
|
});
|
|
|
return await promptDebugger.debug(params);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function debugContextState(params, options = {}) {
|
|
|
const promptDebugger = new PromptDebugger({
|
|
|
...options,
|
|
|
outputTypes: [OutputTypes.CONTEXT_STATE]
|
|
|
});
|
|
|
return await promptDebugger.debug(params);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function debugStructured(params, options = {}) {
|
|
|
const promptDebugger = new PromptDebugger({
|
|
|
...options,
|
|
|
outputTypes: [OutputTypes.STRUCTURED]
|
|
|
});
|
|
|
return await promptDebugger.debug(params);
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async function debugAll(params, options = {}) {
|
|
|
const promptDebugger = new PromptDebugger({
|
|
|
...options,
|
|
|
outputTypes: [OutputTypes.EXACT_PROMPT, OutputTypes.CONTEXT_STATE, OutputTypes.STRUCTURED]
|
|
|
});
|
|
|
return await promptDebugger.debug(params);
|
|
|
} |