rustkernel_ecosystem/
lib.rs

1//! # RustKernels Ecosystem
2//!
3//! Web framework integrations for RustKernels, providing REST APIs, gRPC services,
4//! and middleware for integrating GPU kernels into production applications.
5//!
6//! # Features
7//!
8//! - **Axum Integration**: REST API endpoints for kernel invocation
9//! - **Tower Middleware**: Service layer for kernel execution
10//! - **gRPC Support**: Protobuf-based kernel RPC
11//! - **Actix Actors**: GPU-persistent actors for Actix framework
12//!
13//! # Feature Flags
14//!
15//! - `axum`: Enable Axum REST API support
16//! - `tower`: Enable Tower middleware
17//! - `grpc`: Enable gRPC/Tonic support
18//! - `actix`: Enable Actix actor support
19//! - `full`: Enable all integrations
20//!
21//! # Example
22//!
23//! ```rust,ignore
24//! use rustkernel_ecosystem::axum::{KernelRouter, RouterConfig};
25//!
26//! let router = KernelRouter::new(registry)
27//!     .with_config(RouterConfig::default())
28//!     .build();
29//!
30//! axum::serve(listener, router).await?;
31//! ```
32
33#![warn(missing_docs)]
34#![warn(clippy::all)]
35
36#[cfg(feature = "axum")]
37pub mod axum_integration;
38#[cfg(feature = "axum")]
39pub use axum_integration as axum;
40
41#[cfg(feature = "tower")]
42pub mod tower_integration;
43#[cfg(feature = "tower")]
44pub use tower_integration as tower;
45
46#[cfg(feature = "grpc")]
47pub mod grpc_integration;
48#[cfg(feature = "grpc")]
49pub use grpc_integration as grpc;
50
51#[cfg(feature = "actix")]
52pub mod actix_integration;
53#[cfg(feature = "actix")]
54pub use actix_integration as actix;
55
56// Common types used across integrations
57mod common;
58pub use common::*;
59
60use serde::{Deserialize, Serialize};
61
62/// Error types for ecosystem integrations
63#[derive(Debug, thiserror::Error)]
64pub enum EcosystemError {
65    /// Kernel not found
66    #[error("Kernel not found: {0}")]
67    KernelNotFound(String),
68
69    /// Kernel execution failed
70    #[error("Kernel execution failed: {0}")]
71    ExecutionFailed(String),
72
73    /// Serialization error
74    #[error("Serialization error: {0}")]
75    SerializationError(String),
76
77    /// Invalid request
78    #[error("Invalid request: {0}")]
79    InvalidRequest(String),
80
81    /// Authentication required
82    #[error("Authentication required")]
83    AuthenticationRequired,
84
85    /// Permission denied
86    #[error("Permission denied: {0}")]
87    PermissionDenied(String),
88
89    /// Rate limit exceeded
90    #[error("Rate limit exceeded")]
91    RateLimitExceeded,
92
93    /// Service unavailable
94    #[error("Service unavailable: {0}")]
95    ServiceUnavailable(String),
96
97    /// Internal error
98    #[error("Internal error: {0}")]
99    InternalError(String),
100}
101
102impl From<rustkernel_core::error::KernelError> for EcosystemError {
103    fn from(err: rustkernel_core::error::KernelError) -> Self {
104        match err {
105            rustkernel_core::error::KernelError::KernelNotFound(id) => {
106                EcosystemError::KernelNotFound(id)
107            }
108            rustkernel_core::error::KernelError::Unauthorized(msg) => {
109                EcosystemError::PermissionDenied(msg)
110            }
111            rustkernel_core::error::KernelError::ServiceUnavailable(msg) => {
112                EcosystemError::ServiceUnavailable(msg)
113            }
114            _ => EcosystemError::ExecutionFailed(err.to_string()),
115        }
116    }
117}
118
119/// Kernel invocation request
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct KernelRequest {
122    /// Kernel ID to invoke
123    pub kernel_id: String,
124    /// Input data (JSON)
125    pub input: serde_json::Value,
126    /// Request metadata
127    #[serde(default)]
128    pub metadata: RequestMetadata,
129}
130
131/// Request metadata
132#[derive(Debug, Clone, Default, Serialize, Deserialize)]
133pub struct RequestMetadata {
134    /// Trace ID for distributed tracing
135    pub trace_id: Option<String>,
136    /// Span ID
137    pub span_id: Option<String>,
138    /// Tenant ID
139    pub tenant_id: Option<String>,
140    /// Priority (0-10, higher is more important)
141    pub priority: Option<u8>,
142    /// Timeout in milliseconds
143    pub timeout_ms: Option<u64>,
144}
145
146/// Kernel invocation response
147#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct KernelResponse {
149    /// Request ID
150    pub request_id: String,
151    /// Kernel ID
152    pub kernel_id: String,
153    /// Output data (JSON)
154    pub output: serde_json::Value,
155    /// Execution metadata
156    pub metadata: ResponseMetadata,
157}
158
159/// Response metadata
160#[derive(Debug, Clone, Default, Serialize, Deserialize)]
161pub struct ResponseMetadata {
162    /// Execution duration in microseconds
163    pub duration_us: u64,
164    /// Backend used (CUDA, CPU, etc.)
165    pub backend: String,
166    /// GPU memory used (bytes)
167    pub gpu_memory_bytes: Option<u64>,
168    /// Trace ID
169    pub trace_id: Option<String>,
170}
171
172/// Error response
173#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct ErrorResponse {
175    /// Error code
176    pub code: String,
177    /// Error message
178    pub message: String,
179    /// Request ID (if available)
180    pub request_id: Option<String>,
181    /// Additional details
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub details: Option<serde_json::Value>,
184}
185
186impl ErrorResponse {
187    /// Create from EcosystemError
188    pub fn from_error(err: &EcosystemError, request_id: Option<String>) -> Self {
189        let (code, message) = match err {
190            EcosystemError::KernelNotFound(id) => {
191                ("KERNEL_NOT_FOUND", format!("Kernel not found: {}", id))
192            }
193            EcosystemError::ExecutionFailed(msg) => ("EXECUTION_FAILED", msg.clone()),
194            EcosystemError::SerializationError(msg) => ("SERIALIZATION_ERROR", msg.clone()),
195            EcosystemError::InvalidRequest(msg) => ("INVALID_REQUEST", msg.clone()),
196            EcosystemError::AuthenticationRequired => (
197                "AUTHENTICATION_REQUIRED",
198                "Authentication required".to_string(),
199            ),
200            EcosystemError::PermissionDenied(msg) => ("PERMISSION_DENIED", msg.clone()),
201            EcosystemError::RateLimitExceeded => {
202                ("RATE_LIMIT_EXCEEDED", "Rate limit exceeded".to_string())
203            }
204            EcosystemError::ServiceUnavailable(msg) => ("SERVICE_UNAVAILABLE", msg.clone()),
205            EcosystemError::InternalError(msg) => ("INTERNAL_ERROR", msg.clone()),
206        };
207
208        Self {
209            code: code.to_string(),
210            message,
211            request_id,
212            details: None,
213        }
214    }
215}
216
217/// Health check response
218#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct HealthResponse {
220    /// Overall status
221    pub status: HealthStatus,
222    /// Service version
223    pub version: String,
224    /// Uptime in seconds
225    pub uptime_secs: u64,
226    /// Component health checks
227    #[serde(skip_serializing_if = "Vec::is_empty")]
228    pub components: Vec<ComponentHealth>,
229}
230
231/// Health status
232#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
233#[serde(rename_all = "lowercase")]
234pub enum HealthStatus {
235    /// Service is healthy
236    Healthy,
237    /// Service is degraded
238    Degraded,
239    /// Service is unhealthy
240    Unhealthy,
241}
242
243/// Component health
244#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct ComponentHealth {
246    /// Component name
247    pub name: String,
248    /// Component status
249    pub status: HealthStatus,
250    /// Optional message
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub message: Option<String>,
253}
254
255#[cfg(test)]
256mod tests {
257    use super::*;
258
259    #[test]
260    fn test_error_response() {
261        let err = EcosystemError::KernelNotFound("test-kernel".to_string());
262        let response = ErrorResponse::from_error(&err, Some("req-123".to_string()));
263
264        assert_eq!(response.code, "KERNEL_NOT_FOUND");
265        assert!(response.message.contains("test-kernel"));
266        assert_eq!(response.request_id, Some("req-123".to_string()));
267    }
268
269    #[test]
270    fn test_health_response() {
271        let response = HealthResponse {
272            status: HealthStatus::Healthy,
273            version: "0.1.0".to_string(),
274            uptime_secs: 3600,
275            components: vec![],
276        };
277
278        let json = serde_json::to_string(&response).unwrap();
279        assert!(json.contains("healthy"));
280    }
281}