#[cfg(feature = "providers-extended")]
use super::super::github_copilot;
use super::super::unified_provider::ProviderError;
use super::super::{anthropic, bedrock, cloudflare, macros, mistral, openai, openai_like};
#[cfg(feature = "providers-extra")]
use super::super::{azure, azure_ai, vertex_ai};
use crate::core::net::ProviderEndpointAccess;
use crate::core::traits::provider::ProviderConfig as _;
use std::env;
#[cfg(feature = "providers-extra")]
use std::fs;
pub(super) fn config_str<'a>(config: &'a serde_json::Value, key: &str) -> Option<&'a str> {
config
.get(key)
.and_then(serde_json::Value::as_str)
.filter(|value| !value.trim().is_empty())
}
pub(super) fn config_u32(config: &serde_json::Value, key: &str) -> Option<u32> {
config
.get(key)
.and_then(serde_json::Value::as_u64)
.and_then(|value| u32::try_from(value).ok())
}
pub(super) fn config_u64(config: &serde_json::Value, key: &str) -> Option<u64> {
config.get(key).and_then(serde_json::Value::as_u64)
}
pub(super) fn config_bool(config: &serde_json::Value, key: &str) -> Option<bool> {
config.get(key).and_then(serde_json::Value::as_bool)
}
pub(super) fn config_endpoint_access(
config: &serde_json::Value,
provider: &'static str,
) -> Result<ProviderEndpointAccess, ProviderError> {
config
.get("endpoint_access")
.map(|value| {
serde_json::from_value(value.clone()).map_err(|error| {
ProviderError::configuration(provider, format!("invalid endpoint_access: {error}"))
})
})
.transpose()
.map(|access| access.unwrap_or_default())
}
pub(super) fn config_str_any<'a>(config: &'a serde_json::Value, keys: &[&str]) -> Option<&'a str> {
keys.iter().find_map(|key| config_str(config, key))
}
pub(super) fn env_str_any(keys: &[&str]) -> Option<String> {
keys.iter()
.filter_map(|key| env::var(key).ok())
.find(|value| !value.trim().is_empty())
}
pub(super) fn merge_string_headers(
target: &mut std::collections::HashMap<String, String>,
config: &serde_json::Value,
key: &str,
) {
if let Some(header_map) = config.get(key).and_then(serde_json::Value::as_object) {
for (header_key, header_value) in header_map {
if let Some(header_value) = header_value.as_str() {
target.insert(header_key.clone(), header_value.to_string());
}
}
}
}
pub(super) fn merge_string_headers_value(
target: &mut std::collections::HashMap<String, String>,
value: &serde_json::Value,
) -> bool {
if let Some(header_map) = value.as_object() {
for (header_key, header_value) in header_map {
if let Some(header_value) = header_value.as_str() {
target.insert(header_key.clone(), header_value.to_string());
}
}
return true;
}
false
}
#[cfg(not(feature = "providers-extra"))]
fn build_azure_openai_like_config_for_factory(
config: &serde_json::Value,
provider_name: &'static str,
endpoint_alias: &'static str,
) -> Result<openai_like::OpenAILikeConfig, ProviderError> {
let api_key = macros::require_config_str(config, "api_key", provider_name)?;
let api_base = config_str(config, "base_url")
.or_else(|| config_str(config, "api_base"))
.or_else(|| config_str(config, "endpoint"))
.map(Ok)
.or_else(|| {
config_str(config, endpoint_alias)
.map(|endpoint| validate_openai_like_azure_fallback_base(provider_name, endpoint))
})
.unwrap_or_else(|| {
Err(ProviderError::configuration(
provider_name,
"base_url (or endpoint) is required",
))
})?;
let mut oai_config = openai_like::OpenAILikeConfig::with_api_key(api_base, api_key);
oai_config.provider_name = provider_name.to_string();
if let Some(api_version) = config_str(config, "api_version") {
oai_config.base.api_version = Some(api_version.to_string());
}
if let Some(timeout) = config_u64(config, "timeout") {
oai_config.base.timeout = timeout;
}
if let Some(max_retries) = config_u32(config, "max_retries") {
oai_config.base.max_retries = max_retries;
}
merge_string_headers(&mut oai_config.base.headers, config, "headers");
merge_string_headers(&mut oai_config.custom_headers, config, "custom_headers");
Ok(oai_config)
}
#[cfg(not(feature = "providers-extra"))]
fn validate_openai_like_azure_fallback_base<'a>(
provider_name: &'static str,
endpoint: &'a str,
) -> Result<&'a str, ProviderError> {
let normalized = endpoint.trim_end_matches('/');
let is_openai_like = match provider_name {
"azure" => normalized.contains("/openai/deployments/"),
"azure_ai" => normalized.ends_with("/models") || normalized.contains("/models/"),
_ => true,
};
if is_openai_like {
Ok(endpoint)
} else {
Err(ProviderError::configuration(
provider_name,
format!(
"{provider_name} fallback requires base_url/api_base with an OpenAI-compatible path; enable providers-extra for bare Azure endpoints"
),
))
}
}
#[cfg(not(feature = "providers-extra"))]
pub(super) fn build_azure_ai_openai_like_config_from_factory(
config: &serde_json::Value,
) -> Result<openai_like::OpenAILikeConfig, ProviderError> {
build_azure_openai_like_config_for_factory(config, "azure_ai", "azure_ai_endpoint")
}
#[cfg(not(feature = "providers-extra"))]
pub(super) fn build_azure_openai_like_config_from_factory(
config: &serde_json::Value,
) -> Result<openai_like::OpenAILikeConfig, ProviderError> {
build_azure_openai_like_config_for_factory(config, "azure", "azure_endpoint")
}
pub(super) fn apply_tier1_openai_like_overrides(
config: &mut openai_like::OpenAILikeConfig,
settings: &std::collections::HashMap<String, serde_json::Value>,
) -> Vec<String> {
let mut ignored = Vec::new();
for (key, value) in settings {
let consumed = match key.as_str() {
"headers" => merge_string_headers_value(&mut config.base.headers, value),
"custom_headers" => merge_string_headers_value(&mut config.custom_headers, value),
"model_prefix" => {
if let Some(v) = value.as_str().filter(|v| !v.trim().is_empty()) {
config.model_prefix = Some(v.to_string());
true
} else {
false
}
}
"default_model" => {
if let Some(v) = value.as_str().filter(|v| !v.trim().is_empty()) {
config.default_model = Some(v.to_string());
true
} else {
false
}
}
"pass_through_params" => {
if let Some(v) = value.as_bool() {
config.pass_through_params = v;
true
} else {
false
}
}
"skip_api_key" => {
if let Some(v) = value.as_bool() {
config.skip_api_key = v;
true
} else {
false
}
}
"timeout" => {
if let Some(v) = value.as_u64() {
config.base.timeout = v;
true
} else {
false
}
}
"max_retries" => {
if let Some(v) = value.as_u64().and_then(|n| u32::try_from(n).ok()) {
config.base.max_retries = v;
true
} else {
false
}
}
"organization" => {
if let Some(v) = value.as_str().filter(|v| !v.trim().is_empty()) {
config.base.organization = Some(v.to_string());
true
} else {
false
}
}
"api_version" => {
if let Some(v) = value.as_str().filter(|v| !v.trim().is_empty()) {
config.base.api_version = Some(v.to_string());
true
} else {
false
}
}
_ => false,
};
if !consumed {
ignored.push(key.clone());
}
}
ignored.sort();
ignored
}
pub(super) fn build_openai_config_from_factory(
config: &serde_json::Value,
) -> Result<openai::OpenAIConfig, ProviderError> {
let api_key = macros::require_config_str(config, "api_key", "openai")?;
let mut openai_config = openai::OpenAIConfig::default();
openai_config.base.api_key = Some(api_key.to_string());
openai_config.base.endpoint_access = config_endpoint_access(config, "openai")?;
if let Some(provider_name) = config_str(config, "provider_name") {
openai_config.provider_name = provider_name.to_string();
}
if let Some(base_url) =
config_str(config, "base_url").or_else(|| config_str(config, "api_base"))
{
openai_config.base.api_base = Some(base_url.to_string());
}
if let Some(timeout) = config_u64(config, "timeout") {
openai_config.base.timeout = timeout;
}
if let Some(max_retries) = config_u32(config, "max_retries") {
openai_config.base.max_retries = max_retries;
}
if let Some(organization) = config_str(config, "organization") {
openai_config.organization = Some(organization.to_string());
}
if let Some(project) = config_str(config, "project") {
openai_config.project = Some(project.to_string());
}
merge_string_headers(&mut openai_config.base.headers, config, "headers");
merge_string_headers(&mut openai_config.base.headers, config, "custom_headers");
if let Some(model_mappings) = config
.get("model_mappings")
.and_then(serde_json::Value::as_object)
{
for (from_model, to_model) in model_mappings {
if let Some(to_model) = to_model.as_str() {
openai_config
.model_mappings
.insert(from_model.clone(), to_model.to_string());
}
}
}
Ok(openai_config)
}
pub(super) fn build_anthropic_config_from_factory(
config: &serde_json::Value,
) -> Result<anthropic::AnthropicConfig, ProviderError> {
let api_key = macros::require_config_str(config, "api_key", "anthropic")?;
let mut anthropic_config = anthropic::AnthropicConfig::default().with_api_key(api_key);
anthropic_config.endpoint_access = config_endpoint_access(config, "anthropic")?;
if let Some(base_url) =
config_str(config, "base_url").or_else(|| config_str(config, "api_base"))
{
anthropic_config.base_url = base_url.to_string();
}
if let Some(api_version) = config_str(config, "api_version") {
anthropic_config.api_version = api_version.to_string();
}
if let Some(timeout) = config_u64(config, "timeout") {
anthropic_config.request_timeout = timeout;
}
if let Some(connect_timeout) = config_u64(config, "connect_timeout") {
anthropic_config.connect_timeout = connect_timeout;
}
if let Some(max_retries) = config_u32(config, "max_retries") {
anthropic_config.max_retries = max_retries;
}
if let Some(retry_delay_base) = config_u64(config, "retry_delay_base") {
anthropic_config.retry_delay_base = retry_delay_base;
}
if let Some(proxy_url) = config_str(config, "proxy_url").or_else(|| config_str(config, "proxy"))
{
anthropic_config.proxy_url = Some(proxy_url.to_string());
}
merge_string_headers(&mut anthropic_config.custom_headers, config, "headers");
merge_string_headers(
&mut anthropic_config.custom_headers,
config,
"custom_headers",
);
if let Some(enable_multimodal) = config_bool(config, "enable_multimodal") {
anthropic_config.enable_multimodal = enable_multimodal;
}
if let Some(enable_cache_control) = config_bool(config, "enable_cache_control") {
anthropic_config.enable_cache_control = enable_cache_control;
}
if let Some(enable_computer_use) = config_bool(config, "enable_computer_use") {
anthropic_config.enable_computer_use = enable_computer_use;
}
if let Some(enable_experimental) = config_bool(config, "enable_experimental") {
anthropic_config.enable_experimental = enable_experimental;
}
if let Some(allow_unknown_models) = config_bool(config, "allow_unknown_models") {
anthropic_config.allow_unknown_models = allow_unknown_models;
}
if let Some(models) = config.get("models").and_then(|value| value.as_array()) {
anthropic_config.configured_models = models
.iter()
.filter_map(|value| value.as_str().map(str::to_string))
.collect();
}
if let Some(models) = config
.get("multimodal_models")
.and_then(|value| value.as_array())
{
anthropic_config.configured_multimodal_models = models
.iter()
.filter_map(|value| value.as_str().map(str::to_string))
.collect();
}
anthropic_config
.validate()
.map_err(|e| ProviderError::configuration("anthropic", e))?;
Ok(anthropic_config)
}
pub(super) fn build_mistral_config_from_factory(
config: &serde_json::Value,
) -> Result<mistral::MistralConfig, ProviderError> {
let api_key = macros::require_config_str(config, "api_key", "mistral")?;
let mut mistral_config = mistral::MistralConfig {
api_key: api_key.to_string(),
..Default::default()
};
mistral_config.endpoint_access = config_endpoint_access(config, "mistral")?;
if let Some(base_url) =
config_str(config, "base_url").or_else(|| config_str(config, "api_base"))
{
mistral_config.api_base = base_url.to_string();
}
if let Some(timeout) = config_u64(config, "timeout") {
mistral_config.timeout_seconds = timeout;
}
if let Some(max_retries) = config_u32(config, "max_retries") {
mistral_config.max_retries = max_retries;
}
Ok(mistral_config)
}
pub(super) fn build_cloudflare_config_from_factory(
config: &serde_json::Value,
) -> Result<cloudflare::CloudflareConfig, ProviderError> {
let account_id = config_str(config, "account_id")
.or_else(|| config_str(config, "organization"))
.ok_or_else(|| ProviderError::configuration("cloudflare", "account_id is required"))?;
let api_token = config_str(config, "api_token")
.or_else(|| config_str(config, "api_key"))
.ok_or_else(|| ProviderError::configuration("cloudflare", "api_token is required"))?;
let mut cf_config = cloudflare::CloudflareConfig {
account_id: Some(account_id.to_string()),
api_token: Some(api_token.to_string()),
..Default::default()
};
if let Some(base_url) =
config_str(config, "base_url").or_else(|| config_str(config, "api_base"))
{
cf_config.api_base = Some(base_url.to_string());
}
if let Some(timeout) = config_u64(config, "timeout") {
cf_config.timeout = timeout;
}
if let Some(max_retries) = config_u32(config, "max_retries") {
cf_config.max_retries = max_retries;
}
if let Some(debug) = config_bool(config, "debug") {
cf_config.debug = debug;
}
Ok(cf_config)
}
#[cfg(feature = "providers-extra")]
pub(super) fn build_azure_ai_config_from_factory(
config: &serde_json::Value,
) -> Result<azure_ai::AzureAIConfig, ProviderError> {
let api_key = macros::require_config_str(config, "api_key", "azure_ai")?;
let api_base = config_str(config, "base_url")
.or_else(|| config_str(config, "api_base"))
.or_else(|| config_str(config, "endpoint"))
.or_else(|| config_str(config, "azure_ai_endpoint"))
.ok_or_else(|| {
ProviderError::configuration("azure_ai", "base_url (or endpoint) is required")
})?;
let mut azure_ai_config = azure_ai::AzureAIConfig::new("azure_ai");
azure_ai_config.base.api_key = Some(api_key.to_string());
azure_ai_config.base.api_base = Some(api_base.to_string());
azure_ai_config.base.endpoint_access = config_endpoint_access(config, "azure_ai")?;
if let Some(api_version) = config_str(config, "api_version") {
azure_ai_config.base.api_version = Some(api_version.to_string());
}
if let Some(timeout) = config_u64(config, "timeout") {
azure_ai_config.base.timeout = timeout;
}
if let Some(max_retries) = config_u32(config, "max_retries") {
azure_ai_config.base.max_retries = max_retries;
}
merge_string_headers(&mut azure_ai_config.base.headers, config, "headers");
merge_string_headers(&mut azure_ai_config.base.headers, config, "custom_headers");
Ok(azure_ai_config)
}
#[cfg(feature = "providers-extra")]
pub(super) fn build_azure_config_from_factory(
config: &serde_json::Value,
) -> Result<azure::AzureConfig, ProviderError> {
let api_key = macros::require_config_str(config, "api_key", "azure")?;
let api_base = config_str(config, "base_url")
.or_else(|| config_str(config, "api_base"))
.or_else(|| config_str(config, "endpoint"))
.or_else(|| config_str(config, "azure_endpoint"))
.ok_or_else(|| {
ProviderError::configuration("azure", "base_url (or endpoint) is required")
})?;
let mut azure_config = azure::AzureConfig::new()
.with_api_key(api_key.to_string())
.with_azure_endpoint(api_base.to_string());
azure_config.endpoint_access = config_endpoint_access(config, "azure")?;
if let Some(api_version) = config_str(config, "api_version") {
azure_config.api_version = api_version.to_string();
}
if let Some(deployment_name) =
config_str(config, "deployment_name").or_else(|| config_str(config, "deployment"))
{
azure_config.deployment_name = Some(deployment_name.to_string());
}
if let Some(timeout) = config_u64(config, "timeout") {
azure_config.timeout = timeout;
}
if let Some(max_retries) = config_u32(config, "max_retries") {
azure_config.max_retries = max_retries;
}
merge_string_headers(&mut azure_config.custom_headers, config, "headers");
merge_string_headers(&mut azure_config.custom_headers, config, "custom_headers");
Ok(azure_config)
}
pub(super) fn build_bedrock_config_from_factory(
config: &serde_json::Value,
) -> Result<bedrock::BedrockConfig, ProviderError> {
let aws_access_key_id = config_str_any(
config,
&["aws_access_key_id", "aws_access_key", "access_key"],
)
.map(str::to_string)
.or_else(|| env_str_any(&["AWS_ACCESS_KEY_ID"]))
.ok_or_else(|| {
ProviderError::configuration(
"bedrock",
"aws_access_key_id or AWS_ACCESS_KEY_ID is required",
)
})?;
let aws_secret_access_key = config_str_any(
config,
&["aws_secret_access_key", "aws_secret_key", "secret_key"],
)
.map(str::to_string)
.or_else(|| env_str_any(&["AWS_SECRET_ACCESS_KEY"]))
.ok_or_else(|| {
ProviderError::configuration(
"bedrock",
"aws_secret_access_key or AWS_SECRET_ACCESS_KEY is required",
)
})?;
let aws_session_token = config_str_any(config, &["aws_session_token", "session_token"])
.map(str::to_string)
.or_else(|| env_str_any(&["AWS_SESSION_TOKEN"]));
let aws_region = config_str_any(config, &["aws_region", "aws_region_name", "region"])
.map(str::to_string)
.or_else(|| env_str_any(&["AWS_REGION", "AWS_DEFAULT_REGION"]))
.unwrap_or_else(|| "us-east-1".to_string());
let mut bedrock_config = bedrock::BedrockConfig {
aws_access_key_id,
aws_secret_access_key,
aws_session_token,
aws_region,
..Default::default()
};
bedrock_config.endpoint_access = config_endpoint_access(config, "bedrock")?;
if let Some(timeout) =
config_u64(config, "timeout_seconds").or_else(|| config_u64(config, "timeout"))
{
bedrock_config.timeout_seconds = timeout;
}
if let Some(max_retries) = config_u32(config, "max_retries") {
bedrock_config.max_retries = max_retries;
}
Ok(bedrock_config)
}
#[cfg(feature = "providers-extra")]
pub(super) fn build_vertex_ai_config_from_factory(
config: &serde_json::Value,
) -> Result<vertex_ai::VertexAIProviderConfig, ProviderError> {
if config_str(config, "api_key").is_some() {
return Err(ProviderError::invalid_request(
"vertex_ai",
"Vertex AI native auth does not accept static api_key; configure project/project_id with access_token, credentials_json, credentials_file, or Application Default Credentials",
));
}
let project_id = config_str_any(
config,
&["project_id", "project", "gcp_project", "google_project_id"],
)
.map(str::to_string)
.or_else(|| {
env_str_any(&[
"GOOGLE_CLOUD_PROJECT",
"GOOGLE_PROJECT_ID",
"GCP_PROJECT",
"GCLOUD_PROJECT",
])
})
.ok_or_else(|| {
ProviderError::configuration("vertex_ai", "project_id (or project) is required")
})?;
let mut vertex_config = vertex_ai::VertexAIProviderConfig {
project_id,
..Default::default()
};
vertex_config.endpoint_access = config_endpoint_access(config, "vertex_ai")?;
if let Some(location) = config_str_any(config, &["location", "region", "vertex_location"])
.map(str::to_string)
.or_else(|| env_str_any(&["GOOGLE_CLOUD_LOCATION", "VERTEX_AI_LOCATION"]))
{
vertex_config.location = location;
}
if let Some(api_version) = config_str(config, "api_version") {
vertex_config.api_version = api_version.to_string();
}
if let Some(api_base) = config_str(config, "base_url")
.or_else(|| config_str(config, "api_base"))
.or_else(|| config_str(config, "endpoint"))
{
vertex_config.api_base = Some(api_base.to_string());
}
if let Some(timeout) = config_u64(config, "timeout") {
vertex_config.timeout_seconds = timeout;
}
if let Some(max_retries) = config_u32(config, "max_retries") {
vertex_config.max_retries = max_retries;
}
if let Some(enable_experimental) = config_bool(config, "enable_experimental") {
vertex_config.enable_experimental = enable_experimental;
}
vertex_config.credentials = build_vertex_credentials_from_factory(config)?;
vertex_config
.validate()
.map_err(|err| ProviderError::configuration("vertex_ai", err))?;
Ok(vertex_config)
}
#[cfg(feature = "providers-extra")]
fn build_vertex_credentials_from_factory(
config: &serde_json::Value,
) -> Result<vertex_ai::VertexCredentials, ProviderError> {
if let Some(access_token) = config_str_any(
config,
&[
"access_token",
"vertex_access_token",
"google_access_token",
"bearer_token",
],
) {
return Ok(vertex_ai::VertexCredentials::AccessToken(
access_token.to_string(),
));
}
if let Some(credentials_json) = config_str_any(
config,
&[
"credentials_json",
"vertex_ai_credentials",
"google_credentials_json",
],
) {
return vertex_ai::VertexAuth::parse_credentials(credentials_json).map_err(|err| {
ProviderError::configuration("vertex_ai", format!("invalid credentials_json: {err}"))
});
}
if let Some(credentials_file) = config_str_any(
config,
&[
"credentials_file",
"credential_file",
"google_application_credentials",
],
)
.map(str::to_string)
.or_else(|| env_str_any(&["GOOGLE_APPLICATION_CREDENTIALS"]))
{
let contents = fs::read_to_string(&credentials_file).map_err(|err| {
ProviderError::configuration(
"vertex_ai",
format!("failed to read credentials file '{credentials_file}': {err}"),
)
})?;
return vertex_ai::VertexAuth::parse_credentials(&contents).map_err(|err| {
ProviderError::configuration("vertex_ai", format!("invalid credentials file: {err}"))
});
}
Ok(vertex_ai::VertexCredentials::ApplicationDefault)
}
#[cfg(feature = "providers-extended")]
pub(super) fn build_github_copilot_config_from_factory(
config: &serde_json::Value,
) -> Result<github_copilot::GitHubCopilotConfig, ProviderError> {
if config_str(config, "api_key").is_some() {
return Err(ProviderError::invalid_request(
"github_copilot",
"GitHub Copilot native auth does not accept static api_key; configure token_dir, access_token_file, or api_key_file",
));
}
let mut copilot_config = github_copilot::GitHubCopilotConfig::default();
if let Some(api_base) =
config_str(config, "base_url").or_else(|| config_str(config, "api_base"))
{
copilot_config.api_base = Some(api_base.to_string());
}
if let Some(token_dir) = config_str(config, "token_dir") {
copilot_config.token_dir = Some(token_dir.to_string());
}
if let Some(access_token_file) = config_str(config, "access_token_file") {
copilot_config.access_token_file = Some(access_token_file.to_string());
}
if let Some(api_key_file) = config_str(config, "api_key_file") {
copilot_config.api_key_file = Some(api_key_file.to_string());
}
if let Some(timeout) = config_u64(config, "timeout") {
copilot_config.timeout = timeout;
}
if let Some(max_retries) = config_u32(config, "max_retries") {
copilot_config.max_retries = max_retries;
}
if let Some(disable_system_to_assistant) = config_bool(config, "disable_system_to_assistant") {
copilot_config.disable_system_to_assistant = disable_system_to_assistant;
}
if let Some(debug) = config_bool(config, "debug") {
copilot_config.debug = debug;
}
copilot_config
.validate()
.map_err(|err| ProviderError::configuration("github_copilot", err))?;
Ok(copilot_config)
}
pub(super) fn build_openai_like_config_from_factory(
config: &serde_json::Value,
) -> Result<openai_like::OpenAILikeConfig, ProviderError> {
let api_base = config_str(config, "base_url")
.or_else(|| config_str(config, "api_base"))
.ok_or_else(|| {
ProviderError::configuration("openai_compatible", "base_url (or api_base) is required")
})?;
let api_key = config_str(config, "api_key");
let skip_api_key = config_bool(config, "skip_api_key").unwrap_or(api_key.is_none());
let mut oai_like = if let Some(api_key) = api_key {
openai_like::OpenAILikeConfig::with_api_key(api_base, api_key)
} else {
openai_like::OpenAILikeConfig::new(api_base).with_skip_api_key(skip_api_key)
};
oai_like.base.endpoint_access = config_endpoint_access(config, "openai_compatible")?;
oai_like.skip_api_key = skip_api_key;
oai_like.provider_name = config_str(config, "provider_name")
.unwrap_or("openai_compatible")
.to_string();
if let Some(timeout) = config_u64(config, "timeout") {
oai_like.base.timeout = timeout;
}
if let Some(max_retries) = config_u32(config, "max_retries") {
oai_like.base.max_retries = max_retries;
}
if let Some(prefix) = config_str(config, "model_prefix") {
oai_like.model_prefix = Some(prefix.to_string());
}
if let Some(default_model) = config_str(config, "default_model") {
oai_like.default_model = Some(default_model.to_string());
}
if let Some(pass_through) = config_bool(config, "pass_through_params") {
oai_like.pass_through_params = pass_through;
}
if let Some(organization) = config_str(config, "organization") {
oai_like.base.organization = Some(organization.to_string());
}
if let Some(api_version) = config_str(config, "api_version") {
oai_like.base.api_version = Some(api_version.to_string());
}
merge_string_headers(&mut oai_like.base.headers, config, "headers");
merge_string_headers(&mut oai_like.custom_headers, config, "custom_headers");
Ok(oai_like)
}