mirror of
https://github.com/openai/codex.git
synced 2026-06-01 19:02:59 +00:00
Use inject_if_running for active goal steering (#24924)
## Why
This PR is stacked on #24918, which moves goal steering onto
source-labeled internal model context fragments. Active-turn goal
steering should use the same running-turn injection path as other
runtime steering, so those fragments enter the pending input queue as
`ResponseItem`s through the existing
[`Session::inject_if_running`](8d6f6cdf69/codex-rs/core/src/session/inject.rs (L12-L27))
behavior instead of through a goal-specific conversion wrapper.
## What Changed
- Exposes a narrow `CodexThread::inject_if_running` bridge for callers
that only hold a thread handle.
- Changes `ext/goal` active-turn steering to pass `ResponseItem`s
directly.
- Builds goal steering prompts as contextual internal model context
`ResponseItem`s before injecting them into the running turn.
## Testing
Not run locally; PR metadata update only.
This commit is contained in:
@@ -18,7 +18,6 @@ use codex_protocol::mcp::CallToolResult;
|
||||
use codex_protocol::models::ActivePermissionProfile;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::PermissionProfile;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::openai_models::ReasoningEffort;
|
||||
use codex_protocol::protocol::AdditionalContextEntry;
|
||||
@@ -265,20 +264,16 @@ impl CodexThread {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Injects hidden model-visible items into the currently active turn.
|
||||
/// Injects model-visible items into the currently active turn.
|
||||
///
|
||||
/// This is the runtime-owned counterpart to user-facing `steer_input`.
|
||||
/// This is the thread-level bridge to `Session::inject_if_running` for
|
||||
/// callers that only hold a `CodexThread`.
|
||||
/// It returns the unchanged items when this thread has no active turn.
|
||||
pub async fn inject_response_items_into_active_turn(
|
||||
pub async fn inject_if_running(
|
||||
&self,
|
||||
items: Vec<ResponseInputItem>,
|
||||
) -> Result<(), Vec<ResponseInputItem>> {
|
||||
let response_items = items.iter().cloned().map(ResponseItem::from).collect();
|
||||
self.codex
|
||||
.session
|
||||
.inject_if_running(response_items)
|
||||
.await
|
||||
.map_err(|_| items)
|
||||
items: Vec<ResponseItem>,
|
||||
) -> Result<(), Vec<ResponseItem>> {
|
||||
self.codex.session.inject_if_running(items).await
|
||||
}
|
||||
|
||||
pub async fn set_app_server_client_info(
|
||||
@@ -396,7 +391,7 @@ impl CodexThread {
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Append raw Responses API items to the thread's model-visible history.
|
||||
/// Record raw Responses API items without starting a new turn.
|
||||
pub async fn inject_response_items(&self, items: Vec<ResponseItem>) -> CodexResult<()> {
|
||||
if items.is_empty() {
|
||||
return Err(CodexErr::InvalidRequest(
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::sync::atomic::Ordering;
|
||||
|
||||
use codex_core::ThreadManager;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::protocol::ThreadGoal;
|
||||
|
||||
use crate::accounting::BudgetLimitedGoalDisposition;
|
||||
@@ -275,7 +275,7 @@ impl GoalRuntimeHandle {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn inject_active_turn_steering(&self, item: ResponseInputItem) {
|
||||
pub(crate) async fn inject_active_turn_steering(&self, item: ResponseItem) {
|
||||
let Some(thread_manager) = self.inner.thread_manager.upgrade() else {
|
||||
tracing::debug!("skipping goal steering because thread manager is unavailable");
|
||||
return;
|
||||
@@ -284,11 +284,7 @@ impl GoalRuntimeHandle {
|
||||
tracing::debug!("skipping goal steering because live thread is unavailable");
|
||||
return;
|
||||
};
|
||||
if thread
|
||||
.inject_response_items_into_active_turn(vec![item])
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
if thread.inject_if_running(vec![item]).await.is_err() {
|
||||
tracing::debug!("skipping goal steering because no turn is active");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
use codex_core::context::ContextualUserFragment;
|
||||
use codex_core::context::InternalContextSource;
|
||||
use codex_core::context::InternalModelContextFragment;
|
||||
use codex_protocol::models::ResponseInputItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::protocol::ThreadGoal;
|
||||
|
||||
pub(crate) fn budget_limit_steering_item(goal: &ThreadGoal) -> ResponseInputItem {
|
||||
pub(crate) fn budget_limit_steering_item(goal: &ThreadGoal) -> ResponseItem {
|
||||
goal_context_input_item(budget_limit_prompt(goal))
|
||||
}
|
||||
|
||||
pub(crate) fn objective_updated_steering_item(goal: &ThreadGoal) -> ResponseInputItem {
|
||||
pub(crate) fn objective_updated_steering_item(goal: &ThreadGoal) -> ResponseItem {
|
||||
goal_context_input_item(objective_updated_prompt(goal))
|
||||
}
|
||||
|
||||
fn goal_context_input_item(prompt: String) -> ResponseInputItem {
|
||||
InternalModelContextFragment::new(InternalContextSource::from_static("goal"), prompt)
|
||||
.into_response_input_item()
|
||||
fn goal_context_input_item(prompt: String) -> ResponseItem {
|
||||
ContextualUserFragment::into(InternalModelContextFragment::new(
|
||||
InternalContextSource::from_static("goal"),
|
||||
prompt,
|
||||
))
|
||||
}
|
||||
|
||||
fn budget_limit_prompt(goal: &ThreadGoal) -> String {
|
||||
|
||||
Reference in New Issue
Block a user