feat(external-llm): add external LLM integration (fc-004)
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>
This commit is contained in:
98
mcp/llm-router/toggle.py
Executable file
98
mcp/llm-router/toggle.py
Executable file
@@ -0,0 +1,98 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user