Send base instructions as Responses input messages (#51156)

## What changed

Move base instructions from the top-level `instructions` field into a prefixed `developer` message in `input` for standard Responses requests, matching Responses Lite. Remove the field from HTTP and WebSocket request types and assign instruction messages stable IDs derived from the thread and instruction text.

## Testing

Extend standard and Responses Lite tests to cover instruction message serialization, stable IDs, and resume consistency. Update WebSocket tests to verify that changed instructions trigger a full request without `previous_response_id`.

GitOrigin-RevId: e2de4b8e30a9339377fac133ceda981abd1a112b
This commit is contained in:
pakrym-oai
2026-10-05 18:52:51 +00:00
committed by copyberry
parent 3f1ccb7ceb
commit c9253c4977
81 changed files with 1330 additions and 943 deletions
-5
View File
@@ -283,8 +283,6 @@ pub struct ResponsesApiRequest {
pub stream: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub service_tier: Option<String>,
#[serde(skip_serializing_if = "String::is_empty")]
pub instructions: String,
pub input: Vec<ResponseItem>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tools: Option<ResponsesApiTools>,
@@ -309,7 +307,6 @@ impl<'a> From<&'a ResponsesApiRequest> for ResponseCreateWsRequest<'a> {
fn from(request: &'a ResponsesApiRequest) -> Self {
Self {
model: &request.model,
instructions: &request.instructions,
previous_response_id: None,
input: &request.input,
tools: request.tools.as_ref().map(ResponsesApiTools::as_raw_value),
@@ -338,8 +335,6 @@ pub struct ResponseCreateWsRequest<'a> {
pub stream: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub service_tier: Option<&'a str>,
#[serde(skip_serializing_if = "str::is_empty")]
pub instructions: &'a str,
#[serde(skip_serializing_if = "Option::is_none")]
pub previous_response_id: Option<String>,
pub input: &'a [ResponseItem],
@@ -945,7 +945,6 @@ mod tests {
fn direct_serialization_preserves_websocket_request_payload() {
let api_request = ResponsesApiRequest {
model: "gpt-test".to_string(),
instructions: "Use the available tools.".to_string(),
input: vec![ResponseItem::Message {
id: Some(ResponseItemId::with_suffix("msg", "1")),
role: "user".to_string(),
@@ -998,7 +997,7 @@ mod tests {
let request_text =
serialize_websocket_request(&request).expect("serialize websocket request");
assert!(request_text.starts_with(
r#"{"type":"response.create","model":"gpt-test","stream":true,"service_tier":"priority","instructions":"Use the available tools.","previous_response_id":"resp-1","input":"#
r#"{"type":"response.create","model":"gpt-test","stream":true,"service_tier":"priority","previous_response_id":"resp-1","input":"#
));
let wire_payload =
serde_json::from_str::<Value>(&request_text).expect("parse websocket request");