Compare commits
4 Commits
codex/twit
...
gemini/iss
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f71e2e6c16 | ||
| fbd1b9e88f | |||
|
|
ea38041514 | ||
| 579a775a0a |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -8,3 +8,4 @@
|
||||
*.db-wal
|
||||
*.db-shm
|
||||
__pycache__/
|
||||
.aider*
|
||||
|
||||
182
tasks.py
182
tasks.py
@@ -15,6 +15,7 @@ from gitea_client import GiteaClient
|
||||
HERMES_HOME = Path.home() / ".hermes"
|
||||
TIMMY_HOME = Path.home() / ".timmy"
|
||||
HERMES_AGENT_DIR = HERMES_HOME / "hermes-agent"
|
||||
HERMES_PYTHON = HERMES_AGENT_DIR / "venv" / "bin" / "python3"
|
||||
METRICS_DIR = TIMMY_HOME / "metrics"
|
||||
REPOS = [
|
||||
"Timmy_Foundation/the-nexus",
|
||||
@@ -35,50 +36,133 @@ def newest_file(directory, pattern):
|
||||
files = sorted(directory.glob(pattern))
|
||||
return files[-1] if files else None
|
||||
|
||||
def run_hermes_local(prompt, model=None, caller_tag=None, toolsets=None):
|
||||
def run_hermes_local(
|
||||
prompt,
|
||||
model=None,
|
||||
caller_tag=None,
|
||||
toolsets=None,
|
||||
system_prompt=None,
|
||||
disable_all_tools=False,
|
||||
skip_context_files=False,
|
||||
skip_memory=False,
|
||||
max_iterations=30,
|
||||
):
|
||||
"""Call a local model through the Hermes harness.
|
||||
|
||||
Uses provider="local-llama.cpp" which routes through the custom_providers
|
||||
entry in config.yaml → llama-server at localhost:8081.
|
||||
Runs Hermes inside its own venv so task execution matches the same
|
||||
environment and provider routing as normal Hermes usage.
|
||||
Returns response text plus session metadata or None on failure.
|
||||
Every call creates a Hermes session with telemetry.
|
||||
"""
|
||||
_model = model or HEARTBEAT_MODEL
|
||||
tagged = f"[{caller_tag}] {prompt}" if caller_tag else prompt
|
||||
|
||||
# Import hermes cli.main directly — no subprocess, no env vars
|
||||
_agent_dir = str(HERMES_AGENT_DIR)
|
||||
if _agent_dir not in sys.path:
|
||||
sys.path.insert(0, _agent_dir)
|
||||
old_cwd = os.getcwd()
|
||||
os.chdir(_agent_dir)
|
||||
|
||||
try:
|
||||
from cli import main as hermes_main
|
||||
import io
|
||||
from contextlib import redirect_stdout, redirect_stderr
|
||||
runner = """
|
||||
import io
|
||||
import json
|
||||
import sys
|
||||
from contextlib import redirect_stderr, redirect_stdout
|
||||
from pathlib import Path
|
||||
|
||||
buf = io.StringIO()
|
||||
err = io.StringIO()
|
||||
kwargs = dict(
|
||||
query=tagged,
|
||||
model=_model,
|
||||
provider="local-llama.cpp",
|
||||
quiet=True,
|
||||
agent_dir = Path(sys.argv[1])
|
||||
query = sys.argv[2]
|
||||
model = sys.argv[3]
|
||||
system_prompt = sys.argv[4] or None
|
||||
disable_all_tools = sys.argv[5] == "1"
|
||||
skip_context_files = sys.argv[6] == "1"
|
||||
skip_memory = sys.argv[7] == "1"
|
||||
max_iterations = int(sys.argv[8])
|
||||
if str(agent_dir) not in sys.path:
|
||||
sys.path.insert(0, str(agent_dir))
|
||||
from hermes_cli.runtime_provider import resolve_runtime_provider
|
||||
from run_agent import AIAgent
|
||||
from toolsets import get_all_toolsets
|
||||
|
||||
buf = io.StringIO()
|
||||
err = io.StringIO()
|
||||
payload = {}
|
||||
exit_code = 0
|
||||
|
||||
try:
|
||||
runtime = resolve_runtime_provider()
|
||||
kwargs = {
|
||||
"model": model,
|
||||
"api_key": runtime.get("api_key"),
|
||||
"base_url": runtime.get("base_url"),
|
||||
"provider": runtime.get("provider"),
|
||||
"api_mode": runtime.get("api_mode"),
|
||||
"acp_command": runtime.get("command"),
|
||||
"acp_args": list(runtime.get("args") or []),
|
||||
"max_iterations": max_iterations,
|
||||
"quiet_mode": True,
|
||||
"ephemeral_system_prompt": system_prompt,
|
||||
"skip_context_files": skip_context_files,
|
||||
"skip_memory": skip_memory,
|
||||
}
|
||||
if disable_all_tools:
|
||||
kwargs["disabled_toolsets"] = sorted(get_all_toolsets().keys())
|
||||
agent = AIAgent(**kwargs)
|
||||
with redirect_stdout(buf), redirect_stderr(err):
|
||||
result = agent.run_conversation(query, sync_honcho=False)
|
||||
payload = {
|
||||
"response": result.get("final_response", ""),
|
||||
"session_id": getattr(agent, "session_id", None),
|
||||
"provider": runtime.get("provider"),
|
||||
"base_url": runtime.get("base_url"),
|
||||
"stdout": buf.getvalue(),
|
||||
"stderr": err.getvalue(),
|
||||
}
|
||||
except Exception as exc:
|
||||
exit_code = 1
|
||||
payload = {
|
||||
"error": str(exc),
|
||||
"stdout": buf.getvalue(),
|
||||
"stderr": err.getvalue(),
|
||||
}
|
||||
|
||||
print(json.dumps(payload))
|
||||
sys.exit(exit_code)
|
||||
"""
|
||||
command = [
|
||||
str(HERMES_PYTHON) if HERMES_PYTHON.exists() else sys.executable,
|
||||
"-c",
|
||||
runner,
|
||||
str(HERMES_AGENT_DIR),
|
||||
tagged,
|
||||
_model,
|
||||
system_prompt or "",
|
||||
"1" if disable_all_tools else "0",
|
||||
"1" if skip_context_files else "0",
|
||||
"1" if skip_memory else "0",
|
||||
str(max_iterations),
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
command,
|
||||
cwd=str(HERMES_AGENT_DIR),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=900,
|
||||
)
|
||||
payload = json.loads((result.stdout or "").strip() or "{}")
|
||||
output = str(payload.get("response", "")).strip()
|
||||
stderr_output = str(payload.get("stderr", "")).strip()
|
||||
stdout_output = str(payload.get("stdout", "")).strip()
|
||||
if result.returncode != 0:
|
||||
raise RuntimeError(
|
||||
(
|
||||
result.stderr
|
||||
or str(payload.get("error", "")).strip()
|
||||
or stderr_output
|
||||
or stdout_output
|
||||
or output
|
||||
or "hermes run failed"
|
||||
).strip()
|
||||
)
|
||||
if toolsets:
|
||||
kwargs["toolsets"] = toolsets
|
||||
with redirect_stdout(buf), redirect_stderr(err):
|
||||
hermes_main(**kwargs)
|
||||
output = buf.getvalue().strip()
|
||||
session_id = None
|
||||
lines = []
|
||||
for line in output.split("\n"):
|
||||
if line.startswith("session_id:"):
|
||||
session_id = line.split(":", 1)[1].strip() or None
|
||||
continue
|
||||
lines.append(line)
|
||||
response = "\n".join(lines).strip()
|
||||
|
||||
session_id = payload.get("session_id")
|
||||
response = output
|
||||
|
||||
# Log to metrics jsonl
|
||||
METRICS_DIR.mkdir(parents=True, exist_ok=True)
|
||||
@@ -100,7 +184,7 @@ def run_hermes_local(prompt, model=None, caller_tag=None, toolsets=None):
|
||||
return {
|
||||
"response": response,
|
||||
"session_id": session_id,
|
||||
"raw_output": output,
|
||||
"raw_output": json.dumps(payload, sort_keys=True),
|
||||
}
|
||||
except Exception as e:
|
||||
# Log failure
|
||||
@@ -116,8 +200,6 @@ def run_hermes_local(prompt, model=None, caller_tag=None, toolsets=None):
|
||||
with open(metrics_file, "a") as f:
|
||||
f.write(json.dumps(record) + "\n")
|
||||
return None
|
||||
finally:
|
||||
os.chdir(old_cwd)
|
||||
|
||||
|
||||
def hermes_local(prompt, model=None, caller_tag=None, toolsets=None):
|
||||
@@ -132,6 +214,28 @@ def hermes_local(prompt, model=None, caller_tag=None, toolsets=None):
|
||||
return result.get("response")
|
||||
|
||||
|
||||
ARCHIVE_EPHEMERAL_SYSTEM_PROMPT = (
|
||||
"You are running a private archive-processing microtask for Timmy.\n"
|
||||
"Use only the supplied user message.\n"
|
||||
"Do not use tools, memory, Honcho, SOUL.md, AGENTS.md, or outside knowledge.\n"
|
||||
"Do not invent facts.\n"
|
||||
"If the prompt requests JSON, return only valid JSON."
|
||||
)
|
||||
|
||||
|
||||
def run_archive_hermes(prompt, caller_tag, model=None):
|
||||
return run_hermes_local(
|
||||
prompt=prompt,
|
||||
model=model,
|
||||
caller_tag=caller_tag,
|
||||
system_prompt=ARCHIVE_EPHEMERAL_SYSTEM_PROMPT,
|
||||
disable_all_tools=True,
|
||||
skip_context_files=True,
|
||||
skip_memory=True,
|
||||
max_iterations=3,
|
||||
)
|
||||
|
||||
|
||||
# ── Know Thy Father: Twitter Archive Ingestion ───────────────────────
|
||||
|
||||
ARCHIVE_DIR = TIMMY_HOME / "twitter-archive"
|
||||
@@ -693,7 +797,7 @@ def _know_thy_father_impl():
|
||||
prior_note=previous_note,
|
||||
batch_rows=batch_rows,
|
||||
)
|
||||
draft_run = run_hermes_local(
|
||||
draft_run = run_archive_hermes(
|
||||
prompt=draft_prompt,
|
||||
caller_tag=f"know-thy-father-draft:{batch_id}",
|
||||
)
|
||||
@@ -707,7 +811,7 @@ def _know_thy_father_impl():
|
||||
return {"status": "error", "reason": "draft pass did not return JSON", "batch_id": batch_id}
|
||||
|
||||
critique_prompt = build_archive_critique_prompt(batch_id=batch_id, draft_payload=draft_payload, batch_rows=batch_rows)
|
||||
critique_run = run_hermes_local(
|
||||
critique_run = run_archive_hermes(
|
||||
prompt=critique_prompt,
|
||||
caller_tag=f"know-thy-father-critique:{batch_id}",
|
||||
)
|
||||
@@ -825,7 +929,7 @@ def _archive_weekly_insights_impl():
|
||||
)
|
||||
|
||||
prompt = build_weekly_insight_prompt(profile=profile, recent_batches=recent_batches)
|
||||
insight_run = run_hermes_local(prompt=prompt, caller_tag="archive-weekly-insights")
|
||||
insight_run = run_archive_hermes(prompt=prompt, caller_tag="archive-weekly-insights")
|
||||
if not insight_run:
|
||||
return {"status": "error", "reason": "insight pass failed"}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user