fix: preserve responses reasoning history
This commit is contained in:
parent
b870a77a55
commit
8ed5939ebb
|
|
@ -62,8 +62,8 @@ pub(crate) struct ResponsesRequest {
|
||||||
pub(crate) struct ReasoningConfig {
|
pub(crate) struct ReasoningConfig {
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub effort: Option<String>,
|
pub effort: Option<String>,
|
||||||
/// Reasoning encrypted_content は同一 user turn 内だけ再利用する。
|
/// API 側の reasoning retention policy。Insomnia はこの値を送るが、
|
||||||
/// 古い turn の reasoning item は request input から除外する。
|
/// persisted reasoning item の client-side filtering はしない。
|
||||||
pub context: &'static str,
|
pub context: &'static str,
|
||||||
/// summary の出力制御。`"auto"` 固定で summary_text を受け取る。
|
/// summary の出力制御。`"auto"` 固定で summary_text を受け取る。
|
||||||
pub summary: &'static str,
|
pub summary: &'static str,
|
||||||
|
|
@ -240,9 +240,8 @@ impl OpenAIResponsesScheme {
|
||||||
|
|
||||||
/// `Item` 列を `input[]` に変換する。
|
/// `Item` 列を `input[]` に変換する。
|
||||||
fn convert_items_to_input(items: &[Item]) -> Vec<InputItem> {
|
fn convert_items_to_input(items: &[Item]) -> Vec<InputItem> {
|
||||||
let current_turn_start = current_turn_start_index(items);
|
|
||||||
let mut out = Vec::with_capacity(items.len());
|
let mut out = Vec::with_capacity(items.len());
|
||||||
for (idx, item) in items.iter().enumerate() {
|
for item in items {
|
||||||
match item {
|
match item {
|
||||||
Item::Message { role, content, .. } => {
|
Item::Message { role, content, .. } => {
|
||||||
let (role_str, text_variant): (&'static str, fn(String) -> InputContent) =
|
let (role_str, text_variant): (&'static str, fn(String) -> InputContent) =
|
||||||
|
|
@ -299,9 +298,6 @@ fn convert_items_to_input(items: &[Item]) -> Vec<InputItem> {
|
||||||
encrypted_content,
|
encrypted_content,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if idx < current_turn_start {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let summary_parts = summary
|
let summary_parts = summary
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
|
|
@ -324,26 +320,6 @@ fn convert_items_to_input(items: &[Item]) -> Vec<InputItem> {
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Responses の `reasoning.context = "current_turn"` に合わせ、直近の
|
|
||||||
/// user message 以降だけを current turn とみなす。ToolResult は Responses
|
|
||||||
/// wire 上では user 側 item だが、新しい人間/外部入力ではなく function-call
|
|
||||||
/// chain の継続なので turn reset には使わない。System/developer notes も
|
|
||||||
/// 同一 turn 内の補助入力になり得るため reset しない。
|
|
||||||
fn current_turn_start_index(items: &[Item]) -> usize {
|
|
||||||
items
|
|
||||||
.iter()
|
|
||||||
.rposition(|item| {
|
|
||||||
matches!(
|
|
||||||
item,
|
|
||||||
Item::Message {
|
|
||||||
role: Role::User,
|
|
||||||
..
|
|
||||||
}
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn convert_tool(tool: &ToolDefinition) -> ResponseTool {
|
fn convert_tool(tool: &ToolDefinition) -> ResponseTool {
|
||||||
ResponseTool {
|
ResponseTool {
|
||||||
r#type: "function",
|
r#type: "function",
|
||||||
|
|
@ -506,7 +482,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn old_turn_reasoning_items_are_omitted_for_current_turn_context() {
|
fn persisted_reasoning_items_are_preserved_across_user_turns() {
|
||||||
let scheme = OpenAIResponsesScheme::new();
|
let scheme = OpenAIResponsesScheme::new();
|
||||||
let old_reasoning = Item::reasoning("old").with_encrypted_content("OLD_ENC");
|
let old_reasoning = Item::reasoning("old").with_encrypted_content("OLD_ENC");
|
||||||
let current_reasoning = Item::reasoning("current").with_encrypted_content("CURRENT_ENC");
|
let current_reasoning = Item::reasoning("current").with_encrypted_content("CURRENT_ENC");
|
||||||
|
|
@ -527,7 +503,7 @@ mod tests {
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
assert_eq!(encrypted, vec!["CURRENT_ENC"]);
|
assert_eq!(encrypted, vec!["OLD_ENC", "CURRENT_ENC"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
|
|
@ -85,9 +85,9 @@ reasoning トークンは各ターンの後に破棄される。次ターンに
|
||||||
1. `previous_response_id` パラメータで過去のレスポンスを参照
|
1. `previous_response_id` パラメータで過去のレスポンスを参照
|
||||||
2. `response.output` の全アイテムを次の `input` に手動で渡す
|
2. `response.output` の全アイテムを次の `input` に手動で渡す
|
||||||
|
|
||||||
ステートレス利用(`store=false`、ZDR組織)の場合は `include=["reasoning.encrypted_content"]` を指定すれば暗号化された推論コンテンツを受け取り、次リクエストに渡すことで推論を引き継げる。ただし Insomnia では Responses リクエストに `reasoning.context="current_turn"` を明示し、直近の user message 以降の同一ターン内 reasoning item だけを `input` に残す。過去ターンの persisted `encrypted_content` は、履歴に残っていても次ターンへ盲目的には再送しない。
|
ステートレス利用(`store=false`、ZDR組織)の場合は `include=["reasoning.encrypted_content"]` を指定すれば暗号化された推論コンテンツを受け取り、次リクエストに渡すことで推論を引き継げる。Insomnia は Responses リクエストに `reasoning.context="current_turn"` を明示するが、このパラメータの正確な履歴境界 semantics は provider 側の責務として扱い、履歴から復元した reasoning item を client 側でターン境界に基づいて削除しない。
|
||||||
|
|
||||||
同一ターン内の function-call loop では、`reasoning item → function_call → function_call_output → 次の Responses request` の連続性を保つため、直近 user message 以降の reasoning item は保持する。ToolResult は wire 上で user 側 item に見えるが、新しい user turn ではなく function-call chain の継続なので reasoning reset の境界にはしない。
|
同一ターン内の function-call loop でも、`reasoning item → function_call → function_call_output → 次の Responses request` の連続性を保つため、履歴上の reasoning item は通常の API message として保持する。ToolResult は wire 上で user 側 item に見えるが、reasoning item の削除境界としては扱わない。
|
||||||
|
|
||||||
#### モデル世代差
|
#### モデル世代差
|
||||||
|
|
||||||
|
|
@ -187,7 +187,7 @@ Ollamaはローカル実行プラットフォームで、モデルごとに思
|
||||||
|
|
||||||
**ChatGPT を使うとき**
|
**ChatGPT を使うとき**
|
||||||
- 新規実装は **Responses API** を選ぶ(Chat Completions は推論引き継ぎが弱い)
|
- 新規実装は **Responses API** を選ぶ(Chat Completions は推論引き継ぎが弱い)
|
||||||
- ZDR組織でも `reasoning.encrypted_content` で推論を引き継げるが、Insomnia では `reasoning.context="current_turn"` に合わせて同一 user turn / function-call loop 内だけ再送する
|
- ZDR組織でも `reasoning.encrypted_content` で推論を引き継げる。Insomnia は `reasoning.context="current_turn"` を送るが、履歴上の reasoning item は通常の API message として扱い、独自の turn-boundary filtering はしない
|
||||||
- raw reasoning の抽出を試みない(規約違反の可能性)
|
- raw reasoning の抽出を試みない(規約違反の可能性)
|
||||||
|
|
||||||
**Ollama を使うとき**
|
**Ollama を使うとき**
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user