mirror of
https://github.com/openai/codex.git
synced 2026-11-10 08:43:33 +00:00
[codex] allow CCA image generation and web search extensions (#29909)
## Summary - allow the standalone image-generation and web-search extensions for the actor-authorized provider shape used by CCA - preserve builtin `image_generation` and `web_search` for older models and existing flows - keep ordinary non-OpenAI providers excluded from both extensions - remove only the image extension local managed-AuthManager requirement that CCA cannot satisfy - share actor-authorization detection through `ModelProviderInfo` - keep Core tests focused on routing behavior and cover header-shape edge cases in `model-provider-info` - add a Responses Lite regression that verifies both `image_gen.imagegen` and `web.run` ## Why CCA uses a provider named `local` with `requires_openai_auth: false` and a non-empty `x-openai-actor-authorization` header. Core accepts that provider shape, but both extension provider-name gates rejected it; image generation additionally required a Codex-managed login. The standalone paths must coexist with existing builtin tools. New Responses Lite models can receive `image_gen.imagegen` and `web.run`, while older models continue using builtin tools. ## Impact This enables both standalone extensions for CCA once installed downstream, without removing or changing builtin-tool compatibility for older models. ## Validation - `just test -p codex-core responses_lite_exposes_standalone_tools_for_actor_authorized_provider` - `just test -p codex-core responses_lite_uses_standalone_web_search_and_image_generation` - `just test -p codex-core hosted_tools_follow_provider_auth_model_and_config_gates` - `just test -p codex-image-generation-extension` - `just test -p codex-web-search-extension` - `just test -p codex-model-provider-info` - `just fmt` - `git diff --check`
This commit is contained in:
1 parent
ec300bc7bd
commit
0d4351c1b8
7 files changed
+125
-97
No files matched your search
@@ -98,7 +98,6 @@ use tracing::warn;
|
|||||||
const MULTI_AGENT_V2_NAMESPACE_DESCRIPTION: &str = "Tools for spawning and managing sub-agents.";
|
const MULTI_AGENT_V2_NAMESPACE_DESCRIPTION: &str = "Tools for spawning and managing sub-agents.";
|
||||||
const IMAGE_GEN_NAMESPACE: &str = "image_gen";
|
const IMAGE_GEN_NAMESPACE: &str = "image_gen";
|
||||||
const IMAGEGEN_TOOL_NAME: &str = "imagegen";
|
const IMAGEGEN_TOOL_NAME: &str = "imagegen";
|
||||||
const ACTOR_AUTHORIZATION_HEADER: &str = "x-openai-actor-authorization";
|
|
||||||
|
|
||||||
type PlannedRuntime = Arc<dyn CoreToolRuntime>;
|
type PlannedRuntime = Arc<dyn CoreToolRuntime>;
|
||||||
|
|
||||||
@@ -387,7 +386,10 @@ fn image_generation_tool_enabled(turn_context: &TurnContext) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn image_generation_runtime_enabled(turn_context: &TurnContext) -> bool {
|
fn image_generation_runtime_enabled(turn_context: &TurnContext) -> bool {
|
||||||
(provider_uses_actor_authorization(turn_context)
|
(turn_context
|
||||||
|
.provider
|
||||||
|
.info()
|
||||||
|
.uses_openai_actor_authorization()
|
||||||
|| (turn_context.provider.info().requires_openai_auth
|
|| (turn_context.provider.info().requires_openai_auth
|
||||||
&& turn_context
|
&& turn_context
|
||||||
.auth_manager
|
.auth_manager
|
||||||
@@ -400,16 +402,6 @@ fn image_generation_runtime_enabled(turn_context: &TurnContext) -> bool {
|
|||||||
.contains(&InputModality::Image)
|
.contains(&InputModality::Image)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn provider_uses_actor_authorization(turn_context: &TurnContext) -> bool {
|
|
||||||
let provider_info = turn_context.provider.info();
|
|
||||||
!provider_info.requires_openai_auth
|
|
||||||
&& provider_info.http_headers.as_ref().is_some_and(|headers| {
|
|
||||||
headers.iter().any(|(name, value)| {
|
|
||||||
name.eq_ignore_ascii_case(ACTOR_AUTHORIZATION_HEADER) && !value.trim().is_empty()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn standalone_image_generation_model_visible(turn_context: &TurnContext) -> bool {
|
fn standalone_image_generation_model_visible(turn_context: &TurnContext) -> bool {
|
||||||
if !image_generation_runtime_enabled(turn_context) || !namespace_tools_enabled(turn_context) {
|
if !image_generation_runtime_enabled(turn_context) || !namespace_tools_enabled(turn_context) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -275,22 +275,19 @@ fn use_bedrock_provider(turn: &mut TurnContext) {
|
|||||||
turn.provider = create_model_provider(provider_info, turn.auth_manager.clone());
|
turn.provider = create_model_provider(provider_info, turn.auth_manager.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn use_provider_auth(
|
fn use_actor_authorized_provider(turn: &mut TurnContext) {
|
||||||
turn: &mut TurnContext,
|
|
||||||
requires_openai_auth: bool,
|
|
||||||
actor_header: Option<(&str, &str)>,
|
|
||||||
) {
|
|
||||||
let mut provider_info = turn.config.model_provider.clone();
|
let mut provider_info = turn.config.model_provider.clone();
|
||||||
provider_info.requires_openai_auth = requires_openai_auth;
|
provider_info.requires_openai_auth = false;
|
||||||
provider_info.http_headers = actor_header.map(|(name, value)| {
|
provider_info.http_headers = Some(HashMap::from([
|
||||||
HashMap::from([
|
(
|
||||||
(name.to_string(), value.to_string()),
|
"x-openai-actor-authorization".to_string(),
|
||||||
(
|
"test-actor-authorization".to_string(),
|
||||||
"ChatGPT-Account-ID".to_string(),
|
),
|
||||||
"test-account-id".to_string(),
|
(
|
||||||
),
|
"ChatGPT-Account-ID".to_string(),
|
||||||
])
|
"test-account-id".to_string(),
|
||||||
});
|
),
|
||||||
|
]));
|
||||||
turn.auth_manager = None;
|
turn.auth_manager = None;
|
||||||
update_config(turn, |config| {
|
update_config(turn, |config| {
|
||||||
config.model_provider = provider_info.clone();
|
config.model_provider = provider_info.clone();
|
||||||
@@ -1552,47 +1549,17 @@ async fn hosted_tools_follow_provider_auth_model_and_config_gates() {
|
|||||||
.await;
|
.await;
|
||||||
unrelated_chatgpt_auth.assert_visible_lacks(&["image_generation"]);
|
unrelated_chatgpt_auth.assert_visible_lacks(&["image_generation"]);
|
||||||
|
|
||||||
let provider_without_actor_auth = probe(|turn| {
|
let actor_authorized_provider = probe(|turn| {
|
||||||
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
|
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
|
||||||
use_provider_auth(
|
use_actor_authorized_provider(turn);
|
||||||
turn, /*requires_openai_auth*/ false, /*actor_header*/ None,
|
|
||||||
);
|
|
||||||
turn.model_info.input_modalities = vec![InputModality::Image];
|
turn.model_info.input_modalities = vec![InputModality::Image];
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
provider_without_actor_auth.assert_visible_lacks(&["image_generation"]);
|
actor_authorized_provider.assert_visible_contains(&["image_generation"]);
|
||||||
|
|
||||||
let provider_authenticated = probe(|turn| {
|
|
||||||
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
|
|
||||||
use_provider_auth(
|
|
||||||
turn,
|
|
||||||
/*requires_openai_auth*/ false,
|
|
||||||
Some(("X-OpenAI-Actor-Authorization", "test-actor-authorization")),
|
|
||||||
);
|
|
||||||
turn.model_info.input_modalities = vec![InputModality::Image];
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
provider_authenticated.assert_visible_contains(&["image_generation"]);
|
|
||||||
|
|
||||||
let empty_actor_auth = probe(|turn| {
|
|
||||||
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
|
|
||||||
use_provider_auth(
|
|
||||||
turn,
|
|
||||||
/*requires_openai_auth*/ false,
|
|
||||||
Some(("x-openai-actor-authorization", " ")),
|
|
||||||
);
|
|
||||||
turn.model_info.input_modalities = vec![InputModality::Image];
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
empty_actor_auth.assert_visible_lacks(&["image_generation"]);
|
|
||||||
|
|
||||||
let feature_disabled = probe(|turn| {
|
let feature_disabled = probe(|turn| {
|
||||||
set_feature(turn, Feature::ImageGeneration, /*enabled*/ false);
|
set_feature(turn, Feature::ImageGeneration, /*enabled*/ false);
|
||||||
use_provider_auth(
|
use_actor_authorized_provider(turn);
|
||||||
turn,
|
|
||||||
/*requires_openai_auth*/ false,
|
|
||||||
Some(("x-openai-actor-authorization", "test-actor-authorization")),
|
|
||||||
);
|
|
||||||
turn.model_info.input_modalities = vec![InputModality::Image];
|
turn.model_info.input_modalities = vec![InputModality::Image];
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
@@ -1600,11 +1567,7 @@ async fn hosted_tools_follow_provider_auth_model_and_config_gates() {
|
|||||||
|
|
||||||
let text_only_model = probe(|turn| {
|
let text_only_model = probe(|turn| {
|
||||||
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
|
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
|
||||||
use_provider_auth(
|
use_actor_authorized_provider(turn);
|
||||||
turn,
|
|
||||||
/*requires_openai_auth*/ false,
|
|
||||||
Some(("x-openai-actor-authorization", "test-actor-authorization")),
|
|
||||||
);
|
|
||||||
turn.model_info.input_modalities = vec![];
|
turn.model_info.input_modalities = vec![];
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
@@ -1613,34 +1576,12 @@ async fn hosted_tools_follow_provider_auth_model_and_config_gates() {
|
|||||||
let unsupported_image_generation_provider = probe(|turn| {
|
let unsupported_image_generation_provider = probe(|turn| {
|
||||||
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
|
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
|
||||||
use_bedrock_provider(turn);
|
use_bedrock_provider(turn);
|
||||||
let mut provider_info = turn.provider.info().clone();
|
use_actor_authorized_provider(turn);
|
||||||
provider_info.requires_openai_auth = false;
|
|
||||||
provider_info.http_headers = Some(HashMap::from([(
|
|
||||||
"x-openai-actor-authorization".to_string(),
|
|
||||||
"test-actor-authorization".to_string(),
|
|
||||||
)]));
|
|
||||||
turn.auth_manager = None;
|
|
||||||
update_config(turn, |config| {
|
|
||||||
config.model_provider = provider_info.clone();
|
|
||||||
});
|
|
||||||
turn.provider = create_model_provider(provider_info, /*auth_manager*/ None);
|
|
||||||
turn.model_info.input_modalities = vec![InputModality::Image];
|
turn.model_info.input_modalities = vec![InputModality::Image];
|
||||||
})
|
})
|
||||||
.await;
|
.await;
|
||||||
unsupported_image_generation_provider.assert_visible_lacks(&["image_generation"]);
|
unsupported_image_generation_provider.assert_visible_lacks(&["image_generation"]);
|
||||||
|
|
||||||
let codex_managed_auth_provider = probe(|turn| {
|
|
||||||
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
|
|
||||||
use_provider_auth(
|
|
||||||
turn,
|
|
||||||
/*requires_openai_auth*/ true,
|
|
||||||
Some(("x-openai-actor-authorization", "test-actor-authorization")),
|
|
||||||
);
|
|
||||||
turn.model_info.input_modalities = vec![InputModality::Image];
|
|
||||||
})
|
|
||||||
.await;
|
|
||||||
codex_managed_auth_provider.assert_visible_lacks(&["image_generation"]);
|
|
||||||
|
|
||||||
let image_generation = probe(|turn| {
|
let image_generation = probe(|turn| {
|
||||||
use_chatgpt_auth(turn);
|
use_chatgpt_auth(turn);
|
||||||
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
|
set_feature(turn, Feature::ImageGeneration, /*enabled*/ true);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
@@ -56,6 +57,18 @@ fn has_hosted_tool(tools: &[Value], tool_type: &str) -> bool {
|
|||||||
.any(|tool| tool.get("type").and_then(Value::as_str) == Some(tool_type))
|
.any(|tool| tool.get("type").and_then(Value::as_str) == Some(tool_type))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn has_namespaced_tool(tools: &[Value], namespace: &str, tool_name: &str) -> bool {
|
||||||
|
tools.iter().any(|tool| {
|
||||||
|
tool.get("type").and_then(Value::as_str) == Some("namespace")
|
||||||
|
&& tool.get("name").and_then(Value::as_str) == Some(namespace)
|
||||||
|
&& tool["tools"].as_array().is_some_and(|tools| {
|
||||||
|
tools
|
||||||
|
.iter()
|
||||||
|
.any(|tool| tool.get("name").and_then(Value::as_str) == Some(tool_name))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn additional_tools(body: &Value) -> Result<&[Value]> {
|
fn additional_tools(body: &Value) -> Result<&[Value]> {
|
||||||
body["input"]
|
body["input"]
|
||||||
.as_array()
|
.as_array()
|
||||||
@@ -227,13 +240,58 @@ async fn responses_lite_uses_standalone_web_search_and_image_generation() -> Res
|
|||||||
let body = request.body_json();
|
let body = request.body_json();
|
||||||
assert!(body.get("tools").is_none());
|
assert!(body.get("tools").is_none());
|
||||||
let tools = additional_tools(&body)?;
|
let tools = additional_tools(&body)?;
|
||||||
assert!(!tools.is_empty());
|
assert!(has_namespaced_tool(tools, "web", "run"));
|
||||||
|
assert!(has_namespaced_tool(tools, "image_gen", "imagegen"));
|
||||||
assert!(!has_hosted_tool(tools, "web_search"));
|
assert!(!has_hosted_tool(tools, "web_search"));
|
||||||
assert!(!has_hosted_tool(tools, "image_generation"));
|
assert!(!has_hosted_tool(tools, "image_generation"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn responses_lite_exposes_standalone_tools_for_actor_authorized_provider() -> Result<()> {
|
||||||
|
skip_if_no_network!(Ok(()));
|
||||||
|
|
||||||
|
let server = responses::start_mock_server().await;
|
||||||
|
let response_mock = responses::mount_sse_once(
|
||||||
|
&server,
|
||||||
|
responses::sse(vec![
|
||||||
|
responses::ev_response_created("resp-1"),
|
||||||
|
responses::ev_completed("resp-1"),
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let auth = CodexAuth::from_api_key("dummy");
|
||||||
|
let extensions = responses_extensions(&auth);
|
||||||
|
let mut builder = test_codex()
|
||||||
|
.with_auth(auth)
|
||||||
|
.with_extensions(extensions)
|
||||||
|
.with_model_info_override("gpt-5.4", |model_info| {
|
||||||
|
model_info.use_responses_lite = true;
|
||||||
|
configure_image_capable_model(model_info);
|
||||||
|
})
|
||||||
|
.with_config(|config| {
|
||||||
|
configure_responses_tools(config);
|
||||||
|
config.model_provider.name = "local".to_string();
|
||||||
|
config.model_provider.requires_openai_auth = false;
|
||||||
|
config.model_provider.http_headers = Some(HashMap::from([(
|
||||||
|
"x-openai-actor-authorization".to_string(),
|
||||||
|
"test-actor-authorization".to_string(),
|
||||||
|
)]));
|
||||||
|
});
|
||||||
|
let test = builder.build(&server).await?;
|
||||||
|
|
||||||
|
test.submit_turn("Use standalone tools").await?;
|
||||||
|
|
||||||
|
let body = response_mock.single_request().body_json();
|
||||||
|
let tools = additional_tools(&body)?;
|
||||||
|
assert!(has_namespaced_tool(tools, "web", "run"));
|
||||||
|
assert!(has_namespaced_tool(tools, "image_gen", "imagegen"));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn responses_lite_compact_request_uses_lite_transport_contract() -> Result<()> {
|
async fn responses_lite_compact_request_uses_lite_transport_contract() -> Result<()> {
|
||||||
skip_if_no_network!(Ok(()));
|
skip_if_no_network!(Ok(()));
|
||||||
|
|||||||
@@ -34,11 +34,11 @@ struct ImageGenerationExtensionConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ImageGenerationExtensionConfig {
|
impl ImageGenerationExtensionConfig {
|
||||||
/// Resolves whether standalone image generation should be available for a thread.
|
/// Resolves the image provider and save root for a thread.
|
||||||
fn from_config(config: &Config, resolve_save_root: &SaveRootResolver) -> Self {
|
fn from_config(config: &Config, resolve_save_root: &SaveRootResolver) -> Self {
|
||||||
Self {
|
Self {
|
||||||
// Core selects this executor per turn using the feature flag or model metadata.
|
available: config.model_provider.is_openai()
|
||||||
available: config.model_provider.is_openai(),
|
|| config.model_provider.uses_openai_actor_authorization(),
|
||||||
provider: config.model_provider.clone(),
|
provider: config.model_provider.clone(),
|
||||||
save_root: resolve_save_root(config),
|
save_root: resolve_save_root(config),
|
||||||
}
|
}
|
||||||
@@ -46,7 +46,7 @@ impl ImageGenerationExtensionConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadLifecycleContributor<Config> for ImageGenerationExtension {
|
impl ThreadLifecycleContributor<Config> for ImageGenerationExtension {
|
||||||
/// Seeds image-generation availability when a thread begins.
|
/// Seeds image-generation configuration when a thread begins.
|
||||||
fn on_thread_start<'a>(
|
fn on_thread_start<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
input: ThreadStartInput<'a, Config>,
|
input: ThreadStartInput<'a, Config>,
|
||||||
@@ -63,7 +63,7 @@ impl ThreadLifecycleContributor<Config> for ImageGenerationExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigContributor<Config> for ImageGenerationExtension {
|
impl ConfigContributor<Config> for ImageGenerationExtension {
|
||||||
/// Refreshes image-generation availability after thread configuration changes.
|
/// Refreshes image-generation configuration after thread configuration changes.
|
||||||
fn on_config_changed(
|
fn on_config_changed(
|
||||||
&self,
|
&self,
|
||||||
_session_store: &ExtensionData,
|
_session_store: &ExtensionData,
|
||||||
@@ -88,7 +88,7 @@ impl ToolContributor for ImageGenerationExtension {
|
|||||||
let Some(config) = thread_store.get::<ImageGenerationExtensionConfig>() else {
|
let Some(config) = thread_store.get::<ImageGenerationExtensionConfig>() else {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
};
|
};
|
||||||
if !config.available || !self.auth_manager.current_auth_uses_codex_backend() {
|
if !config.available {
|
||||||
return Vec::new();
|
return Vec::new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ impl From<&Config> for WebSearchExtensionConfig {
|
|||||||
let web_search_mode = config.web_search_mode.value();
|
let web_search_mode = config.web_search_mode.value();
|
||||||
Self {
|
Self {
|
||||||
// Core selects this executor per turn using the feature flag or model metadata.
|
// Core selects this executor per turn using the feature flag or model metadata.
|
||||||
available: config.model_provider.is_openai()
|
available: (config.model_provider.is_openai()
|
||||||
|
|| config.model_provider.uses_openai_actor_authorization())
|
||||||
&& web_search_mode != WebSearchMode::Disabled,
|
&& web_search_mode != WebSearchMode::Disabled,
|
||||||
provider: config.model_provider.clone(),
|
provider: config.model_provider.clone(),
|
||||||
settings: search_settings(config, web_search_mode),
|
settings: search_settings(config, web_search_mode),
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const MAX_STREAM_MAX_RETRIES: u64 = 100;
|
|||||||
const MAX_REQUEST_MAX_RETRIES: u64 = 100;
|
const MAX_REQUEST_MAX_RETRIES: u64 = 100;
|
||||||
|
|
||||||
const OPENAI_PROVIDER_NAME: &str = "OpenAI";
|
const OPENAI_PROVIDER_NAME: &str = "OpenAI";
|
||||||
|
const OPENAI_ACTOR_AUTHORIZATION_HEADER: &str = "x-openai-actor-authorization";
|
||||||
pub const OPENAI_PROVIDER_ID: &str = "openai";
|
pub const OPENAI_PROVIDER_ID: &str = "openai";
|
||||||
pub const CHATGPT_CODEX_BASE_URL: &str = "https://chatgpt.com/backend-api/codex";
|
pub const CHATGPT_CODEX_BASE_URL: &str = "https://chatgpt.com/backend-api/codex";
|
||||||
const AMAZON_BEDROCK_PROVIDER_NAME: &str = "Amazon Bedrock";
|
const AMAZON_BEDROCK_PROVIDER_NAME: &str = "Amazon Bedrock";
|
||||||
@@ -392,6 +393,16 @@ impl ModelProviderInfo {
|
|||||||
self.name == OPENAI_PROVIDER_NAME
|
self.name == OPENAI_PROVIDER_NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn uses_openai_actor_authorization(&self) -> bool {
|
||||||
|
!self.requires_openai_auth
|
||||||
|
&& self.http_headers.as_ref().is_some_and(|headers| {
|
||||||
|
headers.iter().any(|(name, value)| {
|
||||||
|
name.eq_ignore_ascii_case(OPENAI_ACTOR_AUTHORIZATION_HEADER)
|
||||||
|
&& !value.trim().is_empty()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_amazon_bedrock(&self) -> bool {
|
pub fn is_amazon_bedrock(&self) -> bool {
|
||||||
self.name == AMAZON_BEDROCK_PROVIDER_NAME
|
self.name == AMAZON_BEDROCK_PROVIDER_NAME
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,6 +198,31 @@ fn test_supports_remote_compaction_for_non_openai_non_azure_provider() {
|
|||||||
assert!(!provider.supports_remote_compaction());
|
assert!(!provider.supports_remote_compaction());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uses_openai_actor_authorization() {
|
||||||
|
let mut provider = ModelProviderInfo {
|
||||||
|
http_headers: Some(maplit::hashmap! {
|
||||||
|
"X-OpenAI-Actor-Authorization".to_string() => "actor-token".to_string(),
|
||||||
|
}),
|
||||||
|
..ModelProviderInfo::default()
|
||||||
|
};
|
||||||
|
assert!(provider.uses_openai_actor_authorization());
|
||||||
|
|
||||||
|
provider.http_headers = None;
|
||||||
|
assert!(!provider.uses_openai_actor_authorization());
|
||||||
|
|
||||||
|
provider.http_headers = Some(maplit::hashmap! {
|
||||||
|
OPENAI_ACTOR_AUTHORIZATION_HEADER.to_string() => " ".to_string(),
|
||||||
|
});
|
||||||
|
assert!(!provider.uses_openai_actor_authorization());
|
||||||
|
|
||||||
|
provider.http_headers = Some(maplit::hashmap! {
|
||||||
|
OPENAI_ACTOR_AUTHORIZATION_HEADER.to_string() => "actor-token".to_string(),
|
||||||
|
});
|
||||||
|
provider.requires_openai_auth = true;
|
||||||
|
assert!(!provider.uses_openai_actor_authorization());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_deserialize_provider_auth_config_defaults() {
|
fn test_deserialize_provider_auth_config_defaults() {
|
||||||
let base_dir = tempdir().unwrap();
|
let base_dir = tempdir().unwrap();
|
||||||
|
|||||||
Reference in new issue
Block a user