From d0b22b0f15a2de477ccbce2f8f629ae5a74cfeed Mon Sep 17 00:00:00 2001 From: Shanoah Alkire Date: Tue, 14 Oct 2025 10:58:59 -0700 Subject: [PATCH] Add missing docs, still needing closer checking. --- README.md | 27 +- docs/API.md | 866 +++++++++++++++++++++++++ docs/ARCHITECTURE.md | 1186 ++++++++++++++++++++++++++++++++++ docs/LLM_TAB_GUIDE.md | 550 ++++++++++++++++ docs/PROMPT_BUILDER_GUIDE.md | 1082 +++++++++++++++++++++++++++++++ js/file/modelBrowser.js | 28 +- js/sidebar/modelsTabV2.js | 22 +- 7 files changed, 3745 insertions(+), 16 deletions(-) create mode 100644 docs/API.md create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/LLM_TAB_GUIDE.md create mode 100644 docs/PROMPT_BUILDER_GUIDE.md diff --git a/README.md b/README.md index c156ad8..2bbb9c5 100644 --- a/README.md +++ b/README.md @@ -15,20 +15,25 @@ The node suite supports A1111/Civitai metadata formats, while the UI features pr ### LLM Chat Tab Access AI language models directly within ComfyUI for prompt generation, refinement, and creative assistance: -- **Multi-Provider Support**: OpenAI, Anthropic, OpenRouter, Google, LM Studio, Ollama, and custom endpoints +- **Multi-Provider Support**: LM Studio and Ollama (local providers for privacy and unlimited usage) - **Vision Capabilities**: Upload up to 10 images for vision-enabled models with drag-and-drop support - **Streaming Responses**: Real-time text generation with SSE (Server-Sent Events) - **Conversation History**: Maintain context across multiple exchanges -- **Advanced Options**: Temperature, top-p, max tokens, presence/frequency penalties, system prompts +- **Preset System**: Save and reuse complete LLM configurations (model, settings, system prompts) +- **System Prompt Management**: Create, save, and manage custom system prompts +- **Advanced Options**: Temperature, top-p, max tokens, presence/frequency penalties, keep-alive, system prompts - **Keyboard Shortcuts**: Ctrl+Enter to send, Escape to blur textareas - **Accessibility**: Full screen reader support with ARIA labels πŸ“– **[Complete LLM Tab Guide](docs/LLM_TAB_GUIDE.md)** ### Prompt Builder Tab -Tag-based system for constructing complex prompts with LLM enhancement: +Wildcard and tag-based system for constructing complex prompts with LLM enhancement: -- **Tag Categories**: Character, setting, style, quality, camera, lighting, and more +- **Wildcard System**: Use `__category__` syntax for dynamic, randomized prompt generation +- **Tag Library**: Pre-organized tag collections across multiple categories for quick insertion +- **Saved Prompts**: Save and manage complete prompt collections with descriptions +- **Seed-Based Generation**: Control randomization with fixed or random seeds for reproducibility - **LLM Integration**: Send prompts to LLM for expansion, refinement, or creative variations - **Cross-Tab Messaging**: Receive enhanced prompts back from LLM tab automatically - **Positive/Negative Prompts**: Separate construction for better control @@ -120,12 +125,13 @@ Seamless data flow between all components: ### Setting Up LLM Integration -1. **Choose your LLM provider** (OpenAI, Anthropic, LM Studio, Ollama, etc.) -2. **Configure API credentials**: - - For cloud providers: Add API key in LLM tab settings - - For local providers (LM Studio/Ollama): Ensure service is running -3. **Select a model** from the dropdown -4. **Start chatting** or use vision features by uploading images +1. **Choose your LLM provider** (LM Studio or Ollama) +2. **Install and configure**: + - **LM Studio**: Download from [lmstudio.ai](https://lmstudio.ai/), load a model, start local server + - **Ollama**: Install from [ollama.ai](https://ollama.ai/), pull a model with `ollama pull llama3.2` +3. **Select provider** in LLM tab dropdown +4. **Select a model** from the available models list +5. **Start chatting** or use vision features by uploading images See the [LLM Tab Guide](docs/LLM_TAB_GUIDE.md) for detailed setup instructions for each provider. @@ -175,7 +181,6 @@ Example workflows are available in the `example_workflows/` folder. In ComfyUI, - **ComfyUI**: Latest version recommended - **Python**: 3.9+ (included with ComfyUI) - **Optional LLM Providers**: - - Cloud APIs: OpenAI, Anthropic, Google, OpenRouter (require API keys) - Local: LM Studio, Ollama (free, run locally) ### Installation diff --git a/docs/API.md b/docs/API.md new file mode 100644 index 0000000..0438c42 --- /dev/null +++ b/docs/API.md @@ -0,0 +1,866 @@ +# API Documentation + +Complete API reference for Sage Utils backend endpoints. + +## Table of Contents + +1. [Overview](#overview) +2. [LLM Endpoints](#llm-endpoints) +3. [Response Formats](#response-formats) +4. [Error Handling](#error-handling) +5. [SSE Streaming](#sse-streaming) +6. [Integration Examples](#integration-examples) + +--- + +## Overview + +### Base URL + +All endpoints are relative to your ComfyUI server: + +``` +http://localhost:8188/api +``` + +For custom ports or remote servers, adjust accordingly. + +### Authentication + +Most endpoints don't require authentication as they're accessed through the ComfyUI UI. If you're integrating externally, ensure you have network access to the ComfyUI server. + +### Content Types + +**Request**: +- `Content-Type: application/json` for POST endpoints +- No content type needed for GET endpoints + +**Response**: +- `Content-Type: application/json` for standard responses +- `Content-Type: text/event-stream` for SSE streaming endpoints + +--- + +## LLM Endpoints + +### GET /sage_llm/status + +Check availability and configuration of LLM providers (Ollama and LM Studio). + +**Request**: +```http +GET /sage_llm/status +``` + +**Response**: +```json +{ + "success": true, + "ollama": { + "available": true, + "enabled": true, + "url": "http://custom-host:11434" // Only if custom URL configured + }, + "lmstudio": { + "available": true, + "enabled": true, + "url": "http://custom-host:1234" // Only if custom URL configured + } +} +``` + +**Fields**: +- `available` (boolean): Provider library is installed and service is reachable +- `enabled` (boolean): Provider is enabled in settings +- `url` (string, optional): Custom endpoint URL if configured (otherwise uses defaults) + +**Default Endpoints**: +- Ollama: `http://localhost:11434` +- LM Studio: `http://localhost:1234/v1` + +**Use Cases**: +- Check if LLM providers are ready before making requests +- Display provider status in UI +- Validate configuration +- Troubleshoot connection issues + +--- + +### GET /sage_llm/models + +Get available text generation models from all enabled providers. + +**Request**: +```http +GET /sage_llm/models +``` + +**Response**: +```json +{ + "success": true, + "models": { + "ollama": [ + "llama3.2", + "mistral", + "codellama" + ], + "lmstudio": [ + "llama-3.2-3b-instruct", + "mistral-7b-instruct" + ] + } +} +``` + +**Fields**: +- `models` (object): Dictionary mapping provider names to model arrays +- Provider keys: `"ollama"`, `"lmstudio"` +- Each value is an array of model name strings + +**Use Cases**: +- Populate model selection dropdown +- Validate model availability before generation +- Display available models to users + +--- + +### GET /sage_llm/vision_models + +Get available vision-capable models from all enabled providers. + +**Request**: +```http +GET /sage_llm/vision_models +``` + +**Response**: +```json +{ + "success": true, + "models": { + "ollama": [ + "llava", + "bakllava" + ], + "lmstudio": [ + "llava-1.5-7b" + ] + } +} +``` + +**Fields**: +- Same structure as `/sage_llm/models` +- Only includes models with vision capabilities + +**Use Cases**: +- Populate vision model dropdown +- Enable/disable vision features based on availability +- Guide users to install vision models + +--- + +### GET /sage_llm/prompts + +Get predefined system prompts and templates. + +**Request**: +```http +GET /sage_llm/prompts +``` + +**Response**: +```json +{ + "success": true, + "prompts": { + "system": { + "default": "You are a helpful assistant specialized in creating detailed prompts for image generation.", + "creative": "You are a creative writer helping to craft vivid, artistic prompts.", + "technical": "You are a technical expert focused on precise, detailed specifications." + }, + "templates": { + "enhance": "Enhance this prompt with better details: {prompt}", + "expand": "Expand this basic idea into a detailed prompt: {prompt}", + "refine": "Refine this prompt for better quality: {prompt}" + } + } +} +``` + +**Fields**: +- `system` (object): System prompt presets +- `templates` (object): User prompt templates with `{prompt}` placeholder + +**Use Cases**: +- Populate system prompt presets +- Provide quick prompt enhancement templates +- Guide users on effective prompt patterns + +--- + +### POST /sage_llm/generate + +Generate text response (non-streaming). + +**Request**: +```http +POST /sage_llm/generate +Content-Type: application/json + +{ + "provider": "ollama", + "model": "llama3.2", + "prompt": "Create a detailed prompt for a fantasy castle", + "system": "You are a helpful assistant.", + "temperature": 0.7, + "max_tokens": 2048, + "top_p": 0.9, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 +} +``` + +**Parameters**: +- `provider` (string, required): `"ollama"` or `"lmstudio"` +- `model` (string, required): Model name from `/sage_llm/models` +- `prompt` (string, required): User prompt +- `system` (string, optional): System prompt, default: "" +- `temperature` (number, optional): 0.0-2.0, default: 0.7 +- `max_tokens` (number, optional): Max response length, default: 2048 +- `top_p` (number, optional): 0.0-1.0, default: 0.9 +- `presence_penalty` (number, optional): -2.0 to 2.0, default: 0.0 +- `frequency_penalty` (number, optional): -2.0 to 2.0, default: 0.0 + +**Response**: +```json +{ + "success": true, + "response": "A majestic fantasy castle perched atop a floating island, surrounded by cascading waterfalls and mystical clouds. Gothic architecture with towering spires reaching toward the sky, intricate stone carvings, and glowing magical runes. Dramatic lighting with golden sunset rays, volumetric god rays, atmospheric perspective. High detail, epic composition, fantasy art style, masterpiece quality." +} +``` + +**Error Response**: +```json +{ + "success": false, + "error": "Model 'unknown-model' not found", + "status": 400 +} +``` + +**Use Cases**: +- Simple request/response pattern +- When streaming not needed +- Batch processing multiple prompts +- Testing and debugging + +--- + +### POST /sage_llm/generate_stream + +Generate text response with Server-Sent Events (SSE) streaming. + +**Request**: +```http +POST /sage_llm/generate_stream +Content-Type: application/json + +{ + "provider": "ollama", + "model": "llama3.2", + "prompt": "Create a detailed prompt for a fantasy castle", + "system": "You are a helpful assistant.", + "temperature": 0.7, + "max_tokens": 2048 +} +``` + +**Parameters**: Same as `/sage_llm/generate` + +**Response** (SSE Stream): +``` +data: {"token": "A"} + +data: {"token": " majestic"} + +data: {"token": " fantasy"} + +data: {"token": " castle"} + +... + +data: {"done": true, "full_response": "A majestic fantasy castle..."} +``` + +**Event Types**: +- `data: {"token": "..."}` - Individual token +- `data: {"done": true, "full_response": "..."}` - Final event + +**Error in Stream**: +``` +data: {"error": "Generation failed", "done": true} +``` + +**Use Cases**: +- Real-time UI updates +- Better user experience (shows progress) +- Long-running generations +- Interactive applications + +**JavaScript Example**: +```javascript +const eventSource = new EventSource('/sage_llm/generate_stream'); + +eventSource.onmessage = (event) => { + const data = JSON.parse(event.data); + + if (data.token) { + // Append token to UI + responseDiv.textContent += data.token; + } + + if (data.done) { + // Generation complete + eventSource.close(); + } + + if (data.error) { + // Handle error + console.error(data.error); + eventSource.close(); + } +}; +``` + +--- + +### POST /sage_llm/vision_generate + +Generate text response with image input (non-streaming). + +**Request**: +```http +POST /sage_llm/vision_generate +Content-Type: application/json + +{ + "provider": "ollama", + "model": "llava", + "prompt": "Describe this image in detail", + "images": [ + "data:image/jpeg;base64,/9j/4AAQSkZJRg..." + ], + "system": "", + "temperature": 0.7, + "max_tokens": 2048 +} +``` + +**Parameters**: +- All parameters from `/sage_llm/generate`, plus: +- `images` (array, required): Array of base64-encoded images + - Format: `"data:image/[type];base64,[data]"` + - Supported types: jpeg, png, webp, gif + - Max size: 10MB per image + - Max count: 10 images + +**Response**: +```json +{ + "success": true, + "response": "This image shows a serene mountain landscape at sunset. The composition features snow-capped peaks in the background, a crystal-clear alpine lake in the foreground, and dense pine forests on both sides. The lighting is dramatic with golden hour tones, creating long shadows and warm highlights on the mountain faces. The sky displays vibrant oranges and purples. This would translate to a prompt like: 'Mountain landscape, alpine lake, sunset, golden hour lighting, snow-capped peaks, pine forest, dramatic sky, photorealistic, highly detailed, 8k, nature photography'" +} +``` + +**Use Cases**: +- Image-to-prompt generation +- Image analysis and description +- Reference image understanding +- Style identification + +--- + +### POST /sage_llm/vision_generate_stream + +Generate vision response with SSE streaming. + +**Request**: +```http +POST /sage_llm/vision_generate_stream +Content-Type: application/json + +{ + "provider": "ollama", + "model": "llava", + "prompt": "Describe this image", + "images": ["data:image/jpeg;base64,..."], + "temperature": 0.7 +} +``` + +**Parameters**: Same as `/sage_llm/vision_generate` + +**Response**: Same SSE format as `/sage_llm/generate_stream` + +**Use Cases**: +- Real-time vision analysis +- Interactive image exploration +- Progressive image description + +--- + +## Response Formats + +### Success Response + +All successful non-streaming endpoints return: + +```json +{ + "success": true, + "data": { /* endpoint-specific data */ } +} +``` + +Or simplified: + +```json +{ + "success": true, + "response": "generated text", + "models": [...], + /* other fields */ +} +``` + +### Error Response + +All errors return: + +```json +{ + "success": false, + "error": "Human-readable error message", + "status": 400 // HTTP status code +} +``` + +**Common Status Codes**: +- `400`: Bad Request (invalid parameters) +- `404`: Not Found (model or provider not found) +- `500`: Internal Server Error (generation failed) +- `503`: Service Unavailable (provider not running) + +--- + +## Error Handling + +### Client-Side Error Handling + +**Recommended Pattern**: + +```javascript +async function callAPI(endpoint, data) { + try { + const response = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }); + + const json = await response.json(); + + if (!json.success) { + throw new Error(json.error || 'Unknown error'); + } + + return json; + + } catch (error) { + console.error('API Error:', error); + // Show error to user + showNotification(error.message, 'error'); + throw error; + } +} +``` + +### Common Errors + +#### "Model not found" +**Cause**: Model name doesn't exist or provider not available +**Solution**: Check `/sage_llm/models` first, validate model name + +#### "Provider not available" +**Cause**: Ollama or LM Studio not running +**Solution**: Start the service, check `/sage_llm/status` + +#### "Invalid image format" +**Cause**: Image not base64-encoded or wrong format +**Solution**: Ensure proper base64 encoding with data URI prefix + +#### "Max tokens exceeded" +**Cause**: Requested too many tokens +**Solution**: Reduce `max_tokens` or use streaming + +--- + +## SSE Streaming + +### Server-Sent Events (SSE) + +SSE provides real-time streaming of AI responses. + +### Connection + +```javascript +const eventSource = new EventSource(endpoint); +``` + +**Note**: SSE uses GET by default. For POST, use fetch with streaming: + +```javascript +const response = await fetch('/sage_llm/generate_stream', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(requestData) +}); + +const reader = response.body.getReader(); +const decoder = new TextDecoder(); + +while (true) { + const { done, value } = await reader.read(); + if (done) break; + + const chunk = decoder.decode(value); + const lines = chunk.split('\n'); + + for (const line of lines) { + if (line.startsWith('data: ')) { + const data = JSON.parse(line.slice(6)); + handleToken(data); + } + } +} +``` + +### Event Data Format + +**Token Event**: +```json +{"token": "word"} +``` + +**Completion Event**: +```json +{ + "done": true, + "full_response": "complete response text" +} +``` + +**Error Event**: +```json +{ + "error": "error message", + "done": true +} +``` + +### Best Practices + +βœ… **Always close connections**: +```javascript +eventSource.close(); +``` + +βœ… **Handle errors**: +```javascript +eventSource.onerror = (error) => { + console.error('SSE Error:', error); + eventSource.close(); +}; +``` + +βœ… **Accumulate tokens**: +```javascript +let fullText = ''; +eventSource.onmessage = (event) => { + const data = JSON.parse(event.data); + if (data.token) { + fullText += data.token; + updateUI(fullText); + } +}; +``` + +--- + +## Integration Examples + +### Basic Text Generation + +```javascript +async function generatePrompt(userInput) { + const response = await fetch('/sage_llm/generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + provider: 'ollama', + model: 'llama3.2', + prompt: `Create a detailed image prompt: ${userInput}`, + temperature: 0.7, + max_tokens: 1024 + }) + }); + + const data = await response.json(); + return data.response; +} + +// Usage +const prompt = await generatePrompt('cyberpunk city at night'); +console.log(prompt); +``` + +### Streaming Generation + +```javascript +async function streamGeneration(prompt, onToken, onComplete) { + const response = await fetch('/sage_llm/generate_stream', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + provider: 'ollama', + model: 'llama3.2', + prompt: prompt, + temperature: 0.7 + }) + }); + + const reader = response.body.getReader(); + const decoder = new TextDecoder(); + let buffer = ''; + + while (true) { + const { done, value } = await reader.read(); + if (done) break; + + buffer += decoder.decode(value, { stream: true }); + const lines = buffer.split('\n'); + buffer = lines.pop(); // Keep incomplete line in buffer + + for (const line of lines) { + if (line.startsWith('data: ')) { + const data = JSON.parse(line.slice(6)); + + if (data.token) { + onToken(data.token); + } + + if (data.done) { + onComplete(data.full_response); + return; + } + + if (data.error) { + throw new Error(data.error); + } + } + } + } +} + +// Usage +let fullText = ''; +await streamGeneration( + 'Create a fantasy prompt', + (token) => { + fullText += token; + document.getElementById('response').textContent = fullText; + }, + (complete) => { + console.log('Generation complete:', complete); + } +); +``` + +### Vision Analysis + +```javascript +async function analyzeImage(base64Image, question) { + const response = await fetch('/sage_llm/vision_generate', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + provider: 'ollama', + model: 'llava', + prompt: question, + images: [base64Image], + temperature: 0.7, + max_tokens: 2048 + }) + }); + + const data = await response.json(); + return data.response; +} + +// Convert file to base64 +async function fileToBase64(file) { + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onload = () => resolve(reader.result); + reader.onerror = reject; + reader.readAsDataURL(file); + }); +} + +// Usage +const fileInput = document.getElementById('imageUpload'); +const base64 = await fileToBase64(fileInput.files[0]); +const description = await analyzeImage( + base64, + 'Describe this image and suggest a prompt to recreate it' +); +``` + +### Model List Caching + +```javascript +class LLMClient { + constructor() { + this.modelsCache = null; + this.cacheTimestamp = 0; + this.cacheDuration = 60000; // 1 minute + } + + async getModels(refresh = false) { + const now = Date.now(); + + if (!refresh && this.modelsCache && + (now - this.cacheTimestamp) < this.cacheDuration) { + return this.modelsCache; + } + + const response = await fetch('/sage_llm/models'); + const data = await response.json(); + + this.modelsCache = data.models; + this.cacheTimestamp = now; + + return this.modelsCache; + } +} + +// Usage +const client = new LLMClient(); +const models = await client.getModels(); +``` + +### Complete Workflow + +```javascript +// 1. Check status +const status = await fetch('/sage_llm/status').then(r => r.json()); +if (!status.ollama.available) { + alert('Ollama is not running!'); + return; +} + +// 2. Get models +const models = await fetch('/sage_llm/models').then(r => r.json()); +const selectedModel = models.models.ollama[0]; + +// 3. Generate with streaming +let fullResponse = ''; +await streamGeneration( + 'Create a detailed cyberpunk scene prompt', + (token) => { + fullResponse += token; + updateUI(fullResponse); + }, + (complete) => { + // Save to clipboard + navigator.clipboard.writeText(complete); + showNotification('Prompt copied to clipboard!', 'success'); + } +); +``` + +--- + +## Rate Limiting & Performance + +### Client-Side Rate Limiting + +Implement debouncing for frequent requests: + +```javascript +function debounce(func, wait) { + let timeout; + return function(...args) { + clearTimeout(timeout); + timeout = setTimeout(() => func.apply(this, args), wait); + }; +} + +const debouncedGenerate = debounce(async (prompt) => { + const result = await generatePrompt(prompt); + updateUI(result); +}, 300); + +// Usage in input handler +inputField.addEventListener('input', (e) => { + debouncedGenerate(e.target.value); +}); +``` + +### Concurrent Requests + +Limit concurrent generations: + +```javascript +class RequestQueue { + constructor(maxConcurrent = 2) { + this.maxConcurrent = maxConcurrent; + this.running = 0; + this.queue = []; + } + + async add(requestFn) { + if (this.running >= this.maxConcurrent) { + await new Promise(resolve => this.queue.push(resolve)); + } + + this.running++; + try { + return await requestFn(); + } finally { + this.running--; + const next = this.queue.shift(); + if (next) next(); + } + } +} + +const queue = new RequestQueue(2); +await queue.add(() => generatePrompt('prompt 1')); +await queue.add(() => generatePrompt('prompt 2')); +``` + +--- + +## Related Documentation + +- [LLM Tab Guide](LLM_TAB_GUIDE.md) - User guide for LLM interface +- [Prompt Builder Guide](PROMPT_BUILDER_GUIDE.md) - Tag-based prompt construction +- [Architecture](ARCHITECTURE.md) - System design and technical details + +--- + +**API Version**: 1.0 +**Last Updated**: Phase 10 Documentation diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..d52ee00 --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,1186 @@ +# Architecture Documentation + +Technical overview of Sage Utils system design, patterns, and implementation details. + +## Table of Contents + +1. [System Overview](#system-overview) +2. [Module Structure](#module-structure) +3. [Cross-Tab Messaging](#cross-tab-messaging) +4. [Event System](#event-system) +5. [Performance Optimizations](#performance-optimizations) +6. [Accessibility Architecture](#accessibility-architecture) +7. [State Management](#state-management) +8. [Error Handling](#error-handling) +9. [Testing Strategy](#testing-strategy) + +--- + +## System Overview + +### High-Level Architecture + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ ComfyUI Frontend β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ Workflow β”‚ Sidebar β”‚ Gallery β”‚ +β”‚ Canvas β”‚ Tabs β”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ +β”‚ β”‚ LLM Tab │◄────────────────────── +β”‚ β”‚ β”‚ Image Transfer β”‚ +β”‚ β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ β”‚ +β”‚ β”‚Prompt Builderβ”‚ β”‚ +β”‚ β”‚ β”‚ β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ + Cross-Tab Messaging (Event Bus) + β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Backend API (Python/aiohttp) β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ LLM Routes β”‚ Workflow Routes β”‚ Other Routes β”‚ +β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β”‚ +β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ External LLM Providers β”‚ +β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ +β”‚ Ollama β”‚ LM Studio β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Technology Stack + +**Frontend**: +- Pure JavaScript (ES6+) +- No frameworks (for lightweight integration) +- Native Web APIs (fetch, EventSource, FileReader) +- CSS3 for styling + +**Backend**: +- Python 3.9+ +- aiohttp (async web framework) +- Server-Sent Events (SSE) for streaming + +**Communication**: +- REST API (JSON) +- SSE for real-time streaming +- Custom event bus for cross-tab messaging + +--- + +## Module Structure + +### Directory Layout + +``` +comfyui_sageutils/ +β”œβ”€β”€ js/ # Frontend JavaScript +β”‚ β”œβ”€β”€ sidebar/ # Sidebar tab components +β”‚ β”‚ β”œβ”€β”€ llmTab.js # LLM chat interface (4317 lines) +β”‚ β”‚ β”œβ”€β”€ cacheSidebar.js # Main sidebar container +β”‚ β”‚ └── ... +β”‚ β”œβ”€β”€ promptBuilder/ # Prompt builder components +β”‚ β”‚ β”œβ”€β”€ promptGeneration.js # Tag-based prompt UI +β”‚ β”‚ └── ... +β”‚ β”œβ”€β”€ shared/ # Shared utilities +β”‚ β”‚ β”œβ”€β”€ crossTabMessaging.js # Event bus system +β”‚ β”‚ └── performanceUtils.js # Performance helpers +β”‚ β”œβ”€β”€ gallery/ # Gallery integration +β”‚ β”‚ └── galleryEvents.js # Image transfer logic +β”‚ └── llm/ # LLM client +β”‚ └── llmApi.js # API wrapper +β”œβ”€β”€ routes/ # Backend routes +β”‚ β”œβ”€β”€ llm_routes.py # LLM endpoints (842 lines) +β”‚ β”œβ”€β”€ base.py # Route helpers +β”‚ └── ... +β”œβ”€β”€ utils/ # Backend utilities +β”‚ β”œβ”€β”€ llm_wrapper.py # LLM provider abstraction +β”‚ └── settings.py # Configuration +β”œβ”€β”€ nodes/ # ComfyUI custom nodes +β”œβ”€β”€ assets/ # Static assets +β”‚ β”œβ”€β”€ llm_prompts.json # Prompt templates +β”‚ └── default_tag_library.json # Prompt builder tags +└── docs/ # Documentation +``` + +### Module Dependencies + +``` +llmTab.js + β”œβ”€ imports llmApi.js (API calls) + β”œβ”€ imports crossTabMessaging.js (events) + └─ exports createLLMTab() + +promptGeneration.js + β”œβ”€ imports crossTabMessaging.js + └─ exports createPromptGenerationUI() + +crossTabMessaging.js + β”œβ”€ imports performanceUtils.js + └─ exports { + subscribe(), publish(), + sendTextToPromptBuilder(), + sendImageToLLM(), + showNotification() + } + +performanceUtils.js + └─ exports { + debounce(), throttle(), + RateLimiter, BatchProcessor, + rafThrottle(), memoize(), lazy() + } +``` + +### Key Design Principles + +1. **Modularity**: Each feature in its own module +2. **Loose Coupling**: Event bus decouples components +3. **Single Responsibility**: Each module has one clear purpose +4. **Progressive Enhancement**: Features work independently +5. **Performance First**: Optimizations built-in from start + +--- + +## Cross-Tab Messaging + +### Event Bus Architecture + +The `crossTabMessaging.js` module implements a publish-subscribe event bus for cross-component communication. + +```javascript +// Architecture +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Publisher │────────►│ Event Bus │────────►│ Subscriber β”‚ +β”‚ (Gallery) β”‚ publish β”‚ (crossTabMsg.js) β”‚subscribeβ”‚ (LLM Tab) β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Message Types + +```javascript +// Image transfer (Gallery β†’ LLM) +{ + type: 'image-transfer', // MessageTypes.IMAGE_TRANSFER + data: { + images: [{ base64: '...', filename: '...', metadata: {...} }], + source: 'gallery', + autoSwitch: true // Optional: auto-switch to LLM tab + } +} + +// Text transfer (LLM β†’ Prompt Builder) +{ + type: 'text-to-prompt-builder', // MessageTypes.TEXT_TO_PROMPT_BUILDER + data: { + text: 'enhanced prompt...', + source: 'llm', + autoSwitch: true, // Optional: auto-switch to Prompts tab + append: false // Optional: append vs replace + } +} + +// Text transfer (Prompt Builder β†’ LLM) +{ + type: 'text-to-llm', // MessageTypes.TEXT_TO_LLM + data: { + text: 'prompt to enhance...', + source: 'prompts', + autoSwitch: true + } +} + +// Tab switching request +{ + type: 'tab-switch-request', // MessageTypes.TAB_SWITCH_REQUEST + data: { + tabId: 'llm' | 'prompts' | 'gallery' | 'models' | 'files' | 'search', + source: 'user-action', + metadata: {...} // Optional context + } +} + +// Tab switched confirmation +{ + type: 'tab-switched', // MessageTypes.TAB_SWITCHED + data: { + tabId: 'llm', + source: 'sidebar' + } +} + +// Notifications +{ + type: 'notification', // MessageTypes.NOTIFICATION + data: { + message: 'Image uploaded successfully', + type: 'success', // 'info' | 'success' | 'warning' | 'error' + duration: 3000 // Optional: milliseconds + } +} + +// Image queue update +{ + type: 'image-queue-update', // MessageTypes.IMAGE_QUEUE_UPDATE + data: { + count: 5, + source: 'llm' + } +} + +// LLM state request/response +{ + type: 'llm-state-request', // MessageTypes.LLM_STATE_REQUEST + data: { requestId: 'abc123' } +} +{ + type: 'llm-state-response', // MessageTypes.LLM_STATE_RESPONSE + data: { + isGenerating: false, + model: 'gpt-4o', + provider: 'openai' + } +} + +// LLM preset applied +{ + type: 'llm-preset-applied', // MessageTypes.LLM_PRESET_APPLIED + data: { + presetId: 'creative-writer', + presetName: 'Creative Writer', + settings: { temperature: 0.9, ... } + } +} +``` + +### Implementation + +**Getting Event Bus**: +```javascript +import { getEventBus, MessageTypes } from '../shared/crossTabMessaging.js'; + +const bus = getEventBus(); +``` + +**Publishing**: +```javascript +import { getEventBus, MessageTypes } from '../shared/crossTabMessaging.js'; + +const bus = getEventBus(); + +// Publish with rate limiting +bus.publish(MessageTypes.IMAGE_TRANSFER, { + images: [{ base64: imageData, filename: 'img.jpg' }], + source: 'gallery', + autoSwitch: true +}); + +// Ignore rate limiting (use sparingly - critical messages only) +bus.publish(MessageTypes.NOTIFICATION, { + message: 'Critical error occurred', + type: 'error' +}, { ignoreRateLimit: true }); +``` + +**Helper Functions** (recommended): +```javascript +import { + sendImageToLLM, + sendTextToPromptBuilder, + sendTextToLLM, + requestTabSwitch, + showNotification +} from '../shared/crossTabMessaging.js'; + +// Send image to LLM +await sendImageToLLM(imageBase64, 'photo.jpg', { autoSwitch: true }); + +// Send text to Prompt Builder +sendTextToPromptBuilder(promptText, { + source: 'llm', + append: false, + autoSwitch: true +}); + +// Send text to LLM +sendTextToLLM(promptText, { source: 'prompts', autoSwitch: true }); + +// Request tab switch +requestTabSwitch('llm', { source: 'button-click' }); + +// Show notification +showNotification('Operation complete', 'success', 3000); +``` + +**Subscribing**: +```javascript +import { getEventBus, MessageTypes } from '../shared/crossTabMessaging.js'; + +const bus = getEventBus(); + +// Subscribe to message type +const unsubscribe = bus.subscribe(MessageTypes.IMAGE_TRANSFER, (message) => { + console.log('Received:', message.data); + console.log('Timestamp:', message.timestamp); + console.log('Type:', message.type); + + // Process the images + const { images, source, autoSwitch } = message.data; + handleImages(images); +}); + +// Store unsubscribe for cleanup +state.unsubscribers = state.unsubscribers || []; +state.unsubscribers.push(unsubscribe); + +// Later: cleanup +state.unsubscribers.forEach(unsub => unsub()); +``` + +### Rate Limiting + +Rate limits prevent message flooding: + +```javascript +Rate Limits (per second): +- IMAGE_TRANSFER: 10 messages +- STATE_SYNC: 20 messages +- NOTIFICATION: 5 messages +- Default (others): No rate limit by default +``` + +Implementation uses sliding window algorithm in `RateLimiter` class: + +```javascript +class RateLimiter { + constructor(maxCalls, timeWindow) { + this.maxCalls = maxCalls; // Max calls allowed + this.timeWindow = timeWindow; // Time window in ms + this.calls = []; // Timestamp array + } + + allowCall() { + const now = Date.now(); + // Remove calls outside time window + this.calls = this.calls.filter(t => now - t < this.timeWindow); + + if (this.calls.length < this.maxCalls) { + this.calls.push(now); + return true; + } + return false; // Rate limited + } + + getWaitTime() { + if (this.calls.length === 0) return 0; + const oldest = this.calls[0]; + return Math.max(0, this.timeWindow - (Date.now() - oldest)); + } +} +``` + +**Override Rate Limiting** (use sparingly): +```javascript +bus.publish(messageType, data, { ignoreRateLimit: true }); +``` + +### Available Sidebar Tabs + +The sidebar contains multiple tabs that communicate via the event bus: + +- **Models Tab** (`'models'`): Browse and manage models/LoRAs with Civitai integration +- **Files Tab** (`'files'`): File browser and management system +- **Civitai Search Tab** (`'search'`): Search and download content from Civitai +- **Image Gallery Tab** (`'gallery'`): View and manage generated images +- **Prompt Builder Tab** (`'prompts'`): Wildcard-based prompt construction with tag library +- **LLM Tab** (`'llm'`): AI chat interface with vision support and conversation history + +**Tab IDs** for `requestTabSwitch()`: +- `'models'`, `'files'`, `'search'`, `'gallery'`, `'prompts'`, `'llm'` + +--- + +## Event System + +### Event Flow + +``` +User Action + β”‚ + β–Ό +UI Event Handler + β”‚ + β–Ό +Business Logic + β”‚ + β–Ό +API Call (if needed) + β”‚ + β–Ό +Update Local State + β”‚ + β–Ό +Publish Event (if cross-tab) + β”‚ + β–Ό +Subscribers React + β”‚ + β–Ό +Update UI +``` + +### Event Handler Pattern + +**Standard Pattern**: +```javascript +async function handleUserAction(state, ui, data) { + // 1. Validate input + if (!validateInput(data)) { + showError('Invalid input'); + return; + } + + // 2. Update UI (loading state) + ui.button.disabled = true; + ui.spinner.style.display = 'block'; + + try { + // 3. Business logic / API call + const result = await performAction(data); + + // 4. Update state + state.lastResult = result; + + // 5. Update UI (success) + ui.output.textContent = result; + + // 6. Publish event (if needed) + publish('ACTION_COMPLETE', { result }); + + // 7. Show feedback + showNotification('Action successful', 'success'); + + } catch (error) { + // 8. Error handling + console.error('Action failed:', error); + showNotification(error.message, 'error'); + + } finally { + // 9. Cleanup UI + ui.button.disabled = false; + ui.spinner.style.display = 'none'; + } +} +``` + +### Cleanup Pattern + +**Memory Leak Prevention**: +```javascript +function createComponent(container) { + const state = { + unsubscribers: [] // Track subscriptions + }; + + // Subscribe to events + const unsub1 = subscribe('EVENT_1', handler1); + const unsub2 = subscribe('EVENT_2', handler2); + + state.unsubscribers.push(unsub1, unsub2); + + // Return cleanup function + return { + destroy() { + // Unsubscribe from all events + state.unsubscribers.forEach(unsub => unsub()); + + // Clear references + state.unsubscribers = []; + + // Remove DOM listeners + container.innerHTML = ''; + } + }; +} +``` + +--- + +## Performance Optimizations + +### Debouncing + +Delays execution until input stops: + +```javascript +import { debounce } from '../shared/performanceUtils.js'; + +// Create debounced function (300ms delay) +const debouncedUpdate = debounce((text) => { + updatePrompt(text); +}, 300); + +// Use in input handler +textarea.addEventListener('input', (e) => { + debouncedUpdate(e.target.value); + // Only calls updatePrompt 300ms after user stops typing +}); +``` + +**Use Cases**: +- Text input handlers +- Search fields +- Auto-save functionality + +### Throttling + +Limits execution rate: + +```javascript +import { throttle } from '../shared/performanceUtils.js'; + +// Create throttled function (max once per 1000ms) +const throttledScroll = throttle(() => { + updateVisibleItems(); +}, 1000); + +// Use in scroll handler +window.addEventListener('scroll', throttledScroll); +// Only calls updateVisibleItems max once per second +``` + +**Use Cases**: +- Scroll handlers +- Resize handlers +- Mouse move tracking + +### Rate Limiting + +Controls message frequency: + +```javascript +import { RateLimiter } from '../shared/performanceUtils.js'; + +// Create rate limiter (10 per second) +const limiter = new RateLimiter(10, 1000); + +function sendMessage(data) { + if (!limiter.checkLimit()) { + const waitTime = limiter.getWaitTime(); + console.warn(`Rate limited. Wait ${waitTime}ms`); + return false; + } + + // Send message + publish('MESSAGE', data); + return true; +} +``` + +**Use Cases**: +- API calls +- Event publishing +- Resource-intensive operations + +### Batch Processing + +Collects items and processes in batches: + +```javascript +import { BatchProcessor } from '../shared/performanceUtils.js'; + +const processor = new BatchProcessor( + async (items) => { + // Process batch of items + await api.processBatch(items); + }, + { maxSize: 50, maxDelay: 1000 } +); + +// Add items individually +processor.add(item1); +processor.add(item2); +// ... automatically processes when: +// - 50 items collected, OR +// - 1000ms elapsed since first item +``` + +**Use Cases**: +- Bulk API requests +- Database inserts +- Log aggregation + +### Request Animation Frame Throttling + +Syncs with browser rendering: + +```javascript +import { rafThrottle } from '../shared/performanceUtils.js'; + +const throttledAnimate = rafThrottle(() => { + updateAnimation(); +}); + +// Call in game loop +function gameLoop() { + throttledAnimate(); + requestAnimationFrame(gameLoop); +} +``` + +**Use Cases**: +- Animations +- Visual updates +- Canvas rendering + +### Memoization + +Caches function results: + +```javascript +import { memoize } from '../shared/performanceUtils.js'; + +const expensiveComputation = memoize((input) => { + // Complex calculation + return result; +}, 100); // Cache up to 100 results + +// First call: computes +const result1 = expensiveComputation(42); + +// Second call with same input: returns cached +const result2 = expensiveComputation(42); // Instant! +``` + +**Use Cases**: +- Expensive calculations +- Repeated API calls +- Data transformations + +--- + +## Accessibility Architecture + +### WCAG 2.1 Level AA Compliance + +The system implements comprehensive accessibility: + +``` +Principles: +β”œβ”€ Perceivable +β”‚ β”œβ”€ ARIA labels on all interactive elements +β”‚ β”œβ”€ Live regions for dynamic content +β”‚ └─ Semantic HTML structure +β”œβ”€ Operable +β”‚ β”œβ”€ Full keyboard navigation +β”‚ β”œβ”€ Keyboard shortcuts (Ctrl+Enter, Escape) +β”‚ └─ Focus management +β”œβ”€ Understandable +β”‚ β”œβ”€ Clear labels and instructions +β”‚ β”œβ”€ Error messages and validation +β”‚ └─ Consistent behavior +└─ Robust + β”œβ”€ Valid HTML/ARIA + β”œβ”€ Screen reader compatible + └─ Cross-browser support +``` + +### ARIA Implementation + +**Interactive Elements**: +```javascript +// Buttons +button.setAttribute('aria-label', 'Send prompt to LLM'); + +// Inputs +input.setAttribute('aria-label', 'Prompt text'); +input.setAttribute('aria-describedby', 'prompt-help'); + +// Containers +section.setAttribute('role', 'region'); +section.setAttribute('aria-label', 'LLM Response'); +``` + +**Live Regions**: +```javascript +// Dynamic content updates +responseDiv.setAttribute('role', 'log'); +responseDiv.setAttribute('aria-live', 'polite'); +responseDiv.setAttribute('aria-atomic', 'true'); + +// Notifications +notificationDiv.setAttribute('role', 'status'); +notificationDiv.setAttribute('aria-live', 'assertive'); +``` + +**Keyboard Navigation**: +```javascript +// Tab order +element.setAttribute('tabindex', '0'); // Focusable + +// Keyboard shortcuts +textarea.addEventListener('keydown', (e) => { + if (e.ctrlKey && e.key === 'Enter') { + e.preventDefault(); + handleSubmit(); + } + if (e.key === 'Escape') { + e.target.blur(); + } +}); +``` + +### Screen Reader Support + +**Announcements**: +```javascript +function announceToScreenReader(message, priority = 'polite') { + const liveRegion = document.getElementById('sr-announce'); + liveRegion.setAttribute('aria-live', priority); + liveRegion.textContent = message; + + // Clear after announcement + setTimeout(() => { + liveRegion.textContent = ''; + }, 1000); +} + +// Usage +announceToScreenReader('Image uploaded successfully'); +``` + +--- + +## State Management + +### State Pattern + +Each component maintains its own state: + +```javascript +function createComponent() { + // Component state + const state = { + // Data + model: null, + provider: null, + generating: false, + images: [], + + // Settings + settings: { + temperature: 0.7, + maxTokens: 2048 + }, + + // Lifecycle + unsubscribers: [], + initialized: false + }; + + // State modifiers + function setState(updates) { + Object.assign(state, updates); + render(state); + } + + // Component logic... + + return { state, setState, destroy }; +} +``` + +### State Updates + +**Immutable Pattern** (for complex objects): +```javascript +// DON'T mutate directly +state.settings.temperature = 0.8; // ❌ + +// DO create new object +setState({ + settings: { + ...state.settings, + temperature: 0.8 + } +}); // βœ… +``` + +**Simple Updates**: +```javascript +// For simple values, direct assignment is fine +state.generating = true; +state.model = 'llama3.2'; +``` + +### State Persistence + +**Session Storage**: +```javascript +// Save state +function saveState(key, state) { + try { + sessionStorage.setItem(key, JSON.stringify(state)); + } catch (error) { + console.error('Failed to save state:', error); + } +} + +// Load state +function loadState(key, defaultState) { + try { + const saved = sessionStorage.getItem(key); + return saved ? JSON.parse(saved) : defaultState; + } catch (error) { + console.error('Failed to load state:', error); + return defaultState; + } +} +``` + +--- + +## Error Handling + +### Error Hierarchy + +``` +Error Types: +β”œβ”€ Validation Errors (4xx) +β”‚ β”œβ”€ Invalid input +β”‚ β”œβ”€ Missing required fields +β”‚ └─ Format errors +β”œβ”€ Service Errors (5xx) +β”‚ β”œβ”€ API failures +β”‚ β”œβ”€ Provider unavailable +β”‚ └─ Generation errors +└─ Client Errors + β”œβ”€ Network failures + β”œβ”€ Parse errors + └─ Unexpected errors +``` + +### Error Handling Pattern + +```javascript +async function apiCall(endpoint, data) { + try { + // Make request + const response = await fetch(endpoint, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }); + + // Parse response + const json = await response.json(); + + // Check success + if (!json.success) { + // API returned error + throw new APIError(json.error, response.status); + } + + return json; + + } catch (error) { + // Network error or API error + if (error instanceof APIError) { + // Known API error + handleAPIError(error); + } else if (error instanceof TypeError) { + // Network error + handleNetworkError(error); + } else { + // Unexpected error + handleUnexpectedError(error); + } + + throw error; // Re-throw for caller + } +} + +class APIError extends Error { + constructor(message, status) { + super(message); + this.name = 'APIError'; + this.status = status; + } +} +``` + +### User-Friendly Errors + +```javascript +function getUserFriendlyError(error) { + // Map technical errors to user-friendly messages + const errorMap = { + 'ECONNREFUSED': 'Cannot connect to service. Is it running?', + 'Model not found': 'Selected model is not available. Please choose another.', + 'Invalid API key': 'API key is incorrect. Please check your settings.', + '429': 'Rate limit exceeded. Please wait a moment and try again.' + }; + + // Check error patterns + for (const [pattern, message] of Object.entries(errorMap)) { + if (error.message.includes(pattern) || error.status?.toString() === pattern) { + return message; + } + } + + // Default message + return 'An error occurred. Please try again.'; +} +``` + +--- + +## Testing Strategy + +### Testing Pyramid + +``` + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ E2E Tests β”‚ ← Manual/automated UI tests + β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ + β”‚ Integration β”‚ ← API endpoint tests + β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€ + β”‚ Unit Tests β”‚ ← Function/module tests + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +### Unit Testing + +**Example** (using Jest): +```javascript +import { debounce } from '../performanceUtils.js'; + +describe('debounce', () => { + jest.useFakeTimers(); + + it('should delay execution', () => { + const fn = jest.fn(); + const debounced = debounce(fn, 300); + + debounced(); + expect(fn).not.toHaveBeenCalled(); + + jest.advanceTimersByTime(300); + expect(fn).toHaveBeenCalledTimes(1); + }); + + it('should cancel previous call', () => { + const fn = jest.fn(); + const debounced = debounce(fn, 300); + + debounced(); + debounced(); + debounced(); + + jest.advanceTimersByTime(300); + expect(fn).toHaveBeenCalledTimes(1); + }); +}); +``` + +### Integration Testing + +**Example** (API endpoint): +```python +import pytest +from aiohttp import web +from routes.llm_routes import register_routes + +@pytest.fixture +async def client(aiohttp_client): + app = web.Application() + routes = web.RouteTableDef() + register_routes(routes) + app.add_routes(routes) + return await aiohttp_client(app) + +async def test_get_models(client): + resp = await client.get('/sage_llm/models') + assert resp.status == 200 + + data = await resp.json() + assert data['success'] is True + assert 'models' in data +``` + +### Manual Testing Checklist + +**LLM Tab**: +- [ ] Provider selection works +- [ ] Model loading works +- [ ] Text generation succeeds +- [ ] Vision generation with images works +- [ ] Streaming displays properly +- [ ] Error handling shows messages +- [ ] Keyboard shortcuts work +- [ ] Screen reader announces updates + +**Prompt Builder**: +- [ ] Tag selection updates prompt +- [ ] Send to LLM transfers text +- [ ] Receive from LLM updates field +- [ ] Keyboard shortcuts work +- [ ] Validation shows errors + +**Cross-Tab**: +- [ ] Gallery to LLM image transfer +- [ ] LLM to Prompt Builder text transfer +- [ ] Notifications appear +- [ ] Rate limiting prevents flooding + +--- + +## Security Considerations + +### Input Validation + +**Frontend**: +```javascript +function validateImageFile(file) { + // Check type + const allowedTypes = ['image/jpeg', 'image/png', 'image/webp', 'image/gif']; + if (!allowedTypes.includes(file.type.toLowerCase())) { + return { error: 'Unsupported format' }; + } + + // Check size + const maxSize = 10 * 1024 * 1024; // 10MB + if (file.size > maxSize) { + return { error: 'File too large' }; + } + + return null; // Valid +} +``` + +**Backend**: +```python +def validate_request_data(data): + """Validate incoming request data""" + # Check required fields + required = ['provider', 'model', 'prompt'] + for field in required: + if field not in data: + raise ValueError(f'Missing required field: {field}') + + # Validate types + if not isinstance(data['prompt'], str): + raise ValueError('Prompt must be a string') + + # Validate ranges + if 'temperature' in data: + temp = data['temperature'] + if not (0.0 <= temp <= 2.0): + raise ValueError('Temperature must be 0.0-2.0') + + return True +``` + +### API Key Security + +```javascript +// βœ… DO: Store in backend, send via secure headers +const response = await fetch('/sage_llm/generate', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + // API key handled server-side + }, + body: JSON.stringify({ provider, model, prompt }) +}); + +// ❌ DON'T: Include API keys in frontend code +const apiKey = 'sk-...'; // Never do this! +``` + +### Content Security + +```javascript +// βœ… DO: Sanitize user input before display +function sanitizeHTML(html) { + const div = document.createElement('div'); + div.textContent = html; // Sets as text, not HTML + return div.innerHTML; +} + +responseDiv.textContent = sanitizedResponse; // Safe + +// ❌ DON'T: Insert unsanitized content +responseDiv.innerHTML = userInput; // XSS vulnerability! +``` + +--- + +## Performance Metrics + +### Key Metrics + +**Frontend**: +- Time to Interactive (TTI): < 2s +- First Contentful Paint (FCP): < 1s +- Input Latency: < 100ms +- Memory Usage: < 50MB per tab + +**Backend**: +- API Response Time: < 100ms (non-LLM) +- LLM First Token: < 500ms +- Throughput: 100 req/s (non-streaming) +- Memory: < 200MB idle + +### Monitoring + +```javascript +// Performance monitoring +const perfObserver = new PerformanceObserver((list) => { + for (const entry of list.getEntries()) { + console.log('Performance:', entry.name, entry.duration); + } +}); + +perfObserver.observe({ entryTypes: ['measure'] }); + +// Mark key events +performance.mark('generation-start'); +await generateText(prompt); +performance.mark('generation-end'); +performance.measure('generation', 'generation-start', 'generation-end'); +``` + +--- + +## Future Enhancements + +### Planned Features + +1. **Offline Support**: Service workers for caching +2. **WebSocket**: Replace SSE for bidirectional communication +3. **Web Workers**: Offload heavy computation +4. **IndexedDB**: Client-side data persistence +5. **Progressive Web App**: Installable experience + +### Scalability Considerations + +- **Horizontal Scaling**: Multiple ComfyUI instances +- **Load Balancing**: Distribute LLM requests +- **Caching**: Redis for shared state +- **Queue System**: Background job processing + +--- + +## Related Documentation + +- [API Documentation](API.md) - Complete API reference +- [LLM Tab Guide](LLM_TAB_GUIDE.md) - User guide +- [Prompt Builder Guide](PROMPT_BUILDER_GUIDE.md) - Feature guide + +--- + +**Architecture Version**: 1.0 +**Last Updated**: Phase 10 Documentation diff --git a/docs/LLM_TAB_GUIDE.md b/docs/LLM_TAB_GUIDE.md new file mode 100644 index 0000000..327d3f0 --- /dev/null +++ b/docs/LLM_TAB_GUIDE.md @@ -0,0 +1,550 @@ +# LLM Chat Tab - Complete Guide + +The LLM Chat Tab brings AI language model capabilities directly into ComfyUI, enabling you to generate, refine, and enhance prompts without leaving your workflow environment. + +## Table of Contents + +1. [Overview](#overview) +2. [Getting Started](#getting-started) +3. [Supported Providers](#supported-providers) +4. [Text Generation](#text-generation) +5. [Vision Features](#vision-features) +6. [Advanced Options](#advanced-options) +7. [Conversation History](#conversation-history) +8. [Keyboard Shortcuts](#keyboard-shortcuts) +9. [Tips & Best Practices](#tips--best-practices) +10. [Troubleshooting](#troubleshooting) + +--- + +## Overview + +### Key Features + +- **Multi-Provider Support**: LM Studio and Ollama with custom endpoint options +- **Vision Capabilities**: Upload and analyze images with vision-enabled models (drag-and-drop supported) +- **Real-time Streaming**: See responses generate in real-time with SSE +- **Conversation History**: Maintain context across multiple messages +- **Preset System**: Save and reuse complete LLM configurations (model, settings, system prompts) +- **System Prompt Management**: Create, save, and manage system prompts +- **Advanced Options**: Temperature, top-p, max tokens, presence/frequency penalties, keep-alive +- **Cross-Tab Integration**: Send responses to Prompt Builder or receive images from Gallery +- **Keyboard Shortcuts**: Ctrl+Enter to send, Escape to blur textareas +- **Accessibility**: Full keyboard navigation and screen reader support + +### When to Use + +- **Prompt Generation**: Create detailed, creative prompts from simple descriptions +- **Prompt Refinement**: Enhance existing prompts with better structure and keywords +- **Image Analysis**: Describe images or get prompt suggestions from reference images +- **Creative Assistance**: Brainstorm ideas, variations, or themes +- **Quality Control**: Review and improve prompt quality before generation + +--- + +## Getting Started + +### Opening the LLM Tab + +1. Launch ComfyUI +2. Look for the **sidebar** on the right side of the screen +3. Click on the **LLM** or **Chat** tab icon + +### Basic Workflow + +### Basic Workflow + +1. **Select a Provider**: Choose LM Studio or Ollama +2. **Configure if needed**: Set custom endpoints in settings (optional) +3. **Choose a Model**: Select from available models for your provider +4. **Enter Your Prompt**: Type your request in the text area +5. **Send**: Click "Send" or press **Ctrl+Enter** +6. **Review Response**: Watch the AI response stream in real-time + +--- + +## Supported Providers + +Sage Utils currently supports local LLM providers for privacy, unlimited usage, and no API costs. + +### Local Providers + +#### LM Studio +- **Models**: Any GGUF model you load in LM Studio +- **Vision Support**: βœ… (vision-capable models like LLaVA, BakLLaVA, MiniCPM-V) +- **Setup**: + 1. Download and install [LM Studio](https://lmstudio.ai/) + 2. Browse and download models from the built-in model browser + 3. Load a model in LM Studio + 4. Start the local server (Server tab β†’ Start Server, default: http://localhost:1234) + 5. Select "lmstudio" from provider dropdown in Sage Utils + 6. Model will auto-detect + +**Advantages**: +- Free and unlimited +- Complete privacy (runs locally) +- No internet required for generation +- Customizable models and parameters +- User-friendly GUI for model management +- Support for custom endpoints + +**Default Endpoint**: `http://localhost:1234/v1` + +**Recommended Models**: +- Text: Llama 3, Mistral, Qwen, Phi-3 +- Vision: LLaVA 1.6, BakLLaVA, MiniCPM-V + +#### Ollama +- **Models**: Any model available in Ollama library +- **Vision Support**: βœ… (llava, bakllava, minicpm-v models) +- **Setup**: + 1. Install [Ollama](https://ollama.ai/) + 2. Pull a model from terminal: + - Text: `ollama pull llama3.2` or `ollama pull mistral` + - Vision: `ollama pull llava` or `ollama pull bakllava` + 3. Verify model is installed: `ollama list` + 4. Ensure Ollama service is running (runs automatically after install) + 5. Select "ollama" from provider dropdown in Sage Utils + 6. Enter exact model name (e.g., "llama3.2", "llava") + +**Advantages**: +- Free and open source +- Easy model management via CLI +- Lightweight and efficient +- Active community and model library +- Automatic service management +- Simple installation + +**Default Endpoint**: `http://localhost:11434` + +**Popular Models**: +- **Text Generation**: + - `llama3.2` - Fast and capable + - `llama3.1` - Larger, more powerful + - `mistral` - Excellent performance + - `mixtral` - Mixture of experts + - `qwen2.5` - Strong multilingual +- **Vision**: + - `llava` - General vision understanding + - `bakllava` - Enhanced LLaVA variant + - `minicpm-v` - Efficient vision model + - `llava-phi3` - Compact vision model + +**CLI Commands**: +```bash +# List available models remotely +ollama list + +# Pull a model +ollama pull llama3.2 + +# Run a model (test) +ollama run llama3.2 + +# Remove a model +ollama rm modelname +``` + +### Custom Endpoints + +Both providers support custom endpoint URLs for advanced configurations: + +**Use Cases**: +- Docker containers running Ollama/LM Studio +- Remote Ollama instances on your network +- Custom ports (non-default) +- Network-accessible LM Studio instances +- Ollama running in WSL or VMs + +**Setup**: +1. Open Sage Utils settings +2. Enable "Use Custom URL" for your provider +3. Enter custom endpoint (e.g., `http://192.168.1.100:11434`) +4. Save settings +5. Provider will use custom endpoint instead of default + +--- + +## Text Generation + +### Basic Usage + +1. **Type your prompt** in the main text area +2. **Click "Send"** or press **Ctrl+Enter** +3. **Watch the response** stream in real-time +4. **Copy response** using the copy button +5. **Send to Prompt Builder** to use in tag-based prompts + +### Example Prompts + +#### Prompt Generation +``` +Create a detailed prompt for a cyberpunk street scene at night +``` + +#### Prompt Refinement +``` +Enhance this prompt with better details and artistic style: +"a girl in a park" +``` + +#### Creative Variations +``` +Give me 5 variations of this theme: +"fantasy castle on a floating island" +``` + +### System Prompts + +System prompts set the behavior and personality of the AI. They're applied before every request. + +**To use**: +1. Click "Advanced Options" to expand +2. Enter system prompt in the dedicated field +3. System prompt persists across conversations + +**Example System Prompts**: + +``` +You are an expert at creating detailed, artistic prompts for image generation. +Focus on visual details, lighting, composition, and artistic style. +``` + +``` +You are a creative writing assistant specializing in fantasy and sci-fi themes. +Provide vivid descriptions with emphasis on mood and atmosphere. +``` + +--- + +## Vision Features + +### Uploading Images + +Three ways to add images: + +1. **File Upload**: + - Click "Add Image" button + - Select up to 10 images + - Supported: JPEG, PNG, WEBP, GIF + - Max size: 10MB per image + +2. **Drag & Drop**: + - Drag images from your file explorer + - Drop onto the preview area + - Visual feedback during drag + +3. **Paste**: + - Copy image to clipboard + - Click in the LLM tab + - Press Ctrl+V + +### Image Validation + +The system validates all uploads: + +βœ… **Accepted Formats**: JPEG, JPG, PNG, WEBP, GIF +βœ… **Maximum Size**: 10MB per image +βœ… **Maximum Count**: 10 images total + +❌ **Rejected**: +- Unsupported formats (BMP, TIFF, SVG, etc.) +- Files over 10MB +- More than 10 images + +**Error Messages**: +- "Unsupported format (BMP). Supported: JPEG, PNG, WEBP, GIF" +- "File too large (15.3MB). Maximum: 10MB" +- "Maximum 10 images allowed. Please remove some images first." + +### Vision Model Usage + +#### Example Vision Prompts + +**Image Description**: +``` +Describe this image in detail, focusing on composition and artistic elements +``` + +**Prompt Generation from Image**: +``` +Create a detailed prompt that would generate an image similar to this +``` + +**Style Analysis**: +``` +Analyze the artistic style of this image and suggest similar styles +``` + +**Multiple Image Comparison**: +``` +Compare these images and identify common themes or elements +``` + +### Removing Images + +- Click the **Γ—** button on any image preview +- Click **Clear All** to remove all images +- Images are cleared when you start a new conversation + +--- + +## Advanced Options + +Click "Advanced Options" to reveal additional settings: + +### Temperature (0.0 - 2.0) +Controls randomness and creativity: +- **0.0 - 0.3**: Focused, deterministic, consistent +- **0.4 - 0.7**: Balanced (recommended for most use cases) +- **0.8 - 1.2**: Creative, varied, exploratory +- **1.3 - 2.0**: Very random, experimental + +**Default**: 0.7 + +### Top P (0.0 - 1.0) +Controls diversity via nucleus sampling: +- **0.1 - 0.5**: Conservative, safe choices +- **0.6 - 0.9**: Balanced diversity +- **1.0**: Maximum diversity + +**Default**: 0.9 + +### Max Tokens (1 - 4096+) +Maximum length of response: +- **100-500**: Short, concise responses +- **500-1000**: Medium responses +- **1000-2000**: Detailed responses +- **2000+**: Very detailed or multiple examples + +**Default**: 2048 + +### Presence Penalty (-2.0 - 2.0) +Encourages new topics: +- **-2.0 - 0.0**: Allows repetition +- **0.0**: Neutral +- **0.1 - 1.0**: Discourages repetition +- **1.0 - 2.0**: Strongly avoids repetition + +**Default**: 0.0 + +### Frequency Penalty (-2.0 - 2.0) +Reduces word repetition: +- **-2.0 - 0.0**: Allows repeated words +- **0.0**: Neutral +- **0.1 - 1.0**: Discourages frequent words +- **1.0 - 2.0**: Strongly avoids repetition + +**Default**: 0.0 + +### System Prompt +Sets AI behavior and personality. See [System Prompts](#system-prompts) section. + +--- + +## Conversation History + +### How It Works + +The LLM tab maintains conversation context automatically: + +1. **Each message** is stored (both user and AI) +2. **Context is maintained** across multiple exchanges +3. **History is included** in subsequent requests (when enabled) +4. **New conversation** starts fresh + +### Managing Conversations + +**Start New Conversation**: +- Click "New Conversation" button +- Clears all history +- Fresh context for new topic + +**Include/Exclude History**: +- Toggle "Include History" in advanced options +- When enabled: AI remembers previous exchanges +- When disabled: Each message is independent + +**Max History Messages**: +- Set how many previous messages to include +- Default: 10 messages +- Older messages are dropped automatically + +### Viewing History + +Click "Conversation History" to see: +- All messages in current conversation +- User and assistant roles clearly labeled +- Timestamps for each exchange +- Copy individual messages + +### Best Practices + +βœ… **Do**: +- Start new conversation when changing topics +- Use history for iterative refinement +- Keep history enabled for context-dependent tasks +- Clear history when sensitive topics are discussed + +❌ **Don't**: +- Let history grow too large (impacts token usage) +- Mix unrelated topics in one conversation +- Forget to start new conversation for fresh context + +--- + +## Keyboard Shortcuts + +### Text Input +- **Ctrl+Enter**: Send prompt +- **Escape**: Blur (unfocus) textarea + +### Navigation +- **Tab**: Move between fields +- **Shift+Tab**: Move backwards + +### Workflow +1. Type prompt +2. Press **Ctrl+Enter** to send +3. Review response +4. Press **Escape** to return to input +5. Modify and send again + +--- + +## Tips & Best Practices + +### Prompt Writing + +βœ… **Be Specific**: +``` +Good: "Create a detailed cyberpunk street scene with neon signs, rain-slicked +pavement, and crowds of people under umbrellas at night" + +Avoid: "Make a cyberpunk scene" +``` + +βœ… **Use Context**: +``` +"Expand this basic prompt with artistic details, lighting, and composition: +[your basic prompt]" +``` + +βœ… **Request Structure**: +``` +"Format the prompt as: subject, setting, lighting, mood, style, quality tags" +``` + +βœ… **Iterate**: +- Start broad, then refine +- Ask for variations +- Request specific improvements + +### Model Selection + +**For Prompt Generation**: +- GPT-4o: Best balance of quality and speed +- Claude 3.5 Sonnet: Excellent creative writing +- Gemini 1.5 Pro: Great for detailed descriptions +- Local models (Llama 3.2): Free unlimited usage + +**For Vision Tasks**: +- GPT-4o: Fast and accurate +- Claude 3 Opus: Best detail analysis +- LLaVA (local): Free, surprisingly good + +### Token Management + +- **Short prompts**: 500-1000 max tokens +- **Detailed responses**: 1500-2500 max tokens +- **Multiple examples**: 3000-4000 max tokens +- Monitor usage to control costs (cloud providers) + +### Performance + +- Use local models (LM Studio/Ollama) for unlimited free usage +- Enable streaming for better user experience +- Clear history periodically to reduce token usage +- Use appropriate max tokens to avoid waste + +--- + +## Troubleshooting + +### Common Issues + +#### "Please select a model" +**Problem**: No model selected +**Solution**: Choose a model from the dropdown after selecting provider + +#### "API Error: 401 Unauthorized" +**Problem**: Invalid or missing API key +**Solution**: +1. Check API key is entered correctly +2. Verify key is active on provider's platform +3. Check for typos or extra spaces + +#### "Connection failed" (Local providers) +**Problem**: LM Studio or Ollama not running +**Solution**: +1. Ensure service is running +2. Check correct port (LM Studio: 1234, Ollama: 11434) +3. Verify firewall isn't blocking + +#### "Model not found" +**Problem**: Specified model doesn't exist +**Solution**: +1. Refresh model list +2. Check spelling +3. For Ollama: `ollama pull ` first + +#### "Image upload failed" +**Problem**: Invalid image file +**Solution**: +1. Check format (JPEG, PNG, WEBP, GIF only) +2. Verify size (under 10MB) +3. Don't exceed 10 images total + +#### Streaming stops mid-response +**Problem**: Connection interrupted +**Solution**: +1. Click "Stop" then resend +2. Check internet connection (cloud providers) +3. Check local service still running + +#### Response is too short/long +**Problem**: Max tokens setting +**Solution**: Adjust "Max Tokens" in Advanced Options + +#### AI output is repetitive +**Problem**: Penalty settings too low +**Solution**: Increase presence/frequency penalty (0.5-1.0) + +#### AI output is too random +**Problem**: Temperature too high +**Solution**: Decrease temperature (0.5-0.7) + +### Getting Help + +If you encounter issues not covered here: + +1. Check [GitHub Issues](https://github.com/arcum42/ComfyUI_SageUtils/issues) +2. Review [API Documentation](API.md) for technical details +3. Open a new issue with: + - Provider and model being used + - Error message (exact text) + - Steps to reproduce + - Screenshots if applicable + +--- + +## What's Next? + +- Explore the [Prompt Builder Guide](PROMPT_BUILDER_GUIDE.md) +- Learn about [Cross-Tab Integration](README.md#cross-tab-integration) +- Review [API Documentation](API.md) for custom integrations +- Check out [Architecture](ARCHITECTURE.md) for technical details + +--- + +**Happy prompting!** 🎨✨ diff --git a/docs/PROMPT_BUILDER_GUIDE.md b/docs/PROMPT_BUILDER_GUIDE.md new file mode 100644 index 0000000..b44be79 --- /dev/null +++ b/docs/PROMPT_BUILDER_GUIDE.md @@ -0,0 +1,1082 @@ +# Prompt Builder - Complete Guide + +The Prompt Builder provides a wildcard-based system with tag library support for constructing complex, detailed prompts with optional LLM enhancement capabilities. + +## Table of Contents + +1. [Overview](#overview) +2. [Getting Started](#getting-started) +3. [Wildcard System](#wildcard-system) +4. [Tag Library](#tag-library) +5. [Building Prompts](#building-prompts) +6. [LLM Integration](#llm-integration) +7. [Saved Prompts](#saved-prompts) +8. [Advanced Features](#advanced-features) +9. [Keyboard Shortcuts](#keyboard-shortcuts) +10. [Tips & Best Practices](#tips--best-practices) +11. [Troubleshooting](#troubleshooting) + +--- + +## Overview + +### What is Prompt Builder? + +The Prompt Builder is a wildcard and tag-based prompt construction system that helps you: +- **Build complex prompts** using wildcard syntax (`__category__`) +- **Manage tag collections** with organized categories +- **Generate variations** with seed-based randomization +- **Enhance with AI** using LLM integration +- **Manage both positive and negative prompts** separately +- **Save and reuse** successful prompt patterns and collections + +### Key Features + +- **Wildcard System**: Use `__category__` syntax for dynamic prompt generation +- **Tag Library**: Pre-organized tag collections across multiple categories +- **Saved Prompts**: Save and manage complete prompt collections +- **Positive/Negative Prompts**: Separate construction for better control +- **LLM Integration**: Send prompts to LLM for enhancement and creative variations +- **Cross-Tab Messaging**: Receive enhanced prompts from LLM automatically +- **Seed-Based Generation**: Control randomization with fixed or random seeds +- **Multiple Variations**: Generate multiple prompt variations in one click +- **Performance Optimized**: Debounced updates prevent lag +- **Keyboard Shortcuts**: Efficient workflow with Ctrl+Enter + +### When to Use + +βœ… **Use Prompt Builder when**: +- Want randomized variations using wildcard categories +- Building complex prompts with reusable components +- Need consistency across multiple generations with controlled variation +- Managing tag libraries for quick prompt assembly +- Saving and reusing successful prompt patterns +- Building negative prompts with known unwanted elements + +βœ… **Use LLM Tab when**: +- Starting from scratch with just an idea +- Need creative variations or brainstorming +- Want natural language prompt writing +- Analyzing images for prompt generation +- Refining or expanding existing prompts with AI assistance + +**Best Results**: Use both together! Build base with wildcards in Prompt Builder β†’ Send to LLM for enhancement β†’ Receive refined version back + +--- + +## Getting Started + +### Opening Prompt Builder + +1. Launch ComfyUI +2. Look for the **sidebar** on the right side +3. Click on the **Prompt Builder** tab + +### Basic Workflow + +1. **Enter prompts** with wildcard syntax (e.g., `__character__`) +2. **Set seed and count** for controlled randomization +3. **Generate** to replace wildcards with random selections +4. **Optional**: Browse tag library to insert tags/wildcards +5. **Optional**: Send to LLM for AI enhancement +6. **Copy** or **Send to Node** to use in your workflow + +### Quick Example + +Let's build a prompt using wildcards: + +1. **Write Prompt with Wildcards**: + ``` + __character__ in a __location__, __art_style__, __quality__ + ``` + +2. **Generate** (the wildcards are replaced): + ``` + 1girl, long hair, blue eyes in a cyberpunk city, digital art style, masterpiece quality + ``` + +3. **Generate Again** (different random result): + ``` + 1boy, short hair, standing in a forest clearing, oil painting style, highly detailed + ``` + +--- + +## Wildcard System + +### What Are Wildcards? + +Wildcards are placeholders in your prompts that get replaced with random values from a category. They use the syntax `__category_name__`. + +**Example**: +``` +__character__ wearing __clothing__ in a __location__ +``` + +When generated, becomes: +``` +warrior wearing battle armor in a mountain fortress +``` + +### Wildcard Syntax + +**Basic Format**: +- Wildcards are surrounded by double underscores: `__name__` +- Case-sensitive: `__Character__` β‰  `__character__` +- Can be anywhere in your prompt +- Multiple wildcards are replaced independently + +**Examples**: +``` +A __adjective__ __subject__ in a __setting__ at __time_of_day__ +``` + +### Using Wildcards + +1. **Type Manually**: Just type `__category__` directly in the prompt field +2. **Insert from Tag Library**: Click tags in the library to insert wildcards +3. **Highlight Detection**: Valid wildcards are automatically detected +4. **Validation**: The system shows which wildcards are found + +### Seed Control + +Wildcards use seed-based randomization for reproducibility: + +- **Random Seed**: Click "Random Seed" for different results each time +- **Fixed Seed**: Enter a specific number to get same results +- **Count**: Generate multiple variations with one click + +**Example Workflow**: +``` +Prompt: __character__ in __location__ +Seed: 12345 +Count: 3 + +Result 1 (seed 12345): warrior in castle +Result 2 (seed 12346): mage in forest +Result 3 (seed 12347): archer in mountains +``` + +--- + +## Tag Library + +The Tag Library provides organized collections of tags and wildcards you can insert into your prompts. + +### What is the Tag Library? + +A searchable, categorized collection of: +- **Individual Tags**: Specific descriptors (e.g., "masterpiece", "1girl") +- **Tag Sets**: Pre-grouped collections of related tags +- **Wildcard Categories**: Categories that can be inserted as `__category__` + +### Using the Tag Library + +1. **Browse Categories**: Click category tabs to view different tag collections +2. **Search Tags**: Use the search box to find specific tags +3. **Insert Tags**: Click any tag to insert it into your prompt +4. **Insert as Wildcard**: Many categories can be inserted as wildcards + +### Tag Categories + +### Tag Categories + +The tag library organizes tags into categories for easy browsing: + +- **Character Tags**: People, poses, features, expressions +- **Style Tags**: Art styles, mediums, artist references +- **Quality Tags**: Technical quality descriptors +- **Location/Setting Tags**: Environments, backgrounds +- **Lighting Tags**: Lighting types and moods +- **Color Tags**: Color palettes and themes +- **Mood/Atmosphere Tags**: Emotional tone +- **Camera Tags**: Angles, framing, composition +- **Custom Categories**: User-defined tag collections + +**Using Categories**: +- Browse by clicking category tabs +- Search across all categories +- Insert individual tags or entire category as wildcard + +### Managing Tags + +**Insert Tag**: +- Click any tag to insert it at cursor position +- Tags are added with proper formatting + +**Insert as Wildcard**: +- Click category name or special button +- Inserts `__category__` into your prompt +- Will randomize when generating + +**Edit Tags** (if enabled): +- Add new tags to categories +- Create custom categories +- Organize your own tag library + +--- + +## Saved Prompts + +The Saved Prompts feature lets you save, organize, and reuse complete prompt collections. + +### What Are Saved Prompts? + +Complete prompt configurations including: +- Positive prompt text (with wildcards) +- Negative prompt text +- Seed value +- Description/notes +- Category/organization + +### Saving Prompts + +1. Create your prompt with wildcards +2. Click "Save Prompt" button +3. Enter name and optional description +4. Choose category (or create new) +5. Save + +**What Gets Saved**: +- Full positive prompt text +- Full negative prompt text +- Current seed value +- Your description +- Metadata (date created, etc.) + +### Loading Saved Prompts + +1. Open "Saved Prompts" section +2. Browse by category +3. Click prompt name to load +4. All fields populate automatically + +**Options When Loading**: +- **Replace**: Overwrites current prompts +- **Append**: Adds to existing prompts +- **New Seed**: Generate with random seed vs. saved seed + +### Organizing Saved Prompts + +**Categories**: +- Group related prompts together +- Create custom categories +- Filter by category + +**Search**: +- Search by name +- Search by description +- Search prompt content + +**Management**: +- Edit saved prompts +- Delete unused prompts +- Export/Import collections + +--- + +## Building Prompts + +### Positive Prompts + +Positive prompts define **what you want** in the image. + +**Wildcard Strategy**: +1. **Subject** (character or main focus) - `__character__` +2. **Action/Pose** - `__pose__` or `__action__` +3. **Setting** (location, environment) - `__location__` or `__setting__` +4. **Style** (artistic style, medium) - `__art_style__` +5. **Lighting** - `__lighting__` +6. **Quality** (technical tags) - `__quality__` + +**Example with Wildcards**: +``` +Positive: __character__ __pose__ in a __location__, __art_style__, +__lighting__, __quality__ +``` + +**Generated Result** (example): +``` +1girl, standing gracefully in a cherry blossom garden, anime style, +golden hour lighting, masterpiece, highly detailed +``` + +**Mixed Wildcards and Direct Tags**: +``` +Positive: __character__, long flowing hair, wearing elegant white dress, +in a __location__, __art_style__, best quality, 8k +``` + +### Negative Prompts + +Negative prompts define **what you don't want** in the image. + +**Common Approach**: +- Use direct tags for negative prompts +- Wildcards less useful for negatives +- Build standard negative template + +**Standard Negative Tags**: +``` +Negative: low quality, worst quality, blurry, bad anatomy, extra fingers, +poorly drawn hands, deformed, ugly, watermark, signature, text +``` + +**Advanced Negative with Wildcards** (less common): +``` +Negative: __bad_quality__, __bad_anatomy__, __unwanted_elements__ +``` + +### Using Both Together + +Combine wildcards and direct tags for maximum control: + +``` +Positive: +__character__, beautiful detailed eyes, flowing hair, +wearing __clothing__ in a __location__, +__art_style__, __lighting__, +masterpiece, best quality, highly detailed, 8k + +Negative: +low quality, worst quality, blurry, bad anatomy, extra fingers, +poorly drawn, deformed, ugly, watermark, text +``` + +--- + +## LLM Integration + +## LLM Integration + +### Sending to LLM + +The Prompt Builder integrates seamlessly with the LLM tab: + +1. **Build your base prompt** using wildcards and/or tags +2. **Generate** to see a concrete example (optional) +3. **Click "Send to LLM"** button +4. **Prompt is automatically sent** to LLM tab with context +5. **LLM enhances** the prompt +6. **Enhanced version returns** to Prompt Builder automatically + +**Visual Feedback**: +- Button shows "βœ“ Sent!" for 1.5 seconds +- Notification appears confirming transfer +- LLM tab automatically receives prompt + +### What Happens in LLM + +The LLM receives your prompt with helpful context: + +**If sent before generating**: +``` +Enhance this prompt template. It contains wildcards (__name__) that will be +replaced with random values. Improve the structure and add artistic details: + +[Your prompt with wildcards] +``` + +**If sent after generating**: +``` +Enhance this generated prompt with better details, artistic elements, and structure: + +[Your generated prompt] +``` + +You can customize the LLM request or use default enhancement prompts. + +### Receiving Enhanced Prompts + +When the LLM sends a prompt back: + +1. **Notification appears**: "Received text from LLM Tab" +2. **Positive prompt field updates** automatically +3. **Review** the enhanced version +4. **Wildcards preserved** if LLM maintained them +5. **Edit** if needed +6. **Generate** or use directly + +### Workflow Examples + +**Wildcard Enhancement**: +``` +1. You send: __character__ in __location__ +2. LLM enhances: __character__, highly detailed, in a __location__, + cinematic composition, volumetric lighting, trending on artstation +3. Generate creates variations with enhanced template +``` + +**Generated Prompt Enhancement**: +``` +1. Generate: warrior in castle +2. Send to LLM +3. LLM enhances: battle-worn warrior standing in ancient stone castle, + dramatic lighting streaming through gothic windows, leather armor with + intricate details, epic fantasy atmosphere, highly detailed, 8k +``` + +### Manual Editing + +You can always edit prompts manually: + +- **Type directly** in positive/negative fields +- **Add custom tags** not in the library +- **Adjust AI suggestions** to your preference +- **Combine** wildcard-based and freeform text +- **Modify** before or after LLM enhancement + +--- + +## Advanced Features + +### Wildcard Highlighting + +The system automatically detects and highlights wildcards: + +- Valid wildcards show in textarea title +- Count of wildcards displayed +- List of detected wildcards shown on hover +- Invalid syntax warnings (future feature) + +**Viewing Wildcards**: +- Hover over textarea to see detected wildcards +- Check if your `__syntax__` is correct +- Verify wildcard names match available categories + +### Prompt Validation + +Before generating, the system validates: + +- **Wildcard Syntax**: Checks for valid `__name__` format +- **Available Categories**: Warns if wildcard category doesn't exist +- **Empty Prompts**: Prevents generation with no content +- **Seed Value**: Validates seed is a valid number + +**Error Messages**: +- "No wildcards found in prompt" +- "Invalid wildcard syntax" +- "Category '__name__' not found" + +### Seed Management + +Control prompt variation and reproducibility: + +- **Random Seed**: Click to generate new random seed (0-2147483647) +- **Fixed Seed**: Enter specific number for reproducibility +- **Count**: Generate multiple variations with sequential seeds + +**Use Cases**: +- **Random**: Exploring different variations +- **Fixed**: Reproducing exact results +- **Count > 1**: Batch generation with variations + +**Example**: +``` +Prompt: __character__ in __location__ +Seed: 42 +Count: 5 + +Generates 5 prompts using seeds 42, 43, 44, 45, 46 +``` + +### Multiple Variations + +Generate multiple prompts in one click: + +1. Set **Count** to desired number (1-20) +2. Click **Generate** +3. View all results in Results section +4. Each uses sequential seed for reproducibility + +**Results Display**: +- Each variation shown separately +- Seed number displayed for each +- Copy individual results +- Send any result to LLM for further enhancement + +### Custom Wildcard Files + +Create your own wildcard categories: + +1. Navigate to `SageUtils/wildcards/` folder +2. Create `.txt` file named `category.txt` +3. Add one option per line +4. Use as `__category__` in prompts + +**Example Custom Wildcard** (`weapons.txt`): +``` +sword +bow +staff +dagger +hammer +``` + +**Usage**: +``` +__character__ wielding a __weapons__ in __location__ +``` + +### Integration with Nodes + +Send prompts directly to your workflow: + +- **"Send to Node"** button (when node is selected) +- Populates text field in selected CLIPTextEncode or similar node +- Supports positive and negative prompts +- Updates node immediately in workflow + +**Workflow**: +1. Select a text input node in workflow +2. Build/generate prompt in Prompt Builder +3. Click "Send to Node" +4. Node updates with your prompt + +### Receiving Enhanced Prompts + +When the LLM sends a prompt back: + +1. **Notification appears**: "Received text from LLM" +2. **Positive prompt field updates** automatically +3. **Review** the enhanced version +4. **Edit** if needed +5. **Use** in your workflow + +### Manual Editing + +You can always edit prompts manually: + +- **Type directly** in positive/negative fields +- **Add custom tags** not in the library +- **Adjust AI suggestions** to your preference +- **Combine** tag-based and freeform text + +--- + +## Advanced Features + +### Seed Management + +Control prompt variation and reproducibility: + +- **Random Seed**: Click to generate new random seed +- **Fixed Seed**: Enter specific number for reproducibility +- **Count**: Generate multiple variations + +**Use Cases**: +- **Random**: Exploring different variations +- **Fixed**: Reproducing exact results +- **Count > 1**: Batch generation with variations + +### Custom Tag Libraries + +You can extend the built-in tag library: + +1. Navigate to `assets/default_tag_library.json` +2. Add your custom tags following the JSON structure +3. Restart ComfyUI +4. New tags appear in appropriate categories + +**Example Custom Tag**: +```json +{ + "category": "style", + "tags": [ + { + "name": "My Custom Style", + "prompt": "custom artistic style, unique rendering", + "description": "My personal art style preference" + } + ] +} +``` + +### Prompt Templates + +Save successful prompt patterns as templates: + +**Method 1**: Manual Save +- Copy successful prompts to a text file +- Store in `assets/metadata_templates.json` +- Reference when building similar prompts + +**Method 2**: Clipboard +- Use the built-in copy button +- Paste into text editor +- Build your own library + +### Performance Features + +The Prompt Builder includes optimizations: + +- **Debounced Updates**: Text changes wait 300ms before processing +- **Rate Limited Messaging**: Cross-tab updates throttled to prevent overload +- **Efficient Rendering**: Only updates changed elements +- **Memory Management**: Automatic cleanup prevents leaks + +You shouldn't notice these, but they ensure smooth operation! + +--- + +## Keyboard Shortcuts + +### Prompt Fields + +- **Ctrl+Enter**: Generate/update prompt from tags +- **Escape**: Blur (unfocus) text field + +### Workflow + +1. Select tags using mouse/keyboard +2. Press **Ctrl+Enter** in positive field to generate +3. Review result +4. Press **Escape** to unfocus +5. Make adjustments +6. Press **Ctrl+Enter** again + +### Navigation + +- **Tab**: Move between fields +- **Shift+Tab**: Move backwards +- Mouse click to focus specific field + +--- + +## Tips & Best Practices + +### Building Effective Prompts with Wildcards + +βœ… **Start with a Template**: +``` +Good: __character__ __pose__ in __location__, __art_style__, __quality__ +Avoid: random tags without structure +``` + +βœ… **Mix Wildcards and Specific Tags**: +``` +Good: __character__, blue eyes, silver hair, in a __location__, __art_style__ +Avoid: All wildcards OR all specific (use both!) +``` + +βœ… **Layer Your Details**: +1. Core subject (wildcard or specific) +2. Key features (mix of both) +3. Environment (wildcards) +4. Artistic style (wildcards) +5. Quality tags (usually specific) + +βœ… **Use Quality Tags**: +Always include basic quality tags: +``` +masterpiece, best quality, highly detailed +``` + +βœ… **Balance Positive and Negative**: +- Positive: What you want in detail (use wildcards for variation) +- Negative: Common problems (usually specific tags) + +### Wildcard Strategy + +**For Maximum Variation**: +``` +__character__ wearing __clothing__ in a __location__ at __time_of_day__, +__art_style__, __lighting__, __quality__ +``` + +**For Controlled Variation**: +``` +1girl, long hair, wearing __clothing__, standing in a garden, +anime style, __lighting__, masterpiece quality +``` + +**For Minimal Variation**: +``` +1girl, long silver hair, blue eyes, white dress, cherry blossom garden, +anime style, soft lighting, masterpiece quality, 8k +``` + +### Tag Selection Strategy + +**For Character Portraits**: +``` +__character__, __expression__, __clothing__, +simple __background__, __art_style__, __quality__ +``` + +**For Landscape Scenes**: +``` +__location__, __time_of_day__, __weather__, __season__, +__art_style__, __lighting__, __quality__ +``` + +**For Action Scenes**: +``` +__character__ __action__ in __location__, +dynamic angle, __art_style__, __lighting__, __quality__ +``` + +**For Abstract/Artistic**: +``` +__subject__, __color_palette__, __art_style__, +__mood__, __quality__ +``` + +### LLM Enhancement Tips + +**When to Send to LLM**: +- Wildcard template feels basic +- Want creative additions to template +- Need better structure +- Exploring new themes +- Stuck for specific details + +**When to Skip LLM**: +- You have exact wildcards you want +- Using a proven template +- Testing specific wildcard combinations +- Time-sensitive generation +- Want pure randomization without AI interpretation + +**After Receiving Enhancement**: +- Review wildcards (AI might have removed them) +- Check if structure still makes sense +- Remove unwanted additions +- Verify wildcards still work +- Re-generate to test + +### Common Mistakes to Avoid + +❌ **Too Many Wildcards**: +``` +Bad: __a__ __b__ __c__ __d__ __e__ __f__ __g__ +Good: __character__ in __location__, detailed, __art_style__ +``` + +❌ **No Structure**: +``` +Bad: __random__, __stuff__, __things__ +Good: __character__ __pose__ in a __location__, __art_style__, __quality__ +``` + +❌ **Invalid Wildcard Names**: +``` +Bad: __my category__, __123__, __-special-__ +Good: __character__, __art_style__, __quality__ +``` + +❌ **Forgetting Quality Tags**: +``` +Bad: __character__ in __location__ +Good: __character__ in __location__, masterpiece, best quality, highly detailed +``` + +❌ **Only Wildcards in Negative**: +``` +Bad: __bad_things__, __bad_quality__ +Good: low quality, blurry, bad anatomy, poorly drawn +``` + +### Saved Prompts Best Practices + +βœ… **Organize with Categories**: +- Character Portraits +- Landscapes +- Fantasy Scenes +- Sci-Fi Scenes +- etc. + +βœ… **Use Descriptive Names**: +- Good: "Fantasy Warrior - Epic Battle Scene" +- Avoid: "prompt1", "test", "new" + +βœ… **Add Descriptions**: +- Note what makes the prompt special +- List key wildcards used +- Mention best models/settings + +βœ… **Version Your Prompts**: +- "Character Portrait v1" +- "Character Portrait v2 - Enhanced" +- Makes iteration tracking easier + +### Workflow Tips + +**Rapid Exploration**: +1. Create template with many wildcards +2. Set count to 10 +3. Generate batch +4. Pick best results +5. Send best to LLM for enhancement + +**Iterative Refinement**: +1. Start with wildcard template +2. Generate a few variations +3. Replace successful wildcards with specific tags +4. Keep wildcards for elements you want to vary +5. Save as template + +**Consistency Across Set**: +1. Create template with selective wildcards +2. Use fixed seed +3. Generate your set +4. Adjust seed slightly for related variations + +--- +Better: "## Troubleshooting + +### Common Issues + +#### "No wildcards found" +**Problem**: Your prompt doesn't contain valid wildcard syntax +**Solution**: +- Check syntax: must be `__name__` (double underscores each side) +- No spaces: `__art style__` is invalid, use `__art_style__` +- Check spelling of wildcard category names + +#### Wildcards not replaced +**Problem**: Wildcards remain as `__name__` after generation +**Solution**: +- Verify wildcard category exists in tag library +- Check for typos in wildcard name +- Ensure generation button was clicked (not just entered) +- Check console for error messages + +#### "Category not found" +**Problem**: Wildcard category doesn't exist +**Solution**: +- Browse tag library to see available categories +- Check spelling and case sensitivity +- Create custom wildcard file if needed +- Use existing categories from tag library + +#### Can't save prompts +**Problem**: Save button doesn't work or prompts don't persist +**Solution**: +1. Check browser console for errors +2. Verify write permissions to ComfyUI folder +3. Check disk space +4. Try refreshing and saving again + +#### Tag library not loading +**Problem**: Tag library section is empty +**Solution**: +1. Check console for API errors +2. Verify tag routes are registered +3. Check `assets/default_tag_library.json` exists +4. Restart ComfyUI + +#### Send to LLM not working +**Problem**: LLM doesn't receive prompt +**Solution**: +1. Ensure LLM tab exists in sidebar +2. Check browser console for cross-tab messaging errors +3. Try switching to LLM tab first, then send +4. Check notification messages for errors + +#### Generated prompts identical +**Problem**: Multiple generations produce same result +**Solution**: +- Click "Random Seed" for different results +- Change seed value manually +- Check that wildcards are in prompt +- Verify count > 1 if expecting multiple results + +#### Clipboard copy fails +**Problem**: Copy button doesn't work +**Solution**: +1. Check browser clipboard permissions +2. Try manual select and copy (Ctrl+C) +3. Use "Send to Node" instead +4. Check browser console for errors + +### Performance Issues + +#### Slow generation +**Problem**: Takes long time to generate prompts +**Solution**: +- Reduce number of wildcards per prompt +- Lower count value +- Check for very large wildcard files +- Clear saved prompts if database is large + +#### UI lag when typing +**Problem**: Textarea input feels slow +**Solution**: +- This is debouncing (300ms delay) - working as intended +- Reduces server calls while typing +- Wait briefly after typing before expecting updates + +### Getting Help + +**Check Documentation**: +1. This guide for prompt building +2. [LLM Tab Guide](LLM_TAB_GUIDE.md) for LLM features +3. [API Documentation](API.md) for technical details +4. [Architecture](ARCHITECTURE.md) for system design + +**Debug Steps**: +1. Open browser console (F12) +2. Look for error messages (red text) +3. Check network tab for failed API calls +4. Copy error messages when reporting issues + +**Report Issues**: +- Check existing GitHub issues first +- Include error messages from console +- Describe steps to reproduce +- Mention browser and OS +- Include example prompts if relevant + +--- + +**Related Documentation**: +- [LLM Tab Guide](LLM_TAB_GUIDE.md) - AI enhancement features +- [Architecture](ARCHITECTURE.md) - Technical system design +- [API Documentation](API.md) - Backend endpoints and integration" +``` + +❌ **Conflicting Tags**: +``` +Bad: "photorealistic, anime style, cartoon" +Better: "anime style, vibrant colors, detailed shading" +``` + +❌ **Overloading**: +``` +Bad: 50+ tags with everything you can think of +Better: 15-25 well-chosen, specific tags +``` + +❌ **Ignoring Negative Prompts**: +``` +Bad: (empty negative prompt) +Better: "low quality, bad anatomy, blurry, deformed" +``` + +### Workflow Optimization + +**Fast Iteration**: +1. Build base prompt with core tags +2. Generate and review +3. Add refinement tags +4. Generate again +5. Repeat until satisfied + +**Batch Variations**: +1. Build solid base prompt +2. Set count to 4-10 +3. Use random seed +4. Generate multiple variations +5. Pick best, refine further + +**Template Reuse**: +1. Save successful prompts +2. Modify subject/details +3. Keep working structure +4. Adapt to new themes + +--- + +## Troubleshooting + +### Common Issues + +#### Tags not appearing in prompt +**Problem**: Selected tags don't show in generated prompt +**Solution**: +- Click "Generate" or press Ctrl+Enter +- Check if tag category is enabled +- Refresh the tab + +#### "Send to LLM" button doesn't work +**Problem**: No response after clicking +**Solution**: +- Check if LLM tab is loaded +- Ensure ComfyUI server is running +- Look for notification confirming send +- Check browser console for errors + +#### Prompt too long +**Problem**: Generated prompt exceeds model limit +**Solution**: +- Remove less important tags +- Focus on core elements +- Split into multiple generations +- Use more concise tags + +#### LLM enhancement doesn't return +**Problem**: Sent to LLM but no response received +**Solution**: +- Check LLM tab for errors +- Ensure generation completed +- Click "Send to Prompt Builder" manually +- Check cross-tab messaging is working + +#### Random seed not changing +**Problem**: Same seed generates same images +**Solution**: +- Click "Random Seed" button again +- Manually enter different number +- Refresh the tab if stuck + +### Performance Issues + +#### Slow typing in text fields +**Problem**: Lag when typing in prompt fields +**Solution**: +- This is normal (300ms debouncing) +- Type your full text, updates happen after pause +- Feature prevents excessive updates + +#### Tags not loading +**Problem**: Tag categories are empty +**Solution**: +- Check `assets/default_tag_library.json` exists +- Verify JSON format is valid +- Restart ComfyUI +- Check browser console for errors + +### Getting Help + +If issues persist: + +1. Check [GitHub Issues](https://github.com/arcum42/ComfyUI_SageUtils/issues) +2. Review [LLM Tab Guide](LLM_TAB_GUIDE.md) for LLM integration +3. Check [Architecture](ARCHITECTURE.md) for technical details +4. Open new issue with: + - Steps to reproduce + - Expected vs actual behavior + - Screenshots + - Browser console errors + +--- + +## Comparison: Prompt Builder vs LLM Tab + +| Feature | Prompt Builder | LLM Tab | +|---------|---------------|---------| +| **Input Method** | Tag selection | Natural language | +| **Best For** | Structured, consistent prompts | Creative, freeform requests | +| **Speed** | Fast tag picking | Depends on LLM response | +| **Precision** | High (exact tags) | Variable (AI interpretation) | +| **Creativity** | Limited to tags | High (AI suggestions) | +| **Learning Curve** | Low (guided) | Medium (requires prompting skill) | +| **Negative Prompts** | Dedicated field | Must request explicitly | +| **Reproducibility** | High | Medium | +| **Ideal Use** | Technical control | Creative exploration | + +**Recommendation**: Use both together! Start in Prompt Builder for structure, enhance in LLM for creativity. + +--- + +## What's Next? + +- Learn about [LLM Tab](LLM_TAB_GUIDE.md) for prompt enhancement +- Explore [Cross-Tab Integration](../README.md#cross-tab-integration) +- Review [API Documentation](API.md) for custom integrations +- Check [Architecture](ARCHITECTURE.md) for technical details + +--- + +**Build better prompts!** 🎨✨ diff --git a/js/file/modelBrowser.js b/js/file/modelBrowser.js index 4ebf81c..8429863 100644 --- a/js/file/modelBrowser.js +++ b/js/file/modelBrowser.js @@ -7,10 +7,34 @@ import { createButton, BUTTON_VARIANTS } from "../components/buttons.js"; import { createCard } from "../components/layout.js"; import { copyToClipboard } from "../components/clipboard.js"; +import { createComponentLogger } from "../utils/logger.js"; + +// Component logger +const log = createComponentLogger('ModelBrowser'); /** * Model Browser Class * Handles model listing, selection, filtering, and display + * + * @example + * // Basic usage + * const browser = new ModelBrowser({ + * selectionMode: 'single', + * showFileSize: true, + * onSelect: (hash, modelData) => { + * console.log('Selected:', modelData.path); + * } + * }); + * browser.render(container); + * browser.updateModels(modelsArray); + * + * @example + * // With filtering + * browser.applyFilters({ + * search: 'flux', + * type: 'checkpoints', + * sort: 'name-desc' + * }); */ export class ModelBrowser { /** @@ -884,7 +908,7 @@ export class ModelBrowser { return isDescending ? -comparison : comparison; }); - console.log('[ModelBrowser] After filtering:', filtered.length, 'models (from', this.models.length, 'total)'); + log.debug('After filtering:', filtered.length, 'models (from', this.models.length, 'total)'); this.filteredModels = filtered; this.renderList(); @@ -931,7 +955,7 @@ export class ModelBrowser { } const renderTime = performance.now() - startTime; - console.log(`[ModelBrowser] Rendered ${this.filteredModels.length} models in ${renderTime.toFixed(2)}ms`); + log.debug(`Rendered ${this.filteredModels.length} models in ${renderTime.toFixed(2)}ms`); } /** diff --git a/js/sidebar/modelsTabV2.js b/js/sidebar/modelsTabV2.js index 80bf088..7dd2454 100644 --- a/js/sidebar/modelsTabV2.js +++ b/js/sidebar/modelsTabV2.js @@ -28,6 +28,10 @@ import { pullMetadata, updateCacheInfo } from "../shared/api/cacheApi.js"; // Import utilities import { escapeHtml, generateHtmlContent, openHtmlReport } from "../reports/reportGenerator.js"; +import { createComponentLogger } from "../utils/logger.js"; + +// Component logger +const log = createComponentLogger('ModelsTabV2'); /** * Creates the unified header section with filters and actions @@ -596,6 +600,18 @@ async function openScanDialog() { /** * Main function to create the Models Tab V2 * @param {HTMLElement} container - Container element to populate + * @example + * // Basic usage + * const container = document.getElementById('models-tab'); + * createModelsTabV2(container); + * + * @example + * // Typically called from tab manager + * tabManager.addTab({ + * id: 'models', + * label: 'Models', + * factory: (container) => createModelsTabV2(container) + * }); */ export function createModelsTabV2(container) { // Clear container @@ -678,10 +694,10 @@ export function createModelsTabV2(container) { try { const cacheData = selectors.cacheData(); - console.log('[ModelsTabV2] loadModels called, cacheData:', cacheData); + log.debug('loadModels called, cacheData:', cacheData); if (!cacheData || !cacheData.hash) { - console.warn('[ModelsTabV2] No cache data or hash found'); + log.warn('No cache data or hash found'); modelBrowser.updateModels([]); return; } @@ -716,7 +732,7 @@ export function createModelsTabV2(container) { const models = Array.from(modelsByHash.values()); - console.log('[ModelsTabV2] Loaded models:', models.length, '(deduplicated from', Object.keys(cacheData.hash).length, 'paths)'); + log.debug('Loaded models:', models.length, '(deduplicated from', Object.keys(cacheData.hash).length, 'paths)'); modelBrowser.updateModels(models); applyCurrentFilters();