Skip to content

Commit d32d1cb

Browse files
Preserve only params field in Codex extension payload and update tests
Signed-off-by: Volker Christian <me@vchrist.at>
1 parent b087c19 commit d32d1cb

2 files changed

Lines changed: 32 additions & 37 deletions

File tree

src/ai/openai/codex/backend/Reducer.cpp

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ namespace ai::openai::codex::backend {
5050
const ::ai::openai::codex::detail::ProtocolSurfaceEntry& registryEntry = ::ai::openai::codex::detail::entryFor(target);
5151
const std::optional<typed::DecodeDiagnostic> diagnostic =
5252
value.diagnostics.empty() ? std::nullopt : std::optional<typed::DecodeDiagnostic>{value.diagnostics.front()};
53-
return {detail::preserveUnmodeledTypedEvent({std::string(registryEntry.key.name), value.raw, std::nullopt, diagnostic})};
53+
return {detail::preserveUnmodeledTypedEvent(
54+
{std::string(registryEntry.key.name), value.raw.at("params"), std::nullopt, diagnostic})};
5455
}
5556

5657
logger::BoundaryLogger lifecycleLog() {
@@ -330,10 +331,9 @@ namespace ai::openai::codex::backend {
330331
std::uint64_t originalDiagnosticBytes = 0;
331332
const auto account = [&originalDiagnosticBytes](std::size_t bytes) {
332333
const std::uint64_t value = saturatingUint64(bytes);
333-
originalDiagnosticBytes =
334-
value > std::numeric_limits<std::uint64_t>::max() - originalDiagnosticBytes
335-
? std::numeric_limits<std::uint64_t>::max()
336-
: originalDiagnosticBytes + value;
334+
originalDiagnosticBytes = value > std::numeric_limits<std::uint64_t>::max() - originalDiagnosticBytes
335+
? std::numeric_limits<std::uint64_t>::max()
336+
: originalDiagnosticBytes + value;
337337
};
338338
account(extension.diagnostic->surface.size());
339339
account(extension.diagnostic->fieldPath.size());
@@ -430,8 +430,7 @@ namespace ai::openai::codex::backend {
430430
}
431431
state.threadList.hasLoadedPage = true;
432432
++state.threadList.pagesLoaded;
433-
state.threadList.nextCursor =
434-
value.page.nextCursor.hasValue() ? value.page.nextCursor.value : std::nullopt;
433+
state.threadList.nextCursor = value.page.nextCursor.hasValue() ? value.page.nextCursor.value : std::nullopt;
435434
state.threadList.backwardsCursor =
436435
value.page.backwardsCursor.hasValue() ? value.page.backwardsCursor.value : std::nullopt;
437436
state.threadList.complete = !value.page.nextCursor.hasValue();
@@ -496,17 +495,16 @@ namespace ai::openai::codex::backend {
496495
}
497496
retainExtension(state,
498497
{.method = "codex/item-without-id",
499-
.payload =
500-
std::visit(
501-
[](const auto& item) {
502-
using Item = std::decay_t<decltype(item)>;
503-
if constexpr (std::is_same_v<Item, typed::UnknownItem>) {
504-
return item.raw;
505-
} else {
506-
return item.metadata.raw;
507-
}
508-
},
509-
value.item),
498+
.payload = std::visit(
499+
[](const auto& item) {
500+
using Item = std::decay_t<decltype(item)>;
501+
if constexpr (std::is_same_v<Item, typed::UnknownItem>) {
502+
return item.raw;
503+
} else {
504+
return item.metadata.raw;
505+
}
506+
},
507+
value.item),
510508
.decodingError = "typed item has no stable id",
511509
.originalMethodBytes = std::nullopt,
512510
.originalPayloadBytes = std::nullopt,
@@ -674,16 +672,14 @@ namespace ai::openai::codex::backend {
674672
[](const typed::ItemStarted& value) -> std::vector<BackendEvent> {
675673
const auto location = itemLocation(value.item);
676674
if (!location) {
677-
return {CodexExtensionReceived{
678-
"item/started", value.raw, "item event omitted threadId or turnId", std::nullopt}};
675+
return {CodexExtensionReceived{"item/started", value.raw, "item event omitted threadId or turnId", std::nullopt}};
679676
}
680677
return {ItemUpserted{location->first, location->second, value.item, ItemLifecycle::Started, value.startedAtMs}};
681678
},
682679
[](const typed::ItemCompleted& value) -> std::vector<BackendEvent> {
683680
const auto location = itemLocation(value.item);
684681
if (!location) {
685-
return {CodexExtensionReceived{
686-
"item/completed", value.raw, "item event omitted threadId or turnId", std::nullopt}};
682+
return {CodexExtensionReceived{"item/completed", value.raw, "item event omitted threadId or turnId", std::nullopt}};
687683
}
688684
return {ItemUpserted{location->first, location->second, value.item, ItemLifecycle::Completed, value.completedAtMs}};
689685
},
@@ -793,8 +789,7 @@ namespace ai::openai::codex::backend {
793789
return {TurnErrorUpdated{value.threadId, value.turnId, value.error, value.willRetry}};
794790
},
795791
[](const typed::UnknownEvent& value) -> std::vector<BackendEvent> {
796-
return {detail::preserveUnmodeledTypedEvent(
797-
{value.method, value.params, value.decodingError, value.diagnostic})};
792+
return {detail::preserveUnmodeledTypedEvent({value.method, value.params, value.decodingError, value.diagnostic})};
798793
}},
799794
event);
800795
}

tests/component/codex/CodexA11NotificationBackendPreservationTest.cpp

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,11 @@ namespace {
7171
const std::vector<backend::BackendEvent> translated = reducer.translate(notificationEvent<Notification>(raw));
7272
const auto* extension = translated.size() == 1 ? std::get_if<backend::CodexExtensionReceived>(&translated.front()) : nullptr;
7373

74-
const bool translatedExactly = extension && extension->method == method && extension->payload == raw && !extension->decodingError &&
75-
extension->diagnostic && extension->diagnostic->kind == typed::DecodeIssueKind::UnknownEnumValue &&
76-
extension->diagnostic->severity == typed::DecodeIssueSeverity::ForwardCompatibility &&
77-
extension->diagnostic->surface == method &&
78-
extension->diagnostic->fieldPath == "$.params.futureMode";
74+
const bool translatedExactly =
75+
extension && extension->method == method && extension->payload == raw.at("params") && !extension->decodingError &&
76+
extension->diagnostic && extension->diagnostic->kind == typed::DecodeIssueKind::UnknownEnumValue &&
77+
extension->diagnostic->severity == typed::DecodeIssueSeverity::ForwardCompatibility &&
78+
extension->diagnostic->surface == method && extension->diagnostic->fieldPath == "$.params.futureMode";
7979
result.expectTrue(translatedExactly, method + " translates to one exact structured Codex extension without a legacy decode error");
8080

8181
backend::Reduction reduction;
@@ -84,7 +84,7 @@ namespace {
8484
}
8585
const backend::ExtensionRecord* retained = state.recentExtensions.size() == 1 ? &state.recentExtensions.front() : nullptr;
8686
const bool retainedExactly = extension && reduction.changed && !reduction.flushImmediately && retained &&
87-
retained->method == method && retained->payload == raw && !retained->decodingError &&
87+
retained->method == method && retained->payload == raw.at("params") && !retained->decodingError &&
8888
!retained->originalMethodBytes && !retained->originalPayloadBytes &&
8989
!retained->originalDecodingErrorBytes && retained->diagnostic &&
9090
retained->diagnostic->kind == typed::DecodeIssueKind::UnknownEnumValue &&
@@ -168,14 +168,14 @@ namespace {
168168

169169
result.expectTrue(state.recentExtensions.size() == 7 &&
170170
state.recentExtensions.front().method == "thread/realtime/transcript/delta" &&
171-
state.recentExtensions.front().payload.at("params").at("sequence") == 93 &&
171+
state.recentExtensions.front().payload.at("sequence") == 93 &&
172172
state.recentExtensions.back().method == "thread/realtime/transcript/delta" &&
173-
state.recentExtensions.back().payload.at("params").at("sequence") == 99,
173+
state.recentExtensions.back().payload.at("sequence") == 99,
174174
"high-volume realtime audio and transcript notifications retain only the configured deterministic newest suffix");
175175

176176
const std::string largeAudio(4096, 'a');
177177
const Json audioRaw = envelopeFor("thread/realtime/outputAudio/delta", 100, Json{{"audio", {{"data", largeAudio}}}});
178-
const std::uint64_t audioBytes = static_cast<std::uint64_t>(audioRaw.dump().size());
178+
const std::uint64_t audioBytes = static_cast<std::uint64_t>(audioRaw.at("params").dump().size());
179179
applyTranslated(reducer, state, notificationEvent<typed::ThreadRealtimeOutputAudioDeltaNotification>(audioRaw, false));
180180
const backend::ExtensionRecord& boundedAudio = state.recentExtensions.back();
181181
result.expectTrue(boundedAudio.method == "thread/realtime/outputAudio/delta" && boundedAudio.originalPayloadBytes == audioBytes &&
@@ -185,7 +185,7 @@ namespace {
185185

186186
const std::string largeTranscript(4096, 't');
187187
const Json transcriptRaw = envelopeFor("thread/realtime/transcript/delta", 101, Json{{"delta", largeTranscript}});
188-
const std::uint64_t transcriptBytes = static_cast<std::uint64_t>(transcriptRaw.dump().size());
188+
const std::uint64_t transcriptBytes = static_cast<std::uint64_t>(transcriptRaw.at("params").dump().size());
189189
applyTranslated(reducer, state, notificationEvent<typed::ThreadRealtimeTranscriptDeltaNotification>(transcriptRaw, false));
190190
const backend::ExtensionRecord& boundedTranscript = state.recentExtensions.back();
191191
result.expectTrue(boundedTranscript.method == "thread/realtime/transcript/delta" &&
@@ -211,10 +211,10 @@ namespace {
211211
snapshot.recentExtensions.size() == 1 ? &snapshot.recentExtensions.front() : nullptr;
212212
const std::string encodedSafe = frontendSafe ? frontendSafe->payload.dump() : std::string{};
213213

214-
result.expectTrue(canonical && canonical->payload == raw && canonical->payload.dump().find(accessToken) != std::string::npos,
215-
"canonical bounded preservation retains the complete typed realtime envelope before frontend redaction");
214+
result.expectTrue(canonical && canonical->payload == raw.at("params") && canonical->payload.dump().find(accessToken) != std::string::npos,
215+
"canonical bounded preservation retains the complete typed realtime params before frontend redaction");
216216
result.expectTrue(frontendSafe && frontendSafe->method == "thread/realtime/transcript/done" &&
217-
frontendSafe->sensitiveFieldsRedacted && frontendSafe->payload.at("params").at("safe") == "visible" &&
217+
frontendSafe->sensitiveFieldsRedacted && frontendSafe->payload.at("safe") == "visible" &&
218218
encodedSafe.find(accessToken) == std::string::npos && encodedSafe.find(secretAnswer) == std::string::npos,
219219
"new typed notifications reuse the unchanged frontend-safe recursive redaction boundary");
220220
}

0 commit comments

Comments
 (0)