Implements external LLM routing via opencode CLI for: - GitHub Copilot (gpt-5.2, claude-sonnet-4.5, claude-haiku-4.5, o3, gemini-3-pro) - Z.AI (glm-4.7 for code generation) - OpenCode native (big-pickle) Components: - mcp/llm-router/invoke.py: Main router with task-based model selection - mcp/llm-router/delegate.py: Agent delegation helper (respects external mode) - mcp/llm-router/toggle.py: Enable/disable external-only mode - mcp/llm-router/providers/: CLI wrappers for opencode and gemini Features: - Persistent toggle via state/external-mode.json - Task routing: reasoning -> gpt-5.2, code-gen -> glm-4.7, long-context -> gemini - Claude tier mapping: opus -> gpt-5.2, sonnet -> claude-sonnet-4.5, haiku -> claude-haiku-4.5 - Session-start hook announces when external mode is active - Natural language toggle support via component registry Plan: gleaming-routing-mercury Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
99 lines
2.5 KiB
Python
Executable File
99 lines
2.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Toggle external-only mode.
|
|
|
|
Usage:
|
|
toggle.py on [--reason "user requested"]
|
|
toggle.py off
|
|
toggle.py status
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
STATE_FILE = Path.home() / ".claude/state/external-mode.json"
|
|
|
|
|
|
def load_state() -> dict:
|
|
"""Load current state."""
|
|
if STATE_FILE.exists():
|
|
with open(STATE_FILE) as f:
|
|
return json.load(f)
|
|
return {"enabled": False, "activated_at": None, "reason": None}
|
|
|
|
|
|
def save_state(state: dict):
|
|
"""Save state to file."""
|
|
with open(STATE_FILE, "w") as f:
|
|
json.dump(state, f, indent=2)
|
|
|
|
|
|
def enable(reason: Optional[str] = None):
|
|
"""Enable external-only mode."""
|
|
state = {
|
|
"enabled": True,
|
|
"activated_at": datetime.now().isoformat(),
|
|
"reason": reason or "user-requested"
|
|
}
|
|
save_state(state)
|
|
print("External-only mode ENABLED")
|
|
print(f" Activated: {state['activated_at']}")
|
|
print(f" Reason: {state['reason']}")
|
|
print("\nAll agent requests will now use external LLMs.")
|
|
print("Run 'toggle.py off' or '/pa --external off' to disable.")
|
|
|
|
|
|
def disable():
|
|
"""Disable external-only mode."""
|
|
state = {
|
|
"enabled": False,
|
|
"activated_at": None,
|
|
"reason": None
|
|
}
|
|
save_state(state)
|
|
print("External-only mode DISABLED")
|
|
print("\nAll agent requests will now use Claude.")
|
|
|
|
|
|
def status():
|
|
"""Show current mode status."""
|
|
state = load_state()
|
|
if state.get("enabled"):
|
|
print("External-only mode: ENABLED")
|
|
print(f" Activated: {state.get('activated_at', 'unknown')}")
|
|
print(f" Reason: {state.get('reason', 'unknown')}")
|
|
else:
|
|
print("External-only mode: DISABLED")
|
|
print(" Using Claude for all requests.")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="Toggle external-only mode")
|
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
|
|
# on command
|
|
on_parser = subparsers.add_parser("on", help="Enable external-only mode")
|
|
on_parser.add_argument("--reason", help="Reason for enabling")
|
|
|
|
# off command
|
|
subparsers.add_parser("off", help="Disable external-only mode")
|
|
|
|
# status command
|
|
subparsers.add_parser("status", help="Show current mode")
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.command == "on":
|
|
enable(args.reason)
|
|
elif args.command == "off":
|
|
disable()
|
|
elif args.command == "status":
|
|
status()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|