Skip to content

Conversation

@pavanimanchala53
Copy link
Contributor

@pavanimanchala53 pavanimanchala53 commented Jan 4, 2026

Summary

This PR introduces image-based error diagnosis to Cortex via a new diagnose command.

Users can now diagnose system or installation errors directly from screenshots or clipboard images, solving a common problem where errors cannot be copy-pasted from terminals or GUI dialogs.

The implementation uses Claude Vision when available and gracefully falls back to an offline-safe diagnosis when vision APIs, API keys, or dependencies are unavailable.


Related Issue

Closes #266


Type of Change

  • New feature
  • Documentation update

What’s Included

  • New CLI command:
    • cortex diagnose --image <path>
    • cortex diagnose --clipboard
  • Supports PNG / JPG / WebP images
  • Claude Vision API integration for screenshot analysis
  • Safe fallback behavior when:
    • Claude API key is missing
    • Vision is unavailable
    • Pillow dependency is not installed
  • Image diagnosis unit tests
  • Documentation describing usage and fallback behavior

AI Disclosure

  • AI/IDE/Agents used

Details:
Used ChatGPT to assist with reasoning about CLI design, fallback handling, and test coverage.
All code was manually reviewed, adapted, and tested locally.


Testing

  • pytest tests/ -v
  • pytest tests/test_diagnose_image.py -v
  • Manual verification:
    • cortex diagnose --image error.png
    • cortex diagnose --clipboard
    • Verified fallback behavior with missing API key

Checklist

  • Tests pass locally
  • Code follows project style guide
  • Documentation updated
  • Branch updated with main using merge commits

Summary by CodeRabbit

  • New Features

    • Added a CLI diagnose command for image/text/clipboard-based error diagnosis using Vision AI.
  • Bug Fixes

    • Improved image/clipboard loading and error-handling with clearer fallback messages.
  • Documentation

    • Added a guide explaining image-based diagnosis workflow and fallback behaviors.
  • Chores

    • Added Pillow to dependencies for image handling.
  • Tests

    • Added tests for image diagnosis behavior and command availability.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 4, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Added a new diagnose CLI subcommand (--image, --clipboard, --text) that uses a new LLMRouter.diagnose_image() to send PNG bytes to Claude Vision. Includes Pillow as a dependency, runtime fallbacks when Pillow or Claude Vision are unavailable, tests, and documentation.

Changes

Cohort / File(s) Summary
CLI Subcommand Integration
cortex/cli.py
Added diagnose subcommand with flags --image, --clipboard, --text; loads image from file or clipboard, obtains API key, constructs LLMRouter, and routes to diagnose_image() or complete(...) with TaskType.ERROR_DEBUGGING. Minor formatting tweak in install flow.
LLMRouter Image Diagnosis
cortex/llm_router.py
Added diagnose_image(self, image: "Image.Image") -> str; uses lazy Pillow import, io, and base64; converts image to PNG bytes, sends a structured image+text prompt to Claude Vision model, and returns textual diagnosis or standardized fallback messages when dependencies or service are missing.
Dependency Manifests
pyproject.toml, requirements.txt, requirements-dev.txt
Added pillow>=9.0.0 to enable image handling.
Tests
tests/test_diagnose_image.py
New tests for presence of diagnose_image and fallback behavior when claude_api_key is absent using an in-memory PIL image.
Docs
docs/image_error_diagnosis.md
New documentation describing cortex diagnose usage (image, clipboard, text), supported formats, and fallback guidance when Vision APIs or Pillow are unavailable.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant CLI as Cortex CLI
    participant Router as LLMRouter
    participant Vision as Claude Vision API

    User->>CLI: cortex diagnose --image screenshot.png
    CLI->>Router: diagnose_image(image)
    Router->>Router: lazy-import Pillow\nconvert image -> PNG bytes (base64)
    alt Claude Vision available
        Router->>Vision: send image + structured prompt (model)
        Vision-->>Router: textual diagnosis
        Router-->>CLI: diagnosis text
    else Claude Vision unavailable or error
        Router-->>CLI: fallback guidance message
    end
    CLI->>User: display result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I nibble pixels, sniff the screen,

