solace-agent-mesh 1.4.7__py3-none-any.whl → 1.4.8__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/sac/component.py +2 -3
- solace_agent_mesh/assets/docs/404.html +3 -3
- solace_agent_mesh/assets/docs/assets/js/{0e682baa.da822665.js → 0e682baa.d054e1d8.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/166ab619.e27886d9.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/453a82a6.3c6bb61d.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{75384d09.1e7d7cb7.js → 75384d09.c19e8b51.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/a3a92b25.af35e313.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{bac0be12.bf0181cf.js → bac0be12.17de4316.js} +1 -1
- solace_agent_mesh/assets/docs/assets/js/d6a81ee7.829198f1.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/f284c35a.ed8dd236.js +1 -0
- solace_agent_mesh/assets/docs/assets/js/{main.11f9f9f3.js → main.86924c42.js} +2 -2
- solace_agent_mesh/assets/docs/assets/js/runtime~main.0d2ff2b6.js +1 -0
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/rbac-setup-guilde/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/Enterprise/single-sign-on/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-gateway-upgrade-to-0.3.0/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/Migrations/A2A Upgrade To 0.3.0/a2a-technical-migration-map/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/architecture/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/cli/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/orchestrator/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/concepts/plugins/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/debugging/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/deploy/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/deployment/observability/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/component-overview/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/index.html +11 -12
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/configurations/litellm_models/index.html +49 -0
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/installation/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/introduction/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/getting-started/quick-start/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/bedrock-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/custom-agent/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/event-mesh-gateway/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mcp-integration/index.html +5 -5
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/mongodb-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rag-integration/index.html +6 -6
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/rest-gateway/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/slack-integration/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/tutorials/sql-database/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/artifact-management/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/audio-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/data-analysis-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/embeds/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/builtin-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-agents/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/create-gateways/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-python-tools/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/creating-service-providers/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/solace-ai-connector/index.html +4 -4
- solace_agent_mesh/assets/docs/docs/documentation/user-guide/structure/index.html +4 -4
- solace_agent_mesh/assets/docs/lunr-index-1759246102819.json +1 -0
- solace_agent_mesh/assets/docs/lunr-index.json +1 -1
- solace_agent_mesh/assets/docs/search-doc-1759246102819.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 +6 -3
- solace_agent_mesh/cli/commands/init_cmd/__init__.py +3 -3
- solace_agent_mesh/cli/commands/init_cmd/broker_step.py +1 -1
- solace_agent_mesh/cli/commands/init_cmd/env_step.py +1 -1
- solace_agent_mesh/cli/commands/init_cmd/init_cmd_llm.txt +4 -4
- solace_agent_mesh/cli/commands/init_cmd/orchestrator_step.py +12 -1
- solace_agent_mesh/cli/commands/plugin_cmd/install_cmd.py +5 -5
- solace_agent_mesh/client/webui/frontend/static/assets/main-B0PHV3hm.js +339 -0
- solace_agent_mesh/client/webui/frontend/static/index.html +1 -1
- solace_agent_mesh/config_portal/backend/common.py +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{_index-bFMKlzKf.js → _index-BNuqpWDc.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/assets/{manifest-89db7c30.js → manifest-44d62be6.js} +1 -1
- solace_agent_mesh/config_portal/frontend/static/client/index.html +1 -1
- solace_agent_mesh/gateway/http_sse/component.py +21 -15
- solace_agent_mesh/gateway/http_sse/dependencies.py +84 -88
- solace_agent_mesh/gateway/http_sse/main.py +8 -2
- solace_agent_mesh/gateway/http_sse/repository/entities/message.py +3 -1
- solace_agent_mesh/gateway/http_sse/repository/entities/session.py +3 -1
- solace_agent_mesh/gateway/http_sse/repository/interfaces.py +5 -0
- solace_agent_mesh/gateway/http_sse/repository/message_repository.py +25 -23
- solace_agent_mesh/gateway/http_sse/repository/models/__init__.py +12 -4
- solace_agent_mesh/gateway/http_sse/repository/models/message_model.py +19 -1
- solace_agent_mesh/gateway/http_sse/repository/models/session_model.py +19 -1
- solace_agent_mesh/gateway/http_sse/repository/session_repository.py +46 -42
- solace_agent_mesh/gateway/http_sse/routers/artifacts.py +199 -59
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/__init__.py +1 -6
- solace_agent_mesh/gateway/http_sse/routers/dto/requests/session_requests.py +3 -17
- solace_agent_mesh/gateway/http_sse/routers/people.py +4 -37
- solace_agent_mesh/gateway/http_sse/routers/sessions.py +33 -68
- solace_agent_mesh/gateway/http_sse/routers/tasks.py +54 -28
- solace_agent_mesh/gateway/http_sse/services/session_service.py +60 -28
- solace_agent_mesh/gateway/http_sse/shared/__init__.py +122 -1
- solace_agent_mesh/gateway/http_sse/shared/base_repository.py +278 -0
- solace_agent_mesh/gateway/http_sse/shared/database_exceptions.py +274 -0
- solace_agent_mesh/gateway/http_sse/shared/database_helpers.py +43 -0
- solace_agent_mesh/gateway/http_sse/shared/error_dto.py +107 -0
- solace_agent_mesh/gateway/http_sse/shared/exception_handlers.py +192 -0
- solace_agent_mesh/gateway/http_sse/shared/exceptions.py +192 -0
- solace_agent_mesh/gateway/http_sse/shared/pagination.py +138 -0
- solace_agent_mesh/gateway/http_sse/shared/response_utils.py +134 -0
- solace_agent_mesh/gateway/http_sse/shared/utils.py +22 -0
- solace_agent_mesh/templates/plugin_agent_config_template.yaml +1 -1
- solace_agent_mesh/templates/plugin_custom_config_template.yaml +1 -1
- solace_agent_mesh/templates/plugin_gateway_config_template.yaml +1 -1
- solace_agent_mesh/templates/shared_config.yaml +1 -1
- {solace_agent_mesh-1.4.7.dist-info → solace_agent_mesh-1.4.8.dist-info}/METADATA +34 -35
- {solace_agent_mesh-1.4.7.dist-info → solace_agent_mesh-1.4.8.dist-info}/RECORD +109 -98
- solace_agent_mesh/assets/docs/assets/js/166ab619.bdddc63a.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/a3a92b25.6def8980.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/beecea0d.ce915979.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/f284c35a.525933db.js +0 -1
- solace_agent_mesh/assets/docs/assets/js/runtime~main.5922bcf0.js +0 -1
- solace_agent_mesh/assets/docs/lunr-index-1759151175744.json +0 -1
- solace_agent_mesh/assets/docs/search-doc-1759151175744.json +0 -1
- solace_agent_mesh/client/webui/frontend/static/assets/main-BKIoiLSu.js +0 -339
- /solace_agent_mesh/assets/docs/assets/js/{main.11f9f9f3.js.LICENSE.txt → main.86924c42.js.LICENSE.txt} +0 -0
- {solace_agent_mesh-1.4.7.dist-info → solace_agent_mesh-1.4.8.dist-info}/WHEEL +0 -0
- {solace_agent_mesh-1.4.7.dist-info → solace_agent_mesh-1.4.8.dist-info}/entry_points.txt +0 -0
- {solace_agent_mesh-1.4.7.dist-info → solace_agent_mesh-1.4.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -14,7 +14,9 @@ from fastapi import (
|
|
|
14
14
|
Path,
|
|
15
15
|
UploadFile,
|
|
16
16
|
status,
|
|
17
|
+
Request as FastAPIRequest,
|
|
17
18
|
)
|
|
19
|
+
from pydantic import BaseModel, Field
|
|
18
20
|
from fastapi.responses import Response, StreamingResponse
|
|
19
21
|
|
|
20
22
|
try:
|
|
@@ -33,7 +35,6 @@ from urllib.parse import parse_qs, quote, urlparse
|
|
|
33
35
|
from solace_ai_connector.common.log import log
|
|
34
36
|
|
|
35
37
|
from ....common.a2a.types import ArtifactInfo
|
|
36
|
-
from ....common.middleware import ConfigResolver
|
|
37
38
|
from ....common.utils.embeds import (
|
|
38
39
|
LATE_EMBED_TYPES,
|
|
39
40
|
evaluate_embed,
|
|
@@ -41,17 +42,24 @@ from ....common.utils.embeds import (
|
|
|
41
42
|
)
|
|
42
43
|
from ....common.utils.mime_helpers import is_text_based_mime_type
|
|
43
44
|
from ..dependencies import (
|
|
44
|
-
|
|
45
|
+
ValidatedUserConfig,
|
|
45
46
|
get_sac_component,
|
|
46
47
|
get_session_validator,
|
|
47
48
|
get_shared_artifact_service,
|
|
48
49
|
get_user_config,
|
|
49
50
|
get_user_id,
|
|
51
|
+
get_session_manager,
|
|
52
|
+
get_session_business_service_optional,
|
|
53
|
+
get_db_optional,
|
|
50
54
|
)
|
|
51
55
|
|
|
52
56
|
if TYPE_CHECKING:
|
|
53
57
|
from ....gateway.http_sse.component import WebUIBackendComponent
|
|
54
58
|
|
|
59
|
+
from ..session_manager import SessionManager
|
|
60
|
+
from ..services.session_service import SessionService
|
|
61
|
+
from sqlalchemy.orm import Session
|
|
62
|
+
|
|
55
63
|
from ....agent.utils.artifact_helpers import (
|
|
56
64
|
DEFAULT_SCHEMA_MAX_KEYS,
|
|
57
65
|
format_artifact_uri,
|
|
@@ -60,9 +68,191 @@ from ....agent.utils.artifact_helpers import (
|
|
|
60
68
|
save_artifact_with_metadata,
|
|
61
69
|
)
|
|
62
70
|
|
|
71
|
+
|
|
72
|
+
class ArtifactUploadResponse(BaseModel):
|
|
73
|
+
"""Response model for artifact upload with camelCase fields."""
|
|
74
|
+
uri: str
|
|
75
|
+
session_id: str = Field(..., alias="sessionId")
|
|
76
|
+
filename: str
|
|
77
|
+
size: int
|
|
78
|
+
mime_type: str = Field(..., alias="mimeType")
|
|
79
|
+
metadata: dict[str, Any]
|
|
80
|
+
created_at: str = Field(..., alias="createdAt")
|
|
81
|
+
|
|
82
|
+
model_config = {"populate_by_name": True}
|
|
83
|
+
|
|
84
|
+
|
|
63
85
|
router = APIRouter()
|
|
64
86
|
|
|
65
87
|
|
|
88
|
+
@router.post(
|
|
89
|
+
"/upload",
|
|
90
|
+
status_code=status.HTTP_201_CREATED,
|
|
91
|
+
response_model=ArtifactUploadResponse,
|
|
92
|
+
summary="Upload Artifact (Body-Based Session Management)",
|
|
93
|
+
description="Uploads file with sessionId and filename in request body. Creates session if sessionId is null/empty.",
|
|
94
|
+
)
|
|
95
|
+
async def upload_artifact_with_session(
|
|
96
|
+
request: FastAPIRequest,
|
|
97
|
+
upload_file: UploadFile = File(..., description="The file content to upload"),
|
|
98
|
+
sessionId: str | None = Form(None, description="Session ID (null/empty to create new session)", alias="sessionId"),
|
|
99
|
+
filename: str = Form(..., description="The name of the artifact to create/update"),
|
|
100
|
+
metadata_json: str | None = Form(
|
|
101
|
+
None, description="JSON string of artifact metadata (e.g., description, source)"
|
|
102
|
+
),
|
|
103
|
+
artifact_service: BaseArtifactService = Depends(get_shared_artifact_service),
|
|
104
|
+
user_id: str = Depends(get_user_id),
|
|
105
|
+
validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
|
|
106
|
+
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
107
|
+
user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:create"])),
|
|
108
|
+
session_manager: SessionManager = Depends(get_session_manager),
|
|
109
|
+
session_service: SessionService | None = Depends(get_session_business_service_optional),
|
|
110
|
+
db: Session | None = Depends(get_db_optional),
|
|
111
|
+
):
|
|
112
|
+
"""
|
|
113
|
+
Uploads a file to create a new version of the specified artifact.
|
|
114
|
+
|
|
115
|
+
Key features:
|
|
116
|
+
- Session ID and filename provided in request body (not URL)
|
|
117
|
+
- Automatically creates new session if session_id is null/empty
|
|
118
|
+
- Consistent with chat API patterns
|
|
119
|
+
"""
|
|
120
|
+
log_prefix = f"[POST /artifacts/upload] User {user_id}: "
|
|
121
|
+
|
|
122
|
+
# Handle session creation logic (matching chat API pattern)
|
|
123
|
+
effective_session_id = None
|
|
124
|
+
|
|
125
|
+
# Use session ID from request body (matching sessionId pattern in session APIs)
|
|
126
|
+
if sessionId and sessionId.strip():
|
|
127
|
+
effective_session_id = sessionId.strip()
|
|
128
|
+
log.info("%sUsing existing session: %s", log_prefix, effective_session_id)
|
|
129
|
+
else:
|
|
130
|
+
# Create new session when no sessionId provided (like chat does for new conversations)
|
|
131
|
+
effective_session_id = session_manager.create_new_session_id(request)
|
|
132
|
+
log.info("%sCreated new session for file upload: %s", log_prefix, effective_session_id)
|
|
133
|
+
|
|
134
|
+
# Persist session in database if persistence is available (matching chat pattern)
|
|
135
|
+
if session_service and db:
|
|
136
|
+
try:
|
|
137
|
+
session_service.create_session(
|
|
138
|
+
db=db,
|
|
139
|
+
user_id=user_id,
|
|
140
|
+
session_id=effective_session_id,
|
|
141
|
+
agent_id=None, # Will be determined when first message is sent
|
|
142
|
+
name=None, # Will be set when first message is sent
|
|
143
|
+
)
|
|
144
|
+
db.commit()
|
|
145
|
+
log.info("%sSession created and committed to database: %s", log_prefix, effective_session_id)
|
|
146
|
+
except Exception as session_error:
|
|
147
|
+
db.rollback()
|
|
148
|
+
log.warning("%sSession persistence failed, continuing with in-memory session: %s",
|
|
149
|
+
log_prefix, session_error)
|
|
150
|
+
else:
|
|
151
|
+
log.debug("%sNo persistence available - using in-memory session: %s",
|
|
152
|
+
log_prefix, effective_session_id)
|
|
153
|
+
|
|
154
|
+
# Validate inputs
|
|
155
|
+
if not filename or not filename.strip():
|
|
156
|
+
raise HTTPException(
|
|
157
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
158
|
+
detail="Filename is required.",
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
if not upload_file.filename:
|
|
162
|
+
raise HTTPException(
|
|
163
|
+
status_code=status.HTTP_400_BAD_REQUEST,
|
|
164
|
+
detail="File upload is required.",
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
# Validate artifact service availability
|
|
168
|
+
if not artifact_service:
|
|
169
|
+
log.error("%sArtifact service is not configured.", log_prefix)
|
|
170
|
+
raise HTTPException(
|
|
171
|
+
status_code=status.HTTP_501_NOT_IMPLEMENTED,
|
|
172
|
+
detail="Artifact service is not configured.",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Validate session (now that we have an effective_session_id)
|
|
176
|
+
if not validate_session(effective_session_id, user_id):
|
|
177
|
+
log.warning("%sSession validation failed for session: %s", log_prefix, effective_session_id)
|
|
178
|
+
raise HTTPException(
|
|
179
|
+
status_code=status.HTTP_403_FORBIDDEN,
|
|
180
|
+
detail="Invalid session or insufficient permissions.",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
log.info("%sUploading file '%s' to session '%s'", log_prefix, filename.strip(), effective_session_id)
|
|
184
|
+
|
|
185
|
+
try:
|
|
186
|
+
# Read and validate file content
|
|
187
|
+
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
|
+
|
|
194
|
+
mime_type = upload_file.content_type or "application/octet-stream"
|
|
195
|
+
filename_clean = filename.strip()
|
|
196
|
+
|
|
197
|
+
log.debug("%sProcessing file: %s (%d bytes, %s)", log_prefix, filename_clean, len(content_bytes), mime_type)
|
|
198
|
+
|
|
199
|
+
# Parse and validate metadata
|
|
200
|
+
metadata = {}
|
|
201
|
+
if metadata_json and metadata_json.strip():
|
|
202
|
+
try:
|
|
203
|
+
metadata = json.loads(metadata_json.strip())
|
|
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")
|
|
214
|
+
|
|
215
|
+
# Store the artifact using the service
|
|
216
|
+
artifact_uri = await artifact_service.store(
|
|
217
|
+
app_name=app_name,
|
|
218
|
+
user_id=user_id,
|
|
219
|
+
session_id=effective_session_id,
|
|
220
|
+
filename=filename_clean,
|
|
221
|
+
content_bytes=content_bytes,
|
|
222
|
+
mime_type=mime_type,
|
|
223
|
+
metadata=metadata,
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
log.info("%sArtifact stored successfully: %s (%d bytes)", log_prefix, artifact_uri, len(content_bytes))
|
|
227
|
+
|
|
228
|
+
# Return standardized response using Pydantic model (ensures camelCase conversion)
|
|
229
|
+
return ArtifactUploadResponse(
|
|
230
|
+
uri=artifact_uri,
|
|
231
|
+
session_id=effective_session_id, # Will be returned as "sessionId" due to alias
|
|
232
|
+
filename=filename_clean,
|
|
233
|
+
size=len(content_bytes),
|
|
234
|
+
mime_type=mime_type, # Will be returned as "mimeType" due to alias
|
|
235
|
+
metadata=metadata,
|
|
236
|
+
created_at=datetime.now(timezone.utc).isoformat(), # Will be returned as "createdAt" due to alias
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
except HTTPException:
|
|
240
|
+
# Re-raise HTTP exceptions as-is
|
|
241
|
+
raise
|
|
242
|
+
except Exception as e:
|
|
243
|
+
log.exception("%sUnexpected error storing artifact: %s", log_prefix, e)
|
|
244
|
+
raise HTTPException(
|
|
245
|
+
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
246
|
+
detail="Failed to store artifact due to an internal error.",
|
|
247
|
+
)
|
|
248
|
+
finally:
|
|
249
|
+
# Ensure file is properly closed
|
|
250
|
+
try:
|
|
251
|
+
await upload_file.close()
|
|
252
|
+
except Exception as close_error:
|
|
253
|
+
log.warning("%sError closing upload file: %s", log_prefix, close_error)
|
|
254
|
+
|
|
255
|
+
|
|
66
256
|
@router.get(
|
|
67
257
|
"/{session_id}/{filename}/versions",
|
|
68
258
|
response_model=list[int],
|
|
@@ -78,19 +268,12 @@ async def list_artifact_versions(
|
|
|
78
268
|
user_id: str = Depends(get_user_id),
|
|
79
269
|
validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
|
|
80
270
|
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
81
|
-
|
|
82
|
-
user_config: dict = Depends(get_user_config),
|
|
271
|
+
user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:list"])),
|
|
83
272
|
):
|
|
84
273
|
"""
|
|
85
274
|
Lists the available integer versions for a given artifact filename
|
|
86
275
|
associated with the current user and session ID.
|
|
87
276
|
"""
|
|
88
|
-
if not config_resolver.is_feature_enabled(
|
|
89
|
-
user_config, {"required_scopes": ["tool:artifact:list"]}, {}
|
|
90
|
-
):
|
|
91
|
-
raise HTTPException(
|
|
92
|
-
status_code=403, detail="Not authorized to list artifact versions"
|
|
93
|
-
)
|
|
94
277
|
|
|
95
278
|
log_prefix = f"[ArtifactRouter:ListVersions:{filename}] User={user_id}, Session={session_id} -"
|
|
96
279
|
log.info("%s Request received.", log_prefix)
|
|
@@ -166,18 +349,13 @@ async def list_artifacts(
|
|
|
166
349
|
user_id: str = Depends(get_user_id),
|
|
167
350
|
validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
|
|
168
351
|
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
169
|
-
|
|
170
|
-
user_config: dict = Depends(get_user_config),
|
|
352
|
+
user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:list"])),
|
|
171
353
|
):
|
|
172
354
|
"""
|
|
173
355
|
Lists detailed information (filename, size, type, modified date, uri)
|
|
174
356
|
for all artifacts associated with the specified user and session ID
|
|
175
357
|
by calling the artifact helper function.
|
|
176
358
|
"""
|
|
177
|
-
if not config_resolver.is_feature_enabled(
|
|
178
|
-
user_config, {"required_scopes": ["tool:artifact:list"]}, {}
|
|
179
|
-
):
|
|
180
|
-
raise HTTPException(status_code=403, detail="Not authorized to list artifacts")
|
|
181
359
|
|
|
182
360
|
log_prefix = f"[ArtifactRouter:ListInfo] User={user_id}, Session={session_id} -"
|
|
183
361
|
log.info("%s Request received.", log_prefix)
|
|
@@ -241,17 +419,12 @@ async def get_latest_artifact(
|
|
|
241
419
|
user_id: str = Depends(get_user_id),
|
|
242
420
|
validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
|
|
243
421
|
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
244
|
-
|
|
245
|
-
user_config: dict = Depends(get_user_config),
|
|
422
|
+
user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:load"])),
|
|
246
423
|
):
|
|
247
424
|
"""
|
|
248
425
|
Retrieves the content of the latest version of the specified artifact
|
|
249
426
|
associated with the current user and session ID.
|
|
250
427
|
"""
|
|
251
|
-
if not config_resolver.is_feature_enabled(
|
|
252
|
-
user_config, {"required_scopes": ["tool:artifact:load"]}, {}
|
|
253
|
-
):
|
|
254
|
-
raise HTTPException(status_code=403, detail="Not authorized to load artifact")
|
|
255
428
|
log_prefix = (
|
|
256
429
|
f"[ArtifactRouter:GetLatest:{filename}] User={user_id}, Session={session_id} -"
|
|
257
430
|
)
|
|
@@ -386,19 +559,12 @@ async def get_specific_artifact_version(
|
|
|
386
559
|
user_id: str = Depends(get_user_id),
|
|
387
560
|
validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
|
|
388
561
|
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
389
|
-
|
|
390
|
-
user_config: dict = Depends(get_user_config),
|
|
562
|
+
user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:load"])),
|
|
391
563
|
):
|
|
392
564
|
"""
|
|
393
565
|
Retrieves the content of a specific version of the specified artifact
|
|
394
566
|
associated with the current user and session ID.
|
|
395
567
|
"""
|
|
396
|
-
if not config_resolver.is_feature_enabled(
|
|
397
|
-
user_config, {"required_scopes": ["tool:artifact:load"]}, {}
|
|
398
|
-
):
|
|
399
|
-
raise HTTPException(
|
|
400
|
-
status_code=403, detail="Not authorized to load artifact version"
|
|
401
|
-
)
|
|
402
568
|
log_prefix = f"[ArtifactRouter:GetVersion:{filename} v{version}] User={user_id}, Session={session_id} -"
|
|
403
569
|
log.info("%s Request received.", log_prefix)
|
|
404
570
|
|
|
@@ -568,8 +734,7 @@ async def get_artifact_by_uri(
|
|
|
568
734
|
uri: str,
|
|
569
735
|
requesting_user_id: str = Depends(get_user_id),
|
|
570
736
|
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
571
|
-
|
|
572
|
-
user_config: dict = Depends(get_user_config),
|
|
737
|
+
user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:load"])),
|
|
573
738
|
):
|
|
574
739
|
"""
|
|
575
740
|
Resolves an artifact:// URI and streams its content.
|
|
@@ -620,21 +785,6 @@ async def get_artifact_by_uri(
|
|
|
620
785
|
version,
|
|
621
786
|
)
|
|
622
787
|
|
|
623
|
-
if not config_resolver.is_feature_enabled(
|
|
624
|
-
user_config, {"required_scopes": ["tool:artifact:load"]}, {}
|
|
625
|
-
):
|
|
626
|
-
raise HTTPException(
|
|
627
|
-
status_code=403, detail="Not authorized to load artifact by URI"
|
|
628
|
-
)
|
|
629
|
-
log.warning(
|
|
630
|
-
"%s Authorization denied for user '%s' to access artifact URI '%s'",
|
|
631
|
-
log_id_prefix,
|
|
632
|
-
requesting_user_id,
|
|
633
|
-
uri,
|
|
634
|
-
)
|
|
635
|
-
raise HTTPException(
|
|
636
|
-
status_code=403, detail="Permission denied to access this artifact."
|
|
637
|
-
)
|
|
638
788
|
|
|
639
789
|
log.info(
|
|
640
790
|
"%s User '%s' authorized to access artifact URI.",
|
|
@@ -700,17 +850,12 @@ async def upload_artifact(
|
|
|
700
850
|
user_id: str = Depends(get_user_id),
|
|
701
851
|
validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
|
|
702
852
|
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
703
|
-
|
|
704
|
-
user_config: dict = Depends(get_user_config),
|
|
853
|
+
user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:create"])),
|
|
705
854
|
):
|
|
706
855
|
"""
|
|
707
856
|
Uploads a file to create a new version of the specified artifact
|
|
708
857
|
associated with the current user and session ID. Also saves associated metadata.
|
|
709
858
|
"""
|
|
710
|
-
if not config_resolver.is_feature_enabled(
|
|
711
|
-
user_config, {"required_scopes": ["tool:artifact:create"]}, {}
|
|
712
|
-
):
|
|
713
|
-
raise HTTPException(status_code=403, detail="Not authorized to upload artifact")
|
|
714
859
|
log_prefix = (
|
|
715
860
|
f"[ArtifactRouter:Post:{filename}] User={user_id}, Session={session_id} -"
|
|
716
861
|
)
|
|
@@ -856,17 +1001,12 @@ async def delete_artifact(
|
|
|
856
1001
|
user_id: str = Depends(get_user_id),
|
|
857
1002
|
validate_session: Callable[[str, str], bool] = Depends(get_session_validator),
|
|
858
1003
|
component: "WebUIBackendComponent" = Depends(get_sac_component),
|
|
859
|
-
|
|
860
|
-
user_config: dict = Depends(get_user_config),
|
|
1004
|
+
user_config: dict = Depends(ValidatedUserConfig(["tool:artifact:delete"])),
|
|
861
1005
|
):
|
|
862
1006
|
"""
|
|
863
1007
|
Deletes the specified artifact (including all its versions)
|
|
864
1008
|
associated with the current user and session ID.
|
|
865
1009
|
"""
|
|
866
|
-
if not config_resolver.is_feature_enabled(
|
|
867
|
-
user_config, {"required_scopes": ["tool:artifact:delete"]}, {}
|
|
868
|
-
):
|
|
869
|
-
raise HTTPException(status_code=403, detail="Not authorized to delete artifact")
|
|
870
1010
|
log_prefix = (
|
|
871
1011
|
f"[ArtifactRouter:Delete:{filename}] User={user_id}, Session={session_id} -"
|
|
872
1012
|
)
|
|
@@ -3,18 +3,13 @@ Request DTOs for API endpoints.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from .session_requests import (
|
|
6
|
-
GetSessionsRequest,
|
|
7
6
|
GetSessionRequest,
|
|
8
7
|
GetSessionHistoryRequest,
|
|
9
8
|
UpdateSessionRequest,
|
|
10
|
-
DeleteSessionRequest,
|
|
11
9
|
)
|
|
12
10
|
|
|
13
11
|
__all__ = [
|
|
14
|
-
|
|
15
|
-
"GetSessionsRequest",
|
|
16
|
-
"GetSessionRequest",
|
|
12
|
+
"GetSessionRequest",
|
|
17
13
|
"GetSessionHistoryRequest",
|
|
18
14
|
"UpdateSessionRequest",
|
|
19
|
-
"DeleteSessionRequest",
|
|
20
15
|
]
|
|
@@ -2,18 +2,10 @@
|
|
|
2
2
|
Session-related request DTOs.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
-
from typing import Optional
|
|
5
|
+
from typing import Optional
|
|
6
6
|
from pydantic import BaseModel, Field
|
|
7
7
|
|
|
8
|
-
from ....shared.types import SessionId, UserId,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class GetSessionsRequest(BaseModel):
|
|
12
|
-
"""Request DTO for retrieving sessions."""
|
|
13
|
-
user_id: UserId
|
|
14
|
-
pagination: Optional[PaginationInfo] = None
|
|
15
|
-
sort: Optional[SortInfo] = None
|
|
16
|
-
filters: Optional[List[FilterInfo]] = None
|
|
8
|
+
from ....shared.types import SessionId, UserId, PaginationInfo
|
|
17
9
|
|
|
18
10
|
|
|
19
11
|
class GetSessionRequest(BaseModel):
|
|
@@ -33,10 +25,4 @@ class UpdateSessionRequest(BaseModel):
|
|
|
33
25
|
"""Request DTO for updating session details."""
|
|
34
26
|
session_id: SessionId
|
|
35
27
|
user_id: UserId
|
|
36
|
-
name: str = Field(..., min_length=1, max_length=255, description="New session name")
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class DeleteSessionRequest(BaseModel):
|
|
40
|
-
"""Request DTO for deleting a session."""
|
|
41
|
-
session_id: SessionId
|
|
42
|
-
user_id: UserId
|
|
28
|
+
name: str = Field(..., min_length=1, max_length=255, description="New session name")
|
|
@@ -9,11 +9,13 @@ from solace_ai_connector.common.log import log
|
|
|
9
9
|
|
|
10
10
|
from ..dependencies import get_people_service
|
|
11
11
|
from ..services.people_service import PeopleService
|
|
12
|
+
from ..shared.pagination import DataResponse
|
|
13
|
+
from ..shared.response_utils import create_data_response
|
|
12
14
|
|
|
13
15
|
router = APIRouter()
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
@router.get("/people/search", response_model=List[Dict[str, Any]])
|
|
18
|
+
@router.get("/people/search", response_model=DataResponse[List[Dict[str, Any]]])
|
|
17
19
|
async def search_people(
|
|
18
20
|
q: str = Query(
|
|
19
21
|
...,
|
|
@@ -31,39 +33,4 @@ async def search_people(
|
|
|
31
33
|
"""
|
|
32
34
|
log.debug("Endpoint /people/search called with query: '%s'", q)
|
|
33
35
|
results = await people_service.search_for_users(query=q, limit=limit)
|
|
34
|
-
return results
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"""
|
|
38
|
-
API endpoints for people-related features, such as user search for autocomplete.
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
from typing import Any, Dict, List
|
|
42
|
-
|
|
43
|
-
from fastapi import APIRouter, Depends, Query
|
|
44
|
-
from solace_ai_connector.common.log import log
|
|
45
|
-
from ..dependencies import get_people_service
|
|
46
|
-
from ..services.people_service import PeopleService
|
|
47
|
-
|
|
48
|
-
router = APIRouter()
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
@router.get("/people/search", response_model=List[Dict[str, Any]])
|
|
52
|
-
async def search_people(
|
|
53
|
-
q: str = Query(
|
|
54
|
-
...,
|
|
55
|
-
min_length=2,
|
|
56
|
-
max_length=50,
|
|
57
|
-
description="Search query for user name/email.",
|
|
58
|
-
),
|
|
59
|
-
limit: int = Query(
|
|
60
|
-
10, ge=1, le=25, description="Maximum number of results to return."
|
|
61
|
-
),
|
|
62
|
-
people_service: PeopleService = Depends(get_people_service),
|
|
63
|
-
):
|
|
64
|
-
"""
|
|
65
|
-
Searches for users to populate frontend autocomplete suggestions (e.g., for @mentions).
|
|
66
|
-
"""
|
|
67
|
-
log.debug("Endpoint /people/search called with query: '%s'", q)
|
|
68
|
-
results = await people_service.search_for_users(query=q, limit=limit)
|
|
69
|
-
return results
|
|
36
|
+
return create_data_response(results)
|