use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KernelIntent {
pub task: String,
pub criteria: Vec<String>,
pub max_tokens: usize,
}
impl KernelIntent {
#[must_use]
pub fn new(task: impl Into<String>) -> Self {
Self {
task: task.into(),
criteria: Vec::new(),
max_tokens: 1024,
}
}
#[must_use]
pub fn with_criteria(mut self, criteria: impl Into<String>) -> Self {
self.criteria.push(criteria.into());
self
}
#[must_use]
pub fn with_max_tokens(mut self, max_tokens: usize) -> Self {
self.max_tokens = max_tokens;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KernelContext {
pub state: HashMap<String, serde_json::Value>,
pub facts: Vec<ContextFact>,
pub tenant_id: Option<String>,
}
impl KernelContext {
#[must_use]
pub fn new() -> Self {
Self {
state: HashMap::new(),
facts: Vec::new(),
tenant_id: None,
}
}
#[must_use]
pub fn with_state(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
self.state.insert(key.into(), value);
self
}
#[must_use]
pub fn with_fact(
mut self,
key: impl Into<String>,
id: impl Into<String>,
content: impl Into<String>,
) -> Self {
self.facts.push(ContextFact {
key: key.into(),
id: id.into(),
content: content.into(),
});
self
}
#[must_use]
pub fn with_tenant(mut self, tenant_id: impl Into<String>) -> Self {
self.tenant_id = Some(tenant_id.into());
self
}
}
impl Default for KernelContext {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContextFact {
pub key: String,
pub id: String,
pub content: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KernelPolicy {
pub adapter_id: Option<String>,
pub recall_enabled: bool,
pub recall_max_candidates: usize,
pub recall_min_score: f32,
pub seed: Option<u64>,
pub requires_human: bool,
pub required_truths: Vec<String>,
}
impl KernelPolicy {
#[must_use]
pub fn new() -> Self {
Self {
adapter_id: None,
recall_enabled: false,
recall_max_candidates: 5,
recall_min_score: 0.7,
seed: None,
requires_human: false,
required_truths: Vec::new(),
}
}
#[must_use]
pub fn deterministic(seed: u64) -> Self {
Self {
seed: Some(seed),
..Self::new()
}
}
#[must_use]
pub fn with_adapter(mut self, adapter_id: impl Into<String>) -> Self {
self.adapter_id = Some(adapter_id.into());
self
}
#[must_use]
pub fn with_recall(mut self, enabled: bool) -> Self {
self.recall_enabled = enabled;
self
}
#[must_use]
pub fn with_human_required(mut self) -> Self {
self.requires_human = true;
self
}
#[must_use]
pub fn with_required_truth(mut self, truth: impl Into<String>) -> Self {
self.required_truths.push(truth.into());
self
}
}
impl Default for KernelPolicy {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum RiskTier {
Low,
Medium,
High,
Critical,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DataClassification {
Public,
Internal,
Confidential,
Restricted,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoutingPolicy {
pub truth_preferences: HashMap<String, String>,
pub risk_tier_backends: HashMap<RiskTier, Vec<String>>,
pub data_classification_backends: HashMap<DataClassification, Vec<String>>,
pub default_backend: String,
}
impl Default for RoutingPolicy {
fn default() -> Self {
Self {
truth_preferences: HashMap::new(),
risk_tier_backends: HashMap::new(),
data_classification_backends: HashMap::new(),
default_backend: "local".to_string(),
}
}
}
impl RoutingPolicy {
#[must_use]
pub fn default_deny_remote() -> Self {
let mut policy = Self::default();
policy
.risk_tier_backends
.insert(RiskTier::Critical, vec!["local".to_string()]);
policy
.risk_tier_backends
.insert(RiskTier::High, vec!["local".to_string()]);
policy
.data_classification_backends
.insert(DataClassification::Restricted, vec!["local".to_string()]);
policy
.data_classification_backends
.insert(DataClassification::Confidential, vec!["local".to_string()]);
policy
}
#[must_use]
pub fn is_backend_allowed(
&self,
backend_name: &str,
risk_tier: RiskTier,
data_classification: DataClassification,
) -> bool {
if let Some(allowed) = self.risk_tier_backends.get(&risk_tier) {
if !allowed.contains(&backend_name.to_string()) && !allowed.is_empty() {
return false;
}
}
if let Some(allowed) = self.data_classification_backends.get(&data_classification) {
if !allowed.contains(&backend_name.to_string()) && !allowed.is_empty() {
return false;
}
}
true
}
#[must_use]
pub fn select_backend(
&self,
truth_ids: &[String],
risk_tier: RiskTier,
data_classification: DataClassification,
) -> &str {
for truth_id in truth_ids {
if let Some(backend) = self.truth_preferences.get(truth_id) {
return backend;
}
}
if let Some(backends) = self.risk_tier_backends.get(&risk_tier) {
if let Some(backend) = backends.first() {
return backend;
}
}
if let Some(backends) = self.data_classification_backends.get(&data_classification) {
if let Some(backend) = backends.first() {
return backend;
}
}
&self.default_backend
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum DecisionStep {
Reasoning,
Evaluation,
Planning,
}
impl DecisionStep {
#[must_use]
pub fn expected_contract(&self) -> &'static str {
match self {
Self::Reasoning => "Reasoning",
Self::Evaluation => "Evaluation",
Self::Planning => "Planning",
}
}
#[must_use]
pub fn as_str(&self) -> &'static str {
match self {
Self::Reasoning => "reasoning",
Self::Evaluation => "evaluation",
Self::Planning => "planning",
}
}
}
impl Default for DecisionStep {
fn default() -> Self {
Self::Reasoning
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum ReplayTrace {
Local(LocalReplayTrace),
Remote(RemoteReplayTrace),
}
impl ReplayTrace {
#[must_use]
pub fn is_replay_eligible(&self) -> bool {
matches!(self, ReplayTrace::Local(_))
}
#[must_use]
pub fn replayability(&self) -> Replayability {
match self {
ReplayTrace::Local(_) => Replayability::Deterministic,
ReplayTrace::Remote(r) => r.replayability,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Replayability {
Deterministic,
BestEffort,
None,
}
impl Default for Replayability {
fn default() -> Self {
Self::None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ReplayabilityDowngradeReason {
RecallEmbedderNotDeterministic,
RecallCorpusNotContentAddressed,
RemoteBackendUsed,
NoSeedProvided,
MultipleReasons,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocalReplayTrace {
pub base_model_hash: String,
pub adapter: Option<AdapterTrace>,
pub tokenizer_hash: String,
pub seed: u64,
pub sampler: SamplerParams,
pub prompt_version: String,
pub recall: Option<RecallTrace>,
pub weights_mutated: bool,
pub execution_env: ExecutionEnv,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AdapterTrace {
pub adapter_id: String,
pub adapter_hash: String,
pub merged: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SamplerParams {
pub temperature: f32,
pub top_p: f32,
pub top_k: Option<usize>,
}
impl Default for SamplerParams {
fn default() -> Self {
Self {
temperature: 0.0,
top_p: 1.0,
top_k: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecallTrace {
pub corpus_fingerprint: String,
pub candidate_ids: Vec<String>,
pub candidate_scores: Vec<f32>,
pub injected_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionEnv {
pub device: String,
pub backend: String,
pub precision: String,
}
impl Default for ExecutionEnv {
fn default() -> Self {
Self {
device: "cpu".to_string(),
backend: "ndarray".to_string(),
precision: "f32".to_string(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RemoteReplayTrace {
pub provider_name: String,
pub provider_model_id: String,
pub request_fingerprint: String,
pub response_fingerprint: String,
pub temperature: f32,
pub top_p: f32,
pub max_tokens: usize,
pub provider_metadata: HashMap<String, String>,
pub retried: bool,
pub retry_reasons: Vec<String>,
pub replayability: Replayability,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ProposalKind {
Claims,
Plan,
Classification,
Evaluation,
DraftDocument,
Reasoning,
}
impl Default for ProposalKind {
fn default() -> Self {
Self::Reasoning
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ContentKind {
Claim,
Plan,
Classification,
Evaluation,
Draft,
Reasoning,
}
impl Default for ContentKind {
fn default() -> Self {
Self::Reasoning
}
}
impl From<ContentKind> for ProposalKind {
fn from(kind: ContentKind) -> Self {
match kind {
ContentKind::Claim => ProposalKind::Claims,
ContentKind::Plan => ProposalKind::Plan,
ContentKind::Classification => ProposalKind::Classification,
ContentKind::Evaluation => ProposalKind::Evaluation,
ContentKind::Draft => ProposalKind::DraftDocument,
ContentKind::Reasoning => ProposalKind::Reasoning,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProposedContent {
pub id: String,
pub kind: ContentKind,
pub content: String,
pub structured: Option<serde_json::Value>,
pub confidence: Option<f32>,
pub requires_human: bool,
}
impl ProposedContent {
#[must_use]
pub fn new(id: impl Into<String>, kind: ContentKind, content: impl Into<String>) -> Self {
Self {
id: id.into(),
kind,
content: content.into(),
structured: None,
confidence: None,
requires_human: false,
}
}
#[must_use]
pub fn with_human_required(mut self) -> Self {
self.requires_human = true;
self
}
#[must_use]
pub fn with_confidence(mut self, confidence: f32) -> Self {
self.confidence = Some(confidence);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContractResult {
pub name: String,
pub passed: bool,
pub failure_reason: Option<String>,
}
impl ContractResult {
#[must_use]
pub fn passed(name: impl Into<String>) -> Self {
Self {
name: name.into(),
passed: true,
failure_reason: None,
}
}
#[must_use]
pub fn failed(name: impl Into<String>, reason: impl Into<String>) -> Self {
Self {
name: name.into(),
passed: false,
failure_reason: Some(reason.into()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct KernelProposal {
pub id: String,
pub kind: ProposalKind,
pub payload: String,
pub structured_payload: Option<serde_json::Value>,
pub trace_link: ReplayTrace,
pub contract_results: Vec<ContractResult>,
pub requires_human: bool,
pub confidence: Option<f32>,
}
impl KernelProposal {
#[must_use]
pub fn all_contracts_passed(&self) -> bool {
self.contract_results.iter().all(|r| r.passed)
}
#[must_use]
pub fn failed_contracts(&self) -> Vec<&str> {
self.contract_results
.iter()
.filter(|r| !r.passed)
.map(|r| r.name.as_str())
.collect()
}
#[must_use]
pub fn is_auto_promotable(&self) -> bool {
self.all_contracts_passed() && !self.requires_human
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn trace_link_replayability() {
let local = ReplayTrace::Local(LocalReplayTrace {
base_model_hash: "abc123".to_string(),
adapter: None,
tokenizer_hash: "tok123".to_string(),
seed: 42,
sampler: SamplerParams::default(),
prompt_version: "v1".to_string(),
recall: None,
weights_mutated: false,
execution_env: ExecutionEnv::default(),
});
let remote = ReplayTrace::Remote(RemoteReplayTrace {
provider_name: "anthropic".to_string(),
provider_model_id: "claude-3-opus".to_string(),
request_fingerprint: "req123".to_string(),
response_fingerprint: "resp456".to_string(),
temperature: 0.0,
top_p: 1.0,
max_tokens: 1024,
provider_metadata: HashMap::new(),
retried: false,
retry_reasons: vec![],
replayability: Replayability::BestEffort,
});
assert!(local.is_replay_eligible());
assert!(!remote.is_replay_eligible());
assert_eq!(local.replayability(), Replayability::Deterministic);
assert_eq!(remote.replayability(), Replayability::BestEffort);
}
#[test]
fn proposal_kind_conversion() {
assert_eq!(ProposalKind::from(ContentKind::Claim), ProposalKind::Claims);
assert_eq!(ProposalKind::from(ContentKind::Plan), ProposalKind::Plan);
assert_eq!(
ProposalKind::from(ContentKind::Reasoning),
ProposalKind::Reasoning
);
}
#[test]
fn contract_result_helpers() {
let passed = ContractResult::passed("grounded-answering");
assert!(passed.passed);
assert!(passed.failure_reason.is_none());
let failed = ContractResult::failed("reasoning", "missing CONCLUSION");
assert!(!failed.passed);
assert_eq!(failed.failure_reason.as_deref(), Some("missing CONCLUSION"));
}
#[test]
fn kernel_proposal_auto_promotable() {
let local_trace = ReplayTrace::Local(LocalReplayTrace {
base_model_hash: "hash".to_string(),
adapter: None,
tokenizer_hash: "tok".to_string(),
seed: 1,
sampler: SamplerParams::default(),
prompt_version: "v1".to_string(),
recall: None,
weights_mutated: false,
execution_env: ExecutionEnv::default(),
});
let promotable = KernelProposal {
id: "p1".to_string(),
kind: ProposalKind::Claims,
payload: "claim".to_string(),
structured_payload: None,
trace_link: local_trace.clone(),
contract_results: vec![ContractResult::passed("c1")],
requires_human: false,
confidence: Some(0.9),
};
assert!(promotable.is_auto_promotable());
let needs_human = KernelProposal {
id: "p2".to_string(),
kind: ProposalKind::Claims,
payload: "claim".to_string(),
structured_payload: None,
trace_link: local_trace.clone(),
contract_results: vec![ContractResult::passed("c1")],
requires_human: true,
confidence: Some(0.9),
};
assert!(!needs_human.is_auto_promotable());
let failed_contract = KernelProposal {
id: "p3".to_string(),
kind: ProposalKind::Claims,
payload: "claim".to_string(),
structured_payload: None,
trace_link: local_trace,
contract_results: vec![ContractResult::failed("c1", "reason")],
requires_human: false,
confidence: Some(0.9),
};
assert!(!failed_contract.is_auto_promotable());
}
#[test]
fn kernel_intent_builder() {
let intent = KernelIntent::new("analyze_metrics")
.with_criteria("find anomalies")
.with_criteria("suggest fixes")
.with_max_tokens(512);
assert_eq!(intent.task, "analyze_metrics");
assert_eq!(intent.criteria.len(), 2);
assert_eq!(intent.criteria[0], "find anomalies");
assert_eq!(intent.criteria[1], "suggest fixes");
assert_eq!(intent.max_tokens, 512);
}
#[test]
fn kernel_context_builder() {
let context = KernelContext::new()
.with_state("metric", serde_json::json!(0.5))
.with_fact("Seeds", "seed-1", "Some seed fact")
.with_tenant("tenant-123");
assert!(context.state.contains_key("metric"));
assert_eq!(context.facts.len(), 1);
assert_eq!(context.facts[0].key, "Seeds");
assert_eq!(context.facts[0].id, "seed-1");
assert_eq!(context.tenant_id, Some("tenant-123".to_string()));
}
#[test]
fn kernel_context_default() {
let context = KernelContext::default();
assert!(context.state.is_empty());
assert!(context.facts.is_empty());
assert!(context.tenant_id.is_none());
}
#[test]
fn kernel_policy_default() {
let policy = KernelPolicy::default();
assert!(policy.adapter_id.is_none());
assert!(!policy.recall_enabled);
assert_eq!(policy.recall_max_candidates, 5);
assert!((policy.recall_min_score - 0.7).abs() < f32::EPSILON);
assert!(policy.seed.is_none());
assert!(!policy.requires_human);
assert!(policy.required_truths.is_empty());
}
#[test]
fn kernel_policy_deterministic() {
let policy = KernelPolicy::deterministic(42)
.with_adapter("llm/grounded@1.0.0")
.with_recall(true)
.with_human_required()
.with_required_truth("grounded-answering");
assert_eq!(policy.seed, Some(42));
assert_eq!(policy.adapter_id, Some("llm/grounded@1.0.0".to_string()));
assert!(policy.recall_enabled);
assert!(policy.requires_human);
assert_eq!(policy.required_truths, vec!["grounded-answering"]);
}
}