mirror of
https://github.com/openai/codex.git
synced 2026-09-08 15:50:34 +00:00
codex: adapt skill search to extension API changes
This commit is contained in:
2
codex-rs/Cargo.lock
generated
2
codex-rs/Cargo.lock
generated
@@ -3815,9 +3815,9 @@ dependencies = [
|
||||
name = "codex-skill-search-extension"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bm25",
|
||||
"codex-core",
|
||||
"codex-core-skills",
|
||||
"codex-extension-api",
|
||||
"codex-features",
|
||||
"codex-protocol",
|
||||
|
||||
@@ -200,24 +200,29 @@ async fn skill_search_tool_is_visible_and_returns_matching_repo_skill() -> Resul
|
||||
let (sandbox_policy, permission_profile) =
|
||||
turn_permission_fields(PermissionProfile::Disabled, test.config.cwd.as_path());
|
||||
test.codex
|
||||
.submit(Op::UserTurn {
|
||||
environments: None,
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "Find the right repo skill.".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
cwd: test.config.cwd.to_path_buf(),
|
||||
approval_policy: AskForApproval::Never,
|
||||
approvals_reviewer: None,
|
||||
sandbox_policy,
|
||||
permission_profile,
|
||||
model: session_model,
|
||||
effort: None,
|
||||
summary: None,
|
||||
service_tier: None,
|
||||
collaboration_mode: None,
|
||||
personality: None,
|
||||
responsesapi_client_metadata: None,
|
||||
additional_context: Default::default(),
|
||||
thread_settings: codex_protocol::protocol::ThreadSettingsOverrides {
|
||||
environments: Some(local_selections(test.config.cwd.clone())),
|
||||
approval_policy: Some(AskForApproval::Never),
|
||||
sandbox_policy: Some(sandbox_policy),
|
||||
permission_profile,
|
||||
collaboration_mode: Some(codex_protocol::config_types::CollaborationMode {
|
||||
mode: codex_protocol::config_types::ModeKind::Default,
|
||||
settings: codex_protocol::config_types::Settings {
|
||||
model: session_model,
|
||||
reasoning_effort: None,
|
||||
developer_instructions: None,
|
||||
},
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -70,6 +70,7 @@ impl ToolContributor for AllContributors {
|
||||
&self,
|
||||
_session_store: &ExtensionData,
|
||||
_thread_store: &ExtensionData,
|
||||
_turn_store: &ExtensionData,
|
||||
) -> Vec<Arc<dyn ToolExecutor<ToolCall>>> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
@@ -411,6 +411,7 @@ where
|
||||
&self,
|
||||
_session_store: &ExtensionData,
|
||||
thread_store: &ExtensionData,
|
||||
_turn_store: &ExtensionData,
|
||||
) -> Vec<Arc<dyn codex_extension_api::ToolExecutor<codex_extension_api::ToolCall>>> {
|
||||
let Some(runtime) = goal_runtime_handle(thread_store) else {
|
||||
return Vec::new();
|
||||
|
||||
@@ -75,6 +75,7 @@ impl ToolContributor for ImageGenerationExtension {
|
||||
&self,
|
||||
_session_store: &ExtensionData,
|
||||
thread_store: &ExtensionData,
|
||||
_turn_store: &ExtensionData,
|
||||
) -> Vec<Arc<dyn ToolExecutor<ToolCall>>> {
|
||||
let Some(config) = thread_store.get::<ImageGenerationExtensionConfig>() else {
|
||||
return Vec::new();
|
||||
|
||||
@@ -13,9 +13,9 @@ doctest = false
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
bm25 = { workspace = true }
|
||||
codex-core = { workspace = true }
|
||||
codex-core-skills = { workspace = true }
|
||||
codex-extension-api = { workspace = true }
|
||||
codex-features = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_core::config::Config;
|
||||
use codex_core::skills::SkillLoadOutcome;
|
||||
use codex_core_skills::HostLoadedSkills;
|
||||
use codex_extension_api::ConfigContributor;
|
||||
use codex_extension_api::ContextContributor;
|
||||
use codex_extension_api::ExtensionData;
|
||||
@@ -54,10 +54,15 @@ impl ContextContributor for SkillSearchExtension {
|
||||
}
|
||||
|
||||
impl ThreadLifecycleContributor<Config> for SkillSearchExtension {
|
||||
fn on_thread_start(&self, input: ThreadStartInput<'_, Config>) {
|
||||
input
|
||||
.thread_store
|
||||
.insert(SkillSearchExtensionConfig::from_config(input.config));
|
||||
fn on_thread_start<'a>(
|
||||
&'a self,
|
||||
input: ThreadStartInput<'a, Config>,
|
||||
) -> codex_extension_api::ExtensionFuture<'a, ()> {
|
||||
Box::pin(async move {
|
||||
input
|
||||
.thread_store
|
||||
.insert(SkillSearchExtensionConfig::from_config(input.config));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,9 +93,9 @@ impl ToolContributor for SkillSearchExtension {
|
||||
}
|
||||
|
||||
let skills = turn_store
|
||||
.get::<SkillLoadOutcome>()
|
||||
.map_or_else(Vec::new, |outcome| {
|
||||
outcome.allowed_skills_for_implicit_invocation()
|
||||
.get::<HostLoadedSkills>()
|
||||
.map_or_else(Vec::new, |skills| {
|
||||
skills.outcome().allowed_skills_for_implicit_invocation()
|
||||
});
|
||||
let tool = turn_store.get_or_init(|| SkillSearchTool::new(skills));
|
||||
vec![tool]
|
||||
|
||||
@@ -99,14 +99,13 @@ struct SkillSearchArgs {
|
||||
limit: Option<usize>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl ToolExecutor<ToolCall> for SkillSearchTool {
|
||||
fn tool_name(&self) -> ToolName {
|
||||
ToolName::plain(SKILL_SEARCH_TOOL_NAME)
|
||||
}
|
||||
|
||||
fn spec(&self) -> Option<ToolSpec> {
|
||||
Some(ToolSpec::Function(ResponsesApiTool {
|
||||
fn spec(&self) -> ToolSpec {
|
||||
ToolSpec::Function(ResponsesApiTool {
|
||||
name: SKILL_SEARCH_TOOL_NAME.to_string(),
|
||||
description: "Search available Codex skills by relevance and return plain-text matches with their descriptions and SKILL.md paths.".to_string(),
|
||||
strict: false,
|
||||
@@ -128,31 +127,33 @@ impl ToolExecutor<ToolCall> for SkillSearchTool {
|
||||
Some(false.into()),
|
||||
),
|
||||
output_schema: None,
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
||||
fn supports_parallel_tool_calls(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
async fn handle(&self, call: ToolCall) -> Result<Box<dyn ToolOutput>, FunctionCallError> {
|
||||
let args = parse_args(&call)?;
|
||||
let query = args.query.trim();
|
||||
if query.is_empty() {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
"query must not be empty".to_string(),
|
||||
));
|
||||
}
|
||||
let limit = args.limit.unwrap_or(DEFAULT_SKILL_SEARCH_LIMIT);
|
||||
if limit == 0 {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
"limit must be greater than zero".to_string(),
|
||||
));
|
||||
}
|
||||
fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> {
|
||||
Box::pin(async move {
|
||||
let args = parse_args(&call)?;
|
||||
let query = args.query.trim();
|
||||
if query.is_empty() {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
"query must not be empty".to_string(),
|
||||
));
|
||||
}
|
||||
let limit = args.limit.unwrap_or(DEFAULT_SKILL_SEARCH_LIMIT);
|
||||
if limit == 0 {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
"limit must be greater than zero".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Box::new(PlainTextToolOutput {
|
||||
text: self.search(query, limit),
|
||||
}))
|
||||
Ok(Box::new(PlainTextToolOutput {
|
||||
text: self.search(query, limit),
|
||||
}) as Box<dyn ToolOutput>)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,9 +195,14 @@ impl ToolOutput for PlainTextToolOutput {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_core::skills::SkillPolicy;
|
||||
use codex_extension_api::ConversationHistory;
|
||||
use codex_extension_api::NoopTurnItemEmitter;
|
||||
use codex_protocol::models::FunctionCallOutputBody;
|
||||
use codex_protocol::protocol::SkillScope;
|
||||
use codex_protocol::protocol::TruncationPolicy;
|
||||
use codex_tools::ToolPayload;
|
||||
use codex_utils_absolute_path::test_support::PathBufExt;
|
||||
use codex_utils_absolute_path::test_support::test_path_buf;
|
||||
@@ -224,8 +230,14 @@ mod tests {
|
||||
|
||||
fn call(arguments: serde_json::Value) -> ToolCall {
|
||||
ToolCall {
|
||||
turn_id: "turn-skill-search".to_string(),
|
||||
call_id: "call-skill-search".to_string(),
|
||||
tool_name: ToolName::plain(SKILL_SEARCH_TOOL_NAME),
|
||||
model: "gpt-test".to_string(),
|
||||
truncation_policy: TruncationPolicy::Bytes(1024),
|
||||
conversation_history: ConversationHistory::default(),
|
||||
turn_item_emitter: Arc::new(NoopTurnItemEmitter),
|
||||
environments: Vec::new(),
|
||||
payload: ToolPayload::Function {
|
||||
arguments: arguments.to_string(),
|
||||
},
|
||||
|
||||
@@ -138,6 +138,7 @@ where
|
||||
&self,
|
||||
session_store: &ExtensionData,
|
||||
_thread_store: &ExtensionData,
|
||||
_turn_store: &ExtensionData,
|
||||
) -> Vec<Arc<dyn ToolExecutor<ToolCall>>> {
|
||||
if !self.providers.has_orchestrator_provider() {
|
||||
return Vec::new();
|
||||
|
||||
@@ -111,6 +111,7 @@ impl ToolContributor for WebSearchExtension {
|
||||
&self,
|
||||
session_store: &ExtensionData,
|
||||
thread_store: &ExtensionData,
|
||||
_turn_store: &ExtensionData,
|
||||
) -> Vec<Arc<dyn codex_extension_api::ToolExecutor<codex_extension_api::ToolCall>>> {
|
||||
let Some(config) = thread_store.get::<WebSearchExtensionConfig>() else {
|
||||
return Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user