Skip to content

Commit 105b91c

Browse files
committed
phase 4 added additional http-api endpoints
1 parent 2a6540d commit 105b91c

File tree

3 files changed

+323
-2
lines changed

3 files changed

+323
-2
lines changed

symbiont/client.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,14 @@
3838
# Agent DSL models
3939
DslCompileRequest,
4040
DslCompileResponse,
41+
EndpointMetrics,
4142
# System models
4243
HealthResponse,
44+
# Phase 4 HTTP Endpoint Management models
45+
HttpEndpointCreateRequest,
46+
HttpEndpointInfo,
47+
HttpEndpointResponse,
48+
HttpEndpointUpdateRequest,
4349
HttpInputCreateRequest,
4450
HttpInputServerInfo,
4551
HttpInputUpdateRequest,
@@ -1281,3 +1287,106 @@ def count_vectors(self, collection_name: str) -> int:
12811287
"""
12821288
response = self._request("GET", f"vectors/collections/{collection_name}/count")
12831289
return response.json()["count"]
1290+
1291+
# =============================================================================
1292+
# Phase 4 HTTP Endpoint Management Methods
1293+
# =============================================================================
1294+
1295+
def create_http_endpoint(self, endpoint_request: Union[HttpEndpointCreateRequest, Dict[str, Any]]) -> HttpEndpointResponse:
1296+
"""Create a new HTTP endpoint.
1297+
1298+
Args:
1299+
endpoint_request: HTTP endpoint creation request
1300+
1301+
Returns:
1302+
HttpEndpointResponse: Endpoint creation result
1303+
"""
1304+
if isinstance(endpoint_request, dict):
1305+
endpoint_request = HttpEndpointCreateRequest(**endpoint_request)
1306+
1307+
response = self._request("POST", "endpoints", json=endpoint_request.model_dump())
1308+
return HttpEndpointResponse(**response.json())
1309+
1310+
def list_http_endpoints(self) -> List[HttpEndpointInfo]:
1311+
"""List all HTTP endpoints.
1312+
1313+
Returns:
1314+
List[HttpEndpointInfo]: List of endpoint information
1315+
"""
1316+
response = self._request("GET", "endpoints")
1317+
return [HttpEndpointInfo(**endpoint) for endpoint in response.json()]
1318+
1319+
def update_http_endpoint(self, endpoint_request: Union[HttpEndpointUpdateRequest, Dict[str, Any]]) -> HttpEndpointResponse:
1320+
"""Update an existing HTTP endpoint.
1321+
1322+
Args:
1323+
endpoint_request: HTTP endpoint update request
1324+
1325+
Returns:
1326+
HttpEndpointResponse: Endpoint update result
1327+
"""
1328+
if isinstance(endpoint_request, dict):
1329+
endpoint_request = HttpEndpointUpdateRequest(**endpoint_request)
1330+
1331+
response = self._request("PUT", f"endpoints/{endpoint_request.endpoint_id}", json=endpoint_request.model_dump())
1332+
return HttpEndpointResponse(**response.json())
1333+
1334+
def delete_http_endpoint(self, endpoint_id: str) -> Dict[str, Any]:
1335+
"""Delete an HTTP endpoint.
1336+
1337+
Args:
1338+
endpoint_id: The endpoint identifier
1339+
1340+
Returns:
1341+
Dict[str, Any]: Deletion confirmation
1342+
"""
1343+
response = self._request("DELETE", f"endpoints/{endpoint_id}")
1344+
return response.json()
1345+
1346+
def get_http_endpoint(self, endpoint_id: str) -> HttpEndpointInfo:
1347+
"""Get information about a specific HTTP endpoint.
1348+
1349+
Args:
1350+
endpoint_id: The endpoint identifier
1351+
1352+
Returns:
1353+
HttpEndpointInfo: Endpoint information
1354+
"""
1355+
response = self._request("GET", f"endpoints/{endpoint_id}")
1356+
return HttpEndpointInfo(**response.json())
1357+
1358+
def get_endpoint_metrics(self, endpoint_id: str) -> EndpointMetrics:
1359+
"""Get metrics for a specific HTTP endpoint.
1360+
1361+
Args:
1362+
endpoint_id: The endpoint identifier
1363+
1364+
Returns:
1365+
EndpointMetrics: Endpoint metrics information
1366+
"""
1367+
response = self._request("GET", f"endpoints/{endpoint_id}/metrics")
1368+
return EndpointMetrics(**response.json())
1369+
1370+
def enable_http_endpoint(self, endpoint_id: str) -> Dict[str, Any]:
1371+
"""Enable/activate an HTTP endpoint.
1372+
1373+
Args:
1374+
endpoint_id: The endpoint identifier
1375+
1376+
Returns:
1377+
Dict[str, Any]: Operation confirmation
1378+
"""
1379+
response = self._request("POST", f"endpoints/{endpoint_id}/enable")
1380+
return response.json()
1381+
1382+
def disable_http_endpoint(self, endpoint_id: str) -> Dict[str, Any]:
1383+
"""Disable/deactivate an HTTP endpoint.
1384+
1385+
Args:
1386+
endpoint_id: The endpoint identifier
1387+
1388+
Returns:
1389+
Dict[str, Any]: Operation confirmation
1390+
"""
1391+
response = self._request("POST", f"endpoints/{endpoint_id}/disable")
1392+
return response.json()

symbiont/exceptions.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,121 @@ def __init__(self, message: str = "Memory retrieval error", memory_id: str = Non
165165
"""
166166
super().__init__(message)
167167
self.memory_id = memory_id
168+
169+
170+
# =============================================================================
171+
# Phase 3 Vector Database Exception Classes
172+
# =============================================================================
173+
174+
class VectorDatabaseError(SymbiontError):
175+
"""Base exception for vector database errors."""
176+
pass
177+
178+
179+
class QdrantConnectionError(VectorDatabaseError):
180+
"""Raised when Qdrant connection operations fail."""
181+
182+
def __init__(self, message: str = "Qdrant connection error", host: str = None):
183+
"""Initialize the QdrantConnectionError.
184+
185+
Args:
186+
message: Error message describing the connection failure.
187+
host: Optional Qdrant host that failed to connect.
188+
"""
189+
super().__init__(message)
190+
self.host = host
191+
192+
193+
class CollectionNotFoundError(VectorDatabaseError):
194+
"""Raised when a vector collection is not found."""
195+
196+
def __init__(self, message: str = "Vector collection not found", collection_name: str = None):
197+
"""Initialize the CollectionNotFoundError.
198+
199+
Args:
200+
message: Error message describing the collection error.
201+
collection_name: Optional collection name that was not found.
202+
"""
203+
super().__init__(message, 404)
204+
self.collection_name = collection_name
205+
206+
207+
class EmbeddingError(SymbiontError):
208+
"""Raised when embedding generation operations fail."""
209+
210+
def __init__(self, message: str = "Embedding generation error", model: str = None):
211+
"""Initialize the EmbeddingError.
212+
213+
Args:
214+
message: Error message describing the embedding failure.
215+
model: Optional embedding model that failed.
216+
"""
217+
super().__init__(message)
218+
self.model = model
219+
220+
221+
# =============================================================================
222+
# Phase 4 HTTP Endpoint Management Exception Classes
223+
# =============================================================================
224+
225+
class EndpointError(SymbiontError):
226+
"""Base exception for HTTP endpoint management errors."""
227+
pass
228+
229+
230+
class EndpointNotFoundError(EndpointError):
231+
"""Raised when an HTTP endpoint is not found."""
232+
233+
def __init__(self, message: str = "HTTP endpoint not found", endpoint_id: str = None):
234+
"""Initialize the EndpointNotFoundError.
235+
236+
Args:
237+
message: Error message describing the endpoint error.
238+
endpoint_id: Optional endpoint ID that was not found.
239+
"""
240+
super().__init__(message, 404)
241+
self.endpoint_id = endpoint_id
242+
243+
244+
class EndpointConflictError(EndpointError):
245+
"""Raised when an HTTP endpoint creation conflicts with existing endpoints."""
246+
247+
def __init__(self, message: str = "HTTP endpoint conflict", path: str = None, method: str = None):
248+
"""Initialize the EndpointConflictError.
249+
250+
Args:
251+
message: Error message describing the endpoint conflict.
252+
path: Optional endpoint path that conflicts.
253+
method: Optional HTTP method that conflicts.
254+
"""
255+
super().__init__(message, 409)
256+
self.path = path
257+
self.method = method
258+
259+
260+
class EndpointConfigurationError(EndpointError):
261+
"""Raised when HTTP endpoint configuration is invalid."""
262+
263+
def __init__(self, message: str = "Invalid endpoint configuration", config_field: str = None):
264+
"""Initialize the EndpointConfigurationError.
265+
266+
Args:
267+
message: Error message describing the configuration issue.
268+
config_field: Optional configuration field that is invalid.
269+
"""
270+
super().__init__(message, 400)
271+
self.config_field = config_field
272+
273+
274+
class EndpointRateLimitError(EndpointError):
275+
"""Raised when an HTTP endpoint rate limit is exceeded."""
276+
277+
def __init__(self, message: str = "Endpoint rate limit exceeded", endpoint_id: str = None):
278+
"""Initialize the EndpointRateLimitError.
279+
280+
Args:
281+
message: Error message describing the rate limit violation.
282+
endpoint_id: Optional endpoint ID that exceeded the rate limit.
283+
"""
284+
super().__init__(message, 429)
285+
self.endpoint_id = endpoint_id

