agent: Use default prompts from prompt library in system prompt (#28915)

Related to #28490.

- Default prompts from the prompt library are now included as "user
rules" in the system prompt.
- Presence of these user rules is shown at the beginning of the thread
in the UI.
_ Now uses an `Entity<PromptStore>` instead of an `Arc<PromptStore>`.
Motivation for this is emitting a `PromptsUpdatedEvent`.
- Now disallows concurrent reloading of the system prompt. Before this
change it was possible for reloads to race.

Release Notes:

- agent: Added support for including default prompts from the Prompt
Library as "user rules" in the system prompt.

---------

Co-authored-by: Danilo Leal <daniloleal09@gmail.com>
This commit is contained in:
Michael Sloan 2025-04-18 09:32:35 -06:00 committed by GitHub
parent eea6cfb383
commit 502a0f6535
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 433 additions and 165 deletions

View file

@ -4,7 +4,7 @@ use std::ops::Range;
use std::sync::Arc;
use std::time::Instant;
use anyhow::{Context as _, Result, anyhow};
use anyhow::{Result, anyhow};
use assistant_settings::AssistantSettings;
use assistant_tool::{ActionLog, AnyToolCard, Tool, ToolWorkingSet};
use chrono::{DateTime, Utc};
@ -939,7 +939,7 @@ impl Thread {
pub fn to_completion_request(
&self,
request_kind: RequestKind,
cx: &App,
cx: &mut Context<Self>,
) -> LanguageModelRequest {
let mut request = LanguageModelRequest {
messages: vec![],
@ -949,20 +949,33 @@ impl Thread {
};
if let Some(project_context) = self.project_context.borrow().as_ref() {
if let Some(system_prompt) = self
match self
.prompt_builder
.generate_assistant_system_prompt(project_context)
.context("failed to generate assistant system prompt")
.log_err()
{
request.messages.push(LanguageModelRequestMessage {
role: Role::System,
content: vec![MessageContent::Text(system_prompt)],
cache: true,
});
Err(err) => {
let message = format!("{err:?}").into();
log::error!("{message}");
cx.emit(ThreadEvent::ShowError(ThreadError::Message {
header: "Error generating system prompt".into(),
message,
}));
}
Ok(system_prompt) => {
request.messages.push(LanguageModelRequestMessage {
role: Role::System,
content: vec![MessageContent::Text(system_prompt)],
cache: true,
});
}
}
} else {
log::error!("project_context not set.")
let message = "Context for system prompt unexpectedly not ready.".into();
log::error!("{message}");
cx.emit(ThreadEvent::ShowError(ThreadError::Message {
header: "Error generating system prompt".into(),
message,
}));
}
for message in &self.messages {
@ -2163,7 +2176,7 @@ fn main() {{
assert_eq!(message.context, expected_context);
// Check message in request
let request = thread.read_with(cx, |thread, cx| {
let request = thread.update(cx, |thread, cx| {
thread.to_completion_request(RequestKind::Chat, cx)
});
@ -2255,7 +2268,7 @@ fn main() {{
assert!(message3.context.contains("file3.rs"));
// Check entire request to make sure all contexts are properly included
let request = thread.read_with(cx, |thread, cx| {
let request = thread.update(cx, |thread, cx| {
thread.to_completion_request(RequestKind::Chat, cx)
});
@ -2307,7 +2320,7 @@ fn main() {{
assert_eq!(message.context, "");
// Check message in request
let request = thread.read_with(cx, |thread, cx| {
let request = thread.update(cx, |thread, cx| {
thread.to_completion_request(RequestKind::Chat, cx)
});
@ -2327,7 +2340,7 @@ fn main() {{
assert_eq!(message2.context, "");
// Check that both messages appear in the request
let request = thread.read_with(cx, |thread, cx| {
let request = thread.update(cx, |thread, cx| {
thread.to_completion_request(RequestKind::Chat, cx)
});
@ -2369,7 +2382,7 @@ fn main() {{
});
// Create a request and check that it doesn't have a stale buffer warning yet
let initial_request = thread.read_with(cx, |thread, cx| {
let initial_request = thread.update(cx, |thread, cx| {
thread.to_completion_request(RequestKind::Chat, cx)
});
@ -2399,7 +2412,7 @@ fn main() {{
});
// Create a new request and check for the stale buffer warning
let new_request = thread.read_with(cx, |thread, cx| {
let new_request = thread.update(cx, |thread, cx| {
thread.to_completion_request(RequestKind::Chat, cx)
});
@ -2428,6 +2441,7 @@ fn main() {{
language::init(cx);
Project::init_settings(cx);
AssistantSettings::register(cx);
prompt_store::init(cx);
thread_store::init(cx);
workspace::init_settings(cx);
ThemeSettings::register(cx);
@ -2467,7 +2481,8 @@ fn main() {{
cx,
)
})
.await;
.await
.unwrap();
let thread = thread_store.update(cx, |store, cx| store.create_thread(cx));
let context_store = cx.new(|_cx| ContextStore::new(project.downgrade(), None));