LTX-Desktop
基于LTX 2.3 DiT模型的首个完全本地AI视频编辑器,支持文字/图片/音频生视频与专业非线性剪
加载项目详情…
本应用为开源项目,仅供学习研究,请遵守其开源协议。
基于LTX 2.3 DiT模型的首个完全本地AI视频编辑器,支持文字/图片/音频生视频与专业非线性剪
加载项目详情…
本应用为开源项目,仅供学习研究,请遵守其开源协议。
This file provides context for AI coding agents working with this repository.
Aegra is an open-source, self-hosted alternative to LangSmith Deployments. It's a production-ready Agent Protocol server that allows you to run AI agents on your own infrastructure without vendor lock-in.
Key characteristics:
# Install dependencies (from repo root)
uv sync --all-packages
# Start dev server (postgres + auto-migrations + hot reload)
uv run aegra dev
# Run tests
uv run --package aegra-api pytest libs/aegra-api/tests/
uv run --package aegra-cli pytest libs/aegra-cli/tests/
# Lint and format
uv run ruff check .
uv run ruff format .
# Type checking
uv run ty check libs/aegra-api/src/ libs/aegra-cli/src/
# All CI checks at once
make ci-check
# Database migrations (run automatically on server startup)
# To create a new migration:
uv run --package aegra-api alembic revision --autogenerate -m "description"
aegra/
├── libs/
│ ├── aegra-api/ # Core API package
│ │ ├── src/aegra_api/ # Main application code
│ │ │ ├── api/ # Agent Protocol endpoints
│ │ │ ├── services/ # Business logic layer
│ │ │ ├── core/ # Infrastructure (database, auth, orm, health, migrations)
│ │ │ ├── models/ # Pydantic request/response schemas
│ │ │ ├── middleware/ # ASGI middleware
│ │ │ ├── observability/ # OpenTelemetry tracing (Langfuse, Phoenix, OTLP)
│ │ │ ├── utils/ # Helper functions
│ │ │ ├── main.py # FastAPI app entry point
│ │ │ ├── config.py # aegra.json config loading
│ │ │ └── settings.py # Environment settings
│ │ ├── tests/ # Test suite
│ │ └── alembic/ # Database migrations
│ │
│ └── aegra-cli/ # CLI package
│ └── src/aegra_cli/
│ ├── cli.py # Main CLI entry point
│ ├── env.py # .env file loading
│ ├── commands/ # Command implementations (init)
│ ├── utils/ # Docker utilities
│ └── templates/ # Project templates for `aegra init`
│
├── examples/ # Example agents and configs
├── docs/ # Documentation
├── aegra.json # Project configuration (graphs, auth, http, store)
└── docker-compose.yml # Local development setup
Key principle: LangGraph handles ALL state persistence and graph execution. FastAPI provides only HTTP/Agent Protocol compliance.
REDIS_BROKER_ENABLED=true): Runs are dispatched via a Redis job queue (BLPOP). Workers run as concurrent asyncio tasks inside each instance (default: 3 workers x 10 jobs = 30 concurrent runs per instance). Lease-based crash recovery with heartbeat and reaper. Execution params stored in Postgres so workers can reconstruct jobs. OpenTelemetry trace context propagates across the Redis queue boundary.aegra dev, REDIS_BROKER_ENABLED=false): Runs execute as in-process asyncio tasks via LocalExecutor. No Redis needed. SSE uses an in-memory broker.services/executor.py (factory), services/local_executor.py (dev), services/worker_executor.py (prod), services/lease_reaper.py (crash recovery), models/run_job.py (serialized execution params).docs/guides/worker-architecture.mdx for the full architecture documentation.-> None. Never leave the return type blank.X | None union syntax (Python 3.10+), not Optional[X].collections.abc types (Sequence, Mapping, Iterator) over typing equivalents where possible.# CORRECT
def create_user(name: str, age: int) -> User: ...
def process(items: list[str]) -> None: ...
async def fetch(url: str) -> dict[str, Any]: ...
# WRONG — missing return type, missing param types
def create_user(name, age): ...
def process(items): ...
aegra_api.* prefix.ImportError) or the import is from an optional dependency that may not be installed (wrapped in try/except ImportError). "Might be slow" or "only used here" are NOT valid reasons for inline imports. If unsure, put it at the top — only move inline after confirming the import cycle with an actual error.noqa, type: ignore)# noqa: F401 on a dead re-export means you should delete the re-export and fix the importers. # type: ignore on a type mismatch means you should fix the types.# noqa: S311 on random.uniform used for jitter (not security), or # noqa: B017 when an SDK doesn't expose specific exception types.except: or except Exception: pass. Always catch specific exceptions.try block when possible. Narrow the scope of exception handling.HTTPException for expected API errors. Use middleware for unexpected errors.except SomeError: pass is almost always wrong.with statements) for resource cleanup.# CORRECT — guard clause, specific exception
def get_user(user_id: str) -> User:
if not user_id:
raise ValueError("user_id is required")
try:
return db.fetch_user(user_id)
except UserNotFoundError:
raise HTTPException(status_code=404, detail="User not found")
# WRONG — broad catch, swallowed exception, happy path buried
def get_user(user_id):
try:
if user_id:
user = db.fetch_user(user_id)
if user:
return user
except Exception:
pass
return None
def f(items=[]) or def f(data={})). Use None and create inside the function.* separator).# CORRECT — keyword-only args, immutable default
def create_assistant(name: str, *, graph_id: str, config: dict | None = None, metadata: dict | None = None) -> Assistant:
config = config or {}
...
# WRONG — mutable default, too many positional args
def create_assistant(name, graph_id, config={}, metadata={}, version=1, context={}):
...
test_returns_404_when_assistant_not_found, not test_get_assistant_2.pytest — never unittest classes.pytest-asyncio.tests/conftest.py.monkeypatch over unittest.mock where possible.Every new feature or endpoint MUST have tests at all applicable levels:
tests/unit/) — isolated function-level tests with mocked deps (AsyncMock, patch).tests/integration/) — HTTP-level via FastAPI TestClient with mocked DB sessions (DummySessionBase, override_session_dependency). Tests request validation, route logic, status codes. Use create_test_app() + make_client() from tests/fixtures/clients.py.tests/e2e/) — real running server + real DB. Use LangGraph SDK client (get_e2e_client()) or httpx.AsyncClient. Marked @pytest.mark.e2e. Use elog() and check_and_skip_if_geo_blocked() from tests/e2e/_utils.py.Do NOT skip any level unless genuinely not applicable (e.g. pure utility functions don't need E2E).
After implementing a feature or fixing a bug, verify the work end-to-end against a real running server. Don't stop at unit/integration tests — prove it works for real.
docker info. On Windows: cmd.exe /c start "" "C:\Program Files\Docker\Docker\Docker Desktop.exe" then poll docker info. On Mac: open -a Docker then poll. Linux: usually already running.docker compose up -d from repo root. Source code is volume-mounted with hot reload (--reload), so code changes are picked up live — no rebuild needed. Only use --build when dependencies change (pyproject.toml, Dockerfile). Wait for health: poll curl -s http://localhost:2026/health until {"status":"healthy",...}. Check docker compose logs --tail=50 if unhealthy.uv run --package aegra-api pytest libs/aegra-api/tests/e2e/<test_file>.py -vcurl against http://localhost:2026/<endpoint>from langgraph_sdk import get_client; client = get_client(url="http://localhost:2026"), run it, then delete ithttpx to call endpoints, parse responses, and assert results, then clean updocker compose down when done (unless user wants it kept running).Aegra has two execution modes (dev = LocalExecutor, prod = WorkerExecutor). E2E tests should pass in both modes. Use the Makefile targets:
make e2e-dev # Dev mode (no Redis, in-process tasks)
make e2e-prod # Prod mode (Redis workers, lease recovery)
make e2e-both # Run both sequentially
Tests marked @pytest.mark.prod_only are skipped in dev mode (they require Redis workers). Multi-instance and stress tests in tests/e2e/multi_instance/ are manual-only — run them explicitly when testing worker architecture or scaling changes.
These rules exist because AI agents repeatedly make these mistakes. Follow them carefully:
tail. When running tests, builds, or any command where you need to see results, do NOT use | tail -N — the results you need will scroll past and be lost. Use | grep "passed\|failed\|error" to filter, or just let the full output show. If output is too long, use | tail -30 with a generous line count, not | tail -5.pyproject.toml before importing a new dependency.Aegra runs against user-managed Postgres including multi-host HA (PR #299). DB code has invariants that break silently in prod. Before touching DB code, walk this checklist:
Two URLs, do not cross drivers.
settings.db.database_url → asyncpg query-param form. SQLAlchemy only.settings.db.database_url_sync → raw libpq, comma-host preserved. psycopg only (LangGraph pool, migrations precheck).database_url_sync to SQLAlchemy (create_engine, async_engine_from_config) silently breaks HA — SQLAlchemy's URL parser doesn't grok libpq comma-hosts. For sync DBAPI, use psycopg.connect(database_url_sync) directly.Pool ownership. Long-lived pools belong in db_manager only. Short-lived helpers must close deterministically (with or try/finally). Code running before db_manager.initialize() cannot assume pools exist.
Migrations.
down_revision chain. Idempotent + resumable.run_migrations_if_needed() (lock-free precheck). run_migrations() is for aegra db upgrade only. Don't regress the precheck.RUN_MIGRATIONS_ON_STARTUP=false + aegra db upgrade out-of-band. Changing startup behavior needs both .env.example files + docs/guides/deployment.mdx updated.SQL-layer authorization. Every tenant-scoped read/write needs user_id == user.identity in the WHERE, even with @auth.on registered (default-allow when no handler — see GHSA-m98r-6667-4wq7). Routes taking thread_id/assistant_id/cron_id path params verify ownership + 404 at handler entry, not deeper.
Connection footprint. New pools increase per-pod conn count. Extend existing or document the cap impact. PgBouncer/RDS Proxy transaction-pool mode breaks LISTEN/NOTIFY + prepared statements; flag accordingly.
Testing. Mock at the driver layer, not SQLAlchemy, when bypassing SQLAlchemy. Assert the exact URL passed to the driver matches settings.db.* so refactors can't quietly reintroduce SQLAlchemy URL parsing on libpq strings.
.env files or environment variables.eval(), exec(), or pickle on user input.subprocess.run([...], shell=False) — never shell=True with user input.The system uses two connection pools:
URL format: LangGraph requires postgresql:// while SQLAlchemy uses postgresql+asyncpg://
aegra.json defines graphs, auth, HTTP config, and store settings. See docs/configuration.md for full reference.
Agents are Python modules exporting a graph variable. This can be:
Static graph (compiled once, cached):
builder = StateGraph(State)
# ... define nodes and edges
graph = builder.compile() # Must export as 'graph'
Factory function (called per-request with user/config context):
from langgraph_sdk.runtime import ServerRuntime
def graph(runtime: ServerRuntime):
"""Per-request factory — receives user, store, and access context."""
user = runtime.user
builder = StateGraph(State)
# ... customize based on user
return builder.compile()
Supported factory signatures: 0-arg (called once at startup), config-only (dict), runtime-only (ServerRuntime), or both (any order). Factories can use ServerRuntime[T] to receive typed request context on runtime.context (Pydantic BaseModel or dataclass). See docs/reference/configuration.mdx for full details.
examples/graph variableaegra.json under graphslibs/aegra-api/src/aegra_api/api/libs/aegra-api/src/aegra_api/models/libs/aegra-api/src/aegra_api/services/libs/aegra-api/src/aegra_api/main.pylibs/aegra-api/src/aegra_api/core/orm.pyuv run --package aegra-api alembic revision --autogenerate -m "description"libs/aegra-api/alembic/versions/make test (or uv run --package aegra-api pytest libs/aegra-api/tests/) before committingmake lint (or uv run ruff check .) for linting[component] Brief descriptionREADME.md (root), libs/aegra-api/README.md, libs/aegra-cli/README.mdCLAUDE.md (this file)docs/ directory (developer-guide, migration-cheatsheet, configuration, authentication, custom-routes, etc.).env.example files that MUST be kept in sync:
/.env.example — Root file used for development and documentation referencelibs/aegra-cli/src/aegra_cli/templates/env.example.template — Template used by aegra init to generate .env.example for new projects (uses $slug placeholders for project-specific values)$slug in place of project-specific values (PROJECT_NAME, POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, DATABASE_URL comment). All other values should be identical between the two files.aegra-api and aegra-cli MUST always have the same version. Both versions live in their respective pyproject.toml files (libs/aegra-api/pyproject.toml and libs/aegra-cli/pyproject.toml).aegra-cli depends on aegra-api~=X.Y.Z (compatible release). This allows patch updates (X.Y.Z+1) without changing the constraint, but a minor bump requires updating the constraint in aegra-cli/pyproject.toml.0.x.y), the version scheme is 0.MAJOR.PATCH:
version in BOTH pyproject.toml files.version in BOTH pyproject.toml files AND update the aegra-api~= constraint in aegra-cli/pyproject.toml.aegra meta-package (on PyPI, not in this repo) is a name reservation that points to aegra-cli. It does not need to be updated on every release.