diff --git a/cortex/cli.py b/cortex/cli.py index 17004c6..44b2ebc 100644 --- a/cortex/cli.py +++ b/cortex/cli.py @@ -11,7 +11,6 @@ logging.getLogger("cortex.installation_history").setLevel(logging.ERROR) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) - from LLM.interpreter import CommandInterpreter from cortex.coordinator import InstallationCoordinator, StepStatus from cortex.installation_history import ( @@ -53,6 +52,8 @@ def _debug(self, message: str): """Print debug info only in verbose mode""" if self.verbose: console.print(f"[dim][DEBUG] {message}[/dim]") + + def _get_api_key(self) -> Optional[str]: # Check if using Ollama (no API key needed) @@ -202,7 +203,6 @@ def install(self, software: str, execute: bool = False, dry_run: bool = False): interpreter = CommandInterpreter(api_key=api_key, provider=provider) self._print_status("📦", "Planning installation...") - for _ in range(10): self._animate_spinner("Analyzing system requirements...") self._clear_line() @@ -310,7 +310,56 @@ def progress_callback(current, total, step): history.update_installation(install_id, InstallationStatus.FAILED, str(e)) self._print_error(f"Unexpected error: {str(e)}") return 1 + + def diagnose(self,error: Optional[str] = None,image: Optional[str] = None,clipboard: bool = False,): + """Diagnose an error from text input (MVP: text-only).""" + + if not error: + self._print_error("Please provide an error message to diagnose") + return 1 + + cx_print("Diagnosing error...", "info") + + provider = self._get_provider() + + # 🔹 OFFLINE FALLBACK (NO API KEYS REQUIRED) + if provider == "ollama": + print("\nDiagnosis:\n") + print( + f"Detected error:\n{error}\n\n" + "Possible causes:\n" + "- Interrupted package installation\n" + "- Broken dpkg state\n" + "- Locked apt database\n\n" + "Suggested fixes:\n" + "sudo dpkg --configure -a\n" + "sudo apt --fix-broken install\n" + "sudo apt update\n" + ) + return 0 + + # 🔹 LLM PATH (Claude / Kimi later) + api_key = self._get_api_key() + if not api_key: + return 1 + try: + from cortex.llm_router import complete_task, TaskType + + response = complete_task( + prompt=f"Diagnose this Linux error and suggest fixes:\n{error}", + task_type=TaskType.ERROR_DEBUGGING, + ) + + print("\nDiagnosis:\n") + print(response) + return 0 + + except Exception as e: + self._print_error(str(e)) + return 1 + + def history(self, limit: int = 20, status: Optional[str] = None, show_id: Optional[str] = None): """Show installation history""" history = InstallationHistory() @@ -577,6 +626,26 @@ def main(): install_parser.add_argument('software', type=str, help='Software to install') install_parser.add_argument('--execute', action='store_true', help='Execute commands') install_parser.add_argument('--dry-run', action='store_true', help='Show commands only') + + # Diagnose command + diagnose_parser = subparsers.add_parser( + 'diagnose', + help='Diagnose an error from text or image' + ) + diagnose_parser.add_argument( + 'error', + nargs='?', + help='Error message text' + ) + diagnose_parser.add_argument( + '--image', + help='Path to error screenshot (PNG, JPG, WebP)' + ) + diagnose_parser.add_argument( + '--clipboard', + action='store_true', + help='Use image from clipboard' + ) # History command history_parser = subparsers.add_parser('history', help='View history') @@ -634,6 +703,12 @@ def main(): return cli.status() elif args.command == 'install': return cli.install(args.software, execute=args.execute, dry_run=args.dry_run) + elif args.command == 'diagnose': + return cli.diagnose( + error=args.error, + image=args.image, + clipboard=args.clipboard + ) elif args.command == 'history': return cli.history(limit=args.limit, status=args.status, show_id=args.show_id) elif args.command == 'rollback': diff --git a/cortex/llm_router.py b/cortex/llm_router.py index 1ae0cfe..650c72b 100644 --- a/cortex/llm_router.py +++ b/cortex/llm_router.py @@ -17,8 +17,6 @@ from typing import Dict, List, Optional, Any, Literal from enum import Enum from dataclasses import dataclass, asdict -from anthropic import Anthropic -from openai import OpenAI import logging # Configure logging @@ -131,17 +129,27 @@ def __init__( self.kimi_client = None if self.claude_api_key: - self.claude_client = Anthropic(api_key=self.claude_api_key) - logger.info("✅ Claude API client initialized") + try: + from anthropic import Anthropic + self.claude_client = Anthropic(api_key=self.claude_api_key) + logger.info("✅ Claude API client initialized") + except ImportError: + self.claude_client = None + logger.warning("⚠️ anthropic not installed, Claude disabled") else: logger.warning("⚠️ No Claude API key provided") if self.kimi_api_key: - self.kimi_client = OpenAI( - api_key=self.kimi_api_key, - base_url="https://api.moonshot.ai/v1" - ) - logger.info("✅ Kimi K2 API client initialized") + try: + from openai import OpenAI + self.kimi_client = OpenAI( + api_key=self.kimi_api_key, + base_url="https://api.moonshot.ai/v1" + ) + logger.info("✅ Kimi K2 API client initialized") + except ImportError: + self.kimi_client = None + logger.warning("⚠️ openai not installed, Kimi disabled") else: logger.warning("⚠️ No Kimi K2 API key provided") @@ -495,3 +503,6 @@ def complete_task( print("=== Usage Statistics ===") stats = router.get_stats() print(json.dumps(stats, indent=2)) + + + \ No newline at end of file