solace-agent-mesh 1.6.3__py3-none-any.whl → 1.7.0__py3-none-any.whl
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.
Potentially problematic release.
This version of solace-agent-mesh might be problematic. Click here for more details.
- solace_agent_mesh/agent/adk/adk_llm.txt +12 -18
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +1 -1
- solace_agent_mesh/agent/adk/callbacks.py +138 -20
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +2 -0
- solace_agent_mesh/agent/adk/models/lite_llm.py +38 -5
- solace_agent_mesh/agent/adk/models/models_llm.txt +82 -35
- solace_agent_mesh/agent/adk/runner.py +9 -0
- solace_agent_mesh/agent/adk/stream_parser.py +6 -1
- solace_agent_mesh/agent/adk/tool_wrapper.py +3 -0
- solace_agent_mesh/agent/agent_llm.txt +61 -70
- solace_agent_mesh/agent/protocol/event_handlers.py +29 -1
- solace_agent_mesh/agent/protocol/protocol_llm.txt +1 -1
- solace_agent_mesh/agent/proxies/a2a/a2a_llm.txt +190 -0
- solace_agent_mesh/agent/proxies/base/base_llm.txt +148 -0
- solace_agent_mesh/agent/proxies/proxies_llm.txt +283 -0
- solace_agent_mesh/agent/sac/app.py +22 -0
- solace_agent_mesh/agent/sac/component.py +76 -40
- solace_agent_mesh/agent/sac/sac_llm.txt +1 -1
- solace_agent_mesh/agent/sac/task_execution_context.py +21 -0
- solace_agent_mesh/agent/testing/testing_llm.txt +2 -1
- solace_agent_mesh/agent/tools/builtin_artifact_tools.py +13 -148
- solace_agent_mesh/agent/tools/dynamic_tool.py +2 -0
- solace_agent_mesh/agent/tools/tools_llm.txt +93 -80
- solace_agent_mesh/agent/tools/tools_llm_detail.txt +3 -2
- solace_agent_mesh/agent/utils/artifact_helpers.py +4 -0
- solace_agent_mesh/agent/utils/utils_llm.txt +16 -2
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/05749d90.c70b2be9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.92fea363.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15e40e79.36003774.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.e4870a49.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.b63ee53a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/547e15cc.2f7790c1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.45b32c2b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/64195356.c498c4d0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8024126c.fa0e7186.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/94e8668d.09ed9234.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{ab9708a8.3e6dd091.js → ab9708a8.245ae0ef.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.f902fad8.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/db5d6442.3daf1696.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/dd817ffc.c37a755e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/dd81e2b8.b682e9c2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/de915948.44a432bc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e04b235d.c9c50c7b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.d11c67a7.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{e6f9706b.e74a984d.js → e6f9706b.045d0fa1.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/e92d0134.3bda61dd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.5099c51e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.e6488e8b.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.d9606d6a.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +18 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/components/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +196 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +6 -7
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes-deployment/index.html +47 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +160 -169
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +59 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +62 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +10 -6
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/secure-user-delegated-access/index.html +440 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +27 -4
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/wheel-installation/index.html +62 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +5 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +290 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +9 -9
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/session-storage/index.html +251 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/user-feedback/index.html +88 -0
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-gateway-upgrade-to-0.3.0/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +3 -3
- solace_agent_mesh/assets/docs/lunr-index-1762189824009.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1762189824009.json +1 -0
- solace_agent_mesh/assets/docs/search-doc.json +1 -1
- solace_agent_mesh/assets/docs/sitemap.xml +1 -1
- solace_agent_mesh/cli/__init__.py +1 -1
- solace_agent_mesh/cli/commands/docs_cmd.py +4 -1
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-D4_RMYRh.js → authCallback-tcIFZLis.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-UZ3qU6Bq.js → client-CRYdKo2Q.js} +3 -3
- solace_agent_mesh/client/webui/frontend/static/assets/main-CojeY_1w.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-ILja9MCG.js +353 -0
- solace_agent_mesh/client/webui/frontend/static/assets/vendor-CINwxvwV.js +470 -0
- solace_agent_mesh/client/webui/frontend/static/auth-callback.html +3 -3
- solace_agent_mesh/client/webui/frontend/static/index.html +4 -4
- solace_agent_mesh/common/a2a/a2a_llm.txt +13 -20
- solace_agent_mesh/common/a2a/protocol.py +5 -0
- solace_agent_mesh/common/a2a/types.py +1 -0
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +49 -11
- solace_agent_mesh/common/a2a_spec/schemas/artifact_creation_progress.json +23 -6
- solace_agent_mesh/common/a2a_spec/schemas/feedback_event.json +51 -0
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +26 -9
- solace_agent_mesh/common/common_llm.txt +13 -34
- solace_agent_mesh/common/data_parts.py +20 -4
- solace_agent_mesh/common/middleware/middleware_llm.txt +1 -1
- solace_agent_mesh/common/sac/sac_llm.txt +1 -1
- solace_agent_mesh/common/sam_events/sam_events_llm.txt +1 -1
- solace_agent_mesh/common/services/employee_service.py +1 -1
- solace_agent_mesh/common/services/providers/providers_llm.txt +3 -2
- solace_agent_mesh/common/services/services_llm.txt +9 -4
- solace_agent_mesh/common/utils/embeds/constants.py +1 -0
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +1 -1
- solace_agent_mesh/common/utils/embeds/modifiers.py +2 -1
- solace_agent_mesh/common/utils/embeds/resolver.py +58 -6
- solace_agent_mesh/common/utils/embeds/types.py +8 -0
- solace_agent_mesh/common/utils/utils_llm.txt +5 -6
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +1 -1
- solace_agent_mesh/gateway/adapter/__init__.py +1 -0
- solace_agent_mesh/gateway/adapter/base.py +143 -0
- solace_agent_mesh/gateway/adapter/types.py +221 -0
- solace_agent_mesh/gateway/base/app.py +29 -2
- solace_agent_mesh/gateway/base/base_llm.txt +10 -8
- solace_agent_mesh/gateway/base/component.py +573 -142
- solace_agent_mesh/gateway/gateway_llm.txt +55 -59
- solace_agent_mesh/gateway/generic/__init__.py +1 -0
- solace_agent_mesh/gateway/generic/app.py +50 -0
- solace_agent_mesh/gateway/generic/component.py +650 -0
- solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +99 -49
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_fulltext_search_indexes.py +92 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_project_users_table.py +72 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251023_add_soft_delete_and_search.py +150 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_default_agent_to_projects.py +26 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251024_add_projects_table.py +135 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +26 -20
- solace_agent_mesh/gateway/http_sse/app.py +0 -14
- solace_agent_mesh/gateway/http_sse/component.py +17 -56
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +1 -1
- solace_agent_mesh/gateway/http_sse/dependencies.py +21 -3
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +8 -8
- solace_agent_mesh/gateway/http_sse/main.py +23 -5
- solace_agent_mesh/gateway/http_sse/repository/__init__.py +19 -1
- solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +56 -98
- solace_agent_mesh/gateway/http_sse/repository/entities/project.py +81 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/project_user.py +47 -0
- solace_agent_mesh/gateway/http_sse/repository/entities/session.py +23 -1
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +47 -0
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +112 -4
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +9 -1
- solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +51 -60
- solace_agent_mesh/gateway/http_sse/repository/models/project_model.py +51 -0
- solace_agent_mesh/gateway/http_sse/repository/models/project_user_model.py +75 -0
- solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +7 -1
- solace_agent_mesh/gateway/http_sse/repository/project_repository.py +172 -0
- solace_agent_mesh/gateway/http_sse/repository/project_user_repository.py +186 -0
- solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +125 -157
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +269 -8
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +143 -51
- solace_agent_mesh/gateway/http_sse/routers/config.py +69 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +198 -94
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +68 -18
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +13 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +30 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +51 -35
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +2 -0
- solace_agent_mesh/gateway/http_sse/routers/feedback.py +133 -2
- solace_agent_mesh/gateway/http_sse/routers/projects.py +542 -0
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +9 -11
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +154 -3
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +296 -4
- solace_agent_mesh/gateway/http_sse/services/project_service.py +403 -0
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +16 -10
- solace_agent_mesh/gateway/http_sse/services/session_service.py +178 -6
- solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +2 -3
- solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +48 -14
- solace_agent_mesh/solace_agent_mesh_llm.txt +1 -1
- {solace_agent_mesh-1.6.3.dist-info → solace_agent_mesh-1.7.0.dist-info}/METADATA +3 -5
- {solace_agent_mesh-1.6.3.dist-info → solace_agent_mesh-1.7.0.dist-info}/RECORD +218 -175
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.932dd2db.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.76654dd9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.2be20244.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/547e15cc.2cbb060a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/631738c7.7c4594c9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6a520c9d.ba015d81.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/71da7b71.ddbdfbe2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/8024126c.56e59919.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/94e8668d.3b883666.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/da0b5bad.d08a9466.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/dd817ffc.0aa9630a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/dd81e2b8.d590bc9e.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.6b9493d0.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e92d0134.4f395c6b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.720d2ef2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.ed05b14d.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.a8a75e0b.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1761744323675.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1761744323675.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main--3yJYl7S.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-DojKHS49.js +0 -342
- solace_agent_mesh/client/webui/frontend/static/assets/vendor-DSqhjwq_.js +0 -405
- /solace_agent_mesh/assets/docs/assets/js/{main.ed05b14d.js.LICENSE.txt → main.e6488e8b.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.6.3.dist-info → solace_agent_mesh-1.7.0.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.6.3.dist-info → solace_agent_mesh-1.7.0.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.6.3.dist-info → solace_agent_mesh-1.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
## Quick Summary
|
|
2
|
-
This directory contains SQLAlchemy ORM models and Pydantic schemas for database persistence in the HTTP SSE gateway. It provides models for managing chat sessions,
|
|
2
|
+
This directory contains SQLAlchemy ORM models and Pydantic schemas for database persistence in the HTTP SSE gateway. It provides models for managing chat sessions, tasks, task events, and user feedback with proper relationships and database schema definitions.
|
|
3
3
|
|
|
4
4
|
## Files Overview
|
|
5
5
|
- `__init__.py` - Package initialization exposing all SQLAlchemy and Pydantic models
|
|
6
6
|
- `base.py` - SQLAlchemy declarative base configuration
|
|
7
|
+
- `chat_task_model.py` - ChatTaskModel for storing chat tasks with session relationships
|
|
7
8
|
- `feedback_model.py` - FeedbackModel for storing user feedback on tasks
|
|
8
|
-
- `message_model.py` - MessageModel and Pydantic schemas for chat messages with session relationships
|
|
9
9
|
- `session_model.py` - SessionModel and Pydantic schemas for managing chat sessions
|
|
10
10
|
- `task_event_model.py` - TaskEventModel for storing A2A task events with task relationships
|
|
11
11
|
- `task_model.py` - TaskModel for managing tasks with event relationships and token usage tracking
|
|
@@ -14,7 +14,7 @@ This directory contains SQLAlchemy ORM models and Pydantic schemas for database
|
|
|
14
14
|
|
|
15
15
|
### __init__.py
|
|
16
16
|
**Purpose:** Package entry point that exposes all SQLAlchemy models and Pydantic schemas
|
|
17
|
-
**Import:** `from solace_agent_mesh.gateway.http_sse.repository.models import Base,
|
|
17
|
+
**Import:** `from solace_agent_mesh.gateway.http_sse.repository.models import Base, ChatTaskModel, FeedbackModel, SessionModel, CreateSessionModel, UpdateSessionModel, TaskEventModel, TaskModel`
|
|
18
18
|
|
|
19
19
|
**Constants/Variables:**
|
|
20
20
|
- `__all__: List[str]` - Public API exports including all models and schemas
|
|
@@ -36,6 +36,49 @@ engine = create_engine("sqlite:///example.db")
|
|
|
36
36
|
Base.metadata.create_all(engine)
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
### chat_task_model.py
|
|
40
|
+
**Purpose:** SQLAlchemy model for storing chat tasks with session relationships
|
|
41
|
+
**Import:** `from solace_agent_mesh.gateway.http_sse.repository.models.chat_task_model import ChatTaskModel`
|
|
42
|
+
|
|
43
|
+
**Classes:**
|
|
44
|
+
- `ChatTaskModel(Base)` - SQLAlchemy model for chat tasks
|
|
45
|
+
- `id: Column[String]` - Primary key task identifier
|
|
46
|
+
- `session_id: Column[String]` - Foreign key to sessions table with CASCADE delete (indexed)
|
|
47
|
+
- `user_id: Column[String]` - User identifier (indexed)
|
|
48
|
+
- `user_message: Column[Text]` - Optional user message content
|
|
49
|
+
- `message_bubbles: Column[Text]` - Required message bubbles data
|
|
50
|
+
- `task_metadata: Column[Text]` - Optional task metadata
|
|
51
|
+
- `created_time: Column[BigInteger]` - Creation timestamp in epoch milliseconds (indexed)
|
|
52
|
+
- `updated_time: Column[BigInteger]` - Optional update timestamp
|
|
53
|
+
- `session: relationship` - SQLAlchemy relationship to SessionModel
|
|
54
|
+
|
|
55
|
+
**Usage Examples:**
|
|
56
|
+
```python
|
|
57
|
+
from solace_agent_mesh.gateway.http_sse.repository.models.chat_task_model import ChatTaskModel
|
|
58
|
+
from sqlalchemy.orm import sessionmaker
|
|
59
|
+
|
|
60
|
+
# Create a chat task
|
|
61
|
+
chat_task = ChatTaskModel(
|
|
62
|
+
id="task_123",
|
|
63
|
+
session_id="session_456",
|
|
64
|
+
user_id="user_789",
|
|
65
|
+
user_message="Hello, how can you help me?",
|
|
66
|
+
message_bubbles='[{"type": "user", "content": "Hello"}]',
|
|
67
|
+
task_metadata='{"priority": "high"}',
|
|
68
|
+
created_time=1640995200000,
|
|
69
|
+
updated_time=1640995260000
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Add to database
|
|
73
|
+
Session = sessionmaker(bind=engine)
|
|
74
|
+
db_session = Session()
|
|
75
|
+
db_session.add(chat_task)
|
|
76
|
+
db_session.commit()
|
|
77
|
+
|
|
78
|
+
# Access related session
|
|
79
|
+
session = chat_task.session
|
|
80
|
+
```
|
|
81
|
+
|
|
39
82
|
### feedback_model.py
|
|
40
83
|
**Purpose:** SQLAlchemy model for storing user feedback on tasks
|
|
41
84
|
**Import:** `from solace_agent_mesh.gateway.http_sse.repository.models.feedback_model import FeedbackModel`
|
|
@@ -73,60 +116,8 @@ db_session.add(feedback)
|
|
|
73
116
|
db_session.commit()
|
|
74
117
|
```
|
|
75
118
|
|
|
76
|
-
### message_model.py
|
|
77
|
-
**Purpose:** SQLAlchemy model and Pydantic schemas for storing chat messages with session relationships
|
|
78
|
-
**Import:** `from solace_agent_mesh.gateway.http_sse.repository.models.message_model import MessageModel, CreateMessageModel, UpdateMessageModel`
|
|
79
|
-
|
|
80
|
-
**Classes:**
|
|
81
|
-
- `MessageModel(Base)` - SQLAlchemy model for chat messages
|
|
82
|
-
- `id: Column[String]` - Primary key message identifier
|
|
83
|
-
- `session_id: Column[String]` - Foreign key to sessions table with CASCADE delete
|
|
84
|
-
- `message: Column[Text]` - Message content
|
|
85
|
-
- `created_time: Column[BigInteger]` - Creation timestamp (auto-generated)
|
|
86
|
-
- `sender_type: Column[String]` - Type of message sender (max 50 chars)
|
|
87
|
-
- `sender_name: Column[String]` - Name of message sender (max 255 chars)
|
|
88
|
-
- `session: relationship` - SQLAlchemy relationship to SessionModel
|
|
89
|
-
|
|
90
|
-
- `CreateMessageModel(BaseModel)` - Pydantic model for creating messages
|
|
91
|
-
- `id: str` - Message identifier
|
|
92
|
-
- `session_id: str` - Session identifier
|
|
93
|
-
- `message: str` - Message content
|
|
94
|
-
- `sender_type: str` - Sender type
|
|
95
|
-
- `sender_name: str` - Sender name
|
|
96
|
-
- `created_time: int` - Creation timestamp
|
|
97
|
-
|
|
98
|
-
- `UpdateMessageModel(BaseModel)` - Pydantic model for updating messages
|
|
99
|
-
- `message: str` - Updated message content
|
|
100
|
-
- `sender_type: str` - Updated sender type
|
|
101
|
-
- `sender_name: str` - Updated sender name
|
|
102
|
-
|
|
103
|
-
**Usage Examples:**
|
|
104
|
-
```python
|
|
105
|
-
from solace_agent_mesh.gateway.http_sse.repository.models.message_model import MessageModel, CreateMessageModel
|
|
106
|
-
from sqlalchemy.orm import sessionmaker
|
|
107
|
-
|
|
108
|
-
# Create using SQLAlchemy model
|
|
109
|
-
message = MessageModel(
|
|
110
|
-
id="msg_123",
|
|
111
|
-
session_id="session_456",
|
|
112
|
-
message="Hello, world!",
|
|
113
|
-
sender_type="user",
|
|
114
|
-
sender_name="John Doe"
|
|
115
|
-
)
|
|
116
|
-
|
|
117
|
-
# Create using Pydantic model
|
|
118
|
-
create_data = CreateMessageModel(
|
|
119
|
-
id="msg_124",
|
|
120
|
-
session_id="session_456",
|
|
121
|
-
message="How are you?",
|
|
122
|
-
sender_type="user",
|
|
123
|
-
sender_name="John Doe",
|
|
124
|
-
created_time=1640995200000
|
|
125
|
-
)
|
|
126
|
-
```
|
|
127
|
-
|
|
128
119
|
### session_model.py
|
|
129
|
-
**Purpose:** SQLAlchemy model and Pydantic schemas for managing chat sessions with
|
|
120
|
+
**Purpose:** SQLAlchemy model and Pydantic schemas for managing chat sessions with chat task relationships
|
|
130
121
|
**Import:** `from solace_agent_mesh.gateway.http_sse.repository.models.session_model import SessionModel, CreateSessionModel, UpdateSessionModel`
|
|
131
122
|
|
|
132
123
|
**Classes:**
|
|
@@ -137,7 +128,7 @@ create_data = CreateMessageModel(
|
|
|
137
128
|
- `agent_id: Column[String]` - Optional agent identifier
|
|
138
129
|
- `created_time: Column[BigInteger]` - Creation timestamp (auto-generated)
|
|
139
130
|
- `updated_time: Column[BigInteger]` - Last update timestamp (auto-updated)
|
|
140
|
-
- `
|
|
131
|
+
- `chat_tasks: relationship` - SQLAlchemy relationship to ChatTaskModel with cascade delete
|
|
141
132
|
|
|
142
133
|
- `CreateSessionModel(BaseModel)` - Pydantic model for creating sessions
|
|
143
134
|
- `id: str` - Session identifier
|
|
@@ -175,8 +166,8 @@ create_data = CreateSessionModel(
|
|
|
175
166
|
updated_time=1640995200000
|
|
176
167
|
)
|
|
177
168
|
|
|
178
|
-
# Access related
|
|
179
|
-
|
|
169
|
+
# Access related chat tasks
|
|
170
|
+
chat_tasks = session.chat_tasks # Returns list of ChatTaskModel instances
|
|
180
171
|
```
|
|
181
172
|
|
|
182
173
|
### task_event_model.py
|
|
@@ -263,4 +254,4 @@ db_session.commit()
|
|
|
263
254
|
events = task.events # Returns list of TaskEventModel instances
|
|
264
255
|
```
|
|
265
256
|
|
|
266
|
-
# content_hash:
|
|
257
|
+
# content_hash: 65d43bc49dd561efb9955aae2fc39945567e4302c406eea9718ce797d083646c
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SQLAlchemy model for project data.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from sqlalchemy import Column, String, Boolean, BigInteger, Text
|
|
6
|
+
from sqlalchemy.orm import relationship
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
from .base import Base
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ProjectModel(Base):
|
|
13
|
+
"""SQLAlchemy model for projects."""
|
|
14
|
+
|
|
15
|
+
__tablename__ = "projects"
|
|
16
|
+
|
|
17
|
+
id = Column(String, primary_key=True)
|
|
18
|
+
name = Column(String, nullable=False)
|
|
19
|
+
user_id = Column(String, nullable=False)
|
|
20
|
+
description = Column(Text, nullable=True)
|
|
21
|
+
system_prompt = Column(Text, nullable=True)
|
|
22
|
+
default_agent_id = Column(String, nullable=True)
|
|
23
|
+
created_at = Column(BigInteger, nullable=False)
|
|
24
|
+
updated_at = Column(BigInteger, nullable=True)
|
|
25
|
+
deleted_at = Column(BigInteger, nullable=True)
|
|
26
|
+
deleted_by = Column(String, nullable=True)
|
|
27
|
+
|
|
28
|
+
# Relationships
|
|
29
|
+
sessions = relationship("SessionModel", back_populates="project")
|
|
30
|
+
project_users = relationship("ProjectUserModel", back_populates="project", cascade="all, delete-orphan")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class CreateProjectModel(BaseModel):
|
|
34
|
+
"""Pydantic model for creating a project."""
|
|
35
|
+
id: str
|
|
36
|
+
name: str
|
|
37
|
+
user_id: str | None = None
|
|
38
|
+
description: str | None = None
|
|
39
|
+
system_prompt: str | None = None
|
|
40
|
+
default_agent_id: str | None = None
|
|
41
|
+
created_at: int
|
|
42
|
+
updated_at: int | None = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class UpdateProjectModel(BaseModel):
|
|
46
|
+
"""Pydantic model for updating a project."""
|
|
47
|
+
name: str | None = None
|
|
48
|
+
description: str | None = None
|
|
49
|
+
system_prompt: str | None = None
|
|
50
|
+
default_agent_id: str | None = None
|
|
51
|
+
updated_at: int
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SQLAlchemy model for project user access (junction table).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from sqlalchemy import Column, String, BigInteger, ForeignKey, UniqueConstraint, Enum as SQLEnum
|
|
7
|
+
from sqlalchemy.orm import relationship
|
|
8
|
+
from pydantic import BaseModel, field_validator
|
|
9
|
+
from typing import Literal
|
|
10
|
+
|
|
11
|
+
from .base import Base
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ProjectRole(str, Enum):
|
|
15
|
+
"""Valid roles for project users."""
|
|
16
|
+
OWNER = "owner"
|
|
17
|
+
EDITOR = "editor"
|
|
18
|
+
VIEWER = "viewer"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ProjectUserModel(Base):
|
|
22
|
+
"""
|
|
23
|
+
SQLAlchemy model for project user access.
|
|
24
|
+
|
|
25
|
+
This junction table tracks which users have access to which projects,
|
|
26
|
+
enabling multi-user collaboration on projects.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
__tablename__ = "project_users"
|
|
30
|
+
|
|
31
|
+
id = Column(String, primary_key=True)
|
|
32
|
+
project_id = Column(String, ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
|
33
|
+
user_id = Column(String, nullable=False)
|
|
34
|
+
role = Column(SQLEnum(ProjectRole), nullable=False, default=ProjectRole.VIEWER)
|
|
35
|
+
added_at = Column(BigInteger, nullable=False) # Epoch timestamp in milliseconds
|
|
36
|
+
added_by_user_id = Column(String, nullable=False) # User who granted access
|
|
37
|
+
|
|
38
|
+
# Ensure a user can only be added once per project
|
|
39
|
+
__table_args__ = (
|
|
40
|
+
UniqueConstraint('project_id', 'user_id', name='uq_project_user'),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# Relationships
|
|
44
|
+
project = relationship("ProjectModel", back_populates="project_users")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class CreateProjectUserModel(BaseModel):
|
|
48
|
+
"""Pydantic model for creating a project user access record."""
|
|
49
|
+
id: str
|
|
50
|
+
project_id: str
|
|
51
|
+
user_id: str
|
|
52
|
+
role: Literal["owner", "editor", "viewer"] = "viewer"
|
|
53
|
+
added_at: int
|
|
54
|
+
added_by_user_id: str
|
|
55
|
+
|
|
56
|
+
@field_validator('role')
|
|
57
|
+
@classmethod
|
|
58
|
+
def validate_role(cls, v: str) -> str:
|
|
59
|
+
"""Validate that role is one of the allowed values."""
|
|
60
|
+
if v not in [role.value for role in ProjectRole]:
|
|
61
|
+
raise ValueError(f"Role must be one of: {', '.join([role.value for role in ProjectRole])}")
|
|
62
|
+
return v
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class UpdateProjectUserModel(BaseModel):
|
|
66
|
+
"""Pydantic model for updating a project user access record."""
|
|
67
|
+
role: Literal["owner", "editor", "viewer"] | None = None
|
|
68
|
+
|
|
69
|
+
@field_validator('role')
|
|
70
|
+
@classmethod
|
|
71
|
+
def validate_role(cls, v: str | None) -> str | None:
|
|
72
|
+
"""Validate that role is one of the allowed values."""
|
|
73
|
+
if v is not None and v not in [role.value for role in ProjectRole]:
|
|
74
|
+
raise ValueError(f"Role must be one of: {', '.join([role.value for role in ProjectRole])}")
|
|
75
|
+
return v
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
Session SQLAlchemy model and Pydantic models for strongly-typed operations.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from sqlalchemy import BigInteger, Column, String, ForeignKey
|
|
5
6
|
from pydantic import BaseModel
|
|
6
|
-
from sqlalchemy import BigInteger, Column, String
|
|
7
7
|
from sqlalchemy.orm import relationship
|
|
8
8
|
|
|
9
9
|
from ...shared import now_epoch_ms
|
|
@@ -19,15 +19,19 @@ class SessionModel(Base):
|
|
|
19
19
|
name = Column(String, nullable=True)
|
|
20
20
|
user_id = Column(String, nullable=False)
|
|
21
21
|
agent_id = Column(String, nullable=True)
|
|
22
|
+
project_id = Column(String, ForeignKey("projects.id"), nullable=True)
|
|
22
23
|
created_time = Column(BigInteger, nullable=False, default=now_epoch_ms)
|
|
23
24
|
updated_time = Column(
|
|
24
25
|
BigInteger, nullable=False, default=now_epoch_ms, onupdate=now_epoch_ms
|
|
25
26
|
)
|
|
27
|
+
deleted_at = Column(BigInteger, nullable=True)
|
|
28
|
+
deleted_by = Column(String, nullable=True)
|
|
26
29
|
|
|
27
30
|
# Relationship to chat tasks
|
|
28
31
|
chat_tasks = relationship(
|
|
29
32
|
"ChatTaskModel", back_populates="session", cascade="all, delete-orphan"
|
|
30
33
|
)
|
|
34
|
+
project = relationship("ProjectModel", back_populates="sessions")
|
|
31
35
|
|
|
32
36
|
|
|
33
37
|
class CreateSessionModel(BaseModel):
|
|
@@ -36,6 +40,7 @@ class CreateSessionModel(BaseModel):
|
|
|
36
40
|
name: str | None
|
|
37
41
|
user_id: str
|
|
38
42
|
agent_id: str | None
|
|
43
|
+
project_id: str | None = None
|
|
39
44
|
created_time: int
|
|
40
45
|
updated_time: int
|
|
41
46
|
|
|
@@ -44,4 +49,5 @@ class UpdateSessionModel(BaseModel):
|
|
|
44
49
|
"""Pydantic model for updating a session."""
|
|
45
50
|
name: str | None = None
|
|
46
51
|
agent_id: str | None = None
|
|
52
|
+
project_id: str | None = None
|
|
47
53
|
updated_time: int
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Repository implementation for project data access operations.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
from sqlalchemy.orm import Session as DBSession
|
|
8
|
+
from sqlalchemy import or_
|
|
9
|
+
|
|
10
|
+
from .interfaces import IProjectRepository
|
|
11
|
+
from .models import ProjectModel, ProjectUserModel
|
|
12
|
+
from .entities.project import Project
|
|
13
|
+
from ..routers.dto.requests.project_requests import ProjectFilter
|
|
14
|
+
from ..shared import now_epoch_ms
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ProjectRepository(IProjectRepository):
|
|
18
|
+
"""SQLAlchemy implementation of project repository."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, db: DBSession):
|
|
21
|
+
self.db = db
|
|
22
|
+
|
|
23
|
+
def create_project(self, name: str, user_id: str, description: Optional[str] = None,
|
|
24
|
+
system_prompt: Optional[str] = None, default_agent_id: Optional[str] = None) -> Project:
|
|
25
|
+
"""Create a new user project."""
|
|
26
|
+
model = ProjectModel(
|
|
27
|
+
id=str(uuid.uuid4()),
|
|
28
|
+
name=name,
|
|
29
|
+
user_id=user_id,
|
|
30
|
+
description=description,
|
|
31
|
+
system_prompt=system_prompt,
|
|
32
|
+
default_agent_id=default_agent_id,
|
|
33
|
+
created_at=now_epoch_ms(),
|
|
34
|
+
)
|
|
35
|
+
self.db.add(model)
|
|
36
|
+
self.db.flush()
|
|
37
|
+
self.db.refresh(model)
|
|
38
|
+
return self._model_to_entity(model)
|
|
39
|
+
|
|
40
|
+
def get_user_projects(self, user_id: str) -> List[Project]:
|
|
41
|
+
"""
|
|
42
|
+
Get all projects owned by a specific user.
|
|
43
|
+
|
|
44
|
+
Note: This returns only projects where the user is the owner (user_id matches).
|
|
45
|
+
For projects the user has access to via project_users table, use get_accessible_projects().
|
|
46
|
+
"""
|
|
47
|
+
models = self.db.query(ProjectModel).filter(
|
|
48
|
+
ProjectModel.user_id == user_id,
|
|
49
|
+
ProjectModel.deleted_at.is_(None) # Exclude soft-deleted projects
|
|
50
|
+
).all()
|
|
51
|
+
return [self._model_to_entity(model) for model in models]
|
|
52
|
+
|
|
53
|
+
def get_accessible_projects(self, user_id: str) -> List[Project]:
|
|
54
|
+
"""
|
|
55
|
+
Get all projects accessible by a user (owned or shared).
|
|
56
|
+
|
|
57
|
+
This includes:
|
|
58
|
+
- Projects owned by the user (user_id matches)
|
|
59
|
+
- Projects shared with the user (via project_users table)
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
user_id: The user ID
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
List[Project]: List of accessible projects
|
|
66
|
+
"""
|
|
67
|
+
# Query for projects where user is owner OR has access via project_users
|
|
68
|
+
models = self.db.query(ProjectModel).outerjoin(
|
|
69
|
+
ProjectUserModel,
|
|
70
|
+
ProjectModel.id == ProjectUserModel.project_id
|
|
71
|
+
).filter(
|
|
72
|
+
ProjectModel.deleted_at.is_(None), # Exclude soft-deleted projects
|
|
73
|
+
or_(
|
|
74
|
+
ProjectModel.user_id == user_id,
|
|
75
|
+
ProjectUserModel.user_id == user_id
|
|
76
|
+
)
|
|
77
|
+
).distinct().all()
|
|
78
|
+
|
|
79
|
+
return [self._model_to_entity(model) for model in models]
|
|
80
|
+
|
|
81
|
+
def get_filtered_projects(self, project_filter: ProjectFilter) -> List[Project]:
|
|
82
|
+
"""Get projects based on filter criteria."""
|
|
83
|
+
query = self.db.query(ProjectModel).filter(
|
|
84
|
+
ProjectModel.deleted_at.is_(None) # Exclude soft-deleted projects
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if project_filter.user_id is not None:
|
|
88
|
+
query = query.filter(ProjectModel.user_id == project_filter.user_id)
|
|
89
|
+
|
|
90
|
+
models = query.all()
|
|
91
|
+
return [self._model_to_entity(model) for model in models]
|
|
92
|
+
|
|
93
|
+
def get_by_id(self, project_id: str, user_id: str) -> Optional[Project]:
|
|
94
|
+
"""
|
|
95
|
+
Get a project by its ID, ensuring user access.
|
|
96
|
+
|
|
97
|
+
This checks if the user is the owner OR has access via project_users table.
|
|
98
|
+
"""
|
|
99
|
+
model = self.db.query(ProjectModel).outerjoin(
|
|
100
|
+
ProjectUserModel,
|
|
101
|
+
ProjectModel.id == ProjectUserModel.project_id
|
|
102
|
+
).filter(
|
|
103
|
+
ProjectModel.id == project_id,
|
|
104
|
+
ProjectModel.deleted_at.is_(None), # Exclude soft-deleted projects
|
|
105
|
+
or_(
|
|
106
|
+
ProjectModel.user_id == user_id,
|
|
107
|
+
ProjectUserModel.user_id == user_id
|
|
108
|
+
)
|
|
109
|
+
).first()
|
|
110
|
+
|
|
111
|
+
return self._model_to_entity(model) if model else None
|
|
112
|
+
|
|
113
|
+
def update(self, project_id: str, user_id: str, update_data: dict) -> Optional[Project]:
|
|
114
|
+
"""Update a project with the given data, ensuring user access."""
|
|
115
|
+
model = self.db.query(ProjectModel).filter(
|
|
116
|
+
ProjectModel.id == project_id,
|
|
117
|
+
ProjectModel.user_id == user_id, # Only allow updates to user's own projects
|
|
118
|
+
ProjectModel.deleted_at.is_(None) # Exclude soft-deleted projects
|
|
119
|
+
).first()
|
|
120
|
+
|
|
121
|
+
if not model:
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
for field, value in update_data.items():
|
|
125
|
+
if hasattr(model, field):
|
|
126
|
+
setattr(model, field, value)
|
|
127
|
+
|
|
128
|
+
model.updated_at = now_epoch_ms()
|
|
129
|
+
self.db.flush()
|
|
130
|
+
self.db.refresh(model)
|
|
131
|
+
return self._model_to_entity(model)
|
|
132
|
+
|
|
133
|
+
def delete(self, project_id: str, user_id: str) -> bool:
|
|
134
|
+
"""Delete a project by its ID, ensuring user access."""
|
|
135
|
+
result = self.db.query(ProjectModel).filter(
|
|
136
|
+
ProjectModel.id == project_id,
|
|
137
|
+
ProjectModel.user_id == user_id # Only allow deletion of user's own projects
|
|
138
|
+
).delete()
|
|
139
|
+
self.db.flush()
|
|
140
|
+
return result > 0
|
|
141
|
+
|
|
142
|
+
def soft_delete(self, project_id: str, user_id: str) -> bool:
|
|
143
|
+
"""Soft delete a project by its ID, ensuring user access."""
|
|
144
|
+
model = self.db.query(ProjectModel).filter(
|
|
145
|
+
ProjectModel.id == project_id,
|
|
146
|
+
ProjectModel.user_id == user_id, # Only allow deletion of user's own projects
|
|
147
|
+
ProjectModel.deleted_at.is_(None) # Only delete if not already deleted
|
|
148
|
+
).first()
|
|
149
|
+
|
|
150
|
+
if not model:
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
model.deleted_at = now_epoch_ms()
|
|
154
|
+
model.deleted_by = user_id
|
|
155
|
+
model.updated_at = now_epoch_ms()
|
|
156
|
+
self.db.flush()
|
|
157
|
+
return True
|
|
158
|
+
|
|
159
|
+
def _model_to_entity(self, model: ProjectModel) -> Project:
|
|
160
|
+
"""Convert SQLAlchemy model to domain entity."""
|
|
161
|
+
return Project(
|
|
162
|
+
id=model.id,
|
|
163
|
+
name=model.name,
|
|
164
|
+
user_id=model.user_id,
|
|
165
|
+
description=model.description,
|
|
166
|
+
system_prompt=model.system_prompt,
|
|
167
|
+
default_agent_id=model.default_agent_id,
|
|
168
|
+
created_at=model.created_at,
|
|
169
|
+
updated_at=model.updated_at,
|
|
170
|
+
deleted_at=model.deleted_at,
|
|
171
|
+
deleted_by=model.deleted_by,
|
|
172
|
+
)
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Repository implementation for project user access data operations.
|
|
3
|
+
"""
|
|
4
|
+
from typing import List, Optional
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
from sqlalchemy.orm import Session as DBSession
|
|
8
|
+
|
|
9
|
+
from .models import ProjectUserModel
|
|
10
|
+
from .entities.project_user import ProjectUser
|
|
11
|
+
from ..shared import now_epoch_ms
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ProjectUserRepository:
|
|
15
|
+
"""SQLAlchemy implementation of project user repository."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, db: DBSession):
|
|
18
|
+
self.db = db
|
|
19
|
+
|
|
20
|
+
def add_user_to_project(
|
|
21
|
+
self,
|
|
22
|
+
project_id: str,
|
|
23
|
+
user_id: str,
|
|
24
|
+
role: str,
|
|
25
|
+
added_by_user_id: str
|
|
26
|
+
) -> ProjectUser:
|
|
27
|
+
"""
|
|
28
|
+
Add a user to a project with a specific role.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
project_id: The project ID
|
|
32
|
+
user_id: The user ID to add
|
|
33
|
+
role: The role to assign (owner, editor, viewer)
|
|
34
|
+
added_by_user_id: The user ID who is granting access
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
ProjectUser: The created project user access record
|
|
38
|
+
"""
|
|
39
|
+
model = ProjectUserModel(
|
|
40
|
+
id=str(uuid.uuid4()),
|
|
41
|
+
project_id=project_id,
|
|
42
|
+
user_id=user_id,
|
|
43
|
+
role=role,
|
|
44
|
+
added_at=now_epoch_ms(),
|
|
45
|
+
added_by_user_id=added_by_user_id,
|
|
46
|
+
)
|
|
47
|
+
self.db.add(model)
|
|
48
|
+
self.db.commit()
|
|
49
|
+
self.db.refresh(model)
|
|
50
|
+
return self._model_to_entity(model)
|
|
51
|
+
|
|
52
|
+
def get_project_users(self, project_id: str) -> List[ProjectUser]:
|
|
53
|
+
"""
|
|
54
|
+
Get all users who have access to a project.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
project_id: The project ID
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
List[ProjectUser]: List of users with access to the project
|
|
61
|
+
"""
|
|
62
|
+
models = self.db.query(ProjectUserModel).filter(
|
|
63
|
+
ProjectUserModel.project_id == project_id
|
|
64
|
+
).all()
|
|
65
|
+
return [self._model_to_entity(model) for model in models]
|
|
66
|
+
|
|
67
|
+
def get_user_projects_access(self, user_id: str) -> List[ProjectUser]:
|
|
68
|
+
"""
|
|
69
|
+
Get all projects a user has access to.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
user_id: The user ID
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
List[ProjectUser]: List of project access records for the user
|
|
76
|
+
"""
|
|
77
|
+
models = self.db.query(ProjectUserModel).filter(
|
|
78
|
+
ProjectUserModel.user_id == user_id
|
|
79
|
+
).all()
|
|
80
|
+
return [self._model_to_entity(model) for model in models]
|
|
81
|
+
|
|
82
|
+
def get_user_project_access(
|
|
83
|
+
self,
|
|
84
|
+
project_id: str,
|
|
85
|
+
user_id: str
|
|
86
|
+
) -> Optional[ProjectUser]:
|
|
87
|
+
"""
|
|
88
|
+
Get a specific user's access to a project.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
project_id: The project ID
|
|
92
|
+
user_id: The user ID
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Optional[ProjectUser]: The access record if found, None otherwise
|
|
96
|
+
"""
|
|
97
|
+
model = self.db.query(ProjectUserModel).filter(
|
|
98
|
+
ProjectUserModel.project_id == project_id,
|
|
99
|
+
ProjectUserModel.user_id == user_id
|
|
100
|
+
).first()
|
|
101
|
+
|
|
102
|
+
return self._model_to_entity(model) if model else None
|
|
103
|
+
|
|
104
|
+
def update_user_role(
|
|
105
|
+
self,
|
|
106
|
+
project_id: str,
|
|
107
|
+
user_id: str,
|
|
108
|
+
new_role: str
|
|
109
|
+
) -> Optional[ProjectUser]:
|
|
110
|
+
"""
|
|
111
|
+
Update a user's role for a project.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
project_id: The project ID
|
|
115
|
+
user_id: The user ID
|
|
116
|
+
new_role: The new role to assign
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Optional[ProjectUser]: The updated access record if found, None otherwise
|
|
120
|
+
"""
|
|
121
|
+
model = self.db.query(ProjectUserModel).filter(
|
|
122
|
+
ProjectUserModel.project_id == project_id,
|
|
123
|
+
ProjectUserModel.user_id == user_id
|
|
124
|
+
).first()
|
|
125
|
+
|
|
126
|
+
if not model:
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
model.role = new_role
|
|
130
|
+
self.db.commit()
|
|
131
|
+
self.db.refresh(model)
|
|
132
|
+
return self._model_to_entity(model)
|
|
133
|
+
|
|
134
|
+
def remove_user_from_project(
|
|
135
|
+
self,
|
|
136
|
+
project_id: str,
|
|
137
|
+
user_id: str
|
|
138
|
+
) -> bool:
|
|
139
|
+
"""
|
|
140
|
+
Remove a user's access to a project.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
project_id: The project ID
|
|
144
|
+
user_id: The user ID to remove
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
bool: True if removed successfully, False otherwise
|
|
148
|
+
"""
|
|
149
|
+
result = self.db.query(ProjectUserModel).filter(
|
|
150
|
+
ProjectUserModel.project_id == project_id,
|
|
151
|
+
ProjectUserModel.user_id == user_id
|
|
152
|
+
).delete()
|
|
153
|
+
self.db.commit()
|
|
154
|
+
return result > 0
|
|
155
|
+
|
|
156
|
+
def user_has_access(
|
|
157
|
+
self,
|
|
158
|
+
project_id: str,
|
|
159
|
+
user_id: str
|
|
160
|
+
) -> bool:
|
|
161
|
+
"""
|
|
162
|
+
Check if a user has access to a project.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
project_id: The project ID
|
|
166
|
+
user_id: The user ID
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
bool: True if user has access, False otherwise
|
|
170
|
+
"""
|
|
171
|
+
count = self.db.query(ProjectUserModel).filter(
|
|
172
|
+
ProjectUserModel.project_id == project_id,
|
|
173
|
+
ProjectUserModel.user_id == user_id
|
|
174
|
+
).count()
|
|
175
|
+
return count > 0
|
|
176
|
+
|
|
177
|
+
def _model_to_entity(self, model: ProjectUserModel) -> ProjectUser:
|
|
178
|
+
"""Convert SQLAlchemy model to domain entity."""
|
|
179
|
+
return ProjectUser(
|
|
180
|
+
id=model.id,
|
|
181
|
+
project_id=model.project_id,
|
|
182
|
+
user_id=model.user_id,
|
|
183
|
+
role=model.role,
|
|
184
|
+
added_at=model.added_at,
|
|
185
|
+
added_by_user_id=model.added_by_user_id,
|
|
186
|
+
)
|