- Complete UCXL protocol implementation with DHT storage layer - BZZZ Gateway for peer-to-peer networking and content distribution - Temporal navigation engine with version control and timeline browsing - Standardized UCXL error/response codes for Rust, Go, and Python - React-based UI with multi-tab interface and professional styling - libp2p integration for distributed hash table operations - Self-healing network mechanisms and peer management - Comprehensive IPC commands for Tauri desktop integration Major Components: - ucxl-core: Core UCXL protocol and DHT implementation - BZZZ Gateway: Local subnet peer discovery and content replication - Temporal Engine: Version control and state reconstruction - Cross-language standards: Unified error handling across implementations - Modern UI: Professional React interface with DHT and network monitoring Standards Compliance: - UCXL-ERROR-CODES.md and UCXL-RESPONSE-CODES.md v1.0 - Machine-readable error codes with structured payloads - Client guidance for retry logic and error handling - Cross-language compatibility with identical APIs 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
458 lines
15 KiB
Python
458 lines
15 KiB
Python
"""
|
|
UCXL Standard Response and Error Codes Library
|
|
|
|
This module provides standardized UCXL error and response codes for consistent
|
|
cross-service communication and client handling.
|
|
|
|
Based on UCXL-ERROR-CODES.md and UCXL-RESPONSE-CODES.md v1.0
|
|
"""
|
|
|
|
import json
|
|
import uuid
|
|
from datetime import datetime, timezone
|
|
from enum import Enum
|
|
from typing import Any, Dict, Optional
|
|
from dataclasses import dataclass, field, asdict
|
|
|
|
|
|
class UCXLErrorCode(Enum):
|
|
"""Standard UCXL Error Codes following format: UCXL-<HTTP-class>-<SHORT_NAME>"""
|
|
|
|
# 400 - Client Errors
|
|
INVALID_ADDRESS = "UCXL-400-INVALID_ADDRESS"
|
|
MISSING_FIELD = "UCXL-400-MISSING_FIELD"
|
|
INVALID_FORMAT = "UCXL-400-INVALID_FORMAT"
|
|
|
|
# 401 - Authentication
|
|
UNAUTHORIZED = "UCXL-401-UNAUTHORIZED"
|
|
|
|
# 403 - Authorization
|
|
FORBIDDEN = "UCXL-403-FORBIDDEN"
|
|
|
|
# 404 - Not Found
|
|
NOT_FOUND = "UCXL-404-NOT_FOUND"
|
|
|
|
# 409 - Conflict
|
|
CONFLICT = "UCXL-409-CONFLICT"
|
|
|
|
# 422 - Unprocessable Entity
|
|
UNPROCESSABLE_ENTITY = "UCXL-422-UNPROCESSABLE_ENTITY"
|
|
|
|
# 429 - Rate Limiting
|
|
RATE_LIMIT = "UCXL-429-RATE_LIMIT"
|
|
|
|
# 500 - Server Errors
|
|
INTERNAL_ERROR = "UCXL-500-INTERNAL_ERROR"
|
|
|
|
# 503 - Service Unavailable
|
|
SERVICE_UNAVAILABLE = "UCXL-503-SERVICE_UNAVAILABLE"
|
|
|
|
# 504 - Gateway Timeout
|
|
GATEWAY_TIMEOUT = "UCXL-504-GATEWAY_TIMEOUT"
|
|
|
|
def http_status(self) -> int:
|
|
"""Get the HTTP status code for this error code"""
|
|
mapping = {
|
|
self.INVALID_ADDRESS: 400,
|
|
self.MISSING_FIELD: 400,
|
|
self.INVALID_FORMAT: 400,
|
|
self.UNAUTHORIZED: 401,
|
|
self.FORBIDDEN: 403,
|
|
self.NOT_FOUND: 404,
|
|
self.CONFLICT: 409,
|
|
self.UNPROCESSABLE_ENTITY: 422,
|
|
self.RATE_LIMIT: 429,
|
|
self.INTERNAL_ERROR: 500,
|
|
self.SERVICE_UNAVAILABLE: 503,
|
|
self.GATEWAY_TIMEOUT: 504,
|
|
}
|
|
return mapping.get(self, 500)
|
|
|
|
def default_message(self) -> str:
|
|
"""Get the default error message for this code"""
|
|
messages = {
|
|
self.INVALID_ADDRESS: "Invalid UCXL address format",
|
|
self.MISSING_FIELD: "Required field is missing",
|
|
self.INVALID_FORMAT: "Input does not match the expected format",
|
|
self.UNAUTHORIZED: "Authentication credentials missing or invalid",
|
|
self.FORBIDDEN: "Insufficient permissions for the action",
|
|
self.NOT_FOUND: "Requested resource not found",
|
|
self.CONFLICT: "Conflict with current state",
|
|
self.UNPROCESSABLE_ENTITY: "Semantic validation failed",
|
|
self.RATE_LIMIT: "Too many requests; rate limiting in effect",
|
|
self.INTERNAL_ERROR: "Internal server error",
|
|
self.SERVICE_UNAVAILABLE: "Service is currently unavailable",
|
|
self.GATEWAY_TIMEOUT: "Downstream gateway timed out",
|
|
}
|
|
return messages.get(self, "Unknown error")
|
|
|
|
def is_client_error(self) -> bool:
|
|
"""Check if this is a client error (4xx)"""
|
|
return 400 <= self.http_status() < 500
|
|
|
|
def is_server_error(self) -> bool:
|
|
"""Check if this is a server error (5xx)"""
|
|
return 500 <= self.http_status() < 600
|
|
|
|
def should_retry(self) -> bool:
|
|
"""Check if this error should trigger a retry"""
|
|
return self in {
|
|
self.RATE_LIMIT,
|
|
self.INTERNAL_ERROR,
|
|
self.SERVICE_UNAVAILABLE,
|
|
self.GATEWAY_TIMEOUT,
|
|
}
|
|
|
|
|
|
class UCXLResponseCode(Enum):
|
|
"""Standard UCXL Response Codes following format: UCXL-<HTTP-class>-<SHORT_NAME>"""
|
|
|
|
# 200 - Success
|
|
OK = "UCXL-200-OK"
|
|
|
|
# 201 - Created
|
|
CREATED = "UCXL-201-CREATED"
|
|
|
|
# 202 - Accepted (Async)
|
|
ACCEPTED = "UCXL-202-ACCEPTED"
|
|
|
|
# 204 - No Content
|
|
NO_CONTENT = "UCXL-204-NO_CONTENT"
|
|
|
|
# 206 - Partial Content
|
|
PARTIAL_CONTENT = "UCXL-206-PARTIAL_CONTENT"
|
|
|
|
# 304 - Not Modified (Caching)
|
|
NOT_MODIFIED = "UCXL-304-NOT_MODIFIED"
|
|
|
|
def http_status(self) -> int:
|
|
"""Get the HTTP status code for this response code"""
|
|
mapping = {
|
|
self.OK: 200,
|
|
self.CREATED: 201,
|
|
self.ACCEPTED: 202,
|
|
self.NO_CONTENT: 204,
|
|
self.PARTIAL_CONTENT: 206,
|
|
self.NOT_MODIFIED: 304,
|
|
}
|
|
return mapping.get(self, 200)
|
|
|
|
def default_message(self) -> str:
|
|
"""Get the default success message for this code"""
|
|
messages = {
|
|
self.OK: "Request completed successfully",
|
|
self.CREATED: "Resource created successfully",
|
|
self.ACCEPTED: "Request accepted for processing",
|
|
self.NO_CONTENT: "Request completed with no content to return",
|
|
self.PARTIAL_CONTENT: "Partial results returned",
|
|
self.NOT_MODIFIED: "Resource not modified since last fetch",
|
|
}
|
|
return messages.get(self, "Success")
|
|
|
|
def is_async(self) -> bool:
|
|
"""Check if this indicates an asynchronous operation"""
|
|
return self == self.ACCEPTED
|
|
|
|
def is_partial(self) -> bool:
|
|
"""Check if this indicates partial/incomplete results"""
|
|
return self in {self.PARTIAL_CONTENT, self.ACCEPTED}
|
|
|
|
|
|
@dataclass
|
|
class UCXLError:
|
|
"""UCXL Error Details"""
|
|
code: UCXLErrorCode
|
|
message: str
|
|
source: str
|
|
path: str
|
|
request_id: str
|
|
timestamp: datetime
|
|
details: Optional[Dict[str, Any]] = None
|
|
cause: Optional[str] = None
|
|
|
|
def __post_init__(self):
|
|
if isinstance(self.code, str):
|
|
self.code = UCXLErrorCode(self.code)
|
|
|
|
|
|
@dataclass
|
|
class UCXLErrorResponse:
|
|
"""UCXL Standard Error Response Payload"""
|
|
error: UCXLError
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Convert to dictionary"""
|
|
result = asdict(self)
|
|
result['error']['code'] = self.error.code.value
|
|
result['error']['timestamp'] = self.error.timestamp.isoformat()
|
|
return result
|
|
|
|
def to_json(self) -> str:
|
|
"""Convert to JSON string"""
|
|
return json.dumps(self.to_dict())
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: Dict[str, Any]) -> 'UCXLErrorResponse':
|
|
"""Create from dictionary"""
|
|
error_data = data['error']
|
|
error_data['timestamp'] = datetime.fromisoformat(error_data['timestamp'].replace('Z', '+00:00'))
|
|
return cls(error=UCXLError(**error_data))
|
|
|
|
@classmethod
|
|
def from_json(cls, json_str: str) -> 'UCXLErrorResponse':
|
|
"""Create from JSON string"""
|
|
return cls.from_dict(json.loads(json_str))
|
|
|
|
|
|
@dataclass
|
|
class UCXLResponse:
|
|
"""UCXL Response Details"""
|
|
code: UCXLResponseCode
|
|
message: str
|
|
request_id: str
|
|
timestamp: datetime
|
|
data: Optional[Any] = None
|
|
details: Optional[Dict[str, Any]] = None
|
|
|
|
def __post_init__(self):
|
|
if isinstance(self.code, str):
|
|
self.code = UCXLResponseCode(self.code)
|
|
|
|
|
|
@dataclass
|
|
class UCXLSuccessResponse:
|
|
"""UCXL Standard Success Response Payload"""
|
|
response: UCXLResponse
|
|
|
|
def to_dict(self) -> Dict[str, Any]:
|
|
"""Convert to dictionary"""
|
|
result = asdict(self)
|
|
result['response']['code'] = self.response.code.value
|
|
result['response']['timestamp'] = self.response.timestamp.isoformat()
|
|
return result
|
|
|
|
def to_json(self) -> str:
|
|
"""Convert to JSON string"""
|
|
return json.dumps(self.to_dict())
|
|
|
|
@classmethod
|
|
def from_dict(cls, data: Dict[str, Any]) -> 'UCXLSuccessResponse':
|
|
"""Create from dictionary"""
|
|
response_data = data['response']
|
|
response_data['timestamp'] = datetime.fromisoformat(response_data['timestamp'].replace('Z', '+00:00'))
|
|
return cls(response=UCXLResponse(**response_data))
|
|
|
|
@classmethod
|
|
def from_json(cls, json_str: str) -> 'UCXLSuccessResponse':
|
|
"""Create from JSON string"""
|
|
return cls.from_dict(json.loads(json_str))
|
|
|
|
|
|
class UCXLErrorBuilder:
|
|
"""Builder for creating UCXL Error responses"""
|
|
|
|
def __init__(self, code: UCXLErrorCode):
|
|
self.code = code
|
|
self.message = None
|
|
self.details = {}
|
|
self.source = "ucxl-api/v1"
|
|
self.path = "/"
|
|
self.request_id = f"req-{str(uuid.uuid4())[:8]}"
|
|
self.cause = None
|
|
|
|
def message(self, message: str) -> 'UCXLErrorBuilder':
|
|
"""Set a custom error message"""
|
|
self.message = message
|
|
return self
|
|
|
|
def field(self, field: str, provided: Any) -> 'UCXLErrorBuilder':
|
|
"""Add details about the field that failed"""
|
|
self.details["field"] = field
|
|
self.details["provided"] = provided
|
|
return self
|
|
|
|
def expected_format(self, format_str: str) -> 'UCXLErrorBuilder':
|
|
"""Add expected format information"""
|
|
self.details["expected_format"] = format_str
|
|
return self
|
|
|
|
def detail(self, key: str, value: Any) -> 'UCXLErrorBuilder':
|
|
"""Add a detail field"""
|
|
self.details[key] = value
|
|
return self
|
|
|
|
def source(self, source: str) -> 'UCXLErrorBuilder':
|
|
"""Set the source service"""
|
|
self.source = source
|
|
return self
|
|
|
|
def path(self, path: str) -> 'UCXLErrorBuilder':
|
|
"""Set the request path"""
|
|
self.path = path
|
|
return self
|
|
|
|
def request_id(self, request_id: str) -> 'UCXLErrorBuilder':
|
|
"""Set the request ID"""
|
|
self.request_id = request_id
|
|
return self
|
|
|
|
def cause(self, cause: str) -> 'UCXLErrorBuilder':
|
|
"""Set the error cause"""
|
|
self.cause = cause
|
|
return self
|
|
|
|
def build(self) -> UCXLErrorResponse:
|
|
"""Build the error response"""
|
|
message = self.message or self.code.default_message()
|
|
details = self.details if self.details else None
|
|
|
|
error = UCXLError(
|
|
code=self.code,
|
|
message=message,
|
|
details=details,
|
|
source=self.source,
|
|
path=self.path,
|
|
request_id=self.request_id,
|
|
timestamp=datetime.now(timezone.utc),
|
|
cause=self.cause,
|
|
)
|
|
return UCXLErrorResponse(error=error)
|
|
|
|
|
|
class UCXLResponseBuilder:
|
|
"""Builder for creating UCXL Success responses"""
|
|
|
|
def __init__(self, code: UCXLResponseCode):
|
|
self.code = code
|
|
self.message = None
|
|
self.data = None
|
|
self.details = {}
|
|
self.request_id = f"req-{str(uuid.uuid4())[:8]}"
|
|
|
|
def message(self, message: str) -> 'UCXLResponseBuilder':
|
|
"""Set a custom success message"""
|
|
self.message = message
|
|
return self
|
|
|
|
def data(self, data: Any) -> 'UCXLResponseBuilder':
|
|
"""Set the response data"""
|
|
self.data = data
|
|
return self
|
|
|
|
def detail(self, key: str, value: Any) -> 'UCXLResponseBuilder':
|
|
"""Add a detail field"""
|
|
self.details[key] = value
|
|
return self
|
|
|
|
def request_id(self, request_id: str) -> 'UCXLResponseBuilder':
|
|
"""Set the request ID"""
|
|
self.request_id = request_id
|
|
return self
|
|
|
|
def build(self) -> UCXLSuccessResponse:
|
|
"""Build the success response"""
|
|
message = self.message or self.code.default_message()
|
|
details = self.details if self.details else None
|
|
|
|
response = UCXLResponse(
|
|
code=self.code,
|
|
message=message,
|
|
data=self.data,
|
|
details=details,
|
|
request_id=self.request_id,
|
|
timestamp=datetime.now(timezone.utc),
|
|
)
|
|
return UCXLSuccessResponse(response=response)
|
|
|
|
|
|
# Convenience functions for common error patterns
|
|
|
|
def create_invalid_address_error(address: str, path: str = "/", request_id: Optional[str] = None) -> UCXLErrorResponse:
|
|
"""Create a standardized invalid address error"""
|
|
builder = UCXLErrorBuilder(UCXLErrorCode.INVALID_ADDRESS)
|
|
builder.field("address", address)
|
|
builder.expected_format("ucxl://<agent>:<role>@<project>:<task>[/<temporal>/<path>]")
|
|
builder.path(path)
|
|
builder.cause("parse_error")
|
|
if request_id:
|
|
builder.request_id(request_id)
|
|
return builder.build()
|
|
|
|
|
|
def create_not_found_error(resource_id: str, path: str = "/", request_id: Optional[str] = None) -> UCXLErrorResponse:
|
|
"""Create a standardized not found error"""
|
|
builder = UCXLErrorBuilder(UCXLErrorCode.NOT_FOUND)
|
|
builder.field("resource_id", resource_id)
|
|
builder.path(path)
|
|
if request_id:
|
|
builder.request_id(request_id)
|
|
return builder.build()
|
|
|
|
|
|
def create_missing_field_error(field: str, path: str = "/", request_id: Optional[str] = None) -> UCXLErrorResponse:
|
|
"""Create a standardized missing field error"""
|
|
builder = UCXLErrorBuilder(UCXLErrorCode.MISSING_FIELD)
|
|
builder.message(f"Required field '{field}' is missing")
|
|
builder.field(field, None)
|
|
builder.path(path)
|
|
builder.cause("validation_error")
|
|
if request_id:
|
|
builder.request_id(request_id)
|
|
return builder.build()
|
|
|
|
|
|
def create_success_response(data: Any = None, path: str = "/", request_id: Optional[str] = None) -> UCXLSuccessResponse:
|
|
"""Create a standardized success response"""
|
|
builder = UCXLResponseBuilder(UCXLResponseCode.OK)
|
|
if data is not None:
|
|
builder.data(data)
|
|
if request_id:
|
|
builder.request_id(request_id)
|
|
return builder.build()
|
|
|
|
|
|
def create_created_response(resource_id: str, location: str, path: str = "/", request_id: Optional[str] = None) -> UCXLSuccessResponse:
|
|
"""Create a standardized resource created response"""
|
|
builder = UCXLResponseBuilder(UCXLResponseCode.CREATED)
|
|
builder.data({"id": resource_id, "url": location})
|
|
builder.detail("location", location)
|
|
if request_id:
|
|
builder.request_id(request_id)
|
|
return builder.build()
|
|
|
|
|
|
# Example usage and testing
|
|
if __name__ == "__main__":
|
|
# Example error response
|
|
error = UCXLErrorBuilder(UCXLErrorCode.INVALID_ADDRESS) \
|
|
.message("Test error message") \
|
|
.field("address", "ucxl://invalid") \
|
|
.expected_format("ucxl://<agent>:<role>@<project>:<task>") \
|
|
.source("test-service") \
|
|
.path("/test") \
|
|
.cause("parse_error") \
|
|
.build()
|
|
|
|
print("Error Response JSON:")
|
|
print(error.to_json())
|
|
print()
|
|
|
|
# Example success response
|
|
success = UCXLResponseBuilder(UCXLResponseCode.OK) \
|
|
.message("Test success") \
|
|
.data({"test": "value"}) \
|
|
.detail("info", "additional info") \
|
|
.build()
|
|
|
|
print("Success Response JSON:")
|
|
print(success.to_json())
|
|
print()
|
|
|
|
# Test serialization round-trip
|
|
error_json = error.to_json()
|
|
error_restored = UCXLErrorResponse.from_json(error_json)
|
|
print("Error serialization test:", error.error.code == error_restored.error.code)
|
|
|
|
success_json = success.to_json()
|
|
success_restored = UCXLSuccessResponse.from_json(success_json)
|
|
print("Success serialization test:", success.response.code == success_restored.response.code) |