A hop, a prompt, I parse the scene.
Claude may peek or snooze awhile,
I stitch a hint with gentle smile.
Soft paws, bright clues — errors reconcile.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.43% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title check ❓ Inconclusive The title 'Feature/image error diagnose' is vague and uses generic terminology. It lacks specificity about what the feature does and reads more like a branch name than a descriptive PR title. Revise the title to be more specific and descriptive, such as 'Add image-based error diagnosis via CLI' or 'Implement diagnose command for screenshot analysis'.
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description is comprehensive and complete, covering summary, related issue, type of change, implementation details, AI disclosure, testing, and checklist items.
Linked Issues check ✅ Passed The PR successfully implements all primary requirements from issue #266: CLI commands (--image and --clipboard), Claude Vision API integration, support for image formats, and graceful fallback handling when services are unavailable.
Out of Scope Changes check ✅ Passed All changes directly support the image diagnosis feature scope. Added Pillow dependency, LLMRouter.diagnose_image method, CLI diagnose command, tests, and documentation are all aligned with issue #266 objectives.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

github-actions bot commented Jan 4, 2026

CLA Verification Passed

All contributors have signed the CLA.

Contributor Signed As
@pavanimanchala53 @pavanimanchala53
@pavanimanchala53 @pavanimanchala53
@Anshgrover23 @Anshgrover23

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
requirements.txt (1)

10-26: Fix duplicate PyYAML entries.

PyYAML appears three times with conflicting versions:

  • Line 10: PyYAML>=6.0.0
  • Line 22: pyyaml>=6.0.0
  • Line 26: PyYAML==6.0.3

This can cause installation conflicts or unexpected behavior.

🔎 Recommended fix

Remove the duplicate entries and keep only one:

 # Configuration
 PyYAML>=6.0.0
 
 # Environment variable loading from .env files
 python-dotenv>=1.0.0
 
 # Encryption for environment variable secrets
 cryptography>=42.0.0
 
 # Terminal UI
 rich>=13.0.0
 
-# Configuration
-pyyaml>=6.0.0
-
 # Type hints for older Python versions
 typing-extensions>=4.0.0
-PyYAML==6.0.3
cortex/llm_router.py (1)

661-682: Critical: acomplete() lacks the _is_fallback guard, causing infinite recursion.

The _is_fallback guard was added to complete() (line 312) but not to acomplete(). If both Claude and Kimi fail, the async fallback loop will recurse infinitely:

  1. acomplete() → Claude fails → fallback to Kimi
  2. acomplete(force_provider=KIMI) → Kimi fails → fallback to Claude
  3. acomplete(force_provider=CLAUDE) → Claude fails → fallback to Kimi
  4. ... stack overflow
Suggested fix

Add the same _is_fallback parameter and guard to acomplete():

     async def acomplete(
         self,
         messages: list[dict[str, str]],
         task_type: TaskType = TaskType.USER_CHAT,
         force_provider: LLMProvider | None = None,
         temperature: float = 0.7,
         max_tokens: int = 4096,
         tools: list[dict] | None = None,
+        _is_fallback: bool = False,
     ) -> LLMResponse:
         except Exception as e:
             logger.error(f"❌ Error with {routing.provider.value}: {e}")
 
             # Try fallback if enabled
-            if self.enable_fallback:
+            if self.enable_fallback and not _is_fallback:
                 fallback_provider = (
                     LLMProvider.KIMI_K2
                     if routing.provider == LLMProvider.CLAUDE
                     else LLMProvider.CLAUDE
                 )
                 logger.info(f"🔄 Attempting fallback to {fallback_provider.value}")
 
                 return await self.acomplete(
                     messages=messages,
                     task_type=task_type,
                     force_provider=fallback_provider,
                     temperature=temperature,
                     max_tokens=max_tokens,
                     tools=tools,
+                    _is_fallback=True,
                 )
-            else:
-                raise
+
+            raise
🧹 Nitpick comments (6)
tests/test_diagnose_image.py (3)

9-23: Expand test coverage for diagnose_image.

The fallback test is good, but consider adding tests for:

  • Successful Claude Vision response (with mocking)
  • Pillow ImportError scenario
  • Exception during Claude API call
  • Invalid image input handling

This would increase confidence in the feature's robustness.

Based on learnings and coding guidelines requiring >80% test coverage for pull requests.

Example additional test
def test_diagnose_image_missing_pillow(monkeypatch):
    """Test fallback when Pillow is not available."""
    import sys
    import builtins
    
    original_import = builtins.__import__
    
    def mock_import(name, *args, **kwargs):
        if name == "PIL" or name == "PIL.Image":
            raise ImportError("No module named 'PIL'")
        return original_import(name, *args, **kwargs)
    
    monkeypatch.setattr(builtins, "__import__", mock_import)
    
    router = LLMRouter(claude_api_key="test-key")
    image = Image.new("RGB", (100, 100), color="blue")
    result = router.diagnose_image(image)
    
    assert "missing pillow" in result.lower()

25-30: Consider removing trivial test.

This test only checks that the method exists, which would be caught by the first test anyway. Consider replacing it with a more meaningful test case (e.g., testing with a valid Claude API key mock).


1-1: Add module-level docstring.

Per coding guidelines, all Python files should have docstrings. Add a module-level docstring describing the test file's purpose.

Example docstring
+"""
+Tests for image-based error diagnosis functionality in LLMRouter.
+
+Tests cover fallback behavior when Claude Vision is unavailable,
+missing dependencies, and basic method existence.
+"""
 import pytest

As per coding guidelines requiring docstrings for all public APIs.

cortex/llm_router.py (3)

22-22: Unused import: asdict

The asdict function is imported but never used in this file.

Suggested fix
-from dataclasses import asdict, dataclass
+from dataclasses import dataclass

271-271: Clean up development notes from code.

The inline comments # 👈 ADD THIS and # 👈 CRITICAL LINE appear to be development notes that should be removed before merge.

Suggested fix
-        _is_fallback: bool = False,  # 👈 ADD THIS
+        _is_fallback: bool = False,
-                    _is_fallback=True,  # 👈 CRITICAL LINE
+                    _is_fallback=True,

Also applies to: 327-327


364-366: Consider using Claude Sonnet 4 for image diagnosis instead of Opus.

The diagnose_image() method uses claude-3-opus-20240229 while _complete_claude() uses claude-sonnet-4-20250514. Claude Sonnet 4 supports vision and is significantly more cost-effective (input: $3 vs $15 per MTok; output: $15 vs $75 per MTok). Consider aligning both methods to use Sonnet 4 for consistency and cost optimization.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9e75297 and 0631731.

📒 Files selected for processing (6)
  • cortex/cli.py
  • cortex/llm_router.py
  • pyproject.toml
  • requirements-dev.txt
  • requirements.txt
  • tests/test_diagnose_image.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/llm_router.py
  • tests/test_diagnose_image.py
  • cortex/cli.py
{setup.py,setup.cfg,pyproject.toml,**/__init__.py}

📄 CodeRabbit inference engine (AGENTS.md)

Use Python 3.10 or higher as the minimum supported version

Files:

  • pyproject.toml
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain >80% test coverage for pull requests

Files:

  • tests/test_diagnose_image.py
🧬 Code graph analysis (2)
tests/test_diagnose_image.py (1)
cortex/llm_router.py (2)
  • LLMRouter (78-919)
  • diagnose_image (332-400)
cortex/cli.py (2)
cortex/llm_router.py (1)
  • diagnose_image (332-400)
cortex/branding.py (1)
  • cx_print (49-69)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Test (Python 3.11)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.10)
🔇 Additional comments (7)
requirements.txt (1)

7-7: LGTM! Pillow dependency added correctly.

The addition of pillow>=9.0.0 is appropriate for the new image-based diagnosis feature.

requirements-dev.txt (1)

11-11: LGTM! Development dependency added correctly.

Adding Pillow to dev dependencies ensures tests can create and manipulate images for testing the diagnose feature.

cortex/cli.py (1)

1645-1666: LGTM! Diagnose command parser configured correctly.

The argument parser setup for the diagnose subcommand is well-structured with clear help text and appropriate argument types.

pyproject.toml (1)

50-50: LGTM! Pillow dependency added correctly.

The addition aligns with requirements.txt and requirements-dev.txt, ensuring consistent dependency management across the project.

tests/test_diagnose_image.py (1)

1-7: LGTM! Good use of pytest.importorskip.

Using pytest.importorskip ensures tests are skipped gracefully when Pillow is not installed, rather than failing with ImportError.

cortex/llm_router.py (2)

171-171: LGTM!

Downgrading to info level is appropriate since Kimi is an optional fallback provider, not a required component.


455-455: LGTM!

The conditional check for model_dump improves robustness and maintains consistency with the Ollama implementation.

Also applies to: 498-498

Comment on lines 1927 to 1978
elif args.command == "diagnose":
import io

from PIL import Image

image = None

if args.image:
try:
image = Image.open(args.image)
except Exception as e:
print(f"❌ Failed to load image: {e}")
return 1

elif args.clipboard:
try:
from PIL import ImageGrab

image = ImageGrab.grabclipboard()
if image is None:
print("❌ No image found in clipboard")
return 1
except Exception as e:
print(f"❌ Failed to read clipboard: {e}")
return 1

router = LLMRouter()

if image:
cx_print("🔍 Analyzing error screenshot...")
diagnosis = router.diagnose_image(image)
cx_print(diagnosis)
return 0

if args.text:
print("📝 Text diagnosis placeholder")
print(args.text)
return 0

print("❌ Provide an image, clipboard, or error message")
return 1
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: LLMRouter initialized without API key; refactor error handling.

Several issues in the diagnose command handler:

  1. Critical: LLMRouter is initialized without an API key (line 1953), which means it will always fail when trying to use Claude Vision. This defeats the purpose of the feature.
  2. PIL imports should be at the top of the function or module level, not scattered throughout
  3. Error messages inconsistently use print() instead of cx_print()
  4. Missing type hints and docstring (required per coding guidelines)
🔎 Proposed fixes

Fix 1: Add API key handling

         elif args.command == "diagnose":
             import io
 
             from PIL import Image
+            from PIL import ImageGrab
 
             image = None
 
             if args.image:
                 try:
                     image = Image.open(args.image)
                 except Exception as e:
-                    print(f"❌ Failed to load image: {e}")
+                    cli._print_error(f"Failed to load image: {e}")
                     return 1
 
             elif args.clipboard:
                 try:
-                    from PIL import ImageGrab
-
                     image = ImageGrab.grabclipboard()
                     if image is None:
-                        print("❌ No image found in clipboard")
+                        cli._print_error("No image found in clipboard")
                         return 1
                 except Exception as e:
-                    print(f"❌ Failed to read clipboard: {e}")
+                    cli._print_error(f"Failed to read clipboard: {e}")
                     return 1
 
-            router = LLMRouter()
+            # Get API key for LLM router (diagnose_image needs Claude Vision)
+            api_key = cli._get_api_key()
+            if not api_key:
+                return 1
+            
+            router = LLMRouter(claude_api_key=api_key)
 
             if image:
                 cx_print("🔍 Analyzing error screenshot...")
                 diagnosis = router.diagnose_image(image)
                 cx_print(diagnosis)
                 return 0
 
             if args.text:
-                print("📝 Text diagnosis placeholder")
-                print(args.text)
+                cx_print("📝 Text diagnosis placeholder", "info")
+                console.print(args.text)
                 return 0
 
-            print("❌ Provide an image, clipboard, or error message")
+            cli._print_error("Provide an image, clipboard, or error message")
             return 1

Note: Consider extracting this into a method like cli.diagnose(args) for consistency with other commands and to add proper type hints and docstring.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In cortex/cli.py around lines 1927 to 1967, the diagnose command handler
incorrectly instantiates LLMRouter without providing the required API key and
has scattered PIL imports, inconsistent use of print() vs cx_print(), and lacks
type hints/docstring; fix by refactoring into a dedicated function (e.g.,
cli.diagnose(args) with a proper docstring and type hints), move PIL imports to
module top or function start, pass the configured API key (from env/config/args)
into LLMRouter when creating router, replace all print() calls with cx_print()
for consistent CLI output, and centralize clipboard/image loading and error
handling to propagate/log the underlying exception details.

