use sim_citizen_derive::non_citizen;
use sim_kernel::{CapabilityName, Cx, Expr, Object, ObjectCompat, Result, ShapeRef, Symbol, Value};
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SkillRole {
Tool,
Model,
Resource,
Prompt,
Memory,
Retriever,
Judge,
Router,
}
impl SkillRole {
pub fn as_symbol(&self) -> Symbol {
Symbol::new(match self {
Self::Tool => "tool",
Self::Model => "model",
Self::Resource => "resource",
Self::Prompt => "prompt",
Self::Memory => "memory",
Self::Retriever => "retriever",
Self::Judge => "judge",
Self::Router => "router",
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SkillPrivacyPolicy {
MetadataOnly,
NoRaw,
LocalOnly,
AllowRaw,
}
impl SkillPrivacyPolicy {
pub fn as_symbol(&self) -> Symbol {
Symbol::new(match self {
Self::MetadataOnly => "metadata-only",
Self::NoRaw => "no-raw",
Self::LocalOnly => "local-only",
Self::AllowRaw => "allow-raw",
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SkillCacheMode {
Disabled,
ReadThrough,
ReadOnly,
WriteOnly,
Refresh,
}
impl SkillCacheMode {
pub fn as_symbol(&self) -> Symbol {
Symbol::new(match self {
Self::Disabled => "disabled",
Self::ReadThrough => "read-through",
Self::ReadOnly => "read-only",
Self::WriteOnly => "write-only",
Self::Refresh => "refresh",
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum SkillCassetteMode {
Disabled,
RecordReplay,
ReplayOnly,
RecordOnly,
}
impl SkillCassetteMode {
pub fn as_symbol(&self) -> Symbol {
Symbol::new(match self {
Self::Disabled => "disabled",
Self::RecordReplay => "record-replay",
Self::ReplayOnly => "replay-only",
Self::RecordOnly => "record-only",
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SkillPolicy {
pub privacy: SkillPrivacyPolicy,
pub cache: SkillCacheMode,
pub cassette: SkillCassetteMode,
pub idempotent: bool,
pub semantic_key: Option<String>,
}
impl Default for SkillPolicy {
fn default() -> Self {
Self {
privacy: SkillPrivacyPolicy::NoRaw,
cache: SkillCacheMode::Disabled,
cassette: SkillCassetteMode::Disabled,
idempotent: false,
semantic_key: None,
}
}
}
#[derive(Clone)]
#[non_citizen(
reason = "shape-bearing runtime skill card; serializable projection is skill/Card descriptor",
kind = "handle"
)]
pub struct SkillCard {
pub id: String,
pub symbol: Symbol,
pub aliases: Vec<Symbol>,
pub origin: Symbol,
pub title: String,
pub description: String,
pub input_shape: ShapeRef,
pub output_shape: ShapeRef,
pub roles: Vec<SkillRole>,
pub capabilities: Vec<CapabilityName>,
pub policy: SkillPolicy,
pub transport_id: String,
pub transport_kind: String,
pub operation: String,
}
pub struct FixtureSkillSpec {
pub id: String,
pub symbol: Symbol,
pub title: String,
pub description: String,
pub input_shape: ShapeRef,
pub output_shape: ShapeRef,
pub transport_id: String,
pub operation: String,
}
impl SkillCard {
pub fn fixture(spec: FixtureSkillSpec) -> Self {
let id = spec.id;
Self {
capabilities: vec![crate::skill_specific_call_capability(&id)],
id,
symbol: spec.symbol,
aliases: Vec::new(),
origin: Symbol::new("fixture"),
title: spec.title,
description: spec.description,
input_shape: spec.input_shape,
output_shape: spec.output_shape,
roles: vec![SkillRole::Tool],
policy: SkillPolicy::default(),
transport_id: spec.transport_id,
transport_kind: "fixture".to_owned(),
operation: spec.operation,
}
}
pub fn with_capability(mut self, capability: CapabilityName) -> Self {
self.capabilities.push(capability);
self
}
pub fn with_role(mut self, role: SkillRole) -> Self {
if !self.roles.contains(&role) {
self.roles.push(role);
}
self
}
pub fn with_policy(mut self, policy: SkillPolicy) -> Self {
self.policy = policy;
self
}
pub fn with_cache_mode(mut self, cache: SkillCacheMode) -> Self {
self.policy.cache = cache;
self
}
pub fn with_cassette_mode(mut self, cassette: SkillCassetteMode) -> Self {
self.policy.cassette = cassette;
self
}
pub fn with_idempotent(mut self, idempotent: bool) -> Self {
self.policy.idempotent = idempotent;
self
}
pub fn with_semantic_key(mut self, semantic_key: impl Into<String>) -> Self {
self.policy.semantic_key = Some(semantic_key.into());
self
}
pub fn with_privacy(mut self, privacy: SkillPrivacyPolicy) -> Self {
self.policy.privacy = privacy;
self
}
pub fn value(&self, cx: &mut Cx) -> Result<Value> {
cx.factory().opaque(std::sync::Arc::new(self.clone()))
}
pub fn table_value(&self, cx: &mut Cx) -> Result<Value> {
let aliases = cx.factory().list(
self.aliases
.iter()
.map(|alias| cx.factory().symbol(alias.clone()))
.collect::<Result<Vec<_>>>()?,
)?;
let roles = cx.factory().list(
self.roles
.iter()
.map(|role| cx.factory().symbol(role.as_symbol()))
.collect::<Result<Vec<_>>>()?,
)?;
let capabilities = cx.factory().list(
self.capabilities
.iter()
.map(|capability| cx.factory().string(capability.as_str().to_owned()))
.collect::<Result<Vec<_>>>()?,
)?;
let transport = cx.factory().table(vec![
(
Symbol::new("id"),
cx.factory().string(self.transport_id.clone())?,
),
(
Symbol::new("kind"),
cx.factory()
.symbol(Symbol::new(self.transport_kind.clone()))?,
),
(
Symbol::new("operation"),
cx.factory().string(self.operation.clone())?,
),
])?;
let mut policy = vec![
(
Symbol::new("privacy"),
cx.factory().symbol(self.policy.privacy.as_symbol())?,
),
(
Symbol::new("cache"),
cx.factory().symbol(self.policy.cache.as_symbol())?,
),
(
Symbol::new("cassette"),
cx.factory().symbol(self.policy.cassette.as_symbol())?,
),
(
Symbol::new("idempotent"),
cx.factory().bool(self.policy.idempotent)?,
),
];
if let Some(semantic_key) = &self.policy.semantic_key {
policy.push((
Symbol::new("semantic-key"),
cx.factory().string(semantic_key.clone())?,
));
}
let policy = cx.factory().table(policy)?;
cx.factory().table(vec![
(
Symbol::new("kind"),
cx.factory().symbol(Symbol::qualified("skill", "card"))?,
),
(Symbol::new("id"), cx.factory().string(self.id.clone())?),
(
Symbol::new("symbol"),
cx.factory().symbol(self.symbol.clone())?,
),
(Symbol::new("aliases"), aliases),
(
Symbol::new("origin"),
cx.factory().symbol(self.origin.clone())?,
),
(
Symbol::new("title"),
cx.factory().string(self.title.clone())?,
),
(
Symbol::new("description"),
cx.factory().string(self.description.clone())?,
),
(Symbol::new("input-shape"), self.input_shape.clone()),
(Symbol::new("output-shape"), self.output_shape.clone()),
(Symbol::new("roles"), roles),
(Symbol::new("capabilities"), capabilities),
(Symbol::new("policy"), policy),
(Symbol::new("transport"), transport),
])
}
}
impl Object for SkillCard {
fn display(&self, _cx: &mut Cx) -> Result<String> {
Ok(format!("#<skill-card {}>", self.id))
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl ObjectCompat for SkillCard {
fn as_expr(&self, cx: &mut Cx) -> Result<Expr> {
self.to_expr(cx)
}
fn as_table(&self, cx: &mut Cx) -> Result<Value> {
self.table_value(cx)
}
}