Observed since 21 Oct, 10:28 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
An AI powered LSP providing AI highly configurable AI diagnostics.
A Python‑based Language Server that provides AI‑powered diagnostics using LangChain. It integrates with multiple LLM providers (Gemini, OpenRouter, Groq, Cerebras, HuggingFace) and features semantic location matching, cross‑file analysis, and persistent diagnostic storage. Designed to be used with the ai_diagnos.nvim plugin, but can be configured for any editor that supports the Language Server Protocol.
open, save, change, or manual command.[!NOTE] The core will only include a parser for python. Parsers for every other language must be be added via plugins. See here Prompt overrides per language for finer performance can also be found on here
pip and venv (or uv)The LSP is a Python package. You can install it from its repository:
# Clone the repository
git clone https://github.com/VectorZeroAI/ai-diagnos-lsp.git
cd ai-diagnos-lsp
# Install with pip
pip install .
# Or with uv (faster)
uv sync
uv pip install .
The server executable ai-diagnos-lsp will be placed in your Python environment’s bin directory.
The LSP accepts configuration via the initializationOptions field in the LSP initialize request.
The options mirror those of the Neovim plugin. Below is the complete set of options with their types and defaults.
| Option | Type | Default | Description |
|---|---|---|---|
api_key_openrouter | string | – | OpenRouter API key. |
api_key_gemini | string | – | Gemini API key. |
api_key_groq | string | – | Groq API key. |
api_key_cerebras | string | – | Cerebras API key. |
api_key_huggingface | string | – | HuggingFace API key. |
api_key_openai | string | - | OpenAI API key. (For openai usage) |
api_key_claude | string | - | Claude API key. (For claude provider usage) |
use | string | Omniprovider | Wich provider to use. Omniprovider jumps through all the providers one after the other |
model_openrouter | string | "tngtech/tng-r1t-chimera:free" | Model name for OpenRouter. |
model_gemini | string | "gemini-2.5-flash-lite" | Model name for Gemini. |
model_groq | string | "openai/gpt-oss-120b" | Model name for Groq. |
model_cerebras | string | "openai/gpt-oss-120b" | Model name for Cerebras. |
model_huggingface | string | "Qwen2.5-Coder-7B-Instruct" | Model name for HuggingFace. |
model_openai | string | "gpt-5.2" | Model name for openai |
model_claude | string | "claude-sonnet-4-6" | Model name for claude provider |
fallback_models_gemini | array of string | ["gemini-2.5-flash", "gemini-3-flash-preview"] | Fallback models for Gemini. |
fallback_models_groq | array of string | ["openai/gpt-oss-20b", "openai/gpt-oss-safeguard-20b", "qwen/qwen3-32b", "llama-3.3-70b-versatile"] | Fallback models for Groq. |
fallback_models_cerebras | array of string | [] | Fallback models for Cerebras. |
debounce_ms | integer | 3000 | Minimum time (ms) between analyses for the same file. |
max_file_size | integer | 10000 | Maximum number of lines to analyse. Larger files are skipped. |
timeout | integer | 99999 | Maximum wait time (seconds) for the LLM to respond. |
show_progress | boolean | true | Show periodic progress notifications. |
show_progress_every_ms | integer | 5000 | Interval between progress notifications. |
ai_diagnostics_symbol | string | "AI" | (Not used by LSP, but passed through.) |
AnalysisSubsystem | object | See below | Controls which analyses run on which events. |
CrossFileAnalysis | object | See below | Cross‑file analysis specific settings. |
DiagnosticsSubsystem | object | See below | Database and TTL settings. |
plugins | object | See below | Add your own parsers per language, to enable omnilang cross file diagnostics. |
{
"write": ["CrossFile", "Basic", "BasicLogic", "CrossFileLogic", "BasicStyle", "CrossFileStyle"],
"open": ["Basic", "CrossFile", "BasicLogic", "CrossFileLogic", "BasicStyle", "CrossFileStyle"],
"change": [],
"command": ["CrossFile"],
"max_threads": 5
}
write, open, change, command: lists of analysis types to run on each event. Supported types:
"Basic" – single‑file general analysis"CrossFile" – cross‑file general analysis"BasicLogic" – single‑file logic‑focused analysis"CrossFileLogic" – cross‑file logic‑focused analysis"BasicStyle" – single‑file style/convention analysis"CrossFileStyle" – cross‑file style/convention analysis"Deep" – (planned) more thorough analysis"Workspace" – (planned) whole‑workspace analysisCurrently implemented: Basic, CrossFile, BasicLogic, CrossFileLogic, BasicStyle, CrossFileStyle. The others are placeholders for future expansion.
max_threads: maximum concurrent analysis threads.
{
"scope": ["/path/to/project/src"],
"max_analysis_depth": null,
"max_string_size_char": 1000000
}
scope: list of directory paths that limit which imported files are included.max_analysis_depth: how deep to recursively follow imports (null = unlimited).max_string_size_char: maximum character length of the aggregated context.{
"check_ttl_for_deletion": 360,
"sqlite_db_name": "diagnostics.db",
"check_ttl_for_invalidation": 5,
"ttl_until_deletion": 2592000,
"ttl_until_invalidation": 15
}
check_ttl_for_deletion: seconds between checks for old files to delete.sqlite_db_name: database file name.check_ttl_for_invalidation: seconds between checks for stale diagnostics.ttl_until_deletion: seconds after last write before a file’s records are purged (default 30 days).ttl_until_invalidation: seconds after a file write before old diagnostics are considered stale and deleted.In the current plugin system, there are 2 plugin types:
Any plugins can be found in the plugin repo. TODO: ADD THE LINK HERE
To make the LSP have first class support for a language, you must find first class parser for the language and a first class Prompt for the language. You can write these yourself as well, making them specific to your needs.
These are the prompt_overrides, wich are overrides over the normal prompt parts, wich are also activated per filetype, wich enables dedicated per langage support, wich can greatly increase the performance in that specific language.
The prompt overrides are python files, containing any of the following constant strings:
The plugin system will automatically load the python file and get all the overrides that exist.
TASK = """Your task is to find memory leaks"""
NOTE = """Focus on memory leaks"""
FOOTER= """Focus on memory leaks. Dont forget memory leaks."""
These are the plugins that allow the cross file analysis to work with filetypes other then python. You can find them in the plugin repo, for their respective language. The parsers must follow the protocol described in the Parser Protocol section below.
The server is built with pygls and organised into several subsystems:
AIDiagnosLSP – main server class, inherits from LanguageServer. Holds configuration, diagnostics cache, and references to subsystems.AnalysisSubsystem – manages analysis requests. Uses a ThreadPoolExecutor to run analysers concurrently, respects debounce and file size limits, and dispatches to the appropriate analyser based on the event.BasicDiagnoseFunctionWorker (single‑file analysis) and CrossFileAnalyserWorkerThread (cross‑file), along with their logic‑ and style‑focused variants. Each builds a LangChain (prompt + LLM + output parser) and invokes it in a separate thread.DiagnosticsHandlingSubsystem – persistent storage via SQLite. Stores diagnostics per file per analysis type, with timestamps. Provides methods to save new diagnostics, load them for a file, and background threads for TTL‑based cleanup. (Doc here)didOpen, didSave, didChange) or a custom workspace/executeCommand.AnalysisSubsystem.submit_document_for_analysis(doc, event).GeneralDiagnosticsPydanticObjekt).DiagnosticsHandlingSubsystem.save_new_diagnostic() to store the result.load_diagnostics_for_file() which retrieves all stored diagnostics for that file, converts them to LSP Diagnostic objects (using snippet matching via grep), and updates the server’s in‑memory cache.workspace/diagnostic/refresh request to notify the client to pull updated diagnostics.[!NOTE] Due to the fact that neovims support for the pull LSprotocol is incomplete, I also made the language server push the diagnostics once they are loaded. Also note that there is a load_all_diagnostics method that is called on startup, wich may slow the servers startup down, but also loads all the diagnostics for all the files, making the worspace diagnostics navigation directly possible through all of them. (I should make that an optional feature.)
The server implements the following LSP methods:
initialize – stores the configuration from initializationOptions.initialized – initialises subsystems and loads all stored diagnostics.textDocument/didOpen – attempts to load cached diagnostics; if none, triggers analysis.textDocument/didChange – reloads diagnostics from cache (can be configured to trigger analysis).textDocument/didSave – registers a file write and triggers analysis.textDocument/diagnostic – returns diagnostics for a specific document (pull model).workspace/diagnostic – returns diagnostics for the whole workspace.workspace/executeCommand – supports custom commands:
Analyse.Document – expects a URI parameter; forces analysis.Clear.AIDiagnostics – clears diagnostics for a given URI.Clear.AIDiagnostics.All – clears all diagnostics.Set the environment variable AI_DIAGNOS_LOG to anything to enable detailed logging to ai_diagnos_lsp.log in the working directory. (DEBUG USE ONLY)
BE AWARE THAT THE LOGS CONTAIN API KEYS PLAINTEXT
Common issues:
max_file_size and that debounce is not suppressing the request.Contributions are welcome - especially new parsers. Plugin repo is here TODO: ADD IT HERE
A plugin parser is a single executable that acts as a parser function, that gets the file in, and returns the list of files that the file imports back, limited to the project scope defined in the request. Communication happens over stdin/stdout.
Input:
{
"project_scope": ["dir_name", "another_dir_name", "..."],
"file_content": ["line_content", "another_line_content", "..."],
"solid_file_content": "The whole file content as one string",
"file_path": "/file/path"
}
Output:
[
"file/imported/as/filepath", "file/imported/as/filepath", "file/imported/as/filepath"
]
[!NOTE] stdin is used, not arguments
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 1.0 | 21 Mar 2026 | 10 |
This describes our collected data; it is not a security assessment.
Observed since 21 Oct, 10:28 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:e9e96cf05d962b22302523622d09e2e15a360870180a0207e430e69b1a3345d1