agent_runtime_definition_builder/
skill.rs1use async_trait::async_trait;
2use runtime_extension_api::{
3 ContextEvent, ContextHook, ContextPatch, ContextResource, ContextSensitivity, ContextSlot,
4 ExtensionContext, ExtensionError,
5};
6use sha2::{Digest, Sha256};
7
8use crate::DefinitionBuildError;
9
10pub struct StaticSkill {
16 id: String,
17 content: String,
18 version: String,
19 required: bool,
20 sensitivity: ContextSensitivity,
21 digest: String,
22}
23
24impl StaticSkill {
25 pub fn new(
26 id: impl Into<String>,
27 content: impl Into<String>,
28 ) -> Result<Self, DefinitionBuildError> {
29 let id = id.into();
30 let content = content.into();
31 if id.trim().is_empty() || content.trim().is_empty() {
32 return Err(DefinitionBuildError::InvalidSkill(
33 "id and content must not be empty".into(),
34 ));
35 }
36 let digest = format!("sha256:{:x}", Sha256::digest(content.as_bytes()));
37 Ok(Self {
38 id,
39 content,
40 version: "1".into(),
41 required: false,
42 sensitivity: ContextSensitivity::Internal,
43 digest,
44 })
45 }
46
47 pub fn version(mut self, value: impl Into<String>) -> Self {
48 self.version = value.into();
49 self
50 }
51
52 pub fn required(mut self, value: bool) -> Self {
53 self.required = value;
54 self
55 }
56
57 pub fn sensitivity(mut self, value: ContextSensitivity) -> Self {
58 self.sensitivity = value;
59 self
60 }
61}
62
63#[async_trait]
64impl ContextHook for StaticSkill {
65 fn name(&self) -> &str {
66 &self.id
67 }
68
69 fn definition_fingerprint(&self) -> String {
70 format!(
71 "static-skill:{}:{}:{}:{:?}",
72 self.version, self.digest, self.required, self.sensitivity
73 )
74 }
75
76 async fn apply(
77 &self,
78 _: &ExtensionContext,
79 _: &ContextEvent,
80 _: &runtime_extension_api::ContextFrame,
81 ) -> Result<ContextPatch, ExtensionError> {
82 Ok(ContextPatch {
83 resources: vec![ContextResource {
84 id: self.id.clone(),
85 slot: ContextSlot::Skills,
86 source: format!("definition:skill:{}", self.id),
87 content: self.content.clone(),
88 required: self.required,
89 sensitivity: self.sensitivity,
90 version: self.version.clone(),
91 digest: self.digest.clone(),
92 estimated_tokens: self.content.len().div_ceil(4) as u64,
93 }],
94 ..ContextPatch::default()
95 })
96 }
97}