symbiont/models.py

Lines changed: 96 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -874,8 +874,6 @@ class EmbeddingRequest(BaseModel):
874874
model: str = Field("default", description="Embedding model to use")
875875
collection_name: Optional[str] = Field(None, description="Target collection")
876876
metadata: Optional[List[Dict[str, Any]]] = Field(None, description="Metadata for each text")
877-
878-
879877
class EmbeddingResponse(BaseModel):
880878
"""Response from embedding generation."""
881879
embeddings: List[List[float]] = Field(..., description="Generated embeddings")
@@ -884,3 +882,99 @@ class EmbeddingResponse(BaseModel):
884882
token_count: Optional[int] = Field(None, description="Total token count processed")
885883

886884

885+
# =============================================================================
886+
# Phase 4 HTTP Endpoint Management Models
887+
# =============================================================================
888+
889+
class HttpMethod(str, Enum):
890+
"""HTTP method enumeration."""
891+
GET = "GET"
892+
POST = "POST"
893+
PUT = "PUT"
894+
DELETE = "DELETE"
895+
PATCH = "PATCH"
896+
HEAD = "HEAD"
897+
OPTIONS = "OPTIONS"
898+
899+
900+
class EndpointStatus(str, Enum):
901+
"""HTTP endpoint status enumeration."""
902+
ACTIVE = "active"
903+
INACTIVE = "inactive"
904+
MAINTENANCE = "maintenance"
905+
ERROR = "error"
906+
907+
908+
class HttpEndpointCreateRequest(BaseModel):
909+
"""Request to create a new HTTP endpoint."""
910+
path: str = Field(..., description="Endpoint path")
911+
method: HttpMethod = Field(..., description="HTTP method")
912+
agent_id: str = Field(..., description="Agent to handle requests")
913+
description: Optional[str] = Field(None, description="Endpoint description")
914+
auth_required: bool = Field(True, description="Whether authentication is required")
915+
rate_limit: Optional[int] = Field(None, description="Rate limit per minute")
916+
timeout_seconds: int = Field(30, description="Request timeout in seconds")
917+
middleware: List[str] = Field(default_factory=list, description="Middleware to apply")
918+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional endpoint metadata")
919+
920+
921+
class HttpEndpointUpdateRequest(BaseModel):
922+
"""Request to update an existing HTTP endpoint."""
923+
endpoint_id: str = Field(..., description="Endpoint identifier")
924+
path: Optional[str] = Field(None, description="Endpoint path")
925+
method: Optional[HttpMethod] = Field(None, description="HTTP method")
926+
agent_id: Optional[str] = Field(None, description="Agent to handle requests")
927+
description: Optional[str] = Field(None, description="Endpoint description")
928+
auth_required: Optional[bool] = Field(None, description="Whether authentication is required")
929+
rate_limit: Optional[int] = Field(None, description="Rate limit per minute")
930+
timeout_seconds: Optional[int] = Field(None, description="Request timeout in seconds")
931+
status: Optional[EndpointStatus] = Field(None, description="Endpoint status")
932+
middleware: Optional[List[str]] = Field(None, description="Middleware to apply")
933+
metadata: Optional[Dict[str, Any]] = Field(None, description="Additional endpoint metadata")
934+
935+
936+
class EndpointMetrics(BaseModel):
937+
"""HTTP endpoint metrics."""
938+
endpoint_id: str = Field(..., description="Endpoint identifier")
939+
total_requests: int = Field(..., description="Total number of requests")
940+
successful_requests: int = Field(..., description="Number of successful requests")
941+
failed_requests: int = Field(..., description="Number of failed requests")
942+
average_response_time_ms: float = Field(..., description="Average response time in milliseconds")
943+
max_response_time_ms: float = Field(..., description="Maximum response time in milliseconds")
944+
min_response_time_ms: float = Field(..., description="Minimum response time in milliseconds")
945+
requests_per_minute: float = Field(..., description="Current requests per minute rate")
946+
error_rate_percent: float = Field(..., description="Error rate percentage")
947+
last_request_at: Optional[datetime] = Field(None, description="Timestamp of last request")
948+
uptime_seconds: int = Field(..., description="Endpoint uptime in seconds")
949+
950+
951+
class HttpEndpointInfo(BaseModel):
952+
"""HTTP endpoint information."""
953+
endpoint_id: str = Field(..., description="Endpoint identifier")
954+
path: str = Field(..., description="Endpoint path")
955+
method: HttpMethod = Field(..., description="HTTP method")
956+
agent_id: str = Field(..., description="Agent handling requests")
957+
description: Optional[str] = Field(None, description="Endpoint description")
958+
status: EndpointStatus = Field(..., description="Current endpoint status")
959+
auth_required: bool = Field(..., description="Whether authentication is required")
960+
rate_limit: Optional[int] = Field(None, description="Rate limit per minute")
961+
timeout_seconds: int = Field(..., description="Request timeout in seconds")
962+
middleware: List[str] = Field(default_factory=list, description="Applied middleware")
963+
created_at: datetime = Field(..., description="Endpoint creation timestamp")
964+
updated_at: datetime = Field(..., description="Last update timestamp")
965+
created_by: str = Field(..., description="User who created the endpoint")
966+
metrics: Optional[EndpointMetrics] = Field(None, description="Endpoint metrics")
967+
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional endpoint metadata")
968+
969+
970+
class HttpEndpointResponse(BaseModel):
971+
"""Response from HTTP endpoint operations."""
972+
endpoint_id: str = Field(..., description="Endpoint identifier")
973+
status: str = Field(..., description="Operation status")
974+
message: Optional[str] = Field(None, description="Operation message")
975+
endpoint_info: Optional[HttpEndpointInfo] = Field(None, description="Endpoint information")
976+
created_at: Optional[datetime] = Field(None, description="Creation timestamp")
977+
978+
979+
980+

0 commit comments

Comments
 (0)