агенты: добавить поиск инструментов по каталогу

This commit is contained in:
2026-07-21 13:12:46 +03:00
parent 63f8ee333f
commit 99bd05c145
35 changed files with 2088 additions and 155 deletions
+32 -23
View File
@@ -5,7 +5,7 @@ use std::{
};
use crank_core::{CacheScope, CoordinationStateStore, CoordinationStateValue};
use crank_registry::{PostgresRegistry, PublishedAgentTool, RegistryError};
use crank_registry::{PostgresRegistry, PublishedAgentCatalog, PublishedAgentTool, RegistryError};
use serde::{Deserialize, Serialize};
use tokio::sync::{Mutex, RwLock};
use tracing::{info, warn};
@@ -27,15 +27,14 @@ struct CatalogKey {
agent_slug: String,
}
#[derive(Default)]
struct CachedCatalog {
loaded_at: Option<Instant>,
tools: Vec<PublishedAgentTool>,
catalog: PublishedAgentCatalog,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct CatalogSnapshot {
tools: Vec<PublishedAgentTool>,
catalog: PublishedAgentCatalog,
generated_at_ms: u64,
}
@@ -59,12 +58,23 @@ impl PublishedToolCatalog {
workspace_slug: &str,
agent_slug: &str,
) -> Result<Vec<PublishedAgentTool>, RegistryError> {
Ok(self.get_catalog(workspace_slug, agent_slug).await?.tools)
}
pub async fn get_catalog(
&self,
workspace_slug: &str,
agent_slug: &str,
) -> Result<PublishedAgentCatalog, RegistryError> {
self.refresh_if_stale(workspace_slug, agent_slug).await?;
let guard = self.cached.read().await;
Ok(guard
guard
.get(&CatalogKey::new(workspace_slug, agent_slug))
.map(|entry| entry.tools.clone())
.unwrap_or_default())
.map(|entry| entry.catalog.clone())
.ok_or_else(|| RegistryError::PublishedAgentNotFound {
workspace_slug: workspace_slug.to_owned(),
agent_slug: agent_slug.to_owned(),
})
}
async fn refresh_if_stale(
@@ -106,42 +116,41 @@ impl PublishedToolCatalog {
return Ok(());
}
if let Some((tools, age)) = self.load_shared_snapshot(workspace_slug, agent_slug).await {
log_catalog_analysis(workspace_slug, agent_slug, "shared_cache", &tools);
if let Some((catalog, age)) = self.load_shared_snapshot(workspace_slug, agent_slug).await {
log_catalog_analysis(workspace_slug, agent_slug, "shared_cache", &catalog.tools);
let mut guard = self.cached.write().await;
guard.insert(
key,
CachedCatalog {
loaded_at: Instant::now().checked_sub(age),
tools,
catalog,
},
);
return Ok(());
}
let tools = match self
let catalog = match self
.registry
.get_published_agent_tools_by_slug(workspace_slug, agent_slug)
.get_published_agent_catalog_by_slug(workspace_slug, agent_slug)
.await
{
Ok(tools) => tools,
Err(RegistryError::PublishedAgentNotFound { .. }) => Vec::new(),
Ok(catalog) => catalog,
Err(error) => return Err(error),
};
log_catalog_analysis(workspace_slug, agent_slug, "postgres", &tools);
self.store_shared_snapshot(workspace_slug, agent_slug, &tools)
log_catalog_analysis(workspace_slug, agent_slug, "postgres", &catalog.tools);
self.store_shared_snapshot(workspace_slug, agent_slug, &catalog)
.await;
let mut guard = self.cached.write().await;
let previous_count = guard
.get(&key)
.map(|entry| entry.tools.len())
.map(|entry| entry.catalog.tools.len())
.unwrap_or_default();
guard.insert(
key,
CachedCatalog {
loaded_at: Some(Instant::now()),
tools,
catalog,
},
);
@@ -150,7 +159,7 @@ impl PublishedToolCatalog {
agent_slug,
published_tool_count = guard
.get(&CatalogKey::new(workspace_slug, agent_slug))
.map(|entry| entry.tools.len())
.map(|entry| entry.catalog.tools.len())
.unwrap_or_default(),
previous_published_tool_count = previous_count,
"published agent catalog refreshed"
@@ -163,7 +172,7 @@ impl PublishedToolCatalog {
&self,
workspace_slug: &str,
agent_slug: &str,
) -> Option<(Vec<PublishedAgentTool>, Duration)> {
) -> Option<(PublishedAgentCatalog, Duration)> {
if self.refresh_interval.is_zero() {
return None;
}
@@ -179,21 +188,21 @@ impl PublishedToolCatalog {
};
let snapshot = serde_json::from_value::<CatalogSnapshot>(value.payload).ok()?;
let age = Duration::from_millis(now_unix_ms().saturating_sub(snapshot.generated_at_ms));
(age < self.refresh_interval).then_some((snapshot.tools, age))
(age < self.refresh_interval).then_some((snapshot.catalog, age))
}
async fn store_shared_snapshot(
&self,
workspace_slug: &str,
agent_slug: &str,
tools: &[PublishedAgentTool],
catalog: &PublishedAgentCatalog,
) {
let Some(ttl) = catalog_snapshot_ttl(self.refresh_interval) else {
return;
};
let payload = match serde_json::to_value(CatalogSnapshot {
tools: tools.to_vec(),
catalog: catalog.clone(),
generated_at_ms: now_unix_ms(),
}) {
Ok(payload) => payload,