diff --git a/CLAUDE.md b/CLAUDE.md index 7138bd6..4340c62 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,3 +28,50 @@ ## Makefile Best Practices - Avoid variable substitutions - don't be afraid to be unDRY in the Makefile so engineers can copy and paste - Use tabs not spaces, and for fuck sake be happy about it + +## Running OpenCompletion + +### Environment Setup +- Use `vars.sh` to set up environment variables +- Required: MODEL_ENDPOINT_x and MODEL_API_KEY_x variables for AI models +- Run with: `source vars.sh && python app.py` + +### Makefile Commands +- `make venv` - Create virtual environment and install dependencies +- `make init-db` - Initialize database tables +- `make test` - Run all tests +- `make dev-setup` - Install development dependencies + +## OpenCompletion Architecture + +### Frontend Structure +- Main chat interface is in `templates/chat.html` +- Base template with CSS is in `templates/base.html` +- JavaScript code is inline in chat.html for real-time chat functionality +- Uses Socket.IO for WebSocket communication +- Uses marked.js for Markdown rendering and DOMPurify for XSS protection +- Code blocks are rendered with highlight.js for syntax highlighting + +### Code Block Rendering +- Code blocks are processed in messages after markdown conversion +- Copy buttons are added via `addCopyButtonToCodeBlock()` function (line 1176 in chat.html) +- Code blocks support: + - Syntax highlighting via highlight.js + - Line numbers via `addLineNumbers()` function + - Truncation for long code blocks via `truncateCodeBlock()` function + - Copy functionality that preserves full content even when truncated + +### Message Processing Flow +1. Messages received via Socket.IO events (chat_message, message_chunk for streaming) +2. Markdown converted to HTML using marked.js +3. HTML sanitized with DOMPurify +4. Code blocks enhanced with copy buttons, syntax highlighting, and line numbers + +### Code Execution Integration (New) +- Integration with unfirecracker-code-executor service on cammy.foxhop.net +- Service supports 38 working languages with auto-detection +- Endpoints: + - `/execute` - Execute code with specified language + - `/run` - Auto-detect language and execute +- Add play button next to copy button for code blocks +- Display execution results inline below code blocks diff --git a/templates/chat.html b/templates/chat.html index 856e861..f00e44c 100644 --- a/templates/chat.html +++ b/templates/chat.html @@ -85,6 +85,7 @@ // Constants const API_KEY = "dummy-api-key"; const TTS_API_URL = "https://speech.ai.unturf.com/v1/audio/speech"; +const CODE_EXEC_URL = "https://cammy-black.foxhop.net"; // Code execution service URL (served via Caddy) const urlParams = new URLSearchParams(window.location.search); let username = urlParams.get("username") || "guest"; // Default to "guest" if no username in URL @@ -1177,6 +1178,13 @@ function addCopyButtonToCodeBlock(block) { // Check if the full content is stored in a data attribute, otherwise use textContent const contentToCopy = block.dataset.fullContent || block.textContent; + // Create a container for the buttons + const buttonContainer = document.createElement('div'); + buttonContainer.classList.add('code-block-button-container'); + buttonContainer.style.display = 'flex'; + buttonContainer.style.gap = '8px'; + buttonContainer.style.marginBottom = '8px'; + // Create a button to copy the code block's content const copyButton = document.createElement('button'); copyButton.textContent = 'Copy'; @@ -1194,8 +1202,142 @@ function addCopyButtonToCodeBlock(block) { }); }; - // Insert the button before the code block - block.parentNode.insertBefore(copyButton, block); + // Create a button to execute the code block's content + const playButton = document.createElement('button'); + playButton.textContent = '▶ Run'; + playButton.classList.add('play-button'); + playButton.onclick = function() { + executeCodeBlock(contentToCopy, block, playButton); + }; + + // Add buttons to container + buttonContainer.appendChild(copyButton); + buttonContainer.appendChild(playButton); + + // Insert the button container before the code block + block.parentNode.insertBefore(buttonContainer, block); +} + +// Function to execute code block content +async function executeCodeBlock(code, blockElement, playButton) { + // Update button state + playButton.textContent = 'Running...'; + playButton.disabled = true; + + // Check if we already have a results container + let resultsContainer = blockElement.parentNode.querySelector('.code-execution-results'); + if (!resultsContainer) { + // Create results container + resultsContainer = document.createElement('div'); + resultsContainer.classList.add('code-execution-results'); + resultsContainer.style.marginTop = '10px'; + resultsContainer.style.padding = '10px'; + resultsContainer.style.backgroundColor = '#f0f0f0'; + resultsContainer.style.borderRadius = '5px'; + resultsContainer.style.fontFamily = 'monospace'; + resultsContainer.style.fontSize = '14px'; + resultsContainer.style.whiteSpace = 'pre-wrap'; + resultsContainer.style.wordWrap = 'break-word'; + + // Insert after the code block + blockElement.parentNode.insertBefore(resultsContainer, blockElement.nextSibling); + } + + // Clear previous results + resultsContainer.innerHTML = '
print("Hello from Python!")
+for i in range(3):
+ print(f"Count: {i}")
+
+
+ console.log("Hello from JavaScript!");
+const arr = [1, 2, 3];
+arr.forEach(n => console.log(`Number: ${n}`));
+
+
+ puts "Hello from Ruby!"
+3.times do |i|
+ puts "Iteration #{i}"
+end
+
+
+ package main
+import "fmt"
+func main() {
+ fmt.Println("Hello from Go!")
+}
+
+
+ #include <iostream>
+using namespace std;
+int main() {
+ cout << "Hello from C++!" << endl;
+ return 0;
+}
+
+
+