Add LICENSE, pyproject.toml, workflow screenshots, and update script headers

- LICENSE.txt: dual license (CC BY-NC 4.0 / commercial) with full
  attribution for models (Gen-Searcher-8B, Qwen3-VL-8B), runtime
  (SGLang), services (Serper, Jina), tools (gallery-dl), and libraries
  (PyTorch, Pillow, NumPy)
- pyproject.toml: project metadata with publisher id 'ericrollei'
- All .py files: expanded license/attribution headers per-file
- README.md: added Example Workflow section with workflow.png and
  four annotated screenshots (prompting, results, generation, sharpening)
- workflows/: workflow.png + Gen_Search_Prompting, Results,
  Generation_example, Sharpening screenshots
This commit is contained in:
Eric Rollei
2026-04-29 00:35:09 -07:00
parent e127730be7
commit 43e1502bc0
12 changed files with 452 additions and 54 deletions
+174
View File
@@ -0,0 +1,174 @@
# Dual License
This software is available under a dual licensing model.
## For Non-Commercial Use
This work is licensed under a modified Creative Commons Attribution-NonCommercial 4.0
International License, with additional requirements:
### Attribution Requirements
1. **Mandatory Credit**: You must give appropriate credit to Eric Hiss as the original
author by:
- Including a link to the original repository
(https://github.com/EricRollei/Visual_Research) in any documentation
- Maintaining the original copyright and license notices in all copies or substantial
portions of the software
- Including attribution comments in your code that clearly state
"Based on work by Eric Hiss (https://github.com/EricRollei/Visual_Research)"
2. **Modification Notice**: If you modify the software, you must clearly indicate what
changes were made and attribute the original work to Eric Hiss.
3. **No Endorsement**: You may not use the name of the author or contributors to endorse
or promote products derived from this software without specific prior written permission.
### Additional Restrictions
- **NonCommercial** — You may not use the material for commercial purposes.
- **No Redistribution Without Attribution** — You may not redistribute this code without
the required attribution as specified above.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
PURPOSE AND NONINFRINGEMENT.
### For the full text of the CC BY-NC 4.0 License
Visit http://creativecommons.org/licenses/by-nc/4.0/ or send a letter to Creative Commons,
PO Box 1866, Mountain View, CA 94042, USA.
---
## For Commercial Use
For commercial use of this software, a separate commercial license is required.
Please contact Eric Hiss at eric@rollei.us or eric@historic.camera for licensing options
and terms.
### Commercial License Terms (General Overview)
1. **Definition of Commercial Use**: Any use of the software that is primarily intended
for or directed toward commercial advantage or monetary compensation.
2. **License Grant**: Subject to payment of applicable fees and compliance with the terms
of the commercial license agreement, licensee will be granted a non-exclusive,
non-transferable license to use the software for commercial purposes.
3. **Mandatory Attribution**: All commercial uses must include appropriate attribution to
Eric Hiss and a link to the original repository in a manner appropriate to the medium,
visible to end users.
4. **Restrictions**: The commercial license does not allow redistribution, modification,
or creation of derivative works without explicit permission and additional licensing.
5. **Support**: Commercial licenses may include support options depending on the specific
agreement.
6. **Liability**: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR
A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
THE USE OR OTHER DEALINGS IN THE SOFTWARE.
For detailed commercial license terms and pricing, please contact the author directly.
---
## Models
This project is designed to work with the following AI models:
### Gen-Searcher-8B
A fine-tuned vision-language model trained to perform multi-hop web research for
image generation. Fine-tuned from Qwen3-VL-8B by Eric Hiss.
- Base model: Qwen3-VL-8B (see Qwen3-VL below)
- Fine-tune author: Eric Hiss
- Intended use: agentic web research and visual reference gathering inside ComfyUI
### Qwen3-VL-8B
Vision-language model by Alibaba Cloud / Qwen Team.
- License: Apache 2.0
- Repository: https://github.com/QwenLM/Qwen2.5-VL
- Model card: https://huggingface.co/Qwen/Qwen2.5-VL-7B-Instruct
- Paper: https://arxiv.org/abs/2502.13923
- Copyright: Copyright (c) Alibaba Cloud. All rights reserved.
---
## Inference Runtime
### SGLang
Fast inference and serving framework for LLMs and VLMs.
- License: Apache 2.0
- Repository: https://github.com/sgl-project/sglang
- Copyright: Copyright (c) 2023-2024 SGLang Team
---
## Third-Party Services
This project optionally integrates with external web services. Each service is subject to
its own terms of service and privacy policy, which users are responsible for reviewing and
accepting before use.
### Serper (Google Search API)
Web search service used for research queries.
- Website: https://serper.dev
- Terms of Service: https://serper.dev/terms
- Free tier: 2,500 queries/month; paid plans available
- Users must supply their own API key.
### Jina AI Reader
Web page content extraction service.
- Website: https://jina.ai/reader/
- Terms of Service: https://jina.ai/legal/terms
- Free tier available; paid plans available
- Users must supply their own API key (or use anonymously within rate limits).
---
## Third-Party Tools
### gallery-dl
Command-line program to download image galleries from various websites.
- License: GNU General Public License v2.0 (GPL-2.0)
- Repository: https://github.com/mikf/gallery-dl
- Copyright: Copyright (c) 2014-2024 Mike Fährmann
- Note: gallery-dl is an optional external binary invoked as a subprocess.
It is not bundled with this package. The GPL-2.0 license applies to gallery-dl
itself; this project does not incorporate gallery-dl source code.
---
## Third-Party Libraries
This project depends on the following third-party Python libraries:
### PyTorch
Open source machine learning framework.
- License: BSD 3-Clause ("New BSD License")
- Repository: https://github.com/pytorch/pytorch
- Copyright: Copyright (c) 2016-present, Facebook Inc. All rights reserved.
- Full license text: https://github.com/pytorch/pytorch/blob/main/LICENSE
### Pillow (PIL Fork)
Python Imaging Library fork for image processing.
- License: Historical Permission Notice and Disclaimer (HPND)
- Repository: https://github.com/python-pillow/Pillow
- Copyright: Copyright (c) 2010-present by Jeffrey A. Clark and contributors
- Full license text: https://github.com/python-pillow/Pillow/blob/main/LICENSE
### NumPy
Fundamental package for scientific computing with Python.
- License: BSD 3-Clause
- Repository: https://github.com/numpy/numpy
- Copyright: Copyright (c) 2005-present NumPy Developers. All rights reserved.
- Full license text: https://github.com/numpy/numpy/blob/main/LICENSE.txt
---
Copyright (c) 2026 Eric Hiss. All rights reserved.
+42
View File
@@ -32,6 +32,48 @@ when using web thumbnails with edit-mode models.
---
## Example Workflow
An example ComfyUI workflow is included in the `workflows/` folder.
Open `workflows/workflow.png` in ComfyUI to load it directly (the PNG embeds the full
workflow JSON).
### Full workflow overview
![Full workflow](workflows/workflow.png)
### Prompting — Gen-Searcher research and grounded prompt
The Gen-Searcher node takes your brief subject description, performs multi-hop web research
(text search, image search, optional page browsing), and outputs a rich grounded prompt
along with up to four reference images.
![Prompting section](workflows/Gen_Search_Prompting.png)
### Visual search results
Reference images retrieved from the web and returned as individual IMAGE outputs and a
combined batch tensor.
![Search results](workflows/Results.png)
### Generation
The grounded prompt and (optionally) the combined reference description feed into the
generation node of your choice.
![Generation example](workflows/Generation_example.png)
### Post-processing — sharpening and segmentation
Optional post-processing stage using Richardson-Lucy deconvolution, Smart Sharpening,
subject segmentation masking (SAM + GroundingDINO), depth estimation, and metadata
embedding before saving.
![Sharpening and post-processing](workflows/Sharpening.png)
---
## Typical Workflows
### With any text-to-image model
+13 -2
View File
@@ -1,6 +1,17 @@
# Eric Visual Research - ComfyUI Node Package
# Copyright (c) 2026 Eric Hiss. All rights reserved.
# Licensed under the terms in LICENSE.txt (CC BY-NC 4.0 / Commercial dual license).
# https://github.com/EricRollei/Eric_Visual_Research
# License: CC BY-NC 4.0 (non-commercial) / Commercial license — see LICENSE.txt
# Repository: https://github.com/EricRollei/Visual_Research
#
# Third-party acknowledgements:
# Gen-Searcher-8B fine-tuned Qwen3-VL-8B by Eric Hiss
# Qwen3-VL-8B Alibaba Cloud / Qwen Team (Apache 2.0)
# SGLang LMSYS / SGLang Team (Apache 2.0)
# Serper Search API https://serper.dev (service)
# Jina AI Reader https://jina.ai/reader/ (service)
# gallery-dl Mike Fährmann (GPL-2.0, external binary)
# PyTorch Meta / PyTorch contributors (BSD 3-Clause)
# Pillow Jeffrey A. Clark et al. (HPND)
"""
Eric Visual Research - ComfyUI Node Package
+3 -1
View File
@@ -1,5 +1,7 @@
# Eric Visual Research - Node Package
# Eric Visual Research - Node Registry
# Copyright (c) 2026 Eric Hiss. All rights reserved.
# License: CC BY-NC 4.0 (non-commercial) / Commercial license — see LICENSE.txt
# Repository: https://github.com/EricRollei/Visual_Research
from .eric_gen_searcher_node import EricGenSearcherNode
from .eric_reference_describer import EricReferenceDescriber
+170 -49
View File
@@ -1,6 +1,17 @@
# Eric Visual Research - Eric Gen-Searcher Node
# Copyright (c) 2026 Eric Hiss. All rights reserved.
# Licensed under the terms in LICENSE.txt (CC BY-NC 4.0 / Commercial dual license).
# https://github.com/EricRollei/Eric_Visual_Research
# License: CC BY-NC 4.0 (non-commercial) / Commercial license - see LICENSE.txt
# Repository: https://github.com/EricRollei/Visual_Research
#
# Third-party acknowledgements:
# Gen-Searcher-8B Fine-tuned Qwen3-VL-8B; served via SGLang
# Qwen3-VL-8B Alibaba Cloud / Qwen Team (Apache 2.0)
# SGLang LMSYS / SGLang Team (Apache 2.0)
# Serper Search API https://serper.dev
# Jina AI Reader https://jina.ai/reader/
# gallery-dl Mike Fährmann (GPL-2.0, external binary)
# PyTorch Meta / PyTorch contributors (BSD 3-Clause)
# Pillow Jeffrey A. Clark et al. (HPND)
"""
Eric Gen-Searcher Node
Agentic web research for grounded image generation using Gen-Searcher-8B.
@@ -8,6 +19,13 @@ Agentic web research for grounded image generation using Gen-Searcher-8B.
Platform-agnostic: outputs STRING (grounded prompt) and IMAGE (reference images).
Works with any downstream generation model - Flux, SDXL, Qwen-Image, etc.
Two-field mode (recommended):
base_prompt - creative/compositional direction ("dramatic low-angle portrait...")
search_query - specific subject to research visually ("Zendaya Challengers outfit")
The agent uses search_query for targeted web/image searches, then writes a final
prompt that fulfills base_prompt enriched with the visual details it discovered.
Author: Eric Hiss (GitHub: EricRollei)
"""
@@ -38,13 +56,11 @@ logger = logging.getLogger(__name__)
_PACKAGE_ROOT = Path(__file__).resolve().parent.parent
_CONFIG_PATH = _PACKAGE_ROOT / "api_keys.ini"
_REF_MAX_DIM = 768
_CANDIDATE_MULTIPLIER = 3 # collect 3x slots so we have fallbacks when downloads fail
_CANDIDATE_MULTIPLIER = 3
# gallery-dl paths - auto-detected from common locations, or set explicitly
# in api_keys.ini under [gallery_dl] section
def _find_gallery_dl() -> Tuple[str, str]:
"""Find gallery-dl executable and config from common install locations."""
# Check ini config first
if _CONFIG_PATH.exists():
cfg = configparser.ConfigParser()
cfg.read(str(_CONFIG_PATH), encoding="utf-8")
@@ -53,16 +69,12 @@ def _find_gallery_dl() -> Tuple[str, str]:
if exe and os.path.exists(exe):
return exe, conf
# Common Windows locations
candidates_exe = [
# ComfyUI embedded python
r"A:\Comfy25\ComfyUI_windows_portable\python_embeded\Scripts\gallery-dl.exe",
r"A:\Comfy_Dec\python_embeded\Scripts\gallery-dl.exe",
# Standard Python installs
os.path.join(os.environ.get("APPDATA", ""), "Python", "Scripts", "gallery-dl.exe"),
os.path.join(os.environ.get("LOCALAPPDATA", ""), "Programs", "Python",
"Python312", "Scripts", "gallery-dl.exe"),
# PATH lookup
shutil.which("gallery-dl") or "",
]
candidates_conf = [
@@ -83,7 +95,7 @@ _GALLERY_DL_EXE, _GALLERY_DL_CONF = _find_gallery_dl()
_BLOCKED_CDN_DOMAINS = {
"lookaside.instagram.com", "lookaside.fbsbx.com", "lookaside.facebook.com",
"scontent.fbcdn.net", "scontent.instagram.com",
"scontent.fbcdn.net", "scontent.instagram.com",
"external.fwfw1-1.fna.fbcdn.net", "external.fwfw1-2.fna.fbcdn.net",
"t.co",
}
@@ -132,16 +144,36 @@ def _is_gallery_dl_page(url: str) -> bool:
# System prompt
# ---------------------------------------------------------------------------
_GEN_SEARCHER_SYSTEM = """You are Gen-Searcher, an expert AI assistant for knowledge-grounded image generation. Your task is to research the user's image generation prompt, gather visual references, and then write a final detailed image generation prompt.
_GEN_SEARCHER_SYSTEM = """You are Gen-Searcher, an expert AI assistant for knowledge-grounded image generation. Your task is to research visual references and write a final detailed image generation prompt.
You have access to three tools:
- search(query): Search the web for text information
- image_search(query): Search for reference images of a subject
- browse(url): Read the full content of a web page
## Input Formats
You may receive ONE of two formats:
FORMAT A - Single prompt (research AND generate from the same text):
The user's full prompt. Extract searchable visual subjects and research them.
FORMAT B - Separated fields (best results):
SEARCH QUERY: <specific subject/terms to research visually>
BASE PROMPT: <the creative/compositional direction for the final image>
When FORMAT B is used:
- Use the SEARCH QUERY terms for ALL your web and image searches
- The SEARCH QUERY is specific and visual - trust it for search terms
- The BASE PROMPT describes the creative intent: lighting, mood, composition,
style, camera angle, narrative context
- Your final prompt must FULFILL the BASE PROMPT, ENRICHED with the
accurate visual details you discovered by researching the SEARCH QUERY
## Research Strategy
- Aim for 3-6 searches - enough to be thorough, not exhaustive
- Prioritize visual details: colors, textures, shapes, proportions, distinctive features
- For FORMAT B: focus searches tightly on the SEARCH QUERY subject
- Stop when you have enough visual information to write a complete description
## CRITICAL - Final Output Format
@@ -151,6 +183,8 @@ When you have enough information, output ONLY a single paragraph of descriptive
- Just one flowing paragraph describing exactly what to generate
- Must be 150-300 words of vivid visual description
- Start directly with the subject, style, lighting, etc.
- For FORMAT B: weave BASE PROMPT's creative direction with the visual
details you discovered through the SEARCH QUERY research
Example of correct output:
"A cinematic portrait of a young woman with long dark braided hair, wearing an oversized magenta Adidas hoodie with white logo detailing on the hood panels, standing on a red clay tennis court. The hoodie features drawstring closures and a relaxed athletic fit. She wears white tennis shorts and clean white sneakers. Soft afternoon light falls from the left, casting gentle shadows. The background shows blurred court lines and a net. Shot at eye level with a medium telephoto perspective, shallow depth of field. Photorealistic, high detail."
@@ -307,7 +341,6 @@ def _download_direct(url: str, timeout: int = 10) -> Optional[Image.Image]:
def _download_with_gallery_dl(page_url: str, timeout: int = 45) -> Optional[Image.Image]:
if not _GALLERY_DL_EXE or not os.path.exists(_GALLERY_DL_EXE):
print(f"[EricGenSearcher] gallery-dl not found")
return None
tmp_dir = tempfile.mkdtemp(prefix="gensearch_")
try:
@@ -499,23 +532,71 @@ def _execute_tool(fn_name, fn_args, serper_key, jina_key,
return f"[unknown tool: {fn_name}]"
# ---------------------------------------------------------------------------
# Build user message - handles single-prompt and two-field formats
# ---------------------------------------------------------------------------
def _build_user_message(base_prompt: str, search_query: str) -> str:
"""Construct the user message for the agent.
Two-field format (FORMAT B) when search_query is provided:
Separates the visual search subject from the creative direction,
giving the model clear guidance on what to search for vs what to
generate. Produces better-targeted image searches and more accurate
visual descriptions than prompting with a combined creative prompt.
Single-prompt format (FORMAT A) when search_query is blank:
Original behaviour - agent extracts its own search terms from the prompt.
"""
sq = search_query.strip()
bp = base_prompt.strip()
if sq:
return (
f"SEARCH QUERY (use these specific terms for your web and image searches):\n"
f"{sq}\n\n"
f"BASE PROMPT (the creative/compositional direction for the final image):\n"
f"{bp}\n\n"
f"Research the SEARCH QUERY to discover accurate visual details and find "
f"reference images. Then write a final image generation prompt that fulfills "
f"the BASE PROMPT, enriched with the visual details you discovered."
)
else:
return bp
# ---------------------------------------------------------------------------
# Agent loop
# ---------------------------------------------------------------------------
def _run_agent_loop(
prompt, api_url, model, serper_key, jina_key,
max_hops=8, enable_browse=True, max_ref_images=4,
base_prompt: str,
search_query: str,
api_url: str,
model: str,
serper_key: str,
jina_key: str,
max_hops: int = 8,
enable_browse: bool = True,
max_ref_images: int = 4,
) -> Tuple[str, List[Dict], str]:
"""Multi-hop research loop.
Returns (grounded_prompt, image_entries, search_log).
Uses search_query for targeted searches when provided.
"""
max_candidates = max_ref_images * _CANDIDATE_MULTIPLIER
user_message = _build_user_message(base_prompt, search_query)
messages = [
{"role": "system", "content": _GEN_SEARCHER_SYSTEM},
{"role": "user", "content": prompt},
{"role": "user", "content": user_message},
]
image_entries: List[Dict] = []
log_lines: List[str] = []
hops = 0
grounded = prompt
grounded = base_prompt # safe fallback
while hops < max_hops:
response = _chat_completion(api_url, messages, _TOOLS, model, max_tokens=2048)
@@ -530,7 +611,7 @@ def _run_agent_loop(
grounded = re.sub(r"<think>.*?</think>", "", content, flags=re.DOTALL).strip()
grounded = re.sub(r"<tool_call>.*", "", grounded, flags=re.DOTALL).strip()
if not grounded:
grounded = prompt
grounded = base_prompt
if _is_analysis_output(grounded):
grounded = _cleanup_prompt(grounded, api_url, model, messages + [message])
break
@@ -562,9 +643,9 @@ def _run_agent_loop(
"no explanations. Start with the subject."
),
})
resp = _chat_completion(api_url, messages, None, model, max_tokens=1024)
resp = _chat_completion(api_url, messages, None, model, max_tokens=1024)
content = resp["choices"][0]["message"].get("content", "").strip()
grounded = re.sub(r"<think>.*?</think>", "", content, flags=re.DOTALL).strip() or prompt
grounded = re.sub(r"<think>.*?</think>", "", content, flags=re.DOTALL).strip() or base_prompt
if _is_analysis_output(grounded):
grounded = _cleanup_prompt(grounded, api_url, model, messages)
@@ -624,17 +705,24 @@ class EricGenSearcherNode:
"""
Agentic web research for grounded image generation.
Sends your prompt to a running Gen-Searcher-8B model (via SGLang),
which autonomously searches the web for text information and visual
references, then returns a grounded prompt + reference images.
TWO-FIELD MODE (recommended):
search_query - specific subject to research visually
e.g. "Zendaya Challengers movie tennis outfit"
base_prompt - creative/compositional direction
e.g. "dramatic low-angle portrait, golden hour, fashion photography"
The agent searches for search_query to get accurate visual details,
then writes a prompt that fulfills base_prompt enriched with those details.
Better image searches + better final prompt than single-field mode.
SINGLE-FIELD MODE (backward compatible):
Leave search_query blank. The agent extracts its own search terms
from base_prompt. Same as previous behaviour.
PLATFORM AGNOSTIC - outputs wire into any generation node:
grounded_prompt → any STRING prompt input (Flux, SDXL, Qwen-Image, etc.)
ref_image_1..4 → any IMAGE input or Eric Reference Describer
reference_images → batch IMAGE for multi-image nodes
See docs/SERVER_SETUP.md for SGLang server setup instructions.
See api_keys.ini.example for API key configuration.
reference_images → batch IMAGE tensor
"""
CATEGORY = "Eric/VisualResearch"
@@ -647,24 +735,45 @@ class EricGenSearcherNode:
def INPUT_TYPES(cls):
return {
"required": {
"prompt": ("STRING", {
"base_prompt": ("STRING", {
"multiline": True,
"default": "a portrait of ...",
"tooltip": "Your image generation concept. Gen-Searcher researches it and writes a detailed grounded prompt.",
"default": "",
"tooltip": (
"The creative/compositional direction for the final image.\n"
"Describe mood, lighting, style, camera angle, narrative.\n"
"Examples:\n"
" 'dramatic low-angle portrait, golden hour, shallow depth of field'\n"
" 'fashion editorial, studio lighting, minimalist background'\n\n"
"If search_query is blank, this is also used as the search prompt\n"
"(original single-field behaviour)."
),
}),
"agent_api_url": ("STRING", {
"default": "http://localhost:30000",
"tooltip": (
"URL of the running SGLang server.\n"
"See docs/SERVER_SETUP.md for setup instructions.\n"
"Default: http://localhost:30000"
"See docs/SERVER_SETUP.md for setup instructions."
),
}),
},
"optional": {
"search_query": ("STRING", {
"multiline": False,
"default": "",
"tooltip": (
"Specific subject/terms to search for visually (recommended).\n"
"Keep this focused and visual - it drives the web and image searches.\n"
"Examples:\n"
" 'Zendaya Challengers movie tennis outfit'\n"
" 'Stephen Miller politician appearance'\n"
" 'Ferrari SF-23 F1 car cockpit interior'\n"
" 'Art Deco architecture New York Chrysler building'\n\n"
"Leave blank to use base_prompt for searches (original behaviour)."
),
}),
"model_name": ("STRING", {
"default": "",
"tooltip": "Model name override. Leave blank to auto-detect from server.",
"tooltip": "Model name override. Leave blank to auto-detect.",
}),
"max_hops": ("INT", {
"default": 8, "min": 1, "max": 20, "step": 1,
@@ -676,18 +785,17 @@ class EricGenSearcherNode:
}),
"enable_browse": ("BOOLEAN", {
"default": True,
"tooltip": "Allow reading full web pages via Jina for deeper detail. Disable for faster runs.",
"tooltip": "Allow reading full web pages via Jina. Disable for faster runs.",
}),
"fallback_on_error": ("BOOLEAN", {
"default": True,
"tooltip": "Return original prompt if server is unreachable rather than failing.",
"tooltip": "Return original prompt if server unreachable, rather than failing.",
}),
"use_instagram": ("BOOLEAN", {
"default": False,
"tooltip": (
"Allow gallery-dl to download from Instagram.\n"
"WARNING: Use only with a dedicated dummy account.\n"
"Instagram flags automated access aggressively."
"WARNING: Use only with a dedicated dummy account."
),
}),
"use_facebook": ("BOOLEAN", {
@@ -701,13 +809,14 @@ class EricGenSearcherNode:
}
@classmethod
def IS_CHANGED(cls, prompt, **kwargs):
def IS_CHANGED(cls, base_prompt, search_query="", **kwargs):
return float("nan") # always re-run
def research(
self,
prompt: str,
base_prompt: str,
agent_api_url: str = "http://localhost:30000",
search_query: str = "",
model_name: str = "",
max_hops: int = 8,
ref_image_count: int = 4,
@@ -721,34 +830,46 @@ class EricGenSearcherNode:
if not serper_key:
print(f"[EricGenSearcher] WARNING: No Serper key found.\n"
f" Add to {_CONFIG_PATH}: serper = your-key\n"
f" Or set env var SERPER_API_KEY\n"
f" Get a free key at https://serper.dev")
f" Or set env var SERPER_API_KEY")
model = model_name.strip() if model_name.strip() else _detect_model(agent_api_url)
# Log which mode we're using
sq = search_query.strip()
if sq:
print(f"[EricGenSearcher] TWO-FIELD MODE")
print(f"[EricGenSearcher] search_query: {sq[:100]}")
print(f"[EricGenSearcher] base_prompt: {base_prompt[:100]}{'...' if len(base_prompt) > 100 else ''}")
else:
print(f"[EricGenSearcher] SINGLE-FIELD MODE (no search_query)")
print(f"[EricGenSearcher] base_prompt: {base_prompt[:100]}{'...' if len(base_prompt) > 100 else ''}")
print(f"[EricGenSearcher] model={model} url={agent_api_url}")
print(f"[EricGenSearcher] prompt: {prompt[:100]}{'...' if len(prompt) > 100 else ''}")
if _GALLERY_DL_EXE:
platforms = ["Pinterest/Flickr/500px/Reddit/Twitter"]
if use_instagram: platforms.append("Instagram ⚠")
if use_facebook: platforms.append("Facebook ⚠")
print(f"[EricGenSearcher] gallery-dl: {', '.join(platforms)}")
else:
print(f"[EricGenSearcher] gallery-dl not found - direct download only")
placeholder = torch.ones(1, _REF_MAX_DIM, _REF_MAX_DIM, 3) * 0.5
try:
grounded, image_entries, search_log = _run_agent_loop(
prompt=prompt, api_url=agent_api_url, model=model,
serper_key=serper_key, jina_key=jina_key,
max_hops=max_hops, enable_browse=enable_browse,
max_ref_images=ref_image_count,
base_prompt = base_prompt,
search_query = sq,
api_url = agent_api_url,
model = model,
serper_key = serper_key,
jina_key = jina_key,
max_hops = max_hops,
enable_browse = enable_browse,
max_ref_images = ref_image_count,
)
except RuntimeError as e:
if fallback_on_error:
print(f"[EricGenSearcher] ERROR (fallback): {e}")
return (prompt, placeholder, f"ERROR: {e}",
return (base_prompt, placeholder, f"ERROR: {e}",
placeholder, placeholder, placeholder, placeholder)
raise
+12 -2
View File
@@ -1,6 +1,16 @@
# Eric Visual Research - Eric Reference Describer Node
# Copyright (c) 2026 Eric Hiss. All rights reserved.
# Licensed under the terms in LICENSE.txt (CC BY-NC 4.0 / Commercial dual license).
# https://github.com/EricRollei/Eric_Visual_Research
# License: CC BY-NC 4.0 (non-commercial) / Commercial license — see LICENSE.txt
# Repository: https://github.com/EricRollei/Visual_Research
#
# Third-party acknowledgements:
# Qwen3-VL-8B Alibaba Cloud / Qwen Team (Apache 2.0)
# https://github.com/QwenLM/Qwen2.5-VL
# SGLang LMSYS / SGLang Team (Apache 2.0)
# https://github.com/sgl-project/sglang
# PyTorch Meta / PyTorch contributors (BSD 3-Clause)
# Pillow Jeffrey A. Clark et al. (HPND)
# NumPy NumPy Developers (BSD 3-Clause)
"""
Eric Reference Describer Node
+38
View File
@@ -0,0 +1,38 @@
[project]
name = "comfyui-eric-visual-research"
version = "1.0.1"
description = "Agentic web research nodes for ComfyUI — grounded prompt generation and VL reference description"
readme = "README.md"
license = { file = "LICENSE.txt" }
requires-python = ">=3.10"
authors = [{ name = "Eric Hiss", email = "eric@rollei.us" }]
keywords = [
"comfyui",
"image generation",
"web research",
"grounded prompts",
"visual language model",
"agentic",
]
classifiers = [
"Programming Language :: Python :: 3",
"License :: Other/Proprietary License",
"Operating System :: OS Independent",
"Topic :: Multimedia :: Graphics",
"Topic :: Scientific/Engineering :: Artificial Intelligence",
]
dependencies = [
"Pillow",
"torch",
]
[project.urls]
Homepage = "https://github.com/EricRollei/Visual_Research"
Repository = "https://github.com/EricRollei/Visual_Research"
"Bug Tracker" = "https://github.com/EricRollei/Visual_Research/issues"
[tool.comfy]
PublisherId = "ericrollei"
DisplayName = "Eric Visual Research"
Icon = ""
Tags = ["research", "web search", "grounded prompts", "vision-language", "image generation"]
Binary file not shown.

After

Width:  |  Height:  |  Size: 476 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1013 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB