Project Name: Code Debugger Assistant Goal: Build a terminal-based AI assistant that receives a code file, detects the programming language, analyzes errors, proposes fixes with reasoning, and applies changes only after user approval.
The application uses LangChain 1.0, LCEL (LangChain Expression Language), OpenAI-compatible APIs, and a Textual TUI.
Developers often spend time identifying syntax errors, runtime errors, lint issues, and simple logical mistakes. Existing tools either only report errors or directly rewrite code without enough explanation.
This project solves that by creating an AI-powered debugging assistant with a human-in-the-loop approval flow before modifying the source file.
- Students learning programming
- Junior developers
- Developers preparing demo projects
- Trainers teaching debugging workflows
- Engineers who want quick AI-assisted code correction with manual control
- Accept a single code file as input.
- Auto-detect the programming language.
- Read and analyze the code.
- Detect likely issues: syntax errors, missing imports, runtime errors, type mistakes, bad API usage, logical mistakes.
- Generate: error summary, root-cause explanation, proposed corrected code, diff-style change summary.
- Ask user approval before applying changes.
- Apply approved changes to the original file.
- Show the workflow in a Textual TUI.
- Use structured LLM output (Pydantic v2 + OpenAI Structured Outputs).
- Multi-file repo-wide debugging
- Git branch automation
- Full IDE plugin
- Remote code execution sandbox
- Security vulnerability scanning
- Production-grade code review
- Automatic deployment
| Area | Tool |
|---|---|
| Language | Python 3.10+ |
| UI | Textual |
| Chain Composition | LangChain 1.0 LCEL (`PromptTemplate |
| LLM | Any OpenAI-compatible API (LM Studio, OpenAI, Anthropic, Groq, OpenRouter) |
| Model Init | init_chat_model() for provider-agnostic setup |
| Structured Response | Pydantic v2 / OpenAI Structured Outputs |
| Diff Generation | Python difflib |
| File Handling | Plain Python pathlib / open() |
| Language Detection | File extension mapping + optional Pygments fallback |
| Testing | Pytest (36 tests) |
The app is provider-agnostic — switch between providers via .env only, no code changes required.
| Provider | Key env vars |
|---|---|
| LM Studio (local) | MODEL_PROVIDER=openai, BASE_URL=http://localhost:1234/v1, OPENAI_API_KEY=lm-studio |
| OpenAI | MODEL_PROVIDER=openai, MODEL_NAME=gpt-4.1, OPENAI_API_KEY=sk-... |
| Anthropic | MODEL_PROVIDER=anthropic, MODEL_NAME=claude-sonnet-4-5, ANTHROPIC_API_KEY=... |
| Google Gemini | MODEL_PROVIDER=google_genai, MODEL_NAME=gemini-2.0-flash, GOOGLE_API_KEY=... |
| Groq | MODEL_PROVIDER=groq, MODEL_NAME=llama-3.3-70b-versatile, GROQ_API_KEY=... |
| OpenRouter | OPENROUTER_API_KEY=sk-or-v1-..., MODEL_NAME=openai/gpt-4.1 |
OpenRouter note: Setting OPENROUTER_API_KEY auto-activates OpenRouter regardless of MODEL_PROVIDER. Supports 300+ models from a single key. Model names use the provider/model-name format (e.g. anthropic/claude-sonnet-4-5).
- User launches:
python -m app.main - Enter code file path → validated and loaded.
- App detects programming language.
- App runs the AI debugging LCEL chain (background thread, spinner shown).
- App displays: detected language, issues found (with severity badges), AI reasoning, confidence score, diff preview.
- User chooses: Approve & Overwrite / Save Corrected Copy / Reject / Back.
- If approved, app writes corrected code to disk (with
.bakbackup). - Success/failure status shown.
- User can Debug Another File or Quit.
- Invalid path shows a clear error in the UI.
- Empty file is rejected.
- Supported extensions:
.py,.js,.ts,.java,.go,.cpp,.c,.html,.css,.json,.yaml,.rb,.rs,.php,.swift,.kt,.sql,.sh.
- Extension map (primary, fast, deterministic).
- Pygments content-based lexer guess (fallback for unknown extensions).
- Returns
"Unknown"gracefully — file is still passed to LLM.
DEBUG_PROMPT | llm.with_structured_output(DebugAnalysisResult)
- Runs in
@work(thread=True)background worker — UI stays responsive. app.call_from_thread()(notscreen.call_from_thread()) marshals results to UI.
- Original code shown with syntax highlighting.
- Corrected code shown with syntax highlighting.
- Unified diff rendered (green = added, red = removed).
- Each issue rendered with severity badge (
[HIGH]/[MEDIUM]/[LOW]), description, and suggested fix. - Issues rendered as Rich markup via
Static.update()(not dynamicmount()).
| Button | Action |
|---|---|
| Approve & Overwrite | Writes corrected code; creates .bak backup first |
| Save Corrected Copy | Writes <stem>_corrected<ext> alongside original |
| Reject | Leaves all files unchanged |
| Back | Returns to Analysis screen |
.bakbackup auto-created before any overwrite.- Corrected copy:
<stem>_corrected<suffix>naming. - Success/failure message shown in Status screen.
- "Debug Another File" resets state and returns to File Selection screen.
code-debugger-assistant/
├── app/
│ ├── main.py # Entry point
│ ├── config.py # Env vars, extension map, constants
│ ├── agent/
│ │ ├── schemas.py # Pydantic v2: CodeIssue, DebugAnalysisResult
│ │ ├── prompts.py # ChatPromptTemplate (system + human)
│ │ └── debugger_chain.py # LCEL chain + multi-provider setup
│ ├── core/
│ │ ├── file_loader.py # File validation + read (plain pathlib)
│ │ ├── language_detector.py
│ │ ├── diff_builder.py # difflib wrapper
│ │ └── patch_writer.py # overwrite + corrected copy
│ └── ui/
│ ├── debugger_app.py # Root Textual App + shared state
│ ├── screens.py # 5 Textual screens
│ └── widgets.py # DiffViewer, CodeViewer, SectionHeader
├── tests/ # 36 unit tests (all pass)
├── examples/
│ ├── buggy_python.py
│ ├── buggy_js.js
│ └── buggy_java.java
├── .env.example
├── pyproject.toml
└── README.md
Input: { language: str, source_code: str }
↓
ChatPromptTemplate (system prompt + human message)
↓
ChatModel.with_structured_output(DebugAnalysisResult)
↓
DebugAnalysisResult (validated Pydantic v2)
↓
diff_builder.build_diff_lines()
↓
Textual ApprovalScreen → user decision
↓
patch_writer.overwrite_file() | save_corrected_copy() | (no-op)
Why pure LCEL (not create_agent())?
- Debugging is a single-shot structured call, not a multi-turn agent loop.
PromptTemplate | llm.with_structured_output()is simpler, faster, and easier to test.- Human-in-the-loop is handled by the Textual UI layer (Buttons), not LangChain middleware.
from pydantic import BaseModel, Field
from typing import List, Literal
class CodeIssue(BaseModel):
title: str
description: str
line_number: int | None = None
severity: Literal["low", "medium", "high"]
suggested_fix: str
class DebugAnalysisResult(BaseModel):
language: str
error_summary: str
issues: List[CodeIssue]
reasoning: str
corrected_code: str
change_summary: List[str]
confidence_score: float # 0.0-1.0| # | Screen | Purpose |
|---|---|---|
| 1 | FileSelectionScreen |
Enter & validate file path |
| 2 | CodePreviewScreen |
Show source + language badge |
| 3 | AnalysisScreen |
Run LCEL chain (spinner) → show results |
| 4 | ApprovalScreen |
Diff + corrected code + 4 action buttons |
| 5 | StatusScreen |
Final outcome + restart/quit |
Key implementation decisions:
@work(thread=True)for the LLM call keeps UI responsive.app.call_from_thread()(notself.call_from_thread()) —call_from_threadis onApp, notScreen.height: autoon all contentStaticwidgets — prevents collapsed zero-height boxes.- Rich markup via
Static.update()for issues — more reliable than dynamicmount()from sync callbacks. - Screen stack:
while len(stack) > 2for restart — preservesFileSelectionScreenat index 1. font-sizeis not valid in Textual CSS — use widget sizing instead.
You are a senior software debugging assistant.
Identify only ACTUAL errors: syntax, runtime, import, type, logical.
Preserve the developer's original intent and coding style.
Propose the MINIMAL set of changes needed to fix the code.
Return the COMPLETE corrected source code.
Do not add new features, libraries, or logic that wasn't there before.
Do not remove valid logic.
If the code appears correct, say so and return the original unchanged.
Return your answer using the required structured schema only.
- User can load a code file.
- System detects the language.
- System analyzes code using LLM (structured output).
- System shows issues, reasoning, corrected code, and diff.
- User can approve or reject the fix.
- File changes happen only after approval.
- At least 3 languages demoed: Python, JavaScript, Java.
- README clearly explains setup and usage.
- 36 unit tests pass.
| # | Bug | Root Cause | Fix |
|---|---|---|---|
| 1 | App crash after LLM analysis | self.call_from_thread() — not available on Screen |
Changed to app.call_from_thread() |
| 2 | CSS error on Status screen | font-size is not a valid Textual CSS property |
Removed font-size: 2 |
| 3 | "Debug Another File" → blank screen | while stack > 1 removed FileSelectionScreen too |
Changed to while stack > 2 |
| 4 | Issues section empty | Dynamic mount() of IssueCard from sync callback unreliable |
Replaced with pre-declared Static + update() using Rich markup |
| 5 | NameError: Vertical |
Incorrectly removed Vertical from imports |
Restored import |
- Multi-file project analysis
- Git diff integration
- Unit test generation after fix
- LangSmith tracing
- Linter integration (ruff, eslint)
- VS Code extension
- Web UI version
- Dockerized sandbox for safe code execution
- Rollback previous fix
- LangSmith observability