1#![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
56mod common;
58pub use common::*;
59
60use serde::{Deserialize, Serialize};
61
62#[derive(Debug, thiserror::Error)]
64pub enum EcosystemError {
65 #[error("Kernel not found: {0}")]
67 KernelNotFound(String),
68
69 #[error("Kernel execution failed: {0}")]
71 ExecutionFailed(String),
72
73 #[error("Serialization error: {0}")]
75 SerializationError(String),
76
77 #[error("Invalid request: {0}")]
79 InvalidRequest(String),
80
81 #[error("Authentication required")]
83 AuthenticationRequired,
84
85 #[error("Permission denied: {0}")]
87 PermissionDenied(String),
88
89 #[error("Rate limit exceeded")]
91 RateLimitExceeded,
92
93 #[error("Service unavailable: {0}")]
95 ServiceUnavailable(String),
96
97 #[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#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct KernelRequest {
122 pub kernel_id: String,
124 pub input: serde_json::Value,
126 #[serde(default)]
128 pub metadata: RequestMetadata,
129}
130
131#[derive(Debug, Clone, Default, Serialize, Deserialize)]
133pub struct RequestMetadata {
134 pub trace_id: Option<String>,
136 pub span_id: Option<String>,
138 pub tenant_id: Option<String>,
140 pub priority: Option<u8>,
142 pub timeout_ms: Option<u64>,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct KernelResponse {
149 pub request_id: String,
151 pub kernel_id: String,
153 pub output: serde_json::Value,
155 pub metadata: ResponseMetadata,
157}
158
159#[derive(Debug, Clone, Default, Serialize, Deserialize)]
161pub struct ResponseMetadata {
162 pub duration_us: u64,
164 pub backend: String,
166 pub gpu_memory_bytes: Option<u64>,
168 pub trace_id: Option<String>,
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
174pub struct ErrorResponse {
175 pub code: String,
177 pub message: String,
179 pub request_id: Option<String>,
181 #[serde(skip_serializing_if = "Option::is_none")]
183 pub details: Option<serde_json::Value>,
184}
185
186impl ErrorResponse {
187 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#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct HealthResponse {
220 pub status: HealthStatus,
222 pub version: String,
224 pub uptime_secs: u64,
226 #[serde(skip_serializing_if = "Vec::is_empty")]
228 pub components: Vec<ComponentHealth>,
229}
230
231#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
233#[serde(rename_all = "lowercase")]
234pub enum HealthStatus {
235 Healthy,
237 Degraded,
239 Unhealthy,
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct ComponentHealth {
246 pub name: String,
248 pub status: HealthStatus,
250 #[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}