use serde::{Deserialize, Serialize};
#[cfg(feature = "ts-rs")]
use ts_rs::TS;
use crate::observation::ObservationKind;
pub const OBSERVATION_REQUIRED_FIELDS: &[&str] = &["content", "provenance", "scopeIds", "ts"];
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "ts-rs", derive(TS), ts(export, export_to = "../bindings/"))]
#[serde(rename_all = "camelCase")]
pub struct KindRegistryEntry {
pub kind: String,
pub required_fields: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(feature = "ts-rs", derive(TS), ts(export, export_to = "../bindings/"))]
#[serde(rename_all = "camelCase")]
pub struct Capability {
pub version: String,
pub schema_version: u32,
pub supported_kinds: Vec<String>,
pub storage_path: String,
pub kind_registry: Vec<KindRegistryEntry>,
}
impl ObservationKind {
pub fn wire_name(self) -> &'static str {
match self {
ObservationKind::ToolCall => "tool_call",
ObservationKind::Command => "command",
ObservationKind::FileDiff => "file_diff",
ObservationKind::Error => "error",
ObservationKind::Message => "message",
ObservationKind::NodeStart => "node_start",
ObservationKind::NodeEnd => "node_end",
ObservationKind::NodeOutput => "node_output",
ObservationKind::NodeError => "node_error",
}
}
pub fn documented_provenance_fields(self) -> &'static [&'static str] {
match self {
ObservationKind::ToolCall => &["toolName", "hasError"],
ObservationKind::Command => &["toolName", "hasError", "command"],
ObservationKind::FileDiff => &["toolName", "hasError", "filePath"],
ObservationKind::Error => &["source"],
ObservationKind::Message => &["role", "length"],
ObservationKind::NodeStart => &["nodeName"],
ObservationKind::NodeEnd => &["nodeName", "duration", "status"],
ObservationKind::NodeOutput => &["nodeName", "outputType", "duration"],
ObservationKind::NodeError => &["nodeName", "errorType", "errorMessage"],
}
}
pub fn required_fields(self) -> Vec<String> {
let mut fields: Vec<String> = OBSERVATION_REQUIRED_FIELDS
.iter()
.map(|s| (*s).to_string())
.collect();
fields.extend(
self.documented_provenance_fields()
.iter()
.map(|s| (*s).to_string()),
);
fields
}
}
pub fn supported_kind_names() -> Vec<String> {
ObservationKind::ALL
.iter()
.map(|k| k.wire_name().to_string())
.collect()
}
pub fn kind_registry() -> Vec<KindRegistryEntry> {
ObservationKind::ALL
.iter()
.map(|&kind| KindRegistryEntry {
kind: kind.wire_name().to_string(),
required_fields: kind.required_fields(),
})
.collect()
}
pub fn build_capability(
version: impl Into<String>,
schema_version: u32,
storage_path: impl Into<String>,
) -> Capability {
Capability {
version: version.into(),
schema_version,
supported_kinds: supported_kind_names(),
storage_path: storage_path.into(),
kind_registry: kind_registry(),
}
}