ZIm/crates/eval/src/examples/file_change_notification.rs
Oleksiy Syvokon d87603dd60
agent: Send stale file notifications using the project_notifications tool (#34005)
This commit introduces the `project_notifications` tool, which
proactively pushes notifications to the agent.

Unlike other tools, `Thread` automatically invokes this tool on every
turn, even when the LLM doesn't ask for it. When notifications are
available, the tool use and results are inserted into the thread,
simulating an LLM tool call.

As with other tools, users can disable `project_notifications` in
Profiles if they do not want them.

Currently, the tool only notifies users about stale files: that is,
files that have been edited by the user while the agent is also working
on them. In the future, notifications may be expanded to include
compiler diagnostics, long-running processes, and more.

Release Notes:

- Added `project_notifications` tool
2025-07-07 19:48:18 +03:00

74 lines
2.5 KiB
Rust

use agent_settings::AgentProfileId;
use anyhow::Result;
use async_trait::async_trait;
use crate::example::{Example, ExampleContext, ExampleMetadata, JudgeAssertion};
pub struct FileChangeNotificationExample;
#[async_trait(?Send)]
impl Example for FileChangeNotificationExample {
fn meta(&self) -> ExampleMetadata {
ExampleMetadata {
name: "file_change_notification".to_string(),
url: "https://github.com/octocat/hello-world".to_string(),
revision: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d".to_string(),
language_server: None,
max_assertions: None,
profile_id: AgentProfileId::default(),
existing_thread_json: None,
max_turns: Some(3),
}
}
async fn conversation(&self, cx: &mut ExampleContext) -> Result<()> {
// Track README so that the model gets notified of its changes
let project_path = cx.agent_thread().read_with(cx, |thread, cx| {
thread
.project()
.read(cx)
.find_project_path("README", cx)
.expect("README file should exist in this repo")
})?;
let buffer = {
cx.agent_thread()
.update(cx, |thread, cx| {
thread
.project()
.update(cx, |project, cx| project.open_buffer(project_path, cx))
})?
.await?
};
cx.agent_thread().update(cx, |thread, cx| {
thread.action_log().update(cx, |action_log, cx| {
action_log.buffer_read(buffer.clone(), cx);
});
})?;
// Start conversation (specific message is not important)
cx.push_user_message("Find all files in this repo");
cx.run_turn().await?;
// Edit the README buffer - the model should get a notification on next turn
buffer.update(cx, |buffer, cx| {
buffer.edit([(0..buffer.len(), "Surprise!")], None, cx);
})?;
// Run for some more turns.
// The model shouldn't thank us for letting it know about the file change.
cx.run_turns(3).await?;
Ok(())
}
fn thread_assertions(&self) -> Vec<JudgeAssertion> {
vec![JudgeAssertion {
id: "change-file-notification".into(),
description:
"Agent should not acknowledge or mention anything about files that have been changed"
.into(),
}]
}
}