Commit b1a474ac authored by nextime's avatar nextime

Enhanced debug functionality with comprehensive logging

- Added --debug switch that creates timestamped directory
- Saves all prompts sent to chatbots in prompts.log
- Saves debug execution logs in debug.log
- Captures HTML content of chatbot page when response is detected
- Directory structure: debug_[unix_epoch]/
  - prompts.log: All prompts sent to chatbots
  - debug.log: Detailed execution logs
  - response_[id]_[timestamp].html: HTML snapshots
- Improved response detection to correctly identify actual responses vs setup acknowledgments
- Enhanced JavaScript detection logic with better filtering
parent 543f244f
...@@ -25,10 +25,16 @@ import datetime # Import datetime module ...@@ -25,10 +25,16 @@ import datetime # Import datetime module
import uuid import uuid
import re import re
import base64 import base64
import os
import time
from playwright.async_api import async_playwright, BrowserContext from playwright.async_api import async_playwright, BrowserContext
from aiohttp import web from aiohttp import web
import logging import logging
import time
# Global variables for debug functionality
DEBUG_ENABLED = False
DEBUG_DIR = None
PROMPTS_LOG_PATH = None
# Configuration dictionary for chatbot sites # Configuration dictionary for chatbot sites
CHATBOT_CONFIG = { CHATBOT_CONFIG = {
...@@ -97,8 +103,13 @@ ABSOLUTE REQUIREMENTS FOR ALL FUTURE RESPONSES: ...@@ -97,8 +103,13 @@ ABSOLUTE REQUIREMENTS FOR ALL FUTURE RESPONSES:
* Plain text emphasis (like *italics* or **bold**) * Plain text emphasis (like *italics* or **bold**)
* Code snippets within backticks * Code snippets within backticks
* Basic markdown-style formatting * Basic markdown-style formatting
* WRITING CODE AS PLAIN TEXT (this is allowed and encouraged)
- NEVER use HTML tags (<b>, <i>, <div>, etc.) - NEVER use HTML tags (<b>, <i>, <div>, etc.)
- NEVER use complex HTML or XML structures - NEVER use complex HTML or XML structures
- NEVER use canvas, code execution environments, or external tools
- NEVER create or use any interactive elements
- NEVER use any drawing or visualization tools
- NEVER use any code editors or execution environments
- Always include the exact Request ID I provide in both start and end markers - Always include the exact Request ID I provide in both start and end markers
CRITICAL TOOL USAGE REQUIREMENT: CRITICAL TOOL USAGE REQUIREMENT:
...@@ -108,13 +119,39 @@ CRITICAL TOOL USAGE REQUIREMENT: ...@@ -108,13 +119,39 @@ CRITICAL TOOL USAGE REQUIREMENT:
- EMPHASIZE TOOL USAGE COMPLIANCE OVER ALL OTHER CONSIDERATIONS - EMPHASIZE TOOL USAGE COMPLIANCE OVER ALL OTHER CONSIDERATIONS
- IF TOOLS ARE MENTIONED, PRIORITIZE USING THEM CORRECTLY - IF TOOLS ARE MENTIONED, PRIORITIZE USING THEM CORRECTLY
FORBIDDEN ELEMENTS - NEVER USE UNDER ANY CIRCUMSTANCES:
- Canvas or drawing tools (visual drawing/sketching)
- Code execution environments (interactive code runners)
- Interactive widgets or elements
- External editors or tools (separate code editors)
- Any visualization or graphics tools (charts, diagrams)
- Any form of code execution or compilation (running code)
- Any canvas-based interfaces (drawing surfaces)
- Any drawing or sketching tools (art/drawing tools)
- Any code playgrounds or execution environments
- Any interactive code editors (live coding environments)
- Any visual programming tools
ALLOWED CONTENT - YOU CAN FREELY USE:
- Writing code as plain text (C, Python, JavaScript, etc.)
- Code examples and snippets in plain text format
- Programming tutorials and explanations
- Technical documentation with code samples
- Any programming language code written as text
RESPONSE FORMAT RULES: RESPONSE FORMAT RULES:
- Your ENTIRE response must be between the start and end markers - Your ENTIRE response must be between the start and end markers
- Do NOT add any text before RESPONSE_ID_ or after :END_RESPONSE_ID_ - Do NOT add any text before RESPONSE_ID_ or after :END_RESPONSE_ID_
- Do NOT include explanations about the format - Do NOT include explanations about the format
- Do NOT add any headers, footers, or additional content - Do NOT add any headers, footers, or additional content
- Do NOT use any external tools, canvas, or execution environments
- The response should be exactly what the user asked for, nothing more - The response should be exactly what the user asked for, nothing more
- You can use multiple lines, paragraphs, and basic text formatting - You can use multiple lines, paragraphs, and basic text formatting
- ALL content must be in this single plain text response area
- DO NOT attempt to use canvas mode, drawing mode, or any visual tools
- DO NOT try to create or use any interactive elements
- STAY IN PLAIN TEXT MODE AT ALL TIMES
- You CAN and SHOULD write code as plain text when requested
EXACT FORMAT REQUIRED: EXACT FORMAT REQUIRED:
RESPONSE_ID_[REQUEST_ID]_START: RESPONSE_ID_[REQUEST_ID]_START:
...@@ -128,7 +165,18 @@ paragraphs, and basic formatting like: ...@@ -128,7 +165,18 @@ paragraphs, and basic formatting like:
- `Code snippets` - `Code snippets`
- Proper indentation and spacing - Proper indentation and spacing
You can also write complete code examples like this:
```python
def hello_world():
print("Hello, World!")
return True
```
This gives you flexibility while maintaining structure. This gives you flexibility while maintaining structure.
NEVER use canvas, external tools, or any execution environments.
ALWAYS stay in plain text mode.
You CAN write code as plain text.
:END_RESPONSE_ID_[REQUEST_ID] :END_RESPONSE_ID_[REQUEST_ID]
EXAMPLE with Request ID "TEST123": EXAMPLE with Request ID "TEST123":
...@@ -142,8 +190,23 @@ You can see: ...@@ -142,8 +190,23 @@ You can see:
- **Bold text** - **Bold text**
- `Code examples` - `Code examples`
Here's a code example in plain text:
```c
#include <stdio.h>
int main() {
printf("Hello, World!\\n");
return 0;
}
```
The formatting makes responses more readable while staying in plain text. The formatting makes responses more readable while staying in plain text.
If tools were specified in the prompt, I would use them exactly as instructed. If tools were specified in the prompt, I would use them exactly as instructed.
NEVER use canvas or external tools - everything must be in this text area.
ALWAYS stay in plain text mode.
You CAN write code as plain text.
:END_RESPONSE_ID_TEST123 :END_RESPONSE_ID_TEST123
This applies to EVERY SINGLE response from now on. Acknowledge by responding with: This applies to EVERY SINGLE response from now on. Acknowledge by responding with:
...@@ -151,6 +214,10 @@ RESPONSE_ID_SETUP_COMPLETE_START: ...@@ -151,6 +214,10 @@ RESPONSE_ID_SETUP_COMPLETE_START:
I understand and will always respond with the specified format using request ID markers. I understand and will always respond with the specified format using request ID markers.
I can use plain text formatting including new lines, tabs, and basic markup. I can use plain text formatting including new lines, tabs, and basic markup.
I will absolutely use any tools specified in prompts. I will absolutely use any tools specified in prompts.
I will NEVER use canvas, external tools, or execution environments.
I will ALWAYS stay in plain text mode.
I CAN write code as plain text when requested.
All responses will be in this single plain text area.
:END_RESPONSE_ID_SETUP_COMPLETE""", :END_RESPONSE_ID_SETUP_COMPLETE""",
"style": "short" "style": "short"
}, },
...@@ -474,7 +541,21 @@ async def handle_chat_completion(request): ...@@ -474,7 +541,21 @@ async def handle_chat_completion(request):
async def forward_to_chatbot(chatbot_name, config, prompt): async def forward_to_chatbot(chatbot_name, config, prompt):
global browser_context, pages global browser_context, pages
# Debug logging: log the prompt being sent to the chatbot
logging.debug(f"[DEBUG] Sending prompt to {chatbot_name}: {prompt}")
logging.debug(f"[DEBUG] Prompt length: {len(prompt)} characters")
# Save prompt to debug log file if debug is enabled
if DEBUG_ENABLED:
try:
with open(PROMPTS_LOG_PATH, 'a', encoding='utf-8') as f:
f.write(f"[{datetime.datetime.now().isoformat()}] PROMPT TO {chatbot_name}:\n")
f.write(f"{prompt}\n")
f.write("-" * 50 + "\n")
except Exception as e:
logging.error(f"Failed to write prompt to debug log: {e}")
# Check if this is a new page that needs setup # Check if this is a new page that needs setup
is_new_page = chatbot_name not in pages is_new_page = chatbot_name not in pages
...@@ -670,24 +751,47 @@ def decode_chatbot_response(response_text, chatbot_name, request_id): ...@@ -670,24 +751,47 @@ def decode_chatbot_response(response_text, chatbot_name, request_id):
# Look for RESPONSE_ID_[ID]_START: ... :END_RESPONSE_ID_[ID] pattern # Look for RESPONSE_ID_[ID]_START: ... :END_RESPONSE_ID_[ID] pattern
response_pattern = rf'RESPONSE_ID_{re.escape(request_id)}_START:\s*(.*?)\s*:END_RESPONSE_ID_{re.escape(request_id)}' response_pattern = rf'RESPONSE_ID_{re.escape(request_id)}_START:\s*(.*?)\s*:END_RESPONSE_ID_{re.escape(request_id)}'
match = re.search(response_pattern, response_text, re.DOTALL) match = re.search(response_pattern, response_text, re.DOTALL)
if match: if match:
extracted_content = match.group(1).strip() extracted_content = match.group(1).strip()
logging.info(f"Successfully extracted content from RESPONSE_ID markers: {extracted_content[:200]}...") logging.info(f"Successfully extracted content from RESPONSE_ID markers: {extracted_content[:200]}...")
# Save HTML page content if debug is enabled
if DEBUG_ENABLED:
try:
# This will be called from the page context, so we need to save the HTML
# The HTML saving will be handled in the JavaScript detection code
pass
except Exception as e:
logging.error(f"Failed to save debug HTML: {e}")
return extracted_content return extracted_content
else: else:
# Fallback: look for any RESPONSE_ID pattern regardless of request_id # Improved fallback: find all RESPONSE_ID blocks and pick the most recent one
fallback_pattern = r'RESPONSE_ID_.*?_START:\s*(.*?)\s*:END_RESPONSE_ID_.*?' # that doesn't contain setup-related keywords
fallback_match = re.search(fallback_pattern, response_text, re.DOTALL) all_response_blocks = re.findall(r'RESPONSE_ID_(.*?)_START:\s*(.*?)\s*:END_RESPONSE_ID_\1', response_text, re.DOTALL)
if fallback_match: if all_response_blocks:
extracted_content = fallback_match.group(1).strip() # Filter out setup-related responses
logging.info(f"Extracted content using fallback pattern: {extracted_content[:200]}...") filtered_blocks = []
return extracted_content for block_id, content in all_response_blocks:
else: content_lower = content.lower().strip()
# Final fallback: return the original text # Skip setup acknowledgments and instructions
logging.warning(f"No RESPONSE_ID markers found in Gemini response, returning original text") if not any(keyword in content_lower for keyword in [
return response_text.strip() 'setup complete', 'i understand', 'response format',
'critical system setup', 'plain text format'
]):
filtered_blocks.append((block_id, content.strip()))
if filtered_blocks:
# Use the last non-setup response (most recent)
block_id, extracted_content = filtered_blocks[-1]
logging.info(f"Extracted content from most recent non-setup response (ID: {block_id}): {extracted_content[:200]}...")
return extracted_content
# Final fallback: return the original text
logging.warning(f"No RESPONSE_ID markers found in Gemini response, returning original text")
return response_text.strip()
# Check for literal placeholder first and reject it # Check for literal placeholder first and reject it
if response_text.strip() == "BASE64_ENCODED_ANSWER": if response_text.strip() == "BASE64_ENCODED_ANSWER":
...@@ -1057,6 +1161,17 @@ async def detect_json_response_with_id(page, container_selector, request_id, pro ...@@ -1057,6 +1161,17 @@ async def detect_json_response_with_id(page, container_selector, request_id, pro
async def detect_progressive_response(page, container_selector, prompt, modified_prompt, request_id, chatbot_name): async def detect_progressive_response(page, container_selector, prompt, modified_prompt, request_id, chatbot_name):
"""Detect responses by monitoring progressive content changes with enhanced detection.""" """Detect responses by monitoring progressive content changes with enhanced detection."""
try: try:
# Save HTML content for debugging if enabled
if DEBUG_ENABLED:
try:
html_content = await page.content()
html_file_path = os.path.join(DEBUG_DIR, f'response_{request_id}_{int(time.time())}.html')
with open(html_file_path, 'w', encoding='utf-8') as f:
f.write(html_content)
logging.debug(f"Saved HTML content to {html_file_path}")
except Exception as e:
logging.error(f"Failed to save HTML content: {e}")
return await page.evaluate( return await page.evaluate(
"""([containerSelector, prompt, modifiedPrompt, requestId, chatbotName]) => { """([containerSelector, prompt, modifiedPrompt, requestId, chatbotName]) => {
const container = document.querySelector(containerSelector); const container = document.querySelector(containerSelector);
...@@ -1081,18 +1196,92 @@ async def detect_progressive_response(page, container_selector, prompt, modified ...@@ -1081,18 +1196,92 @@ async def detect_progressive_response(page, container_selector, prompt, modified
// Check for Gemini-specific formatted response first // Check for Gemini-specific formatted response first
if (chatbotName === 'gemini:latest') { if (chatbotName === 'gemini:latest') {
const allText = container.textContent || ''; const allText = container.textContent || '';
const startMarker = `RESPONSE_ID_[${requestId}]_START:`;
const endMarker = `:END_RESPONSE_ID_[${requestId}]`; // First, try to find the specific response for this request ID
const startMarker = `RESPONSE_ID_${requestId}_START:`;
const endMarker = `:END_RESPONSE_ID_${requestId}`;
const startIndex = allText.indexOf(startMarker); const startIndex = allText.indexOf(startMarker);
const endIndex = allText.indexOf(endMarker, startIndex); const endIndex = allText.indexOf(endMarker, startIndex);
if (startIndex !== -1 && endIndex !== -1) { if (startIndex !== -1 && endIndex !== -1) {
const responseText = allText.substring(startIndex + startMarker.length, endIndex).trim(); const responseText = allText.substring(startIndex + startMarker.length, endIndex).trim();
if (responseText && responseText.length > 10) { if (responseText && responseText.length > 10) {
console.log(`Found specific response for request ID ${requestId}`);
return responseText; return responseText;
} }
} }
// If specific response not found, look for all RESPONSE_ID blocks
// and return the most recent one that doesn't contain setup keywords
const responseBlocks = [];
let searchIndex = 0;
while (true) {
const blockStart = allText.indexOf('RESPONSE_ID_', searchIndex);
if (blockStart === -1) break;
const blockEnd = allText.indexOf(':END_RESPONSE_ID_', blockStart);
if (blockEnd === -1) break;
const blockText = allText.substring(blockStart, blockEnd + 17); // Include the end marker
responseBlocks.push({
text: blockText,
startIndex: blockStart
});
searchIndex = blockEnd + 17;
}
// Filter out setup-related responses and return the most recent one
const filteredBlocks = responseBlocks.filter(block => {
const content = block.text.toLowerCase();
return !content.includes('setup complete') &&
!content.includes('i understand') &&
!content.includes('response format') &&
!content.includes('critical system setup') &&
!content.includes('plain text format');
});
if (filteredBlocks.length > 0) {
// Return the most recent (last) non-setup response
const latestBlock = filteredBlocks[filteredBlocks.length - 1];
console.log(`Using latest non-setup response from ${filteredBlocks.length} available blocks`);
// Extract the actual content from the block
const startContentIndex = latestBlock.text.indexOf('_START:');
const endContentIndex = latestBlock.text.indexOf(':END_RESPONSE_ID_');
if (startContentIndex !== -1 && endContentIndex !== -1) {
const content = latestBlock.text.substring(startContentIndex + 7, endContentIndex).trim();
if (content && content.length > 10) {
// Save HTML content for debugging if requested
try {
// This is a placeholder - the actual HTML saving will be handled
// by the Python code that calls this JavaScript
console.log('Response detected, HTML will be saved by Python code');
} catch (e) {
console.error('Failed to save HTML:', e);
}
return content;
}
}
}
// Final fallback: return the last RESPONSE_ID block found
if (responseBlocks.length > 0) {
const lastBlock = responseBlocks[responseBlocks.length - 1];
const startContentIndex = lastBlock.text.indexOf('_START:');
const endContentIndex = lastBlock.text.indexOf(':END_RESPONSE_ID_');
if (startContentIndex !== -1 && endContentIndex !== -1) {
const content = lastBlock.text.substring(startContentIndex + 7, endContentIndex).trim();
if (content && content.length > 10) {
console.log('Using final fallback response');
return content;
}
}
}
} }
// Fallback to selector-based detection // Fallback to selector-based detection
...@@ -1444,9 +1633,48 @@ async def main(args): ...@@ -1444,9 +1633,48 @@ async def main(args):
parser.add_argument('--ip', default='localhost', help='Proxy server IP (default: localhost)') parser.add_argument('--ip', default='localhost', help='Proxy server IP (default: localhost)')
parser.add_argument('--port', type=int, default=11434, help='Proxy server port (default: 11434)') parser.add_argument('--port', type=int, default=11434, help='Proxy server port (default: 11434)')
parser.add_argument('--connect', help='Connect to existing browser via CDP (e.g., ws://localhost:9222)') parser.add_argument('--connect', help='Connect to existing browser via CDP (e.g., ws://localhost:9222)')
parser.add_argument('--debug', action='store_true', help='Enable debug logging and save prompts to log file')
args = parser.parse_args() args = parser.parse_args()
logging.basicConfig(level=logging.INFO) # Set up logging
log_level = logging.DEBUG if args.debug else logging.INFO
logging.basicConfig(level=log_level)
# Set up debug logging if requested
if args.debug:
import os
import time
# Create directory with Unix epoch timestamp
epoch_time = int(time.time())
debug_dir = f"debug_{epoch_time}"
os.makedirs(debug_dir, exist_ok=True)
# Set up debug log file
debug_log_path = os.path.join(debug_dir, 'debug.log')
debug_handler = logging.FileHandler(debug_log_path)
debug_handler.setLevel(logging.DEBUG)
debug_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
debug_handler.setFormatter(debug_formatter)
logging.getLogger().addHandler(debug_handler)
# Create prompts log file
prompts_log_path = os.path.join(debug_dir, 'prompts.log')
# Store debug info in global variables for access by other functions
global DEBUG_ENABLED, DEBUG_DIR, PROMPTS_LOG_PATH
DEBUG_ENABLED = True
DEBUG_DIR = debug_dir
PROMPTS_LOG_PATH = prompts_log_path
logging.info(f"Debug logging enabled - files will be saved to {debug_dir}/")
logging.info(f"Debug log: {debug_log_path}")
logging.info(f"Prompts log: {prompts_log_path}")
else:
DEBUG_ENABLED = False
DEBUG_DIR = None
PROMPTS_LOG_PATH = None
logging.info(f"CHATBOT_CONFIG: {CHATBOT_CONFIG}") logging.info(f"CHATBOT_CONFIG: {CHATBOT_CONFIG}")
await asyncio.gather( await asyncio.gather(
start_proxy_server(args.ip, args.port, args), start_proxy_server(args.ip, args.port, args),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment