agent: Allow customizing temperature by provider/model (#30033)

Adds a new `agent.model_parameters` setting that allows the user to
specify a custom temperature for a provider AND/OR model:

```json5
    "model_parameters": [
      // To set parameters for all requests to OpenAI models:
      {
        "provider": "openai",
        "temperature": 0.5
      },
      // To set parameters for all requests in general:
      {
        "temperature": 0
      },
      // To set parameters for a specific provider and model:
      {
        "provider": "zed.dev",
        "model": "claude-3-7-sonnet-latest",
        "temperature": 1.0
      }
    ],
```

Release Notes:

- agent: Allow customizing temperature by provider/model

---------

Co-authored-by: Max Brunsfeld <maxbrunsfeld@gmail.com>
Co-authored-by: Marshall Bowers <git@maxdeviant.com>
This commit is contained in:
Agus Zubiaga 2025-05-06 17:36:25 -03:00 committed by GitHub
parent 0055a20512
commit 3cdf5ce947
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 348 additions and 106 deletions

View file

@ -1417,7 +1417,10 @@ impl ActiveThread {
messages: vec![request_message],
tools: vec![],
stop: vec![],
temperature: None,
temperature: AssistantSettings::temperature_for_model(
&configured_model.model,
cx,
),
};
Some(configured_model.model.count_tokens(request, cx))

View file

@ -2,6 +2,7 @@ use crate::context::ContextLoadResult;
use crate::inline_prompt_editor::CodegenStatus;
use crate::{context::load_context, context_store::ContextStore};
use anyhow::Result;
use assistant_settings::AssistantSettings;
use client::telemetry::Telemetry;
use collections::HashSet;
use editor::{Anchor, AnchorRangeExt, MultiBuffer, MultiBufferSnapshot, ToOffset as _, ToPoint};
@ -383,7 +384,7 @@ impl CodegenAlternative {
if user_prompt.trim().to_lowercase() == "delete" {
async { Ok(LanguageModelTextStream::default()) }.boxed_local()
} else {
let request = self.build_request(user_prompt, cx)?;
let request = self.build_request(&model, user_prompt, cx)?;
cx.spawn(async move |_, cx| model.stream_completion_text(request.await, &cx).await)
.boxed_local()
};
@ -393,6 +394,7 @@ impl CodegenAlternative {
fn build_request(
&self,
model: &Arc<dyn LanguageModel>,
user_prompt: String,
cx: &mut App,
) -> Result<Task<LanguageModelRequest>> {
@ -441,6 +443,8 @@ impl CodegenAlternative {
}
});
let temperature = AssistantSettings::temperature_for_model(&model, cx);
Ok(cx.spawn(async move |_cx| {
let mut request_message = LanguageModelRequestMessage {
role: Role::User,
@ -463,7 +467,7 @@ impl CodegenAlternative {
mode: None,
tools: Vec::new(),
stop: Vec::new(),
temperature: None,
temperature,
messages: vec![request_message],
}
}))

View file

@ -8,7 +8,7 @@ use crate::ui::{
AnimatedLabel, MaxModeTooltip,
preview::{AgentPreview, UsageCallout},
};
use assistant_settings::CompletionMode;
use assistant_settings::{AssistantSettings, CompletionMode};
use buffer_diff::BufferDiff;
use client::UserStore;
use collections::{HashMap, HashSet};
@ -1273,7 +1273,7 @@ impl MessageEditor {
messages: vec![request_message],
tools: vec![],
stop: vec![],
temperature: None,
temperature: AssistantSettings::temperature_for_model(&model.model, cx),
};
Some(model.model.count_tokens(request, cx))

View file

@ -6,6 +6,7 @@ use crate::inline_prompt_editor::{
use crate::terminal_codegen::{CLEAR_INPUT, CodegenEvent, TerminalCodegen};
use crate::thread_store::{TextThreadStore, ThreadStore};
use anyhow::{Context as _, Result};
use assistant_settings::AssistantSettings;
use client::telemetry::Telemetry;
use collections::{HashMap, VecDeque};
use editor::{MultiBuffer, actions::SelectAll};
@ -266,6 +267,12 @@ impl TerminalInlineAssistant {
load_context(contexts, project, &assist.prompt_store, cx)
})?;
let ConfiguredModel { model, .. } = LanguageModelRegistry::read_global(cx)
.inline_assistant_model()
.context("No inline assistant model")?;
let temperature = AssistantSettings::temperature_for_model(&model, cx);
Ok(cx.background_spawn(async move {
let mut request_message = LanguageModelRequestMessage {
role: Role::User,
@ -287,7 +294,7 @@ impl TerminalInlineAssistant {
messages: vec![request_message],
tools: Vec::new(),
stop: Vec::new(),
temperature: None,
temperature,
}
}))
}

View file

@ -1145,7 +1145,7 @@ impl Thread {
messages: vec![],
tools: Vec::new(),
stop: Vec::new(),
temperature: None,
temperature: AssistantSettings::temperature_for_model(&model, cx),
};
let available_tools = self.available_tools(cx, model.clone());
@ -1251,7 +1251,12 @@ impl Thread {
request
}
fn to_summarize_request(&self, added_user_message: String) -> LanguageModelRequest {
fn to_summarize_request(
&self,
model: &Arc<dyn LanguageModel>,
added_user_message: String,
cx: &App,
) -> LanguageModelRequest {
let mut request = LanguageModelRequest {
thread_id: None,
prompt_id: None,
@ -1259,7 +1264,7 @@ impl Thread {
messages: vec![],
tools: Vec::new(),
stop: Vec::new(),
temperature: None,
temperature: AssistantSettings::temperature_for_model(model, cx),
};
for message in &self.messages {
@ -1696,7 +1701,7 @@ impl Thread {
If the conversation is about a specific subject, include it in the title. \
Be descriptive. DO NOT speak in the first person.";
let request = self.to_summarize_request(added_user_message.into());
let request = self.to_summarize_request(&model.model, added_user_message.into(), cx);
self.pending_summary = cx.spawn(async move |this, cx| {
async move {
@ -1782,7 +1787,7 @@ impl Thread {
4. Any action items or next steps if any\n\
Format it in Markdown with headings and bullet points.";
let request = self.to_summarize_request(added_user_message.into());
let request = self.to_summarize_request(&model, added_user_message.into(), cx);
*self.detailed_summary_tx.borrow_mut() = DetailedSummaryState::Generating {
message_id: last_message_id,
@ -2655,7 +2660,7 @@ struct PendingCompletion {
mod tests {
use super::*;
use crate::{ThreadStore, context::load_context, context_store::ContextStore, thread_store};
use assistant_settings::AssistantSettings;
use assistant_settings::{AssistantSettings, LanguageModelParameters};
use assistant_tool::ToolRegistry;
use editor::EditorSettings;
use gpui::TestAppContext;
@ -3066,6 +3071,100 @@ fn main() {{
);
}
#[gpui::test]
async fn test_temperature_setting(cx: &mut TestAppContext) {
init_test_settings(cx);
let project = create_test_project(
cx,
json!({"code.rs": "fn main() {\n println!(\"Hello, world!\");\n}"}),
)
.await;
let (_workspace, _thread_store, thread, _context_store, model) =
setup_test_environment(cx, project.clone()).await;
// Both model and provider
cx.update(|cx| {
AssistantSettings::override_global(
AssistantSettings {
model_parameters: vec![LanguageModelParameters {
provider: Some(model.provider_id().0.to_string().into()),
model: Some(model.id().0.clone()),
temperature: Some(0.66),
}],
..AssistantSettings::get_global(cx).clone()
},
cx,
);
});
let request = thread.update(cx, |thread, cx| {
thread.to_completion_request(model.clone(), cx)
});
assert_eq!(request.temperature, Some(0.66));
// Only model
cx.update(|cx| {
AssistantSettings::override_global(
AssistantSettings {
model_parameters: vec![LanguageModelParameters {
provider: None,
model: Some(model.id().0.clone()),
temperature: Some(0.66),
}],
..AssistantSettings::get_global(cx).clone()
},
cx,
);
});
let request = thread.update(cx, |thread, cx| {
thread.to_completion_request(model.clone(), cx)
});
assert_eq!(request.temperature, Some(0.66));
// Only provider
cx.update(|cx| {
AssistantSettings::override_global(
AssistantSettings {
model_parameters: vec![LanguageModelParameters {
provider: Some(model.provider_id().0.to_string().into()),
model: None,
temperature: Some(0.66),
}],
..AssistantSettings::get_global(cx).clone()
},
cx,
);
});
let request = thread.update(cx, |thread, cx| {
thread.to_completion_request(model.clone(), cx)
});
assert_eq!(request.temperature, Some(0.66));
// Same model name, different provider
cx.update(|cx| {
AssistantSettings::override_global(
AssistantSettings {
model_parameters: vec![LanguageModelParameters {
provider: Some("anthropic".into()),
model: Some(model.id().0.clone()),
temperature: Some(0.66),
}],
..AssistantSettings::get_global(cx).clone()
},
cx,
);
});
let request = thread.update(cx, |thread, cx| {
thread.to_completion_request(model.clone(), cx)
});
assert_eq!(request.temperature, None);
}
fn init_test_settings(cx: &mut TestAppContext) {
cx.update(|cx| {
let settings_store = SettingsStore::test(cx);