✅ Addressed in commit ec86f9d

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pavanimanchala53 Address this one as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
cortex/cli.py (2)

1921-1963: Consider extracting diagnose logic into a dedicated method.

The diagnose command handler would benefit from extraction into a method like cli.diagnose(args) for consistency with other command handlers (e.g., cli.ask(), cli.install()). This would allow proper type hints and a docstring as required by coding guidelines.

🔎 Suggested refactor

Add a new method to the CortexCLI class:

def diagnose(self, args: argparse.Namespace) -> int:
    """
    Diagnose installation or system errors from images or text.
    
    Args:
        args: Parsed command-line arguments with image, clipboard, or text options.
        
    Returns:
        Exit code (0 for success, 1 for error).
    """
    import io
    from PIL import Image, ImageGrab
    
    image = None
    
    if args.image:
        try:
            image = Image.open(args.image)
        except Exception as e:
            self._print_error(f"Failed to load image: {e}")
            return 1
    
    elif args.clipboard:
        try:
            image = ImageGrab.grabclipboard()
            if image is None:
                self._print_error("No image found in clipboard")
                return 1
        except Exception as e:
            self._print_error(f"Failed to read clipboard: {e}")
            return 1
    
    # Get API key for LLM router (diagnose_image needs Claude Vision)
    api_key = self._get_api_key()
    if not api_key:
        return 1
    
    router = LLMRouter(claude_api_key=api_key)
    if image:
        cx_print("🔍 Analyzing error screenshot...")
        diagnosis = router.diagnose_image(image)
        cx_print(diagnosis)
        return 0
    
    if args.text:
        cx_print("📝 Text diagnosis placeholder", "info")
        console.print(args.text)
        return 0
    
    self._print_error("Provide an image, clipboard, or error message")
    return 1

Then in main(), replace lines 1921-1963 with:

elif args.command == "diagnose":
    return cli.diagnose(args)

1946-1956: Minor optimization: defer router creation until needed.

The LLMRouter is created at line 1951 before checking whether an image is actually available. If only --text is provided (which currently just prints a placeholder), the router creation is unnecessary. Consider moving router initialization inside the if image: block.

🔎 Suggested optimization
-    # Get API key for LLM router (diagnose_image needs Claude Vision)
-    api_key = cli._get_api_key()
-    if not api_key:
-        return 1
-
-    router = LLMRouter(claude_api_key=api_key)
     if image:
+        # Get API key for LLM router (diagnose_image needs Claude Vision)
+        api_key = cli._get_api_key()
+        if not api_key:
+            return 1
+        
+        router = LLMRouter(claude_api_key=api_key)
         cx_print("🔍 Analyzing error screenshot...")
         diagnosis = router.diagnose_image(image)
         cx_print(diagnosis)
         return 0
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0631731 and bf27fbb.

📒 Files selected for processing (3)
  • cortex/cli.py
  • cortex/llm_router.py
  • docs/image_error_diagnosis.md
✅ Files skipped from review due to trivial changes (1)
  • docs/image_error_diagnosis.md
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/cli.py
  • cortex/llm_router.py
🧬 Code graph analysis (1)
cortex/cli.py (1)
cortex/llm_router.py (2)
  • LLMRouter (81-929)
  • diagnose_image (335-410)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Build Package
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.10)
🔇 Additional comments (7)
cortex/cli.py (2)

23-23: LGTM - LLMRouter import added correctly.

The import is properly placed with other cortex module imports.


1639-1660: LGTM - Diagnose command arguments are well-defined.

The argument structure supports the three input modes (image file, clipboard, text) with clear help text. The non-exclusive nature allows users to provide multiple inputs if needed.

cortex/llm_router.py (5)

15-16: LGTM - Proper use of TYPE_CHECKING for optional dependencies.

The imports are well-structured:

  • base64 and io support image encoding for Claude Vision
  • TYPE_CHECKING correctly isolates PIL.Image type hint import, avoiding runtime dependency when PIL is unavailable

