mirror of
https://github.com/openai/codex.git
synced 2026-03-05 21:45:28 +03:00
add metrics for external config import (#13501)
This commit is contained in:
@@ -7,6 +7,9 @@ use std::path::Path;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use toml::Value as TomlValue;
|
use toml::Value as TomlValue;
|
||||||
|
|
||||||
|
const EXTERNAL_AGENT_CONFIG_DETECT_METRIC: &str = "codex.external_agent_config.detect";
|
||||||
|
const EXTERNAL_AGENT_CONFIG_IMPORT_METRIC: &str = "codex.external_agent_config.import";
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct ExternalAgentConfigDetectOptions {
|
pub struct ExternalAgentConfigDetectOptions {
|
||||||
pub include_home: bool,
|
pub include_home: bool,
|
||||||
@@ -74,13 +77,28 @@ impl ExternalAgentConfigService {
|
|||||||
for migration_item in migration_items {
|
for migration_item in migration_items {
|
||||||
match migration_item.item_type {
|
match migration_item.item_type {
|
||||||
ExternalAgentConfigMigrationItemType::Config => {
|
ExternalAgentConfigMigrationItemType::Config => {
|
||||||
self.import_config(migration_item.cwd.as_deref())?
|
self.import_config(migration_item.cwd.as_deref())?;
|
||||||
|
emit_migration_metric(
|
||||||
|
EXTERNAL_AGENT_CONFIG_IMPORT_METRIC,
|
||||||
|
ExternalAgentConfigMigrationItemType::Config,
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ExternalAgentConfigMigrationItemType::Skills => {
|
ExternalAgentConfigMigrationItemType::Skills => {
|
||||||
self.import_skills(migration_item.cwd.as_deref())?
|
let skills_count = self.import_skills(migration_item.cwd.as_deref())?;
|
||||||
|
emit_migration_metric(
|
||||||
|
EXTERNAL_AGENT_CONFIG_IMPORT_METRIC,
|
||||||
|
ExternalAgentConfigMigrationItemType::Skills,
|
||||||
|
Some(skills_count),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ExternalAgentConfigMigrationItemType::AgentsMd => {
|
ExternalAgentConfigMigrationItemType::AgentsMd => {
|
||||||
self.import_agents_md(migration_item.cwd.as_deref())?
|
self.import_agents_md(migration_item.cwd.as_deref())?;
|
||||||
|
emit_migration_metric(
|
||||||
|
EXTERNAL_AGENT_CONFIG_IMPORT_METRIC,
|
||||||
|
ExternalAgentConfigMigrationItemType::AgentsMd,
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ExternalAgentConfigMigrationItemType::McpServerConfig => {}
|
ExternalAgentConfigMigrationItemType::McpServerConfig => {}
|
||||||
}
|
}
|
||||||
@@ -132,6 +150,11 @@ impl ExternalAgentConfigService {
|
|||||||
),
|
),
|
||||||
cwd: cwd.clone(),
|
cwd: cwd.clone(),
|
||||||
});
|
});
|
||||||
|
emit_migration_metric(
|
||||||
|
EXTERNAL_AGENT_CONFIG_DETECT_METRIC,
|
||||||
|
ExternalAgentConfigMigrationItemType::Config,
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,12 +167,8 @@ impl ExternalAgentConfigService {
|
|||||||
|| self.home_target_skills_dir(),
|
|| self.home_target_skills_dir(),
|
||||||
|repo_root| repo_root.join(".agents").join("skills"),
|
|repo_root| repo_root.join(".agents").join("skills"),
|
||||||
);
|
);
|
||||||
let source_skill_names = collect_subdirectory_names(&source_skills)?;
|
let skills_count = count_missing_subdirectories(&source_skills, &target_skills)?;
|
||||||
let target_skill_names = collect_subdirectory_names(&target_skills)?;
|
if skills_count > 0 {
|
||||||
if source_skill_names
|
|
||||||
.iter()
|
|
||||||
.any(|skill_name| !target_skill_names.contains(skill_name))
|
|
||||||
{
|
|
||||||
items.push(ExternalAgentConfigMigrationItem {
|
items.push(ExternalAgentConfigMigrationItem {
|
||||||
item_type: ExternalAgentConfigMigrationItemType::Skills,
|
item_type: ExternalAgentConfigMigrationItemType::Skills,
|
||||||
description: format!(
|
description: format!(
|
||||||
@@ -159,6 +178,11 @@ impl ExternalAgentConfigService {
|
|||||||
),
|
),
|
||||||
cwd: cwd.clone(),
|
cwd: cwd.clone(),
|
||||||
});
|
});
|
||||||
|
emit_migration_metric(
|
||||||
|
EXTERNAL_AGENT_CONFIG_DETECT_METRIC,
|
||||||
|
ExternalAgentConfigMigrationItemType::Skills,
|
||||||
|
Some(skills_count),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let source_agents_md = if let Some(repo_root) = repo_root {
|
let source_agents_md = if let Some(repo_root) = repo_root {
|
||||||
@@ -183,6 +207,11 @@ impl ExternalAgentConfigService {
|
|||||||
),
|
),
|
||||||
cwd,
|
cwd,
|
||||||
});
|
});
|
||||||
|
emit_migration_metric(
|
||||||
|
EXTERNAL_AGENT_CONFIG_DETECT_METRIC,
|
||||||
|
ExternalAgentConfigMigrationItemType::AgentsMd,
|
||||||
|
None,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -247,14 +276,14 @@ impl ExternalAgentConfigService {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_skills(&self, cwd: Option<&Path>) -> io::Result<()> {
|
fn import_skills(&self, cwd: Option<&Path>) -> io::Result<usize> {
|
||||||
let (source_skills, target_skills) = if let Some(repo_root) = find_repo_root(cwd)? {
|
let (source_skills, target_skills) = if let Some(repo_root) = find_repo_root(cwd)? {
|
||||||
(
|
(
|
||||||
repo_root.join(".claude").join("skills"),
|
repo_root.join(".claude").join("skills"),
|
||||||
repo_root.join(".agents").join("skills"),
|
repo_root.join(".agents").join("skills"),
|
||||||
)
|
)
|
||||||
} else if cwd.is_some_and(|cwd| !cwd.as_os_str().is_empty()) {
|
} else if cwd.is_some_and(|cwd| !cwd.as_os_str().is_empty()) {
|
||||||
return Ok(());
|
return Ok(0);
|
||||||
} else {
|
} else {
|
||||||
(
|
(
|
||||||
self.claude_home.join("skills"),
|
self.claude_home.join("skills"),
|
||||||
@@ -262,10 +291,11 @@ impl ExternalAgentConfigService {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
if !source_skills.is_dir() {
|
if !source_skills.is_dir() {
|
||||||
return Ok(());
|
return Ok(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::create_dir_all(&target_skills)?;
|
fs::create_dir_all(&target_skills)?;
|
||||||
|
let mut copied_count = 0usize;
|
||||||
|
|
||||||
for entry in fs::read_dir(&source_skills)? {
|
for entry in fs::read_dir(&source_skills)? {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
@@ -280,9 +310,10 @@ impl ExternalAgentConfigService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
copy_dir_recursive(&entry.path(), &target)?;
|
copy_dir_recursive(&entry.path(), &target)?;
|
||||||
|
copied_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(copied_count)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn import_agents_md(&self, cwd: Option<&Path>) -> io::Result<()> {
|
fn import_agents_md(&self, cwd: Option<&Path>) -> io::Result<()> {
|
||||||
@@ -374,6 +405,15 @@ fn collect_subdirectory_names(path: &Path) -> io::Result<HashSet<OsString>> {
|
|||||||
Ok(names)
|
Ok(names)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn count_missing_subdirectories(source: &Path, target: &Path) -> io::Result<usize> {
|
||||||
|
let source_names = collect_subdirectory_names(source)?;
|
||||||
|
let target_names = collect_subdirectory_names(target)?;
|
||||||
|
Ok(source_names
|
||||||
|
.iter()
|
||||||
|
.filter(|name| !target_names.contains(*name))
|
||||||
|
.count())
|
||||||
|
}
|
||||||
|
|
||||||
fn is_missing_or_empty_text_file(path: &Path) -> io::Result<bool> {
|
fn is_missing_or_empty_text_file(path: &Path) -> io::Result<bool> {
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
@@ -614,6 +654,39 @@ fn invalid_data_error(message: impl Into<String>) -> io::Error {
|
|||||||
io::Error::new(io::ErrorKind::InvalidData, message.into())
|
io::Error::new(io::ErrorKind::InvalidData, message.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn migration_metric_tags(
|
||||||
|
item_type: ExternalAgentConfigMigrationItemType,
|
||||||
|
skills_count: Option<usize>,
|
||||||
|
) -> Vec<(&'static str, String)> {
|
||||||
|
let migration_type = match item_type {
|
||||||
|
ExternalAgentConfigMigrationItemType::Config => "config",
|
||||||
|
ExternalAgentConfigMigrationItemType::Skills => "skills",
|
||||||
|
ExternalAgentConfigMigrationItemType::AgentsMd => "agents_md",
|
||||||
|
ExternalAgentConfigMigrationItemType::McpServerConfig => "mcp_server_config",
|
||||||
|
};
|
||||||
|
let mut tags = vec![("migration_type", migration_type.to_string())];
|
||||||
|
if item_type == ExternalAgentConfigMigrationItemType::Skills {
|
||||||
|
tags.push(("skills_count", skills_count.unwrap_or(0).to_string()));
|
||||||
|
}
|
||||||
|
tags
|
||||||
|
}
|
||||||
|
|
||||||
|
fn emit_migration_metric(
|
||||||
|
metric_name: &str,
|
||||||
|
item_type: ExternalAgentConfigMigrationItemType,
|
||||||
|
skills_count: Option<usize>,
|
||||||
|
) {
|
||||||
|
let Some(metrics) = codex_otel::metrics::global() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let tags = migration_metric_tags(item_type, skills_count);
|
||||||
|
let tag_refs = tags
|
||||||
|
.iter()
|
||||||
|
.map(|(key, value)| (*key, value.as_str()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let _ = metrics.counter(metric_name, 1, &tag_refs);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -984,4 +1057,33 @@ mod tests {
|
|||||||
"Codex guidance"
|
"Codex guidance"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn migration_metric_tags_for_skills_include_skills_count() {
|
||||||
|
assert_eq!(
|
||||||
|
migration_metric_tags(ExternalAgentConfigMigrationItemType::Skills, Some(3)),
|
||||||
|
vec![
|
||||||
|
("migration_type", "skills".to_string()),
|
||||||
|
("skills_count", "3".to_string()),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_skills_returns_only_new_skill_directory_count() {
|
||||||
|
let (_root, claude_home, codex_home) = fixture_paths();
|
||||||
|
let agents_skills = codex_home
|
||||||
|
.parent()
|
||||||
|
.map(|parent| parent.join(".agents").join("skills"))
|
||||||
|
.unwrap_or_else(|| PathBuf::from(".agents").join("skills"));
|
||||||
|
fs::create_dir_all(claude_home.join("skills").join("skill-a")).expect("create source a");
|
||||||
|
fs::create_dir_all(claude_home.join("skills").join("skill-b")).expect("create source b");
|
||||||
|
fs::create_dir_all(agents_skills.join("skill-a")).expect("create existing target");
|
||||||
|
|
||||||
|
let copied_count = service_for_paths(claude_home, codex_home)
|
||||||
|
.import_skills(None)
|
||||||
|
.expect("import skills");
|
||||||
|
|
||||||
|
assert_eq!(copied_count, 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user