solace-agent-mesh 1.6.1__py3-none-any.whl → 1.13.2__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/alembic/README +74 -0
- solace_agent_mesh/agent/adk/alembic/env.py +77 -0
- solace_agent_mesh/agent/adk/alembic/script.py.mako +28 -0
- solace_agent_mesh/agent/adk/alembic/versions/e2902798564d_adk_session_db_upgrade.py +52 -0
- solace_agent_mesh/agent/adk/alembic.ini +112 -0
- solace_agent_mesh/agent/adk/app_llm_agent.py +26 -0
- solace_agent_mesh/agent/adk/artifacts/filesystem_artifact_service.py +165 -1
- solace_agent_mesh/agent/adk/artifacts/s3_artifact_service.py +163 -0
- solace_agent_mesh/agent/adk/callbacks.py +852 -109
- solace_agent_mesh/agent/adk/embed_resolving_mcp_toolset.py +234 -36
- solace_agent_mesh/agent/adk/intelligent_mcp_callbacks.py +52 -5
- solace_agent_mesh/agent/adk/mcp_content_processor.py +1 -1
- solace_agent_mesh/agent/adk/models/lite_llm.py +77 -21
- solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +24 -137
- solace_agent_mesh/agent/adk/runner.py +85 -20
- solace_agent_mesh/agent/adk/schema_migration.py +88 -0
- solace_agent_mesh/agent/adk/services.py +94 -18
- solace_agent_mesh/agent/adk/setup.py +281 -65
- solace_agent_mesh/agent/adk/stream_parser.py +231 -37
- solace_agent_mesh/agent/adk/tool_wrapper.py +3 -0
- solace_agent_mesh/agent/protocol/event_handlers.py +472 -137
- solace_agent_mesh/agent/proxies/a2a/app.py +3 -2
- solace_agent_mesh/agent/proxies/a2a/component.py +572 -75
- solace_agent_mesh/agent/proxies/a2a/config.py +80 -4
- solace_agent_mesh/agent/proxies/base/app.py +3 -2
- solace_agent_mesh/agent/proxies/base/component.py +188 -22
- solace_agent_mesh/agent/proxies/base/proxy_task_context.py +3 -1
- solace_agent_mesh/agent/sac/app.py +91 -3
- solace_agent_mesh/agent/sac/component.py +591 -157
- solace_agent_mesh/agent/sac/patch_adk.py +8 -16
- solace_agent_mesh/agent/sac/task_execution_context.py +146 -4
- solace_agent_mesh/agent/tools/__init__.py +3 -0
- solace_agent_mesh/agent/tools/audio_tools.py +3 -3
- solace_agent_mesh/agent/tools/builtin_artifact_tools.py +710 -171
- solace_agent_mesh/agent/tools/deep_research_tools.py +2161 -0
- solace_agent_mesh/agent/tools/dynamic_tool.py +2 -0
- solace_agent_mesh/agent/tools/peer_agent_tool.py +82 -15
- solace_agent_mesh/agent/tools/time_tools.py +126 -0
- solace_agent_mesh/agent/tools/tool_config_types.py +57 -2
- solace_agent_mesh/agent/tools/web_search_tools.py +279 -0
- solace_agent_mesh/agent/tools/web_tools.py +125 -17
- solace_agent_mesh/agent/utils/artifact_helpers.py +248 -6
- solace_agent_mesh/agent/utils/context_helpers.py +17 -0
- solace_agent_mesh/assets/docs/404.html +6 -6
- solace_agent_mesh/assets/docs/assets/css/{styles.906a1503.css → styles.8162edfb.css} +1 -1
- solace_agent_mesh/assets/docs/assets/js/05749d90.19ac4f35.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.e186750d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/15e40e79.434bb30f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/17896441.e612dfb4.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2279.550aa580.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/{17896441.a5e82f9b.js.LICENSE.txt → 2279.550aa580.js.LICENSE.txt} +6 -0
- solace_agent_mesh/assets/docs/assets/js/240a0364.83e37aa8.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2987107d.a80604f9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/2e32b5e0.2f0db237.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3a6c6137.7e61915d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ac1795d.7f7ab1c1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/3ff0015d.e53c9b78.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/41adc471.0e95b87c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/4667dc50.bf2ad456.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/49eed117.493d6f99.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{509e993c.4c7a1a6d.js → 509e993c.a1fbf45a.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/547e15cc.8e6da617.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/55b7b518.29d6e75d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5b8d9c11.d4eb37b8.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.1ee87753.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/60702c0e.a8bdd79b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/631738c7.fa471607.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/64195356.09dbd087.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/66d4869e.30340bd3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6a520c9d.b6e3f2ce.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6aaedf65.7253541d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.a5b36a60.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6d84eae0.fd23ba4a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/71da7b71.374b9d54.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/729898df.7249e9fd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/7e294c01.7c5f6906.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8024126c.e3467286.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/81a99df0.7ed65d45.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/82fbfb93.161823a5.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/8b032486.91a91afc.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/924ffdeb.975e428a.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/94e8668d.16083b3f.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9bb13469.4523ae20.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a7d42657.a956689d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/a94703ab.3e5fbcb3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e563275.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ad87452a.9d73dad6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/c93cbaa0.0e0d8baf.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cab03b5b.6a073091.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/cbe2e9ea.07e170dd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/da0b5bad.b62f7b08.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.06d23db6.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e1b6eeb4.deb2b62e.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e3d9abda.1476f570.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.acc800d3.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e92d0134.c147a429.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ee0c2fe7.94d0a351.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.cc97854c.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.74710fc1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/main.d634009f.js +2 -0
- solace_agent_mesh/assets/docs/assets/js/runtime~main.27bb82a7.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +68 -68
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/artifact-management/index.html +50 -50
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/audio-tools/index.html +42 -42
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/data-analysis-tools/index.html +55 -55
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/embeds/index.html +82 -68
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/image-tools/index.html +81 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/index.html +67 -50
- solace_agent_mesh/assets/docs/docs/documentation/components/builtin-tools/research-tools/index.html +136 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/cli/index.html +178 -144
- solace_agent_mesh/assets/docs/docs/documentation/components/gateways/index.html +43 -42
- solace_agent_mesh/assets/docs/docs/documentation/components/index.html +20 -18
- solace_agent_mesh/assets/docs/docs/documentation/components/orchestrator/index.html +23 -23
- solace_agent_mesh/assets/docs/docs/documentation/components/platform-service/index.html +33 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/plugins/index.html +45 -45
- solace_agent_mesh/assets/docs/docs/documentation/components/projects/index.html +182 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/prompts/index.html +147 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/proxies/index.html +208 -125
- solace_agent_mesh/assets/docs/docs/documentation/components/speech/index.html +52 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +28 -49
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +29 -30
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +14 -14
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/index.html +47 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/kubernetes/kubernetes-deployment-guide/index.html +197 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/logging/index.html +90 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +17 -16
- solace_agent_mesh/assets/docs/docs/documentation/deploying/proxy_configuration/index.html +49 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-agents/index.html +38 -38
- solace_agent_mesh/assets/docs/docs/documentation/developing/create-gateways/index.html +162 -171
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-python-tools/index.html +67 -49
- solace_agent_mesh/assets/docs/docs/documentation/developing/creating-service-providers/index.html +17 -17
- solace_agent_mesh/assets/docs/docs/documentation/developing/evaluations/index.html +51 -51
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +22 -22
- solace_agent_mesh/assets/docs/docs/documentation/developing/structure/index.html +27 -27
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/bedrock-agents/index.html +135 -135
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/custom-agent/index.html +66 -66
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +51 -51
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mcp-integration/index.html +50 -38
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/mongodb-integration/index.html +86 -86
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rag-integration/index.html +51 -51
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/rest-gateway/index.html +24 -24
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/slack-integration/index.html +30 -30
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/sql-database/index.html +44 -44
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/teams-integration/index.html +115 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/agent-builder/index.html +86 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/connectors/index.html +67 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/index.html +23 -19
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +40 -37
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/openapi-tools/index.html +324 -0
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +112 -87
- 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 +87 -64
- 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 +44 -44
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +39 -37
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +30 -30
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/try-agent-mesh/index.html +18 -18
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/vibe_coding/index.html +62 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/artifact-storage/index.html +311 -0
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/configurations/index.html +39 -42
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +14 -14
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +27 -25
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +69 -69
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +72 -72
- 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 +42 -42
- solace_agent_mesh/assets/docs/docs/documentation/migrations/a2a-upgrade/a2a-technical-migration-map/index.html +20 -20
- solace_agent_mesh/assets/docs/docs/documentation/migrations/platform-service-split/index.html +85 -0
- solace_agent_mesh/assets/docs/lunr-index-1768329217460.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1768329217460.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/add_cmd/__init__.py +3 -1
- solace_agent_mesh/cli/commands/add_cmd/agent_cmd.py +6 -1
- solace_agent_mesh/cli/commands/add_cmd/proxy_cmd.py +100 -0
- solace_agent_mesh/cli/commands/docs_cmd.py +4 -1
- solace_agent_mesh/cli/commands/eval_cmd.py +1 -1
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +15 -0
- solace_agent_mesh/cli/commands/init_cmd/directory_step.py +1 -1
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +30 -3
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +3 -4
- solace_agent_mesh/cli/commands/init_cmd/platform_service_step.py +85 -0
- solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +16 -3
- solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +2 -1
- solace_agent_mesh/cli/commands/plugin_cmd/catalog_cmd.py +1 -0
- solace_agent_mesh/cli/commands/plugin_cmd/create_cmd.py +3 -3
- solace_agent_mesh/cli/commands/run_cmd.py +64 -49
- solace_agent_mesh/cli/commands/tools_cmd.py +315 -0
- solace_agent_mesh/cli/main.py +15 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-BTf6dqwp.js → authCallback-KnKMP_vb.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/client-DpBL2stg.js +25 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-Cd498TV2.js +435 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-rSf8Vu29.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/vendor-CGk8Suyh.js +565 -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/client/webui/frontend/static/mockServiceWorker.js +336 -0
- solace_agent_mesh/client/webui/frontend/static/ui-version.json +6 -0
- solace_agent_mesh/common/a2a/events.py +2 -1
- solace_agent_mesh/common/a2a/protocol.py +5 -0
- solace_agent_mesh/common/a2a/types.py +2 -1
- 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/agent_registry.py +38 -11
- solace_agent_mesh/common/data_parts.py +144 -4
- solace_agent_mesh/common/error_handlers.py +83 -0
- solace_agent_mesh/common/exceptions.py +24 -0
- solace_agent_mesh/common/oauth/__init__.py +17 -0
- solace_agent_mesh/common/oauth/oauth_client.py +408 -0
- solace_agent_mesh/common/oauth/utils.py +50 -0
- solace_agent_mesh/common/rag_dto.py +156 -0
- solace_agent_mesh/common/sac/sam_component_base.py +97 -19
- solace_agent_mesh/common/sam_events/event_service.py +2 -2
- solace_agent_mesh/common/services/employee_service.py +1 -1
- solace_agent_mesh/common/utils/embeds/constants.py +1 -0
- solace_agent_mesh/common/utils/embeds/converter.py +1 -8
- solace_agent_mesh/common/utils/embeds/modifiers.py +4 -28
- solace_agent_mesh/common/utils/embeds/resolver.py +152 -31
- solace_agent_mesh/common/utils/embeds/types.py +9 -0
- solace_agent_mesh/common/utils/log_formatters.py +20 -0
- solace_agent_mesh/common/utils/mime_helpers.py +12 -5
- solace_agent_mesh/common/utils/pydantic_utils.py +90 -3
- solace_agent_mesh/common/utils/rbac_utils.py +69 -0
- solace_agent_mesh/common/utils/templates/__init__.py +8 -0
- solace_agent_mesh/common/utils/templates/liquid_renderer.py +210 -0
- solace_agent_mesh/common/utils/templates/template_resolver.py +161 -0
- solace_agent_mesh/config_portal/backend/common.py +12 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-CljP4_mv.js +103 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{components-Rk0n-9cK.js → components-CaC6hG8d.js} +22 -22
- solace_agent_mesh/config_portal/frontend/static/client/assets/{entry.client-mvZjNKiz.js → entry.client-H_TM0YBt.js} +3 -3
- solace_agent_mesh/config_portal/frontend/static/client/assets/{index-DzNKzXrc.js → index-CnFykb2v.js} +16 -16
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-f8439d40.js +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-BIMqslJB.css +1 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-mJmTIdIk.js +10 -0
- solace_agent_mesh/config_portal/frontend/static/client/index.html +3 -3
- solace_agent_mesh/core_a2a/service.py +3 -2
- solace_agent_mesh/gateway/adapter/__init__.py +1 -0
- solace_agent_mesh/gateway/adapter/base.py +170 -0
- solace_agent_mesh/gateway/adapter/types.py +230 -0
- solace_agent_mesh/gateway/base/app.py +39 -2
- solace_agent_mesh/gateway/base/auth_interface.py +103 -0
- solace_agent_mesh/gateway/base/component.py +1027 -151
- 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 +894 -0
- solace_agent_mesh/gateway/http_sse/alembic/env.py +0 -7
- 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 +109 -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/20251108_create_prompt_tables_with_sharing.py +154 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251115_add_parent_task_id.py +32 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251126_add_background_task_fields.py +47 -0
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251202_add_versioned_fields_to_prompts.py +52 -0
- solace_agent_mesh/gateway/http_sse/alembic.ini +0 -36
- solace_agent_mesh/gateway/http_sse/app.py +40 -11
- solace_agent_mesh/gateway/http_sse/component.py +285 -160
- solace_agent_mesh/gateway/http_sse/dependencies.py +149 -114
- solace_agent_mesh/gateway/http_sse/main.py +68 -450
- solace_agent_mesh/gateway/http_sse/repository/__init__.py +19 -1
- solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +2 -2
- 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 +26 -3
- solace_agent_mesh/gateway/http_sse/repository/entities/task.py +7 -0
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +47 -0
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +114 -6
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +13 -0
- 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/prompt_model.py +159 -0
- solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +8 -2
- solace_agent_mesh/gateway/http_sse/repository/models/task_model.py +8 -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/session_repository.py +177 -11
- solace_agent_mesh/gateway/http_sse/repository/task_repository.py +86 -2
- solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +38 -7
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +256 -58
- solace_agent_mesh/gateway/http_sse/routers/auth.py +168 -134
- solace_agent_mesh/gateway/http_sse/routers/config.py +302 -8
- solace_agent_mesh/gateway/http_sse/routers/dto/project_dto.py +69 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/prompt_dto.py +255 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/project_requests.py +48 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +14 -1
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/base_responses.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/project_responses.py +31 -0
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +5 -2
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/version_responses.py +31 -0
- solace_agent_mesh/gateway/http_sse/routers/feedback.py +133 -2
- solace_agent_mesh/gateway/http_sse/routers/people.py +2 -2
- solace_agent_mesh/gateway/http_sse/routers/projects.py +768 -0
- solace_agent_mesh/gateway/http_sse/routers/prompts.py +1416 -0
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +167 -7
- solace_agent_mesh/gateway/http_sse/routers/speech.py +355 -0
- solace_agent_mesh/gateway/http_sse/routers/sse.py +131 -8
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +670 -18
- solace_agent_mesh/gateway/http_sse/routers/users.py +1 -1
- solace_agent_mesh/gateway/http_sse/routers/version.py +343 -0
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +92 -9
- solace_agent_mesh/gateway/http_sse/services/audio_service.py +1227 -0
- solace_agent_mesh/gateway/http_sse/services/background_task_monitor.py +186 -0
- solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +1 -1
- solace_agent_mesh/gateway/http_sse/services/feedback_service.py +1 -1
- solace_agent_mesh/gateway/http_sse/services/project_service.py +930 -0
- solace_agent_mesh/gateway/http_sse/services/prompt_builder_assistant.py +303 -0
- solace_agent_mesh/gateway/http_sse/services/session_service.py +361 -12
- solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +354 -4
- solace_agent_mesh/gateway/http_sse/session_manager.py +15 -15
- solace_agent_mesh/gateway/http_sse/sse_manager.py +286 -166
- solace_agent_mesh/gateway/http_sse/utils/artifact_copy_utils.py +370 -0
- solace_agent_mesh/gateway/http_sse/utils/stim_utils.py +41 -1
- solace_agent_mesh/services/__init__.py +0 -0
- solace_agent_mesh/services/platform/__init__.py +29 -0
- solace_agent_mesh/services/platform/alembic/env.py +85 -0
- solace_agent_mesh/services/platform/alembic/script.py.mako +28 -0
- solace_agent_mesh/services/platform/alembic.ini +109 -0
- solace_agent_mesh/services/platform/api/__init__.py +3 -0
- solace_agent_mesh/services/platform/api/dependencies.py +154 -0
- solace_agent_mesh/services/platform/api/main.py +314 -0
- solace_agent_mesh/services/platform/api/middleware.py +51 -0
- solace_agent_mesh/services/platform/api/routers/__init__.py +33 -0
- solace_agent_mesh/services/platform/api/routers/health_router.py +31 -0
- solace_agent_mesh/services/platform/app.py +215 -0
- solace_agent_mesh/services/platform/component.py +777 -0
- solace_agent_mesh/shared/__init__.py +14 -0
- solace_agent_mesh/shared/api/__init__.py +42 -0
- solace_agent_mesh/shared/auth/__init__.py +26 -0
- solace_agent_mesh/shared/auth/dependencies.py +204 -0
- solace_agent_mesh/shared/auth/middleware.py +347 -0
- solace_agent_mesh/shared/database/__init__.py +20 -0
- solace_agent_mesh/{gateway/http_sse/shared → shared/database}/base_repository.py +1 -1
- solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_exceptions.py +1 -1
- solace_agent_mesh/{gateway/http_sse/shared → shared/database}/database_helpers.py +1 -1
- solace_agent_mesh/shared/exceptions/__init__.py +36 -0
- solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exception_handlers.py +19 -5
- solace_agent_mesh/shared/utils/__init__.py +21 -0
- solace_agent_mesh/templates/logging_config_template.yaml +48 -0
- solace_agent_mesh/templates/main_orchestrator.yaml +12 -1
- solace_agent_mesh/templates/platform.yaml +49 -0
- solace_agent_mesh/templates/plugin_readme_template.md +3 -25
- solace_agent_mesh/templates/plugin_tool_config_template.yaml +109 -0
- solace_agent_mesh/templates/proxy_template.yaml +62 -0
- solace_agent_mesh/templates/webui.yaml +148 -6
- solace_agent_mesh/tools/web_search/__init__.py +18 -0
- solace_agent_mesh/tools/web_search/base.py +84 -0
- solace_agent_mesh/tools/web_search/google_search.py +247 -0
- solace_agent_mesh/tools/web_search/models.py +99 -0
- {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/METADATA +31 -12
- solace_agent_mesh-1.13.2.dist-info/RECORD +591 -0
- {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/WHEEL +1 -1
- solace_agent_mesh/agent/adk/adk_llm.txt +0 -232
- solace_agent_mesh/agent/adk/adk_llm_detail.txt +0 -566
- solace_agent_mesh/agent/adk/artifacts/artifacts_llm.txt +0 -171
- solace_agent_mesh/agent/adk/models/models_llm.txt +0 -142
- solace_agent_mesh/agent/agent_llm.txt +0 -378
- solace_agent_mesh/agent/agent_llm_detail.txt +0 -1702
- solace_agent_mesh/agent/protocol/protocol_llm.txt +0 -81
- solace_agent_mesh/agent/protocol/protocol_llm_detail.txt +0 -92
- solace_agent_mesh/agent/sac/sac_llm.txt +0 -189
- solace_agent_mesh/agent/sac/sac_llm_detail.txt +0 -200
- solace_agent_mesh/agent/testing/testing_llm.txt +0 -57
- solace_agent_mesh/agent/testing/testing_llm_detail.txt +0 -68
- solace_agent_mesh/agent/tools/tools_llm.txt +0 -263
- solace_agent_mesh/agent/tools/tools_llm_detail.txt +0 -274
- solace_agent_mesh/agent/utils/utils_llm.txt +0 -138
- solace_agent_mesh/agent/utils/utils_llm_detail.txt +0 -149
- solace_agent_mesh/assets/docs/assets/js/15ba94aa.932dd2db.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/17896441.a5e82f9b.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/240a0364.7eac6021.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/2e32b5e0.33f5d75b.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/3a6c6137.f5940cfa.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/55b7b518.f2b1d1ba.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/631738c7.a8b1ef8b.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/6d84eae0.4a5fbf39.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/8024126c.56e59919.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/81a99df0.07034dd9.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/82fbfb93.139a1a1f.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/94e8668d.b5ddb7a1.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/9bb13469.dd1c9b54.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/a94703ab.0438dbc2.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ab9708a8.3e6dd091.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/c93cbaa0.eaff365e.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/e6f9706b.e74a984d.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e92d0134.cf6d6522.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/main.b12eac43.js +0 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.e268214e.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1761248203150.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1761248203150.json +0 -1
- solace_agent_mesh/cli/commands/add_cmd/add_cmd_llm.txt +0 -250
- solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +0 -365
- solace_agent_mesh/cli/commands/plugin_cmd/plugin_cmd_llm.txt +0 -305
- solace_agent_mesh/client/webui/frontend/static/assets/client-CaY59VuC.js +0 -25
- solace_agent_mesh/client/webui/frontend/static/assets/main-B32noGmR.js +0 -342
- solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/vendor-BEmvJSYz.js +0 -405
- solace_agent_mesh/common/a2a/a2a_llm.txt +0 -182
- solace_agent_mesh/common/a2a/a2a_llm_detail.txt +0 -193
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm.txt +0 -407
- solace_agent_mesh/common/a2a_spec/a2a_spec_llm_detail.txt +0 -736
- solace_agent_mesh/common/a2a_spec/schemas/schemas_llm.txt +0 -313
- solace_agent_mesh/common/common_llm.txt +0 -251
- solace_agent_mesh/common/common_llm_detail.txt +0 -2562
- solace_agent_mesh/common/middleware/middleware_llm.txt +0 -174
- solace_agent_mesh/common/middleware/middleware_llm_detail.txt +0 -185
- solace_agent_mesh/common/sac/sac_llm.txt +0 -71
- solace_agent_mesh/common/sac/sac_llm_detail.txt +0 -82
- solace_agent_mesh/common/sam_events/sam_events_llm.txt +0 -104
- solace_agent_mesh/common/sam_events/sam_events_llm_detail.txt +0 -115
- solace_agent_mesh/common/services/providers/providers_llm.txt +0 -80
- solace_agent_mesh/common/services/services_llm.txt +0 -363
- solace_agent_mesh/common/services/services_llm_detail.txt +0 -459
- solace_agent_mesh/common/utils/embeds/embeds_llm.txt +0 -220
- solace_agent_mesh/common/utils/utils_llm.txt +0 -336
- solace_agent_mesh/common/utils/utils_llm_detail.txt +0 -572
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +0 -98
- solace_agent_mesh/config_portal/frontend/static/client/assets/manifest-61038fc6.js +0 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-BWvk5-gF.js +0 -10
- solace_agent_mesh/config_portal/frontend/static/client/assets/root-DxRwaWiE.css +0 -1
- solace_agent_mesh/core_a2a/core_a2a_llm.txt +0 -90
- solace_agent_mesh/core_a2a/core_a2a_llm_detail.txt +0 -101
- solace_agent_mesh/gateway/base/base_llm.txt +0 -224
- solace_agent_mesh/gateway/base/base_llm_detail.txt +0 -235
- solace_agent_mesh/gateway/gateway_llm.txt +0 -373
- solace_agent_mesh/gateway/gateway_llm_detail.txt +0 -3885
- solace_agent_mesh/gateway/http_sse/alembic/alembic_llm.txt +0 -295
- solace_agent_mesh/gateway/http_sse/alembic/versions/versions_llm.txt +0 -155
- solace_agent_mesh/gateway/http_sse/components/components_llm.txt +0 -105
- solace_agent_mesh/gateway/http_sse/http_sse_llm.txt +0 -299
- solace_agent_mesh/gateway/http_sse/http_sse_llm_detail.txt +0 -3278
- solace_agent_mesh/gateway/http_sse/repository/entities/entities_llm.txt +0 -263
- solace_agent_mesh/gateway/http_sse/repository/models/models_llm.txt +0 -266
- solace_agent_mesh/gateway/http_sse/repository/repository_llm.txt +0 -340
- solace_agent_mesh/gateway/http_sse/routers/dto/dto_llm.txt +0 -346
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/requests_llm.txt +0 -83
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/responses_llm.txt +0 -107
- solace_agent_mesh/gateway/http_sse/routers/routers_llm.txt +0 -314
- solace_agent_mesh/gateway/http_sse/services/services_llm.txt +0 -297
- solace_agent_mesh/gateway/http_sse/shared/__init__.py +0 -146
- solace_agent_mesh/gateway/http_sse/shared/shared_llm.txt +0 -285
- solace_agent_mesh/gateway/http_sse/utils/utils_llm.txt +0 -47
- solace_agent_mesh/llm.txt +0 -228
- solace_agent_mesh/llm_detail.txt +0 -2835
- solace_agent_mesh/solace_agent_mesh_llm.txt +0 -362
- solace_agent_mesh/solace_agent_mesh_llm_detail.txt +0 -8599
- solace_agent_mesh/templates/logging_config_template.ini +0 -45
- solace_agent_mesh/templates/templates_llm.txt +0 -147
- solace_agent_mesh-1.6.1.dist-info/RECORD +0 -525
- /solace_agent_mesh/assets/docs/assets/js/{main.b12eac43.js.LICENSE.txt → main.d634009f.js.LICENSE.txt} +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/auth_utils.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/pagination.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/api}/response_utils.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/error_dto.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/exceptions}/exceptions.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/enums.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/timestamp_utils.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/types.py +0 -0
- /solace_agent_mesh/{gateway/http_sse/shared → shared/utils}/utils.py +0 -0
- {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.6.1.dist-info → solace_agent_mesh-1.13.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,768 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project API controller using 3-tiered architecture.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
from typing import List, Optional, Dict, Any
|
|
8
|
+
from fastapi import (
|
|
9
|
+
APIRouter,
|
|
10
|
+
Depends,
|
|
11
|
+
HTTPException,
|
|
12
|
+
status,
|
|
13
|
+
Request,
|
|
14
|
+
Form,
|
|
15
|
+
File,
|
|
16
|
+
UploadFile,
|
|
17
|
+
)
|
|
18
|
+
from fastapi.responses import StreamingResponse
|
|
19
|
+
from sqlalchemy.orm import Session
|
|
20
|
+
from solace_ai_connector.common.log import log
|
|
21
|
+
|
|
22
|
+
from ..dependencies import get_project_service, get_sac_component, get_api_config, get_db
|
|
23
|
+
from ..services.project_service import ProjectService
|
|
24
|
+
from solace_agent_mesh.shared.api.auth_utils import get_current_user
|
|
25
|
+
from ....common.a2a.types import ArtifactInfo
|
|
26
|
+
from typing import TYPE_CHECKING
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from ..component import WebUIBackendComponent
|
|
30
|
+
|
|
31
|
+
from .dto.requests.project_requests import (
|
|
32
|
+
CreateProjectRequest,
|
|
33
|
+
UpdateProjectRequest,
|
|
34
|
+
GetProjectRequest,
|
|
35
|
+
GetProjectsRequest,
|
|
36
|
+
DeleteProjectRequest,
|
|
37
|
+
)
|
|
38
|
+
from .dto.responses.project_responses import (
|
|
39
|
+
ProjectResponse,
|
|
40
|
+
ProjectListResponse,
|
|
41
|
+
)
|
|
42
|
+
from .dto.project_dto import (
|
|
43
|
+
ProjectImportOptions,
|
|
44
|
+
ProjectImportResponse,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
router = APIRouter()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def check_projects_enabled(
|
|
51
|
+
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
52
|
+
api_config: Dict[str, Any] = Depends(get_api_config),
|
|
53
|
+
) -> None:
|
|
54
|
+
"""
|
|
55
|
+
Dependency to check if projects feature is enabled.
|
|
56
|
+
Raises HTTPException if projects are disabled.
|
|
57
|
+
"""
|
|
58
|
+
# Check if persistence is enabled (required for projects)
|
|
59
|
+
persistence_enabled = api_config.get("persistence_enabled", False)
|
|
60
|
+
if not persistence_enabled:
|
|
61
|
+
log.warning("Projects API called but persistence is not enabled")
|
|
62
|
+
raise HTTPException(
|
|
63
|
+
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
64
|
+
detail="Projects feature requires persistence to be enabled. Please configure session_service.type as 'sql'."
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Check explicit projects config
|
|
68
|
+
projects_config = component.get_config("projects", {})
|
|
69
|
+
if isinstance(projects_config, dict):
|
|
70
|
+
projects_explicitly_enabled = projects_config.get("enabled", True)
|
|
71
|
+
if not projects_explicitly_enabled:
|
|
72
|
+
log.warning("Projects API called but projects are explicitly disabled in config")
|
|
73
|
+
raise HTTPException(
|
|
74
|
+
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
75
|
+
detail="Projects feature is disabled. Please enable it in the configuration."
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Check frontend_feature_enablement override
|
|
79
|
+
feature_flags = component.get_config("frontend_feature_enablement", {})
|
|
80
|
+
if "projects" in feature_flags:
|
|
81
|
+
projects_flag = feature_flags.get("projects", True)
|
|
82
|
+
if not projects_flag:
|
|
83
|
+
log.warning("Projects API called but projects are disabled via feature flag")
|
|
84
|
+
raise HTTPException(
|
|
85
|
+
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
86
|
+
detail="Projects feature is disabled via feature flag."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
@router.post("/projects", response_model=ProjectResponse, status_code=status.HTTP_201_CREATED)
|
|
91
|
+
async def create_project(
|
|
92
|
+
name: str = Form(...),
|
|
93
|
+
description: Optional[str] = Form(None),
|
|
94
|
+
system_prompt: Optional[str] = Form(None, alias="systemPrompt"),
|
|
95
|
+
default_agent_id: Optional[str] = Form(None, alias="defaultAgentId"),
|
|
96
|
+
file_metadata: Optional[str] = Form(None, alias="fileMetadata"),
|
|
97
|
+
files: Optional[List[UploadFile]] = File(None),
|
|
98
|
+
user: dict = Depends(get_current_user),
|
|
99
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
100
|
+
db: Session = Depends(get_db),
|
|
101
|
+
_: None = Depends(check_projects_enabled),
|
|
102
|
+
):
|
|
103
|
+
"""
|
|
104
|
+
Create a new project for the authenticated user.
|
|
105
|
+
"""
|
|
106
|
+
user_id = user.get("id")
|
|
107
|
+
log.info(f"Creating project '{name}' for user {user_id}")
|
|
108
|
+
log.info(f"Received system_prompt: {system_prompt}")
|
|
109
|
+
log.info(f"Received file_metadata: {file_metadata}")
|
|
110
|
+
|
|
111
|
+
try:
|
|
112
|
+
if files:
|
|
113
|
+
log.info(f"Received {len(files)} files for project creation:")
|
|
114
|
+
for file in files:
|
|
115
|
+
log.info(f" - Filename: {file.filename}, Content-Type: {file.content_type}")
|
|
116
|
+
else:
|
|
117
|
+
log.info("No files received for project creation.")
|
|
118
|
+
|
|
119
|
+
request_dto = CreateProjectRequest(
|
|
120
|
+
name=name,
|
|
121
|
+
description=description,
|
|
122
|
+
system_prompt=system_prompt,
|
|
123
|
+
default_agent_id=default_agent_id,
|
|
124
|
+
file_metadata=file_metadata,
|
|
125
|
+
user_id=user_id
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
parsed_file_metadata = {}
|
|
129
|
+
if request_dto.file_metadata:
|
|
130
|
+
try:
|
|
131
|
+
parsed_file_metadata = json.loads(request_dto.file_metadata)
|
|
132
|
+
except json.JSONDecodeError:
|
|
133
|
+
log.warning("Could not parse file_metadata JSON string, ignoring.")
|
|
134
|
+
pass
|
|
135
|
+
|
|
136
|
+
project = await project_service.create_project(
|
|
137
|
+
db=db,
|
|
138
|
+
name=request_dto.name,
|
|
139
|
+
user_id=request_dto.user_id,
|
|
140
|
+
description=request_dto.description,
|
|
141
|
+
system_prompt=request_dto.system_prompt,
|
|
142
|
+
default_agent_id=request_dto.default_agent_id,
|
|
143
|
+
files=files,
|
|
144
|
+
file_metadata=parsed_file_metadata,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
return ProjectResponse(
|
|
148
|
+
id=project.id,
|
|
149
|
+
name=project.name,
|
|
150
|
+
user_id=project.user_id,
|
|
151
|
+
description=project.description,
|
|
152
|
+
system_prompt=project.system_prompt,
|
|
153
|
+
default_agent_id=project.default_agent_id,
|
|
154
|
+
created_at=project.created_at,
|
|
155
|
+
updated_at=project.updated_at,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
except ValueError as e:
|
|
159
|
+
error_msg = str(e)
|
|
160
|
+
log.warning(f"Validation error creating project: {error_msg}")
|
|
161
|
+
# Check if this is a file size error
|
|
162
|
+
if "exceeds maximum" in error_msg.lower() and "bytes" in error_msg.lower():
|
|
163
|
+
raise HTTPException(
|
|
164
|
+
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
|
|
165
|
+
detail=error_msg
|
|
166
|
+
)
|
|
167
|
+
raise HTTPException(
|
|
168
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
169
|
+
detail=error_msg
|
|
170
|
+
)
|
|
171
|
+
except Exception as e:
|
|
172
|
+
log.error("Error creating project for user %s: %s", user_id, e)
|
|
173
|
+
raise HTTPException(
|
|
174
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
175
|
+
detail="Failed to create project"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@router.get("/projects", response_model=ProjectListResponse)
|
|
180
|
+
async def get_user_projects(
|
|
181
|
+
include_artifact_count: bool = False,
|
|
182
|
+
user: dict = Depends(get_current_user),
|
|
183
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
184
|
+
db: Session = Depends(get_db),
|
|
185
|
+
_: None = Depends(check_projects_enabled),
|
|
186
|
+
):
|
|
187
|
+
"""
|
|
188
|
+
Get all projects owned by the authenticated user.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
include_artifact_count: If True, includes artifact count for each project
|
|
192
|
+
"""
|
|
193
|
+
user_id = user.get("id")
|
|
194
|
+
log.info(f"Fetching projects for user_id: {user_id}, include_artifact_count: {include_artifact_count}")
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
request_dto = GetProjectsRequest(user_id=user_id)
|
|
198
|
+
|
|
199
|
+
if include_artifact_count:
|
|
200
|
+
# Fetch projects with artifact counts
|
|
201
|
+
projects_with_counts = await project_service.get_user_projects_with_counts(db, request_dto.user_id)
|
|
202
|
+
|
|
203
|
+
project_responses = [
|
|
204
|
+
ProjectResponse(
|
|
205
|
+
id=p.id,
|
|
206
|
+
name=p.name,
|
|
207
|
+
user_id=p.user_id,
|
|
208
|
+
description=p.description,
|
|
209
|
+
system_prompt=p.system_prompt,
|
|
210
|
+
default_agent_id=p.default_agent_id,
|
|
211
|
+
artifact_count=count,
|
|
212
|
+
created_at=p.created_at,
|
|
213
|
+
updated_at=p.updated_at,
|
|
214
|
+
)
|
|
215
|
+
for p, count in projects_with_counts
|
|
216
|
+
]
|
|
217
|
+
else:
|
|
218
|
+
# Fetch projects without counts (faster)
|
|
219
|
+
projects = project_service.get_user_projects(db, request_dto.user_id)
|
|
220
|
+
|
|
221
|
+
project_responses = [
|
|
222
|
+
ProjectResponse(
|
|
223
|
+
id=p.id,
|
|
224
|
+
name=p.name,
|
|
225
|
+
user_id=p.user_id,
|
|
226
|
+
description=p.description,
|
|
227
|
+
system_prompt=p.system_prompt,
|
|
228
|
+
default_agent_id=p.default_agent_id,
|
|
229
|
+
created_at=p.created_at,
|
|
230
|
+
updated_at=p.updated_at,
|
|
231
|
+
)
|
|
232
|
+
for p in projects
|
|
233
|
+
]
|
|
234
|
+
|
|
235
|
+
return ProjectListResponse(
|
|
236
|
+
projects=project_responses,
|
|
237
|
+
total=len(project_responses)
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
except Exception as e:
|
|
241
|
+
log.error("Error fetching projects for user %s: %s", user_id, e)
|
|
242
|
+
raise HTTPException(
|
|
243
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
244
|
+
detail="Failed to retrieve projects"
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
@router.get("/projects/{project_id}", response_model=ProjectResponse)
|
|
249
|
+
async def get_project(
|
|
250
|
+
project_id: str,
|
|
251
|
+
user: dict = Depends(get_current_user),
|
|
252
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
253
|
+
db: Session = Depends(get_db),
|
|
254
|
+
_: None = Depends(check_projects_enabled),
|
|
255
|
+
):
|
|
256
|
+
"""
|
|
257
|
+
Get a specific project by ID.
|
|
258
|
+
"""
|
|
259
|
+
user_id = user.get("id")
|
|
260
|
+
log.info("User %s attempting to fetch project_id: %s", user_id, project_id)
|
|
261
|
+
|
|
262
|
+
try:
|
|
263
|
+
if (
|
|
264
|
+
not project_id
|
|
265
|
+
or project_id.strip() == ""
|
|
266
|
+
or project_id in ["null", "undefined"]
|
|
267
|
+
):
|
|
268
|
+
raise HTTPException(
|
|
269
|
+
status_code=status.HTTP_404_NOT_FOUND, detail="Project not found."
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
request_dto = GetProjectRequest(project_id=project_id, user_id=user_id)
|
|
273
|
+
|
|
274
|
+
project = project_service.get_project(
|
|
275
|
+
db=db,
|
|
276
|
+
project_id=request_dto.project_id,
|
|
277
|
+
user_id=request_dto.user_id
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
if not project:
|
|
281
|
+
raise HTTPException(
|
|
282
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
283
|
+
detail="Project not found."
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
log.info("User %s authorized. Fetching project_id: %s", user_id, project_id)
|
|
287
|
+
|
|
288
|
+
return ProjectResponse(
|
|
289
|
+
id=project.id,
|
|
290
|
+
name=project.name,
|
|
291
|
+
user_id=project.user_id,
|
|
292
|
+
description=project.description,
|
|
293
|
+
system_prompt=project.system_prompt,
|
|
294
|
+
default_agent_id=project.default_agent_id,
|
|
295
|
+
created_at=project.created_at,
|
|
296
|
+
updated_at=project.updated_at,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
except HTTPException:
|
|
300
|
+
raise
|
|
301
|
+
except Exception as e:
|
|
302
|
+
log.error(
|
|
303
|
+
"Error fetching project %s for user %s: %s",
|
|
304
|
+
project_id,
|
|
305
|
+
user_id,
|
|
306
|
+
e,
|
|
307
|
+
)
|
|
308
|
+
raise HTTPException(
|
|
309
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
310
|
+
detail="Failed to retrieve project"
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@router.get("/projects/{project_id}/artifacts", response_model=List[ArtifactInfo])
|
|
315
|
+
async def get_project_artifacts(
|
|
316
|
+
project_id: str,
|
|
317
|
+
user: dict = Depends(get_current_user),
|
|
318
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
319
|
+
db: Session = Depends(get_db),
|
|
320
|
+
_: None = Depends(check_projects_enabled),
|
|
321
|
+
):
|
|
322
|
+
"""
|
|
323
|
+
Get all artifacts for a specific project.
|
|
324
|
+
"""
|
|
325
|
+
user_id = user.get("id")
|
|
326
|
+
log.info("User %s attempting to fetch artifacts for project_id: %s", user_id, project_id)
|
|
327
|
+
|
|
328
|
+
try:
|
|
329
|
+
artifacts = await project_service.get_project_artifacts(
|
|
330
|
+
db=db, project_id=project_id, user_id=user_id
|
|
331
|
+
)
|
|
332
|
+
return artifacts
|
|
333
|
+
except ValueError as e:
|
|
334
|
+
log.warning(f"Validation error getting project artifacts: {e}")
|
|
335
|
+
raise HTTPException(
|
|
336
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
337
|
+
detail=str(e)
|
|
338
|
+
)
|
|
339
|
+
except Exception as e:
|
|
340
|
+
log.error(
|
|
341
|
+
"Error fetching artifacts for project %s for user %s: %s",
|
|
342
|
+
project_id,
|
|
343
|
+
user_id,
|
|
344
|
+
e,
|
|
345
|
+
)
|
|
346
|
+
raise HTTPException(
|
|
347
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
348
|
+
detail="Failed to retrieve project artifacts"
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
@router.post("/projects/{project_id}/artifacts", status_code=status.HTTP_201_CREATED)
|
|
353
|
+
async def add_project_artifacts(
|
|
354
|
+
project_id: str,
|
|
355
|
+
files: List[UploadFile] = File(...),
|
|
356
|
+
file_metadata: Optional[str] = Form(None, alias="fileMetadata"),
|
|
357
|
+
user: dict = Depends(get_current_user),
|
|
358
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
359
|
+
db: Session = Depends(get_db),
|
|
360
|
+
_: None = Depends(check_projects_enabled),
|
|
361
|
+
):
|
|
362
|
+
"""
|
|
363
|
+
Add one or more artifacts to a project.
|
|
364
|
+
"""
|
|
365
|
+
user_id = user.get("id")
|
|
366
|
+
log.info(f"User {user_id} attempting to add artifacts to project {project_id}")
|
|
367
|
+
|
|
368
|
+
try:
|
|
369
|
+
parsed_file_metadata = {}
|
|
370
|
+
if file_metadata:
|
|
371
|
+
try:
|
|
372
|
+
parsed_file_metadata = json.loads(file_metadata)
|
|
373
|
+
except json.JSONDecodeError:
|
|
374
|
+
log.warning(f"Could not parse file_metadata for project {project_id}, ignoring.")
|
|
375
|
+
pass
|
|
376
|
+
|
|
377
|
+
results = await project_service.add_artifacts_to_project(
|
|
378
|
+
db=db,
|
|
379
|
+
project_id=project_id,
|
|
380
|
+
user_id=user_id,
|
|
381
|
+
files=files,
|
|
382
|
+
file_metadata=parsed_file_metadata
|
|
383
|
+
)
|
|
384
|
+
return results
|
|
385
|
+
except ValueError as e:
|
|
386
|
+
error_msg = str(e)
|
|
387
|
+
log.warning(f"Validation error adding artifacts to project {project_id}: {error_msg}")
|
|
388
|
+
# Could be 404 if project not found, 413 if file too large, or 400 if other validation fails
|
|
389
|
+
if "not found" in error_msg.lower():
|
|
390
|
+
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=error_msg)
|
|
391
|
+
if "exceeds maximum" in error_msg.lower() and "bytes" in error_msg.lower():
|
|
392
|
+
raise HTTPException(status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, detail=error_msg)
|
|
393
|
+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=error_msg)
|
|
394
|
+
except Exception as e:
|
|
395
|
+
log.error(
|
|
396
|
+
"Error adding artifacts to project %s for user %s: %s",
|
|
397
|
+
project_id,
|
|
398
|
+
user_id,
|
|
399
|
+
e,
|
|
400
|
+
)
|
|
401
|
+
raise HTTPException(
|
|
402
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
403
|
+
detail="Failed to add artifacts to project"
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
@router.patch("/projects/{project_id}/artifacts/{filename}", status_code=status.HTTP_200_OK)
|
|
408
|
+
async def update_project_artifact_metadata(
|
|
409
|
+
project_id: str,
|
|
410
|
+
filename: str,
|
|
411
|
+
description: Optional[str] = Form(None),
|
|
412
|
+
user: dict = Depends(get_current_user),
|
|
413
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
414
|
+
db: Session = Depends(get_db),
|
|
415
|
+
_: None = Depends(check_projects_enabled),
|
|
416
|
+
):
|
|
417
|
+
"""
|
|
418
|
+
Update metadata (description) for a project artifact.
|
|
419
|
+
"""
|
|
420
|
+
user_id = user.get("id")
|
|
421
|
+
log.info(f"User {user_id} attempting to update metadata for artifact '{filename}' in project {project_id}")
|
|
422
|
+
|
|
423
|
+
try:
|
|
424
|
+
success = await project_service.update_artifact_metadata(
|
|
425
|
+
db=db,
|
|
426
|
+
project_id=project_id,
|
|
427
|
+
user_id=user_id,
|
|
428
|
+
filename=filename,
|
|
429
|
+
description=description,
|
|
430
|
+
)
|
|
431
|
+
if not success:
|
|
432
|
+
raise HTTPException(
|
|
433
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
434
|
+
detail="Project or artifact not found, or access denied."
|
|
435
|
+
)
|
|
436
|
+
|
|
437
|
+
return {"message": "Artifact metadata updated successfully"}
|
|
438
|
+
except ValueError as e:
|
|
439
|
+
log.warning(f"Validation error updating artifact metadata in project {project_id}: {e}")
|
|
440
|
+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
441
|
+
except HTTPException:
|
|
442
|
+
raise
|
|
443
|
+
except Exception as e:
|
|
444
|
+
log.error(
|
|
445
|
+
"Error updating metadata for artifact '%s' in project %s for user %s: %s",
|
|
446
|
+
filename,
|
|
447
|
+
project_id,
|
|
448
|
+
user_id,
|
|
449
|
+
e,
|
|
450
|
+
)
|
|
451
|
+
raise HTTPException(
|
|
452
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
453
|
+
detail="Failed to update artifact metadata"
|
|
454
|
+
)
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
@router.delete("/projects/{project_id}/artifacts/{filename}", status_code=status.HTTP_204_NO_CONTENT)
|
|
458
|
+
async def delete_project_artifact(
|
|
459
|
+
project_id: str,
|
|
460
|
+
filename: str,
|
|
461
|
+
user: dict = Depends(get_current_user),
|
|
462
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
463
|
+
db: Session = Depends(get_db),
|
|
464
|
+
_: None = Depends(check_projects_enabled),
|
|
465
|
+
):
|
|
466
|
+
"""
|
|
467
|
+
Delete an artifact from a project.
|
|
468
|
+
"""
|
|
469
|
+
user_id = user.get("id")
|
|
470
|
+
log.info(f"User {user_id} attempting to delete artifact '{filename}' from project {project_id}")
|
|
471
|
+
|
|
472
|
+
try:
|
|
473
|
+
success = await project_service.delete_artifact_from_project(
|
|
474
|
+
db=db,
|
|
475
|
+
project_id=project_id,
|
|
476
|
+
user_id=user_id,
|
|
477
|
+
filename=filename,
|
|
478
|
+
)
|
|
479
|
+
if not success:
|
|
480
|
+
raise HTTPException(
|
|
481
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
482
|
+
detail="Project not found or access denied."
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
return
|
|
486
|
+
except ValueError as e:
|
|
487
|
+
log.warning(f"Validation error deleting artifact from project {project_id}: {e}")
|
|
488
|
+
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e))
|
|
489
|
+
except HTTPException:
|
|
490
|
+
raise
|
|
491
|
+
except Exception as e:
|
|
492
|
+
log.error(
|
|
493
|
+
"Error deleting artifact '%s' from project %s for user %s: %s",
|
|
494
|
+
filename,
|
|
495
|
+
project_id,
|
|
496
|
+
user_id,
|
|
497
|
+
e,
|
|
498
|
+
)
|
|
499
|
+
raise HTTPException(
|
|
500
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
501
|
+
detail="Failed to delete artifact from project"
|
|
502
|
+
)
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
@router.put("/projects/{project_id}", response_model=ProjectResponse)
|
|
506
|
+
async def update_project(
|
|
507
|
+
project_id: str,
|
|
508
|
+
request: UpdateProjectRequest,
|
|
509
|
+
user: dict = Depends(get_current_user),
|
|
510
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
511
|
+
db: Session = Depends(get_db),
|
|
512
|
+
_: None = Depends(check_projects_enabled),
|
|
513
|
+
):
|
|
514
|
+
"""
|
|
515
|
+
Update a project's details.
|
|
516
|
+
"""
|
|
517
|
+
user_id = user.get("id")
|
|
518
|
+
log.info("User %s attempting to update project %s", user_id, project_id)
|
|
519
|
+
|
|
520
|
+
try:
|
|
521
|
+
if (
|
|
522
|
+
not project_id
|
|
523
|
+
or project_id.strip() == ""
|
|
524
|
+
or project_id in ["null", "undefined"]
|
|
525
|
+
):
|
|
526
|
+
raise HTTPException(
|
|
527
|
+
status_code=status.HTTP_404_NOT_FOUND, detail="Project not found."
|
|
528
|
+
)
|
|
529
|
+
|
|
530
|
+
update_fields = request.model_dump(exclude_unset=True, by_alias=False)
|
|
531
|
+
|
|
532
|
+
# Pass only explicitly set fields to the service
|
|
533
|
+
kwargs = {
|
|
534
|
+
'db': db,
|
|
535
|
+
'project_id': project_id,
|
|
536
|
+
'user_id': user_id,
|
|
537
|
+
'name': update_fields.get('name', ...),
|
|
538
|
+
'description': update_fields.get('description', ...),
|
|
539
|
+
'system_prompt': update_fields.get('system_prompt', ...),
|
|
540
|
+
'default_agent_id': update_fields.get('default_agent_id', ...),
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
project = project_service.update_project(**kwargs)
|
|
544
|
+
|
|
545
|
+
if not project:
|
|
546
|
+
raise HTTPException(
|
|
547
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
548
|
+
detail="Project not found."
|
|
549
|
+
)
|
|
550
|
+
|
|
551
|
+
log.info("Project %s updated successfully", project_id)
|
|
552
|
+
|
|
553
|
+
return ProjectResponse(
|
|
554
|
+
id=project.id,
|
|
555
|
+
name=project.name,
|
|
556
|
+
user_id=project.user_id,
|
|
557
|
+
description=project.description,
|
|
558
|
+
system_prompt=project.system_prompt,
|
|
559
|
+
default_agent_id=project.default_agent_id,
|
|
560
|
+
created_at=project.created_at,
|
|
561
|
+
updated_at=project.updated_at,
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
except HTTPException:
|
|
565
|
+
raise
|
|
566
|
+
except ValueError as e:
|
|
567
|
+
log.warning("Validation error updating project %s: %s", project_id, e)
|
|
568
|
+
raise HTTPException(
|
|
569
|
+
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=str(e)
|
|
570
|
+
)
|
|
571
|
+
except Exception as e:
|
|
572
|
+
log.error(
|
|
573
|
+
"Error updating project %s for user %s: %s",
|
|
574
|
+
project_id,
|
|
575
|
+
user_id,
|
|
576
|
+
e,
|
|
577
|
+
)
|
|
578
|
+
raise HTTPException(
|
|
579
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
580
|
+
detail="Failed to update project"
|
|
581
|
+
)
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
@router.delete("/projects/{project_id}", status_code=status.HTTP_204_NO_CONTENT)
|
|
585
|
+
async def delete_project(
|
|
586
|
+
project_id: str,
|
|
587
|
+
user: dict = Depends(get_current_user),
|
|
588
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
589
|
+
db: Session = Depends(get_db),
|
|
590
|
+
_: None = Depends(check_projects_enabled),
|
|
591
|
+
):
|
|
592
|
+
"""
|
|
593
|
+
Soft delete a project (marks as deleted without removing from database).
|
|
594
|
+
"""
|
|
595
|
+
user_id = user.get("id")
|
|
596
|
+
log.info("User %s attempting to soft delete project %s", user_id, project_id)
|
|
597
|
+
|
|
598
|
+
try:
|
|
599
|
+
request_dto = DeleteProjectRequest(project_id=project_id, user_id=user_id)
|
|
600
|
+
|
|
601
|
+
success = project_service.soft_delete_project(
|
|
602
|
+
db=db,
|
|
603
|
+
project_id=request_dto.project_id,
|
|
604
|
+
user_id=request_dto.user_id
|
|
605
|
+
)
|
|
606
|
+
|
|
607
|
+
if not success:
|
|
608
|
+
raise HTTPException(
|
|
609
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
610
|
+
detail="Project not found."
|
|
611
|
+
)
|
|
612
|
+
|
|
613
|
+
log.info("Project %s soft deleted successfully", project_id)
|
|
614
|
+
|
|
615
|
+
except HTTPException:
|
|
616
|
+
raise
|
|
617
|
+
except ValueError as e:
|
|
618
|
+
log.warning("Validation error deleting project %s: %s", project_id, e)
|
|
619
|
+
raise HTTPException(
|
|
620
|
+
status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)
|
|
621
|
+
)
|
|
622
|
+
except Exception as e:
|
|
623
|
+
log.error(
|
|
624
|
+
"Error deleting project %s for user %s: %s",
|
|
625
|
+
project_id,
|
|
626
|
+
user_id,
|
|
627
|
+
e,
|
|
628
|
+
)
|
|
629
|
+
raise HTTPException(
|
|
630
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
631
|
+
detail="Failed to delete project"
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
@router.get("/projects/{project_id}/export")
|
|
636
|
+
async def export_project(
|
|
637
|
+
project_id: str,
|
|
638
|
+
user: dict = Depends(get_current_user),
|
|
639
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
640
|
+
db: Session = Depends(get_db),
|
|
641
|
+
_: None = Depends(check_projects_enabled),
|
|
642
|
+
):
|
|
643
|
+
"""
|
|
644
|
+
Export project as ZIP containing:
|
|
645
|
+
- project.json (metadata)
|
|
646
|
+
- artifacts/ (all project files)
|
|
647
|
+
|
|
648
|
+
Excludes: chat history, sessions
|
|
649
|
+
"""
|
|
650
|
+
user_id = user.get("id")
|
|
651
|
+
log.info(f"User {user_id} exporting project {project_id}")
|
|
652
|
+
|
|
653
|
+
try:
|
|
654
|
+
# Create ZIP file
|
|
655
|
+
zip_buffer = await project_service.export_project_as_zip(
|
|
656
|
+
db=db,
|
|
657
|
+
project_id=project_id,
|
|
658
|
+
user_id=user_id
|
|
659
|
+
)
|
|
660
|
+
|
|
661
|
+
# Get project for filename
|
|
662
|
+
project = project_service.get_project(db, project_id, user_id)
|
|
663
|
+
if not project:
|
|
664
|
+
raise HTTPException(
|
|
665
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
666
|
+
detail="Project not found"
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
# Create safe filename
|
|
670
|
+
safe_name = project.name.replace(' ', '-').replace('/', '-')
|
|
671
|
+
filename = f"project-{safe_name}-{project_id[:8]}.zip"
|
|
672
|
+
|
|
673
|
+
log.info(f"Project {project_id} exported successfully")
|
|
674
|
+
|
|
675
|
+
return StreamingResponse(
|
|
676
|
+
zip_buffer,
|
|
677
|
+
media_type="application/zip",
|
|
678
|
+
headers={
|
|
679
|
+
"Content-Disposition": f'attachment; filename="{filename}"'
|
|
680
|
+
}
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
except ValueError as e:
|
|
684
|
+
log.warning(f"Validation error exporting project {project_id}: {e}")
|
|
685
|
+
raise HTTPException(
|
|
686
|
+
status_code=status.HTTP_404_NOT_FOUND,
|
|
687
|
+
detail=str(e)
|
|
688
|
+
)
|
|
689
|
+
except Exception as e:
|
|
690
|
+
log.error(f"Error exporting project {project_id}: {e}")
|
|
691
|
+
raise HTTPException(
|
|
692
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
693
|
+
detail="Failed to export project"
|
|
694
|
+
)
|
|
695
|
+
|
|
696
|
+
|
|
697
|
+
@router.post("/projects/import", response_model=ProjectImportResponse)
|
|
698
|
+
async def import_project(
|
|
699
|
+
file: UploadFile = File(...),
|
|
700
|
+
options: Optional[str] = Form(None),
|
|
701
|
+
user: dict = Depends(get_current_user),
|
|
702
|
+
project_service: ProjectService = Depends(get_project_service),
|
|
703
|
+
db: Session = Depends(get_db),
|
|
704
|
+
_: None = Depends(check_projects_enabled),
|
|
705
|
+
):
|
|
706
|
+
"""
|
|
707
|
+
Import project from ZIP file.
|
|
708
|
+
Handles name conflicts automatically.
|
|
709
|
+
"""
|
|
710
|
+
user_id = user.get("id")
|
|
711
|
+
log.info(f"User {user_id} importing project from {file.filename}")
|
|
712
|
+
|
|
713
|
+
try:
|
|
714
|
+
# Validate file type
|
|
715
|
+
if not file.filename.endswith('.zip'):
|
|
716
|
+
raise HTTPException(
|
|
717
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
718
|
+
detail="File must be a ZIP archive"
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
# Parse options
|
|
722
|
+
import_options = ProjectImportOptions()
|
|
723
|
+
if options:
|
|
724
|
+
try:
|
|
725
|
+
options_dict = json.loads(options)
|
|
726
|
+
import_options = ProjectImportOptions(**options_dict)
|
|
727
|
+
except (json.JSONDecodeError, ValueError) as e:
|
|
728
|
+
log.warning(f"Invalid import options: {e}")
|
|
729
|
+
|
|
730
|
+
# Import project
|
|
731
|
+
project, artifacts_count, warnings = await project_service.import_project_from_zip(
|
|
732
|
+
db=db,
|
|
733
|
+
zip_file=file,
|
|
734
|
+
user_id=user_id,
|
|
735
|
+
preserve_name=import_options.preserve_name,
|
|
736
|
+
custom_name=import_options.custom_name,
|
|
737
|
+
)
|
|
738
|
+
|
|
739
|
+
log.info(
|
|
740
|
+
f"Project imported successfully: {project.id} with {artifacts_count} artifacts"
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
return ProjectImportResponse(
|
|
744
|
+
project_id=project.id,
|
|
745
|
+
name=project.name,
|
|
746
|
+
artifacts_imported=artifacts_count,
|
|
747
|
+
warnings=warnings,
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
except ValueError as e:
|
|
751
|
+
error_msg = str(e)
|
|
752
|
+
log.warning(f"Validation error importing project: {error_msg}")
|
|
753
|
+
# Check if this is a file size error
|
|
754
|
+
if "exceeds maximum" in error_msg.lower():
|
|
755
|
+
raise HTTPException(
|
|
756
|
+
status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
|
|
757
|
+
detail=error_msg
|
|
758
|
+
)
|
|
759
|
+
raise HTTPException(
|
|
760
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
761
|
+
detail=error_msg
|
|
762
|
+
)
|
|
763
|
+
except Exception as e:
|
|
764
|
+
log.error(f"Error importing project: {e}")
|
|
765
|
+
raise HTTPException(
|
|
766
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
767
|
+
detail="Failed to import project"
|
|
768
|
+
)
|