mirror of
https://github.com/openai/codex.git
synced 2026-09-20 12:47:38 +00:00
feat(extension-api): add global instructions contributor
This commit is contained in:
1
codex-rs/Cargo.lock
generated
1
codex-rs/Cargo.lock
generated
@@ -2922,6 +2922,7 @@ dependencies = [
|
||||
"codex-context-fragments",
|
||||
"codex-protocol",
|
||||
"codex-tools",
|
||||
"codex-utils-absolute-path",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -47,6 +47,11 @@ pub use codex_core::skills::SkillsManager;
|
||||
pub use codex_core::thread_store_from_config;
|
||||
pub use codex_exec_server::EnvironmentManager;
|
||||
pub use codex_exec_server::ExecServerRuntimePaths;
|
||||
pub use codex_extension_api::ExtensionRegistryBuilder;
|
||||
pub use codex_extension_api::GlobalInstruction;
|
||||
pub use codex_extension_api::GlobalInstructions;
|
||||
pub use codex_extension_api::GlobalInstructionsContributor;
|
||||
pub use codex_extension_api::GlobalInstructionsFuture;
|
||||
pub use codex_extension_api::empty_extension_registry;
|
||||
pub use codex_features::Feature;
|
||||
pub use codex_features::Features;
|
||||
|
||||
@@ -18,3 +18,4 @@ async-trait = { workspace = true }
|
||||
codex-context-fragments = { workspace = true }
|
||||
codex-protocol = { workspace = true }
|
||||
codex-tools = { workspace = true }
|
||||
codex-utils-absolute-path = { workspace = true }
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_context_fragments::ContextualUserFragment;
|
||||
@@ -7,6 +8,7 @@ use codex_protocol::protocol::ReviewDecision;
|
||||
use codex_protocol::protocol::TokenUsageInfo;
|
||||
use codex_tools::ToolCall;
|
||||
use codex_tools::ToolExecutor;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
|
||||
use crate::ExtensionData;
|
||||
|
||||
@@ -34,6 +36,42 @@ pub use turn_lifecycle::TurnErrorInput;
|
||||
pub use turn_lifecycle::TurnStartInput;
|
||||
pub use turn_lifecycle::TurnStopInput;
|
||||
|
||||
/// One model-visible global instruction and its optional source path.
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct GlobalInstruction {
|
||||
pub contents: String,
|
||||
pub source: Option<AbsolutePathBuf>,
|
||||
}
|
||||
|
||||
impl GlobalInstruction {
|
||||
pub fn new(contents: impl Into<String>, source: Option<AbsolutePathBuf>) -> Self {
|
||||
Self {
|
||||
contents: contents.into(),
|
||||
source,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Global instructions resolved by a host-installed contributor.
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct GlobalInstructions {
|
||||
pub instructions: Vec<GlobalInstruction>,
|
||||
pub warnings: Vec<String>,
|
||||
}
|
||||
|
||||
/// Future returned while resolving global instructions.
|
||||
pub type GlobalInstructionsFuture<'a> =
|
||||
Pin<Box<dyn Future<Output = Result<GlobalInstructions, String>> + Send + 'a>>;
|
||||
|
||||
/// Resolves model-visible instructions that apply globally to a thread.
|
||||
///
|
||||
/// Implementations should resolve their backing source only when this method
|
||||
/// is called. Recoverable source issues may be returned in `warnings`; an
|
||||
/// error indicates that the requested resolution could not be completed.
|
||||
pub trait GlobalInstructionsContributor: Send + Sync {
|
||||
fn contribute(&self) -> GlobalInstructionsFuture<'_>;
|
||||
}
|
||||
|
||||
/// Extension contribution that adds prompt fragments during prompt assembly.
|
||||
pub trait ContextContributor: Send + Sync {
|
||||
fn contribute<'a>(
|
||||
|
||||
@@ -31,6 +31,10 @@ pub use codex_tools::parse_tool_input_schema_without_compaction;
|
||||
pub use contributors::ApprovalReviewContributor;
|
||||
pub use contributors::ConfigContributor;
|
||||
pub use contributors::ContextContributor;
|
||||
pub use contributors::GlobalInstruction;
|
||||
pub use contributors::GlobalInstructions;
|
||||
pub use contributors::GlobalInstructionsContributor;
|
||||
pub use contributors::GlobalInstructionsFuture;
|
||||
pub use contributors::PromptFragment;
|
||||
pub use contributors::PromptSlot;
|
||||
pub use contributors::ThreadIdleInput;
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::ConfigContributor;
|
||||
use crate::ContextContributor;
|
||||
use crate::ExtensionData;
|
||||
use crate::ExtensionEventSink;
|
||||
use crate::GlobalInstructionsContributor;
|
||||
use crate::NoopExtensionEventSink;
|
||||
use crate::ThreadLifecycleContributor;
|
||||
use crate::TokenUsageContributor;
|
||||
@@ -19,6 +20,7 @@ use crate::TurnLifecycleContributor;
|
||||
/// Mutable registry used while hosts register typed runtime contributions.
|
||||
pub struct ExtensionRegistryBuilder<C: Sync> {
|
||||
event_sink: Arc<dyn ExtensionEventSink>,
|
||||
global_instructions_contributor: Option<Arc<dyn GlobalInstructionsContributor>>,
|
||||
thread_lifecycle_contributors: Vec<Arc<dyn ThreadLifecycleContributor<C>>>,
|
||||
turn_lifecycle_contributors: Vec<Arc<dyn TurnLifecycleContributor>>,
|
||||
config_contributors: Vec<Arc<dyn ConfigContributor<C>>>,
|
||||
@@ -35,6 +37,7 @@ impl<C: Sync> Default for ExtensionRegistryBuilder<C> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
event_sink: Arc::new(NoopExtensionEventSink),
|
||||
global_instructions_contributor: None,
|
||||
thread_lifecycle_contributors: Vec::new(),
|
||||
turn_lifecycle_contributors: Vec::new(),
|
||||
config_contributors: Vec::new(),
|
||||
@@ -68,6 +71,14 @@ impl<C: Sync> ExtensionRegistryBuilder<C> {
|
||||
Arc::clone(&self.event_sink)
|
||||
}
|
||||
|
||||
/// Registers the contributor that resolves global model instructions.
|
||||
pub fn global_instructions_contributor(
|
||||
&mut self,
|
||||
contributor: Arc<dyn GlobalInstructionsContributor>,
|
||||
) {
|
||||
self.global_instructions_contributor = Some(contributor);
|
||||
}
|
||||
|
||||
/// Registers one approval-review contributor.
|
||||
pub fn approval_review_contributor(&mut self, contributor: Arc<dyn ApprovalReviewContributor>) {
|
||||
self.approval_review_contributors.push(contributor);
|
||||
@@ -125,6 +136,7 @@ impl<C: Sync> ExtensionRegistryBuilder<C> {
|
||||
pub fn build(self) -> ExtensionRegistry<C> {
|
||||
ExtensionRegistry {
|
||||
event_sink: self.event_sink,
|
||||
global_instructions_contributor: self.global_instructions_contributor,
|
||||
thread_lifecycle_contributors: self.thread_lifecycle_contributors,
|
||||
turn_lifecycle_contributors: self.turn_lifecycle_contributors,
|
||||
config_contributors: self.config_contributors,
|
||||
@@ -142,6 +154,7 @@ impl<C: Sync> ExtensionRegistryBuilder<C> {
|
||||
/// Immutable typed registry produced after extensions are installed.
|
||||
pub struct ExtensionRegistry<C: Sync> {
|
||||
event_sink: Arc<dyn ExtensionEventSink>,
|
||||
global_instructions_contributor: Option<Arc<dyn GlobalInstructionsContributor>>,
|
||||
thread_lifecycle_contributors: Vec<Arc<dyn ThreadLifecycleContributor<C>>>,
|
||||
turn_lifecycle_contributors: Vec<Arc<dyn TurnLifecycleContributor>>,
|
||||
config_contributors: Vec<Arc<dyn ConfigContributor<C>>>,
|
||||
@@ -160,6 +173,13 @@ impl<C: Sync> ExtensionRegistry<C> {
|
||||
Arc::clone(&self.event_sink)
|
||||
}
|
||||
|
||||
/// Returns the contributor that resolves global model instructions.
|
||||
pub fn global_instructions_contributor(
|
||||
&self,
|
||||
) -> Option<&Arc<dyn GlobalInstructionsContributor>> {
|
||||
self.global_instructions_contributor.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the registered thread-lifecycle contributors.
|
||||
pub fn thread_lifecycle_contributors(&self) -> &[Arc<dyn ThreadLifecycleContributor<C>>] {
|
||||
&self.thread_lifecycle_contributors
|
||||
|
||||
33
codex-rs/ext/extension-api/tests/registry.rs
Normal file
33
codex-rs/ext/extension-api/tests/registry.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_extension_api::ExtensionRegistryBuilder;
|
||||
use codex_extension_api::GlobalInstructions;
|
||||
use codex_extension_api::GlobalInstructionsContributor;
|
||||
use codex_extension_api::GlobalInstructionsFuture;
|
||||
|
||||
struct StaticGlobalInstructionsContributor;
|
||||
|
||||
impl GlobalInstructionsContributor for StaticGlobalInstructionsContributor {
|
||||
fn contribute(&self) -> GlobalInstructionsFuture<'_> {
|
||||
Box::pin(std::future::ready(Ok(GlobalInstructions::default())))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn global_instructions_contributor_is_optional_and_singular() {
|
||||
let empty = ExtensionRegistryBuilder::<()>::new().build();
|
||||
assert!(empty.global_instructions_contributor().is_none());
|
||||
|
||||
let contributor: Arc<dyn GlobalInstructionsContributor> =
|
||||
Arc::new(StaticGlobalInstructionsContributor);
|
||||
let mut builder = ExtensionRegistryBuilder::<()>::new();
|
||||
builder.global_instructions_contributor(Arc::clone(&contributor));
|
||||
let registry = builder.build();
|
||||
|
||||
assert!(Arc::ptr_eq(
|
||||
registry
|
||||
.global_instructions_contributor()
|
||||
.expect("registered contributor"),
|
||||
&contributor
|
||||
));
|
||||
}
|
||||
Reference in New Issue
Block a user