use serde::{Deserialize, Serialize};
use std::fmt;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(transparent)]
pub struct DiagnosticCode(pub String);
impl DiagnosticCode {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for DiagnosticCode {
fn from(value: &str) -> Self {
Self(value.to_string())
}
}
impl From<String> for DiagnosticCode {
fn from(value: String) -> Self {
Self(value)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct Diagnostic {
pub severity: DiagnosticSeverity,
pub code: DiagnosticCode,
pub message: String,
pub source: Option<String>,
pub help: Option<String>,
}
impl Diagnostic {
pub fn new(
severity: DiagnosticSeverity,
code: impl Into<DiagnosticCode>,
message: impl Into<String>,
) -> Self {
Self {
severity,
code: code.into(),
message: message.into(),
source: None,
help: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum DiagnosticSeverity {
Info,
Warning,
Error,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeCapabilities {
pub native: bool,
pub server: bool,
pub wasm: bool,
pub mobile: MobileCapability,
pub requirements: Vec<RuntimeRequirement>,
pub max_recommended_input_bytes: Option<u64>,
}
impl RuntimeCapabilities {
pub fn pure_rust() -> Self {
Self {
native: true,
server: true,
wasm: true,
mobile: MobileCapability::Wasm,
requirements: Vec::new(),
max_recommended_input_bytes: None,
}
}
pub fn with_max_recommended_input_bytes(mut self, bytes: u64) -> Self {
self.max_recommended_input_bytes = Some(bytes);
self
}
pub fn with_requirement(
mut self,
name: impl Into<String>,
description: impl Into<String>,
required: bool,
) -> Self {
self.requirements.push(RuntimeRequirement {
name: name.into(),
description: Some(description.into()),
required,
});
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum MobileCapability {
Native,
Wasm,
ApiOnly,
Unsupported,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeRequirement {
pub name: String,
pub description: Option<String>,
pub required: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(transparent)]
pub struct OperationId(pub String);
impl OperationId {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for OperationId {
fn from(value: &str) -> Self {
Self(value.to_string())
}
}
impl From<String> for OperationId {
fn from(value: String) -> Self {
Self(value)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct OperationMetadata {
pub id: OperationId,
pub name: String,
pub description: Option<String>,
pub version: String,
pub capabilities: RuntimeCapabilities,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PackageSurface {
pub library: String,
pub version: String,
pub operations: Vec<SurfaceOperation>,
pub capabilities: RuntimeCapabilities,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SurfaceOperation {
pub id: OperationId,
pub name: String,
pub description: Option<String>,
pub input_schema: serde_json::Value,
pub output_schema: serde_json::Value,
pub example_request: serde_json::Value,
pub wasm_supported: bool,
pub server_supported: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum SurfaceExecutionMode {
InMemory,
PlannedJob,
BackgroundJob,
ExternalCommand,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub enum SurfaceSideEffect {
None,
ReadsFiles,
WritesFiles,
Network,
ExternalProcess,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct SurfaceArtifactExpectation {
pub id: String,
pub kind: String,
pub media_type: String,
pub required: bool,
pub description: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct SurfaceExecutionPlan {
pub operation: OperationId,
pub mode: SurfaceExecutionMode,
pub side_effects: Vec<SurfaceSideEffect>,
pub cancellable: bool,
pub progress_unit: Option<String>,
pub expected_artifacts: Vec<SurfaceArtifactExpectation>,
pub requirements: Vec<RuntimeRequirement>,
pub max_recommended_input_bytes: Option<u64>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SurfaceRequest {
pub operation: OperationId,
pub input: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SurfaceResponse {
pub operation: OperationId,
pub value: serde_json::Value,
pub diagnostics: Vec<Diagnostic>,
pub artifacts: Vec<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SurfaceError {
pub code: String,
pub message: String,
pub operation: Option<OperationId>,
pub details: serde_json::Value,
}
impl SurfaceError {
pub fn invalid_request(
operation: Option<impl Into<OperationId>>,
message: impl Into<String>,
) -> Self {
Self::new("invalid_request", operation, message, serde_json::json!({}))
}
pub fn unsupported_operation(
operation: impl Into<OperationId>,
package: impl Into<String>,
) -> Self {
let operation = operation.into();
let package = package.into();
Self::new(
"unsupported_operation",
Some(operation.clone()),
format!(
"unsupported operation `{}` for {}",
operation.as_str(),
package
),
serde_json::json!({"package": package}),
)
}
pub fn unsupported_value(
operation: Option<impl Into<OperationId>>,
field: impl Into<String>,
value: impl Into<String>,
allowed: &[&str],
) -> Self {
let field = field.into();
let value = value.into();
Self::new(
"unsupported_value",
operation,
format!("unsupported value `{value}` for `{field}`"),
serde_json::json!({
"field": field,
"value": value,
"allowed": allowed
}),
)
}
pub fn resource_limit(
operation: Option<impl Into<OperationId>>,
field: impl Into<String>,
limit: usize,
actual: usize,
) -> Self {
let field = field.into();
Self::new(
"resource_limit",
operation,
format!("`{field}` exceeds the maximum supported size of {limit}"),
serde_json::json!({
"field": field,
"limit": limit,
"actual": actual
}),
)
}
pub fn cancelled(operation: impl Into<OperationId>, message: impl Into<String>) -> Self {
Self::new("cancelled", Some(operation), message, serde_json::json!({}))
}
pub fn execution_failed(
operation: impl Into<OperationId>,
message: impl Into<String>,
details: serde_json::Value,
) -> Self {
Self::new("execution_failed", Some(operation), message, details)
}
pub fn artifact_error(
operation: impl Into<OperationId>,
message: impl Into<String>,
details: serde_json::Value,
) -> Self {
Self::new("artifact_error", Some(operation), message, details)
}
pub fn missing_dependency(
operation: Option<impl Into<OperationId>>,
dependency: impl Into<String>,
setup: impl Into<String>,
) -> Self {
let dependency = dependency.into();
let setup = setup.into();
Self::new(
"missing_dependency",
operation,
format!("missing required dependency `{dependency}`"),
serde_json::json!({
"dependency": dependency,
"setup": setup
}),
)
}
pub fn new(
code: impl Into<String>,
operation: Option<impl Into<OperationId>>,
message: impl Into<String>,
details: serde_json::Value,
) -> Self {
Self {
code: code.into(),
message: message.into(),
operation: operation.map(Into::into),
details,
}
}
pub fn to_error_string(&self) -> String {
serde_json::to_string(self).unwrap_or_else(|_| self.message.clone())
}
}
impl fmt::Display for SurfaceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for SurfaceError {}
pub fn parse_surface_error(error: &str) -> Option<SurfaceError> {
serde_json::from_str(error).ok()
}
pub fn parse_surface_input<T: for<'de> Deserialize<'de>>(
operation: Option<&str>,
input: serde_json::Value,
) -> Result<T, String> {
serde_json::from_value(input).map_err(|error| {
SurfaceError::invalid_request(
operation.map(OperationId::new),
format!("invalid request: {error}"),
)
.to_error_string()
})
}
pub fn require_non_empty<T>(operation: &str, field: &str, values: &[T]) -> Result<(), String> {
if values.is_empty() {
Err(SurfaceError::invalid_request(
Some(OperationId::new(operation)),
format!("invalid request: {field} must not be empty"),
)
.to_error_string())
} else {
Ok(())
}
}
pub fn validate_max_items(
operation: &str,
field: &str,
actual: usize,
limit: usize,
) -> Result<(), String> {
if actual > limit {
Err(
SurfaceError::resource_limit(Some(OperationId::new(operation)), field, limit, actual)
.to_error_string(),
)
} else {
Ok(())
}
}
pub fn validate_matching_lengths(
operation: &str,
left_field: &str,
left_len: usize,
right_field: &str,
right_len: usize,
) -> Result<(), String> {
if left_len != right_len {
Err(SurfaceError::invalid_request(
Some(OperationId::new(operation)),
format!(
"invalid request: `{left_field}` length {left_len} must match `{right_field}` length {right_len}"
),
)
.to_error_string())
} else {
Ok(())
}
}
pub fn surface_operation(
id: impl Into<String>,
name: impl Into<String>,
description: impl Into<String>,
example_request: serde_json::Value,
) -> SurfaceOperation {
let id = id.into();
SurfaceOperation {
id: OperationId::new(id.clone()),
name: name.into(),
description: Some(description.into()),
input_schema: surface_input_schema(&id, &example_request),
output_schema: surface_output_schema(&id),
example_request,
wasm_supported: true,
server_supported: true,
}
}
pub fn surface_operation_with_execution_plan(
id: impl Into<String>,
name: impl Into<String>,
description: impl Into<String>,
example_request: serde_json::Value,
execution_plan: SurfaceExecutionPlan,
) -> SurfaceOperation {
let mut operation = surface_operation(id, name, description, example_request);
let execution_plan = surface_execution_plan_value(&execution_plan);
insert_schema_extension(
&mut operation.input_schema,
"xExecutionPlan",
execution_plan.clone(),
);
insert_schema_extension(
&mut operation.output_schema,
"xExecutionPlan",
execution_plan,
);
operation
}
pub fn surface_execution_plan_value(plan: &SurfaceExecutionPlan) -> serde_json::Value {
serde_json::to_value(plan).unwrap_or_else(|_| serde_json::json!({}))
}
fn insert_schema_extension(schema: &mut serde_json::Value, key: &str, value: serde_json::Value) {
if let serde_json::Value::Object(object) = schema {
object.insert(key.to_string(), value);
}
}
pub fn surface_input_schema(
operation: &str,
example_request: &serde_json::Value,
) -> serde_json::Value {
let properties = match example_request {
serde_json::Value::Object(object) => object
.iter()
.map(|(key, value)| (key.clone(), infer_schema_for_value(key, value)))
.collect::<serde_json::Map<_, _>>(),
_ => serde_json::Map::new(),
};
let required = required_fields_for_operation(operation, example_request);
serde_json::json!({
"type": "object",
"additionalProperties": false,
"properties": properties,
"required": required,
"xOperationCategory": operation_category(operation),
"xReleaseStability": "stable",
"xContractPolicy": "additiveOnly",
"xErrorShape": {
"code": "string",
"message": "string",
"operation": "string|null",
"details": "object"
},
"xResourceLimits": {
"maxRecommendedInputBytes": 1048576,
"largePayloadBehavior": "reject or deterministically truncate by operation-specific limit"
}
})
}
pub fn surface_output_schema(operation: &str) -> serde_json::Value {
serde_json::json!({
"type": "object",
"required": ["operation", "title", "message", "summary", "result"],
"properties": {
"operation": {"type": "string", "const": operation},
"title": {"type": "string", "minLength": 1},
"message": {"type": "string", "minLength": 1},
"summary": {"type": "object"},
"result": {}
},
"additionalProperties": true
})
}
pub fn operation_category(operation: &str) -> &'static str {
match operation {
"describe"
| "analysis.describe"
| "classification.models"
| "classification.schema"
| "embeddings.backends"
| "qa.models" => "debug",
"runtime.softmax" => "support",
_ => "workflow",
}
}
fn required_fields_for_operation(
operation: &str,
example_request: &serde_json::Value,
) -> Vec<String> {
if operation == "describe" || operation.ends_with(".models") || operation.ends_with(".describe")
{
return Vec::new();
}
let optional = [
"dimensions",
"embedding",
"id",
"includeNearDuplicates",
"includePunctuation",
"includeSemanticNeighbors",
"keywordLimit",
"linguistics",
"lowercase",
"maxAlternatives",
"maxTokens",
"minTokensForDecision",
"mode",
"model",
"n",
"ngramSizes",
"normalizeWhitespace",
"options",
"order",
"profile",
"previewLimit",
"seed",
"sentenceLevel",
"shingleSizes",
"streamId",
"summarySentences",
"topK",
"truncation",
];
match example_request {
serde_json::Value::Object(object) => object
.keys()
.filter(|key| !optional.contains(&key.as_str()))
.cloned()
.collect(),
_ => Vec::new(),
}
}
fn infer_schema_for_value(key: &str, value: &serde_json::Value) -> serde_json::Value {
let mut schema = match value {
serde_json::Value::Bool(_) => serde_json::json!({"type": "boolean"}),
serde_json::Value::Number(number) if number.is_i64() || number.is_u64() => {
serde_json::json!({"type": "integer", "minimum": 0})
}
serde_json::Value::Number(_) => serde_json::json!({"type": "number"}),
serde_json::Value::String(_) => serde_json::json!({"type": "string", "minLength": 1}),
serde_json::Value::Array(values) => {
let item_schema = values
.first()
.map(|value| infer_schema_for_value("item", value))
.unwrap_or_else(|| serde_json::json!({}));
serde_json::json!({"type": "array", "items": item_schema, "minItems": 1})
}
serde_json::Value::Object(object) => serde_json::json!({
"type": "object",
"additionalProperties": true,
"properties": object
.iter()
.map(|(key, value)| (key.clone(), infer_schema_for_value(key, value)))
.collect::<serde_json::Map<_, _>>()
}),
serde_json::Value::Null => serde_json::json!({}),
};
if matches!(
key,
"topK" | "top_k" | "maxTokens" | "max_tokens" | "order" | "dimensions" | "n"
) {
if let serde_json::Value::Object(object) = &mut schema {
object.insert("minimum".to_string(), serde_json::json!(1));
object.insert("maximum".to_string(), serde_json::json!(4096));
}
}
schema
}
pub fn describe_surface_response(
surface: &PackageSurface,
request: SurfaceRequest,
) -> SurfaceResponse {
let result = serde_json::json!({
"library": &surface.library,
"version": &surface.version,
"operationCount": surface.operations.len(),
"operations": surface
.operations
.iter()
.map(|operation| operation.id.as_str())
.collect::<Vec<_>>(),
"input": request.input
});
structured_surface_response(
request.operation,
"Package surface metadata",
format!(
"{} exposes {} package-surface operations.",
surface.library,
surface.operations.len()
),
serde_json::json!({
"operationCount": surface.operations.len(),
"runtime": {
"wasm": surface.capabilities.wasm,
"server": surface.capabilities.server,
"native": surface.capabilities.native
}
}),
result,
)
}
pub fn surface_response(operation: OperationId, value: serde_json::Value) -> SurfaceResponse {
let title = operation.as_str().to_string();
let message = format!("Ran package-surface operation `{}`.", operation.as_str());
let value = ensure_structured_surface_value(&operation, title, message, value);
SurfaceResponse {
operation,
value,
diagnostics: Vec::new(),
artifacts: Vec::new(),
}
}
pub fn structured_surface_value(
operation: &OperationId,
title: impl Into<String>,
message: impl Into<String>,
summary: serde_json::Value,
result: serde_json::Value,
) -> serde_json::Value {
let mut object = match &result {
serde_json::Value::Object(map) => map.clone(),
_ => serde_json::Map::new(),
};
object.insert("title".to_string(), serde_json::Value::String(title.into()));
object.insert(
"operation".to_string(),
serde_json::Value::String(operation.as_str().to_string()),
);
object.insert(
"message".to_string(),
serde_json::Value::String(message.into()),
);
object.insert("summary".to_string(), summary);
object.insert("result".to_string(), result);
serde_json::Value::Object(object)
}
pub fn ensure_structured_surface_value(
operation: &OperationId,
title: impl Into<String>,
message: impl Into<String>,
value: serde_json::Value,
) -> serde_json::Value {
let result = value.clone();
let mut object = match value {
serde_json::Value::Object(map) => map,
_ => serde_json::Map::new(),
};
object
.entry("operation".to_string())
.or_insert_with(|| serde_json::Value::String(operation.as_str().to_string()));
object
.entry("title".to_string())
.or_insert_with(|| serde_json::Value::String(title.into()));
object
.entry("message".to_string())
.or_insert_with(|| serde_json::Value::String(message.into()));
object
.entry("summary".to_string())
.or_insert_with(|| operation_summary(&result));
object.entry("result".to_string()).or_insert(result);
serde_json::Value::Object(object)
}
pub fn structured_surface_response(
operation: OperationId,
title: impl Into<String>,
message: impl Into<String>,
summary: serde_json::Value,
result: serde_json::Value,
) -> SurfaceResponse {
let value = structured_surface_value(&operation, title, message, summary, result);
surface_response(operation, value)
}
pub fn structured_operation_response(
surface: &PackageSurface,
operation: OperationId,
result: serde_json::Value,
) -> SurfaceResponse {
let metadata = surface
.operations
.iter()
.find(|candidate| candidate.id.as_str() == operation.as_str());
let title = metadata
.map(|operation| operation.name.clone())
.unwrap_or_else(|| operation.as_str().to_string());
let message = metadata
.and_then(|operation| operation.description.clone())
.unwrap_or_else(|| format!("Ran package-surface operation `{}`.", operation.as_str()));
let summary = operation_summary(&result);
structured_surface_response(operation, title, message, summary, result)
}
fn operation_summary(result: &serde_json::Value) -> serde_json::Value {
match result {
serde_json::Value::Object(object) => {
let mut summary = serde_json::Map::new();
summary.insert("status".to_string(), serde_json::json!("ok"));
for key in [
"count",
"width",
"height",
"format",
"pixelFormat",
"dimensions",
"operationCount",
] {
if let Some(value) = object.get(key) {
summary.insert(key.to_string(), value.clone());
}
}
if let Some((key, value)) = object
.iter()
.find(|(_, value)| matches!(value, serde_json::Value::Array(_)))
{
summary.insert(
format!("{key}Count"),
serde_json::json!(value.as_array().map(Vec::len).unwrap_or(0)),
);
}
serde_json::Value::Object(summary)
}
serde_json::Value::Array(values) => {
serde_json::json!({"status": "ok", "count": values.len()})
}
_ => serde_json::json!({"status": "ok"}),
}
}
pub mod cli {
use std::fs;
use std::io::{self, Read};
use super::{
ensure_structured_surface_value, OperationId, PackageSurface, SurfaceRequest,
SurfaceResponse,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CliAdapterMetadata {
pub library_crate: &'static str,
pub surface_kind: &'static str,
pub library_import: &'static str,
pub server_package: &'static str,
pub app_package: &'static str,
pub wasm_package: &'static str,
}
pub fn package_metadata_json(metadata: CliAdapterMetadata, surface: PackageSurface) -> String {
serde_json::json!({
"package": format!("{}-cli", metadata.library_crate),
"surface": metadata.surface_kind,
"library": metadata.library_crate,
"libraryImport": metadata.library_import,
"serverPackage": metadata.server_package,
"appPackage": metadata.app_package,
"wasmPackage": metadata.wasm_package,
"operations": surface.operations
})
.to_string()
}
pub fn command_schema_json() -> String {
serde_json::json!({
"commands": [
{"name": "info", "description": "Print package and adapter metadata."},
{"name": "schema", "description": "Print the CLI command schema."},
{"name": "operations", "description": "Print library operations."},
{"name": "run", "description": "Run one library-owned operation."}
]
})
.to_string()
}
pub fn read_json_input(
json: Option<String>,
file: Option<String>,
) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
let input = if let Some(json) = json {
json
} else if let Some(file) = file {
fs::read_to_string(file)?
} else {
let mut buffer = String::new();
io::stdin().read_to_string(&mut buffer)?;
if buffer.trim().is_empty() {
"{}".to_string()
} else {
buffer
}
};
Ok(serde_json::from_str(&input)?)
}
pub fn run_wrapped_operation(
operation: &str,
input: serde_json::Value,
runner: fn(SurfaceRequest) -> Result<SurfaceResponse, String>,
) -> Result<SurfaceResponse, String> {
let mut response = runner(SurfaceRequest {
operation: OperationId::new(operation),
input,
})?;
let value = std::mem::take(&mut response.value);
response.value = ensure_structured_surface_value(
&response.operation,
operation.to_string(),
format!("Ran package-surface operation `{}`.", operation),
value,
);
Ok(response)
}
}
pub mod server {
use std::io::{self, BufRead, BufReader, Read, Write};
use std::net::{TcpListener, TcpStream};
use super::{
parse_surface_error, Diagnostic, DiagnosticSeverity, OperationId, PackageSurface,
SurfaceRequest, SurfaceResponse,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ServerAdapterMetadata {
pub library_crate: &'static str,
pub surface_kind: &'static str,
pub library_import: &'static str,
pub cli_package: &'static str,
pub app_package: &'static str,
pub wasm_package: &'static str,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HttpResponse {
pub status_code: u16,
pub reason: &'static str,
pub content_type: &'static str,
pub body: String,
}
pub fn serve(
addr: &str,
metadata: ServerAdapterMetadata,
surface_provider: fn() -> PackageSurface,
runner: fn(SurfaceRequest) -> Result<SurfaceResponse, String>,
) -> io::Result<()> {
let listener = TcpListener::bind(addr)?;
for stream in listener.incoming() {
handle_stream(stream?, metadata, surface_provider, runner)?;
}
Ok(())
}
pub fn response_for(
method: &str,
path: &str,
body: &str,
metadata: ServerAdapterMetadata,
surface_provider: fn() -> PackageSurface,
runner: fn(SurfaceRequest) -> Result<SurfaceResponse, String>,
) -> HttpResponse {
match (method, path) {
("OPTIONS", _) => HttpResponse {
status_code: 204,
reason: "No Content",
content_type: "application/json",
body: String::new(),
},
("GET", "/health") => json_response(
200,
"OK",
serde_json::json!({
"ok": true,
"package": format!("{}-server", metadata.library_crate),
"library": metadata.library_crate
}),
),
("GET", "/api/package") => json_response(
200,
"OK",
package_metadata_value(metadata, surface_provider()),
),
("GET", "/api/schema") => {
json_response(200, "OK", schema_value(metadata, surface_provider()))
}
("GET", "/api/operations") => {
json_response(200, "OK", serde_json::json!(surface_provider().operations))
}
("POST", "/api/run") => run_response(body, metadata, runner),
("POST", path) if path.starts_with("/api/") => {
let operation = path.trim_start_matches("/api/");
run_request(
SurfaceRequest {
operation: OperationId::new(operation),
input: parse_json_or_empty(body),
},
metadata,
runner,
)
}
_ => json_response(
404,
"Not Found",
serde_json::json!({
"error": "not found",
"path": path
}),
),
}
}
pub fn package_metadata_json(
metadata: ServerAdapterMetadata,
surface: PackageSurface,
) -> String {
package_metadata_value(metadata, surface).to_string()
}
fn package_metadata_value(
metadata: ServerAdapterMetadata,
surface: PackageSurface,
) -> serde_json::Value {
serde_json::json!({
"package": format!("{}-server", metadata.library_crate),
"surface": metadata.surface_kind,
"library": metadata.library_crate,
"libraryImport": metadata.library_import,
"cliPackage": metadata.cli_package,
"appPackage": metadata.app_package,
"wasmPackage": metadata.wasm_package,
"endpoints": [
"GET /health",
"GET /api/package",
"GET /api/schema",
"GET /api/operations",
"POST /api/run",
"POST /api/<operation-id>"
],
"runtimeMetadata": {
"candleDevice": serde_json::Value::Null
},
"operations": surface.operations
})
}
fn schema_value(metadata: ServerAdapterMetadata, surface: PackageSurface) -> serde_json::Value {
let operations = surface
.operations
.into_iter()
.map(|operation| {
let path = format!("/api/{}", operation.id.as_str());
(
path,
serde_json::json!({
"post": {
"summary": operation.name,
"description": operation.description,
"requestBody": operation.input_schema,
"responses": {"200": operation.output_schema}
}
}),
)
})
.collect::<serde_json::Map<_, _>>();
serde_json::json!({
"openapi": "3.1.0",
"info": {
"title": format!("{} API", metadata.library_crate),
"version": env!("CARGO_PKG_VERSION")
},
"paths": operations
})
}
fn run_response(
body: &str,
metadata: ServerAdapterMetadata,
runner: fn(SurfaceRequest) -> Result<SurfaceResponse, String>,
) -> HttpResponse {
let payload = match serde_json::from_str::<serde_json::Value>(body) {
Ok(value) => value,
Err(error) => {
return diagnostic_response(
400,
"Bad Request",
"invalid_request",
&format!("invalid JSON: {error}"),
metadata,
);
}
};
let operation = payload
.get("operation")
.and_then(serde_json::Value::as_str)
.unwrap_or("describe")
.to_string();
let input = payload
.get("input")
.cloned()
.unwrap_or_else(|| payload.clone());
run_request(
SurfaceRequest {
operation: OperationId::new(operation),
input,
},
metadata,
runner,
)
}
fn run_request(
request: SurfaceRequest,
metadata: ServerAdapterMetadata,
runner: fn(SurfaceRequest) -> Result<SurfaceResponse, String>,
) -> HttpResponse {
match runner(request) {
Ok(response) => json_response(200, "OK", serde_json::json!(response)),
Err(error) => {
diagnostic_response(400, "Bad Request", "operation_failed", &error, metadata)
}
}
}
fn diagnostic_response(
status_code: u16,
reason: &'static str,
code: &str,
message: &str,
metadata: ServerAdapterMetadata,
) -> HttpResponse {
let parsed = parse_surface_error(message);
let diagnostic_code = parsed
.as_ref()
.map(|error| error.code.as_str())
.unwrap_or(code);
let diagnostic_message = parsed
.as_ref()
.map(|error| error.message.as_str())
.unwrap_or(message);
let details = parsed
.as_ref()
.map(|error| error.details.clone())
.unwrap_or_else(|| serde_json::json!({}));
json_response(
status_code,
reason,
serde_json::json!({
"diagnostics": [Diagnostic {
severity: DiagnosticSeverity::Error,
code: diagnostic_code.into(),
message: diagnostic_message.to_string(),
source: Some(format!("{}-server", metadata.library_crate)),
help: None,
}],
"error": {
"code": diagnostic_code,
"message": diagnostic_message,
"details": details
}
}),
)
}
fn parse_json_or_empty(body: &str) -> serde_json::Value {
if body.trim().is_empty() {
serde_json::json!({})
} else {
serde_json::from_str(body).unwrap_or_else(|_| serde_json::json!({"raw": body}))
}
}
fn handle_stream(
mut stream: TcpStream,
metadata: ServerAdapterMetadata,
surface_provider: fn() -> PackageSurface,
runner: fn(SurfaceRequest) -> Result<SurfaceResponse, String>,
) -> io::Result<()> {
let mut reader = BufReader::new(stream.try_clone()?);
let mut request_line = String::new();
reader.read_line(&mut request_line)?;
let mut content_length = 0usize;
loop {
let mut header = String::new();
reader.read_line(&mut header)?;
let trimmed = header.trim_end();
if trimmed.is_empty() {
break;
}
if let Some((name, value)) = trimmed.split_once(':') {
if name.eq_ignore_ascii_case("content-length") {
content_length = value.trim().parse().unwrap_or(0);
}
}
}
let mut body = vec![0; content_length];
if content_length > 0 {
reader.read_exact(&mut body)?;
}
let body = String::from_utf8_lossy(&body);
let mut parts = request_line.split_whitespace();
let method = parts.next().unwrap_or("GET");
let path = parts.next().unwrap_or("/");
let response = response_for(method, path, &body, metadata, surface_provider, runner);
write_response(&mut stream, response)
}
fn json_response(
status_code: u16,
reason: &'static str,
value: serde_json::Value,
) -> HttpResponse {
HttpResponse {
status_code,
reason,
content_type: "application/json",
body: value.to_string(),
}
}
fn write_response(stream: &mut TcpStream, response: HttpResponse) -> io::Result<()> {
write!(
stream,
"HTTP/1.1 {} {}\r\nContent-Type: {}\r\nContent-Length: {}\r\nAccess-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: content-type\r\nAccess-Control-Allow-Methods: GET, POST, OPTIONS\r\nConnection: close\r\n\r\n{}",
response.status_code,
response.reason,
response.content_type,
response.body.len(),
response.body
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(transparent)]
pub struct JobId(pub String);
impl JobId {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for JobId {
fn from(value: &str) -> Self {
Self(value.to_string())
}
}
impl From<String> for JobId {
fn from(value: String) -> Self {
Self(value)
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[serde(transparent)]
pub struct ArtifactId(pub String);
impl ArtifactId {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for ArtifactId {
fn from(value: &str) -> Self {
Self(value.to_string())
}
}
impl From<String> for ArtifactId {
fn from(value: String) -> Self {
Self(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn diagnostic_uses_camel_case_json() {
let diagnostic = Diagnostic::new(DiagnosticSeverity::Warning, "demo.warning", "check");
let json = serde_json::to_string(&diagnostic).expect("serialize diagnostic");
assert!(json.contains("\"severity\":\"warning\""));
assert!(json.contains("\"code\":\"demo.warning\""));
}
#[test]
fn pure_rust_capabilities_allow_wasm_and_server() {
let capabilities = RuntimeCapabilities::pure_rust();
assert!(capabilities.native);
assert!(capabilities.server);
assert!(capabilities.wasm);
assert_eq!(capabilities.mobile, MobileCapability::Wasm);
}
#[test]
fn capability_builders_preserve_pure_rust_defaults() {
let capabilities = RuntimeCapabilities::pure_rust()
.with_max_recommended_input_bytes(1024)
.with_requirement("fixture", "test fixture input", false);
assert!(capabilities.native);
assert!(capabilities.server);
assert!(capabilities.wasm);
assert_eq!(capabilities.max_recommended_input_bytes, Some(1024));
assert_eq!(capabilities.requirements[0].name, "fixture");
assert!(!capabilities.requirements[0].required);
}
#[test]
fn package_surface_uses_camel_case_json() {
let surface = PackageSurface {
library: "demo-core".to_string(),
version: "0.1.0".to_string(),
capabilities: RuntimeCapabilities::pure_rust(),
operations: vec![SurfaceOperation {
id: OperationId::new("describe"),
name: "Describe".to_string(),
description: Some("Describe package surface".to_string()),
input_schema: serde_json::json!({"type": "object"}),
output_schema: serde_json::json!({"type": "object"}),
example_request: serde_json::json!({}),
wasm_supported: true,
server_supported: true,
}],
};
let json = serde_json::to_string(&surface).expect("serialize surface");
assert!(json.contains("\"inputSchema\""));
assert!(json.contains("\"exampleRequest\""));
assert!(json.contains("\"wasmSupported\":true"));
}
#[test]
fn surface_helpers_preserve_standard_response_shape() {
let surface = PackageSurface {
library: "demo".to_string(),
version: "0.1.0".to_string(),
capabilities: RuntimeCapabilities::pure_rust(),
operations: vec![surface_operation(
"describe",
"Describe",
"Describe demo package",
serde_json::json!({"includeOperations": true}),
)],
};
let response = describe_surface_response(
&surface,
SurfaceRequest {
operation: OperationId::new("describe"),
input: serde_json::json!({"includeOperations": true}),
},
);
assert_eq!(response.operation.as_str(), "describe");
assert_eq!(response.value["library"], "demo");
assert_eq!(response.value["operationCount"], 1);
assert_eq!(response.diagnostics, Vec::new());
assert_eq!(response.artifacts, Vec::<serde_json::Value>::new());
}
#[test]
fn surface_operation_declares_release_contract_schema() {
let operation = surface_operation(
"demo.run",
"Run demo",
"Run a demo workflow",
serde_json::json!({"text": "hello", "topK": 3}),
);
assert_eq!(operation.input_schema["additionalProperties"], false);
assert_eq!(operation.input_schema["xOperationCategory"], "workflow");
assert_eq!(operation.input_schema["xReleaseStability"], "stable");
assert_eq!(
operation.input_schema["required"],
serde_json::json!(["text"])
);
assert_eq!(operation.input_schema["properties"]["topK"]["minimum"], 1);
assert_eq!(operation.output_schema["required"][0], "operation");
}
#[test]
fn typed_surface_errors_roundtrip_for_transport_adapters() {
let error = SurfaceError::unsupported_operation("demo.missing", "demo-package");
let serialized = error.to_error_string();
let parsed = parse_surface_error(&serialized).expect("typed surface error");
assert_eq!(parsed.code, "unsupported_operation");
assert_eq!(parsed.operation.unwrap().as_str(), "demo.missing");
assert!(parsed.message.contains("unsupported operation"));
}
#[test]
fn surface_error_is_standard_error_type() {
let error = SurfaceError::invalid_request(Some("demo.run"), "invalid request: missing id");
let boxed: Box<dyn std::error::Error> = Box::new(error.clone());
assert_eq!(boxed.to_string(), "invalid request: missing id");
assert_eq!(error.to_string(), "invalid request: missing id");
}
#[test]
fn validation_helpers_return_typed_errors() {
let limit = validate_max_items("demo.run", "values", 3, 2).expect_err("limit error");
let parsed = parse_surface_error(&limit).expect("typed resource error");
assert_eq!(parsed.code, "resource_limit");
assert_eq!(parsed.details["field"], "values");
assert_eq!(parsed.details["actual"], 3);
let length =
validate_matching_lengths("demo.run", "left", 2, "right", 3).expect_err("length");
let parsed = parse_surface_error(&length).expect("typed length error");
assert_eq!(parsed.code, "invalid_request");
assert!(parsed.message.contains("left"));
assert!(parsed.message.contains("right"));
}
#[test]
fn execution_plan_serializes_to_schema_extension() {
let plan = SurfaceExecutionPlan {
operation: OperationId::new("demo.run"),
mode: SurfaceExecutionMode::PlannedJob,
side_effects: vec![SurfaceSideEffect::None],
cancellable: true,
progress_unit: Some("items".to_string()),
expected_artifacts: vec![SurfaceArtifactExpectation {
id: "report".to_string(),
kind: "json".to_string(),
media_type: "application/json".to_string(),
required: true,
description: Some("Structured report".to_string()),
}],
requirements: vec![RuntimeRequirement {
name: "runtime-core".to_string(),
description: Some("Pure Rust planner".to_string()),
required: true,
}],
max_recommended_input_bytes: Some(1024),
};
let operation = surface_operation_with_execution_plan(
"demo.run",
"Run demo",
"Build a deterministic demo plan",
serde_json::json!({"items": [1]}),
plan,
);
assert_eq!(
operation.input_schema["xExecutionPlan"]["mode"],
serde_json::json!("plannedJob")
);
assert_eq!(
operation.output_schema["xExecutionPlan"]["expectedArtifacts"][0]["id"],
serde_json::json!("report")
);
assert_eq!(
operation.input_schema["xExecutionPlan"]["sideEffects"],
serde_json::json!(["none"])
);
}
#[test]
fn new_surface_error_constructors_are_typed_json() {
let cancelled = SurfaceError::cancelled("demo.run", "cancelled by request");
assert_eq!(cancelled.code, "cancelled");
assert_eq!(cancelled.operation.unwrap().as_str(), "demo.run");
let execution = SurfaceError::execution_failed(
"demo.run",
"execution failed",
serde_json::json!({
"stage": "prepare"
}),
);
assert_eq!(execution.code, "execution_failed");
assert_eq!(execution.details["stage"], "prepare");
let artifact = SurfaceError::artifact_error(
"demo.run",
"artifact invalid",
serde_json::json!({
"artifact": "report"
}),
);
assert_eq!(artifact.code, "artifact_error");
assert_eq!(artifact.details["artifact"], "report");
}
#[test]
fn structured_operation_response_preserves_result_fields() {
let surface = PackageSurface {
library: "demo".to_string(),
version: "0.1.0".to_string(),
capabilities: RuntimeCapabilities::pure_rust(),
operations: vec![surface_operation(
"demo.run",
"Run demo",
"Run a demo workflow",
serde_json::json!({"values": [1, 2]}),
)],
};
let response = structured_operation_response(
&surface,
OperationId::new("demo.run"),
serde_json::json!({"count": 2, "values": [1, 2]}),
);
assert_eq!(response.value["count"], 2);
assert_eq!(response.value["operation"], "demo.run");
assert_eq!(response.value["title"], "Run demo");
assert_eq!(response.value["summary"]["count"], 2);
assert_eq!(
response.value["result"]["values"],
serde_json::json!([1, 2])
);
}
}