aegra-api 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aegra_api-0.1.0/.gitignore +32 -0
- aegra_api-0.1.0/PKG-INFO +244 -0
- aegra_api-0.1.0/README.md +202 -0
- aegra_api-0.1.0/alembic/env.py +100 -0
- aegra_api-0.1.0/alembic/script.py.mako +24 -0
- aegra_api-0.1.0/alembic/versions/20250817172544_initial_schema.py +196 -0
- aegra_api-0.1.0/alembic/versions/20250830161758_add_context.py +43 -0
- aegra_api-0.1.0/alembic/versions/20250831174511_add_cascade_delete_for_runs_thread_fkey.py +42 -0
- aegra_api-0.1.0/alembic/versions/20250913193817_add_version_table.py +82 -0
- aegra_api-0.1.0/alembic/versions/20250913213535_add_metadata_for_assistant_table_and_.py +76 -0
- aegra_api-0.1.0/alembic/versions/20251115152415_migrate_run_status_to_langgraph_.py +70 -0
- aegra_api-0.1.0/alembic.ini +107 -0
- aegra_api-0.1.0/pyproject.toml +109 -0
- aegra_api-0.1.0/src/aegra_api/__init__.py +3 -0
- aegra_api-0.1.0/src/aegra_api/api/__init__.py +1 -0
- aegra_api-0.1.0/src/aegra_api/api/assistants.py +235 -0
- aegra_api-0.1.0/src/aegra_api/api/runs.py +1110 -0
- aegra_api-0.1.0/src/aegra_api/api/store.py +200 -0
- aegra_api-0.1.0/src/aegra_api/api/threads.py +761 -0
- aegra_api-0.1.0/src/aegra_api/config.py +204 -0
- aegra_api-0.1.0/src/aegra_api/constants.py +5 -0
- aegra_api-0.1.0/src/aegra_api/core/__init__.py +0 -0
- aegra_api-0.1.0/src/aegra_api/core/app_loader.py +91 -0
- aegra_api-0.1.0/src/aegra_api/core/auth_ctx.py +65 -0
- aegra_api-0.1.0/src/aegra_api/core/auth_deps.py +186 -0
- aegra_api-0.1.0/src/aegra_api/core/auth_handlers.py +248 -0
- aegra_api-0.1.0/src/aegra_api/core/auth_middleware.py +331 -0
- aegra_api-0.1.0/src/aegra_api/core/database.py +123 -0
- aegra_api-0.1.0/src/aegra_api/core/health.py +131 -0
- aegra_api-0.1.0/src/aegra_api/core/orm.py +165 -0
- aegra_api-0.1.0/src/aegra_api/core/route_merger.py +69 -0
- aegra_api-0.1.0/src/aegra_api/core/serializers/__init__.py +7 -0
- aegra_api-0.1.0/src/aegra_api/core/serializers/base.py +22 -0
- aegra_api-0.1.0/src/aegra_api/core/serializers/general.py +54 -0
- aegra_api-0.1.0/src/aegra_api/core/serializers/langgraph.py +102 -0
- aegra_api-0.1.0/src/aegra_api/core/sse.py +178 -0
- aegra_api-0.1.0/src/aegra_api/main.py +303 -0
- aegra_api-0.1.0/src/aegra_api/middleware/__init__.py +4 -0
- aegra_api-0.1.0/src/aegra_api/middleware/double_encoded_json.py +74 -0
- aegra_api-0.1.0/src/aegra_api/middleware/logger_middleware.py +95 -0
- aegra_api-0.1.0/src/aegra_api/models/__init__.py +76 -0
- aegra_api-0.1.0/src/aegra_api/models/assistants.py +81 -0
- aegra_api-0.1.0/src/aegra_api/models/auth.py +62 -0
- aegra_api-0.1.0/src/aegra_api/models/enums.py +29 -0
- aegra_api-0.1.0/src/aegra_api/models/errors.py +29 -0
- aegra_api-0.1.0/src/aegra_api/models/runs.py +124 -0
- aegra_api-0.1.0/src/aegra_api/models/store.py +67 -0
- aegra_api-0.1.0/src/aegra_api/models/threads.py +152 -0
- aegra_api-0.1.0/src/aegra_api/observability/__init__.py +1 -0
- aegra_api-0.1.0/src/aegra_api/observability/base.py +88 -0
- aegra_api-0.1.0/src/aegra_api/observability/otel.py +133 -0
- aegra_api-0.1.0/src/aegra_api/observability/setup.py +27 -0
- aegra_api-0.1.0/src/aegra_api/observability/targets/__init__.py +11 -0
- aegra_api-0.1.0/src/aegra_api/observability/targets/base.py +18 -0
- aegra_api-0.1.0/src/aegra_api/observability/targets/langfuse.py +33 -0
- aegra_api-0.1.0/src/aegra_api/observability/targets/otlp.py +38 -0
- aegra_api-0.1.0/src/aegra_api/observability/targets/phoenix.py +24 -0
- aegra_api-0.1.0/src/aegra_api/services/__init__.py +0 -0
- aegra_api-0.1.0/src/aegra_api/services/assistant_service.py +569 -0
- aegra_api-0.1.0/src/aegra_api/services/base_broker.py +59 -0
- aegra_api-0.1.0/src/aegra_api/services/broker.py +141 -0
- aegra_api-0.1.0/src/aegra_api/services/event_converter.py +157 -0
- aegra_api-0.1.0/src/aegra_api/services/event_store.py +196 -0
- aegra_api-0.1.0/src/aegra_api/services/graph_streaming.py +433 -0
- aegra_api-0.1.0/src/aegra_api/services/langgraph_service.py +456 -0
- aegra_api-0.1.0/src/aegra_api/services/streaming_service.py +362 -0
- aegra_api-0.1.0/src/aegra_api/services/thread_state_service.py +128 -0
- aegra_api-0.1.0/src/aegra_api/settings.py +124 -0
- aegra_api-0.1.0/src/aegra_api/utils/__init__.py +3 -0
- aegra_api-0.1.0/src/aegra_api/utils/assistants.py +23 -0
- aegra_api-0.1.0/src/aegra_api/utils/run_utils.py +60 -0
- aegra_api-0.1.0/src/aegra_api/utils/setup_logging.py +122 -0
- aegra_api-0.1.0/src/aegra_api/utils/sse_utils.py +26 -0
- aegra_api-0.1.0/src/aegra_api/utils/status_compat.py +57 -0
- aegra_api-0.1.0/tests/README.md +197 -0
- aegra_api-0.1.0/tests/__init__.py +0 -0
- aegra_api-0.1.0/tests/conftest.py +199 -0
- aegra_api-0.1.0/tests/e2e/__init__.py +0 -0
- aegra_api-0.1.0/tests/e2e/_utils.py +69 -0
- aegra_api-0.1.0/tests/e2e/conftest.py +7 -0
- aegra_api-0.1.0/tests/e2e/manual_auth_tests/README.md +64 -0
- aegra_api-0.1.0/tests/e2e/manual_auth_tests/__init__.py +1 -0
- aegra_api-0.1.0/tests/e2e/manual_auth_tests/conftest.py +52 -0
- aegra_api-0.1.0/tests/e2e/manual_auth_tests/test_auth_e2e.py +324 -0
- aegra_api-0.1.0/tests/e2e/manual_auth_tests/test_authorization_handlers_e2e.py +239 -0
- aegra_api-0.1.0/tests/e2e/test_assistants/__init__.py +0 -0
- aegra_api-0.1.0/tests/e2e/test_assistants/test_assistant_deletion.py +185 -0
- aegra_api-0.1.0/tests/e2e/test_assistants/test_assistant_graph.py +277 -0
- aegra_api-0.1.0/tests/e2e/test_assistants/test_assistant_search.py +153 -0
- aegra_api-0.1.0/tests/e2e/test_assistants/test_assistant_version.py +107 -0
- aegra_api-0.1.0/tests/e2e/test_custom_routes/__init__.py +1 -0
- aegra_api-0.1.0/tests/e2e/test_custom_routes/test_custom_routes_e2e.py +69 -0
- aegra_api-0.1.0/tests/e2e/test_human_in_loop/__init__.py +0 -0
- aegra_api-0.1.0/tests/e2e/test_human_in_loop/test_human_in_loop.py +814 -0
- aegra_api-0.1.0/tests/e2e/test_runs/__init__.py +0 -0
- aegra_api-0.1.0/tests/e2e/test_runs/test_background_run_join.py +151 -0
- aegra_api-0.1.0/tests/e2e/test_runs/test_cancel_run_e2e.py +316 -0
- aegra_api-0.1.0/tests/e2e/test_runs/test_run_join_output.py +73 -0
- aegra_api-0.1.0/tests/e2e/test_runs/test_runs.py +299 -0
- aegra_api-0.1.0/tests/e2e/test_store/__init__.py +0 -0
- aegra_api-0.1.0/tests/e2e/test_store/test_store.py +63 -0
- aegra_api-0.1.0/tests/e2e/test_streaming/__init__.py +0 -0
- aegra_api-0.1.0/tests/e2e/test_streaming/test_chat_streaming.py +77 -0
- aegra_api-0.1.0/tests/e2e/test_streaming/test_events_mode.py +172 -0
- aegra_api-0.1.0/tests/e2e/test_streaming/test_streaming_error_e2e.py +326 -0
- aegra_api-0.1.0/tests/e2e/test_threads/__init__.py +0 -0
- aegra_api-0.1.0/tests/e2e/test_threads/test_get_state_endpoint.py +168 -0
- aegra_api-0.1.0/tests/e2e/test_threads/test_history_endpoint.py +59 -0
- aegra_api-0.1.0/tests/e2e/test_threads/test_state_endpoint.py +78 -0
- aegra_api-0.1.0/tests/e2e/test_threads/test_thread_deletion.py +193 -0
- aegra_api-0.1.0/tests/e2e/test_threads/test_update_state_endpoint.py +454 -0
- aegra_api-0.1.0/tests/fixtures/__init__.py +1 -0
- aegra_api-0.1.0/tests/fixtures/auth.py +15 -0
- aegra_api-0.1.0/tests/fixtures/clients.py +40 -0
- aegra_api-0.1.0/tests/fixtures/custom_routes_with_auth.py +63 -0
- aegra_api-0.1.0/tests/fixtures/database.py +48 -0
- aegra_api-0.1.0/tests/fixtures/langgraph.py +179 -0
- aegra_api-0.1.0/tests/fixtures/mock_jwt_auth.py +133 -0
- aegra_api-0.1.0/tests/fixtures/session_fixtures.py +77 -0
- aegra_api-0.1.0/tests/fixtures/test_helpers.py +124 -0
- aegra_api-0.1.0/tests/integration/__init__.py +0 -0
- aegra_api-0.1.0/tests/integration/conftest.py +7 -0
- aegra_api-0.1.0/tests/integration/test_api/__init__.py +0 -0
- aegra_api-0.1.0/tests/integration/test_api/test_assistants_crud.py +737 -0
- aegra_api-0.1.0/tests/integration/test_api/test_runs_crud.py +790 -0
- aegra_api-0.1.0/tests/integration/test_api/test_store_crud.py +522 -0
- aegra_api-0.1.0/tests/integration/test_api/test_threads_crud.py +858 -0
- aegra_api-0.1.0/tests/integration/test_api/test_threads_history.py +202 -0
- aegra_api-0.1.0/tests/integration/test_auth_flow.py +350 -0
- aegra_api-0.1.0/tests/integration/test_auth_handlers_integration.py +175 -0
- aegra_api-0.1.0/tests/integration/test_cancel_run.py +189 -0
- aegra_api-0.1.0/tests/integration/test_custom_routes.py +209 -0
- aegra_api-0.1.0/tests/integration/test_services/__init__.py +0 -0
- aegra_api-0.1.0/tests/integration/test_services/test_assistant_service_db.py +460 -0
- aegra_api-0.1.0/tests/integration/test_services/test_event_store_integration.py +443 -0
- aegra_api-0.1.0/tests/integration/test_services/test_langgraph_service_integration.py +446 -0
- aegra_api-0.1.0/tests/integration/test_services/test_streaming_hitl.py +100 -0
- aegra_api-0.1.0/tests/integration/test_streaming_errors.py +303 -0
- aegra_api-0.1.0/tests/unit/__init__.py +0 -0
- aegra_api-0.1.0/tests/unit/conftest.py +23 -0
- aegra_api-0.1.0/tests/unit/test_api/test_runs.py +347 -0
- aegra_api-0.1.0/tests/unit/test_api/test_runs_streaming.py +218 -0
- aegra_api-0.1.0/tests/unit/test_api/test_runs_wait.py +744 -0
- aegra_api-0.1.0/tests/unit/test_api/test_thread_status.py +59 -0
- aegra_api-0.1.0/tests/unit/test_core/__init__.py +0 -0
- aegra_api-0.1.0/tests/unit/test_core/test_app_loader.py +91 -0
- aegra_api-0.1.0/tests/unit/test_core/test_auth_ctx.py +169 -0
- aegra_api-0.1.0/tests/unit/test_core/test_auth_deps.py +239 -0
- aegra_api-0.1.0/tests/unit/test_core/test_auth_handlers.py +426 -0
- aegra_api-0.1.0/tests/unit/test_core/test_auth_middleware.py +526 -0
- aegra_api-0.1.0/tests/unit/test_core/test_config.py +202 -0
- aegra_api-0.1.0/tests/unit/test_core/test_database_manager.py +228 -0
- aegra_api-0.1.0/tests/unit/test_core/test_route_merger.py +106 -0
- aegra_api-0.1.0/tests/unit/test_core/test_serializers/__init__.py +0 -0
- aegra_api-0.1.0/tests/unit/test_core/test_serializers/test_general.py +408 -0
- aegra_api-0.1.0/tests/unit/test_core/test_sse.py +294 -0
- aegra_api-0.1.0/tests/unit/test_cors_config.py +196 -0
- aegra_api-0.1.0/tests/unit/test_main.py +113 -0
- aegra_api-0.1.0/tests/unit/test_middleware/__init__.py +0 -0
- aegra_api-0.1.0/tests/unit/test_middleware/test_double_encoded_json.py +389 -0
- aegra_api-0.1.0/tests/unit/test_middleware/test_logger_middleware.py +112 -0
- aegra_api-0.1.0/tests/unit/test_models/__init__.py +0 -0
- aegra_api-0.1.0/tests/unit/test_models/test_run_status_validation.py +85 -0
- aegra_api-0.1.0/tests/unit/test_models/test_runcreate_validation.py +24 -0
- aegra_api-0.1.0/tests/unit/test_models/test_thread_status_validation.py +85 -0
- aegra_api-0.1.0/tests/unit/test_observability/__init__.py +0 -0
- aegra_api-0.1.0/tests/unit/test_observability/test_base.py +288 -0
- aegra_api-0.1.0/tests/unit/test_observability/test_otel.py +272 -0
- aegra_api-0.1.0/tests/unit/test_observability/test_setup.py +78 -0
- aegra_api-0.1.0/tests/unit/test_observability/test_targets/__init__.py +0 -0
- aegra_api-0.1.0/tests/unit/test_observability/test_targets/test_base.py +51 -0
- aegra_api-0.1.0/tests/unit/test_observability/test_targets/test_langfuse.py +72 -0
- aegra_api-0.1.0/tests/unit/test_observability/test_targets/test_otlp.py +84 -0
- aegra_api-0.1.0/tests/unit/test_observability/test_targets/test_phoenix.py +54 -0
- aegra_api-0.1.0/tests/unit/test_services/__init__.py +0 -0
- aegra_api-0.1.0/tests/unit/test_services/test_assistant_service.py +1021 -0
- aegra_api-0.1.0/tests/unit/test_services/test_assistant_service_schemas.py +337 -0
- aegra_api-0.1.0/tests/unit/test_services/test_broker.py +212 -0
- aegra_api-0.1.0/tests/unit/test_services/test_event_converter.py +351 -0
- aegra_api-0.1.0/tests/unit/test_services/test_event_converter_namespace.py +120 -0
- aegra_api-0.1.0/tests/unit/test_services/test_event_store.py +375 -0
- aegra_api-0.1.0/tests/unit/test_services/test_graph_streaming.py +719 -0
- aegra_api-0.1.0/tests/unit/test_services/test_graph_streaming_events_mode.py +138 -0
- aegra_api-0.1.0/tests/unit/test_services/test_langgraph_service.py +864 -0
- aegra_api-0.1.0/tests/unit/test_services/test_streaming_service.py +460 -0
- aegra_api-0.1.0/tests/unit/test_services/test_thread_state_service.py +130 -0
- aegra_api-0.1.0/tests/unit/test_threads/test_state.py +222 -0
- aegra_api-0.1.0/tests/unit/test_threads/test_state_checkpoint.py +274 -0
- aegra_api-0.1.0/tests/unit/test_utils/__init__.py +0 -0
- aegra_api-0.1.0/tests/unit/test_utils/test_assistants_utils.py +101 -0
- aegra_api-0.1.0/tests/unit/test_utils/test_run_utils.py +83 -0
- aegra_api-0.1.0/tests/unit/test_utils/test_sse_utils.py +135 -0
- aegra_api-0.1.0/tests/unit/test_utils/test_status_compat.py +52 -0
- aegra_api-0.1.0/uv.lock +2555 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Python-generated files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[oc]
|
|
4
|
+
build/
|
|
5
|
+
dist/
|
|
6
|
+
wheels/
|
|
7
|
+
*.egg-info
|
|
8
|
+
|
|
9
|
+
# Virtual environments
|
|
10
|
+
.venv
|
|
11
|
+
|
|
12
|
+
# env files
|
|
13
|
+
.env
|
|
14
|
+
|
|
15
|
+
# Pycharm
|
|
16
|
+
.idea/
|
|
17
|
+
|
|
18
|
+
# VSCode
|
|
19
|
+
.vscode/
|
|
20
|
+
|
|
21
|
+
# Testing
|
|
22
|
+
.coverage
|
|
23
|
+
coverage.xml
|
|
24
|
+
htmlcov/
|
|
25
|
+
.pytest_cache/
|
|
26
|
+
.mypy_cache/
|
|
27
|
+
|
|
28
|
+
# Ruff
|
|
29
|
+
.ruff_cache/
|
|
30
|
+
|
|
31
|
+
# Pre-commit
|
|
32
|
+
.pre-commit-cache/
|
aegra_api-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aegra-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Aegra core API - Self-hosted Agent Protocol server
|
|
5
|
+
Project-URL: Homepage, https://github.com/ibbybuilds/aegra
|
|
6
|
+
Project-URL: Documentation, https://github.com/ibbybuilds/aegra#readme
|
|
7
|
+
Project-URL: Repository, https://github.com/ibbybuilds/aegra
|
|
8
|
+
Project-URL: Issues, https://github.com/ibbybuilds/aegra/issues
|
|
9
|
+
Author-email: Muhammad Ibrahim <mibrahim37612@gmail.com>
|
|
10
|
+
License-Expression: Apache-2.0
|
|
11
|
+
Keywords: agent-protocol,agents,fastapi,langgraph,llm
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Framework :: FastAPI
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Requires-Dist: alembic>=1.16.4
|
|
21
|
+
Requires-Dist: asgi-correlation-id>=4.3.4
|
|
22
|
+
Requires-Dist: asyncpg>=0.30.0
|
|
23
|
+
Requires-Dist: fastapi>=0.116.1
|
|
24
|
+
Requires-Dist: greenlet>=3.2.3
|
|
25
|
+
Requires-Dist: langchain-openai>=1.0.3
|
|
26
|
+
Requires-Dist: langchain>=1.0.8
|
|
27
|
+
Requires-Dist: langgraph-checkpoint-postgres>=2.0.23
|
|
28
|
+
Requires-Dist: langgraph>=1.0.3
|
|
29
|
+
Requires-Dist: openinference-instrumentation-langchain>=0.1.58
|
|
30
|
+
Requires-Dist: opentelemetry-api>=1.39.1
|
|
31
|
+
Requires-Dist: opentelemetry-exporter-otlp>=1.39.1
|
|
32
|
+
Requires-Dist: opentelemetry-sdk>=1.39.1
|
|
33
|
+
Requires-Dist: psycopg[binary]>=3.2.9
|
|
34
|
+
Requires-Dist: pydantic-settings>=2.12.0
|
|
35
|
+
Requires-Dist: pydantic>=2.11.7
|
|
36
|
+
Requires-Dist: pyjwt>=2.10.1
|
|
37
|
+
Requires-Dist: python-dotenv>=1.1.1
|
|
38
|
+
Requires-Dist: sqlalchemy>=2.0.0
|
|
39
|
+
Requires-Dist: structlog>=25.4.0
|
|
40
|
+
Requires-Dist: uvicorn>=0.35.0
|
|
41
|
+
Description-Content-Type: text/markdown
|
|
42
|
+
|
|
43
|
+
# aegra-api
|
|
44
|
+
|
|
45
|
+
Aegra API - Self-hosted Agent Protocol server.
|
|
46
|
+
|
|
47
|
+
Aegra is an open-source, self-hosted alternative to LangGraph Platform. This package provides the core API server that implements the Agent Protocol, allowing you to run AI agents on your own infrastructure without vendor lock-in.
|
|
48
|
+
|
|
49
|
+
## Features
|
|
50
|
+
|
|
51
|
+
- **Agent Protocol Compliant**: Works with Agent Chat UI, LangGraph Studio, CopilotKit
|
|
52
|
+
- **Drop-in Replacement**: Compatible with the LangGraph SDK
|
|
53
|
+
- **Self-Hosted**: Run on your own PostgreSQL database
|
|
54
|
+
- **Streaming Support**: Real-time streaming of agent responses
|
|
55
|
+
- **Human-in-the-Loop**: Built-in support for human approval workflows
|
|
56
|
+
- **Vector Store**: Semantic search capabilities with PostgreSQL
|
|
57
|
+
|
|
58
|
+
## Installation
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
pip install aegra-api
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Quick Start
|
|
65
|
+
|
|
66
|
+
The easiest way to get started is with the [aegra-cli](../aegra-cli/README.md):
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Install the CLI
|
|
70
|
+
pip install aegra-cli
|
|
71
|
+
|
|
72
|
+
# Initialize a new project
|
|
73
|
+
aegra init --docker
|
|
74
|
+
|
|
75
|
+
# Start services
|
|
76
|
+
aegra up
|
|
77
|
+
|
|
78
|
+
# Apply migrations
|
|
79
|
+
aegra db upgrade
|
|
80
|
+
|
|
81
|
+
# Start development server
|
|
82
|
+
aegra dev
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Manual Setup
|
|
86
|
+
|
|
87
|
+
If you prefer manual setup:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
# Install dependencies
|
|
91
|
+
pip install aegra-api
|
|
92
|
+
|
|
93
|
+
# Set environment variables
|
|
94
|
+
export POSTGRES_USER=aegra
|
|
95
|
+
export POSTGRES_PASSWORD=aegra_secret
|
|
96
|
+
export POSTGRES_HOST=localhost
|
|
97
|
+
export POSTGRES_DB=aegra
|
|
98
|
+
|
|
99
|
+
# Run migrations
|
|
100
|
+
alembic upgrade head
|
|
101
|
+
|
|
102
|
+
# Start server
|
|
103
|
+
uvicorn aegra_api.main:app --reload
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Configuration
|
|
107
|
+
|
|
108
|
+
### aegra.json
|
|
109
|
+
|
|
110
|
+
Define your agent graphs in `aegra.json`:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"graphs": {
|
|
115
|
+
"agent": "./graphs/my_agent/graph.py:graph",
|
|
116
|
+
"assistant": "./graphs/assistant/graph.py:graph"
|
|
117
|
+
},
|
|
118
|
+
"http": {
|
|
119
|
+
"app": "./custom_routes.py:app"
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Environment Variables
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# Database
|
|
128
|
+
POSTGRES_USER=aegra
|
|
129
|
+
POSTGRES_PASSWORD=aegra_secret
|
|
130
|
+
POSTGRES_HOST=localhost
|
|
131
|
+
POSTGRES_DB=aegra
|
|
132
|
+
|
|
133
|
+
# Authentication
|
|
134
|
+
AUTH_TYPE=noop # Options: noop, custom
|
|
135
|
+
|
|
136
|
+
# Server
|
|
137
|
+
HOST=0.0.0.0
|
|
138
|
+
PORT=8000
|
|
139
|
+
|
|
140
|
+
# Configuration
|
|
141
|
+
AEGRA_CONFIG=aegra.json
|
|
142
|
+
|
|
143
|
+
# LLM (for example agents)
|
|
144
|
+
OPENAI_API_KEY=sk-...
|
|
145
|
+
|
|
146
|
+
# Observability (optional)
|
|
147
|
+
OTEL_TARGETS=LANGFUSE,PHOENIX
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## API Endpoints
|
|
151
|
+
|
|
152
|
+
| Endpoint | Method | Description |
|
|
153
|
+
|----------|--------|-------------|
|
|
154
|
+
| `/assistants` | POST | Create assistant from graph_id |
|
|
155
|
+
| `/assistants` | GET | List user's assistants |
|
|
156
|
+
| `/assistants/{id}` | GET | Get assistant details |
|
|
157
|
+
| `/threads` | POST | Create conversation thread |
|
|
158
|
+
| `/threads/{id}/state` | GET | Get thread state |
|
|
159
|
+
| `/threads/{id}/runs` | POST | Execute graph (streaming/background) |
|
|
160
|
+
| `/runs/{id}/stream` | POST | Stream run events |
|
|
161
|
+
| `/store` | PUT | Save to vector store |
|
|
162
|
+
| `/store/search` | POST | Semantic search |
|
|
163
|
+
| `/health` | GET | Health check |
|
|
164
|
+
|
|
165
|
+
## Creating Graphs
|
|
166
|
+
|
|
167
|
+
Agents are Python modules exporting a compiled `graph` variable:
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
# graphs/my_agent/graph.py
|
|
171
|
+
from typing import TypedDict
|
|
172
|
+
from langgraph.graph import StateGraph, START, END
|
|
173
|
+
|
|
174
|
+
class State(TypedDict):
|
|
175
|
+
messages: list[str]
|
|
176
|
+
|
|
177
|
+
def process_node(state: State) -> State:
|
|
178
|
+
messages = state.get("messages", [])
|
|
179
|
+
messages.append("Processed!")
|
|
180
|
+
return {"messages": messages}
|
|
181
|
+
|
|
182
|
+
# Build the graph
|
|
183
|
+
builder = StateGraph(State)
|
|
184
|
+
builder.add_node("process", process_node)
|
|
185
|
+
builder.add_edge(START, "process")
|
|
186
|
+
builder.add_edge("process", END)
|
|
187
|
+
|
|
188
|
+
# Export as 'graph'
|
|
189
|
+
graph = builder.compile()
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Architecture
|
|
193
|
+
|
|
194
|
+
```
|
|
195
|
+
+---------------------------------------------------------+
|
|
196
|
+
| FastAPI HTTP Layer (Agent Protocol API) |
|
|
197
|
+
| - /assistants, /threads, /runs, /store endpoints |
|
|
198
|
+
+---------------------------------------------------------+
|
|
199
|
+
| Middleware Stack |
|
|
200
|
+
| - Auth, CORS, Structured Logging, Correlation ID |
|
|
201
|
+
+---------------------------------------------------------+
|
|
202
|
+
| Service Layer (Business Logic) |
|
|
203
|
+
| - LangGraphService, AssistantService, StreamingService |
|
|
204
|
+
+---------------------------------------------------------+
|
|
205
|
+
| LangGraph Runtime |
|
|
206
|
+
| - Graph execution, state management, tool execution |
|
|
207
|
+
+---------------------------------------------------------+
|
|
208
|
+
| Database Layer (PostgreSQL) |
|
|
209
|
+
| - AsyncPostgresSaver (checkpoints), AsyncPostgresStore |
|
|
210
|
+
+---------------------------------------------------------+
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Package Structure
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
libs/aegra-api/
|
|
217
|
+
├── src/aegra_api/
|
|
218
|
+
│ ├── api/ # Agent Protocol endpoints
|
|
219
|
+
│ │ ├── assistants.py # /assistants CRUD
|
|
220
|
+
│ │ ├── threads.py # /threads and state management
|
|
221
|
+
│ │ ├── runs.py # /runs execution and streaming
|
|
222
|
+
│ │ └── store.py # /store vector storage
|
|
223
|
+
│ ├── services/ # Business logic layer
|
|
224
|
+
│ ├── core/ # Infrastructure (database, auth, orm)
|
|
225
|
+
│ ├── models/ # Pydantic request/response schemas
|
|
226
|
+
│ ├── middleware/ # ASGI middleware
|
|
227
|
+
│ ├── observability/ # OpenTelemetry tracing
|
|
228
|
+
│ ├── utils/ # Helper functions
|
|
229
|
+
│ ├── main.py # FastAPI app entry point
|
|
230
|
+
│ ├── config.py # HTTP/store config loading
|
|
231
|
+
│ └── settings.py # Environment settings
|
|
232
|
+
├── tests/ # Test suite
|
|
233
|
+
├── alembic/ # Database migrations
|
|
234
|
+
└── pyproject.toml
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Related Packages
|
|
238
|
+
|
|
239
|
+
- **aegra-cli**: Command-line interface for project management
|
|
240
|
+
- **aegra**: Meta-package that installs both aegra-api and aegra-cli
|
|
241
|
+
|
|
242
|
+
## Documentation
|
|
243
|
+
|
|
244
|
+
For full documentation, see the [CLAUDE.md](../../CLAUDE.md) file in the repository root.
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# aegra-api
|
|
2
|
+
|
|
3
|
+
Aegra API - Self-hosted Agent Protocol server.
|
|
4
|
+
|
|
5
|
+
Aegra is an open-source, self-hosted alternative to LangGraph Platform. This package provides the core API server that implements the Agent Protocol, allowing you to run AI agents on your own infrastructure without vendor lock-in.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Agent Protocol Compliant**: Works with Agent Chat UI, LangGraph Studio, CopilotKit
|
|
10
|
+
- **Drop-in Replacement**: Compatible with the LangGraph SDK
|
|
11
|
+
- **Self-Hosted**: Run on your own PostgreSQL database
|
|
12
|
+
- **Streaming Support**: Real-time streaming of agent responses
|
|
13
|
+
- **Human-in-the-Loop**: Built-in support for human approval workflows
|
|
14
|
+
- **Vector Store**: Semantic search capabilities with PostgreSQL
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pip install aegra-api
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
The easiest way to get started is with the [aegra-cli](../aegra-cli/README.md):
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Install the CLI
|
|
28
|
+
pip install aegra-cli
|
|
29
|
+
|
|
30
|
+
# Initialize a new project
|
|
31
|
+
aegra init --docker
|
|
32
|
+
|
|
33
|
+
# Start services
|
|
34
|
+
aegra up
|
|
35
|
+
|
|
36
|
+
# Apply migrations
|
|
37
|
+
aegra db upgrade
|
|
38
|
+
|
|
39
|
+
# Start development server
|
|
40
|
+
aegra dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Manual Setup
|
|
44
|
+
|
|
45
|
+
If you prefer manual setup:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Install dependencies
|
|
49
|
+
pip install aegra-api
|
|
50
|
+
|
|
51
|
+
# Set environment variables
|
|
52
|
+
export POSTGRES_USER=aegra
|
|
53
|
+
export POSTGRES_PASSWORD=aegra_secret
|
|
54
|
+
export POSTGRES_HOST=localhost
|
|
55
|
+
export POSTGRES_DB=aegra
|
|
56
|
+
|
|
57
|
+
# Run migrations
|
|
58
|
+
alembic upgrade head
|
|
59
|
+
|
|
60
|
+
# Start server
|
|
61
|
+
uvicorn aegra_api.main:app --reload
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
### aegra.json
|
|
67
|
+
|
|
68
|
+
Define your agent graphs in `aegra.json`:
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"graphs": {
|
|
73
|
+
"agent": "./graphs/my_agent/graph.py:graph",
|
|
74
|
+
"assistant": "./graphs/assistant/graph.py:graph"
|
|
75
|
+
},
|
|
76
|
+
"http": {
|
|
77
|
+
"app": "./custom_routes.py:app"
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Environment Variables
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Database
|
|
86
|
+
POSTGRES_USER=aegra
|
|
87
|
+
POSTGRES_PASSWORD=aegra_secret
|
|
88
|
+
POSTGRES_HOST=localhost
|
|
89
|
+
POSTGRES_DB=aegra
|
|
90
|
+
|
|
91
|
+
# Authentication
|
|
92
|
+
AUTH_TYPE=noop # Options: noop, custom
|
|
93
|
+
|
|
94
|
+
# Server
|
|
95
|
+
HOST=0.0.0.0
|
|
96
|
+
PORT=8000
|
|
97
|
+
|
|
98
|
+
# Configuration
|
|
99
|
+
AEGRA_CONFIG=aegra.json
|
|
100
|
+
|
|
101
|
+
# LLM (for example agents)
|
|
102
|
+
OPENAI_API_KEY=sk-...
|
|
103
|
+
|
|
104
|
+
# Observability (optional)
|
|
105
|
+
OTEL_TARGETS=LANGFUSE,PHOENIX
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## API Endpoints
|
|
109
|
+
|
|
110
|
+
| Endpoint | Method | Description |
|
|
111
|
+
|----------|--------|-------------|
|
|
112
|
+
| `/assistants` | POST | Create assistant from graph_id |
|
|
113
|
+
| `/assistants` | GET | List user's assistants |
|
|
114
|
+
| `/assistants/{id}` | GET | Get assistant details |
|
|
115
|
+
| `/threads` | POST | Create conversation thread |
|
|
116
|
+
| `/threads/{id}/state` | GET | Get thread state |
|
|
117
|
+
| `/threads/{id}/runs` | POST | Execute graph (streaming/background) |
|
|
118
|
+
| `/runs/{id}/stream` | POST | Stream run events |
|
|
119
|
+
| `/store` | PUT | Save to vector store |
|
|
120
|
+
| `/store/search` | POST | Semantic search |
|
|
121
|
+
| `/health` | GET | Health check |
|
|
122
|
+
|
|
123
|
+
## Creating Graphs
|
|
124
|
+
|
|
125
|
+
Agents are Python modules exporting a compiled `graph` variable:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
# graphs/my_agent/graph.py
|
|
129
|
+
from typing import TypedDict
|
|
130
|
+
from langgraph.graph import StateGraph, START, END
|
|
131
|
+
|
|
132
|
+
class State(TypedDict):
|
|
133
|
+
messages: list[str]
|
|
134
|
+
|
|
135
|
+
def process_node(state: State) -> State:
|
|
136
|
+
messages = state.get("messages", [])
|
|
137
|
+
messages.append("Processed!")
|
|
138
|
+
return {"messages": messages}
|
|
139
|
+
|
|
140
|
+
# Build the graph
|
|
141
|
+
builder = StateGraph(State)
|
|
142
|
+
builder.add_node("process", process_node)
|
|
143
|
+
builder.add_edge(START, "process")
|
|
144
|
+
builder.add_edge("process", END)
|
|
145
|
+
|
|
146
|
+
# Export as 'graph'
|
|
147
|
+
graph = builder.compile()
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## Architecture
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
+---------------------------------------------------------+
|
|
154
|
+
| FastAPI HTTP Layer (Agent Protocol API) |
|
|
155
|
+
| - /assistants, /threads, /runs, /store endpoints |
|
|
156
|
+
+---------------------------------------------------------+
|
|
157
|
+
| Middleware Stack |
|
|
158
|
+
| - Auth, CORS, Structured Logging, Correlation ID |
|
|
159
|
+
+---------------------------------------------------------+
|
|
160
|
+
| Service Layer (Business Logic) |
|
|
161
|
+
| - LangGraphService, AssistantService, StreamingService |
|
|
162
|
+
+---------------------------------------------------------+
|
|
163
|
+
| LangGraph Runtime |
|
|
164
|
+
| - Graph execution, state management, tool execution |
|
|
165
|
+
+---------------------------------------------------------+
|
|
166
|
+
| Database Layer (PostgreSQL) |
|
|
167
|
+
| - AsyncPostgresSaver (checkpoints), AsyncPostgresStore |
|
|
168
|
+
+---------------------------------------------------------+
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Package Structure
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
libs/aegra-api/
|
|
175
|
+
├── src/aegra_api/
|
|
176
|
+
│ ├── api/ # Agent Protocol endpoints
|
|
177
|
+
│ │ ├── assistants.py # /assistants CRUD
|
|
178
|
+
│ │ ├── threads.py # /threads and state management
|
|
179
|
+
│ │ ├── runs.py # /runs execution and streaming
|
|
180
|
+
│ │ └── store.py # /store vector storage
|
|
181
|
+
│ ├── services/ # Business logic layer
|
|
182
|
+
│ ├── core/ # Infrastructure (database, auth, orm)
|
|
183
|
+
│ ├── models/ # Pydantic request/response schemas
|
|
184
|
+
│ ├── middleware/ # ASGI middleware
|
|
185
|
+
│ ├── observability/ # OpenTelemetry tracing
|
|
186
|
+
│ ├── utils/ # Helper functions
|
|
187
|
+
│ ├── main.py # FastAPI app entry point
|
|
188
|
+
│ ├── config.py # HTTP/store config loading
|
|
189
|
+
│ └── settings.py # Environment settings
|
|
190
|
+
├── tests/ # Test suite
|
|
191
|
+
├── alembic/ # Database migrations
|
|
192
|
+
└── pyproject.toml
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Related Packages
|
|
196
|
+
|
|
197
|
+
- **aegra-cli**: Command-line interface for project management
|
|
198
|
+
- **aegra**: Meta-package that installs both aegra-api and aegra-cli
|
|
199
|
+
|
|
200
|
+
## Documentation
|
|
201
|
+
|
|
202
|
+
For full documentation, see the [CLAUDE.md](../../CLAUDE.md) file in the repository root.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Alembic environment configuration for Aegra database migrations."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from logging.config import fileConfig
|
|
5
|
+
|
|
6
|
+
from sqlalchemy import pool
|
|
7
|
+
from sqlalchemy.engine import Connection
|
|
8
|
+
from sqlalchemy.ext.asyncio import async_engine_from_config
|
|
9
|
+
|
|
10
|
+
# Import your SQLAlchemy models here
|
|
11
|
+
from aegra_api.core.orm import Base
|
|
12
|
+
from aegra_api.settings import settings
|
|
13
|
+
from alembic import context
|
|
14
|
+
|
|
15
|
+
# This is the Alembic Config object, which provides
|
|
16
|
+
# access to the values within the .ini file in use.
|
|
17
|
+
config = context.config
|
|
18
|
+
|
|
19
|
+
section = config.config_ini_section
|
|
20
|
+
config.set_section_option(section, "DB_HOST", settings.db.POSTGRES_HOST)
|
|
21
|
+
config.set_section_option(section, "DB_PORT", settings.db.POSTGRES_PORT)
|
|
22
|
+
config.set_section_option(section, "DB_USER", settings.db.POSTGRES_USER)
|
|
23
|
+
config.set_section_option(section, "DB_NAME", settings.db.POSTGRES_DB)
|
|
24
|
+
config.set_section_option(section, "DB_PASS", settings.db.POSTGRES_PASSWORD)
|
|
25
|
+
|
|
26
|
+
# Interpret the config file for Python logging.
|
|
27
|
+
# This line sets up loggers basically.
|
|
28
|
+
if config.config_file_name is not None:
|
|
29
|
+
fileConfig(config.config_file_name)
|
|
30
|
+
|
|
31
|
+
# add your model's MetaData object here
|
|
32
|
+
# for 'autogenerate' support
|
|
33
|
+
target_metadata = Base.metadata
|
|
34
|
+
|
|
35
|
+
# other values from the config, defined by the needs of env.py,
|
|
36
|
+
# can be acquired:
|
|
37
|
+
# my_important_option = config.get_main_option("my_important_option")
|
|
38
|
+
# ... etc.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def run_migrations_offline() -> None:
|
|
42
|
+
"""Run migrations in 'offline' mode.
|
|
43
|
+
|
|
44
|
+
This configures the context with just a URL
|
|
45
|
+
and not an Engine, though an Engine is acceptable
|
|
46
|
+
here as well. By skipping the Engine creation
|
|
47
|
+
we don't even need a DBAPI to be available.
|
|
48
|
+
|
|
49
|
+
Calls to context.execute() here emit the given string to the
|
|
50
|
+
script output.
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
context.configure(
|
|
54
|
+
url=config.get_main_option("sqlalchemy.url"),
|
|
55
|
+
target_metadata=target_metadata,
|
|
56
|
+
literal_binds=True,
|
|
57
|
+
dialect_opts={"paramstyle": "named"},
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
with context.begin_transaction():
|
|
61
|
+
context.run_migrations()
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def do_run_migrations(connection: Connection) -> None:
|
|
65
|
+
"""Run migrations with the given connection."""
|
|
66
|
+
context.configure(connection=connection, target_metadata=target_metadata)
|
|
67
|
+
|
|
68
|
+
with context.begin_transaction():
|
|
69
|
+
context.run_migrations()
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def run_async_migrations() -> None:
|
|
73
|
+
"""In this scenario we need to create an Engine
|
|
74
|
+
and associate a connection with the context.
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
configuration = config.get_section(config.config_ini_section)
|
|
78
|
+
configuration["sqlalchemy.url"] = config.get_main_option("sqlalchemy.url")
|
|
79
|
+
|
|
80
|
+
connectable = async_engine_from_config(
|
|
81
|
+
configuration,
|
|
82
|
+
prefix="sqlalchemy.",
|
|
83
|
+
poolclass=pool.NullPool,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
async with connectable.connect() as connection:
|
|
87
|
+
await connection.run_sync(do_run_migrations)
|
|
88
|
+
|
|
89
|
+
await connectable.dispose()
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def run_migrations_online() -> None:
|
|
93
|
+
"""Run migrations in 'online' mode."""
|
|
94
|
+
asyncio.run(run_async_migrations())
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
if context.is_offline_mode():
|
|
98
|
+
run_migrations_offline()
|
|
99
|
+
else:
|
|
100
|
+
run_migrations_online()
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""${message}
|
|
2
|
+
|
|
3
|
+
Revision ID: ${up_revision}
|
|
4
|
+
Revises: ${down_revision | comma,n}
|
|
5
|
+
Create Date: ${create_date}
|
|
6
|
+
|
|
7
|
+
"""
|
|
8
|
+
from alembic import op
|
|
9
|
+
import sqlalchemy as sa
|
|
10
|
+
${imports if imports else ""}
|
|
11
|
+
|
|
12
|
+
# revision identifiers, used by Alembic.
|
|
13
|
+
revision = ${repr(up_revision)}
|
|
14
|
+
down_revision = ${repr(down_revision)}
|
|
15
|
+
branch_labels = ${repr(branch_labels)}
|
|
16
|
+
depends_on = ${repr(depends_on)}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade() -> None:
|
|
20
|
+
${upgrades if upgrades else "pass"}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def downgrade() -> None:
|
|
24
|
+
${downgrades if downgrades else "pass"}
|