mirror of
https://github.com/openai/codex.git
synced 2026-11-09 08:40:33 +00:00
Require content kinds for extension prompt fragments (#40177)
## What changed - Require each `PromptFragment` to include a producer-owned `ContentItemKind`. - Re-export `ContentItemKind` from `codex_extension_api` and assign stable kinds to skills, memories, examples, and test contributors. - Limit prompt slots to developer policy and developer capabilities, and combine contributed fragments into a single developer update. ## Testing - Update extension registry tests to verify fragment ordering with the new content-kind metadata. GitOrigin-RevId: ef8624a9b4ae9dce025bca9c0954d91dd8075e85
This commit is contained in:
1 parent
eff640f458
commit
546eed94ac
9 files changed
+67
-83
No files matched your search
@@ -128,6 +128,9 @@ impl codex_extension_api::ContextContributor for GuardianMemoryContextProbe {
|
|||||||
{
|
{
|
||||||
vec![codex_extension_api::PromptFragment::developer_policy(
|
vec![codex_extension_api::PromptFragment::developer_policy(
|
||||||
GUARDIAN_MEMORY_CONTEXT_PROBE,
|
GUARDIAN_MEMORY_CONTEXT_PROBE,
|
||||||
|
codex_extension_api::ContentItemKind(
|
||||||
|
"guardian.memory_context_probe".to_string(),
|
||||||
|
),
|
||||||
)]
|
)]
|
||||||
} else {
|
} else {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
|
|||||||
@@ -62,8 +62,6 @@ use codex_execpolicy::prefix_rule_migration;
|
|||||||
use codex_extension_api::ConversationHistorySnapshot;
|
use codex_extension_api::ConversationHistorySnapshot;
|
||||||
use codex_extension_api::ExtensionDataInit;
|
use codex_extension_api::ExtensionDataInit;
|
||||||
use codex_extension_api::LoadedUserInstructions;
|
use codex_extension_api::LoadedUserInstructions;
|
||||||
use codex_extension_api::PromptFragment;
|
|
||||||
use codex_extension_api::PromptSlot;
|
|
||||||
use codex_extension_api::TurnContextContributionInput;
|
use codex_extension_api::TurnContextContributionInput;
|
||||||
use codex_features::FEATURES;
|
use codex_features::FEATURES;
|
||||||
use codex_features::Feature;
|
use codex_features::Feature;
|
||||||
@@ -999,25 +997,6 @@ async fn thread_title_from_thread_store(
|
|||||||
(!title.is_empty() && thread.preview.trim() != title).then(|| title.to_string())
|
(!title.is_empty() && thread.preview.trim() != title).then(|| title.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_prompt_fragment(
|
|
||||||
fragment: PromptFragment,
|
|
||||||
developer_sections: &mut Vec<String>,
|
|
||||||
contextual_user_sections: &mut Vec<String>,
|
|
||||||
separate_developer_sections: &mut Vec<String>,
|
|
||||||
) {
|
|
||||||
match fragment.slot() {
|
|
||||||
PromptSlot::DeveloperPolicy | PromptSlot::DeveloperCapabilities => {
|
|
||||||
developer_sections.push(fragment.text().to_string());
|
|
||||||
}
|
|
||||||
PromptSlot::ContextualUser => {
|
|
||||||
contextual_user_sections.push(fragment.text().to_string());
|
|
||||||
}
|
|
||||||
PromptSlot::SeparateDeveloper => {
|
|
||||||
separate_developer_sections.push(fragment.text().to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Session {
|
impl Session {
|
||||||
pub(crate) async fn app_server_client_metadata(&self) -> AppServerClientMetadata {
|
pub(crate) async fn app_server_client_metadata(&self) -> AppServerClientMetadata {
|
||||||
let state = self.state.lock().await;
|
let state = self.state.lock().await;
|
||||||
@@ -3486,8 +3465,6 @@ impl Session {
|
|||||||
) -> Vec<ResponseItem> {
|
) -> Vec<ResponseItem> {
|
||||||
let turn_context = step_context.turn.as_ref();
|
let turn_context = step_context.turn.as_ref();
|
||||||
let mut developer_sections = Vec::new();
|
let mut developer_sections = Vec::new();
|
||||||
let mut contextual_user_sections = Vec::new();
|
|
||||||
let mut separate_developer_sections = Vec::new();
|
|
||||||
let context_contributors = self.services.extensions.context_contributors().to_vec();
|
let context_contributors = self.services.extensions.context_contributors().to_vec();
|
||||||
|
|
||||||
for contributor in &context_contributors {
|
for contributor in &context_contributors {
|
||||||
@@ -3502,34 +3479,13 @@ impl Session {
|
|||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
push_prompt_fragment(
|
developer_sections.push(fragment.text().to_string());
|
||||||
fragment,
|
|
||||||
&mut developer_sections,
|
|
||||||
&mut contextual_user_sections,
|
|
||||||
&mut separate_developer_sections,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut items = Vec::with_capacity(3);
|
crate::context_manager::updates::build_developer_update_item(developer_sections)
|
||||||
if let Some(developer_message) =
|
.into_iter()
|
||||||
crate::context_manager::updates::build_developer_update_item(developer_sections)
|
.collect()
|
||||||
{
|
|
||||||
items.push(developer_message);
|
|
||||||
}
|
|
||||||
for section in separate_developer_sections {
|
|
||||||
if let Some(developer_message) =
|
|
||||||
crate::context_manager::updates::build_developer_update_item(vec![section])
|
|
||||||
{
|
|
||||||
items.push(developer_message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(contextual_user_message) =
|
|
||||||
crate::context_manager::updates::build_contextual_user_message(contextual_user_sections)
|
|
||||||
{
|
|
||||||
items.push(contextual_user_message);
|
|
||||||
}
|
|
||||||
items
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn build_initial_context_with_world_state(
|
pub(crate) async fn build_initial_context_with_world_state(
|
||||||
@@ -3598,12 +3554,7 @@ impl Session {
|
|||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
push_prompt_fragment(
|
developer_sections.push(fragment.text().to_string());
|
||||||
fragment,
|
|
||||||
&mut developer_sections,
|
|
||||||
&mut contextual_user_sections,
|
|
||||||
&mut separate_developer_sections,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for contributor in &context_contributors {
|
for contributor in &context_contributors {
|
||||||
@@ -3618,12 +3569,7 @@ impl Session {
|
|||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
push_prompt_fragment(
|
developer_sections.push(fragment.text().to_string());
|
||||||
fragment,
|
|
||||||
&mut developer_sections,
|
|
||||||
&mut contextual_user_sections,
|
|
||||||
&mut separate_developer_sections,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This is full-context metadata. Steady-state context diffs should not re-emit it.
|
// This is full-context metadata. Steady-state context diffs should not re-emit it.
|
||||||
|
|||||||
@@ -9239,6 +9239,7 @@ impl codex_extension_api::ContextContributor for PromptExtensionTestContributor
|
|||||||
.then(|| {
|
.then(|| {
|
||||||
codex_extension_api::PromptFragment::developer_policy(
|
codex_extension_api::PromptFragment::developer_policy(
|
||||||
"prompt extension enabled",
|
"prompt extension enabled",
|
||||||
|
codex_extension_api::ContentItemKind("test.prompt_extension".to_string()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -9271,6 +9272,7 @@ impl codex_extension_api::ContextContributor for TurnContextExtensionTestContrib
|
|||||||
.then(|| {
|
.then(|| {
|
||||||
codex_extension_api::PromptFragment::developer_policy(
|
codex_extension_api::PromptFragment::developer_policy(
|
||||||
"turn context extension enabled",
|
"turn context extension enabled",
|
||||||
|
codex_extension_api::ContentItemKind("test.turn_context".to_string()),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::sync::Arc;
|
|||||||
use std::sync::atomic::AtomicU64;
|
use std::sync::atomic::AtomicU64;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
|
use codex_extension_api::ContentItemKind;
|
||||||
use codex_extension_api::ContextContributor;
|
use codex_extension_api::ContextContributor;
|
||||||
use codex_extension_api::ExtensionData;
|
use codex_extension_api::ExtensionData;
|
||||||
use codex_extension_api::ExtensionRegistryBuilder;
|
use codex_extension_api::ExtensionRegistryBuilder;
|
||||||
@@ -28,6 +29,7 @@ impl ContextContributor for StyleContributor {
|
|||||||
|
|
||||||
vec![PromptFragment::developer_policy(
|
vec![PromptFragment::developer_policy(
|
||||||
"Prefer short answers unless the user asks for detail.",
|
"Prefer short answers unless the user asks for detail.",
|
||||||
|
ContentItemKind("example.style_instructions".to_string()),
|
||||||
)]
|
)]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -48,6 +50,7 @@ impl ContextContributor for UsageContributor {
|
|||||||
|
|
||||||
vec![PromptFragment::developer_capability(
|
vec![PromptFragment::developer_capability(
|
||||||
"This extension can contribute more than one prompt fragment.",
|
"This extension can contribute more than one prompt fragment.",
|
||||||
|
ContentItemKind("example.usage_instructions".to_string()),
|
||||||
)]
|
)]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,41 +1,38 @@
|
|||||||
// All this file should be replaced by the existing fragment implementation ofc
|
// All this file should be replaced by the existing fragment implementation ofc
|
||||||
|
|
||||||
|
use codex_protocol::models::ContentItemKind;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub enum PromptSlot {
|
pub enum PromptSlot {
|
||||||
DeveloperPolicy,
|
DeveloperPolicy,
|
||||||
DeveloperCapabilities,
|
DeveloperCapabilities,
|
||||||
ContextualUser,
|
|
||||||
SeparateDeveloper,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
pub struct PromptFragment {
|
pub struct PromptFragment {
|
||||||
slot: PromptSlot,
|
slot: PromptSlot,
|
||||||
text: String,
|
text: String,
|
||||||
|
content_kind: ContentItemKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PromptFragment {
|
impl PromptFragment {
|
||||||
/// Creates a prompt fragment for the given slot.
|
/// Creates a prompt fragment for the given slot.
|
||||||
pub fn new(slot: PromptSlot, text: impl Into<String>) -> Self {
|
pub fn new(slot: PromptSlot, text: impl Into<String>, content_kind: ContentItemKind) -> Self {
|
||||||
Self {
|
Self {
|
||||||
slot,
|
slot,
|
||||||
text: text.into(),
|
text: text.into(),
|
||||||
|
content_kind,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a developer-policy prompt fragment.
|
/// Creates a developer-policy prompt fragment.
|
||||||
pub fn developer_policy(text: impl Into<String>) -> Self {
|
pub fn developer_policy(text: impl Into<String>, content_kind: ContentItemKind) -> Self {
|
||||||
Self::new(PromptSlot::DeveloperPolicy, text)
|
Self::new(PromptSlot::DeveloperPolicy, text, content_kind)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a developer-capabilities prompt fragment.
|
/// Creates a developer-capabilities prompt fragment.
|
||||||
pub fn developer_capability(text: impl Into<String>) -> Self {
|
pub fn developer_capability(text: impl Into<String>, content_kind: ContentItemKind) -> Self {
|
||||||
Self::new(PromptSlot::DeveloperCapabilities, text)
|
Self::new(PromptSlot::DeveloperCapabilities, text, content_kind)
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a separate top-level developer prompt fragment.
|
|
||||||
pub fn separate_developer(text: impl Into<String>) -> Self {
|
|
||||||
Self::new(PromptSlot::SeparateDeveloper, text)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the target prompt slot.
|
/// Returns the target prompt slot.
|
||||||
@@ -47,4 +44,9 @@ impl PromptFragment {
|
|||||||
pub fn text(&self) -> &str {
|
pub fn text(&self) -> &str {
|
||||||
&self.text
|
&self.text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the producer-owned classification of the model-visible text.
|
||||||
|
pub fn content_kind(&self) -> &ContentItemKind {
|
||||||
|
&self.content_kind
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -17,6 +17,7 @@ pub use capabilities::NoopResponseItemInjector;
|
|||||||
pub use capabilities::ResponseItemInjectionFuture;
|
pub use capabilities::ResponseItemInjectionFuture;
|
||||||
pub use capabilities::ResponseItemInjector;
|
pub use capabilities::ResponseItemInjector;
|
||||||
pub use codex_context_fragments::ContextualUserFragment;
|
pub use codex_context_fragments::ContextualUserFragment;
|
||||||
|
pub use codex_protocol::models::ContentItemKind;
|
||||||
pub use codex_protocol::models::ResponseItem;
|
pub use codex_protocol::models::ResponseItem;
|
||||||
pub use codex_protocol::security_risk::SecurityRiskScore;
|
pub use codex_protocol::security_risk::SecurityRiskScore;
|
||||||
pub use codex_tools::ConversationHistory;
|
pub use codex_tools::ConversationHistory;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use std::sync::Mutex;
|
|||||||
|
|
||||||
use codex_extension_api::ApprovalReviewContributor;
|
use codex_extension_api::ApprovalReviewContributor;
|
||||||
use codex_extension_api::ConfigContributor;
|
use codex_extension_api::ConfigContributor;
|
||||||
|
use codex_extension_api::ContentItemKind;
|
||||||
use codex_extension_api::ContextContributor;
|
use codex_extension_api::ContextContributor;
|
||||||
use codex_extension_api::ContextualUserFragment;
|
use codex_extension_api::ContextualUserFragment;
|
||||||
use codex_extension_api::ExtensionData;
|
use codex_extension_api::ExtensionData;
|
||||||
@@ -16,7 +17,6 @@ use codex_extension_api::ExtensionRegistryBuilder;
|
|||||||
use codex_extension_api::ExtensionWarning;
|
use codex_extension_api::ExtensionWarning;
|
||||||
use codex_extension_api::McpServerContributionContext;
|
use codex_extension_api::McpServerContributionContext;
|
||||||
use codex_extension_api::PromptFragment;
|
use codex_extension_api::PromptFragment;
|
||||||
use codex_extension_api::PromptSlot;
|
|
||||||
use codex_extension_api::SkillInvocationContributor;
|
use codex_extension_api::SkillInvocationContributor;
|
||||||
use codex_extension_api::ThreadLifecycleContributor;
|
use codex_extension_api::ThreadLifecycleContributor;
|
||||||
use codex_extension_api::TokenUsageContributor;
|
use codex_extension_api::TokenUsageContributor;
|
||||||
@@ -194,6 +194,7 @@ impl ContextContributor for NamedContextContributor {
|
|||||||
) -> ExtensionFuture<'a, Vec<PromptFragment>> {
|
) -> ExtensionFuture<'a, Vec<PromptFragment>> {
|
||||||
Box::pin(std::future::ready(vec![PromptFragment::developer_policy(
|
Box::pin(std::future::ready(vec![PromptFragment::developer_policy(
|
||||||
self.0,
|
self.0,
|
||||||
|
ContentItemKind("test.thread_context".to_string()),
|
||||||
)]))
|
)]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -205,10 +206,12 @@ impl ContextContributor for NamedTurnContextContributor {
|
|||||||
&'a self,
|
&'a self,
|
||||||
_input: TurnContextContributionInput<'a>,
|
_input: TurnContextContributionInput<'a>,
|
||||||
) -> ExtensionFuture<'a, Vec<PromptFragment>> {
|
) -> ExtensionFuture<'a, Vec<PromptFragment>> {
|
||||||
Box::pin(std::future::ready(vec![PromptFragment::new(
|
Box::pin(std::future::ready(vec![
|
||||||
PromptSlot::ContextualUser,
|
PromptFragment::developer_capability(
|
||||||
self.0,
|
self.0,
|
||||||
)]))
|
ContentItemKind("test.turn_context".to_string()),
|
||||||
|
),
|
||||||
|
]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,10 +292,22 @@ async fn contributors_preserve_registration_order() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
fragments,
|
fragments,
|
||||||
vec![
|
vec![
|
||||||
PromptFragment::developer_policy("first"),
|
PromptFragment::developer_policy(
|
||||||
PromptFragment::developer_policy("second"),
|
"first",
|
||||||
PromptFragment::new(PromptSlot::ContextualUser, "turn-first"),
|
ContentItemKind("test.thread_context".to_string()),
|
||||||
PromptFragment::new(PromptSlot::ContextualUser, "turn-second"),
|
),
|
||||||
|
PromptFragment::developer_policy(
|
||||||
|
"second",
|
||||||
|
ContentItemKind("test.thread_context".to_string()),
|
||||||
|
),
|
||||||
|
PromptFragment::developer_capability(
|
||||||
|
"turn-first",
|
||||||
|
ContentItemKind("test.turn_context".to_string()),
|
||||||
|
),
|
||||||
|
PromptFragment::developer_capability(
|
||||||
|
"turn-second",
|
||||||
|
ContentItemKind("test.turn_context".to_string()),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use codex_core::config::Config;
|
use codex_core::config::Config;
|
||||||
use codex_extension_api::ConfigContributor;
|
use codex_extension_api::ConfigContributor;
|
||||||
|
use codex_extension_api::ContentItemKind;
|
||||||
use codex_extension_api::ContextContributor;
|
use codex_extension_api::ContextContributor;
|
||||||
use codex_extension_api::ExtensionData;
|
use codex_extension_api::ExtensionData;
|
||||||
use codex_extension_api::ExtensionFuture;
|
use codex_extension_api::ExtensionFuture;
|
||||||
@@ -63,7 +64,12 @@ impl ContextContributor for MemoriesExtension {
|
|||||||
|
|
||||||
build_memory_tool_developer_instructions(&config.codex_home)
|
build_memory_tool_developer_instructions(&config.codex_home)
|
||||||
.await
|
.await
|
||||||
.map(PromptFragment::developer_policy)
|
.map(|instructions| {
|
||||||
|
PromptFragment::developer_policy(
|
||||||
|
instructions,
|
||||||
|
ContentItemKind("memories.instructions".to_string()),
|
||||||
|
)
|
||||||
|
})
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use codex_exec_server::FileSystemSandboxContext;
|
|||||||
use codex_exec_server::LOCAL_ENVIRONMENT_ID;
|
use codex_exec_server::LOCAL_ENVIRONMENT_ID;
|
||||||
use codex_exec_server::ResolvedSelectedCapabilityRoot;
|
use codex_exec_server::ResolvedSelectedCapabilityRoot;
|
||||||
use codex_extension_api::ConfigContributor;
|
use codex_extension_api::ConfigContributor;
|
||||||
|
use codex_extension_api::ContentItemKind;
|
||||||
use codex_extension_api::ContextContributor;
|
use codex_extension_api::ContextContributor;
|
||||||
use codex_extension_api::ContextualUserFragment;
|
use codex_extension_api::ContextualUserFragment;
|
||||||
use codex_extension_api::ExtensionData;
|
use codex_extension_api::ExtensionData;
|
||||||
@@ -236,7 +237,12 @@ where
|
|||||||
}
|
}
|
||||||
rendered
|
rendered
|
||||||
.fragment
|
.fragment
|
||||||
.map(|fragment| PromptFragment::developer_capability(fragment.render()))
|
.map(|fragment| {
|
||||||
|
PromptFragment::developer_capability(
|
||||||
|
fragment.render(),
|
||||||
|
ContentItemKind("skills.catalog".to_string()),
|
||||||
|
)
|
||||||
|
})
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in new issue
Block a user