Forward thread originators from standalone extensions (#33677)

## Why

Standalone web search and image requests need to preserve the trusted,
thread-scoped originator used for billing attribution.

## What changed

- Seed extension data with the resolved thread originator and recognize
  `codex_work_cca` and `chatgpt_cca` service names as originators.
- Forward non-default originators from the web search and image generation
  extensions with the shared originator-header helper.

## Testing

- Cover originator resolution, header validation, and forwarding for web
  search, image generation, and image editing.

GitOrigin-RevId: 165dace8e4d24e4bd92f234bff5ec77a21a7ff6e
This commit is contained in:
charleschen-oai authored and copyberry committed 2026-07-16 19:31:26 +00:00
1 parent 71448a29e7
commit 78ba047bda
16 files changed
+248 -44

No files matched your search

+17 -4
View File
@@ -3,6 +3,7 @@ use codex_api::ImageGenerationRequest;
use codex_api::ImageResponse;
use codex_api::ImagesClient;
use codex_api::ReqwestTransport;
use codex_login::default_client::add_originator_header;
use codex_login::default_client::build_reqwest_client;
use codex_model_provider::SharedModelProvider;
use http::HeaderMap;
@@ -10,12 +11,16 @@ use http::HeaderMap;
#[derive(Clone)]
pub(crate) struct CodexImagesBackend {
provider: SharedModelProvider,
originator: Option<String>,
}
impl CodexImagesBackend {
/// Creates a backend that sends image requests through the active model provider.
pub(crate) fn new(provider: SharedModelProvider) -> Self {
Self { provider }
pub(crate) fn new(provider: SharedModelProvider, originator: Option<String>) -> Self {
Self {
provider,
originator,
}
}
/// Resolves the provider and auth required for the current image API request.
@@ -44,7 +49,7 @@ impl CodexImagesBackend {
) -> Result<ImageResponse, String> {
self.client()
.await?
.generate(&request, HeaderMap::new())
.generate(&request, image_request_headers(self.originator.as_deref()))
.await
.map_err(|err| err.to_string())
}
@@ -53,8 +58,16 @@ impl CodexImagesBackend {
pub(crate) async fn edit(&self, request: ImageEditRequest) -> Result<ImageResponse, String> {
self.client()
.await?
.edit(&request, HeaderMap::new())
.edit(&request, image_request_headers(self.originator.as_deref()))
.await
.map_err(|err| err.to_string())
}
}
fn image_request_headers(originator: Option<&str>) -> HeaderMap {
let mut headers = HeaderMap::new();
if let Some(originator) = originator {
add_originator_header(&mut headers, originator);
}
headers
}