diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..8991d52 --- /dev/null +++ b/LICENSE.txt @@ -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. diff --git a/README.md b/README.md index 7efd20b..83ee0ae 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/__init__.py b/__init__.py index cc0100a..b61ed09 100644 --- a/__init__.py +++ b/__init__.py @@ -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 diff --git a/nodes/__init__.py b/nodes/__init__.py index 78ea0f4..70f5b28 100644 --- a/nodes/__init__.py +++ b/nodes/__init__.py @@ -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 diff --git a/nodes/eric_gen_searcher_node.py b/nodes/eric_gen_searcher_node.py index 88b804d..0639861 100644 --- a/nodes/eric_gen_searcher_node.py +++ b/nodes/eric_gen_searcher_node.py @@ -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: + BASE PROMPT: + + 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".*?", "", content, flags=re.DOTALL).strip() grounded = re.sub(r".*", "", 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".*?", "", content, flags=re.DOTALL).strip() or prompt + grounded = re.sub(r".*?", "", 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 diff --git a/nodes/eric_reference_describer.py b/nodes/eric_reference_describer.py index 532a5f8..3793ae2 100644 --- a/nodes/eric_reference_describer.py +++ b/nodes/eric_reference_describer.py @@ -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 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4ec8186 --- /dev/null +++ b/pyproject.toml @@ -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"] diff --git a/workflows/Gen_Search_Prompting.png b/workflows/Gen_Search_Prompting.png new file mode 100644 index 0000000..005cd29 Binary files /dev/null and b/workflows/Gen_Search_Prompting.png differ diff --git a/workflows/Generation_example.png b/workflows/Generation_example.png new file mode 100644 index 0000000..6b21fa8 Binary files /dev/null and b/workflows/Generation_example.png differ diff --git a/workflows/Results.png b/workflows/Results.png new file mode 100644 index 0000000..39f39ca Binary files /dev/null and b/workflows/Results.png differ diff --git a/workflows/Sharpening.png b/workflows/Sharpening.png new file mode 100644 index 0000000..a00e951 Binary files /dev/null and b/workflows/Sharpening.png differ diff --git a/workflows/workflow.png b/workflows/workflow.png new file mode 100644 index 0000000..2aa8b45 Binary files /dev/null and b/workflows/workflow.png differ