This pattern prevents import errors when Pillow is not installed while still providing type hints.

Also applies to: 22-22, 24-24, 29-31


174-174: LGTM - Appropriate logging level for optional provider.

Changed from warning to info level, which is correct since Kimi K2 is an optional provider and its absence is informational rather than problematic.


274-274: Excellent fix - prevents infinite fallback recursion.

The _is_fallback parameter ensures that if the primary provider fails, the router attempts fallback exactly once, then raises an exception if the fallback also fails. This prevents infinite loops that could occur if both providers repeatedly fail.

The guarded recursion at line 330 passing _is_fallback=True is the critical safeguard.

Also applies to: 315-333


465-465: LGTM - Defensive handling of raw_response serialization.

The conditional check for model_dump() method existence prevents errors when the response object doesn't support serialization, falling back to None. This is good defensive programming.

Also applies to: 508-508


585-586: LGTM - Minor docstring clarification.

The updated Returns description is clearer and more specific about what the dictionary contains.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
cortex/llm_router.py (1)

334-409: Critical: Update deprecated Claude model.

The model claude-3-opus-20240229 on Line 372 is deprecated per previous review feedback. Update to a current vision-capable model such as claude-opus-4-1-20250805 or one from the Claude 3.5/4 families.

Additionally, max_tokens=500 (Line 373) may be insufficient for detailed error diagnoses with suggested fixes. Consider increasing to 1024 or higher to provide more comprehensive diagnostic responses.

