mirror of
https://github.com/openai/codex.git
synced 2026-10-21 17:48:44 +00:00
[codex] Wire reasoning summary delivery config
This commit is contained in:
@@ -2300,6 +2300,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ReasoningSummaryDelivery": {
|
||||
"description": "Controls when reasoning summaries are delivered relative to later response items.",
|
||||
"enum": [
|
||||
"sequential",
|
||||
"concurrent",
|
||||
"concurrent_cutoff"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"RemoteControlDisableParams": {
|
||||
"properties": {
|
||||
"ephemeral": {
|
||||
@@ -3673,6 +3682,23 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -4184,6 +4210,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -4361,6 +4404,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
+70
@@ -7936,6 +7936,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"reasoning_summary_delivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"review_model": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -14844,6 +14854,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ReasoningSummaryDelivery": {
|
||||
"description": "Controls when reasoning summaries are delivered relative to later response items.",
|
||||
"enum": [
|
||||
"sequential",
|
||||
"concurrent",
|
||||
"concurrent_cutoff"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningSummaryPartAddedNotification": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
@@ -17329,6 +17348,23 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -19062,6 +19098,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -19459,6 +19512,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/v2/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
+70
@@ -4176,6 +4176,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"reasoning_summary_delivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"review_model": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -11248,6 +11258,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ReasoningSummaryDelivery": {
|
||||
"description": "Controls when reasoning summaries are delivered relative to later response items.",
|
||||
"enum": [
|
||||
"sequential",
|
||||
"concurrent",
|
||||
"concurrent_cutoff"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningSummaryPartAddedNotification": {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"properties": {
|
||||
@@ -15108,6 +15127,23 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -16841,6 +16877,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
@@ -17238,6 +17291,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
@@ -381,6 +381,16 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"reasoning_summary_delivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"review_model": {
|
||||
"type": [
|
||||
"string",
|
||||
@@ -710,6 +720,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ReasoningSummaryDelivery": {
|
||||
"description": "Controls when reasoning summaries are delivered relative to later response items.",
|
||||
"enum": [
|
||||
"sequential",
|
||||
"concurrent",
|
||||
"concurrent_cutoff"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SandboxMode": {
|
||||
"enum": [
|
||||
"read-only",
|
||||
|
||||
@@ -63,6 +63,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ReasoningSummaryDelivery": {
|
||||
"description": "Controls when reasoning summaries are delivered relative to later response items.",
|
||||
"enum": [
|
||||
"sequential",
|
||||
"concurrent",
|
||||
"concurrent_cutoff"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SandboxMode": {
|
||||
"enum": [
|
||||
"read-only",
|
||||
@@ -146,6 +155,23 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
@@ -449,6 +449,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ReasoningSummaryDelivery": {
|
||||
"description": "Controls when reasoning summaries are delivered relative to later response items.",
|
||||
"enum": [
|
||||
"sequential",
|
||||
"concurrent",
|
||||
"concurrent_cutoff"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ResponseItem": {
|
||||
"oneOf": [
|
||||
{
|
||||
@@ -1398,6 +1407,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
@@ -227,6 +227,15 @@
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"ReasoningSummaryDelivery": {
|
||||
"description": "Controls when reasoning summaries are delivered relative to later response items.",
|
||||
"enum": [
|
||||
"sequential",
|
||||
"concurrent",
|
||||
"concurrent_cutoff"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"SandboxMode": {
|
||||
"enum": [
|
||||
"read-only",
|
||||
@@ -365,6 +374,23 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"reasoningSummaryDelivery": {
|
||||
"anyOf": [
|
||||
{
|
||||
"anyOf": [
|
||||
{
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
]
|
||||
},
|
||||
"sandbox": {
|
||||
"anyOf": [
|
||||
{
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* Controls when reasoning summaries are delivered relative to later response items.
|
||||
*/
|
||||
export type ReasoningSummaryDelivery = "sequential" | "concurrent" | "concurrent_cutoff";
|
||||
@@ -64,6 +64,7 @@ export type { ReasoningEffort } from "./ReasoningEffort";
|
||||
export type { ReasoningItemContent } from "./ReasoningItemContent";
|
||||
export type { ReasoningItemReasoningSummary } from "./ReasoningItemReasoningSummary";
|
||||
export type { ReasoningSummary } from "./ReasoningSummary";
|
||||
export type { ReasoningSummaryDelivery } from "./ReasoningSummaryDelivery";
|
||||
export type { RequestId } from "./RequestId";
|
||||
export type { Resource } from "./Resource";
|
||||
export type { ResourceContent } from "./ResourceContent";
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ import type { AutoCompactTokenLimitScope } from "../AutoCompactTokenLimitScope";
|
||||
import type { ForcedLoginMethod } from "../ForcedLoginMethod";
|
||||
import type { ReasoningEffort } from "../ReasoningEffort";
|
||||
import type { ReasoningSummary } from "../ReasoningSummary";
|
||||
import type { ReasoningSummaryDelivery } from "../ReasoningSummaryDelivery";
|
||||
import type { Verbosity } from "../Verbosity";
|
||||
import type { WebSearchMode } from "../WebSearchMode";
|
||||
import type { JsonValue } from "../serde_json/JsonValue";
|
||||
@@ -20,4 +21,4 @@ export type Config = {model: string | null, review_model: string | null, model_c
|
||||
* [UNSTABLE] Optional default for where approval requests are routed for
|
||||
* review.
|
||||
*/
|
||||
approvals_reviewer: ApprovalsReviewer | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: ForcedChatgptWorkspaceIds | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, service_tier: string | null, analytics: AnalyticsConfig | null, desktop: { [key in string]?: JsonValue } | null} & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
|
||||
approvals_reviewer: ApprovalsReviewer | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: ForcedChatgptWorkspaceIds | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, reasoning_summary_delivery: ReasoningSummaryDelivery | null, model_verbosity: Verbosity | null, service_tier: string | null, analytics: AnalyticsConfig | null, desktop: { [key in string]?: JsonValue } | null} & ({ [key in string]?: number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null });
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// GENERATED CODE! DO NOT MODIFY BY HAND!
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { ReasoningSummaryDelivery } from "../ReasoningSummaryDelivery";
|
||||
import type { JsonValue } from "../serde_json/JsonValue";
|
||||
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
|
||||
import type { AskForApproval } from "./AskForApproval";
|
||||
@@ -30,7 +31,7 @@ model?: string | null, modelProvider?: string | null, serviceTier?: string | nul
|
||||
* Override where approval requests are routed for review on this thread
|
||||
* and subsequent turns.
|
||||
*/
|
||||
approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, ephemeral?: boolean, /**
|
||||
approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, reasoningSummaryDelivery?: ReasoningSummaryDelivery | null | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, ephemeral?: boolean, /**
|
||||
* Optional client-supplied analytics source classification for this forked thread.
|
||||
*/
|
||||
threadSource?: ThreadSource | null};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Personality } from "../Personality";
|
||||
import type { ReasoningSummaryDelivery } from "../ReasoningSummaryDelivery";
|
||||
import type { JsonValue } from "../serde_json/JsonValue";
|
||||
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
|
||||
import type { AskForApproval } from "./AskForApproval";
|
||||
@@ -30,4 +31,4 @@ model?: string | null, modelProvider?: string | null, serviceTier?: string | nul
|
||||
* Override where approval requests are routed for review on this thread
|
||||
* and subsequent turns.
|
||||
*/
|
||||
approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null};
|
||||
approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, reasoningSummaryDelivery?: ReasoningSummaryDelivery | null | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { Personality } from "../Personality";
|
||||
import type { ReasoningSummaryDelivery } from "../ReasoningSummaryDelivery";
|
||||
import type { JsonValue } from "../serde_json/JsonValue";
|
||||
import type { ApprovalsReviewer } from "./ApprovalsReviewer";
|
||||
import type { AskForApproval } from "./AskForApproval";
|
||||
@@ -13,7 +14,7 @@ export type ThreadStartParams = {model?: string | null, modelProvider?: string |
|
||||
* Override where approval requests are routed for review on this thread
|
||||
* and subsequent turns.
|
||||
*/
|
||||
approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, serviceName?: string | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, ephemeral?: boolean | null, sessionStartSource?: ThreadStartSource | null, /**
|
||||
approvalsReviewer?: ApprovalsReviewer | null, sandbox?: SandboxMode | null, reasoningSummaryDelivery?: ReasoningSummaryDelivery | null | null, config?: { [key in string]?: JsonValue } | null, serviceName?: string | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, ephemeral?: boolean | null, sessionStartSource?: ThreadStartSource | null, /**
|
||||
* Optional client-supplied analytics source classification for this thread.
|
||||
*/
|
||||
threadSource?: ThreadSource | null};
|
||||
|
||||
@@ -7,6 +7,7 @@ use codex_experimental_api_macros::ExperimentalApi;
|
||||
use codex_protocol::config_types::AutoCompactTokenLimitScope;
|
||||
use codex_protocol::config_types::ForcedLoginMethod;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
use codex_protocol::config_types::WebSearchToolConfig;
|
||||
@@ -265,6 +266,7 @@ pub struct Config {
|
||||
pub compact_prompt: Option<String>,
|
||||
pub model_reasoning_effort: Option<ReasoningEffort>,
|
||||
pub model_reasoning_summary: Option<ReasoningSummary>,
|
||||
pub reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
pub model_verbosity: Option<Verbosity>,
|
||||
pub service_tier: Option<String>,
|
||||
pub analytics: Option<AnalyticsConfig>,
|
||||
|
||||
@@ -2,6 +2,7 @@ use super::*;
|
||||
use crate::ServerNotification;
|
||||
use codex_protocol::approvals::ElicitationRequest as CoreElicitationRequest;
|
||||
use codex_protocol::config_types::MultiAgentMode;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::items::AgentMessageContent;
|
||||
use codex_protocol::items::AgentMessageItem;
|
||||
use codex_protocol::items::CollabAgentTool as CoreCollabAgentTool;
|
||||
@@ -1718,6 +1719,7 @@ fn config_granular_approval_policy_is_marked_experimental() {
|
||||
compact_prompt: None,
|
||||
model_reasoning_effort: None,
|
||||
model_reasoning_summary: None,
|
||||
reasoning_summary_delivery: None,
|
||||
model_verbosity: None,
|
||||
service_tier: None,
|
||||
analytics: None,
|
||||
@@ -1751,6 +1753,7 @@ fn config_approvals_reviewer_is_marked_experimental() {
|
||||
compact_prompt: None,
|
||||
model_reasoning_effort: None,
|
||||
model_reasoning_summary: None,
|
||||
reasoning_summary_delivery: None,
|
||||
model_verbosity: None,
|
||||
service_tier: None,
|
||||
analytics: None,
|
||||
@@ -3921,20 +3924,40 @@ fn dynamic_tool_response_serializes_text_and_image_content_items() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn thread_start_params_preserve_explicit_null_service_tier() {
|
||||
let params: ThreadStartParams =
|
||||
serde_json::from_value(json!({ "serviceTier": null })).expect("params should deserialize");
|
||||
fn thread_start_params_preserve_nullable_overrides() {
|
||||
let params: ThreadStartParams = serde_json::from_value(json!({
|
||||
"serviceTier": null,
|
||||
"reasoningSummaryDelivery": "concurrent_cutoff"
|
||||
}))
|
||||
.expect("params should deserialize");
|
||||
assert_eq!(params.service_tier, Some(None));
|
||||
assert_eq!(
|
||||
params.reasoning_summary_delivery,
|
||||
Some(Some(ReasoningSummaryDelivery::ConcurrentCutoff))
|
||||
);
|
||||
|
||||
let serialized = serde_json::to_value(¶ms).expect("params should serialize");
|
||||
assert_eq!(
|
||||
serialized.get("serviceTier"),
|
||||
Some(&serde_json::Value::Null)
|
||||
);
|
||||
assert_eq!(
|
||||
serialized.get("reasoningSummaryDelivery"),
|
||||
Some(&json!("concurrent_cutoff"))
|
||||
);
|
||||
|
||||
let params: ThreadStartParams =
|
||||
serde_json::from_value(json!({ "reasoningSummaryDelivery": null }))
|
||||
.expect("params should deserialize");
|
||||
assert_eq!(params.reasoning_summary_delivery, Some(None));
|
||||
|
||||
let serialized_without_override =
|
||||
serde_json::to_value(ThreadStartParams::default()).expect("params should serialize");
|
||||
assert_eq!(serialized_without_override.get("serviceTier"), None);
|
||||
assert_eq!(
|
||||
serialized_without_override.get("reasoningSummaryDelivery"),
|
||||
None
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -18,6 +18,7 @@ use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::MultiAgentMode;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
pub use codex_protocol::dynamic_tools::DynamicToolFunctionSpec;
|
||||
pub use codex_protocol::dynamic_tools::DynamicToolNamespaceSpec;
|
||||
pub use codex_protocol::dynamic_tools::DynamicToolNamespaceTool;
|
||||
@@ -90,6 +91,14 @@ pub struct ThreadStartParams {
|
||||
#[experimental("thread/start.permissions")]
|
||||
#[ts(optional = nullable)]
|
||||
pub permissions: Option<String>,
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option",
|
||||
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
#[ts(optional = nullable)]
|
||||
pub reasoning_summary_delivery: Option<Option<ReasoningSummaryDelivery>>,
|
||||
#[ts(optional = nullable)]
|
||||
pub config: Option<HashMap<String, JsonValue>>,
|
||||
#[ts(optional = nullable)]
|
||||
@@ -376,6 +385,14 @@ pub struct ThreadResumeParams {
|
||||
#[experimental("thread/resume.permissions")]
|
||||
#[ts(optional = nullable)]
|
||||
pub permissions: Option<String>,
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option",
|
||||
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
#[ts(optional = nullable)]
|
||||
pub reasoning_summary_delivery: Option<Option<ReasoningSummaryDelivery>>,
|
||||
#[ts(optional = nullable)]
|
||||
pub config: Option<HashMap<String, serde_json::Value>>,
|
||||
#[ts(optional = nullable)]
|
||||
@@ -544,6 +561,14 @@ pub struct ThreadForkParams {
|
||||
#[experimental("thread/fork.permissions")]
|
||||
#[ts(optional = nullable)]
|
||||
pub permissions: Option<String>,
|
||||
#[serde(
|
||||
default,
|
||||
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option",
|
||||
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
#[ts(optional = nullable)]
|
||||
pub reasoning_summary_delivery: Option<Option<ReasoningSummaryDelivery>>,
|
||||
#[ts(optional = nullable)]
|
||||
pub config: Option<HashMap<String, serde_json::Value>>,
|
||||
#[ts(optional = nullable)]
|
||||
|
||||
@@ -628,6 +628,25 @@ pub(super) async fn handle_pending_thread_resume_request(
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(reasoning_summary_delivery) = pending.reasoning_summary_delivery
|
||||
&& let Err(error) = conversation
|
||||
.set_reasoning_summary_delivery(reasoning_summary_delivery)
|
||||
.await
|
||||
{
|
||||
let _ = thread_state_manager
|
||||
.unsubscribe_connection_from_thread(conversation_id, connection_id)
|
||||
.await;
|
||||
outgoing
|
||||
.send_error(
|
||||
request_id,
|
||||
invalid_request(format!(
|
||||
"invalid reasoning_summary_delivery override: {error}"
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
|
||||
let config_snapshot = pending.config_snapshot;
|
||||
let cwd = config_snapshot.cwd().clone();
|
||||
let ThreadConfigSnapshot {
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::error_code::method_not_found;
|
||||
use codex_app_server_protocol::SelectedCapabilityRoot;
|
||||
use codex_extension_api::ExtensionDataInit;
|
||||
use codex_protocol::config_types::MultiAgentMode;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_DANGER_FULL_ACCESS;
|
||||
use codex_protocol::models::BUILT_IN_PERMISSION_PROFILE_WORKSPACE;
|
||||
use codex_protocol::protocol::ThreadHistoryMode;
|
||||
@@ -910,13 +911,14 @@ impl ThreadRequestProcessor {
|
||||
model_provider,
|
||||
allow_provider_model_fallback,
|
||||
service_tier,
|
||||
reasoning_summary_delivery,
|
||||
cwd,
|
||||
runtime_workspace_roots,
|
||||
approval_policy,
|
||||
approvals_reviewer,
|
||||
sandbox,
|
||||
permissions,
|
||||
config,
|
||||
config: request_overrides,
|
||||
service_name,
|
||||
base_instructions,
|
||||
developer_instructions,
|
||||
@@ -978,8 +980,9 @@ impl ThreadRequestProcessor {
|
||||
app_server_client_name,
|
||||
app_server_client_version,
|
||||
supports_openai_form_elicitation,
|
||||
config,
|
||||
request_overrides,
|
||||
typesafe_overrides,
|
||||
reasoning_summary_delivery,
|
||||
dynamic_tools,
|
||||
selected_capability_roots.unwrap_or_default(),
|
||||
history_mode.map(Into::into),
|
||||
@@ -1056,6 +1059,7 @@ impl ThreadRequestProcessor {
|
||||
supports_openai_form_elicitation: bool,
|
||||
config_overrides: Option<HashMap<String, serde_json::Value>>,
|
||||
typesafe_overrides: ConfigOverrides,
|
||||
reasoning_summary_delivery: Option<Option<ReasoningSummaryDelivery>>,
|
||||
dynamic_tools: Option<Vec<DynamicToolSpec>>,
|
||||
selected_capability_roots: Vec<SelectedCapabilityRoot>,
|
||||
history_mode: Option<ThreadHistoryMode>,
|
||||
@@ -1139,6 +1143,10 @@ impl ThreadRequestProcessor {
|
||||
.map_err(|err| config_load_error(&err))?;
|
||||
}
|
||||
|
||||
if let Some(reasoning_summary_delivery) = reasoning_summary_delivery {
|
||||
config.reasoning_summary_delivery = reasoning_summary_delivery;
|
||||
}
|
||||
|
||||
let environments = environments.unwrap_or_else(|| {
|
||||
listener_task_context
|
||||
.thread_manager
|
||||
@@ -2681,6 +2689,7 @@ impl ThreadRequestProcessor {
|
||||
model,
|
||||
model_provider,
|
||||
service_tier,
|
||||
reasoning_summary_delivery,
|
||||
cwd,
|
||||
runtime_workspace_roots,
|
||||
approval_policy,
|
||||
@@ -2741,7 +2750,7 @@ impl ThreadRequestProcessor {
|
||||
.await;
|
||||
|
||||
// Derive a Config using the same logic as new conversation, honoring overrides if provided.
|
||||
let config = match self
|
||||
let mut config = match self
|
||||
.config_manager
|
||||
.load_for_cwd(request_overrides, typesafe_overrides, history_cwd)
|
||||
.await
|
||||
@@ -2753,6 +2762,9 @@ impl ThreadRequestProcessor {
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
if let Some(reasoning_summary_delivery) = reasoning_summary_delivery {
|
||||
config.reasoning_summary_delivery = reasoning_summary_delivery;
|
||||
}
|
||||
|
||||
let response_history = thread_history.clone();
|
||||
|
||||
@@ -3122,6 +3134,7 @@ impl ThreadRequestProcessor {
|
||||
include_turns: !params.exclude_turns,
|
||||
initial_turns_page: params.initial_turns_page.clone(),
|
||||
redact_resume_payloads,
|
||||
reasoning_summary_delivery: params.reasoning_summary_delivery,
|
||||
}),
|
||||
);
|
||||
if listener_command_tx.send(command).is_err() {
|
||||
@@ -3392,6 +3405,7 @@ impl ThreadRequestProcessor {
|
||||
model,
|
||||
model_provider,
|
||||
service_tier,
|
||||
reasoning_summary_delivery,
|
||||
cwd,
|
||||
runtime_workspace_roots,
|
||||
approval_policy,
|
||||
@@ -3477,11 +3491,14 @@ impl ThreadRequestProcessor {
|
||||
);
|
||||
typesafe_overrides.ephemeral = ephemeral.then_some(true);
|
||||
// Derive a Config using the same logic as new conversation, honoring overrides if provided.
|
||||
let config = self
|
||||
let mut config = self
|
||||
.config_manager
|
||||
.load_for_cwd(request_overrides, typesafe_overrides, history_cwd)
|
||||
.await
|
||||
.map_err(|err| config_load_error(&err))?;
|
||||
if let Some(reasoning_summary_delivery) = reasoning_summary_delivery {
|
||||
config.reasoning_summary_delivery = reasoning_summary_delivery;
|
||||
}
|
||||
|
||||
let fallback_model_provider = config.model_provider_id.clone();
|
||||
|
||||
|
||||
@@ -747,6 +747,7 @@ mod thread_processor_behavior_tests {
|
||||
approvals_reviewer: None,
|
||||
sandbox: None,
|
||||
permissions: None,
|
||||
reasoning_summary_delivery: None,
|
||||
config: None,
|
||||
base_instructions: None,
|
||||
developer_instructions: None,
|
||||
|
||||
@@ -12,6 +12,7 @@ use codex_file_watcher::WatchRegistration;
|
||||
use codex_protocol::ThreadId;
|
||||
#[cfg(test)]
|
||||
use codex_protocol::config_types::MultiAgentMode;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_protocol::protocol::RolloutItem;
|
||||
use codex_rollout::state_db::StateDbHandle;
|
||||
@@ -41,6 +42,7 @@ pub(crate) struct PendingThreadResumeRequest {
|
||||
pub(crate) initial_turns_page:
|
||||
Option<codex_app_server_protocol::ThreadResumeInitialTurnsPageParams>,
|
||||
pub(crate) redact_resume_payloads: bool,
|
||||
pub(crate) reasoning_summary_delivery: Option<Option<ReasoningSummaryDelivery>>,
|
||||
}
|
||||
|
||||
// ThreadListenerCommand is used to perform operations in the context of the thread listener, for serialization purposes.
|
||||
|
||||
@@ -885,6 +885,7 @@ async fn skills_changed_notification_is_emitted_after_skill_change() -> Result<(
|
||||
approvals_reviewer: None,
|
||||
sandbox: None,
|
||||
permissions: None,
|
||||
reasoning_summary_delivery: None,
|
||||
config: None,
|
||||
service_name: None,
|
||||
base_instructions: None,
|
||||
|
||||
@@ -57,6 +57,7 @@ use codex_core::ARCHIVED_SESSIONS_SUBDIR;
|
||||
use codex_login::REFRESH_TOKEN_URL_OVERRIDE_ENV_VAR;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::mcp::CallToolResult;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
@@ -2896,10 +2897,24 @@ async fn thread_resume_rejoins_running_thread_even_with_override_mismatch() -> R
|
||||
responses::ev_completed("resp-2"),
|
||||
]))
|
||||
.set_delay(std::time::Duration::from_millis(500));
|
||||
let _response_mock =
|
||||
responses::mount_response_sequence(&server, vec![first_response, second_response]).await;
|
||||
let third_response = responses::sse_response(responses::sse(vec![
|
||||
responses::ev_response_created("resp-3"),
|
||||
responses::ev_assistant_message("msg-3", "Done"),
|
||||
responses::ev_completed("resp-3"),
|
||||
]));
|
||||
let response_mock = responses::mount_response_sequence(
|
||||
&server,
|
||||
vec![first_response, second_response, third_response],
|
||||
)
|
||||
.await;
|
||||
let codex_home = TempDir::new()?;
|
||||
create_config_toml(codex_home.path(), &server.uri())?;
|
||||
let config_path = codex_home.path().join("config.toml");
|
||||
let config = std::fs::read_to_string(&config_path)?;
|
||||
std::fs::write(
|
||||
config_path,
|
||||
config.replace("Mock provider for test", "OpenAI"),
|
||||
)?;
|
||||
|
||||
let mut primary = TestAppServer::new(codex_home.path()).await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, primary.initialize()).await??;
|
||||
@@ -2969,6 +2984,7 @@ async fn thread_resume_rejoins_running_thread_even_with_override_mismatch() -> R
|
||||
thread_id: thread.id.clone(),
|
||||
model: Some("not-the-running-model".to_string()),
|
||||
cwd: Some("/tmp".to_string()),
|
||||
reasoning_summary_delivery: Some(Some(ReasoningSummaryDelivery::ConcurrentCutoff)),
|
||||
initial_turns_page: Some(ThreadResumeInitialTurnsPageParams {
|
||||
limit: None,
|
||||
sort_direction: None,
|
||||
@@ -3014,6 +3030,37 @@ async fn thread_resume_rejoins_running_thread_even_with_override_mismatch() -> R
|
||||
)
|
||||
.await??;
|
||||
|
||||
let turn_id = primary
|
||||
.send_turn_start_request(TurnStartParams {
|
||||
thread_id: thread.id,
|
||||
client_user_message_id: None,
|
||||
input: vec![UserInput::Text {
|
||||
text: "use updated delivery".to_string(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
summary: Some(codex_protocol::config_types::ReasoningSummary::Auto),
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
timeout(
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
primary.read_stream_until_response_message(RequestId::Integer(turn_id)),
|
||||
)
|
||||
.await??;
|
||||
timeout(
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
primary.read_stream_until_notification_message("turn/completed"),
|
||||
)
|
||||
.await??;
|
||||
|
||||
let requests = response_mock.requests();
|
||||
assert_eq!(requests.len(), 3);
|
||||
assert!(requests[1].body_json().get("stream_options").is_none());
|
||||
assert_eq!(
|
||||
requests[2].body_json()["stream_options"],
|
||||
json!({ "reasoning_summary_delivery": "concurrent_cutoff" })
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::error::ApiError;
|
||||
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::config_types::Verbosity as VerbosityConfig;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
use codex_protocol::openai_models::ReasoningEffort as ReasoningEffortConfig;
|
||||
@@ -178,6 +179,11 @@ pub struct TextControls {
|
||||
pub format: Option<TextFormat>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Clone, PartialEq, Eq)]
|
||||
pub struct StreamOptions {
|
||||
pub reasoning_summary_delivery: ReasoningSummaryDelivery,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Default, Clone, PartialEq)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum OpenAiVerbosity {
|
||||
@@ -210,6 +216,8 @@ pub struct ResponsesApiRequest {
|
||||
pub reasoning: Option<Reasoning>,
|
||||
pub store: bool,
|
||||
pub stream: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stream_options: Option<StreamOptions>,
|
||||
pub include: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub service_tier: Option<String>,
|
||||
@@ -234,6 +242,7 @@ impl From<&ResponsesApiRequest> for ResponseCreateWsRequest {
|
||||
reasoning: request.reasoning.clone(),
|
||||
store: request.store,
|
||||
stream: request.stream,
|
||||
stream_options: request.stream_options.clone(),
|
||||
include: request.include.clone(),
|
||||
service_tier: request.service_tier.clone(),
|
||||
prompt_cache_key: request.prompt_cache_key.clone(),
|
||||
@@ -259,6 +268,8 @@ pub struct ResponseCreateWsRequest {
|
||||
pub reasoning: Option<Reasoning>,
|
||||
pub store: bool,
|
||||
pub stream: bool,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub stream_options: Option<StreamOptions>,
|
||||
pub include: Vec<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub service_tier: Option<String>,
|
||||
|
||||
@@ -851,6 +851,7 @@ mod tests {
|
||||
reasoning: None,
|
||||
store: false,
|
||||
stream: true,
|
||||
stream_options: None,
|
||||
include: vec!["reasoning.encrypted_content".to_string()],
|
||||
service_tier: Some("priority".to_string()),
|
||||
prompt_cache_key: Some("cache-key".to_string()),
|
||||
|
||||
@@ -39,6 +39,7 @@ pub use crate::common::ResponseEvent;
|
||||
pub use crate::common::ResponseStream;
|
||||
pub use crate::common::ResponsesApiRequest;
|
||||
pub use crate::common::ResponsesWsRequest;
|
||||
pub use crate::common::StreamOptions;
|
||||
pub use crate::common::TextControls;
|
||||
pub use crate::common::WS_REQUEST_HEADER_TRACEPARENT_CLIENT_METADATA_KEY;
|
||||
pub use crate::common::WS_REQUEST_HEADER_TRACESTATE_CLIENT_METADATA_KEY;
|
||||
|
||||
@@ -321,6 +321,7 @@ async fn responses_client_stream_request_preserves_item_ids() -> Result<()> {
|
||||
reasoning: None,
|
||||
store: false,
|
||||
stream: true,
|
||||
stream_options: None,
|
||||
include: Vec::new(),
|
||||
service_tier: None,
|
||||
prompt_cache_key: None,
|
||||
@@ -407,6 +408,7 @@ async fn streaming_client_retries_on_transport_error() -> Result<()> {
|
||||
reasoning: None,
|
||||
store: false,
|
||||
stream: true,
|
||||
stream_options: None,
|
||||
include: Vec::new(),
|
||||
service_tier: None,
|
||||
prompt_cache_key: None,
|
||||
@@ -526,6 +528,7 @@ async fn azure_store_sends_ids_and_headers() -> Result<()> {
|
||||
reasoning: None,
|
||||
store: true,
|
||||
stream: true,
|
||||
stream_options: None,
|
||||
include: Vec::new(),
|
||||
service_tier: None,
|
||||
prompt_cache_key: None,
|
||||
|
||||
@@ -39,6 +39,7 @@ use codex_protocol::config_types::AutoCompactTokenLimitScope;
|
||||
use codex_protocol::config_types::ForcedLoginMethod;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::config_types::TrustLevel;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
@@ -352,6 +353,7 @@ pub struct ConfigToml {
|
||||
pub model_reasoning_effort: Option<ReasoningEffort>,
|
||||
pub plan_mode_reasoning_effort: Option<ReasoningEffort>,
|
||||
pub model_reasoning_summary: Option<ReasoningSummary>,
|
||||
pub reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
/// Optional verbosity control for GPT-5 models (Responses API `text.verbosity`).
|
||||
pub model_verbosity: Option<Verbosity>,
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ use crate::types::SessionPickerViewMode;
|
||||
use crate::types::WindowsToml;
|
||||
use codex_features::FeaturesToml;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
@@ -35,6 +36,7 @@ pub struct ConfigProfile {
|
||||
pub model_reasoning_effort: Option<ReasoningEffort>,
|
||||
pub plan_mode_reasoning_effort: Option<ReasoningEffort>,
|
||||
pub model_reasoning_summary: Option<ReasoningSummary>,
|
||||
pub reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
pub model_verbosity: Option<Verbosity>,
|
||||
/// Optional path to a JSON model catalog (applied on startup only).
|
||||
pub model_catalog_json: Option<AbsolutePathBuf>,
|
||||
|
||||
@@ -774,6 +774,9 @@
|
||||
"plan_mode_reasoning_effort": {
|
||||
"$ref": "#/definitions/ReasoningEffort"
|
||||
},
|
||||
"reasoning_summary_delivery": {
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
"sandbox_mode": {
|
||||
"$ref": "#/definitions/SandboxMode"
|
||||
},
|
||||
@@ -2714,6 +2717,15 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"ReasoningSummaryDelivery": {
|
||||
"description": "Controls when reasoning summaries are delivered relative to later response items.",
|
||||
"enum": [
|
||||
"sequential",
|
||||
"concurrent",
|
||||
"concurrent_cutoff"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
"RolloutBudgetConfigToml": {
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
@@ -5415,6 +5427,9 @@
|
||||
"default": null,
|
||||
"description": "Experimental / do not use. Realtime websocket session selection. `version` controls v1/v2 and `type` controls conversational/transcription."
|
||||
},
|
||||
"reasoning_summary_delivery": {
|
||||
"$ref": "#/definitions/ReasoningSummaryDelivery"
|
||||
},
|
||||
"review_model": {
|
||||
"description": "Review model override used by the `/review` feature.",
|
||||
"type": "string"
|
||||
|
||||
@@ -56,6 +56,7 @@ use codex_api::ResponsesWebsocketConnection as ApiWebSocketConnection;
|
||||
use codex_api::ResponsesWsRequest;
|
||||
use codex_api::SharedAuthProvider;
|
||||
use codex_api::SseTelemetry;
|
||||
use codex_api::StreamOptions;
|
||||
use codex_api::TransportError;
|
||||
use codex_api::WebsocketTelemetry;
|
||||
use codex_api::auth_header_telemetry;
|
||||
@@ -73,6 +74,7 @@ use codex_protocol::auth::AuthMode;
|
||||
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::config_types::ReasoningSummary as ReasoningSummaryConfig;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::config_types::Verbosity as VerbosityConfig;
|
||||
use codex_protocol::models::ContentItem;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
@@ -311,6 +313,7 @@ fn responses_request_properties_match(
|
||||
reasoning: previous_reasoning,
|
||||
store: previous_store,
|
||||
stream: previous_stream,
|
||||
stream_options: previous_stream_options,
|
||||
include: previous_include,
|
||||
service_tier: previous_service_tier,
|
||||
prompt_cache_key: previous_prompt_cache_key,
|
||||
@@ -327,6 +330,7 @@ fn responses_request_properties_match(
|
||||
reasoning: current_reasoning,
|
||||
store: current_store,
|
||||
stream: current_stream,
|
||||
stream_options: current_stream_options,
|
||||
include: current_include,
|
||||
service_tier: current_service_tier,
|
||||
prompt_cache_key: current_prompt_cache_key,
|
||||
@@ -342,6 +346,7 @@ fn responses_request_properties_match(
|
||||
&& previous_reasoning == current_reasoning
|
||||
&& previous_store == current_store
|
||||
&& previous_stream == current_stream
|
||||
&& previous_stream_options == current_stream_options
|
||||
&& previous_include == current_include
|
||||
&& previous_service_tier == current_service_tier
|
||||
&& previous_prompt_cache_key == current_prompt_cache_key
|
||||
@@ -550,6 +555,7 @@ impl ModelClient {
|
||||
model_info,
|
||||
settings.effort,
|
||||
settings.summary,
|
||||
/*reasoning_summary_delivery*/ None,
|
||||
settings.service_tier,
|
||||
responses_metadata,
|
||||
)?;
|
||||
@@ -816,11 +822,13 @@ impl ModelClient {
|
||||
model_info: &ModelInfo,
|
||||
effort: Option<ReasoningEffortConfig>,
|
||||
summary: ReasoningSummaryConfig,
|
||||
reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
service_tier: Option<String>,
|
||||
responses_metadata: &CodexResponsesMetadata,
|
||||
) -> Result<ResponsesApiRequest> {
|
||||
let mut input = prompt.get_formatted_input_for_request(model_info.use_responses_lite);
|
||||
if !self.state.provider.info().is_openai() {
|
||||
let is_openai = self.state.provider.info().is_openai();
|
||||
if !is_openai {
|
||||
input
|
||||
.iter_mut()
|
||||
.for_each(ResponseItem::clear_internal_chat_message_metadata_passthrough);
|
||||
@@ -872,6 +880,12 @@ impl ModelClient {
|
||||
);
|
||||
let prompt_cache_key = Some(self.prompt_cache_key());
|
||||
let service_tier = model_info.service_tier_for_request(service_tier);
|
||||
let stream_options =
|
||||
reasoning_summary_delivery
|
||||
.filter(|_| is_openai)
|
||||
.map(|reasoning_summary_delivery| StreamOptions {
|
||||
reasoning_summary_delivery,
|
||||
});
|
||||
let request = ResponsesApiRequest {
|
||||
model: model_info.slug.clone(),
|
||||
instructions,
|
||||
@@ -882,6 +896,7 @@ impl ModelClient {
|
||||
reasoning,
|
||||
store: provider.is_azure_responses_endpoint(),
|
||||
stream: true,
|
||||
stream_options,
|
||||
include,
|
||||
service_tier,
|
||||
prompt_cache_key,
|
||||
@@ -1361,6 +1376,7 @@ impl ModelClientSession {
|
||||
session_telemetry: &SessionTelemetry,
|
||||
effort: Option<ReasoningEffortConfig>,
|
||||
summary: ReasoningSummaryConfig,
|
||||
reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
service_tier: Option<String>,
|
||||
responses_metadata: &CodexResponsesMetadata,
|
||||
inference_trace: &InferenceTraceContext,
|
||||
@@ -1400,6 +1416,7 @@ impl ModelClientSession {
|
||||
model_info,
|
||||
effort.clone(),
|
||||
summary,
|
||||
reasoning_summary_delivery,
|
||||
service_tier.clone(),
|
||||
responses_metadata,
|
||||
)?;
|
||||
@@ -1487,6 +1504,7 @@ impl ModelClientSession {
|
||||
session_telemetry: &SessionTelemetry,
|
||||
effort: Option<ReasoningEffortConfig>,
|
||||
summary: ReasoningSummaryConfig,
|
||||
reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
service_tier: Option<String>,
|
||||
responses_metadata: &CodexResponsesMetadata,
|
||||
warmup: bool,
|
||||
@@ -1513,6 +1531,7 @@ impl ModelClientSession {
|
||||
model_info,
|
||||
effort.clone(),
|
||||
summary,
|
||||
reasoning_summary_delivery,
|
||||
service_tier.clone(),
|
||||
responses_metadata,
|
||||
)?;
|
||||
@@ -1677,6 +1696,7 @@ impl ModelClientSession {
|
||||
session_telemetry: &SessionTelemetry,
|
||||
effort: Option<ReasoningEffortConfig>,
|
||||
summary: ReasoningSummaryConfig,
|
||||
reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
service_tier: Option<String>,
|
||||
responses_metadata: &CodexResponsesMetadata,
|
||||
) -> Result<()> {
|
||||
@@ -1695,6 +1715,7 @@ impl ModelClientSession {
|
||||
session_telemetry,
|
||||
effort,
|
||||
summary,
|
||||
reasoning_summary_delivery,
|
||||
service_tier,
|
||||
responses_metadata,
|
||||
/*warmup*/ true,
|
||||
@@ -1738,6 +1759,7 @@ impl ModelClientSession {
|
||||
session_telemetry: &SessionTelemetry,
|
||||
effort: Option<ReasoningEffortConfig>,
|
||||
summary: ReasoningSummaryConfig,
|
||||
reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
service_tier: Option<String>,
|
||||
responses_metadata: &CodexResponsesMetadata,
|
||||
inference_trace: &InferenceTraceContext,
|
||||
@@ -1754,6 +1776,7 @@ impl ModelClientSession {
|
||||
session_telemetry,
|
||||
effort.clone(),
|
||||
summary,
|
||||
reasoning_summary_delivery,
|
||||
service_tier.clone(),
|
||||
responses_metadata,
|
||||
/*warmup*/ false,
|
||||
@@ -1775,6 +1798,7 @@ impl ModelClientSession {
|
||||
session_telemetry,
|
||||
effort,
|
||||
summary,
|
||||
reasoning_summary_delivery,
|
||||
service_tier,
|
||||
responses_metadata,
|
||||
inference_trace,
|
||||
|
||||
@@ -116,6 +116,7 @@ fn serializes_text_verbosity_when_set() {
|
||||
reasoning: None,
|
||||
store: false,
|
||||
stream: true,
|
||||
stream_options: None,
|
||||
include: vec![],
|
||||
prompt_cache_key: None,
|
||||
service_tier: None,
|
||||
@@ -163,6 +164,7 @@ fn serializes_text_schema_with_strict_format() {
|
||||
reasoning: None,
|
||||
store: false,
|
||||
stream: true,
|
||||
stream_options: None,
|
||||
include: vec![],
|
||||
prompt_cache_key: None,
|
||||
service_tier: None,
|
||||
@@ -224,6 +226,7 @@ fn omits_text_when_not_set() {
|
||||
reasoning: None,
|
||||
store: false,
|
||||
stream: true,
|
||||
stream_options: None,
|
||||
include: vec![],
|
||||
prompt_cache_key: None,
|
||||
service_tier: None,
|
||||
@@ -247,6 +250,7 @@ fn serializes_flex_service_tier_when_set() {
|
||||
reasoning: None,
|
||||
store: false,
|
||||
stream: true,
|
||||
stream_options: None,
|
||||
include: vec![],
|
||||
prompt_cache_key: None,
|
||||
service_tier: Some(ServiceTier::Flex.to_string()),
|
||||
|
||||
@@ -10,6 +10,7 @@ use codex_protocol::config_types::ApprovalsReviewer;
|
||||
use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::config_types::WindowsSandboxLevel;
|
||||
use codex_protocol::error::CodexErr;
|
||||
use codex_protocol::error::Result as CodexResult;
|
||||
@@ -195,6 +196,19 @@ impl CodexThread {
|
||||
self.codex.submit(op).await
|
||||
}
|
||||
|
||||
pub async fn set_reasoning_summary_delivery(
|
||||
&self,
|
||||
reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
) -> ConstraintResult<()> {
|
||||
self.codex
|
||||
.session
|
||||
.update_settings(SessionSettingsUpdate {
|
||||
reasoning_summary_delivery: Some(reasoning_summary_delivery),
|
||||
..Default::default()
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
/// Returns the session telemetry handle for thread-scoped production instrumentation.
|
||||
pub fn session_telemetry(&self) -> SessionTelemetry {
|
||||
self.codex.session.services.session_telemetry.clone()
|
||||
|
||||
@@ -672,6 +672,7 @@ async fn drain_to_completed(
|
||||
&turn_context.session_telemetry,
|
||||
turn_context.reasoning_effort.clone(),
|
||||
turn_context.reasoning_summary,
|
||||
turn_context.config.reasoning_summary_delivery,
|
||||
turn_context.config.service_tier.clone(),
|
||||
responses_metadata,
|
||||
// Rollout tracing currently models remote compaction only; local compaction streams
|
||||
|
||||
@@ -365,6 +365,7 @@ async fn run_remote_compaction_request_v2(
|
||||
&turn_context.session_telemetry,
|
||||
turn_context.reasoning_effort.clone(),
|
||||
turn_context.reasoning_summary,
|
||||
turn_context.config.reasoning_summary_delivery,
|
||||
turn_context.config.service_tier.clone(),
|
||||
responses_metadata,
|
||||
&InferenceTraceContext::disabled(),
|
||||
|
||||
@@ -89,6 +89,7 @@ use codex_protocol::config_types::AutoCompactTokenLimitScope;
|
||||
use codex_protocol::config_types::ForcedLoginMethod;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::config_types::SERVICE_TIER_DEFAULT_REQUEST_VALUE;
|
||||
use codex_protocol::config_types::SandboxMode;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
@@ -947,6 +948,9 @@ pub struct Config {
|
||||
/// using the Responses API. When unset, the model catalog default is used.
|
||||
pub model_reasoning_summary: Option<ReasoningSummary>,
|
||||
|
||||
/// Optional delivery mode for reasoning summaries in streaming Responses API requests.
|
||||
pub reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
|
||||
/// Optional override to force-enable reasoning summaries for the configured model.
|
||||
pub model_supports_reasoning_summaries: Option<bool>,
|
||||
|
||||
@@ -3578,7 +3582,6 @@ impl Config {
|
||||
None => Some(service_tier),
|
||||
}
|
||||
});
|
||||
|
||||
let compact_prompt = compact_prompt.or(cfg.compact_prompt).and_then(|value| {
|
||||
let trimmed = value.trim();
|
||||
if trimmed.is_empty() {
|
||||
@@ -3888,6 +3891,7 @@ impl Config {
|
||||
model_reasoning_effort: cfg.model_reasoning_effort,
|
||||
plan_mode_reasoning_effort: cfg.plan_mode_reasoning_effort,
|
||||
model_reasoning_summary: cfg.model_reasoning_summary,
|
||||
reasoning_summary_delivery: cfg.reasoning_summary_delivery,
|
||||
model_supports_reasoning_summaries: cfg.model_supports_reasoning_summaries,
|
||||
model_catalog,
|
||||
model_verbosity: cfg.model_verbosity,
|
||||
|
||||
@@ -134,6 +134,7 @@ fn save_config_resolved_fields(
|
||||
lock_config.web_search = Some(config.web_search_mode.value());
|
||||
lock_config.model_provider = Some(config.model_provider_id.clone());
|
||||
lock_config.plan_mode_reasoning_effort = config.plan_mode_reasoning_effort.clone();
|
||||
lock_config.reasoning_summary_delivery = config.reasoning_summary_delivery;
|
||||
lock_config.model_verbosity = config.model_verbosity;
|
||||
lock_config.include_permissions_instructions = Some(config.include_permissions_instructions);
|
||||
lock_config.include_apps_instructions = Some(config.include_apps_instructions);
|
||||
@@ -238,6 +239,7 @@ where
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use pretty_assertions::assert_eq;
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -265,6 +267,7 @@ mod tests {
|
||||
.enable(Feature::RolloutBudget)
|
||||
.expect("rollout_budget should be enableable in tests");
|
||||
config.current_time_reminder = Some(crate::config::CurrentTimeReminderConfig::default());
|
||||
config.reasoning_summary_delivery = Some(ReasoningSummaryDelivery::ConcurrentCutoff);
|
||||
config
|
||||
.features
|
||||
.enable(Feature::CurrentTimeReminder)
|
||||
@@ -280,6 +283,10 @@ mod tests {
|
||||
assert_eq!(lock.instructions, Some(sc.base_instructions.clone()));
|
||||
assert_eq!(lock.developer_instructions, sc.developer_instructions);
|
||||
assert_eq!(lock.compact_prompt, sc.compact_prompt);
|
||||
assert_eq!(
|
||||
lock.reasoning_summary_delivery,
|
||||
Some(ReasoningSummaryDelivery::ConcurrentCutoff)
|
||||
);
|
||||
assert_eq!(lock.model, Some(sc.collaboration_mode.model().to_string()));
|
||||
assert_eq!(
|
||||
lock.model_reasoning_effort,
|
||||
|
||||
@@ -640,6 +640,7 @@ impl Codex {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
model_reasoning_summary: config.model_reasoning_summary,
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery,
|
||||
service_tier,
|
||||
developer_instructions: config.developer_instructions.clone(),
|
||||
personality: config.personality,
|
||||
|
||||
@@ -11,6 +11,7 @@ use codex_extension_api::ExtensionDataInit;
|
||||
use codex_login::auth::AgentIdentityAuthPolicy;
|
||||
use codex_protocol::SessionId;
|
||||
use codex_protocol::capabilities::SelectedCapabilityRoot;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::config_types::SERVICE_TIER_DEFAULT_REQUEST_VALUE;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
use codex_protocol::permissions::FileSystemPath;
|
||||
@@ -55,6 +56,7 @@ pub(crate) struct SessionConfiguration {
|
||||
|
||||
pub(super) collaboration_mode: CollaborationMode,
|
||||
pub(super) model_reasoning_summary: Option<ReasoningSummaryConfig>,
|
||||
pub(super) reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
pub(super) service_tier: Option<String>,
|
||||
|
||||
/// Developer instructions that supplement the base instructions.
|
||||
@@ -235,6 +237,9 @@ impl SessionConfiguration {
|
||||
if let Some(summary) = updates.reasoning_summary {
|
||||
next_configuration.model_reasoning_summary = Some(summary);
|
||||
}
|
||||
if let Some(reasoning_summary_delivery) = updates.reasoning_summary_delivery {
|
||||
next_configuration.reasoning_summary_delivery = reasoning_summary_delivery;
|
||||
}
|
||||
if let Some(service_tier) = updates.service_tier.clone() {
|
||||
// TODO(aibrahim): Remove once v2 clients no longer send the legacy
|
||||
// "fast" service tier value.
|
||||
@@ -427,6 +432,7 @@ pub(crate) struct SessionSettingsUpdate {
|
||||
pub(crate) windows_sandbox_level: Option<WindowsSandboxLevel>,
|
||||
pub(crate) collaboration_mode: Option<CollaborationMode>,
|
||||
pub(crate) reasoning_summary: Option<ReasoningSummaryConfig>,
|
||||
pub(crate) reasoning_summary_delivery: Option<Option<ReasoningSummaryDelivery>>,
|
||||
pub(crate) service_tier: Option<Option<String>>,
|
||||
pub(crate) final_output_json_schema: Option<Option<Value>>,
|
||||
pub(crate) personality: Option<Personality>,
|
||||
|
||||
@@ -3665,6 +3665,7 @@ async fn set_rate_limits_retains_previous_credits() {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
model_reasoning_summary: config.model_reasoning_summary,
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery,
|
||||
developer_instructions: config.developer_instructions.clone(),
|
||||
service_tier: None,
|
||||
personality: config.personality,
|
||||
@@ -3772,6 +3773,7 @@ async fn set_rate_limits_updates_plan_type_when_present() {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
model_reasoning_summary: config.model_reasoning_summary,
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery,
|
||||
developer_instructions: config.developer_instructions.clone(),
|
||||
service_tier: None,
|
||||
personality: config.personality,
|
||||
@@ -4305,6 +4307,7 @@ pub(crate) async fn make_session_configuration_for_tests() -> SessionConfigurati
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
model_reasoning_summary: config.model_reasoning_summary,
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery,
|
||||
developer_instructions: config.developer_instructions.clone(),
|
||||
service_tier: None,
|
||||
personality: config.personality,
|
||||
@@ -5176,6 +5179,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_packaged_zsh() {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
model_reasoning_summary: config.model_reasoning_summary,
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery,
|
||||
developer_instructions: config.developer_instructions.clone(),
|
||||
service_tier: None,
|
||||
personality: config.personality,
|
||||
@@ -5308,6 +5312,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
model_reasoning_summary: config.model_reasoning_summary,
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery,
|
||||
developer_instructions: config.developer_instructions.clone(),
|
||||
service_tier: None,
|
||||
personality: config.personality,
|
||||
@@ -5557,6 +5562,7 @@ async fn make_session_with_config_and_rx(
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
model_reasoning_summary: config.model_reasoning_summary,
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery,
|
||||
developer_instructions: config.developer_instructions.clone(),
|
||||
service_tier: None,
|
||||
personality: config.personality,
|
||||
@@ -5665,6 +5671,7 @@ async fn make_session_with_history_source_and_agent_control_and_rx(
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
model_reasoning_summary: config.model_reasoning_summary,
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery,
|
||||
developer_instructions: config.developer_instructions.clone(),
|
||||
service_tier: None,
|
||||
personality: config.personality,
|
||||
@@ -7435,6 +7442,7 @@ where
|
||||
provider: config.model_provider.clone(),
|
||||
collaboration_mode,
|
||||
model_reasoning_summary: config.model_reasoning_summary,
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery,
|
||||
developer_instructions: config.developer_instructions.clone(),
|
||||
service_tier: None,
|
||||
personality: config.personality,
|
||||
|
||||
@@ -1916,6 +1916,7 @@ async fn try_run_sampling_request(
|
||||
&turn_context.session_telemetry,
|
||||
turn_context.reasoning_effort.clone(),
|
||||
turn_context.reasoning_summary,
|
||||
turn_context.config.reasoning_summary_delivery,
|
||||
turn_context.config.service_tier.clone(),
|
||||
responses_metadata,
|
||||
&inference_trace,
|
||||
|
||||
@@ -426,6 +426,8 @@ impl Session {
|
||||
per_turn_config.model_reasoning_effort =
|
||||
session_configuration.collaboration_mode.reasoning_effort();
|
||||
per_turn_config.model_reasoning_summary = session_configuration.model_reasoning_summary;
|
||||
per_turn_config.reasoning_summary_delivery =
|
||||
session_configuration.reasoning_summary_delivery;
|
||||
per_turn_config.service_tier = session_configuration.service_tier.clone();
|
||||
per_turn_config.personality = session_configuration.personality;
|
||||
per_turn_config.approvals_reviewer = session_configuration.approvals_reviewer;
|
||||
|
||||
@@ -312,6 +312,7 @@ async fn schedule_startup_prewarm_inner(
|
||||
&startup_turn_context.session_telemetry,
|
||||
startup_turn_context.reasoning_effort.clone(),
|
||||
startup_turn_context.reasoning_summary,
|
||||
startup_turn_context.config.reasoning_summary_delivery,
|
||||
startup_turn_context.config.service_tier.clone(),
|
||||
&responses_metadata,
|
||||
)
|
||||
|
||||
@@ -154,6 +154,7 @@ async fn responses_stream_includes_subagent_header_on_review() {
|
||||
&session_telemetry,
|
||||
effort,
|
||||
summary.unwrap_or(model_info.default_reasoning_summary),
|
||||
config.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
@@ -288,6 +289,7 @@ async fn responses_stream_includes_subagent_header_on_other() {
|
||||
&session_telemetry,
|
||||
effort,
|
||||
summary.unwrap_or(model_info.default_reasoning_summary),
|
||||
config.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
@@ -408,6 +410,7 @@ async fn responses_respects_model_info_overrides_from_config() {
|
||||
&session_telemetry,
|
||||
effort,
|
||||
summary.unwrap_or(model_info.default_reasoning_summary),
|
||||
config.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
|
||||
@@ -25,6 +25,7 @@ use codex_protocol::config_types::CollaborationMode;
|
||||
use codex_protocol::config_types::ModeKind;
|
||||
use codex_protocol::config_types::ModelProviderAuthInfo;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::config_types::Settings;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
use codex_protocol::error::CodexErr;
|
||||
@@ -247,7 +248,7 @@ async fn openai_stateless_responses_requests_preserve_item_turn_metadata_across_
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn non_openai_responses_requests_omit_item_passthrough_metadata() {
|
||||
async fn non_openai_responses_requests_omit_openai_internal_fields() {
|
||||
let server = MockServer::start().await;
|
||||
let response_mock = mount_sse_once(
|
||||
&server,
|
||||
@@ -263,6 +264,7 @@ async fn non_openai_responses_requests_omit_item_passthrough_metadata() {
|
||||
.with_config(move |config| {
|
||||
config.model_provider_id = provider.name.clone();
|
||||
config.model_provider = provider;
|
||||
config.reasoning_summary_delivery = Some(ReasoningSummaryDelivery::ConcurrentCutoff);
|
||||
})
|
||||
.build(&server)
|
||||
.await
|
||||
@@ -285,6 +287,7 @@ async fn non_openai_responses_requests_omit_item_passthrough_metadata() {
|
||||
wait_for_event(&codex, |event| matches!(event, EventMsg::TurnComplete(_))).await;
|
||||
|
||||
let body = response_mock.single_request().body_json();
|
||||
assert!(body.get("stream_options").is_none());
|
||||
let input = body["input"]
|
||||
.as_array()
|
||||
.expect("request should include input items");
|
||||
@@ -1356,6 +1359,7 @@ async fn send_provider_auth_request(server: &MockServer, auth: ModelProviderAuth
|
||||
&session_telemetry,
|
||||
effort,
|
||||
summary.unwrap_or(ReasoningSummary::Auto),
|
||||
config.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
@@ -2416,6 +2420,46 @@ async fn configured_reasoning_summary_is_sent() -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn configured_reasoning_summary_delivery_is_sent() -> anyhow::Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
let server = MockServer::start().await;
|
||||
|
||||
let resp_mock = mount_sse_once(
|
||||
&server,
|
||||
sse(vec![ev_response_created("resp1"), ev_completed("resp1")]),
|
||||
)
|
||||
.await;
|
||||
let TestCodex { codex, .. } = test_codex()
|
||||
.with_config(|config| {
|
||||
config.reasoning_summary_delivery = Some(ReasoningSummaryDelivery::ConcurrentCutoff);
|
||||
})
|
||||
.build(&server)
|
||||
.await?;
|
||||
|
||||
codex
|
||||
.submit(Op::UserInput {
|
||||
items: vec![UserInput::Text {
|
||||
text: "hello".into(),
|
||||
text_elements: Vec::new(),
|
||||
}],
|
||||
final_output_json_schema: None,
|
||||
responsesapi_client_metadata: None,
|
||||
additional_context: Default::default(),
|
||||
thread_settings: Default::default(),
|
||||
})
|
||||
.await?;
|
||||
|
||||
wait_for_event(&codex, |ev| matches!(ev, EventMsg::TurnComplete(_))).await;
|
||||
|
||||
pretty_assertions::assert_eq!(
|
||||
resp_mock.single_request().body_json()["stream_options"],
|
||||
json!({ "reasoning_summary_delivery": "concurrent_cutoff" })
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn responses_lite_sets_all_turns_context_and_disables_parallel_tool_calls()
|
||||
-> anyhow::Result<()> {
|
||||
@@ -3034,6 +3078,7 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() {
|
||||
&session_telemetry,
|
||||
effort,
|
||||
summary.unwrap_or(ReasoningSummary::Auto),
|
||||
config.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
|
||||
@@ -21,6 +21,7 @@ use codex_protocol::SessionId;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::account::PlanType;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
use codex_protocol::models::BaseInstructions;
|
||||
use codex_protocol::models::ContentItem;
|
||||
@@ -107,6 +108,7 @@ struct WebsocketTestHarness {
|
||||
model_info: ModelInfo,
|
||||
effort: Option<ReasoningEffortConfig>,
|
||||
summary: ReasoningSummary,
|
||||
reasoning_summary_delivery: Option<ReasoningSummaryDelivery>,
|
||||
session_telemetry: SessionTelemetry,
|
||||
}
|
||||
|
||||
@@ -156,7 +158,14 @@ async fn responses_websocket_streams_request() {
|
||||
]]])
|
||||
.await;
|
||||
|
||||
let harness = websocket_harness(&server).await;
|
||||
let mut provider =
|
||||
ModelProviderInfo::create_openai_provider(Some(format!("{}/v1", server.uri())));
|
||||
provider.request_max_retries = Some(0);
|
||||
provider.stream_max_retries = Some(0);
|
||||
provider.stream_idle_timeout_ms = Some(5_000);
|
||||
let mut harness =
|
||||
websocket_harness_with_provider_options(provider, /*runtime_metrics_enabled*/ false).await;
|
||||
harness.reasoning_summary_delivery = Some(ReasoningSummaryDelivery::ConcurrentCutoff);
|
||||
let mut client_session = harness.client.new_session();
|
||||
let mut prompt = prompt_with_input(vec![message_item("hello")]);
|
||||
prompt.input[0].set_id(Some("msg_existing".to_string()));
|
||||
@@ -172,6 +181,10 @@ async fn responses_websocket_streams_request() {
|
||||
assert_eq!(body["stream"], serde_json::Value::Bool(true));
|
||||
assert_eq!(body["input"].as_array().map(Vec::len), Some(1));
|
||||
assert_eq!(body["input"][0].get("id"), None);
|
||||
assert_eq!(
|
||||
body["stream_options"],
|
||||
json!({ "reasoning_summary_delivery": "concurrent_cutoff" })
|
||||
);
|
||||
let handshake = server.single_handshake();
|
||||
assert_eq!(
|
||||
handshake.header(OPENAI_BETA_HEADER),
|
||||
@@ -397,6 +410,7 @@ async fn responses_websocket_request_prewarm_reuses_connection() {
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
)
|
||||
@@ -461,6 +475,7 @@ async fn responses_websocket_request_prewarm_uses_caller_supplied_metadata() {
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
)
|
||||
@@ -505,6 +520,7 @@ async fn responses_websocket_request_prewarm_traces_logical_request() {
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&prewarm_responses_metadata,
|
||||
)
|
||||
@@ -551,6 +567,7 @@ async fn responses_websocket_request_prewarm_traces_logical_request() {
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&inference_trace,
|
||||
@@ -724,6 +741,7 @@ async fn responses_websocket_preconnect_is_reused_even_with_header_changes() {
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
@@ -764,6 +782,7 @@ async fn responses_websocket_request_prewarm_is_reused_even_with_header_changes(
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&prewarm_responses_metadata,
|
||||
)
|
||||
@@ -777,6 +796,7 @@ async fn responses_websocket_request_prewarm_is_reused_even_with_header_changes(
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
@@ -832,6 +852,7 @@ async fn responses_websocket_prewarm_uses_v2_when_provider_supports_websockets()
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
)
|
||||
@@ -1238,6 +1259,7 @@ async fn responses_websocket_emits_reasoning_included_event() {
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
@@ -1313,6 +1335,7 @@ async fn responses_websocket_emits_rate_limit_events() {
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
@@ -1969,6 +1992,7 @@ async fn responses_websocket_v2_after_error_uses_full_create_without_previous_re
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
@@ -2058,6 +2082,7 @@ async fn responses_websocket_v2_surfaces_terminal_error_without_close_handshake(
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
@@ -2262,6 +2287,7 @@ async fn websocket_harness_with_provider_options(
|
||||
model_info,
|
||||
effort,
|
||||
summary,
|
||||
reasoning_summary_delivery: None,
|
||||
session_telemetry,
|
||||
}
|
||||
}
|
||||
@@ -2295,6 +2321,7 @@ async fn stream_until_complete_with_model_info(
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
/*service_tier*/ None,
|
||||
&responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
@@ -2344,6 +2371,7 @@ async fn stream_until_complete_with_metadata(
|
||||
&harness.session_telemetry,
|
||||
harness.effort.clone(),
|
||||
harness.summary,
|
||||
harness.reasoning_summary_delivery,
|
||||
service_tier.map(|service_tier| service_tier.request_value().to_string()),
|
||||
responses_metadata,
|
||||
&codex_rollout_trace::InferenceTraceContext::disabled(),
|
||||
|
||||
@@ -283,6 +283,7 @@ impl MemoryStartupContext {
|
||||
&context.session_telemetry,
|
||||
context.reasoning_effort.clone(),
|
||||
context.reasoning_summary,
|
||||
config.reasoning_summary_delivery,
|
||||
context.service_tier.clone(),
|
||||
&responses_metadata,
|
||||
&InferenceTraceContext::disabled(),
|
||||
|
||||
@@ -53,6 +53,16 @@ pub enum ReasoningSummary {
|
||||
None,
|
||||
}
|
||||
|
||||
/// Controls when reasoning summaries are delivered relative to later response items.
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Display, JsonSchema, TS)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
#[strum(serialize_all = "snake_case")]
|
||||
pub enum ReasoningSummaryDelivery {
|
||||
Sequential,
|
||||
Concurrent,
|
||||
ConcurrentCutoff,
|
||||
}
|
||||
|
||||
/// Controls output length/detail on GPT-5 models via the Responses API.
|
||||
/// Serialized with lowercase values to match the OpenAI API.
|
||||
#[derive(
|
||||
|
||||
@@ -255,6 +255,7 @@ fn new_config(model: Option<String>, arg0_paths: Arg0DispatchPaths) -> anyhow::R
|
||||
model_reasoning_effort: None,
|
||||
plan_mode_reasoning_effort: None,
|
||||
model_reasoning_summary: None,
|
||||
reasoning_summary_delivery: None,
|
||||
model_supports_reasoning_summaries: None,
|
||||
model_catalog: None,
|
||||
model_verbosity: None,
|
||||
|
||||
@@ -1415,6 +1415,7 @@ fn thread_start_params_from_config(
|
||||
model: config.model.clone(),
|
||||
model_provider: thread_params_mode.model_provider_from_config(config),
|
||||
service_tier: service_tier_override_from_config(config),
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery.map(Some),
|
||||
cwd: thread_cwd_from_config(config, thread_params_mode, remote_cwd_override),
|
||||
runtime_workspace_roots: Some(config.workspace_roots.clone()),
|
||||
approval_policy: Some(config.permissions.approval_policy.value().into()),
|
||||
@@ -1453,6 +1454,7 @@ fn thread_resume_params_from_config(
|
||||
model: config.model.clone(),
|
||||
model_provider: thread_params_mode.model_provider_from_config(&config),
|
||||
service_tier: service_tier_override_from_config(&config),
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery.map(Some),
|
||||
cwd: thread_cwd_from_config(&config, thread_params_mode, remote_cwd_override),
|
||||
runtime_workspace_roots: Some(config.workspace_roots.clone()),
|
||||
approval_policy: Some(config.permissions.approval_policy.value().into()),
|
||||
@@ -1488,6 +1490,7 @@ fn thread_fork_params_from_config(
|
||||
model: config.model.clone(),
|
||||
model_provider: thread_params_mode.model_provider_from_config(&config),
|
||||
service_tier: service_tier_override_from_config(&config),
|
||||
reasoning_summary_delivery: config.reasoning_summary_delivery.map(Some),
|
||||
cwd: thread_cwd_from_config(&config, thread_params_mode, remote_cwd_override),
|
||||
runtime_workspace_roots: Some(config.workspace_roots.clone()),
|
||||
approval_policy: Some(config.permissions.approval_policy.value().into()),
|
||||
@@ -1773,6 +1776,7 @@ mod tests {
|
||||
use codex_features::Feature;
|
||||
use codex_protocol::config_types::Personality;
|
||||
use codex_protocol::config_types::ReasoningSummary;
|
||||
use codex_protocol::config_types::ReasoningSummaryDelivery;
|
||||
use codex_protocol::config_types::ServiceTier;
|
||||
use codex_protocol::config_types::Verbosity;
|
||||
use codex_protocol::config_types::WebSearchMode;
|
||||
@@ -2178,6 +2182,7 @@ mod tests {
|
||||
.expect("test web search mode should be allowed");
|
||||
config.bypass_hook_trust = true;
|
||||
config.service_tier = Some(ServiceTier::Fast.request_value().to_string());
|
||||
config.reasoning_summary_delivery = Some(ReasoningSummaryDelivery::ConcurrentCutoff);
|
||||
let thread_id = ThreadId::new();
|
||||
|
||||
let start = thread_start_params_from_config(
|
||||
@@ -2203,6 +2208,10 @@ mod tests {
|
||||
assert_eq!(start.service_tier, expected_service_tier);
|
||||
assert_eq!(resume.service_tier, expected_service_tier);
|
||||
assert_eq!(fork.service_tier, expected_service_tier);
|
||||
let expected_delivery = Some(Some(ReasoningSummaryDelivery::ConcurrentCutoff));
|
||||
assert_eq!(start.reasoning_summary_delivery, expected_delivery);
|
||||
assert_eq!(resume.reasoning_summary_delivery, expected_delivery);
|
||||
assert_eq!(fork.reasoning_summary_delivery, expected_delivery);
|
||||
let string = |value: &str| serde_json::Value::String(value.to_string());
|
||||
let expected_config = HashMap::from([
|
||||
("model_reasoning_effort".to_string(), string("high")),
|
||||
|
||||
Reference in New Issue
Block a user