- Add helper functions to extract first image and generate description from chat messages
- Update base.html and index.html with og:image, og:description, twitter:card meta tags
- Chat rooms now use first image from messages for unfurling instead of site logo
- Description shows first 500 chars of room content for link previews
- Add default OG image (black with OC) as fallback when no image found
- is_base64_image() now detects any base64 image (jpeg, png, gif, webp, etc.)
not just the hardcoded jpeg/png patterns
- Added [Vision] debug logging to trace image fetch/save flow
URLs like eBay images contain % and _ which have special meaning in SQL
LIKE patterns. Added escape_like_pattern() helper to escape these chars.
This fixes saved base64 images not being found on refresh.
- Find the most recent image message
- Include full text conversation history for context
- Only send base64 for the most recent image
- Skip older images entirely (no useful text in them)
Multiple large base64 images can overwhelm the vision model. Now we:
1. First pass: identify the 2 most recent image messages
2. Second pass: build chat history, skipping older images
Remove race condition where syncDropdownsAndQueryString() was called before
dropdowns were populated, causing empty values to overwrite localStorage.
Model and voice restoration now happens only in populateModelDropdown() and
populateVoiceDropdown() after async fetch completes.
- Add find_saved_base64_for_url() to lookup existing saved base64 in DB
- Check for existing saved version before fetching external images
- Prevent duplicate saves in save_fetched_image_as_message()
- Use in-memory cache for faster repeated lookups within same session
- Fix Message.query.filter_by() chain mocking in test_bedrock_streaming_protocol
and test_streaming_protocol_backwards_compatibility
- Add is_gevent_patched() helper to detect monkey patching
- Skip bedrock test at runtime when gevent has already patched (avoids RecursionError)
- save_fetched_image_as_message() creates a new message with base64 content
- build_message_content() now accepts room_id to persist fetched images
- Fetched images saved with username "system" (added to SYSTEM_USERS)
- Prevents re-fetching: once fetched, base64 version is in chat history
- extract_external_image_url() to find http/https URLs in img tags
- fetch_external_image_as_base64() fetches via CORS proxy, converts to base64
- build_message_content() now handles both base64 and external image URLs
- Caches fetched images to avoid re-fetching
- Uses cors-proxy.uncloseai.com for robots.txt compliance
- Fetch external images through proxy.unturf.com (respects robots.txt)
- Convert fetched images to base64 for vision API
- Cache both fetched images and descriptions
- Shows "Fetching image..." for external URLs, "Generating description..." for base64
- Handles 403 responses when blocked by robots.txt
Backend:
- Track VISION_MODELS list at startup from available endpoints
- Add is_vision_model() to detect vision-capable models (*-vl*, *vision*, gpt-4o)
- Add extract_base64_from_img_tag() and build_message_content() helpers
- Modify chat_gpt() to include base64 images for vision models
- Add GET /vision endpoint for vision availability status
- Add POST /vision/describe endpoint for image alt-text generation
Frontend:
- Check vision availability on page load via /vision
- Add hover event delegation on chat images
- On hover: call vision model, cache result, set img.title and img.alt
- Shows cursor:wait while loading description
Update code execution error detection to use stderr exclusively
instead of checking both stdout and stderr. This aligns with the
Unsandbox API's proper stream separation where:
- stdout is for program output
- stderr is for errors/warnings
Changes:
- Remove stdout from error detection logic
- Only trigger auto-fix when exit_code != 0 AND stderr is non-empty
- Send only stderr to /api/fix-code endpoint
This prevents false positives where stdout contains normal output
that was previously being treated as error content.
- Use codeBlock.dataset.autoExecAttempt to pass attempt number
- executeCodeBlock transfers attempt to results container
- No longer pre-creates empty results container
- Clean up data attribute after use
- Add null check for classList before calling contains()
- Handles case where nextSibling might be a text node
- Prevents 'classList is undefined' error
- Correctly insert results container after button container
- Use buttonContainer.parentNode.insertBefore() instead of preElement
- Add proper styling when creating results container
- Fixes 'Child to insert before is not a child of this node' error
- Post fixed code to chat so user can see what was changed
- Store fix data in window.pendingAutoExec for message handler
- Auto-trigger execution on newly posted code block
- Preserve attempt counter across fix iterations
- User now sees: original error → fixed code posted → auto-execution → results
- Remove chat message posting that prevented automatic re-execution
- Fixed code now executes directly using same blockElement
- Simplify re-execution flow with direct recursive call
- Results display in same execution results container
Extract infrastructure/proxy debugging information from CLAUDE.md
into unturf-debugging.md to avoid exposing internal network details.
- Created unturf-debugging.md with full proxy chain documentation
- Added unturf-debugging.md to .gitignore
- Simplified CLAUDE.md to reference debugging doc
- Documented Caddy configuration (not nginx)
- Included troubleshooting steps for 502 errors
This keeps sensitive infrastructure details out of the public repo
while maintaining documentation for internal debugging.
Update /api/fix-code endpoint to try MODEL_3 (Qwen Coder) first for
better code generation, with graceful fallback to MODEL_1 (Hermes) if
MODEL_3 is unavailable or returns errors.
This ensures the auto-fix feature works reliably even when specialized
code models are temporarily unavailable (e.g., 502 errors).
Implements an AI-powered auto-fix system that automatically attempts to
repair code execution errors up to 3 times.
Backend changes (app.py):
- Add /api/fix-code endpoint that uses MODEL_1 (Hermes) to analyze
stderr output and generate corrected code
- System prompt instructs AI to output only raw fixed code without
explanations or markdown formatting
- Accepts code, language, stderr, exit_code, and attempt number
Frontend changes (templates/chat.html):
- Modify executeCodeBlock() to detect failed executions (exit_code !== 0)
- Track fix attempts per code block (max 3) using dataset attributes
- Call /api/fix-code when errors are detected
- Display fixed code as new chat message with markdown formatting
- Automatically re-execute the corrected code recursively
- Show progress messages during auto-fix attempts
- Display warning when max attempts (3) are exhausted
Features:
- Fixes common issues: missing imports, syntax errors, type errors
- Posts fixed code to chat for transparency
- Prevents infinite loops with 3-attempt limit
- Graceful error handling with user-friendly status messages
Add inline script at top of base.html <head> to set theme immediately
before any rendering occurs. Prevents white flash when loading chat
and other pages that extend base.html in dark mode.
- Chat messages: logged-in user's name links to /profile
- User lists (sidebar): logged-in user's name is clickable link
- Previous messages: same profile link logic applied
- Other users still link to /profile/username (future feature)
Anywhere the logged-in user's name appears, it now links to their
profile settings page for quick access to logout and username change.
- Add inline script to set theme before page render (browse, profile)
- Prevents white flash when loading pages in dark mode
- Add logout button with confirmation to profile page
- Username already clickable in browse page header (links to profile)
When SMTP_FROM_EMAIL is not set, automatically derive the sender domain from:
1. Flask request.host (if not localhost/127.0.0.1)
2. System FQDN hostname (socket.getfqdn())
3. SMTP_USER or fallback to noreply@opencompletion.local
This allows the app to use the correct sender domain (e.g., noreply@ai.foxhop.net)
when deployed on different hosts, ensuring proper email relay through mx servers.