Observed since 21 Oct, 11:02 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
ai-incident-investigator
Provider-neutral AI-powered incident classification, root-cause analysis, and reporting.
AI Incident Investigator is a Python application for automated log analysis, incident investigation, and AI-assisted root-cause analysis. It combines a FastAPI backend, CLI tooling, database-backed incident records, optional S3-compatible storage, and a built-in dashboard for structured investigations.
It helps teams investigate application and infrastructure incidents by:
Install the package with the database backend you want to use:
# SQLite
pip install "ai-incident-investigator[sqlite]"
# PostgreSQL
pip install "ai-incident-investigator[postgresql]"
# MySQL / MariaDB
pip install "ai-incident-investigator[mysql]"
If you also want S3 or MinIO support:
pip install "ai-incident-investigator[sqlite,s3]"
For all integrations:
pip install "ai-incident-investigator[all]"
For local development:
pip install -e ".[dev]"
Create a .env file in the directory where you run the application:
AI_PROVIDER=openai
AI_MODEL=gpt-5.4-mini
AI_API_KEY=your-key
DATABASE_URL=sqlite+aiosqlite:///./incident_investigator.db
STORAGE_BACKEND=local
LOCAL_STORAGE_PATH=./data/logs
MAX_UPLOAD_SIZE_BYTES=10485760
STORAGE_BACKEND=s3
S3_BUCKET=incident-logs
S3_PREFIX=uploads
S3_ENDPOINT_URL=http://localhost:9000
S3_REGION=us-east-1
S3_ACCESS_KEY_ID=minioadmin
S3_SECRET_ACCESS_KEY=minioadmin
S3_USE_SSL=false
Omit S3_ENDPOINT_URL for normal AWS S3. Credentials can also come from the standard AWS credential chain.
ALLOWED_LOG_EXTENSIONS=.log,.txt,.out,.err,.json,.jsonl,.csv,.yaml,.yml
ALLOWED_LOG_CONTENT_TYPES=text/plain,text/csv,application/json,application/x-ndjson,application/yaml,text/yaml,application/octet-stream
REJECT_BINARY_LOGS=true
MAX_UPLOAD_SIZE_BYTES=10485760
MAX_LOG_CHARACTERS=50000
MAX_UPLOAD_SIZE_BYTES limits stored input, while MAX_LOG_CHARACTERS limits the amount of text sent to the AI model.
Start the service:
incident-investigator
# or explicitly:
incident-investigator serve --host 127.0.0.1 --port 8000
Open the API docs:
For production deployments, it is usually recommended to disable automatic creation:
DATABASE_AUTO_CREATE=false
Then run:
incident-investigator migrate
Before starting the app, you can run:
incident-investigator doctor
This checks the current environment, configured database driver, AI provider, and optional storage dependencies. The same validation is also run automatically by serve and migrate.
The GitHub/GitHub Enterprise integration is optional. When enabled, the application can use repository context to validate stack-trace and file references and present them as evidence, not as direct instructions to the model.
Example configuration:
GITHUB_ENABLED=true
GITHUB_BASE_URL=https://github.company.example
GITHUB_TOKEN=replace-with-read-only-token
GITHUB_ORGANIZATION=my-organization
GITHUB_SOURCE_LOOKUP_ENABLED=true
GITHUB_CONTEXT_LINES=25
GITHUB_MAX_SEARCH_RESULTS=5
GITHUB_MAX_CANDIDATES=8
GITHUB_MAX_QUERIES=6
GITHUB_TIMEOUT_SECONDS=10
GITHUB_VERIFY_SSL=true
The integration is read-only and intentionally designed to fail safely: if GitHub is unavailable or lookup fails, the core investigation still continues with log-based inference as a fallback.
Possible source statuses include:
resolvedinferred_from_logmultiple_candidatesnot_foundnot_configuredlookup_failedAnalyze and store a log:
curl -X POST "http://127.0.0.1:8000/api/v1/incidents/analyze" \
-H "Content-Type: multipart/form-data" \
-F "log_file=@log-example.log"
Run orchestration for an incident:
curl -X POST "http://127.0.0.1:8000/api/v1/incidents/INCIDENT_ID/orchestrate"
Download the original log:
curl -OJ "http://127.0.0.1:8000/api/v1/incidents/INCIDENT_ID/log"
The application can analyze compressed support bundles such as ZIP, TAR, TGZ, TBZ2, TXZ, GZIP, BZIP2, and XZ archives.
curl -X POST http://127.0.0.1:8000/api/v1/incidents/analyze-archive \
-F archive_file=@support-bundle.zip \
-F problem_description="Device disconnected during a call" \
-F incident_time="2026-07-25T14:35:00" \
-F timezone="Europe/Belgrade" \
-F system_name="X52-2"
This workflow validates archive contents, enforces extraction limits, redacts common secrets, builds a component timeline, and exposes evidence-linked root-cause analysis.
Version 0.7.0 adds workspace-scoped accounts and authorization. Roles include:
owneradmininvestigatorviewerExample session settings:
SESSION_SECRET_KEY=replace-with-a-long-random-secret
SESSION_COOKIE_NAME=incident_investigator_session
SESSION_MAX_AGE_SECONDS=604800
SESSION_HTTPS_ONLY=true
REGISTRATION_ENABLED=true
The system tracks input, output, and total token usage for analysis runs when the provider returns usage metadata. You can configure USD pricing per one million tokens:
AI_MODEL_PRICING_JSON={"openai:your-model":{"input_per_1m":1.25,"output_per_1m":5.00},"anthropic/your-model":{"input_per_1m":3.00,"output_per_1m":15.00}}
If a model has no configured price, the UI shows that status instead of inventing a value.
.env, database files, stored logs, credentials, caches, or build artifacts.ruff check .
pytest
python -m build
python -m twine check dist/*
MIT
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
fastapiEnvironment{
"extras": "standard",
"marker": null,
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | fastapi[standard]>=0.115 | runtime |
openai-agentsEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | openai-agents>=0.2 | runtime |
pydantic-settingsEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | pydantic-settings>=2.7 | runtime |
python-multipartEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | python-multipart>=0.0.18 | runtime |
sqlalchemyEnvironment{
"extras": "asyncio",
"marker": null,
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | sqlalchemy[asyncio]>=2.0 | runtime |
itsdangerousEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | itsdangerous>=2.2 | runtime |
email-validatorEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | email-validator>=2.2 | runtime |
alembicEnvironment{
"extras": "",
"marker": null,
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | alembic>=1.14 | runtime |
aiosqliteEnvironment{
"extras": "",
"marker": "extra == \"sqlite\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | aiosqlite>=0.20; extra == "sqlite" | runtime |
asyncpgEnvironment{
"extras": "",
"marker": "extra == \"postgresql\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | asyncpg>=0.30; extra == "postgresql" | runtime |
aiomysqlEnvironment{
"extras": "",
"marker": "extra == \"mysql\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | aiomysql>=0.2; extra == "mysql" | runtime |
openai-agentsEnvironment{
"extras": "litellm",
"marker": "extra == \"litellm\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | openai-agents[litellm]>=0.2; extra == "litellm" | runtime |
boto3Environment{
"extras": "",
"marker": "extra == \"s3\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | boto3>=1.35; extra == "s3" | runtime |
aiosqliteEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | aiosqlite>=0.20; extra == "all" | runtime |
asyncpgEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | asyncpg>=0.30; extra == "all" | runtime |
aiomysqlEnvironment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | aiomysql>=0.2; extra == "all" | runtime |
openai-agentsEnvironment{
"extras": "litellm",
"marker": "extra == \"all\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | openai-agents[litellm]>=0.2; extra == "all" | runtime |
boto3Environment{
"extras": "",
"marker": "extra == \"all\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | boto3>=1.35; extra == "all" | runtime |
aiosqliteEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | aiosqlite>=0.20; extra == "dev" | runtime |
asyncpgEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | asyncpg>=0.30; extra == "dev" | runtime |
aiomysqlEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | aiomysql>=0.2; extra == "dev" | runtime |
openai-agentsEnvironment{
"extras": "litellm",
"marker": "extra == \"dev\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | openai-agents[litellm]>=0.2; extra == "dev" | runtime |
boto3Environment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | boto3>=1.35; extra == "dev" | runtime |
buildEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | build; extra == "dev" | runtime |
httpxEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | httpx; extra == "dev" | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | pytest; extra == "dev" | runtime |
pytest-asyncioEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | pytest-asyncio; extra == "dev" | runtime |
ruffEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | ruff; extra == "dev" | runtime |
twineEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.12",
"evaluation": "NOT_EVALUATED"
} | twine; extra == "dev" | runtime |
bdist_wheel · 112.84 KB · Python py3
sdist · 100.72 KB · Python source
Shared repository data · GitHub
Observed since 21 Oct, 11:02 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:2aef41af62dd6d9b2b321a71c487d67f80212f964aa263b61d322d5a218b588a