feat(tui): deduplicate model metadata and populate model details cache from session

- Add `seen_meta` set and `push_meta` helper to avoid duplicate entries when building model metadata strings.
- Extend metadata handling to include context length fallback, architecture/family information, embedding length, size formatting, and quantization details.
- Introduce `populate_model_details_cache_from_session` to load model details from the controller, with a fallback to cached details.
- Update `refresh_models` to use the new cache‑population method instead of manually clearing the cache.
This commit is contained in:
2025-10-13 23:36:26 +02:00
parent 44a00619b5
commit 990f93d467
2 changed files with 71 additions and 8 deletions

View File

@@ -7257,6 +7257,25 @@ impl ChatApp {
Ok(())
}
async fn populate_model_details_cache_from_session(&mut self) {
self.model_details_cache.clear();
let mut populated = false;
if let Ok(details) = self.controller.all_model_details(false).await {
for info in details {
self.model_details_cache.insert(info.name.clone(), info);
}
populated = !self.model_details_cache.is_empty();
}
if !populated {
let cached = self.controller.cached_model_details().await;
for info in cached {
self.model_details_cache.insert(info.name.clone(), info);
}
}
}
async fn refresh_models(&mut self) -> Result<()> {
let config_model_name = self.controller.config().general.default_model.clone();
let config_model_provider = self.controller.config().general.default_provider.clone();
@@ -7286,9 +7305,9 @@ impl ChatApp {
}
self.models = all_models;
self.model_details_cache.clear();
self.model_info_panel.clear();
self.set_model_info_visible(false);
self.populate_model_details_cache_from_session().await;
self.recompute_available_providers();

View File

@@ -2832,22 +2832,64 @@ fn build_model_selector_label(
}
let mut meta_parts: Vec<String> = Vec::new();
let mut seen_meta: HashSet<String> = HashSet::new();
let mut push_meta = |value: String| {
let trimmed = value.trim();
if trimmed.is_empty() {
return;
}
let key = trimmed.to_ascii_lowercase();
if seen_meta.insert(key) {
meta_parts.push(trimmed.to_string());
}
};
if let Some(detail) = detail {
if let Some(ctx) = detail.context_length {
push_meta(format!("max tokens {}", ctx));
} else if let Some(ctx) = model.context_window {
push_meta(format!("max tokens {}", ctx));
}
if let Some(parameters) = detail
.parameter_size
.as_ref()
.or(detail.parameters.as_ref())
&& !parameters.trim().is_empty()
{
meta_parts.push(parameters.trim().to_string());
push_meta(parameters.trim().to_string());
}
if let Some(arch) = detail.architecture.as_deref() {
let trimmed = arch.trim();
if !trimmed.is_empty() {
push_meta(format!("arch {}", trimmed));
}
} else if let Some(family) = detail.family.as_deref() {
let trimmed = family.trim();
if !trimmed.is_empty() {
push_meta(format!("family {}", trimmed));
}
} else if !detail.families.is_empty() {
let families = detail
.families
.iter()
.map(|f| f.trim())
.filter(|f| !f.is_empty())
.take(2)
.collect::<Vec<_>>()
.join("/");
if !families.is_empty() {
push_meta(format!("family {}", families));
}
}
if let Some(embedding) = detail.embedding_length {
push_meta(format!("embedding {}", embedding));
}
if let Some(size) = detail.size {
meta_parts.push(format_short_size(size));
}
if let Some(ctx) = detail.context_length {
meta_parts.push(format!("ctx {}", ctx));
push_meta(format_short_size(size));
}
if let Some(quant) = detail
@@ -2855,8 +2897,10 @@ fn build_model_selector_label(
.as_ref()
.filter(|q| !q.trim().is_empty())
{
meta_parts.push(quant.trim().to_string());
push_meta(format!("quant {}", quant.trim()));
}
} else if let Some(ctx) = model.context_window {
push_meta(format!("max tokens {}", ctx));
}
if let Some(desc) = model.description.as_deref() {