tea_context/providers/
tool_hints.rs1use std::str::FromStr;
2
3use crate::{
4 BudgetBehavior, CacheScope, ContextError, ContextProvider, ContextProviderFuture,
5 ContextProviderId, ContextRequest, PromptAuthority, PromptModule, PromptModuleId,
6 PromptPriority, PromptProvenance, PromptSegment, PromptSegmentId, TrustLevel,
7};
8
9#[derive(Debug, Clone)]
11pub struct ToolHintProvider {
12 id: ContextProviderId,
13}
14
15impl ToolHintProvider {
16 pub fn new() -> Result<Self, crate::ContextIdentityError> {
22 Ok(Self {
23 id: ContextProviderId::from_str("builtin.tool_hints")?,
24 })
25 }
26}
27
28impl ContextProvider for ToolHintProvider {
29 fn id(&self) -> &ContextProviderId {
30 &self.id
31 }
32
33 fn provide(&self, request: ContextRequest) -> ContextProviderFuture<'_> {
34 let provider_id = self.id.clone();
35 Box::pin(async move {
36 let snippets = request
37 .active_tools()
38 .iter()
39 .filter_map(|tool| tool.prompt_snippet().map(|snippet| (tool, snippet)))
40 .map(|(tool, snippet)| {
41 let segment_id = format!("tool.{}.snippet", tool.name().as_str());
42 let locator = format!("{}@{}", tool.name(), tool.version());
43 PromptSegment::new(
44 PromptSegmentId::from_str(&segment_id).map_err(value_error)?,
45 format!("Tool `{}`: {snippet}", tool.name()),
46 PromptProvenance::new(provider_id.clone(), "tool_spec", Some(locator))
47 .map_err(value_error)?,
48 TrustLevel::Delegated,
49 CacheScope::Profile,
50 BudgetBehavior::Omit,
51 )
52 .map_err(value_error)
53 })
54 .collect::<Result<Vec<_>, ContextError>>()?;
55 let guidelines = request
56 .active_tools()
57 .iter()
58 .filter(|tool| !tool.prompt_guidelines().is_empty())
59 .map(|tool| {
60 let segment_id = format!("tool.{}.guidelines", tool.name().as_str());
61 let locator = format!("{}@{}", tool.name(), tool.version());
62 let guidelines = tool
63 .prompt_guidelines()
64 .iter()
65 .map(|guideline| format!("- {guideline}"))
66 .collect::<Vec<_>>()
67 .join("\n");
68 PromptSegment::new(
69 PromptSegmentId::from_str(&segment_id).map_err(value_error)?,
70 format!("Tool `{}` guidelines:\n{guidelines}", tool.name()),
71 PromptProvenance::new(provider_id.clone(), "tool_spec", Some(locator))
72 .map_err(value_error)?,
73 TrustLevel::Delegated,
74 CacheScope::Profile,
75 BudgetBehavior::Omit,
76 )
77 .map_err(value_error)
78 })
79 .collect::<Result<Vec<_>, ContextError>>()?;
80 let mut modules = Vec::new();
81 if !snippets.is_empty() {
82 modules.push(
83 PromptModule::new(
84 PromptModuleId::from_str("tool.active_snippets").map_err(value_error)?,
85 PromptAuthority::Tool,
86 PromptPriority::new(1),
87 snippets,
88 )
89 .map_err(value_error)?,
90 );
91 }
92 if !guidelines.is_empty() {
93 modules.push(
94 PromptModule::new(
95 PromptModuleId::from_str("tool.active_guidelines").map_err(value_error)?,
96 PromptAuthority::Tool,
97 PromptPriority::new(0),
98 guidelines,
99 )
100 .map_err(value_error)?,
101 );
102 }
103 Ok(modules)
104 })
105 }
106}
107
108fn value_error(error: impl std::fmt::Display) -> ContextError {
109 ContextError::new(crate::ContextErrorCode::InvalidValue, error.to_string())
110}