mirror of
https://github.com/openai/codex.git
synced 2026-09-15 12:08:01 +00:00
Add a validation
This commit is contained in:
@@ -112,6 +112,8 @@ impl ModelClient {
|
||||
/// For Chat providers, the underlying stream is optionally aggregated
|
||||
/// based on the `show_raw_agent_reasoning` flag in the config.
|
||||
pub async fn stream(&self, prompt: &PromptBuilder) -> Result<ResponseStream> {
|
||||
self.validate_prompt_wire_api(prompt)?;
|
||||
|
||||
match self.provider.wire_api {
|
||||
WireApi::Responses => self.stream_responses_api(prompt).await,
|
||||
WireApi::Chat => {
|
||||
@@ -132,6 +134,17 @@ impl ModelClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_prompt_wire_api(&self, prompt: &PromptBuilder) -> Result<()> {
|
||||
if prompt.wire_api != self.provider.wire_api {
|
||||
return Err(CodexErr::UnsupportedOperation(format!(
|
||||
"prompt configured for {:?} wire API but provider {} expects {:?}",
|
||||
prompt.wire_api, self.provider.name, self.provider.wire_api
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Streams a turn via the OpenAI Chat Completions API.
|
||||
///
|
||||
/// This path is only used when the provider is configured with
|
||||
|
||||
@@ -5,6 +5,8 @@ use crate::model_family::ModelFamily;
|
||||
use crate::model_provider_info::WireApi;
|
||||
use crate::tools::spec::create_tools_json_for_chat_completions_api;
|
||||
use crate::tools::spec::create_tools_json_for_responses_api;
|
||||
use codex_api::CompactionInput;
|
||||
use codex_api::Prompt;
|
||||
pub use codex_api::common::ResponseEvent;
|
||||
use codex_apply_patch::APPLY_PATCH_TOOL_INSTRUCTIONS;
|
||||
use codex_protocol::models::ResponseItem;
|
||||
@@ -18,7 +20,6 @@ use std::pin::Pin;
|
||||
use std::task::Context;
|
||||
use std::task::Poll;
|
||||
use tokio::sync::mpsc;
|
||||
use codex_api::{CompactionInput, Prompt};
|
||||
|
||||
/// Review thread system prompt. Edit `core/src/review_prompt.md` to customize.
|
||||
pub const REVIEW_PROMPT: &str = include_str!("../review_prompt.md");
|
||||
|
||||
@@ -10,6 +10,7 @@ use codex_core::ModelProviderInfo;
|
||||
use codex_core::PromptBuilder;
|
||||
use codex_core::ResponseItem;
|
||||
use codex_core::WireApi;
|
||||
use codex_core::error::CodexErr;
|
||||
use codex_otel::otel_event_manager::OtelEventManager;
|
||||
use codex_protocol::ConversationId;
|
||||
use codex_protocol::models::ReasoningItemContent;
|
||||
@@ -41,21 +42,7 @@ async fn run_request(input: Vec<ResponseItem>) -> Value {
|
||||
.mount(&server)
|
||||
.await;
|
||||
|
||||
let provider = ModelProviderInfo {
|
||||
name: "mock".into(),
|
||||
base_url: Some(format!("{}/v1", server.uri())),
|
||||
env_key: None,
|
||||
env_key_instructions: None,
|
||||
experimental_bearer_token: None,
|
||||
wire_api: WireApi::Chat,
|
||||
query_params: None,
|
||||
http_headers: None,
|
||||
env_http_headers: None,
|
||||
request_max_retries: Some(0),
|
||||
stream_max_retries: Some(0),
|
||||
stream_idle_timeout_ms: Some(5_000),
|
||||
requires_openai_auth: false,
|
||||
};
|
||||
let provider = chat_provider(format!("{}/v1", server.uri()));
|
||||
|
||||
let codex_home = match TempDir::new() {
|
||||
Ok(dir) => dir,
|
||||
@@ -184,6 +171,24 @@ fn first_assistant(messages: &[Value]) -> &Value {
|
||||
}
|
||||
}
|
||||
|
||||
fn chat_provider(base_url: String) -> ModelProviderInfo {
|
||||
ModelProviderInfo {
|
||||
name: "mock".into(),
|
||||
base_url: Some(base_url),
|
||||
env_key: None,
|
||||
env_key_instructions: None,
|
||||
experimental_bearer_token: None,
|
||||
wire_api: WireApi::Chat,
|
||||
query_params: None,
|
||||
http_headers: None,
|
||||
env_http_headers: None,
|
||||
request_max_retries: Some(0),
|
||||
stream_max_retries: Some(0),
|
||||
stream_idle_timeout_ms: Some(5_000),
|
||||
requires_openai_auth: false,
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn omits_reasoning_when_none_present() {
|
||||
skip_if_no_network!();
|
||||
@@ -316,3 +321,52 @@ async fn suppresses_duplicate_assistant_messages() {
|
||||
Value::String("dup".into())
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn errors_on_mismatched_prompt_wire_api() {
|
||||
let provider = chat_provider("https://example.com/v1".into());
|
||||
|
||||
let codex_home = match TempDir::new() {
|
||||
Ok(dir) => dir,
|
||||
Err(e) => panic!("failed to create TempDir: {e}"),
|
||||
};
|
||||
let mut config = load_default_config_for_test(&codex_home);
|
||||
config.model_provider_id = provider.name.clone();
|
||||
config.model_provider = provider.clone();
|
||||
config.show_raw_agent_reasoning = true;
|
||||
let effort = config.model_reasoning_effort;
|
||||
let summary = config.model_reasoning_summary;
|
||||
let config = Arc::new(config);
|
||||
|
||||
let conversation_id = ConversationId::new();
|
||||
|
||||
let otel_event_manager = OtelEventManager::new(
|
||||
conversation_id,
|
||||
config.model.as_str(),
|
||||
config.model_family.slug.as_str(),
|
||||
None,
|
||||
Some("test@test.com".to_string()),
|
||||
Some(AuthMode::ChatGPT),
|
||||
false,
|
||||
"test".to_string(),
|
||||
);
|
||||
|
||||
let client = ModelClient::new(
|
||||
Arc::clone(&config),
|
||||
None,
|
||||
otel_event_manager,
|
||||
provider,
|
||||
effort,
|
||||
summary,
|
||||
conversation_id,
|
||||
codex_protocol::protocol::SessionSource::Exec,
|
||||
);
|
||||
|
||||
let prompt = PromptBuilder::new().with_input(vec![user_message("u1")]);
|
||||
|
||||
let err = match client.stream(&prompt).await {
|
||||
Ok(_) => panic!("wire API mismatch should error before sending request"),
|
||||
Err(e) => e,
|
||||
};
|
||||
assert!(matches!(err, CodexErr::UnsupportedOperation(_)));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user