fix(ai): dedupe built-in providers

This commit is contained in:
Fini 2026-05-31 14:20:25 +08:00
parent 14c6cd2bf3
commit 86cedad794
3 changed files with 101 additions and 5 deletions

View file

@ -182,6 +182,21 @@ impl BuiltinAgentConfig {
pub fn ready(&self) -> bool {
self.enabled && !self.api_key.trim().is_empty() && !self.model.trim().is_empty()
}
pub fn matches_config(
&self,
display_name: &str,
api_key: &str,
model: &str,
kind: BuiltinAgentKind,
base_url: &str,
) -> bool {
self.kind == kind
&& self.display_name.trim() == display_name.trim()
&& self.api_key.trim() == api_key.trim()
&& self.model.trim() == model.trim()
&& self.base_url.trim().trim_end_matches('/') == base_url.trim().trim_end_matches('/')
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -474,15 +489,26 @@ impl AgentSettings {
kind: BuiltinAgentKind,
base_url: impl Into<String>,
) -> String {
let display_name = display_name.into();
let api_key = api_key.into();
let model = model.into();
let base_url = base_url.into();
if let Some(existing) = self
.builtin_agents
.iter()
.find(|agent| agent.matches_config(&display_name, &api_key, &model, kind, &base_url))
{
return existing.id.clone();
}
let id = format!("builtin-{}", self.next_builtin_agent_id.max(1));
self.next_builtin_agent_id = self.next_builtin_agent_id.max(1).saturating_add(1);
self.builtin_agents.push(BuiltinAgentConfig {
id: id.clone(),
display_name: display_name.into(),
display_name,
kind,
api_key: api_key.into(),
model: model.into(),
base_url: base_url.into(),
api_key,
model,
base_url,
enabled: true,
});
id

View file

@ -1,5 +1,17 @@
use crate::agent_settings::AgentSettings;
#[test]
fn duplicate_builtin_agent_config_reuses_existing_provider() {
let mut s = AgentSettings::default();
let first = s.add_builtin_agent_with_defaults("MINIMAX", "sk-test", "MiniMax-M2.7");
let second = s.add_builtin_agent_with_defaults("MINIMAX", "sk-test", "MiniMax-M2.7");
assert_eq!(second, first);
assert_eq!(s.builtin_agents.len(), 1);
assert_eq!(s.next_builtin_agent_id, 2);
}
#[test]
fn builtin_agent_draft_does_not_persist_until_save() {
let mut s = AgentSettings::default();

View file

@ -252,10 +252,11 @@ fn apply_payload(state: &mut EditorState, payload: SettingsPayload) {
eui.agent_settings.connected = c;
}
if let Some(agents) = payload.builtin_agents {
eui.agent_settings.builtin_agents = agents
let agents = agents
.into_iter()
.filter_map(builtin_agent_from_payload)
.collect();
eui.agent_settings.builtin_agents = dedupe_builtin_agents(agents);
eui.agent_settings.next_builtin_agent_id =
next_builtin_agent_id(&eui.agent_settings.builtin_agents);
}
@ -442,6 +443,25 @@ fn next_builtin_agent_id(agents: &[BuiltinAgentConfig]) -> u64 {
.saturating_add(1)
}
fn dedupe_builtin_agents(agents: Vec<BuiltinAgentConfig>) -> Vec<BuiltinAgentConfig> {
let mut deduped: Vec<BuiltinAgentConfig> = Vec::new();
for agent in agents {
let is_duplicate = deduped.iter().any(|existing| {
existing.matches_config(
&agent.display_name,
&agent.api_key,
&agent.model,
agent.kind,
&agent.base_url,
)
});
if !is_duplicate {
deduped.push(agent);
}
}
deduped
}
fn next_acp_agent_id(agents: &[AcpAgentConfig]) -> u64 {
agents
.iter()
@ -668,6 +688,44 @@ mod tests {
);
}
#[test]
fn duplicate_builtin_agents_are_deduped_on_load() {
let settings = r#"{
"version": 1,
"builtin_agents": [
{
"id": "builtin-1",
"display_name": "MINIMAX",
"kind": "openai-compat",
"api_key": "sk-test",
"model": "MiniMax-M2.7",
"base_url": "https://api.minimaxi.com/v1",
"enabled": true
},
{
"id": "builtin-2",
"display_name": "MINIMAX",
"kind": "openai-compat",
"api_key": "sk-test",
"model": "MiniMax-M2.7",
"base_url": "https://api.minimaxi.com/v1",
"enabled": true
}
]
}"#;
let payload: SettingsPayload = serde_json::from_str(settings).unwrap();
let mut dst = EditorState::new();
apply_payload(&mut dst, payload);
assert_eq!(dst.editor_ui.agent_settings.builtin_agents.len(), 1);
assert_eq!(
dst.editor_ui.agent_settings.builtin_agents[0].id,
"builtin-1"
);
assert_eq!(dst.editor_ui.agent_settings.next_builtin_agent_id, 2);
}
#[test]
fn acp_agents_round_trip_through_payload() {
let mut src = EditorState::new();