ext/memories/src/tools/ad_hoc_note.rs
91 lines
use codex_extension_api::JsonToolOutput;use codex_extension_api::ToolCall;use codex_extension_api::ToolExecutor;use codex_extension_api::ToolName;use codex_extension_api::ToolSpec;use codex_otel::MetricsClient;use schemars::JsonSchema;use serde::Deserialize;use serde_json::json;use crate::ADD_AD_HOC_NOTE_TOOL_NAME;use crate::backend::AddAdHocMemoryNoteRequest;use crate::backend::AddAdHocMemoryNoteResponse;use crate::backend::MemoriesBackend;use crate::metrics::record_tool_call;use super::backend_error_to_function_call;use super::memory_function_tool;use super::memory_tool_name;use super::parse_args;#[derive(Deserialize, JsonSchema)]#[serde(deny_unknown_fields)]struct AddAdHocNoteArgs { /// Name of the note file to create, in /// YYYY-MM-DDTHH-MM-SS-<slug>.md format. The slug must use only lowercase /// ASCII letters, digits, and hyphens. #[schemars( length(min = 24, max = 128), regex(pattern = r"^\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}-[a-z0-9][a-z0-9-]{0,79}\.md$") )] filename: String, /// Verbatim Markdown note to append to the ad-hoc memory notes. #[schemars(length(min = 1))] note: String,}#[derive(Clone)]pub(super) struct AddAdHocNoteTool<B> { pub(super) backend: B, pub(super) metrics_client: Option<MetricsClient>,}impl<B> ToolExecutor<ToolCall> for AddAdHocNoteTool<B>where B: MemoriesBackend,{ fn tool_name(&self) -> ToolName { memory_tool_name(ADD_AD_HOC_NOTE_TOOL_NAME) } fn spec(&self) -> ToolSpec { memory_function_tool::<AddAdHocNoteArgs, AddAdHocMemoryNoteResponse>( ADD_AD_HOC_NOTE_TOOL_NAME, "Create one append-only ad-hoc memory note after the user explicitly asks Codex to remember, forget, or update something.", ) } fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> { Box::pin(self.handle_call(call)) }}impl<B> AddAdHocNoteTool<B>where B: MemoriesBackend,{ async fn handle_call( &self, call: ToolCall, ) -> Result<Box<dyn codex_extension_api::ToolOutput>, codex_extension_api::FunctionCallError> { let backend = self.backend.clone(); let args: AddAdHocNoteArgs = parse_args(&call)?; let response = backend .add_ad_hoc_note(AddAdHocMemoryNoteRequest { filename: args.filename, note: args.note, }) .await; record_tool_call( self.metrics_client.as_ref(), ADD_AD_HOC_NOTE_TOOL_NAME, "ad_hoc_notes", response.is_ok(), "not_applicable", ); let response = response.map_err(backend_error_to_function_call)?; Ok(Box::new(JsonToolOutput::new(json!(response)))) }}