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
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project API controller using 3-tiered architecture.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
from typing import List, Optional, Dict, Any
|
|
7
|
+
from fastapi import (
|
|
8
|
+
APIRouter,
|
|
9
|
+
Depends,
|
|
10
|
+
HTTPException,
|
|
11
|
+
status,
|
|
12
|
+
Request,
|
|
13
|
+
Form,
|
|
14
|
+
File,
|
|
15
|
+
UploadFile,
|
|
16
|
+
)
|
|
17
|
+
from sqlalchemy.orm import Session
|
|
18
|
+
from solace_ai_connector.common.log import log
|
|
19
|
+
|
|
20
|
+
from ..dependencies import get_project_service, get_sac_component, get_api_config, get_db
|
|
21
|
+
from ..services.project_service import ProjectService
|
|
22
|
+
from ..shared.auth_utils import get_current_user
|
|
23
|
+
from ....common.a2a.types import ArtifactInfo
|
|
24
|
+
from typing import TYPE_CHECKING
|
|
25
|
+
|
|
26
|
+
if TYPE_CHECKING:
|
|
27
|
+
from ..component import WebUIBackendComponent
|
|
28
|
+
|
|
29
|
+
from .dto.requests.project_requests import (
|
|
30
|
+
CreateProjectRequest,
|
|
31
|
+
UpdateProjectRequest,
|
|
32
|
+
GetProjectRequest,
|
|
33
|
+
GetProjectsRequest,
|
|
34
|
+
DeleteProjectRequest,
|
|
35
|
+
)
|
|
36
|
+
from .dto.responses.project_responses import (
|
|
37
|
+
ProjectResponse,
|
|
38
|
+
ProjectListResponse,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
router = APIRouter()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def check_projects_enabled(
|
|
45
|
+
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
46
|
+
api_config: Dict[str, Any] = Depends(get_api_config),
|
|
47
|
+
) -> None:
|
|
48
|
+
"""
|
|
49
|
+
Dependency to check if projects feature is enabled.
|
|
50
|
+
Raises HTTPException if projects are disabled.
|
|
51
|
+
"""
|
|
52
|
+
# Check if persistence is enabled (required for projects)
|
|
53
|
+
persistence_enabled = api_config.get("persistence_enabled", False)
|
|
54
|
+
if not persistence_enabled:
|
|
55
|
+
log.warning("Projects API called but persistence is not enabled")
|
|
56
|
+
raise HTTPException(
|
|
57
|
+
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
58
|
+
detail="Projects feature requires persistence to be enabled. Please configure session_service.type as 'sql'."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# Check explicit projects config
|
|
62
|
+
projects_config = component.get_config("projects", {})
|
|
63
|
+
if isinstance(projects_config, dict):
|
|
64
|
+
projects_explicitly_enabled = projects_config.get("enabled", True)
|
|
65
|
+
if not projects_explicitly_enabled:
|
|
66
|
+
log.warning("Projects API called but projects are explicitly disabled in config")
|
|
67
|
+
raise HTTPException(
|
|
68
|
+
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
69
|
+
detail="Projects feature is disabled. Please enable it in the configuration."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Check frontend_feature_enablement override
|
|
73
|
+
feature_flags = component.get_config("frontend_feature_enablement", {})
|
|
74
|
+
if "projects" in feature_flags:
|
|
75
|
+
projects_flag = feature_flags.get("projects", True)
|
|
76
|
+
if not projects_flag:
|
|
77
|
+
log.warning("Projects API called but projects are disabled via feature flag")
|
|
78
|
+
raise HTTPException(
|
|
79
|
+
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
80
|
+
detail="Projects feature is disabled via feature flag."
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@router.post("/projects", response_model=ProjectResponse, status_code=status.HTTP_201_CREATED)
|
|
85
|
+
async def create_project(
|
|
86
|
+
name: str = Form(...),
|
|
87
|
+
description: Optional[str] = Form(None),
|
|
88
|
+
system_prompt: Optional[str] = Form(None, alias="systemPrompt"),
|
|
89
|
+
default_agent_id: Optional[str] = Form(None, alias="defaultAgentId"),
|
|
90
|
+
file_metadata: Optional[str] = Form(None, alias="fileMetadata"),
|
|
91
|
+
files: Optional[List[UploadFile]] = File(None),
|
|
92
|
+
user: dict = Depends(get_current_user),
|
|
93
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
94
|
+
db: Session = Depends(get_db),
|
|
95
|
+
_: None = Depends(check_projects_enabled),
|
|
96
|
+
):
|
|
97
|
+
"""
|
|
98
|
+
Create a new project for the authenticated user.
|
|
99
|
+
"""
|
|
100
|
+
user_id = user.get("id")
|
|
101
|
+
log.info(f"Creating project '{name}' for user {user_id}")
|
|
102
|
+
log.info(f"Received system_prompt: {system_prompt}")
|
|
103
|
+
log.info(f"Received file_metadata: {file_metadata}")
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
if files:
|
|
107
|
+
log.info(f"Received {len(files)} files for project creation:")
|
|
108
|
+
for file in files:
|
|
109
|
+
log.info(f" - Filename: {file.filename}, Content-Type: {file.content_type}")
|
|
110
|
+
else:
|
|
111
|
+
log.info("No files received for project creation.")
|
|
112
|
+
|
|
113
|
+
request_dto = CreateProjectRequest(
|
|
114
|
+
name=name,
|
|
115
|
+
description=description,
|
|
116
|
+
system_prompt=system_prompt,
|
|
117
|
+
default_agent_id=default_agent_id,
|
|
118
|
+
file_metadata=file_metadata,
|
|
119
|
+
user_id=user_id
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
parsed_file_metadata = {}
|
|
123
|
+
if request_dto.file_metadata:
|
|
124
|
+
try:
|
|
125
|
+
parsed_file_metadata = json.loads(request_dto.file_metadata)
|
|
126
|
+
except json.JSONDecodeError:
|
|
127
|
+
log.warning("Could not parse file_metadata JSON string, ignoring.")
|
|
128
|
+
pass
|
|
129
|
+
|
|
130
|
+
project = await project_service.create_project(
|
|
131
|
+
db=db,
|
|
132
|
+
name=request_dto.name,
|
|
133
|
+
user_id=request_dto.user_id,
|
|
134
|
+
description=request_dto.description,
|
|
135
|
+
system_prompt=request_dto.system_prompt,
|
|
136
|
+
default_agent_id=request_dto.default_agent_id,
|
|
137
|
+
files=files,
|
|
138
|
+
file_metadata=parsed_file_metadata,
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return ProjectResponse(
|
|
142
|
+
id=project.id,
|
|
143
|
+
name=project.name,
|
|
144
|
+
user_id=project.user_id,
|
|
145
|
+
description=project.description,
|
|
146
|
+
system_prompt=project.system_prompt,
|
|
147
|
+
default_agent_id=project.default_agent_id,
|
|
148
|
+
created_at=project.created_at,
|
|
149
|
+
updated_at=project.updated_at,
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
except ValueError as e:
|
|
153
|
+
log.warning(f"Validation error creating project: {e}")
|
|
154
|
+
raise HTTPException(
|
|
155
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
156
|
+
detail=str(e)
|
|
157
|
+
)
|
|
158
|
+
except Exception as e:
|
|
159
|
+
log.error("Error creating project for user %s: %s", user_id, e)
|
|
160
|
+
raise HTTPException(
|
|
161
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
162
|
+
detail="Failed to create project"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@router.get("/projects", response_model=ProjectListResponse)
|
|
167
|
+
async def get_user_projects(
|
|
168
|
+
user: dict = Depends(get_current_user),
|
|
169
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
170
|
+
db: Session = Depends(get_db),
|
|
171
|
+
_: None = Depends(check_projects_enabled),
|
|
172
|
+
):
|
|
173
|
+
"""
|
|
174
|
+
Get all projects owned by the authenticated user.
|
|
175
|
+
"""
|
|
176
|
+
user_id = user.get("id")
|
|
177
|
+
log.info(f"Fetching projects for user_id: {user_id}")
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
request_dto = GetProjectsRequest(user_id=user_id)
|
|
181
|
+
|
|
182
|
+
projects = project_service.get_user_projects(db, request_dto.user_id)
|
|
183
|
+
|
|
184
|
+
project_responses = [
|
|
185
|
+
ProjectResponse(
|
|
186
|
+
id=p.id,
|
|
187
|
+
name=p.name,
|
|
188
|
+
user_id=p.user_id,
|
|
189
|
+
description=p.description,
|
|
190
|
+
system_prompt=p.system_prompt,
|
|
191
|
+
default_agent_id=p.default_agent_id,
|
|
192
|
+
created_at=p.created_at,
|
|
193
|
+
updated_at=p.updated_at,
|
|
194
|
+
)
|
|
195
|
+
for p in projects
|
|
196
|
+
]
|
|
197
|
+
|
|
198
|
+
return ProjectListResponse(
|
|
199
|
+
projects=project_responses,
|
|
200
|
+
total=len(project_responses)
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
except Exception as e:
|
|
204
|
+
log.error("Error fetching projects for user %s: %s", user_id, e)
|
|
205
|
+
raise HTTPException(
|
|
206
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
207
|
+
detail="Failed to retrieve projects"
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@router.get("/projects/{project_id}", response_model=ProjectResponse)
|
|
212
|
+
async def get_project(
|
|
213
|
+
project_id: str,
|
|
214
|
+
user: dict = Depends(get_current_user),
|
|
215
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
216
|
+
db: Session = Depends(get_db),
|
|
217
|
+
_: None = Depends(check_projects_enabled),
|
|
218
|
+
):
|
|
219
|
+
"""
|
|
220
|
+
Get a specific project by ID.
|
|
221
|
+
"""
|
|
222
|
+
user_id = user.get("id")
|
|
223
|
+
log.info("User %s attempting to fetch project_id: %s", user_id, project_id)
|
|
224
|
+
|
|
225
|
+
try:
|
|
226
|
+
if (
|
|
227
|
+
not project_id
|
|
228
|
+
or project_id.strip() == ""
|
|
229
|
+
or project_id in ["null", "undefined"]
|
|
230
|
+
):
|
|
231
|
+
raise HTTPException(
|
|
232
|
+
status_code=status.HTTP_404_NOT_FOUND, detail="Project not found."
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
request_dto = GetProjectRequest(project_id=project_id, user_id=user_id)
|
|
236
|
+
|
|
237
|
+
project = project_service.get_project(
|
|
238
|
+
db=db,
|
|
239
|
+
project_id=request_dto.project_id,
|
|
240
|
+
user_id=request_dto.user_id
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
if not project:
|
|
244
|
+
raise HTTPException(
|
|
245
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
246
|
+
detail="Project not found."
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
log.info("User %s authorized. Fetching project_id: %s", user_id, project_id)
|
|
250
|
+
|
|
251
|
+
return ProjectResponse(
|
|
252
|
+
id=project.id,
|
|
253
|
+
name=project.name,
|
|
254
|
+
user_id=project.user_id,
|
|
255
|
+
description=project.description,
|
|
256
|
+
system_prompt=project.system_prompt,
|
|
257
|
+
default_agent_id=project.default_agent_id,
|
|
258
|
+
created_at=project.created_at,
|
|
259
|
+
updated_at=project.updated_at,
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
except HTTPException:
|
|
263
|
+
raise
|
|
264
|
+
except Exception as e:
|
|
265
|
+
log.error(
|
|
266
|
+
"Error fetching project %s for user %s: %s",
|
|
267
|
+
project_id,
|
|
268
|
+
user_id,
|
|
269
|
+
e,
|
|
270
|
+
)
|
|
271
|
+
raise HTTPException(
|
|
272
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
273
|
+
detail="Failed to retrieve project"
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
@router.get("/projects/{project_id}/artifacts", response_model=List[ArtifactInfo])
|
|
278
|
+
async def get_project_artifacts(
|
|
279
|
+
project_id: str,
|
|
280
|
+
user: dict = Depends(get_current_user),
|
|
281
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
282
|
+
db: Session = Depends(get_db),
|
|
283
|
+
_: None = Depends(check_projects_enabled),
|
|
284
|
+
):
|
|
285
|
+
"""
|
|
286
|
+
Get all artifacts for a specific project.
|
|
287
|
+
"""
|
|
288
|
+
user_id = user.get("id")
|
|
289
|
+
log.info("User %s attempting to fetch artifacts for project_id: %s", user_id, project_id)
|
|
290
|
+
|
|
291
|
+
try:
|
|
292
|
+
artifacts = await project_service.get_project_artifacts(
|
|
293
|
+
db=db, project_id=project_id, user_id=user_id
|
|
294
|
+
)
|
|
295
|
+
return artifacts
|
|
296
|
+
except ValueError as e:
|
|
297
|
+
log.warning(f"Validation error getting project artifacts: {e}")
|
|
298
|
+
raise HTTPException(
|
|
299
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
300
|
+
detail=str(e)
|
|
301
|
+
)
|
|
302
|
+
except Exception as e:
|
|
303
|
+
log.error(
|
|
304
|
+
"Error fetching artifacts for project %s for user %s: %s",
|
|
305
|
+
project_id,
|
|
306
|
+
user_id,
|
|
307
|
+
e,
|
|
308
|
+
)
|
|
309
|
+
raise HTTPException(
|
|
310
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
311
|
+
detail="Failed to retrieve project artifacts"
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@router.post("/projects/{project_id}/artifacts", status_code=status.HTTP_201_CREATED)
|
|
316
|
+
async def add_project_artifacts(
|
|
317
|
+
project_id: str,
|
|
318
|
+
files: List[UploadFile] = File(...),
|
|
319
|
+
file_metadata: Optional[str] = Form(None, alias="fileMetadata"),
|
|
320
|
+
user: dict = Depends(get_current_user),
|
|
321
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
322
|
+
db: Session = Depends(get_db),
|
|
323
|
+
_: None = Depends(check_projects_enabled),
|
|
324
|
+
):
|
|
325
|
+
"""
|
|
326
|
+
Add one or more artifacts to a project.
|
|
327
|
+
"""
|
|
328
|
+
user_id = user.get("id")
|
|
329
|
+
log.info(f"User {user_id} attempting to add artifacts to project {project_id}")
|
|
330
|
+
|
|
331
|
+
try:
|
|
332
|
+
parsed_file_metadata = {}
|
|
333
|
+
if file_metadata:
|
|
334
|
+
try:
|
|
335
|
+
parsed_file_metadata = json.loads(file_metadata)
|
|
336
|
+
except json.JSONDecodeError:
|
|
337
|
+
log.warning(f"Could not parse file_metadata for project {project_id}, ignoring.")
|
|
338
|
+
pass
|
|
339
|
+
|
|
340
|
+
results = await project_service.add_artifacts_to_project(
|
|
341
|
+
db=db,
|
|
342
|
+
project_id=project_id,
|
|
343
|
+
user_id=user_id,
|
|
344
|
+
files=files,
|
|
345
|
+
file_metadata=parsed_file_metadata
|
|
346
|
+
)
|
|
347
|
+
return results
|
|
348
|
+
except ValueError as e:
|
|
349
|
+
log.warning(f"Validation error adding artifacts to project {project_id}: {e}")
|
|
350
|
+
# Could be 404 if project not found, or 400 if other validation fails
|
|
351
|
+
if "not found" in str(e).lower():
|
|
352
|
+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e))
|
|
353
|
+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
354
|
+
except Exception as e:
|
|
355
|
+
log.error(
|
|
356
|
+
"Error adding artifacts to project %s for user %s: %s",
|
|
357
|
+
project_id,
|
|
358
|
+
user_id,
|
|
359
|
+
e,
|
|
360
|
+
)
|
|
361
|
+
raise HTTPException(
|
|
362
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
363
|
+
detail="Failed to add artifacts to project"
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
@router.delete("/projects/{project_id}/artifacts/{filename}", status_code=status.HTTP_204_NO_CONTENT)
|
|
368
|
+
async def delete_project_artifact(
|
|
369
|
+
project_id: str,
|
|
370
|
+
filename: str,
|
|
371
|
+
user: dict = Depends(get_current_user),
|
|
372
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
373
|
+
db: Session = Depends(get_db),
|
|
374
|
+
_: None = Depends(check_projects_enabled),
|
|
375
|
+
):
|
|
376
|
+
"""
|
|
377
|
+
Delete an artifact from a project.
|
|
378
|
+
"""
|
|
379
|
+
user_id = user.get("id")
|
|
380
|
+
log.info(f"User {user_id} attempting to delete artifact '{filename}' from project {project_id}")
|
|
381
|
+
|
|
382
|
+
try:
|
|
383
|
+
success = await project_service.delete_artifact_from_project(
|
|
384
|
+
db=db,
|
|
385
|
+
project_id=project_id,
|
|
386
|
+
user_id=user_id,
|
|
387
|
+
filename=filename,
|
|
388
|
+
)
|
|
389
|
+
if not success:
|
|
390
|
+
raise HTTPException(
|
|
391
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
392
|
+
detail="Project not found or access denied."
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
return
|
|
396
|
+
except ValueError as e:
|
|
397
|
+
log.warning(f"Validation error deleting artifact from project {project_id}: {e}")
|
|
398
|
+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
399
|
+
except HTTPException:
|
|
400
|
+
raise
|
|
401
|
+
except Exception as e:
|
|
402
|
+
log.error(
|
|
403
|
+
"Error deleting artifact '%s' from project %s for user %s: %s",
|
|
404
|
+
filename,
|
|
405
|
+
project_id,
|
|
406
|
+
user_id,
|
|
407
|
+
e,
|
|
408
|
+
)
|
|
409
|
+
raise HTTPException(
|
|
410
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
411
|
+
detail="Failed to delete artifact from project"
|
|
412
|
+
)
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
@router.put("/projects/{project_id}", response_model=ProjectResponse)
|
|
416
|
+
async def update_project(
|
|
417
|
+
project_id: str,
|
|
418
|
+
request: UpdateProjectRequest,
|
|
419
|
+
user: dict = Depends(get_current_user),
|
|
420
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
421
|
+
db: Session = Depends(get_db),
|
|
422
|
+
_: None = Depends(check_projects_enabled),
|
|
423
|
+
):
|
|
424
|
+
"""
|
|
425
|
+
Update a project's details.
|
|
426
|
+
"""
|
|
427
|
+
user_id = user.get("id")
|
|
428
|
+
log.info("User %s attempting to update project %s", user_id, project_id)
|
|
429
|
+
|
|
430
|
+
try:
|
|
431
|
+
if (
|
|
432
|
+
not project_id
|
|
433
|
+
or project_id.strip() == ""
|
|
434
|
+
or project_id in ["null", "undefined"]
|
|
435
|
+
):
|
|
436
|
+
raise HTTPException(
|
|
437
|
+
status_code=status.HTTP_404_NOT_FOUND, detail="Project not found."
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
update_fields = request.model_dump(exclude_unset=True, by_alias=False)
|
|
441
|
+
|
|
442
|
+
# Pass only explicitly set fields to the service
|
|
443
|
+
kwargs = {
|
|
444
|
+
'db': db,
|
|
445
|
+
'project_id': project_id,
|
|
446
|
+
'user_id': user_id,
|
|
447
|
+
'name': update_fields.get('name', ...),
|
|
448
|
+
'description': update_fields.get('description', ...),
|
|
449
|
+
'system_prompt': update_fields.get('system_prompt', ...),
|
|
450
|
+
'default_agent_id': update_fields.get('default_agent_id', ...),
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
project = project_service.update_project(**kwargs)
|
|
454
|
+
|
|
455
|
+
if not project:
|
|
456
|
+
raise HTTPException(
|
|
457
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
458
|
+
detail="Project not found."
|
|
459
|
+
)
|
|
460
|
+
|
|
461
|
+
log.info("Project %s updated successfully", project_id)
|
|
462
|
+
|
|
463
|
+
return ProjectResponse(
|
|
464
|
+
id=project.id,
|
|
465
|
+
name=project.name,
|
|
466
|
+
user_id=project.user_id,
|
|
467
|
+
description=project.description,
|
|
468
|
+
system_prompt=project.system_prompt,
|
|
469
|
+
default_agent_id=project.default_agent_id,
|
|
470
|
+
created_at=project.created_at,
|
|
471
|
+
updated_at=project.updated_at,
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
except HTTPException:
|
|
475
|
+
raise
|
|
476
|
+
except ValueError as e:
|
|
477
|
+
log.warning("Validation error updating project %s: %s", project_id, e)
|
|
478
|
+
raise HTTPException(
|
|
479
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e)
|
|
480
|
+
)
|
|
481
|
+
except Exception as e:
|
|
482
|
+
log.error(
|
|
483
|
+
"Error updating project %s for user %s: %s",
|
|
484
|
+
project_id,
|
|
485
|
+
user_id,
|
|
486
|
+
e,
|
|
487
|
+
)
|
|
488
|
+
raise HTTPException(
|
|
489
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
490
|
+
detail="Failed to update project"
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
@router.delete("/projects/{project_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
495
|
+
async def delete_project(
|
|
496
|
+
project_id: str,
|
|
497
|
+
user: dict = Depends(get_current_user),
|
|
498
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
499
|
+
db: Session = Depends(get_db),
|
|
500
|
+
_: None = Depends(check_projects_enabled),
|
|
501
|
+
):
|
|
502
|
+
"""
|
|
503
|
+
Soft delete a project (marks as deleted without removing from database).
|
|
504
|
+
"""
|
|
505
|
+
user_id = user.get("id")
|
|
506
|
+
log.info("User %s attempting to soft delete project %s", user_id, project_id)
|
|
507
|
+
|
|
508
|
+
try:
|
|
509
|
+
request_dto = DeleteProjectRequest(project_id=project_id, user_id=user_id)
|
|
510
|
+
|
|
511
|
+
success = project_service.soft_delete_project(
|
|
512
|
+
db=db,
|
|
513
|
+
project_id=request_dto.project_id,
|
|
514
|
+
user_id=request_dto.user_id
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
if not success:
|
|
518
|
+
raise HTTPException(
|
|
519
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
520
|
+
detail="Project not found."
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
log.info("Project %s soft deleted successfully", project_id)
|
|
524
|
+
|
|
525
|
+
except HTTPException:
|
|
526
|
+
raise
|
|
527
|
+
except ValueError as e:
|
|
528
|
+
log.warning("Validation error deleting project %s: %s", project_id, e)
|
|
529
|
+
raise HTTPException(
|
|
530
|
+
status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)
|
|
531
|
+
)
|
|
532
|
+
except Exception as e:
|
|
533
|
+
log.error(
|
|
534
|
+
"Error deleting project %s for user %s: %s",
|
|
535
|
+
project_id,
|
|
536
|
+
user_id,
|
|
537
|
+
e,
|
|
538
|
+
)
|
|
539
|
+
raise HTTPException(
|
|
540
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
541
|
+
detail="Failed to delete project"
|
|
542
|
+
)
|
|
@@ -49,7 +49,6 @@ The `routers` directory contains FastAPI router modules that define the REST API
|
|
|
49
49
|
- `get_latest_artifact(session_id: str, filename: str) -> StreamingResponse` - Downloads latest artifact version with embed resolution
|
|
50
50
|
- `get_specific_artifact_version(session_id: str, filename: str, version: Union[int, str]) -> StreamingResponse` - Downloads specific version
|
|
51
51
|
- `get_artifact_by_uri(uri: str) -> StreamingResponse` - Downloads artifact by formal artifact:// URI
|
|
52
|
-
- `upload_artifact(session_id: str, filename: str, upload_file: UploadFile, metadata_json: Optional[str]) -> Dict[str, Any]` - Uploads new artifact version with metadata
|
|
53
52
|
- `delete_artifact(session_id: str, filename: str) -> Response` - Deletes artifact and all versions
|
|
54
53
|
|
|
55
54
|
#### auth.py
|
|
@@ -100,6 +99,8 @@ The `routers` directory contains FastAPI router modules that define the REST API
|
|
|
100
99
|
- `get_session_history(session_id: str, user: dict) -> List[MessageResponse]` - Gets session message history
|
|
101
100
|
- `update_session_name(session_id: str, name: str, user: dict) -> SessionResponse` - Updates session name with validation
|
|
102
101
|
- `delete_session(session_id: str, user: dict) -> None` - Deletes session with cascade notifications
|
|
102
|
+
- `save_task(session_id: str, request: SaveTaskRequest, user: dict) -> TaskResponse` - Saves complete task interaction (upsert)
|
|
103
|
+
- `get_session_tasks(session_id: str, user: dict) -> TaskListResponse` - Gets all tasks for a session
|
|
103
104
|
|
|
104
105
|
#### sse.py
|
|
105
106
|
**Purpose:** Provides Server-Sent Events endpoint for real-time streaming
|
|
@@ -293,22 +294,19 @@ import httpx
|
|
|
293
294
|
import json
|
|
294
295
|
from pathlib import Path
|
|
295
296
|
|
|
296
|
-
# Upload an artifact with metadata
|
|
297
|
+
# Upload an artifact with metadata and automatic session creation
|
|
297
298
|
async def upload_artifact_with_metadata(session_id: str, filename: str, file_path: Path, metadata: dict = None):
|
|
298
299
|
files = {"upload_file": (filename, file_path.open("rb"))}
|
|
299
|
-
data = {
|
|
300
|
+
data = {
|
|
301
|
+
"sessionId": session_id, # Can be null/empty to create new session
|
|
302
|
+
"filename": filename
|
|
303
|
+
}
|
|
300
304
|
|
|
301
305
|
if metadata:
|
|
302
306
|
data["metadata_json"] = json.dumps(metadata)
|
|
303
307
|
|
|
304
308
|
async with httpx.AsyncClient() as client:
|
|
305
309
|
response = await client.post(
|
|
306
|
-
|
|
307
|
-
files=files,
|
|
308
|
-
data=data
|
|
309
|
-
)
|
|
310
|
-
return response.json()
|
|
311
|
-
|
|
312
|
-
# Upload artifact with automatic session creation
|
|
310
|
+
"http://localhost:
|
|
313
311
|
|
|
314
|
-
# content_hash:
|
|
312
|
+
# content_hash: 8e5e35d1d55cb448656d32d58b4600db192110e8de163a93765eef3e04086329
|