🔎 Proposed fix
         try:
             response = self.claude_client.messages.create(
-                model="claude-3-opus-20240229",
-                max_tokens=500,
+                model="claude-opus-4-1-20250805",
+                max_tokens=1024,
                 messages=[
🧹 Nitpick comments (3)
tests/test_diagnose_image.py (2)

9-22: Consider expanding test coverage for diagnose_image.

The current test only validates the fallback when Claude is unavailable. Consider adding tests for:

  1. Successful Claude Vision response (mock the API call)
  2. API errors (authentication failure, network error, rate limit)
  3. Empty or malformed API responses
  4. Different image formats (PNG, JPG, WebP as mentioned in requirements)
  5. Large images that might exceed context limits

The Pillow-missing fallback can't be tested in this module due to importorskip on Line 3, but could be tested in a separate module that doesn't import PIL.


25-30: Consider removing or strengthening this test.

This test only verifies the method exists, which is implicitly validated by test_diagnose_image_fallback_without_claude on Line 19. Consider either:

  • Removing this test as redundant
  • Replacing it with a test that validates the method signature (parameters, return type)
  • Or use it to test that the method is callable with a real PIL Image object
cortex/cli.py (1)

1935-1977: Good API key handling; consider making diagnose options mutually exclusive.

The diagnose command implementation correctly initializes LLMRouter with an API key (Lines 1960-1965), addressing previous review feedback.

However, the options --image, --clipboard, and --text are not mutually exclusive. If a user provides multiple options (e.g., both --image and --clipboard), --image takes precedence silently. Consider:

  1. Making these options mutually exclusive using argparse.add_mutually_exclusive_group() in the argument parser (around Lines 1658-1674)
  2. Or document the precedence order in the help text

Also note that text-based diagnosis (Lines 1972-1975) remains a placeholder as flagged in previous reviews. If this feature is deferred, consider adding a TODO comment or filing an issue.

🔎 Proposed fix to make options mutually exclusive

In the argument parser section (around Lines 1653-1674):

 diagnose_parser = subparsers.add_parser(
     "diagnose",
     help="Diagnose installation or system errors",
 )

+diagnose_group = diagnose_parser.add_mutually_exclusive_group(required=True)
+
-diagnose_parser.add_argument(
+diagnose_group.add_argument(
     "--image",
     type=str,
     help="Path to error screenshot (PNG, JPG, WebP)",
 )

-diagnose_parser.add_argument(
+diagnose_group.add_argument(
     "--clipboard",
     action="store_true",
     help="Read error screenshot from clipboard",
 )

-diagnose_parser.add_argument(
+diagnose_group.add_argument(
     "--text",
     type=str,
     help="Raw error text (fallback)",
 )
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf27fbb and ec86f9d.

📒 Files selected for processing (7)
  • cortex/cli.py
  • cortex/llm_router.py
  • docs/image_error_diagnosis.md
  • pyproject.toml
  • requirements-dev.txt
  • requirements.txt
  • tests/test_diagnose_image.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/image_error_diagnosis.md
  • pyproject.toml
  • requirements.txt
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide
Type hints required in Python code
Docstrings required for all public APIs

Files:

  • cortex/llm_router.py
  • tests/test_diagnose_image.py
  • cortex/cli.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain >80% test coverage for pull requests

Files:

  • tests/test_diagnose_image.py
🧠 Learnings (1)
📚 Learning: 2025-12-11T12:03:24.071Z
Learnt from: CR
Repo: cortexlinux/cortex PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-11T12:03:24.071Z
Learning: Applies to **/*install*.py : Implement audit logging to ~/.cortex/history.db for all package operations

Applied to files:

  • cortex/cli.py
🧬 Code graph analysis (2)
tests/test_diagnose_image.py (1)
cortex/llm_router.py (1)
  • diagnose_image (334-409)
cortex/cli.py (1)
cortex/llm_router.py (1)
  • diagnose_image (334-409)
🪛 GitHub Actions: CI
cortex/cli.py

[error] 1-1: Black formatting check failed. 1 file would be reformatted by black --write. Run 'black --write' to fix code style issues.

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.10)
🔇 Additional comments (3)
cortex/llm_router.py (1)

15-16: LGTM: Proper import handling for optional dependency.

The use of TYPE_CHECKING to conditionally import PIL's Image type is the correct pattern for optional dependencies. This allows static type checkers to validate the type hints without requiring Pillow at import time.

Also applies to: 24-24, 29-30

cortex/cli.py (1)

23-23: LGTM: Import added for diagnose feature.

The LLMRouter import is correctly placed and necessary for the new diagnose command.

requirements-dev.txt (1)

11-11: Pillow is correctly placed in main requirements.

Pillow is already declared in both requirements.txt and pyproject.toml, ensuring users have access to the cortex diagnose --image feature at runtime without additional manual installation.

Comment on lines +1936 to +1924
import io

from PIL import Image, ImageGrab
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Wrap PIL imports in try-except to provide better error handling.

The PIL imports on Line 1938 occur outside any try-except block. If Pillow is not installed, the ImportError will be raised before reaching the try-except blocks that handle image loading (Lines 1943-1947, 1950-1958), resulting in a less user-friendly error message.

Consider wrapping the imports:

🔎 Proposed fix
         elif args.command == "diagnose":
-            import io
-
-            from PIL import Image, ImageGrab
+            try:
+                import io
+                from PIL import Image, ImageGrab
+            except ImportError:
+                cli._print_error("Pillow is required for the diagnose command")
+                cx_print("Install with: pip install pillow", "info")
+                return 1

             image = None
🤖 Prompt for AI Agents
In cortex/cli.py around lines 1936 to 1938, the PIL imports (from PIL import
Image, ImageGrab) are executed outside any try-except so an ImportError will
surface before the existing image-loading error handling; wrap these imports in
a try-except ImportError block (or move them inside the existing try blocks) and
on ImportError raise or log a clear user-friendly message indicating Pillow is
required and how to install it (e.g., suggest pip install Pillow), so the script
fails with a helpful error instead of a raw ImportError.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pavanimanchala53 Address this one.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

- Add `diagnose` command supporting image and clipboard input
- Use Claude Vision when available with graceful offline fallback
- Add Pillow dependency for image handling
- Add tests for image diagnosis fallback behavior
- Add documentation for image-based diagnosis
Copy link
Collaborator

@Anshgrover23 Anshgrover23 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pavanimanchala53 Making it draft tell me whenever its ready to review.

@Anshgrover23 Anshgrover23 marked this pull request as draft January 5, 2026 11:08
@mikejmorgan-ai mikejmorgan-ai self-assigned this Jan 10, 2026
@sonarqubecloud
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Screenshot/Image Input for Error Diagnosis

3 participants