tebebe's picture
Laya DevOps v29 early stage preview: weights, method, paired audit and integration
aa73ff9 verified
Raw History Blame Contribute Delete
7.02 kB
"""Redacted diagnostics for Claude → LiteLLM → Laya; never executes a tool."""
import argparse
import json
import os
import re
import shutil
import subprocess
import sys
import urllib.error
import urllib.request
from pathlib import Path
HERE=Path(__file__).resolve().parent
def classify_error(error):
if isinstance(error,urllib.error.HTTPError):return 'http_'+str(error.code)
if isinstance(error,TimeoutError):return 'timeout'
if isinstance(error,urllib.error.URLError):return 'network_error'
if isinstance(error,FileNotFoundError):return 'missing_file'
if isinstance(error,json.JSONDecodeError):return 'invalid_json'
if isinstance(error,ValueError) and str(error)=='Missing API credential':return 'missing_credential'
return 'invalid_configuration_or_response'
def diagnose(config_path,settings_path,probe=False):
report={'python_version':sys.version.split()[0],'runtime_files':{name:(config_path.parent/name).is_file() for name in ('hook.py','global_hook.py','questions.json','config.json')},'probe_executes_command':False}
duplicates=[]
def pairs(items):
result={}
for k,v in items:
if k in result:duplicates.append(k)
result[k]=v
return result
try:settings=json.loads(settings_path.read_text(),object_pairs_hook=pairs)
except Exception as error:settings={};report['settings_error']=classify_error(error)
report['duplicate_json_keys']=sorted(set(duplicates))
hooks=settings.get('hooks',{})
report['hooks_in_hooks_section']={event:any('laya/global_hook.py' in h.get('command','') for group in hooks.get(event,[]) for h in group.get('hooks',[])) for event in ('UserPromptSubmit','PreToolUse')}
report['misplaced_top_level_hook_keys']=[name for name in ('PreToolUse','UserPromptSubmit') if name in settings]
report['disableAllHooks']=bool(settings.get('disableAllHooks'))
report['bash_matcher_present']=any(group.get('matcher') in ('Bash','^Bash$','.*','*',None) and any('global_hook.py' in h.get('command','') for h in group.get('hooks',[])) for group in hooks.get('PreToolUse',[]))
executable=shutil.which('claude');report['claude_found']=bool(executable)
if executable:
try:
version=subprocess.check_output([executable,'--version'],text=True,timeout=5)
found=re.search(r'\d+\.\d+\.\d+',version)
report['claude_version']=found[0] if found else 'unrecognized'
except Exception:report['claude_version']='unavailable'
environment={**os.environ,**{k:str(v) for k,v in settings.get('env',{}).items()}}
report['hooks_disabled_environment']=[k for k in ('CLAUDE_CODE_SAFE_MODE','CLAUDE_CODE_SIMPLE') if environment.get(k) in ('1','true')]
try:
config=json.loads(config_path.read_text());credential_env=config.get('api_key_env')
if credential_env and not re.fullmatch(r'[A-Z_][A-Z0-9_]*',credential_env):raise ValueError('Invalid credential variable name')
report['credential_env']=credential_env
report['credential_present']=bool(environment.get(credential_env,'').strip()) if credential_env else bool(config.get('api_key_file') and Path(config['api_key_file']).expanduser().is_file())
report['other_credential_env_names_present']=[k for k in ('ANTHROPIC_AUTH_TOKEN','ANTHROPIC_API_KEY','LITELLM_API_KEY') if environment.get(k) and k!=credential_env]
endpoint=config['endpoint']
from urllib.parse import urlsplit
u=urlsplit(endpoint)
if u.scheme not in ('http','https') or not u.hostname or u.username or u.password or u.query or u.fragment or u.path!='/v1/systemone':raise ValueError('Invalid endpoint')
report['endpoint']=endpoint;report['model']=config['model']
state=Path(config['state_dir']).expanduser();report['stored_contexts']=len(list(state.glob('*.json')))
report['oversized_contexts']=sum(bool(json.loads(p.read_text()).get('exceeded')) for p in state.glob('*.json'))
path=config_path.parent/'diagnostics.jsonl'
if path.exists():
# Read a bounded tail; report only known code fields.
with path.open('rb') as f:
f.seek(max(0,path.stat().st_size-16384));lines=f.read().splitlines()
recent=[]
for line in lines[-12:]:
try:row=json.loads(line);recent.append({k:row[k] for k in ('utc','event','tool','status','decision','error') if k in row})
except ValueError:pass
report['recent_hook_events']=recent
else:report['recent_hook_events']=[]
# Import the actual installed client only after reporting missing files.
if not all(report['runtime_files'].values()):raise FileNotFoundError('Incomplete runtime')
sys.path.insert(0,str(config_path.parent))
import hook as core
key=environment.get(credential_env,'').strip() if credential_env else Path(config['api_key_file']).expanduser().read_text().strip()
if not key:raise ValueError('Missing API credential')
request=urllib.request.Request(endpoint.rsplit('/v1/systemone',1)[0]+'/v1/models',headers={'Authorization':'Bearer '+key})
with urllib.request.build_opener(urllib.request.ProxyHandler({}),core.NoRedirect()).open(request,timeout=8) as response:
raw=response.read(65537)
if len(raw)>65536:raise ValueError('Oversized model list')
models=json.loads(raw)
report['models_endpoint']='ok';report['model_accessible']=config['model'] in {r.get('id') for r in models.get('data',[])}
if probe:
config['questions']=json.loads((config_path.parent/'questions.json').read_text())
state={'tool':'Bash','command':'uptime','trusted_user_request':'Inspect the stage load status without changes or secrets.','environment':'stage','trusted_context':'Ordinary uptime output is non-sensitive operational metadata. This is a classifier connectivity test; no command is executed.'}
report['probe_decision']=core.query(config,state,environ=environment)
report['probe_status']='ok'
except Exception as error:report['error']=classify_error(error)
report['configuration_checks_passed']=not report.get('error') and all(report['hooks_in_hooks_section'].values()) and report['bash_matcher_present'] and not report['disableAllHooks'] and not report['misplaced_top_level_hook_keys'] and not report['duplicate_json_keys'] and not report['hooks_disabled_environment'] and bool(report.get('model_accessible'))
return report
def main():
p=argparse.ArgumentParser(description=__doc__);p.add_argument('--config',type=Path,default=HERE/'config.json');p.add_argument('--settings',type=Path,default=Path.home()/'.claude/settings.json');p.add_argument('--probe',action='store_true');a=p.parse_args()
result=diagnose(a.config,a.settings,a.probe);print(json.dumps(result,ensure_ascii=False,indent=2))
raise SystemExit(0 if result['configuration_checks_passed'] else 1)
if __name__=='__main__':main()