#!/usr/bin/env python3 """Patch hermes-agent gateway to auto-resolve relative media paths. ROOT CAUSE: When the LLM uses write_file, it often saves files with relative paths like "广东天气预报.md" or "/tmp/广东天气预报.md", then puts "MEDIA:广东天气预报.md" in the response. The gateway's extract_media() parses this bare filename, but the subsequent send_document() call fails because the path is relative and os.path.isfile() returns False. FIX: After extract_media() returns the media_files list, scan each path: 1. If already absolute and exists → keep as-is 2. If relative → search well-known directories (/tmp/, /data/hermes/uploads/, current working directory, HERMES_HOME) for a file with that name 3. Replace the path with the resolved absolute path 4. If still not found → keep the original (will fail with clear error) This is a surgical patch to the _deliver_response() method in base.py, inserted right after the "media_files, response = self.extract_media(response)" line. """ import re import sys import os import glob def patch_file(filepath: str): with open(filepath, 'r') as f: content = f.read() # Find the insertion point: right after extract_media() call old = """\ # Extract MEDIA: tags (from TTS tool) before other processing media_files, response = self.extract_media(response)""" new = """\ # Extract MEDIA: tags (from TTS tool) before other processing media_files, response = self.extract_media(response) # Auto-resolve relative media paths to absolute paths. # The LLM often uses bare filenames in MEDIA: tags (e.g. "report.md") # which fail because send_document() can't find them. _media_search_dirs = ['/tmp', '/data/hermes/uploads', '/data', os.getcwd()] _hermes_home = os.path.expanduser('~/.hermes') if _hermes_home not in _media_search_dirs: _media_search_dirs.append(_hermes_home) _resolved_media = [] for _mpath, _mvoice in media_files: if os.path.isabs(_mpath) and os.path.isfile(_mpath): _resolved_media.append((_mpath, _mvoice)) continue # Try to find the file in well-known directories _basename = os.path.basename(_mpath) _found = None for _search_dir in _media_search_dirs: _candidate = os.path.join(_search_dir, _basename) if os.path.isfile(_candidate): _found = _candidate break if _found: logger.info("[%s] Resolved relative media path '%s' -> '%s'", self.name, _mpath, _found) _resolved_media.append((_found, _mvoice)) else: # Keep original — will fail with a clear error message _resolved_media.append((_mpath, _mvoice)) logger.warning("[%s] Could not resolve media path '%s' — searched %s", self.name, _mpath, _media_search_dirs) media_files = _resolved_media""" if old not in content: print(f"WARNING: Could not find insertion point in {filepath}", file=sys.stderr) print("The upstream code may have changed. Skipping this patch.", file=sys.stderr) sys.exit(0) content = content.replace(old, new, 1) with open(filepath, 'w') as f: f.write(content) print(f"Patched {filepath}: auto-resolve relative media paths before sending") if __name__ == "__main__": candidates = [ "/app/hermes-agent/gateway/platforms/base.py", ] candidates.extend(glob.glob("/app/venv/lib/**/gateway/platforms/base.py", recursive=True)) filepath = None for c in candidates: if os.path.isfile(c): filepath = c break if not filepath: print("WARNING: base.py not found in any candidate location", file=sys.stderr) print(f"Checked: {candidates}", file=sys.stderr) print("Skipping patch_resolve_media_paths.", file=sys.stderr) sys.exit(0) patch_file(filepath)