Replay serverside file and web search#46
Conversation
Greptile SummaryThis PR adds server-side replay for
Confidence Score: 4/5Safe to merge after addressing the FileSearchResultItem.text backward-compat deserialization failure for stored results. The FileSearchResultItem.text field has no #[serde(default)], so any stored FileSearchCallOutput persisted with include=["file_search_call.results"] (using the old content array format) will fail to deserialize after this deploy. The failure surfaces at the multi-turn boundary — exactly when the replay machinery this PR introduces is invoked — and produces a hard error rather than degraded output. Everything else in the PR is well-guarded: WebSearchCallOutput.action has #[serde(default)], replay_content on both types has #[serde(default)], and the shared driver correctly avoids allocation on the no-match path. src/api_types/responses.rs — FileSearchResultItem.text needs #[serde(default)] for backward compatibility with stored results items. Important Files Changed
Sequence DiagramsequenceDiagram
participant Client
participant Hadrian
participant Provider
participant SearchBackend
Note over Client,SearchBackend: Turn 1 — search executes
Client->>Hadrian: POST /responses (web_search tool declared)
Hadrian->>Provider: function tool "web_search" (rewritten)
Provider-->>Hadrian: "function_call { query }"
Hadrian->>SearchBackend: execute_search(query)
SearchBackend-->>Hadrian: results
Hadrian-->>Client: SSE: web_search_call item (action + replay_content stored)
Hadrian->>Hadrian: persist response with replay_content
Note over Client,SearchBackend: Turn 2 — history replay
Client->>Hadrian: POST /responses (previous_response_id or manual history)
Note right of Hadrian: input contains WebSearchCall item
Hadrian->>Hadrian: "rewrite_web_search_history()<br/>WebSearchCall → function_call + function_call_output<br/>(replay_content as output)"
Hadrian->>Provider: history with function pairs (provider-compatible)
Provider-->>Hadrian: next response
Hadrian-->>Client: SSE response
Prompt To Fix All With AIFix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
src/api_types/responses.rs:767-769
The schema migration from `content: Vec<FileSearchResultContent>` to `text: String` is not backward-compatible for stored `FileSearchCallOutput` items that previously had `include=["file_search_call.results"]`. Any such item in the DB has `"content": [{"type":"text","text":"..."}]` and no `"text"` key. Because `text` carries no `#[serde(default)]`, serde will fail with "missing field `text`" when deserializing those items, causing the entire `FileSearchCallOutput` — and therefore the multi-turn input payload — to fail. The struct doc-comment also explicitly says "each field is optional in the spec", so the spec allows omitting `text`, yet the Rust type treats it as required. Adding `#[serde(default)]` yields an empty string for old items rather than a panic, matching the same degradation strategy used for `replay_content`.
```suggestion
/// The text retrieved from the file. OpenAI's Responses API uses a flat
/// string here (unlike the Assistants API's typed `content` array).
#[serde(default)]
pub text: String,
```
Reviews (5): Last reviewed commit: "Review fixes" | Re-trigger Greptile |
| /// The text retrieved from the file. OpenAI's Responses API uses a flat | ||
| /// string here (unlike the Assistants API's typed `content` array). | ||
| pub text: String, |
There was a problem hiding this comment.
The schema migration from
content: Vec<FileSearchResultContent> to text: String is not backward-compatible for stored FileSearchCallOutput items that previously had include=["file_search_call.results"]. Any such item in the DB has "content": [{"type":"text","text":"..."}] and no "text" key. Because text carries no #[serde(default)], serde will fail with "missing field text" when deserializing those items, causing the entire FileSearchCallOutput — and therefore the multi-turn input payload — to fail. The struct doc-comment also explicitly says "each field is optional in the spec", so the spec allows omitting text, yet the Rust type treats it as required. Adding #[serde(default)] yields an empty string for old items rather than a panic, matching the same degradation strategy used for replay_content.
| /// The text retrieved from the file. OpenAI's Responses API uses a flat | |
| /// string here (unlike the Assistants API's typed `content` array). | |
| pub text: String, | |
| /// The text retrieved from the file. OpenAI's Responses API uses a flat | |
| /// string here (unlike the Assistants API's typed `content` array). | |
| #[serde(default)] | |
| pub text: String, |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/api_types/responses.rs
Line: 767-769
Comment:
The schema migration from `content: Vec<FileSearchResultContent>` to `text: String` is not backward-compatible for stored `FileSearchCallOutput` items that previously had `include=["file_search_call.results"]`. Any such item in the DB has `"content": [{"type":"text","text":"..."}]` and no `"text"` key. Because `text` carries no `#[serde(default)]`, serde will fail with "missing field `text`" when deserializing those items, causing the entire `FileSearchCallOutput` — and therefore the multi-turn input payload — to fail. The struct doc-comment also explicitly says "each field is optional in the spec", so the spec allows omitting `text`, yet the Rust type treats it as required. Adding `#[serde(default)]` yields an empty string for old items rather than a panic, matching the same degradation strategy used for `replay_content`.
```suggestion
/// The text retrieved from the file. OpenAI's Responses API uses a flat
/// string here (unlike the Assistants API's typed `content` array).
#[serde(default)]
pub text: String,
```
How can I resolve this? If you propose a fix, please make it concise.
No description provided.