solace-agent-mesh 1.5.1__py3-none-any.whl → 1.6.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/callbacks.py +0 -5
- solace_agent_mesh/agent/adk/models/lite_llm.py +123 -8
- solace_agent_mesh/agent/adk/models/oauth2_token_manager.py +245 -0
- solace_agent_mesh/agent/protocol/event_handlers.py +40 -1
- solace_agent_mesh/agent/proxies/__init__.py +0 -0
- solace_agent_mesh/agent/proxies/a2a/__init__.py +3 -0
- solace_agent_mesh/agent/proxies/a2a/app.py +55 -0
- solace_agent_mesh/agent/proxies/a2a/component.py +1115 -0
- solace_agent_mesh/agent/proxies/a2a/config.py +140 -0
- solace_agent_mesh/agent/proxies/a2a/oauth_token_cache.py +104 -0
- solace_agent_mesh/agent/proxies/base/__init__.py +3 -0
- solace_agent_mesh/agent/proxies/base/app.py +99 -0
- solace_agent_mesh/agent/proxies/base/component.py +619 -0
- solace_agent_mesh/agent/proxies/base/config.py +85 -0
- solace_agent_mesh/agent/proxies/base/proxy_task_context.py +17 -0
- solace_agent_mesh/agent/sac/app.py +9 -3
- solace_agent_mesh/agent/sac/component.py +160 -8
- solace_agent_mesh/agent/tools/audio_tools.py +125 -8
- solace_agent_mesh/agent/tools/web_tools.py +10 -5
- solace_agent_mesh/agent/utils/artifact_helpers.py +141 -3
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.eda4bcb2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.f4b15f3b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/71da7b71.38583438.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/77cf947d.48cb18a2.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/924ffdeb.8095e148.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/9e9d0a82.570c057b.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{ad71b5ed.60668e9e.js → ad71b5ed.af3ecfd1.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/ceb2a7a6.5d92d7d0.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{da0b5bad.9d369087.js → da0b5bad.d08a9466.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/db924877.e98d12a1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/de915948.27d6b065.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.e74a984d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.42f59cdd.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.15b02f97.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js → main.20feee82.js} +2 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.0d198646.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/components/agents/index.html +15 -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 +4 -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 +4 -4
- 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/proxies/index.html +262 -0
- solace_agent_mesh/assets/docs/docs/documentation/deploying/debugging/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deploying/deployment-options/index.html +31 -3
- solace_agent_mesh/assets/docs/docs/documentation/deploying/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/deploying/observability/index.html +3 -3
- 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 +5 -5
- 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 +135 -0
- solace_agent_mesh/assets/docs/docs/documentation/developing/index.html +6 -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 +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/developing/tutorials/event-mesh-gateway/index.html +5 -5
- 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/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/installation/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/rbac-setup-guide/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/enterprise/single-sign-on/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/architecture/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/index.html +3 -3
- 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/configurations/index.html +6 -5
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/installation/index.html +3 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/large_language_models/index.html +100 -3
- solace_agent_mesh/assets/docs/docs/documentation/installing-and-configuring/run-project/index.html +3 -3
- 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-1761165361160.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1761165361160.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/agent_cmd.py +2 -69
- solace_agent_mesh/cli/commands/eval_cmd.py +11 -49
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +0 -5
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +10 -12
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +9 -61
- solace_agent_mesh/cli/commands/init_cmd/webui_gateway_step.py +9 -49
- solace_agent_mesh/cli/commands/plugin_cmd/add_cmd.py +1 -2
- solace_agent_mesh/client/webui/frontend/static/assets/{authCallback-DwrxZE0E.js → authCallback-BTf6dqwp.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/{client-DarGQzyw.js → client-CaY59VuC.js} +1 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-BGTaW0uv.js +342 -0
- solace_agent_mesh/client/webui/frontend/static/assets/main-DHJKSW1S.css +1 -0
- solace_agent_mesh/client/webui/frontend/static/assets/{vendor-BKIeiHj_.js → vendor-BEmvJSYz.js} +1 -1
- 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/__init__.py +24 -0
- solace_agent_mesh/common/a2a/artifact.py +39 -0
- solace_agent_mesh/common/a2a/events.py +29 -0
- solace_agent_mesh/common/a2a/message.py +68 -0
- solace_agent_mesh/common/a2a/protocol.py +73 -1
- solace_agent_mesh/common/agent_registry.py +83 -3
- solace_agent_mesh/common/constants.py +3 -1
- solace_agent_mesh/common/utils/pydantic_utils.py +12 -0
- solace_agent_mesh/config_portal/backend/common.py +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-ByU1X1HD.js +98 -0
- solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-44d62be6.js → manifest-61038fc6.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
- solace_agent_mesh/evaluation/evaluator.py +128 -104
- solace_agent_mesh/evaluation/message_organizer.py +116 -110
- solace_agent_mesh/evaluation/report_data_processor.py +84 -86
- solace_agent_mesh/evaluation/report_generator.py +73 -79
- solace_agent_mesh/evaluation/run.py +421 -235
- solace_agent_mesh/evaluation/shared/__init__.py +92 -0
- solace_agent_mesh/evaluation/shared/constants.py +47 -0
- solace_agent_mesh/evaluation/shared/exceptions.py +50 -0
- solace_agent_mesh/evaluation/shared/helpers.py +35 -0
- solace_agent_mesh/evaluation/shared/test_case_loader.py +167 -0
- solace_agent_mesh/evaluation/shared/test_suite_loader.py +280 -0
- solace_agent_mesh/evaluation/subscriber.py +111 -232
- solace_agent_mesh/evaluation/summary_builder.py +227 -117
- solace_agent_mesh/gateway/base/app.py +1 -1
- solace_agent_mesh/gateway/base/component.py +8 -1
- solace_agent_mesh/gateway/http_sse/alembic/versions/20251015_add_session_performance_indexes.py +70 -0
- solace_agent_mesh/gateway/http_sse/component.py +98 -2
- solace_agent_mesh/gateway/http_sse/dependencies.py +4 -4
- solace_agent_mesh/gateway/http_sse/main.py +2 -1
- solace_agent_mesh/gateway/http_sse/repository/chat_task_repository.py +12 -13
- solace_agent_mesh/gateway/http_sse/repository/feedback_repository.py +15 -18
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +25 -18
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +30 -26
- solace_agent_mesh/gateway/http_sse/repository/task_repository.py +35 -44
- solace_agent_mesh/gateway/http_sse/routers/agent_cards.py +4 -3
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +95 -203
- solace_agent_mesh/gateway/http_sse/routers/dto/responses/session_responses.py +4 -3
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +2 -2
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +33 -41
- solace_agent_mesh/gateway/http_sse/routers/visualization.py +17 -11
- solace_agent_mesh/gateway/http_sse/services/data_retention_service.py +4 -4
- solace_agent_mesh/gateway/http_sse/services/feedback_service.py +51 -43
- solace_agent_mesh/gateway/http_sse/services/session_service.py +20 -20
- solace_agent_mesh/gateway/http_sse/services/task_logger_service.py +8 -8
- solace_agent_mesh/gateway/http_sse/shared/base_repository.py +45 -71
- solace_agent_mesh/gateway/http_sse/shared/types.py +0 -18
- solace_agent_mesh/templates/gateway_config_template.yaml +0 -5
- solace_agent_mesh/templates/logging_config_template.ini +10 -6
- solace_agent_mesh/templates/plugin_gateway_config_template.yaml +0 -3
- solace_agent_mesh/templates/shared_config.yaml +40 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/METADATA +47 -21
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/RECORD +162 -141
- solace_agent_mesh/assets/docs/assets/js/5c2bd65f.e49689dd.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/6ad8f0bd.39d5851d.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/71da7b71.804d6567.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/77cf947d.64c9bd6c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/9e9d0a82.dd810042.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/db924877.cbc66f02.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/de915948.139b4b9c.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/e6f9706b.582a78ca.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.5766a13d.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/ff4d71f2.9c0297a6.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/runtime~main.18dc45dd.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1760121512891.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1760121512891.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-2nd1gbaH.js +0 -339
- solace_agent_mesh/client/webui/frontend/static/assets/main-DoKXctCM.css +0 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/_index-BNuqpWDc.js +0 -98
- solace_agent_mesh/evaluation/config_loader.py +0 -657
- solace_agent_mesh/evaluation/test_case_loader.py +0 -714
- /solace_agent_mesh/assets/docs/assets/js/{main.bd3c34f3.js.LICENSE.txt → main.20feee82.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.5.1.dist-info → solace_agent_mesh-1.6.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -45,32 +45,32 @@ from ..dependencies import (
|
|
|
45
45
|
get_sac_component,
|
|
46
46
|
get_session_validator,
|
|
47
47
|
get_shared_artifact_service,
|
|
48
|
-
get_user_config,
|
|
49
48
|
get_user_id,
|
|
50
49
|
get_session_manager,
|
|
51
50
|
get_session_business_service_optional,
|
|
52
51
|
get_db_optional,
|
|
53
52
|
)
|
|
54
53
|
|
|
55
|
-
if TYPE_CHECKING:
|
|
56
|
-
from ....gateway.http_sse.component import WebUIBackendComponent
|
|
57
54
|
|
|
58
55
|
from ..session_manager import SessionManager
|
|
59
56
|
from ..services.session_service import SessionService
|
|
60
57
|
from sqlalchemy.orm import Session
|
|
61
58
|
|
|
62
59
|
from ....agent.utils.artifact_helpers import (
|
|
63
|
-
DEFAULT_SCHEMA_MAX_KEYS,
|
|
64
|
-
format_artifact_uri,
|
|
65
60
|
get_artifact_info_list,
|
|
66
61
|
load_artifact_content_or_metadata,
|
|
67
|
-
|
|
62
|
+
process_artifact_upload,
|
|
68
63
|
)
|
|
69
64
|
|
|
65
|
+
if TYPE_CHECKING:
|
|
66
|
+
from ....gateway.http_sse.component import WebUIBackendComponent
|
|
67
|
+
|
|
70
68
|
log = logging.getLogger(__name__)
|
|
71
69
|
|
|
70
|
+
|
|
72
71
|
class ArtifactUploadResponse(BaseModel):
|
|
73
72
|
"""Response model for artifact upload with camelCase fields."""
|
|
73
|
+
|
|
74
74
|
uri: str
|
|
75
75
|
session_id: str = Field(..., alias="sessionId")
|
|
76
76
|
filename: str
|
|
@@ -95,7 +95,11 @@ router = APIRouter()
|
|
|
95
95
|
async def upload_artifact_with_session(
|
|
96
96
|
request: FastAPIRequest,
|
|
97
97
|
upload_file: UploadFile = File(..., description="The file content to upload"),
|
|
98
|
-
sessionId: str | None = Form(
|
|
98
|
+
sessionId: str | None = Form(
|
|
99
|
+
None,
|
|
100
|
+
description="Session ID (null/empty to create new session)",
|
|
101
|
+
alias="sessionId",
|
|
102
|
+
),
|
|
99
103
|
filename: str = Form(..., description="The name of the artifact to create/update"),
|
|
100
104
|
metadata_json: str | None = Form(
|
|
101
105
|
None, description="JSON string of artifact metadata (e.g., description, source)"
|
|
@@ -106,7 +110,9 @@ async def upload_artifact_with_session(
|
|
|
106
110
|
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
107
111
|
user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:create"])),
|
|
108
112
|
session_manager: SessionManager = Depends(get_session_manager),
|
|
109
|
-
session_service: SessionService | None = Depends(
|
|
113
|
+
session_service: SessionService | None = Depends(
|
|
114
|
+
get_session_business_service_optional
|
|
115
|
+
),
|
|
110
116
|
db: Session | None = Depends(get_db_optional),
|
|
111
117
|
):
|
|
112
118
|
"""
|
|
@@ -129,7 +135,11 @@ async def upload_artifact_with_session(
|
|
|
129
135
|
else:
|
|
130
136
|
# Create new session when no sessionId provided (like chat does for new conversations)
|
|
131
137
|
effective_session_id = session_manager.create_new_session_id(request)
|
|
132
|
-
log.info(
|
|
138
|
+
log.info(
|
|
139
|
+
"%sCreated new session for file upload: %s",
|
|
140
|
+
log_prefix,
|
|
141
|
+
effective_session_id,
|
|
142
|
+
)
|
|
133
143
|
|
|
134
144
|
# Persist session in database if persistence is available (matching chat pattern)
|
|
135
145
|
if session_service and db:
|
|
@@ -139,17 +149,27 @@ async def upload_artifact_with_session(
|
|
|
139
149
|
user_id=user_id,
|
|
140
150
|
session_id=effective_session_id,
|
|
141
151
|
agent_id=None, # Will be determined when first message is sent
|
|
142
|
-
name=None,
|
|
152
|
+
name=None, # Will be set when first message is sent
|
|
143
153
|
)
|
|
144
154
|
db.commit()
|
|
145
|
-
log.info(
|
|
155
|
+
log.info(
|
|
156
|
+
"%sSession created and committed to database: %s",
|
|
157
|
+
log_prefix,
|
|
158
|
+
effective_session_id,
|
|
159
|
+
)
|
|
146
160
|
except Exception as session_error:
|
|
147
161
|
db.rollback()
|
|
148
|
-
log.warning(
|
|
149
|
-
|
|
162
|
+
log.warning(
|
|
163
|
+
"%sSession persistence failed, continuing with in-memory session: %s",
|
|
164
|
+
log_prefix,
|
|
165
|
+
session_error,
|
|
166
|
+
)
|
|
150
167
|
else:
|
|
151
|
-
log.debug(
|
|
152
|
-
|
|
168
|
+
log.debug(
|
|
169
|
+
"%sNo persistence available - using in-memory session: %s",
|
|
170
|
+
log_prefix,
|
|
171
|
+
effective_session_id,
|
|
172
|
+
)
|
|
153
173
|
|
|
154
174
|
# Validate inputs
|
|
155
175
|
if not filename or not filename.strip():
|
|
@@ -174,56 +194,83 @@ async def upload_artifact_with_session(
|
|
|
174
194
|
|
|
175
195
|
# Validate session (now that we have an effective_session_id)
|
|
176
196
|
if not validate_session(effective_session_id, user_id):
|
|
177
|
-
log.warning(
|
|
197
|
+
log.warning(
|
|
198
|
+
"%sSession validation failed for session: %s",
|
|
199
|
+
log_prefix,
|
|
200
|
+
effective_session_id,
|
|
201
|
+
)
|
|
178
202
|
raise HTTPException(
|
|
179
203
|
status_code=status.HTTP_403_FORBIDDEN,
|
|
180
204
|
detail="Invalid session or insufficient permissions.",
|
|
181
205
|
)
|
|
182
206
|
|
|
183
|
-
log.info(
|
|
207
|
+
log.info(
|
|
208
|
+
"%sUploading file '%s' to session '%s'",
|
|
209
|
+
log_prefix,
|
|
210
|
+
filename.strip(),
|
|
211
|
+
effective_session_id,
|
|
212
|
+
)
|
|
184
213
|
|
|
185
214
|
try:
|
|
186
215
|
# Read and validate file content
|
|
187
216
|
content_bytes = await upload_file.read()
|
|
188
|
-
if not content_bytes:
|
|
189
|
-
raise HTTPException(
|
|
190
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
191
|
-
detail="File is empty.",
|
|
192
|
-
)
|
|
193
217
|
|
|
194
218
|
mime_type = upload_file.content_type or "application/octet-stream"
|
|
195
219
|
filename_clean = filename.strip()
|
|
196
220
|
|
|
197
|
-
log.debug(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if not isinstance(metadata, dict):
|
|
205
|
-
raise ValueError("Metadata must be a JSON object")
|
|
206
|
-
except (json.JSONDecodeError, ValueError) as e:
|
|
207
|
-
log.warning("%sInvalid metadata JSON: %s", log_prefix, e)
|
|
208
|
-
raise HTTPException(
|
|
209
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
210
|
-
detail=f"Invalid JSON in metadata field: {str(e)}",
|
|
211
|
-
)
|
|
212
|
-
|
|
213
|
-
app_name = component.get_config("name", "A2A_WebUI_App")
|
|
221
|
+
log.debug(
|
|
222
|
+
"%sProcessing file: %s (%d bytes, %s)",
|
|
223
|
+
log_prefix,
|
|
224
|
+
filename_clean,
|
|
225
|
+
len(content_bytes),
|
|
226
|
+
mime_type,
|
|
227
|
+
)
|
|
214
228
|
|
|
215
|
-
#
|
|
216
|
-
|
|
217
|
-
|
|
229
|
+
# Use the common upload helper
|
|
230
|
+
upload_result = await process_artifact_upload(
|
|
231
|
+
artifact_service=artifact_service,
|
|
232
|
+
component=component,
|
|
218
233
|
user_id=user_id,
|
|
219
234
|
session_id=effective_session_id,
|
|
220
235
|
filename=filename_clean,
|
|
221
236
|
content_bytes=content_bytes,
|
|
222
237
|
mime_type=mime_type,
|
|
223
|
-
|
|
238
|
+
metadata_json=metadata_json,
|
|
239
|
+
log_prefix=log_prefix,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
if upload_result["status"] != "success":
|
|
243
|
+
error_msg = upload_result.get("message", "Failed to upload artifact")
|
|
244
|
+
error_type = upload_result.get("error", "unknown")
|
|
245
|
+
|
|
246
|
+
if error_type in ["invalid_filename", "empty_file"]:
|
|
247
|
+
status_code = status.HTTP_400_BAD_REQUEST
|
|
248
|
+
else:
|
|
249
|
+
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
250
|
+
|
|
251
|
+
log.error("%s%s", log_prefix, error_msg)
|
|
252
|
+
raise HTTPException(status_code=status_code, detail=error_msg)
|
|
253
|
+
|
|
254
|
+
artifact_uri = upload_result["artifact_uri"]
|
|
255
|
+
saved_version = upload_result["version"]
|
|
256
|
+
|
|
257
|
+
log.info(
|
|
258
|
+
"%sArtifact stored successfully: %s (%d bytes), version: %s",
|
|
259
|
+
log_prefix,
|
|
260
|
+
artifact_uri,
|
|
261
|
+
len(content_bytes),
|
|
262
|
+
saved_version,
|
|
224
263
|
)
|
|
225
264
|
|
|
226
|
-
|
|
265
|
+
# Get metadata from upload result (it was already parsed and validated)
|
|
266
|
+
metadata_dict = {}
|
|
267
|
+
if metadata_json and metadata_json.strip():
|
|
268
|
+
try:
|
|
269
|
+
metadata_dict = json.loads(metadata_json.strip())
|
|
270
|
+
if not isinstance(metadata_dict, dict):
|
|
271
|
+
metadata_dict = {}
|
|
272
|
+
except json.JSONDecodeError:
|
|
273
|
+
metadata_dict = {}
|
|
227
274
|
|
|
228
275
|
# Return standardized response using Pydantic model (ensures camelCase conversion)
|
|
229
276
|
return ArtifactUploadResponse(
|
|
@@ -232,8 +279,10 @@ async def upload_artifact_with_session(
|
|
|
232
279
|
filename=filename_clean,
|
|
233
280
|
size=len(content_bytes),
|
|
234
281
|
mime_type=mime_type, # Will be returned as "mimeType" due to alias
|
|
235
|
-
metadata=
|
|
236
|
-
created_at=datetime.now(
|
|
282
|
+
metadata=metadata_dict,
|
|
283
|
+
created_at=datetime.now(
|
|
284
|
+
timezone.utc
|
|
285
|
+
).isoformat(), # Will be returned as "createdAt" due to alias
|
|
237
286
|
)
|
|
238
287
|
|
|
239
288
|
except HTTPException:
|
|
@@ -785,7 +834,6 @@ async def get_artifact_by_uri(
|
|
|
785
834
|
version,
|
|
786
835
|
)
|
|
787
836
|
|
|
788
|
-
|
|
789
837
|
log.info(
|
|
790
838
|
"%s User '%s' authorized to access artifact URI.",
|
|
791
839
|
log_id_prefix,
|
|
@@ -828,162 +876,6 @@ async def get_artifact_by_uri(
|
|
|
828
876
|
)
|
|
829
877
|
|
|
830
878
|
|
|
831
|
-
@router.post(
|
|
832
|
-
"/{session_id}/{filename}",
|
|
833
|
-
status_code=status.HTTP_201_CREATED,
|
|
834
|
-
response_model=dict[str, Any],
|
|
835
|
-
summary="Upload Artifact (Create/Update Version with Metadata)",
|
|
836
|
-
description="Uploads file content and optional metadata to create or update an artifact version.",
|
|
837
|
-
)
|
|
838
|
-
async def upload_artifact(
|
|
839
|
-
session_id: str = Path(
|
|
840
|
-
..., title="Session ID", description="The session ID to upload artifacts to"
|
|
841
|
-
),
|
|
842
|
-
filename: str = Path(
|
|
843
|
-
..., title="Filename", description="The name of the artifact to create/update"
|
|
844
|
-
),
|
|
845
|
-
upload_file: UploadFile = File(..., description="The file content to upload"),
|
|
846
|
-
metadata_json: str | None = Form(
|
|
847
|
-
None, description="JSON string of artifact metadata (e.g., description, source)"
|
|
848
|
-
),
|
|
849
|
-
artifact_service: BaseArtifactService = Depends(get_shared_artifact_service),
|
|
850
|
-
user_id: str = Depends(get_user_id),
|
|
851
|
-
validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
|
|
852
|
-
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
853
|
-
user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:create"])),
|
|
854
|
-
):
|
|
855
|
-
"""
|
|
856
|
-
Uploads a file to create a new version of the specified artifact
|
|
857
|
-
associated with the current user and session ID. Also saves associated metadata.
|
|
858
|
-
"""
|
|
859
|
-
log_prefix = (
|
|
860
|
-
f"[ArtifactRouter:Post:{filename}] User={user_id}, Session={session_id} -"
|
|
861
|
-
)
|
|
862
|
-
log.info(
|
|
863
|
-
"%s Request received. Upload filename: '%s', content type: %s",
|
|
864
|
-
log_prefix,
|
|
865
|
-
upload_file.filename,
|
|
866
|
-
upload_file.content_type,
|
|
867
|
-
)
|
|
868
|
-
|
|
869
|
-
# Validate session exists and belongs to user
|
|
870
|
-
if not validate_session(session_id, user_id):
|
|
871
|
-
log.warning("%s Session validation failed or access denied.", log_prefix)
|
|
872
|
-
raise HTTPException(
|
|
873
|
-
status_code=status.HTTP_404_NOT_FOUND,
|
|
874
|
-
detail="Session not found or access denied.",
|
|
875
|
-
)
|
|
876
|
-
|
|
877
|
-
if artifact_service is None:
|
|
878
|
-
log.error("%s Artifact service is not configured or available.", log_prefix)
|
|
879
|
-
raise HTTPException(
|
|
880
|
-
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
881
|
-
detail="Artifact service is not configured.",
|
|
882
|
-
)
|
|
883
|
-
|
|
884
|
-
try:
|
|
885
|
-
content_bytes = await upload_file.read()
|
|
886
|
-
if not content_bytes:
|
|
887
|
-
log.warning("%s Uploaded file is empty.", log_prefix)
|
|
888
|
-
raise HTTPException(
|
|
889
|
-
status_code=status.HTTP_400_BAD_REQUEST,
|
|
890
|
-
detail="Uploaded file cannot be empty.",
|
|
891
|
-
)
|
|
892
|
-
|
|
893
|
-
mime_type = upload_file.content_type or "application/octet-stream"
|
|
894
|
-
|
|
895
|
-
parsed_metadata = {}
|
|
896
|
-
if metadata_json:
|
|
897
|
-
try:
|
|
898
|
-
parsed_metadata = json.loads(metadata_json)
|
|
899
|
-
if not isinstance(parsed_metadata, dict):
|
|
900
|
-
log.warning(
|
|
901
|
-
"%s Metadata JSON did not parse to a dictionary. Ignoring.",
|
|
902
|
-
log_prefix,
|
|
903
|
-
)
|
|
904
|
-
parsed_metadata = {}
|
|
905
|
-
except json.JSONDecodeError as json_err:
|
|
906
|
-
log.warning(
|
|
907
|
-
"%s Failed to parse metadata_json: %s. Proceeding without it.",
|
|
908
|
-
log_prefix,
|
|
909
|
-
json_err,
|
|
910
|
-
)
|
|
911
|
-
|
|
912
|
-
app_name = component.get_config("name", "A2A_WebUI_App")
|
|
913
|
-
current_timestamp = datetime.now(timezone.utc)
|
|
914
|
-
|
|
915
|
-
save_result = await save_artifact_with_metadata(
|
|
916
|
-
artifact_service=artifact_service,
|
|
917
|
-
app_name=app_name,
|
|
918
|
-
user_id=user_id,
|
|
919
|
-
session_id=session_id,
|
|
920
|
-
filename=filename,
|
|
921
|
-
content_bytes=content_bytes,
|
|
922
|
-
mime_type=mime_type,
|
|
923
|
-
metadata_dict=parsed_metadata,
|
|
924
|
-
timestamp=current_timestamp,
|
|
925
|
-
schema_max_keys=component.get_config(
|
|
926
|
-
"schema_max_keys", DEFAULT_SCHEMA_MAX_KEYS
|
|
927
|
-
),
|
|
928
|
-
)
|
|
929
|
-
|
|
930
|
-
if save_result["status"] == "success":
|
|
931
|
-
log.info(
|
|
932
|
-
"%s Artifact and metadata processing completed. Data version: %s, Metadata version: %s. Message: %s",
|
|
933
|
-
log_prefix,
|
|
934
|
-
save_result.get("data_version"),
|
|
935
|
-
save_result.get("metadata_version"),
|
|
936
|
-
save_result.get("message"),
|
|
937
|
-
)
|
|
938
|
-
saved_version = save_result.get("data_version")
|
|
939
|
-
artifact_uri = format_artifact_uri(
|
|
940
|
-
app_name=app_name,
|
|
941
|
-
user_id=user_id,
|
|
942
|
-
session_id=session_id,
|
|
943
|
-
filename=filename,
|
|
944
|
-
version=saved_version,
|
|
945
|
-
)
|
|
946
|
-
log.info(
|
|
947
|
-
"%s Successfully saved artifact. Returning URI: %s",
|
|
948
|
-
log_prefix,
|
|
949
|
-
artifact_uri,
|
|
950
|
-
)
|
|
951
|
-
return {
|
|
952
|
-
"filename": filename,
|
|
953
|
-
"data_version": saved_version,
|
|
954
|
-
"metadata_version": save_result.get("metadata_version"),
|
|
955
|
-
"mime_type": mime_type,
|
|
956
|
-
"size": len(content_bytes),
|
|
957
|
-
"message": save_result.get("message"),
|
|
958
|
-
"status": save_result["status"],
|
|
959
|
-
"uri": artifact_uri,
|
|
960
|
-
}
|
|
961
|
-
else:
|
|
962
|
-
log.error(
|
|
963
|
-
"%s Failed to save artifact and metadata: %s",
|
|
964
|
-
log_prefix,
|
|
965
|
-
save_result.get("message"),
|
|
966
|
-
)
|
|
967
|
-
raise HTTPException(
|
|
968
|
-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
969
|
-
detail=save_result.get(
|
|
970
|
-
"message", "Failed to save artifact with metadata."
|
|
971
|
-
),
|
|
972
|
-
)
|
|
973
|
-
|
|
974
|
-
except HTTPException:
|
|
975
|
-
raise
|
|
976
|
-
except Exception as e:
|
|
977
|
-
log.exception("%s Error saving artifact: %s", log_prefix, e)
|
|
978
|
-
raise HTTPException(
|
|
979
|
-
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
980
|
-
detail=f"Failed to save artifact: {str(e)}",
|
|
981
|
-
)
|
|
982
|
-
finally:
|
|
983
|
-
await upload_file.close()
|
|
984
|
-
log.debug("%s Upload file closed.", log_prefix)
|
|
985
|
-
|
|
986
|
-
|
|
987
879
|
@router.delete(
|
|
988
880
|
"/{session_id}/{filename}",
|
|
989
881
|
status_code=status.HTTP_204_NO_CONTENT,
|
|
@@ -4,7 +4,8 @@ Session-related response DTOs.
|
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel, ConfigDict, Field
|
|
6
6
|
|
|
7
|
-
from ....shared.
|
|
7
|
+
from ....shared.pagination import PaginationMeta
|
|
8
|
+
from ....shared.types import SessionId, UserId
|
|
8
9
|
from .base_responses import BaseTimestampResponse
|
|
9
10
|
|
|
10
11
|
|
|
@@ -20,10 +21,10 @@ class SessionResponse(BaseTimestampResponse):
|
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class SessionListResponse(BaseModel):
|
|
23
|
-
"""Response DTO for a list of sessions."""
|
|
24
|
+
"""Response DTO for a list of sessions (legacy - use PaginatedResponse instead)."""
|
|
24
25
|
|
|
25
26
|
model_config = ConfigDict(populate_by_name=True)
|
|
26
27
|
|
|
27
28
|
sessions: list[SessionResponse]
|
|
28
|
-
pagination:
|
|
29
|
+
pagination: PaginationMeta | None = None
|
|
29
30
|
total_count: int = Field(alias="totalCount")
|
|
@@ -149,8 +149,8 @@ async def save_task(
|
|
|
149
149
|
# Check if task already exists to determine status code
|
|
150
150
|
from ..repository.chat_task_repository import ChatTaskRepository
|
|
151
151
|
|
|
152
|
-
task_repo = ChatTaskRepository(
|
|
153
|
-
existing_task = task_repo.find_by_id(request.task_id, user_id)
|
|
152
|
+
task_repo = ChatTaskRepository()
|
|
153
|
+
existing_task = task_repo.find_by_id(db, request.task_id, user_id)
|
|
154
154
|
is_update = existing_task is not None
|
|
155
155
|
|
|
156
156
|
# Save the task - pass strings directly
|
|
@@ -3,50 +3,40 @@ API Router for submitting and managing tasks to agents.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
-
import yaml
|
|
7
6
|
from datetime import datetime
|
|
8
|
-
from
|
|
9
|
-
APIRouter,
|
|
10
|
-
Depends,
|
|
11
|
-
HTTPException,
|
|
12
|
-
Request as FastAPIRequest,
|
|
13
|
-
Response,
|
|
14
|
-
status,
|
|
15
|
-
)
|
|
16
|
-
from fastapi.exceptions import RequestValidationError
|
|
17
|
-
from typing import List, Optional, Union
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
from ....gateway.http_sse.session_manager import SessionManager
|
|
21
|
-
from ....gateway.http_sse.services.task_service import TaskService
|
|
22
|
-
from ....gateway.http_sse.services.session_service import SessionService
|
|
23
|
-
from ....gateway.http_sse.repository.interfaces import ITaskRepository
|
|
24
|
-
from ....gateway.http_sse.repository.entities import Task
|
|
25
|
-
from ....gateway.http_sse.shared.types import PaginationParams, UserId
|
|
26
|
-
from ..utils.stim_utils import create_stim_from_task_data
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
27
8
|
|
|
9
|
+
import yaml
|
|
28
10
|
from a2a.types import (
|
|
29
11
|
CancelTaskRequest,
|
|
30
12
|
SendMessageRequest,
|
|
31
|
-
SendStreamingMessageRequest,
|
|
32
13
|
SendMessageSuccessResponse,
|
|
14
|
+
SendStreamingMessageRequest,
|
|
33
15
|
SendStreamingMessageSuccessResponse,
|
|
34
16
|
)
|
|
35
|
-
from
|
|
17
|
+
from fastapi import APIRouter, Depends, HTTPException, Response, status
|
|
18
|
+
from fastapi import Request as FastAPIRequest
|
|
19
|
+
from sqlalchemy.orm import Session as DBSession
|
|
36
20
|
|
|
21
|
+
from ....common import a2a
|
|
37
22
|
from ....gateway.http_sse.dependencies import (
|
|
38
|
-
|
|
23
|
+
get_db,
|
|
39
24
|
get_sac_component,
|
|
40
|
-
get_task_service,
|
|
41
25
|
get_session_business_service,
|
|
26
|
+
get_session_manager,
|
|
42
27
|
get_task_repository,
|
|
43
|
-
|
|
28
|
+
get_task_service,
|
|
44
29
|
get_user_config,
|
|
45
|
-
|
|
30
|
+
get_user_id,
|
|
46
31
|
)
|
|
32
|
+
from ....gateway.http_sse.repository.entities import Task
|
|
33
|
+
from ....gateway.http_sse.repository.interfaces import ITaskRepository
|
|
47
34
|
from ....gateway.http_sse.services.session_service import SessionService
|
|
48
|
-
|
|
49
|
-
from
|
|
35
|
+
from ....gateway.http_sse.services.task_service import TaskService
|
|
36
|
+
from ....gateway.http_sse.session_manager import SessionManager
|
|
37
|
+
from ....gateway.http_sse.shared.pagination import PaginationParams
|
|
38
|
+
from ....gateway.http_sse.shared.types import UserId
|
|
39
|
+
from ..utils.stim_utils import create_stim_from_task_data
|
|
50
40
|
|
|
51
41
|
if TYPE_CHECKING:
|
|
52
42
|
from ....gateway.http_sse.component import WebUIBackendComponent
|
|
@@ -55,9 +45,10 @@ router = APIRouter()
|
|
|
55
45
|
|
|
56
46
|
log = logging.getLogger(__name__)
|
|
57
47
|
|
|
48
|
+
|
|
58
49
|
async def _submit_task(
|
|
59
50
|
request: FastAPIRequest,
|
|
60
|
-
payload:
|
|
51
|
+
payload: SendMessageRequest | SendStreamingMessageRequest,
|
|
61
52
|
session_manager: SessionManager,
|
|
62
53
|
component: "WebUIBackendComponent",
|
|
63
54
|
is_streaming: bool,
|
|
@@ -203,25 +194,25 @@ async def _submit_task(
|
|
|
203
194
|
)
|
|
204
195
|
|
|
205
196
|
|
|
206
|
-
@router.get("/tasks", response_model=
|
|
197
|
+
@router.get("/tasks", response_model=list[Task], tags=["Tasks"])
|
|
207
198
|
async def search_tasks(
|
|
208
199
|
request: FastAPIRequest,
|
|
209
|
-
start_date:
|
|
210
|
-
end_date:
|
|
211
|
-
search: Optional[str] = None,
|
|
200
|
+
start_date: str | None = None,
|
|
201
|
+
end_date: str | None = None,
|
|
212
202
|
page: int = 1,
|
|
213
203
|
page_size: int = 20,
|
|
214
|
-
query_user_id:
|
|
204
|
+
query_user_id: str | None = None,
|
|
205
|
+
db: DBSession = Depends(get_db),
|
|
215
206
|
user_id: UserId = Depends(get_user_id),
|
|
216
207
|
user_config: dict = Depends(get_user_config),
|
|
217
208
|
repo: ITaskRepository = Depends(get_task_repository),
|
|
218
209
|
):
|
|
219
210
|
"""
|
|
220
|
-
Lists and
|
|
221
|
-
- Regular users can only
|
|
222
|
-
- Users with the 'tasks:read:all' scope can
|
|
211
|
+
Lists and filters historical tasks by date.
|
|
212
|
+
- Regular users can only view their own tasks.
|
|
213
|
+
- Users with the 'tasks:read:all' scope can view any user's tasks by providing `query_user_id`.
|
|
223
214
|
"""
|
|
224
|
-
log_prefix =
|
|
215
|
+
log_prefix = "[GET /api/v1/tasks] "
|
|
225
216
|
log.info("%sRequest from user %s", log_prefix, user_id)
|
|
226
217
|
|
|
227
218
|
target_user_id = user_id
|
|
@@ -265,14 +256,14 @@ async def search_tasks(
|
|
|
265
256
|
detail="Invalid end_date format. Use ISO 8601 format.",
|
|
266
257
|
)
|
|
267
258
|
|
|
268
|
-
pagination = PaginationParams(
|
|
259
|
+
pagination = PaginationParams(page_number=page, page_size=page_size)
|
|
269
260
|
|
|
270
261
|
try:
|
|
271
262
|
tasks = repo.search(
|
|
263
|
+
db,
|
|
272
264
|
user_id=target_user_id,
|
|
273
265
|
start_date=start_time_ms,
|
|
274
266
|
end_date=end_time_ms,
|
|
275
|
-
search_query=search,
|
|
276
267
|
pagination=pagination,
|
|
277
268
|
)
|
|
278
269
|
return tasks
|
|
@@ -288,6 +279,7 @@ async def search_tasks(
|
|
|
288
279
|
async def get_task_as_stim_file(
|
|
289
280
|
task_id: str,
|
|
290
281
|
request: FastAPIRequest,
|
|
282
|
+
db: DBSession = Depends(get_db),
|
|
291
283
|
user_id: UserId = Depends(get_user_id),
|
|
292
284
|
user_config: dict = Depends(get_user_config),
|
|
293
285
|
repo: ITaskRepository = Depends(get_task_repository),
|
|
@@ -299,7 +291,7 @@ async def get_task_as_stim_file(
|
|
|
299
291
|
log.info("%sRequest from user %s", log_prefix, user_id)
|
|
300
292
|
|
|
301
293
|
try:
|
|
302
|
-
result = repo.find_by_id_with_events(task_id)
|
|
294
|
+
result = repo.find_by_id_with_events(db, task_id)
|
|
303
295
|
if not result:
|
|
304
296
|
raise HTTPException(
|
|
305
297
|
status_code=status.HTTP_404_NOT_FOUND,
|
|
@@ -134,26 +134,32 @@ from sse_starlette.sse import EventSourceResponse
|
|
|
134
134
|
|
|
135
135
|
def _generate_sse_url(fastapi_request: FastAPIRequest, stream_id: str) -> str:
|
|
136
136
|
"""
|
|
137
|
-
Generate SSE endpoint URL with proper scheme detection for reverse proxy scenarios.
|
|
137
|
+
Generate SSE endpoint URL with proper scheme and host detection for reverse proxy scenarios.
|
|
138
138
|
|
|
139
139
|
Args:
|
|
140
140
|
fastapi_request: The FastAPI request object
|
|
141
141
|
stream_id: The stream ID for the SSE endpoint
|
|
142
142
|
|
|
143
143
|
Returns:
|
|
144
|
-
Complete SSE URL with correct scheme (http/https)
|
|
144
|
+
Complete SSE URL with correct scheme (http/https) and host.
|
|
145
145
|
"""
|
|
146
|
+
base_url = fastapi_request.url_for(
|
|
147
|
+
"get_visualization_stream_events", stream_id=stream_id
|
|
148
|
+
)
|
|
149
|
+
|
|
146
150
|
forwarded_proto = fastapi_request.headers.get("x-forwarded-proto")
|
|
147
|
-
|
|
148
|
-
|
|
151
|
+
forwarded_host = fastapi_request.headers.get("x-forwarded-host")
|
|
152
|
+
|
|
153
|
+
if forwarded_proto and forwarded_host:
|
|
154
|
+
# In a reverse proxy environment like GitHub Codespaces, reconstruct the URL
|
|
155
|
+
# using the forwarded headers to ensure it's publicly accessible.
|
|
156
|
+
return str(base_url.replace(scheme=forwarded_proto, netloc=forwarded_host))
|
|
157
|
+
elif forwarded_proto:
|
|
158
|
+
# Handle cases with only a forwarded protocol (standard reverse proxy)
|
|
159
|
+
return str(base_url.replace(scheme=forwarded_proto))
|
|
149
160
|
else:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
return str(
|
|
153
|
-
fastapi_request.url_for(
|
|
154
|
-
"get_visualization_stream_events", stream_id=stream_id
|
|
155
|
-
).replace(scheme=scheme)
|
|
156
|
-
)
|
|
161
|
+
# Default behavior when not behind a reverse proxy
|
|
162
|
+
return str(base_url)
|
|
157
163
|
|
|
158
164
|
|
|
159
165
|
def _translate_target_to_solace_topics(
|
|
@@ -188,8 +188,8 @@ class DataRetentionService:
|
|
|
188
188
|
|
|
189
189
|
db = self.session_factory()
|
|
190
190
|
try:
|
|
191
|
-
repo = TaskRepository(
|
|
192
|
-
total_deleted = repo.delete_tasks_older_than(cutoff_time_ms, batch_size)
|
|
191
|
+
repo = TaskRepository()
|
|
192
|
+
total_deleted = repo.delete_tasks_older_than(db, cutoff_time_ms, batch_size)
|
|
193
193
|
|
|
194
194
|
if total_deleted == 0:
|
|
195
195
|
log.info(
|
|
@@ -241,8 +241,8 @@ class DataRetentionService:
|
|
|
241
241
|
|
|
242
242
|
db = self.session_factory()
|
|
243
243
|
try:
|
|
244
|
-
repo = FeedbackRepository(
|
|
245
|
-
total_deleted = repo.delete_feedback_older_than(cutoff_time_ms, batch_size)
|
|
244
|
+
repo = FeedbackRepository()
|
|
245
|
+
total_deleted = repo.delete_feedback_older_than(db, cutoff_time_ms, batch_size)
|
|
246
246
|
|
|
247
247
|
if total_deleted == 0:
|
|
248
248
|
log.info(
|