Z User commited on
Commit
460aac4
·
1 Parent(s): f0b15e1

fix: version display v0.12.0 + auto-inject MEDIA: from execute_code

Browse files

- start.sh: Add version patch block to force __version__='0.12.0' in __init__.py
Maps git date tags (v2026.4.30) to semver (0.12.0) for correct banner display
- patch_auto_media.py: Add Pass 2 response text scanning with regex to catch
files created by execute_code/terminal that don't use write_file tool
- patch_resolve_media_paths.py: Add /data/ to media search directories so
files saved to /data/ root (not just /data/hermes/uploads/) are found

scripts/patch_auto_media.py CHANGED
@@ -1,17 +1,24 @@
1
  #!/usr/bin/env python3
2
- """Patch hermes-agent gateway to auto-inject MEDIA: tags from write_file tool calls.
3
 
4
- Problem: When the LLM creates files via write_file but doesn't include MEDIA: tags
5
- in its final response, the gateway has no way to detect and deliver those files as
6
- native attachments. The LLM may say "I've sent the file" when it actually only saved
7
- it locally.
8
 
9
- Solution: After the existing TTS MEDIA: propagation block in _run_agent(), scan the
10
- assistant messages for write_file tool calls. Extract file paths from the tool call
11
- arguments and append MEDIA: tags for files that:
 
 
 
 
 
 
 
12
  1. Actually exist on disk
13
  2. Have a document/media extension
14
- 3. Are not already referenced in the final_response
15
 
16
  This mirrors the existing TTS propagation pattern (gateway/run.py ~line 10968).
17
  """
@@ -35,10 +42,10 @@ def patch_gateway(filepath: str):
35
 
36
  new = ''' final_response = final_response + "\n" + "\n".join(unique_tags)
37
 
38
- # Auto-inject MEDIA: tags for write_file tool results.
39
- # When the LLM creates files via write_file but forgets to include
40
- # MEDIA: tags in its response, this ensures the files are still
41
- # delivered as native attachments on Feishu/WeChat/etc.
42
  _doc_exts = {
43
  '.md', '.txt', '.csv', '.json', '.xml', '.yaml', '.yml', '.toml', '.log',
44
  '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
@@ -48,6 +55,8 @@ def patch_gateway(filepath: str):
48
  '.ogg', '.opus', '.mp3', '.wav', '.m4a',
49
  }
50
  _auto_media_paths = []
 
 
51
  for _msg in result.get("messages", []):
52
  if _msg.get("role") != "assistant":
53
  continue
@@ -67,26 +76,39 @@ def patch_gateway(filepath: str):
67
  if not _fpath:
68
  continue
69
  _fpath = os.path.expanduser(_fpath)
70
- # Only inject for files with document/media extensions
71
  _ext = os.path.splitext(_fpath)[1].lower()
72
  if _ext not in _doc_exts:
73
  continue
74
- # File must actually exist
75
  if not os.path.isfile(_fpath):
76
  continue
77
- # Avoid duplicates and paths already in response
78
  _media_tag = f"MEDIA:{_fpath}"
79
  if _media_tag in final_response:
80
  continue
81
- if _fpath in final_response:
82
- continue
83
  _auto_media_paths.append(_media_tag)
84
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  if _auto_media_paths:
86
  logger.info(
87
- "Auto-injecting %d MEDIA: tag(s) from write_file tool calls: %s",
88
  len(_auto_media_paths),
89
- ", ".join(os.path.basename(p.split(":")[1]) for p in _auto_media_paths),
90
  )
91
  final_response = final_response + "\n" + "\n".join(_auto_media_paths)
92
 
@@ -102,7 +124,7 @@ def patch_gateway(filepath: str):
102
  with open(filepath, 'w') as f:
103
  f.write(content)
104
 
105
- print(f"Patched {filepath}: auto-inject MEDIA: tags from write_file tool calls")
106
 
107
 
108
  if __name__ == "__main__":
 
1
  #!/usr/bin/env python3
2
+ """Patch hermes-agent gateway to auto-inject MEDIA: tags from tool calls and response text.
3
 
4
+ Problem: When the LLM creates files via write_file/execute_code/terminal but doesn't
5
+ include MEDIA: tags in its final response, the gateway has no way to detect and deliver
6
+ those files as native attachments. The LLM may say "I've sent the file" (hallucination)
7
+ or "I can't send attachments" (also hallucination) when it actually only saved locally.
8
 
9
+ Solution: After the existing TTS MEDIA: propagation block in _run_agent(), run TWO passes:
10
+
11
+ Pass 1 Tool call scanning: scan assistant messages for write_file tool calls and
12
+ extract file paths from the arguments.
13
+
14
+ Pass 2 — Response text scanning: scan final_response for file paths that match
15
+ document/media extensions and exist on disk. This catches files created by
16
+ execute_code, terminal, or any other tool that writes to disk.
17
+
18
+ Files are only injected if:
19
  1. Actually exist on disk
20
  2. Have a document/media extension
21
+ 3. Are not already referenced in the final_response via MEDIA: tag
22
 
23
  This mirrors the existing TTS propagation pattern (gateway/run.py ~line 10968).
24
  """
 
42
 
43
  new = ''' final_response = final_response + "\n" + "\n".join(unique_tags)
44
 
45
+ # Auto-inject MEDIA: tags for tool-created files.
46
+ # When the LLM creates files but forgets to include MEDIA: tags in its
47
+ # response, this ensures the files are still delivered as native attachments
48
+ # on Feishu/WeChat/etc.
49
  _doc_exts = {
50
  '.md', '.txt', '.csv', '.json', '.xml', '.yaml', '.yml', '.toml', '.log',
51
  '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
 
55
  '.ogg', '.opus', '.mp3', '.wav', '.m4a',
56
  }
