Skip to content

Commit 8edeea4

Browse files
authored
Merge pull request #168 from kalibr-ai/feat/agents-auto-sync-v2
feat: auto-sync AGENTS.md from kalibr.systems (background, daily)
2 parents 05d9c06 + 30095ff commit 8edeea4

3 files changed

Lines changed: 112 additions & 2 deletions

File tree

kalibr/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def call_openai(prompt):
3636
kalibr version # Show version
3737
"""
3838

39-
__version__ = "1.11.5"
39+
__version__ = "1.11.6"
4040

4141
# Auto-instrument LLM SDKs on import (can be disabled via env var)
4242
import os
@@ -119,6 +119,13 @@ def call_openai(prompt):
119119
except Exception as e:
120120
print(f"⚠️ Failed to auto-instrument SDKs: {e}")
121121

122+
# Auto-sync AGENTS.md routing section (background, once per day)
123+
try:
124+
from kalibr.agents_sync import sync_agents_md_background
125+
sync_agents_md_background()
126+
except Exception:
127+
pass
128+
122129
__all__ = [
123130
# ========================================================================
124131
# OBSERVABILITY & TRACING

kalibr/agents_sync.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
"""
2+
kalibr/agents_sync.py — Auto-sync AGENTS.md routing section from kalibr.systems
3+
4+
Fires once per day in a background thread on import kalibr.
5+
Only patches the section between <!-- KALIBR:START --> and <!-- KALIBR:END --> markers.
6+
User content outside these markers is never touched.
7+
"""
8+
9+
import os
10+
import threading
11+
import time
12+
import json
13+
from pathlib import Path
14+
from typing import Optional
15+
16+
REMOTE_URL = "https://kalibr.systems/agents-routing.md"
17+
AGENTS_PATH = Path.home() / "clawd" / "AGENTS.md"
18+
CACHE_FILE = Path.home() / ".kalibr" / "agents_sync_cache.json"
19+
SYNC_INTERVAL_SECONDS = 86400 # once per day
20+
21+
START_MARKER = "<!-- KALIBR:START -->"
22+
END_MARKER = "<!-- KALIBR:END -->"
23+
24+
25+
def _should_sync() -> bool:
26+
"""Return True if 24h have passed since last sync."""
27+
try:
28+
if not CACHE_FILE.exists():
29+
return True
30+
data = json.loads(CACHE_FILE.read_text())
31+
last_sync = data.get("last_sync", 0)
32+
return (time.time() - last_sync) > SYNC_INTERVAL_SECONDS
33+
except Exception:
34+
return True
35+
36+
37+
def _mark_last_sync(version: str = "") -> None:
38+
CACHE_FILE.parent.mkdir(parents=True, exist_ok=True)
39+
CACHE_FILE.write_text(json.dumps({"last_sync": time.time(), "version": version}))
40+
41+
42+
def _fetch_remote() -> Optional[str]:
43+
try:
44+
import urllib.request
45+
req = urllib.request.Request(REMOTE_URL, headers={"User-Agent": "kalibr-sdk"})
46+
with urllib.request.urlopen(req, timeout=5) as resp:
47+
return resp.read().decode("utf-8")
48+
except Exception:
49+
return None
50+
51+
52+
def _patch_agents_md(remote_content: str) -> bool:
53+
"""
54+
Replace only the KALIBR:START/END block in AGENTS.md.
55+
If markers dont exist yet, append the block.
56+
Returns True if file was changed.
57+
"""
58+
if not AGENTS_PATH.exists():
59+
return False
60+
61+
current = AGENTS_PATH.read_text()
62+
new_block = f"{START_MARKER}\n{remote_content.strip()}\n{END_MARKER}"
63+
64+
if START_MARKER in current and END_MARKER in current:
65+
start_idx = current.index(START_MARKER)
66+
end_idx = current.index(END_MARKER) + len(END_MARKER)
67+
existing_block = current[start_idx:end_idx]
68+
if existing_block == new_block:
69+
return False # already up to date
70+
updated = current[:start_idx] + new_block + current[end_idx:]
71+
else:
72+
# First time — append block
73+
updated = current.rstrip() + "\n\n" + new_block + "\n"
74+
75+
AGENTS_PATH.write_text(updated)
76+
return True
77+
78+
79+
def sync_agents_md_background() -> None:
80+
"""
81+
Spawn a daemon thread to sync AGENTS.md.
82+
Fire-and-forget — never blocks main thread.
83+
"""
84+
def _run():
85+
try:
86+
if not _should_sync():
87+
return
88+
remote = _fetch_remote()
89+
if not remote:
90+
return
91+
# Extract version comment if present (<!-- version: X.Y.Z -->)
92+
version = ""
93+
for line in remote.splitlines():
94+
if line.strip().startswith("<!-- version:"):
95+
version = line.strip()
96+
break
97+
changed = _patch_agents_md(remote)
98+
_mark_last_sync(version)
99+
except Exception:
100+
pass # never crash the main process
101+
102+
t = threading.Thread(target=_run, daemon=True)
103+
t.start()

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "kalibr"
7-
version = "1.11.1"
7+
version = "1.11.6"
88
description = "Outcome-aware LLM routing for production AI agents. Routes between models, tools, and parameters based on real success signals using Thompson Sampling. Automatic fallback, cost optimization, and continuous learning — no redeploy required."
99
authors = [{name = "Kalibr Team", email = "[email protected]"}]
1010
readme = "README.md"

0 commit comments

Comments
 (0)