templatureがcodexエンドポイントで使えない件の修正
This commit is contained in:
@@ -16,12 +16,13 @@ pub use scheme_impl::OpenAIResponsesState;
|
||||
|
||||
/// OpenAI Responses scheme 本体。
|
||||
///
|
||||
/// `store` / `include_encrypted_content` / `send_max_output_tokens` は
|
||||
/// scheme 固定の wire 設定で、デフォルトは公式 OpenAI Responses API
|
||||
/// 向け (stateless + ZDR + `max_output_tokens` 送出可)。ChatGPT backend
|
||||
/// (codex-oauth) のように受理パラメータが subset の経路では provider 層で
|
||||
/// `send_max_output_tokens=false` 等に上書きする。`ModelCapability` には
|
||||
/// 入れない(モデル能力ではなく wire policy)。
|
||||
/// `store` / `include_encrypted_content` / `send_max_output_tokens` /
|
||||
/// `send_sampling_params` は scheme 固定の wire 設定で、デフォルトは
|
||||
/// 公式 OpenAI Responses API 向け (stateless + ZDR + `max_output_tokens`
|
||||
/// / `temperature` / `top_p` 送出可)。ChatGPT backend (codex-oauth) の
|
||||
/// ように受理パラメータが subset の経路では provider 層で
|
||||
/// `send_max_output_tokens=false` / `send_sampling_params=false` に
|
||||
/// 上書きする。`ModelCapability` には入れない(モデル能力ではなく wire policy)。
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct OpenAIResponsesScheme {
|
||||
/// サーバ側に response を保存するか。ZDR/stateless 運用では `false`。
|
||||
@@ -33,6 +34,10 @@ pub struct OpenAIResponsesScheme {
|
||||
/// 受理するが、ChatGPT backend (codex-oauth) は `Unsupported parameter`
|
||||
/// で 400 を返すため、その経路では `false` にする。
|
||||
pub send_max_output_tokens: bool,
|
||||
/// `temperature` / `top_p` を body に載せるか。公式 OpenAI Responses API
|
||||
/// は受理するが、ChatGPT backend (codex-oauth) は `Unsupported parameter`
|
||||
/// で 400 を返すため、その経路では `false` にする。
|
||||
pub send_sampling_params: bool,
|
||||
}
|
||||
|
||||
impl Default for OpenAIResponsesScheme {
|
||||
@@ -41,13 +46,14 @@ impl Default for OpenAIResponsesScheme {
|
||||
store: false,
|
||||
include_encrypted_content: true,
|
||||
send_max_output_tokens: true,
|
||||
send_sampling_params: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenAIResponsesScheme {
|
||||
/// デフォルト設定 (`store=false`, `include=["reasoning.encrypted_content"]`,
|
||||
/// `send_max_output_tokens=true`)。
|
||||
/// `send_max_output_tokens=true`, `send_sampling_params=true`)。
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
@@ -69,4 +75,10 @@ impl OpenAIResponsesScheme {
|
||||
self.send_max_output_tokens = send;
|
||||
self
|
||||
}
|
||||
|
||||
/// `temperature` / `top_p` を body に載せるかを上書き。
|
||||
pub fn with_send_sampling_params(mut self, send: bool) -> Self {
|
||||
self.send_sampling_params = send;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ pub(crate) struct ResponsesRequest {
|
||||
/// が `false` のときは `None` のまま送る (skip_serializing_if で除外)。
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub max_output_tokens: Option<u32>,
|
||||
/// 公式 OpenAI Responses API では受理されるが、ChatGPT backend
|
||||
/// (codex-oauth) は `temperature` / `top_p` を 400 で弾く。scheme の
|
||||
/// `send_sampling_params` が `false` のときは `None` のまま送る。
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub temperature: Option<f32>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -203,8 +206,16 @@ impl OpenAIResponsesScheme {
|
||||
} else {
|
||||
None
|
||||
},
|
||||
temperature: request.config.temperature,
|
||||
top_p: request.config.top_p,
|
||||
temperature: if self.send_sampling_params {
|
||||
request.config.temperature
|
||||
} else {
|
||||
None
|
||||
},
|
||||
top_p: if self.send_sampling_params {
|
||||
request.config.top_p
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -471,6 +482,29 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sampling_params_passed_through_by_default() {
|
||||
let scheme = OpenAIResponsesScheme::new();
|
||||
let req = Request::new().user("hi").temperature(0.4).top_p(0.9);
|
||||
let body = scheme.build_request("gpt-5", &req, &cap_with_reasoning());
|
||||
assert_eq!(body.temperature, Some(0.4));
|
||||
assert_eq!(body.top_p, Some(0.9));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sampling_params_dropped_when_send_disabled() {
|
||||
let scheme = OpenAIResponsesScheme::new().with_send_sampling_params(false);
|
||||
let req = Request::new().user("hi").temperature(0.4).top_p(0.9);
|
||||
let body = scheme.build_request("gpt-5", &req, &cap_with_reasoning());
|
||||
assert_eq!(body.temperature, None);
|
||||
assert_eq!(body.top_p, None);
|
||||
let json = serde_json::to_value(&body).unwrap();
|
||||
assert!(
|
||||
json.get("temperature").is_none() && json.get("top_p").is_none(),
|
||||
"temperature/top_p keys must not appear in serialised body, got: {json}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tool_schema_without_properties_is_normalized() {
|
||||
// schemars は引数なし struct から `type:"object"` だけのスキーマを
|
||||
|
||||
@@ -64,6 +64,21 @@ impl Scheme for OpenAIResponsesScheme {
|
||||
"OpenAI Responses (ChatGPT backend)",
|
||||
));
|
||||
}
|
||||
// 同上、`temperature` / `top_p` も ChatGPT backend では 400 で弾かれる。
|
||||
if !self.send_sampling_params {
|
||||
if config.temperature.is_some() {
|
||||
warnings.push(ConfigWarning::unsupported(
|
||||
"temperature",
|
||||
"OpenAI Responses (ChatGPT backend)",
|
||||
));
|
||||
}
|
||||
if config.top_p.is_some() {
|
||||
warnings.push(ConfigWarning::unsupported(
|
||||
"top_p",
|
||||
"OpenAI Responses (ChatGPT backend)",
|
||||
));
|
||||
}
|
||||
}
|
||||
warnings
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1030,9 +1030,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
.prompts
|
||||
.compact_system()
|
||||
.map_err(PodError::PromptCatalog)?;
|
||||
let mut summary_worker = Worker::new(summary_client)
|
||||
.system_prompt(summary_system_prompt)
|
||||
.temperature(0.0);
|
||||
let mut summary_worker = Worker::new(summary_client).system_prompt(summary_system_prompt);
|
||||
|
||||
// Cumulative input-token meter + interceptor. The meter is bumped
|
||||
// from the on_usage callback and read on every pre_llm_request.
|
||||
@@ -1407,9 +1405,7 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
||||
.unwrap_or(manifest::defaults::MEMORY_EXTRACT_WORKER_MAX_INPUT_TOKENS);
|
||||
|
||||
let client = self.build_extractor_client(memory_cfg)?;
|
||||
let mut extract_worker = Worker::new(client)
|
||||
.system_prompt(extract::EXTRACT_SYSTEM_PROMPT)
|
||||
.temperature(0.0);
|
||||
let mut extract_worker = Worker::new(client).system_prompt(extract::EXTRACT_SYSTEM_PROMPT);
|
||||
|
||||
// Cumulative input-token meter + interceptor (mirror of
|
||||
// CompactWorkerInterceptor). Aborts the extract worker if its
|
||||
|
||||
@@ -142,11 +142,13 @@ fn build_from_config(config: &ModelConfig) -> Result<Box<dyn LlmClient>, Provide
|
||||
SchemeKind::OpenaiChat => build_transport(OpenAIScheme::new(), config, resolved),
|
||||
SchemeKind::Gemini => build_transport(GeminiScheme::new(), config, resolved),
|
||||
SchemeKind::OpenaiResponses => {
|
||||
// ChatGPT backend (codex-oauth) は `max_output_tokens` を
|
||||
// 400 で弾くため、その経路では送出を止める。
|
||||
let scheme = OpenAIResponsesScheme::new().with_send_max_output_tokens(
|
||||
!matches!(config.auth, AuthRef::CodexOAuth),
|
||||
);
|
||||
// ChatGPT backend (codex-oauth) は `max_output_tokens` /
|
||||
// `temperature` / `top_p` を 400 で弾くため、その経路では
|
||||
// 送出を止める。
|
||||
let send_to_official = !matches!(config.auth, AuthRef::CodexOAuth);
|
||||
let scheme = OpenAIResponsesScheme::new()
|
||||
.with_send_max_output_tokens(send_to_official)
|
||||
.with_send_sampling_params(send_to_official);
|
||||
build_transport(scheme, config, resolved)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user