57
  _auto_media_paths = []
58
+
59
+ # Pass 1: Scan write_file tool calls for file paths
60
  for _msg in result.get("messages", []):
61
  if _msg.get("role") != "assistant":
62
  continue
 
76
  if not _fpath:
77
  continue
78
  _fpath = os.path.expanduser(_fpath)
 
79
  _ext = os.path.splitext(_fpath)[1].lower()
80
  if _ext not in _doc_exts:
81
  continue
 
82
  if not os.path.isfile(_fpath):
83
  continue
 
84
  _media_tag = f"MEDIA:{_fpath}"
85
  if _media_tag in final_response:
86
  continue
 
 
87
  _auto_media_paths.append(_media_tag)
88
 
89
+ # Pass 2: Scan final_response text for file paths created by
90
+ # execute_code/terminal or any other tool.
91
+ # Matches patterns like: /data/hermes/uploads/report.md
92
+ # "/tmp/weather.md"
93
+ # 文件已保存至:/data/hermes/weather.md
94
+ _path_re = re.compile(r'["\'`\u201c]?(/[^\s"\'`\u201d<>|]+?\.(?:md|txt|csv|json|xml|yaml|yml|log|pdf|doc|docx|xls|xlsx|ppt|pptx|html|htm|png|jpg|jpeg|gif|webp|mp4|mp3|wav|zip))\b', re.IGNORECASE)
95
+ for _match in _path_re.finditer(final_response):
96
+ _candidate = _match.group(1)
97
+ _candidate = os.path.expanduser(_candidate)
98
+ if not os.path.isfile(_candidate):
99
+ continue
100
+ _media_tag = f"MEDIA:{_candidate}"
101
+ if _media_tag in final_response:
102
+ continue
103
+ if _media_tag in _auto_media_paths:
104
+ continue
105
+ _auto_media_paths.append(_media_tag)
106
+
107
  if _auto_media_paths:
108
  logger.info(
109
+ "Auto-injecting %d MEDIA: tag(s): %s",
110
  len(_auto_media_paths),
111
+ ", ".join(os.path.basename(p.split(":", 1)[1]) for p in _auto_media_paths),
112
  )
113
  final_response = final_response + "\n" + "\n".join(_auto_media_paths)
114
 
 
124
  with open(filepath, 'w') as f:
125
  f.write(content)
126
 
127
+ print(f"Patched {filepath}: auto-inject MEDIA: tags from write_file + response text scanning")
128
 
129
 
130
  if __name__ == "__main__":
scripts/patch_resolve_media_paths.py CHANGED
@@ -41,7 +41,7 @@ def patch_file(filepath: str):
41
  # Auto-resolve relative media paths to absolute paths.
42
  # The LLM often uses bare filenames in MEDIA: tags (e.g. "report.md")
43
  # which fail because send_document() can't find them.
44
- _media_search_dirs = ['/tmp', '/data/hermes/uploads', os.getcwd()]
45
  _hermes_home = os.path.expanduser('~/.hermes')
46
  if _hermes_home not in _media_search_dirs:
47
  _media_search_dirs.append(_hermes_home)
 
41
  # Auto-resolve relative media paths to absolute paths.
42
  # The LLM often uses bare filenames in MEDIA: tags (e.g. "report.md")
43
  # which fail because send_document() can't find them.
44
+ _media_search_dirs = ['/tmp', '/data/hermes/uploads', '/data', os.getcwd()]
45
  _hermes_home = os.path.expanduser('~/.hermes')
46
  if _hermes_home not in _media_search_dirs:
47
  _media_search_dirs.append(_hermes_home)
start.sh CHANGED
@@ -708,6 +708,26 @@ node index.js >> /data/hermes/logs/webui.log 2>&1 &
708
  WEBUI_PID=$!
709
  echo "[$(date)] WebUI BFF PID: $WEBUI_PID"
710
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
711
  # Trigger hermes-agent auto-update in background (framework first, then UI)
712
  update_hermes_agent_background &
713
 
 
708
  WEBUI_PID=$!
709
  echo "[$(date)] WebUI BFF PID: $WEBUI_PID"
710
 
711
+ # ── Force-correct version display ──
712
+ # Ensure __init__.py shows semver (e.g. 0.12.0) not git tag date (e.g. 2026.4.30)
713
+ # This runs after any potential auto-update has changed the files
714
+ HERMES_INIT="/app/hermes-agent/hermes_cli/__init__.py"
715
+ if [ -f "$HERMES_INIT" ]; then
716
+ # Read the git tag from version file to map date → semver
717
+ CURRENT_TAG="$(cat /data/hermes/agent.version 2>/dev/null | head -1)"
718
+ CURRENT_TAG="${CURRENT_TAG:-v2026.4.30}"
719
+ # Build version mapping: date-tag → semver
720
+ case "$CURRENT_TAG" in
721
+ v2026.4.30) SEMVER="0.12.0"; RDATE="2026.4.30" ;;
722
+ *) SEMVER=""; RDATE="" ;;
723
+ esac
724
+ if [ -n "$SEMVER" ]; then
725
+ sed -i "s/__version__\s*=\s*\"[^"]*\"/__version__ = \"$SEMVER\"/" "$HERMES_INIT"
726
+ sed -i "s/__release_date__\s*=\s*\"[^"]*\"/__release_date__ = \"$RDATE\"/" "$HERMES_INIT"
727
+ echo "Version patched: v$SEMVER ($RDATE)"
728
+ fi
729
+ fi
730
+
731
  # Trigger hermes-agent auto-update in background (framework first, then UI)
732
  update_hermes_agent_background &
733