Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3239f5cc0f | ||
|
|
1d1d32f0cb | ||
|
|
79103ea040 | ||
|
|
794e2db64d | ||
|
|
9a30697d64 | ||
|
|
df5b036b5c | ||
|
|
058c22b97e | ||
|
|
d406c2603f | ||
|
|
32fa53457f | ||
|
|
d62825552f | ||
|
|
da5bba95a6 | ||
|
|
64b4b2f37e |
@@ -13,6 +13,56 @@ Custom nodes for managing [Ollama](https://ollama.com/) models in ComfyUI workfl
|
||||
- 💾 **Model Caching** - Per-endpoint caching for better performance
|
||||
- ✨ **No CORS Issues** - Backend API proxy eliminates browser restrictions
|
||||
|
||||
## � Screenshots
|
||||
|
||||
<!-- TODO: Add workflow screenshots here -->
|
||||
_Screenshots coming soon! See the [Quick Start Guide](#-quick-start-guide) below to get started._
|
||||
|
||||
## �🚀 30-Second Quickstart
|
||||
|
||||
**Want to test it right now?**
|
||||
|
||||
1. **Install Ollama**: Download from [ollama.com](https://ollama.com/) and run `ollama pull llama3.2`
|
||||
2. **Install this extension**: Via ComfyUI-Manager, search "Ollama Manager"
|
||||
3. **Add 3 nodes**: `Ollama Client` → `Ollama Model Selector` → `Ollama Chat Completion`
|
||||
4. **Type a prompt**: "Write a haiku about AI"
|
||||
5. **Execute!** 🎉
|
||||
|
||||
That's it! The model selector auto-fetches your models when you connect the nodes.
|
||||
|
||||
## Before You Begin
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. **Ollama Installed & Running**
|
||||
- Download from [ollama.com](https://ollama.com/)
|
||||
- Verify it's running: `curl http://localhost:11434/api/tags`
|
||||
- Pull at least one model: `ollama pull llama3.2`
|
||||
|
||||
2. **ComfyUI Installed**
|
||||
- Get it from [github.com/comfyanonymous/ComfyUI](https://github.com/comfyanonymous/ComfyUI)
|
||||
|
||||
3. **Python Dependencies** (auto-installed)
|
||||
- httpx ≥0.28.1
|
||||
- loguru ≥0.7.3
|
||||
- rich ≥14.2.0
|
||||
|
||||
### Recommended Models for Testing
|
||||
|
||||
```bash
|
||||
# Small & fast (1.3GB) - Great for testing
|
||||
ollama pull llama3.2
|
||||
|
||||
# Multimodal vision (4.7GB) - For image workflows
|
||||
ollama pull llava
|
||||
|
||||
# Coding assistant (3.8GB) - For code generation
|
||||
ollama pull codellama
|
||||
|
||||
# Check what you have installed
|
||||
ollama list
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
### Recommended: ComfyUI-Manager
|
||||
@@ -204,6 +254,119 @@ The architecture provides a clean, composable workflow:
|
||||
|
||||
This pattern optimizes memory by unloading models when not needed, while maintaining full conversation context and precise control over generation parameters.
|
||||
|
||||
## 📚 Real-World Use Cases
|
||||
|
||||
### Use Case 1: Build a Simple Chatbot
|
||||
|
||||
**Goal**: Create a conversational AI that remembers context
|
||||
|
||||
**Workflow**:
|
||||
```text
|
||||
[Ollama Client] → [Model Selector: llama3.2]
|
||||
↓
|
||||
[Chat Node 1]
|
||||
prompt: "My name is Alice"
|
||||
system: "You are a friendly assistant"
|
||||
↓ (pass history)
|
||||
[Chat Node 2]
|
||||
prompt: "What's my name?"
|
||||
↓
|
||||
Response: "Your name is Alice!"
|
||||
```
|
||||
|
||||
**Why it works**: The `history` output carries conversation context between nodes.
|
||||
|
||||
### Use Case 2: Extract Structured Data
|
||||
|
||||
**Goal**: Parse unstructured text into JSON for downstream processing
|
||||
|
||||
**Workflow**:
|
||||
```text
|
||||
[Ollama Client] → [Model Selector: llama3.2]
|
||||
↓
|
||||
[Chat Completion]
|
||||
format: "json"
|
||||
prompt: "Extract data from: 'John is 35 and lives in NYC'"
|
||||
system: "Return JSON with: name, age, city"
|
||||
↓
|
||||
Output: {"name": "John", "age": 35, "city": "NYC"}
|
||||
```
|
||||
|
||||
**When to use**: Data extraction, API integrations, workflow automation.
|
||||
|
||||
### Use Case 3: Vision + Text Workflows
|
||||
|
||||
**Goal**: Analyze images with AI and generate descriptions
|
||||
|
||||
**Workflow**:
|
||||
```text
|
||||
[Load Image] → [Ollama Client] → [Model Selector: llava]
|
||||
↓
|
||||
[Chat Completion]
|
||||
image: (connected from Load Image)
|
||||
prompt: "Describe this image in detail"
|
||||
↓
|
||||
Response: "A sunset over mountains..."
|
||||
```
|
||||
|
||||
**Models with vision**: `llava`, `llava-llama3`, `bakllava`
|
||||
|
||||
### Use Case 4: Deterministic Code Generation
|
||||
|
||||
**Goal**: Generate the same code every time for testing/CI
|
||||
|
||||
**Workflow**:
|
||||
```text
|
||||
[Ollama Client] → [Model Selector: codellama]
|
||||
↓
|
||||
[Seed=42] → [Temperature=0.0] → [Chat Completion]
|
||||
prompt: "Write a Python function to sort a list"
|
||||
↓
|
||||
(Same code every run - cached!)
|
||||
```
|
||||
|
||||
**Why it works**: Seed + low temperature = deterministic output + ComfyUI caching.
|
||||
|
||||
### Use Case 5: Memory-Efficient Batch Processing
|
||||
|
||||
**Goal**: Process multiple prompts without keeping all models loaded
|
||||
|
||||
**Workflow**:
|
||||
```text
|
||||
[Client] → [Selector: llama3.2] → [Load Model]
|
||||
↓
|
||||
[Chat: Process Batch 1]
|
||||
↓
|
||||
[Unload Model]
|
||||
↓
|
||||
[Selector: codellama] → [Load Model]
|
||||
↓
|
||||
[Chat: Process Batch 2]
|
||||
↓
|
||||
[Unload Model]
|
||||
```
|
||||
|
||||
**When to use**: Limited VRAM, multiple models, sequential processing.
|
||||
|
||||
### Use Case 6: AI-Assisted Image Prompts
|
||||
|
||||
**Goal**: Generate better Stable Diffusion prompts using AI
|
||||
|
||||
**Workflow**:
|
||||
```text
|
||||
[User Input: "cat"] → [Ollama Client] → [Model Selector: llama3.2]
|
||||
↓
|
||||
[Chat Completion]
|
||||
system: "Expand this into a detailed Stable Diffusion prompt"
|
||||
prompt: "cat"
|
||||
↓
|
||||
Response: "A fluffy orange tabby cat with green eyes..."
|
||||
↓
|
||||
[Stable Diffusion Node]
|
||||
```
|
||||
|
||||
**Result**: Better image quality from AI-enhanced prompts!
|
||||
|
||||
## Configuration
|
||||
|
||||
### Ollama Endpoint
|
||||
@@ -309,11 +472,42 @@ Output: {"name": "Alice", "age": 30}
|
||||
```
|
||||
|
||||
**Use cases:**
|
||||
|
||||
- Debugging conversation flow
|
||||
- Monitoring context length
|
||||
- Workflow conditional logic based on message count
|
||||
- Understanding what the model "remembers"
|
||||
|
||||
### Working with Images (Vision Models)
|
||||
|
||||
**Supported Models**: `llava`, `llava-llama3`, `bakllava`
|
||||
|
||||
**Basic Image Analysis**:
|
||||
|
||||
```text
|
||||
[Load Image Node] → [Ollama Chat Completion]
|
||||
├── client: (from Model Selector: llava)
|
||||
├── image: (connected from Load Image)
|
||||
└── prompt: "What do you see in this image?"
|
||||
```
|
||||
|
||||
**Image + Conversation Context**:
|
||||
|
||||
```text
|
||||
[Load Image] → [Chat 1: "Describe this image"]
|
||||
↓ (history + image)
|
||||
[Chat 2: "What colors are dominant?"]
|
||||
↓ (history)
|
||||
[Chat 3: "Suggest a caption"]
|
||||
```
|
||||
|
||||
**Tips for Vision Workflows**:
|
||||
|
||||
- Use `llava` for general image understanding
|
||||
- Vision models are larger (~4-7GB) - ensure adequate VRAM
|
||||
- Image input is optional - node works with/without images
|
||||
- Combine with text-only prompts for creative workflows
|
||||
|
||||
## Logging
|
||||
|
||||
Logs are written to:
|
||||
@@ -328,6 +522,56 @@ Example log output:
|
||||
08:36:32 | INFO | load-def456 | ✅ Model 'llava:latest' loaded successfully
|
||||
```
|
||||
|
||||
## 💡 Tips & Best Practices
|
||||
|
||||
### Performance Optimization
|
||||
|
||||
1. **Keep frequently-used models loaded**: Set `keep_alive: "-1"` to avoid reload delays
|
||||
2. **Unload when switching models**: Free memory for the next model
|
||||
3. **Use smaller models for testing**: `llama3.2:1b` is fast and uses less VRAM
|
||||
4. **Enable caching**: Use `OllamaOptionSeed` for deterministic outputs that cache
|
||||
|
||||
### Workflow Design
|
||||
|
||||
1. **Reuse Client nodes**: Create one client, connect to multiple nodes
|
||||
2. **Chain history properly**: Always connect `history` output → `history` input for conversations
|
||||
3. **Use debug nodes during development**: Monitor conversation length and content
|
||||
4. **Test with small models first**: Validate workflow logic before using large models
|
||||
|
||||
### Prompt Engineering
|
||||
|
||||
1. **Write clear system prompts**: Define the model's role and constraints
|
||||
2. **Be specific in user prompts**: Vague prompts = vague responses
|
||||
3. **Use JSON mode for structured data**: Set `format: "json"` and describe the schema
|
||||
4. **Iterate on temperature**: Start at 0.7, adjust based on creativity needs
|
||||
|
||||
### Resource Management
|
||||
|
||||
1. **Monitor `ollama ps`**: See what's loaded and consuming memory
|
||||
2. **Set appropriate keep_alive**: Balance convenience vs. memory usage
|
||||
- `-1`: Keep forever (for active work)
|
||||
- `5m`: Short tasks
|
||||
- `0`: Unload immediately (memory-constrained systems)
|
||||
3. **Use quantized models**: `model:7b-q4` uses less memory than `model:7b`
|
||||
|
||||
### Error Handling
|
||||
|
||||
1. **Always check Ollama is running**: `curl http://localhost:11434/api/tags`
|
||||
2. **Test endpoints separately**: Verify Ollama works before debugging ComfyUI
|
||||
3. **Check logs first**: `logs/ollama_manager.json` has detailed error info
|
||||
4. **Start simple**: Basic workflow first, add complexity incrementally
|
||||
|
||||
### Model Selection Guide
|
||||
|
||||
| Use Case | Recommended Model | Size | Notes |
|
||||
|----------|------------------|------|-------|
|
||||
| **Quick Testing** | `llama3.2:1b` | 1.3GB | Fast, low memory |
|
||||
| **General Chat** | `llama3.2` | 2GB | Best balance |
|
||||
| **Code Generation** | `codellama` | 3.8GB | Trained on code |
|
||||
| **Image Analysis** | `llava` | 4.7GB | Vision + text |
|
||||
| **Long Context** | `llama3.2:8b` | 4.7GB | Better reasoning |
|
||||
| **Production** | `llama3.2:70b` | 40GB | Highest quality |
|
||||
|
||||
## Requirements
|
||||
|
||||
- Python ≥3.12
|
||||
@@ -376,30 +620,209 @@ pytest
|
||||
|
||||
### Nodes don't appear in ComfyUI
|
||||
|
||||
**Symptoms**: Can't find "Ollama" nodes in the Add Node menu
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Check that dependencies are installed: `pip list | grep -E "httpx|loguru|rich"`
|
||||
2. Restart ComfyUI completely
|
||||
3. Check ComfyUI console for error messages
|
||||
4. Verify Ollama is running: `curl http://localhost:11434/api/tags`
|
||||
2. Restart ComfyUI completely (not just refresh browser)
|
||||
3. Check ComfyUI console for error messages during startup
|
||||
4. Verify the custom_nodes folder: `ls ComfyUI/custom_nodes/comfyui-ollama-model-manager`
|
||||
5. Try reinstalling: `cd custom_nodes/comfyui-ollama-model-manager && python install.py`
|
||||
|
||||
### Model dropdown is empty
|
||||
|
||||
**Symptoms**: Model Selector dropdown shows no models
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Check Ollama is running**: `curl http://localhost:11434/api/tags`
|
||||
- If error: Start Ollama (`ollama serve` or launch the app)
|
||||
2. **Check you have models**: `ollama list`
|
||||
- If empty: Pull a model (`ollama pull llama3.2`)
|
||||
3. **Check endpoint URL**: Make sure the Ollama Client node has the correct endpoint
|
||||
- Default: `http://localhost:11434`
|
||||
- For remote: `http://your-server-ip:11434`
|
||||
4. **Try manual refresh**: Set `refresh` to `true` in Model Selector and re-execute
|
||||
5. **Check logs**: Look in `ComfyUI/custom_nodes/comfyui-ollama-model-manager/logs/`
|
||||
|
||||
### "Connection refused" or "Cannot connect" errors
|
||||
|
||||
**Symptoms**: Errors about connection failures
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Verify Ollama is accessible**:
|
||||
```bash
|
||||
curl http://localhost:11434/api/tags
|
||||
```
|
||||
Should return JSON with model list
|
||||
|
||||
2. **Check firewall** (if using remote Ollama):
|
||||
- Port 11434 must be open
|
||||
- Ollama must be bound to `0.0.0.0` not just `127.0.0.1`
|
||||
- Set environment variable: `OLLAMA_HOST=0.0.0.0:11434`
|
||||
|
||||
3. **Docker users**: Make sure port is exposed
|
||||
```bash
|
||||
docker run -p 11434:11434 ollama/ollama
|
||||
```
|
||||
|
||||
### Import errors
|
||||
|
||||
If you see `ModuleNotFoundError`, install dependencies manually:
|
||||
**Symptoms**: `ModuleNotFoundError: No module named 'httpx'`
|
||||
|
||||
**Solutions**:
|
||||
|
||||
```bash
|
||||
# Standard installation
|
||||
pip install httpx loguru rich
|
||||
|
||||
# With uv (faster)
|
||||
uv pip install httpx loguru rich
|
||||
|
||||
# ComfyUI portable (Windows)
|
||||
ComfyUI\python_embeded\python.exe -m pip install httpx loguru rich
|
||||
|
||||
# Virtual environment
|
||||
source venv/bin/activate # or venv\Scripts\activate on Windows
|
||||
pip install httpx loguru rich
|
||||
```
|
||||
|
||||
### Models loading slowly
|
||||
|
||||
**Symptoms**: Long wait times when loading models
|
||||
|
||||
**Causes & Solutions**:
|
||||
|
||||
1. **Large models take time** - `llava:34b` will be slower than `llama3.2:1b`
|
||||
2. **First load is always slower** - Model needs to be read from disk
|
||||
3. **Check available RAM/VRAM** - Insufficient memory causes swapping
|
||||
4. **Use keep_alive wisely** - Keep frequently-used models loaded with `keep_alive: "-1"`
|
||||
5. **Consider smaller quantized models**: `llama3.2:1b` vs `llama3.2:8b`
|
||||
|
||||
### Permission errors (Windows)
|
||||
|
||||
Close ComfyUI and run:
|
||||
```bash
|
||||
ComfyUI\python_embeded\python.exe -m pip install --upgrade httpx loguru rich
|
||||
**Symptoms**: "Access denied" when installing
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. Close ComfyUI completely
|
||||
2. Run as administrator or use:
|
||||
```bash
|
||||
ComfyUI\python_embeded\python.exe -m pip install --upgrade httpx loguru rich
|
||||
```
|
||||
|
||||
### Chat responses are repetitive or low quality
|
||||
|
||||
**Symptoms**: Model repeats itself or gives poor answers
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Adjust temperature**: Higher = more creative (try 0.7-1.0)
|
||||
2. **Increase repeat_penalty**: Use `OllamaOptionRepeatPenalty` node (try 1.1-1.3)
|
||||
3. **Tune top_p/top_k**: Use `OllamaOptionTopP` and `OllamaOptionTopK`
|
||||
4. **Better system prompt**: Guide the model's behavior more explicitly
|
||||
5. **Try a different model**: Some models are better at certain tasks
|
||||
|
||||
### "Out of memory" errors
|
||||
|
||||
**Symptoms**: CUDA/memory allocation failures
|
||||
|
||||
**Solutions**:
|
||||
|
||||
1. **Unload unused models**: Use the "Ollama Unload Model" node
|
||||
2. **Use smaller models**: `llama3.2:1b` instead of `llama3.2:70b`
|
||||
3. **Reduce context length**: Use `OllamaOptionExtraBody` with `{"num_ctx": 2048}`
|
||||
4. **Check what's loaded**: Run `ollama ps` to see active models
|
||||
5. **Close other applications**: Free up VRAM/RAM
|
||||
|
||||
### Still having issues?
|
||||
|
||||
1. **Check logs**: `ComfyUI/custom_nodes/comfyui-ollama-model-manager/logs/ollama_manager.json`
|
||||
2. **Enable debug logging**: Set log level in `log_config.py`
|
||||
3. **Test Ollama directly**: `curl http://localhost:11434/api/generate -d '{"model":"llama3.2","prompt":"test"}'`
|
||||
4. **Report bugs**: [Open an issue](https://github.com/darth-veitcher/comfyui-ollama-model-manager/issues) with logs
|
||||
|
||||
## FAQ
|
||||
|
||||
### Q: Do I need to run Ollama on the same machine as ComfyUI?
|
||||
|
||||
**A**: No! You can run Ollama on a different machine. Just set the endpoint in the **Ollama Client** node to your remote server's IP:
|
||||
|
||||
```text
|
||||
http://192.168.1.100:11434
|
||||
```
|
||||
|
||||
Make sure Ollama is configured to accept remote connections (`OLLAMA_HOST=0.0.0.0`).
|
||||
|
||||
### Q: Can I use this with OpenAI or other LLM APIs?
|
||||
|
||||
**A**: This extension is specifically for Ollama. However, Ollama is compatible with the OpenAI API format, so you can point OpenAI-compatible nodes at Ollama's endpoint.
|
||||
|
||||
### Q: Why do models load slowly the first time?
|
||||
|
||||
**A**: Ollama loads models from disk into RAM/VRAM. First load is always slower. Once loaded, subsequent generations are fast. Use `keep_alive: "-1"` to keep models resident.
|
||||
|
||||
### Q: How much memory do I need?
|
||||
|
||||
**A**: Depends on the model:
|
||||
|
||||
- `llama3.2:1b` → ~1.5GB RAM/VRAM
|
||||
- `llama3.2:3b` → ~2.5GB RAM/VRAM
|
||||
- `llama3.2:8b` → ~5GB RAM/VRAM
|
||||
- `llama3.2:70b` → ~40GB RAM/VRAM
|
||||
|
||||
Ollama can use CPU RAM if VRAM is insufficient (but slower).
|
||||
|
||||
### Q: Can I run multiple models at once?
|
||||
|
||||
**A**: Yes! Load multiple models and switch between them using different Model Selector nodes. Each model consumes memory independently.
|
||||
|
||||
### Q: Does this work with LoRAs or fine-tuned models?
|
||||
|
||||
**A**: Yes! If you've created or imported models into Ollama, they'll appear in the model list. Use `ollama list` to see all available models.
|
||||
|
||||
### Q: How do I update models?
|
||||
|
||||
**A**: Use Ollama's CLI:
|
||||
|
||||
```bash
|
||||
ollama pull llama3.2 # Updates to latest version
|
||||
```
|
||||
|
||||
The model list in ComfyUI will update automatically.
|
||||
|
||||
### Q: Can I use this offline?
|
||||
|
||||
**A**: Yes! Once models are pulled with `ollama pull`, they're stored locally. No internet needed for inference.
|
||||
|
||||
### Q: What's the difference between this and other LLM nodes?
|
||||
|
||||
**A**: This extension is designed specifically for Ollama with features like:
|
||||
|
||||
- Auto-fetching model lists (no manual entry)
|
||||
- Model loading/unloading for memory management
|
||||
- Native Ollama parameter support (top_k, repeat_penalty, etc.)
|
||||
- Built-in conversation history tracking
|
||||
- No CORS issues (backend proxy)
|
||||
|
||||
## License
|
||||
|
||||
[Add your license here]
|
||||
MIT License - See LICENSE file for details.
|
||||
|
||||
## Credits
|
||||
|
||||
- Built for [ComfyUI](https://github.com/comfyanonymous/ComfyUI)
|
||||
- Uses [Ollama](https://ollama.com/) API
|
||||
- Created by [darth-veitcher](https://github.com/darth-veitcher)
|
||||
|
||||
## Support
|
||||
|
||||
- 🐛 **Report bugs**: [GitHub Issues](https://github.com/darth-veitcher/comfyui-ollama-model-manager/issues)
|
||||
- 💬 **Discussions**: [GitHub Discussions](https://github.com/darth-veitcher/comfyui-ollama-model-manager/discussions)
|
||||
- ⭐ **Star the repo** if you find it useful!
|
||||
|
||||
---
|
||||
|
||||
**Made with ❤️ for the ComfyUI community**
|
||||
|
||||
@@ -98,7 +98,6 @@ class OllamaChatCompletion:
|
||||
RETURN_NAMES = ("response", "history")
|
||||
FUNCTION = "generate"
|
||||
CATEGORY = "Ollama"
|
||||
OUTPUT_NODE = True
|
||||
|
||||
@classmethod
|
||||
def IS_CHANGED(cls, **kwargs) -> float:
|
||||
@@ -122,19 +121,32 @@ class OllamaChatCompletion:
|
||||
if options and isinstance(options, dict) and "seed" in options:
|
||||
# Return a hash of all inputs for cache key
|
||||
# This allows ComfyUI to cache results when inputs are identical
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
cache_key = json.dumps(
|
||||
{
|
||||
"model": kwargs.get("model", ""),
|
||||
"prompt": kwargs.get("prompt", ""),
|
||||
"system_prompt": kwargs.get("system_prompt", ""),
|
||||
"format": kwargs.get("format", "none"),
|
||||
"options": options,
|
||||
},
|
||||
sort_keys=True,
|
||||
)
|
||||
return hash(cache_key)
|
||||
client = kwargs.get("client", {})
|
||||
history = kwargs.get("history", [])
|
||||
image = kwargs.get("image")
|
||||
|
||||
cache_data = {
|
||||
"endpoint": (
|
||||
client.get("endpoint", "") if isinstance(client, dict) else ""
|
||||
),
|
||||
"model": kwargs.get("model", ""),
|
||||
"prompt": kwargs.get("prompt", ""),
|
||||
"system_prompt": kwargs.get("system_prompt", ""),
|
||||
"format": kwargs.get("format", "none"),
|
||||
"options": options,
|
||||
"history": history if history else [],
|
||||
}
|
||||
|
||||
# Include image hash if present (don't include full tensor in cache key)
|
||||
if image is not None:
|
||||
cache_data["has_image"] = True
|
||||
|
||||
cache_key = json.dumps(cache_data, sort_keys=True)
|
||||
# Use hashlib for stable hash across sessions (Python's hash() is randomized)
|
||||
return hashlib.sha256(cache_key.encode()).hexdigest()
|
||||
|
||||
# No seed present - return NaN to force re-execution (non-deterministic)
|
||||
return float("nan")
|
||||
@@ -150,14 +162,11 @@ class OllamaChatCompletion:
|
||||
Returns:
|
||||
True if valid, error message string if invalid
|
||||
"""
|
||||
model = kwargs.get("model", "").strip()
|
||||
if not model:
|
||||
return "Model name cannot be empty. Please connect a model selector."
|
||||
|
||||
prompt = kwargs.get("prompt", "").strip()
|
||||
if not prompt:
|
||||
return "Prompt cannot be empty. Please provide a user message."
|
||||
|
||||
# Note: We intentionally don't validate model or prompt here because:
|
||||
# 1. Empty model validation happens at ComfyUI's node connection level
|
||||
# 2. Empty strings are valid for optional text inputs (system_prompt)
|
||||
# 3. Validation errors were being incorrectly applied to all fields
|
||||
# Let the actual execution handle missing required values
|
||||
return True
|
||||
|
||||
def generate(
|
||||
@@ -165,7 +174,7 @@ class OllamaChatCompletion:
|
||||
client: Dict[str, str],
|
||||
model: str,
|
||||
prompt: str,
|
||||
system_prompt: str = "",
|
||||
system_prompt: str | None = None,
|
||||
history: List[Dict[str, str]] | None = None,
|
||||
options: Dict[str, Any] | None = None,
|
||||
format: str = "none",
|
||||
@@ -199,7 +208,7 @@ class OllamaChatCompletion:
|
||||
messages.extend(history)
|
||||
|
||||
# Add system prompt if provided and not already in history
|
||||
if system_prompt and system_prompt.strip():
|
||||
if system_prompt is not None and system_prompt.strip():
|
||||
# Only add system prompt if it's not already the first message
|
||||
if not messages or messages[0].get("role") != "system":
|
||||
messages.insert(
|
||||
|
||||
@@ -9,6 +9,23 @@ from .log_config import get_logger, set_request_id
|
||||
from .ollama_client import fetch_models_from_ollama, load_model, unload_model
|
||||
from .state import get_endpoint, get_models, set_models
|
||||
|
||||
|
||||
class AnyType(str):
|
||||
"""A special type that is compatible with any other type in ComfyUI.
|
||||
|
||||
This is used for wildcard/passthrough inputs and outputs that should accept
|
||||
any connection type. The __ne__ override makes ComfyUI's type checking
|
||||
accept this as compatible with all types.
|
||||
|
||||
Credit: This pattern is used by pythongossss, rgthree, and ComfyUI-Impact-Pack.
|
||||
"""
|
||||
|
||||
def __ne__(self, __value: object) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
any_type = AnyType("*")
|
||||
|
||||
log = get_logger()
|
||||
|
||||
|
||||
@@ -61,15 +78,16 @@ class OllamaModelSelector:
|
||||
|
||||
@classmethod
|
||||
def INPUT_TYPES(cls):
|
||||
# Get cached models if available
|
||||
cached_models = get_models(None)
|
||||
# Provide a placeholder that won't make dropdown tiny
|
||||
model_list = cached_models if cached_models else ["<connect client to fetch>"]
|
||||
|
||||
return {
|
||||
"required": {
|
||||
"client": ("OLLAMA_CLIENT",),
|
||||
"model": (model_list,), # COMBO with cached or empty list
|
||||
"model": (
|
||||
"STRING",
|
||||
{
|
||||
"default": "",
|
||||
"tooltip": "Model name - will be auto-populated when you connect a client",
|
||||
},
|
||||
),
|
||||
},
|
||||
"optional": {
|
||||
"refresh": ("BOOLEAN", {"default": False}),
|
||||
@@ -165,11 +183,11 @@ class OllamaLoadModel:
|
||||
),
|
||||
},
|
||||
"optional": {
|
||||
"dependencies": ("*",),
|
||||
"dependencies": (any_type,),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("OLLAMA_CLIENT", "STRING", "*")
|
||||
RETURN_TYPES = ("OLLAMA_CLIENT", "STRING", any_type)
|
||||
RETURN_NAMES = ("client", "result", "dependencies")
|
||||
FUNCTION = "load_model_op"
|
||||
CATEGORY = "Ollama"
|
||||
@@ -217,11 +235,11 @@ class OllamaUnloadModel:
|
||||
"model": ("STRING",),
|
||||
},
|
||||
"optional": {
|
||||
"dependencies": ("*",),
|
||||
"dependencies": (any_type,),
|
||||
},
|
||||
}
|
||||
|
||||
RETURN_TYPES = ("OLLAMA_CLIENT", "STRING", "*")
|
||||
RETURN_TYPES = ("OLLAMA_CLIENT", "STRING", any_type)
|
||||
RETURN_NAMES = ("client", "result", "dependencies")
|
||||
FUNCTION = "unload_model_op"
|
||||
CATEGORY = "Ollama"
|
||||
|
||||
+74
-11
@@ -277,7 +277,8 @@ class TestOllamaChatCompletionNode:
|
||||
|
||||
# Same inputs should produce same hash (cacheable)
|
||||
assert result1 == result2
|
||||
assert isinstance(result1, int) # Hash returns int
|
||||
assert isinstance(result1, str) # Hash returns stable string
|
||||
assert len(result1) == 64 # SHA256 hex digest
|
||||
|
||||
# Different inputs should produce different hash
|
||||
result3 = OllamaChatCompletion.IS_CHANGED(
|
||||
@@ -289,25 +290,87 @@ class TestOllamaChatCompletionNode:
|
||||
)
|
||||
assert result1 != result3
|
||||
|
||||
def test_validate_inputs_empty_model(self):
|
||||
"""Test validation rejects empty model."""
|
||||
def test_validate_inputs_always_passes(self):
|
||||
"""Test that VALIDATE_INPUTS always returns True.
|
||||
|
||||
Validation was removed because:
|
||||
1. ComfyUI was incorrectly applying validation errors to all fields
|
||||
2. Empty model/prompt validation happens at execution time with clearer errors
|
||||
3. Optional fields like system_prompt should allow empty values
|
||||
"""
|
||||
# Test with empty model
|
||||
result = OllamaChatCompletion.VALIDATE_INPUTS(model="", prompt="Test prompt")
|
||||
assert isinstance(result, str)
|
||||
assert "model" in result.lower()
|
||||
assert result is True
|
||||
|
||||
def test_validate_inputs_empty_prompt(self):
|
||||
"""Test validation rejects empty prompt."""
|
||||
# Test with empty prompt
|
||||
result = OllamaChatCompletion.VALIDATE_INPUTS(model="llama3.2", prompt="")
|
||||
assert isinstance(result, str)
|
||||
assert "prompt" in result.lower()
|
||||
assert result is True
|
||||
|
||||
def test_validate_inputs_valid(self):
|
||||
"""Test validation passes with valid inputs."""
|
||||
# Test with valid inputs
|
||||
result = OllamaChatCompletion.VALIDATE_INPUTS(
|
||||
model="llama3.2", prompt="Test prompt"
|
||||
)
|
||||
assert result is True
|
||||
|
||||
# Test with None model
|
||||
result = OllamaChatCompletion.VALIDATE_INPUTS(model=None, prompt="Test")
|
||||
assert result is True
|
||||
|
||||
# Test with None prompt
|
||||
result = OllamaChatCompletion.VALIDATE_INPUTS(model="llama3.2", prompt=None)
|
||||
assert result is True
|
||||
|
||||
@patch("comfyui_ollama_model_manager.chat.run_async")
|
||||
def test_generate_with_none_system_prompt(self, mock_run_async):
|
||||
"""Test that None system_prompt is handled correctly."""
|
||||
mock_run_async.return_value = {
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Hello!",
|
||||
},
|
||||
"done": True,
|
||||
}
|
||||
|
||||
node = OllamaChatCompletion()
|
||||
client = {"endpoint": "http://localhost:11434"}
|
||||
|
||||
# Test with explicit None
|
||||
response, history = node.generate(
|
||||
client=client,
|
||||
model="llama3.2",
|
||||
prompt="Hello",
|
||||
system_prompt=None,
|
||||
)
|
||||
|
||||
assert response == "Hello!"
|
||||
assert len(history) == 2 # user + assistant, no system
|
||||
assert history[0]["role"] == "user"
|
||||
|
||||
@patch("comfyui_ollama_model_manager.chat.run_async")
|
||||
def test_generate_with_none_history(self, mock_run_async):
|
||||
"""Test that None history is handled correctly."""
|
||||
mock_run_async.return_value = {
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "Hello!",
|
||||
},
|
||||
"done": True,
|
||||
}
|
||||
|
||||
node = OllamaChatCompletion()
|
||||
client = {"endpoint": "http://localhost:11434"}
|
||||
|
||||
# Test with explicit None history
|
||||
response, history = node.generate(
|
||||
client=client,
|
||||
model="llama3.2",
|
||||
prompt="Hello",
|
||||
history=None,
|
||||
)
|
||||
|
||||
assert response == "Hello!"
|
||||
assert len(history) == 2 # Fresh conversation
|
||||
|
||||
@patch("comfyui_ollama_model_manager.chat.run_async")
|
||||
def test_generate_simple_prompt(self, mock_run_async):
|
||||
"""Test simple prompt generation."""
|
||||
|
||||
Reference in New Issue
Block a user