- Reset master to upstream/main (16,697 commits) - Overlay 2,271 local-only files (skills, tools, workspace, configs, apps) - Restore IDENTITY.md and USER.md templates - Build verified, gateway running, Discord working Co-Authored-By: Claude Opus 4.6 <[email protected]>
126 lines
4.0 KiB
Python
126 lines
4.0 KiB
Python
#!/usr/bin/env python3
|
|
"""collect_usage.py
|
|
Collects usage & health metrics from configured providers and logs into SQLite.
|
|
Runs safely without sudo; writes DB to /home/alex/clawd/data/usage.db
|
|
"""
|
|
import os
|
|
import sqlite3
|
|
import time
|
|
import json
|
|
import subprocess
|
|
from datetime import datetime
|
|
|
|
DB_DIR = os.path.expanduser('~/clawd/data')
|
|
DB_PATH = os.path.join(DB_DIR, 'usage.db')
|
|
LOG_PATH = '/var/log/openclaw/usage.log'
|
|
|
|
os.makedirs(DB_DIR, exist_ok=True)
|
|
|
|
def init_db(conn):
|
|
c = conn.cursor()
|
|
c.execute('''CREATE TABLE IF NOT EXISTS metrics (
|
|
ts INTEGER,
|
|
provider TEXT,
|
|
metric TEXT,
|
|
value REAL,
|
|
raw TEXT
|
|
)''')
|
|
conn.commit()
|
|
|
|
def insert(conn, provider, metric, value, raw=None):
|
|
c = conn.cursor()
|
|
c.execute('INSERT INTO metrics (ts,provider,metric,value,raw) VALUES (?,?,?,?,?)',
|
|
(int(time.time()), provider, metric, value or 0.0, json.dumps(raw) if raw is not None else None))
|
|
conn.commit()
|
|
|
|
def check_ollama():
|
|
try:
|
|
out = subprocess.check_output(['curl','-sS','http://192.168.1.144:11434/api/tags'], timeout=10)
|
|
j = json.loads(out)
|
|
models = len(j.get('models', []))
|
|
return True, {'models': models}
|
|
except Exception as e:
|
|
return False, {'error': str(e)}
|
|
|
|
def check_nvidia():
|
|
# call nvidia_api.py health if available
|
|
script = os.path.expanduser('~/clawd/skills/nvidia-agent/scripts/nvidia_api.py')
|
|
if os.path.exists(script):
|
|
try:
|
|
out = subprocess.check_output([script,'health'], timeout=20)
|
|
j = json.loads(out.decode())
|
|
return True, j
|
|
except Exception as e:
|
|
return False, {'error': str(e)}
|
|
return None, None
|
|
|
|
def check_logs_for_rate_limits(window_minutes=30):
|
|
res = []
|
|
cutoff = time.time() - (window_minutes*60)
|
|
if os.path.exists(LOG_PATH):
|
|
try:
|
|
with open(LOG_PATH,'r') as f:
|
|
for line in f:
|
|
# Expect ISO timestamp at start
|
|
if not line.strip():
|
|
continue
|
|
try:
|
|
ts_part = line.split()[0]
|
|
try:
|
|
ts = datetime.fromisoformat(ts_part).timestamp()
|
|
except Exception:
|
|
ts = None
|
|
except Exception:
|
|
ts = None
|
|
if ts and ts < cutoff:
|
|
continue
|
|
if 'rate_limit' in line or '429' in line or 'RESOURCE_EXHAUSTED' in line:
|
|
res.append(line.strip())
|
|
except Exception:
|
|
pass
|
|
return res
|
|
|
|
|
|
def main():
|
|
conn = sqlite3.connect(DB_PATH)
|
|
init_db(conn)
|
|
|
|
# Ollama
|
|
ok, data = check_ollama()
|
|
if ok:
|
|
insert(conn, 'ollama', 'models_count', data.get('models',0), data)
|
|
else:
|
|
insert(conn, 'ollama', 'up', 0, data)
|
|
|
|
# NVIDIA
|
|
ok, data = check_nvidia()
|
|
if ok is True:
|
|
insert(conn, 'nvidia', 'models_count', data.get('models_count',0), data)
|
|
elif ok is False:
|
|
insert(conn, 'nvidia', 'up', 0, data)
|
|
|
|
# Logs rate limits
|
|
rl = check_logs_for_rate_limits(30)
|
|
insert(conn, 'logs', 'rate_limit_hits_30m', len(rl), {'lines': rl[:10]})
|
|
|
|
# Optionally OpenAI admin cost (if key present)
|
|
admin_key_path = os.path.expanduser('~/.openclaw/credentials/openai-admin.key')
|
|
if os.path.exists(admin_key_path):
|
|
try:
|
|
key = open(admin_key_path).read().strip()
|
|
import requests
|
|
r = requests.get('https://api.openai.com/v1/usage', headers={'Authorization': f'Bearer {key}'}, timeout=10)
|
|
if r.status_code == 200:
|
|
js = r.json()
|
|
# placeholder parsing
|
|
insert(conn, 'openai', 'admin_raw_ok', 1, js)
|
|
else:
|
|
insert(conn, 'openai', 'admin_ok', 0, {'status': r.status_code, 'body': r.text})
|
|
except Exception as e:
|
|
insert(conn, 'openai', 'admin_ok', 0, {'error': str(e)})
|
|
|
|
conn.close()
|
|
|
|
if __name__ == '__main__':
|
|
main()
|