mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-20 00:11:50 +00:00
refactor(core): centralize opaque provider params
This commit is contained in:
parent
30ad040b25
commit
451e96661e
36 changed files with 315 additions and 179 deletions
|
|
@ -40,7 +40,7 @@ async fn bedrock_request_is_signed_and_contains_audio() {
|
|||
api_base: Some(&api_base),
|
||||
custom_llm_provider: Some("bedrock"),
|
||||
extra_headers: None,
|
||||
optional_params,
|
||||
optional_params: optional_params.into(),
|
||||
timeout: None,
|
||||
})
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
use crate::Error;
|
||||
use serde_json::{Map, Value};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::types::{AudioTranscriptionRequestData, AudioTranscriptionResponseData};
|
||||
use crate::params::OpaqueParams;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum AudioTranscriptionAuth {
|
||||
|
|
@ -15,22 +16,16 @@ pub enum AudioTranscriptionAuth {
|
|||
pub trait AudioTranscriptionProviderConfig: Sync {
|
||||
fn supported_transcription_params(&self) -> &'static [&'static str];
|
||||
|
||||
fn map_transcription_params(&self, params: &Map<String, Value>) -> Map<String, Value> {
|
||||
params
|
||||
.iter()
|
||||
.filter(|(key, _)| {
|
||||
self.supported_transcription_params()
|
||||
.contains(&key.as_str())
|
||||
})
|
||||
.map(|(key, value)| (key.clone(), value.clone()))
|
||||
.collect()
|
||||
#[tracing::instrument(target = "litellm::function_trace", level = "trace", skip_all)]
|
||||
fn map_transcription_params(&self, params: &OpaqueParams) -> OpaqueParams {
|
||||
params.retain_supported(self.supported_transcription_params())
|
||||
}
|
||||
|
||||
fn transform_transcription_request(
|
||||
&self,
|
||||
model: &str,
|
||||
audio: Value,
|
||||
optional_params: Map<String, Value>,
|
||||
optional_params: OpaqueParams,
|
||||
) -> Result<AudioTranscriptionRequestData, Error>;
|
||||
|
||||
fn transform_transcription_response(
|
||||
|
|
@ -43,14 +38,14 @@ pub trait AudioTranscriptionProviderConfig: Sync {
|
|||
&self,
|
||||
api_base: Option<&str>,
|
||||
model: &str,
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
env_lookup: &dyn Fn(&str) -> Option<String>,
|
||||
) -> Result<String, Error>;
|
||||
|
||||
fn auth_strategy(
|
||||
&self,
|
||||
model: &str,
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
env_lookup: &dyn Fn(&str) -> Option<String>,
|
||||
) -> Result<AudioTranscriptionAuth, Error>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::{Map, Value};
|
||||
|
||||
use super::transformation::{AudioTranscriptionAuth, AudioTranscriptionProviderConfig};
|
||||
use crate::params::OpaqueParams;
|
||||
|
||||
pub struct AudioTranscriptionRequest<'a> {
|
||||
pub model: &'a str,
|
||||
|
|
@ -12,7 +13,7 @@ pub struct AudioTranscriptionRequest<'a> {
|
|||
pub api_base: Option<&'a str>,
|
||||
pub custom_llm_provider: Option<&'a str>,
|
||||
pub extra_headers: Option<Map<String, Value>>,
|
||||
pub optional_params: Map<String, Value>,
|
||||
pub optional_params: OpaqueParams,
|
||||
pub timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
|
|
@ -26,7 +27,7 @@ pub struct ProviderAudioTranscriptionRequest {
|
|||
pub(super) upstream_headers: Vec<(String, String)>,
|
||||
pub(super) auth: AudioTranscriptionAuth,
|
||||
#[cfg(feature = "bedrock-auth")]
|
||||
pub(super) optional_params: Map<String, Value>,
|
||||
pub(super) optional_params: OpaqueParams,
|
||||
pub(super) timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,9 @@ pub mod response_utils;
|
|||
pub mod transformation;
|
||||
pub mod types;
|
||||
|
||||
use serde_json::{Map, Value};
|
||||
use serde_json::Value;
|
||||
|
||||
use crate::params::OpaqueParams;
|
||||
use handler::execute_chat_completions_provider_call;
|
||||
use prepare::{parse_messages, resolve_provider_config, resolve_request};
|
||||
use types::{ChatCompletionsRequest, ChatCompletionsResponse};
|
||||
|
|
@ -38,7 +39,7 @@ pub fn chat_completions_decline_reason(
|
|||
model: &str,
|
||||
custom_llm_provider: Option<&str>,
|
||||
messages: Value,
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
) -> Option<&'static str> {
|
||||
let Ok((_, config)) = resolve_provider_config(model, custom_llm_provider) else {
|
||||
return Some("provider is not on the rust chat completions path");
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ fn request<'a>(
|
|||
model,
|
||||
messages,
|
||||
optional_params: match optional_params {
|
||||
Value::Object(map) => map,
|
||||
Value::Object(map) => map.into(),
|
||||
other => panic!("params must be an object, got {other}"),
|
||||
},
|
||||
api_key: Some("sk-test"),
|
||||
|
|
@ -489,7 +489,7 @@ fn decline_reason(
|
|||
params: Value,
|
||||
) -> Option<&'static str> {
|
||||
let params = match params {
|
||||
Value::Object(map) => map,
|
||||
Value::Object(map) => map.into(),
|
||||
other => panic!("params must be an object, got {other}"),
|
||||
};
|
||||
super::chat_completions_decline_reason(model, provider, messages, ¶ms)
|
||||
|
|
@ -663,7 +663,7 @@ mod round_trip {
|
|||
model: "anthropic/claude-sonnet-4-5",
|
||||
messages,
|
||||
optional_params: match params {
|
||||
Value::Object(map) => map,
|
||||
Value::Object(map) => map.into(),
|
||||
other => panic!("params must be an object, got {other}"),
|
||||
},
|
||||
api_key: Some("sk-test"),
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
use crate::Error;
|
||||
use serde_json::{Map, Value};
|
||||
use serde_json::Value;
|
||||
|
||||
use super::types::{
|
||||
ChatCompletionsResponse, ChatMessage, ChatMessageContent, ProviderChatRequestData,
|
||||
ProviderChatResponseData,
|
||||
};
|
||||
use crate::params::OpaqueParams;
|
||||
|
||||
/// How the upstream call is authenticated. API-key strategies are resolved in
|
||||
/// `prepare`; SigV4 needs the serialized body, so the handler signs it.
|
||||
|
|
@ -36,7 +37,7 @@ pub trait ChatCompletionsProviderConfig: Sync {
|
|||
&self,
|
||||
api_base: Option<&str>,
|
||||
model: &str,
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
env_lookup: &dyn Fn(&str) -> Option<String>,
|
||||
) -> Result<String, Error>;
|
||||
|
||||
|
|
@ -44,7 +45,7 @@ pub trait ChatCompletionsProviderConfig: Sync {
|
|||
&self,
|
||||
api_key: Option<&str>,
|
||||
model: &str,
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
env_lookup: &dyn Fn(&str) -> Option<String>,
|
||||
) -> Result<ChatCompletionsAuth, Error>;
|
||||
|
||||
|
|
@ -74,7 +75,7 @@ pub trait ChatCompletionsProviderConfig: Sync {
|
|||
fn unsupported_reason(
|
||||
&self,
|
||||
messages: &[ChatMessage],
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
) -> Option<Unsupported> {
|
||||
unsupported_param(
|
||||
self.supported_openai_params(),
|
||||
|
|
@ -88,7 +89,7 @@ pub trait ChatCompletionsProviderConfig: Sync {
|
|||
&self,
|
||||
model: &str,
|
||||
messages: Vec<ChatMessage>,
|
||||
optional_params: Map<String, Value>,
|
||||
optional_params: OpaqueParams,
|
||||
) -> Result<ProviderChatRequestData, Error>;
|
||||
|
||||
fn transform_response(
|
||||
|
|
@ -101,7 +102,7 @@ pub trait ChatCompletionsProviderConfig: Sync {
|
|||
pub fn unsupported_param(
|
||||
supported: &'static [(&'static str, &'static str)],
|
||||
config: &'static [&'static str],
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
) -> Option<Unsupported> {
|
||||
if optional_params
|
||||
.get(STREAM_PARAM)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize};
|
|||
use serde_json::{Map, Value};
|
||||
|
||||
use super::transformation::{ChatCompletionsAuth, ChatCompletionsProviderConfig};
|
||||
use crate::params::OpaqueParams;
|
||||
|
||||
/// A `/chat/completions` call as it crosses into the core.
|
||||
///
|
||||
|
|
@ -14,7 +15,7 @@ use super::transformation::{ChatCompletionsAuth, ChatCompletionsProviderConfig};
|
|||
pub struct ChatCompletionsRequest<'a> {
|
||||
pub model: &'a str,
|
||||
pub messages: Value,
|
||||
pub optional_params: Map<String, Value>,
|
||||
pub optional_params: OpaqueParams,
|
||||
pub api_key: Option<&'a str>,
|
||||
pub api_base: Option<&'a str>,
|
||||
pub custom_llm_provider: Option<&'a str>,
|
||||
|
|
@ -26,7 +27,7 @@ pub(super) struct ResolvedChatCompletionsRequest<'a> {
|
|||
pub(super) model: String,
|
||||
pub(super) config: &'static dyn ChatCompletionsProviderConfig,
|
||||
pub(super) messages: Vec<ChatMessage>,
|
||||
pub(super) optional_params: Map<String, Value>,
|
||||
pub(super) optional_params: OpaqueParams,
|
||||
pub(super) api_key: Option<&'a str>,
|
||||
pub(super) api_base: Option<&'a str>,
|
||||
pub(super) extra_headers: Option<Map<String, Value>>,
|
||||
|
|
@ -41,7 +42,7 @@ pub(super) struct ProviderChatCompletionsRequest {
|
|||
pub(super) upstream_headers: Vec<(String, String)>,
|
||||
pub(super) auth: ChatCompletionsAuth,
|
||||
#[cfg_attr(not(feature = "bedrock-auth"), allow(dead_code))]
|
||||
pub(super) optional_params: Map<String, Value>,
|
||||
pub(super) optional_params: OpaqueParams,
|
||||
pub(super) timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ pub(crate) mod llms;
|
|||
mod media;
|
||||
pub mod messages;
|
||||
pub mod ocr;
|
||||
pub mod params;
|
||||
pub mod providers;
|
||||
pub mod responses;
|
||||
mod url_utils;
|
||||
|
|
|
|||
|
|
@ -18,13 +18,17 @@ pub(crate) struct AzureAICohereParseConfig;
|
|||
impl BaseOcrConfig for AzureAICohereParseConfig {
|
||||
type ProviderResponse = CohereResponse;
|
||||
|
||||
fn get_supported_ocr_params(&self, model: &str) -> &'static [&'static str] {
|
||||
CohereParseConfig.get_supported_ocr_params(model)
|
||||
}
|
||||
|
||||
async fn prepare_request(
|
||||
&self,
|
||||
request: &LiteLLMOcrRequest,
|
||||
client: &OcrClient,
|
||||
) -> Result<reqwest::Request, OcrError> {
|
||||
let params = crate::ocr::wire::decode_request_value::<CohereParams>(
|
||||
serde_json::Value::Object(request.optional_params.clone()),
|
||||
serde_json::Value::Object(request.optional_params.clone().into()),
|
||||
"optional_params",
|
||||
)?;
|
||||
let config = AzureAuthInputs {
|
||||
|
|
|
|||
|
|
@ -629,6 +629,10 @@ pub(crate) struct AzureDocumentIntelligenceOCRConfig;
|
|||
impl BaseOcrConfig for AzureDocumentIntelligenceOCRConfig {
|
||||
type ProviderResponse = AzureDocumentIntelligenceOperation;
|
||||
|
||||
fn get_supported_ocr_params(&self, _model: &str) -> &'static [&'static str] {
|
||||
&["pages", "features"]
|
||||
}
|
||||
|
||||
async fn prepare_request(
|
||||
&self,
|
||||
request: &LiteLLMOcrRequest,
|
||||
|
|
@ -686,7 +690,8 @@ impl BaseOcrConfig for AzureDocumentIntelligenceOCRConfig {
|
|||
fn map_ocr_params(
|
||||
request: &LiteLLMOcrRequest,
|
||||
) -> Result<DocumentIntelligenceParams, OcrRequestError> {
|
||||
let params = params::decode_input_params(request.optional_params.clone(), "optional_params")?;
|
||||
let params =
|
||||
params::decode_input_params(request.optional_params.clone().into(), "optional_params")?;
|
||||
let crate::ocr::prepare::ParsedProviderParams {
|
||||
known: params,
|
||||
extra_params: _extra_params,
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@ use crate::Error;
|
|||
use crate::auth::{InputSource, Sourced};
|
||||
use crate::constants::AZURE_AI_OCR_PATH;
|
||||
use crate::llms::base_llm::ocr::transformation::BaseOcrConfig;
|
||||
use crate::llms::mistral::ocr::MistralOcrResponse;
|
||||
use crate::llms::mistral::ocr::transformation::MistralOCRConfig;
|
||||
use crate::llms::mistral::ocr::{MistralOcrParams, MistralOcrResponse};
|
||||
use crate::ocr::OcrClient;
|
||||
use crate::ocr::document::{inline_remote_document, validate_inline_document};
|
||||
use crate::ocr::error::{OcrError, OcrRequestError, OcrResponseError};
|
||||
use crate::ocr::prepare::{
|
||||
_prepare_ocr_request, ParsedProviderParams, credential_env, transform_request_body,
|
||||
};
|
||||
use crate::ocr::prepare::{credential_env, transform_request_body};
|
||||
use crate::ocr::types::{LiteLLMOcrRequest, LiteLLMOcrResponse, OcrConnection};
|
||||
use crate::providers::azure_ai::auth::AzureAuthInputs;
|
||||
use crate::url_utils::ApiUrl;
|
||||
|
|
@ -23,15 +21,16 @@ pub(crate) struct AzureAIOCRConfig;
|
|||
impl BaseOcrConfig for AzureAIOCRConfig {
|
||||
type ProviderResponse = MistralOcrResponse;
|
||||
|
||||
fn get_supported_ocr_params(&self, model: &str) -> &'static [&'static str] {
|
||||
MistralOCRConfig.get_supported_ocr_params(model)
|
||||
}
|
||||
|
||||
async fn prepare_request(
|
||||
&self,
|
||||
request: &LiteLLMOcrRequest,
|
||||
client: &OcrClient,
|
||||
) -> Result<reqwest::Request, OcrError> {
|
||||
let ParsedProviderParams {
|
||||
known: params,
|
||||
extra_params: _extra_params,
|
||||
} = _prepare_ocr_request::<MistralOcrParams>(request)?;
|
||||
let params = self.map_ocr_params(&request.model, &request.optional_params);
|
||||
let config = AzureAuthInputs {
|
||||
azure_ad_token_provider: request.azure_ad_token_provider.clone(),
|
||||
..AzureAuthInputs::from_sourced_optional_params(
|
||||
|
|
|
|||
|
|
@ -6,10 +6,17 @@ use crate::ocr::OcrClient;
|
|||
use crate::ocr::error::{OcrError, OcrResponseError};
|
||||
use crate::ocr::types::{LiteLLMOcrRequest, LiteLLMOcrResponse, OcrResponseFormat};
|
||||
use crate::ocr::wire::DecodedOcrResponse;
|
||||
use crate::params::OpaqueParams;
|
||||
|
||||
pub(crate) trait BaseOcrConfig: Send + Sync + Sized + 'static {
|
||||
type ProviderResponse: DeserializeOwned + Send;
|
||||
|
||||
fn get_supported_ocr_params(&self, model: &str) -> &'static [&'static str];
|
||||
|
||||
fn map_ocr_params(&self, model: &str, params: &OpaqueParams) -> OpaqueParams {
|
||||
params.retain_supported(self.get_supported_ocr_params(model))
|
||||
}
|
||||
|
||||
fn prepare_request(
|
||||
&self,
|
||||
request: &LiteLLMOcrRequest,
|
||||
|
|
|
|||
|
|
@ -290,13 +290,17 @@ impl CohereParseConfig {
|
|||
impl BaseOcrConfig for CohereParseConfig {
|
||||
type ProviderResponse = CohereResponse;
|
||||
|
||||
fn get_supported_ocr_params(&self, _model: &str) -> &'static [&'static str] {
|
||||
&["output_format"]
|
||||
}
|
||||
|
||||
async fn prepare_request(
|
||||
&self,
|
||||
request: &LiteLLMOcrRequest,
|
||||
client: &OcrClient,
|
||||
) -> Result<reqwest::Request, OcrError> {
|
||||
let params = crate::ocr::wire::decode_request_value::<CohereParams>(
|
||||
serde_json::Value::Object(request.optional_params.clone()),
|
||||
serde_json::Value::Object(request.optional_params.clone().into()),
|
||||
"optional_params",
|
||||
)?;
|
||||
let headers = validate_environment(&request.connection, &credential_env)?;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
pub(crate) mod transformation;
|
||||
|
||||
pub(crate) use transformation::{MistralOcrParams, MistralOcrResponse};
|
||||
pub(crate) use transformation::MistralOcrResponse;
|
||||
|
|
|
|||
|
|
@ -3,50 +3,14 @@ use serde_json::{Map, Value};
|
|||
|
||||
use crate::ocr::error::{OcrRequestError, OcrResponseError};
|
||||
use crate::ocr::types::{LiteLLMOcrResponse, OcrDocument};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub(crate) enum MistralOcrPages {
|
||||
Range(String),
|
||||
Indices(Vec<i64>),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
pub(crate) struct MistralOcrParams {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub pages: Option<MistralOcrPages>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub include_image_base64: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub image_limit: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub image_min_size: Option<i64>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub bbox_annotation_format: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub document_annotation_format: Option<Value>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub document_annotation_prompt: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub extract_header: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub extract_footer: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub table_format: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub confidence_scores_granularity: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub include_blocks: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub id: Option<String>,
|
||||
}
|
||||
use crate::params::OpaqueParams;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub(crate) struct MistralOcrRequest {
|
||||
pub model: String,
|
||||
pub document: OcrDocument,
|
||||
#[serde(flatten)]
|
||||
pub params: MistralOcrParams,
|
||||
pub params: OpaqueParams,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
|
|
@ -63,7 +27,7 @@ pub(crate) struct MistralOcrResponse {
|
|||
pub(crate) fn transform_ocr_request(
|
||||
model: &str,
|
||||
document: OcrDocument,
|
||||
params: &MistralOcrParams,
|
||||
params: &OpaqueParams,
|
||||
) -> Result<MistralOcrRequest, OcrRequestError> {
|
||||
Ok(MistralOcrRequest {
|
||||
model: model.to_string(),
|
||||
|
|
@ -94,7 +58,8 @@ mod mapping_tests {
|
|||
use serde_json::{Value, json};
|
||||
|
||||
fn mapped_params(value: Value) -> Value {
|
||||
serde_json::to_value(serde_json::from_value::<MistralOcrParams>(value).unwrap()).unwrap()
|
||||
let params = serde_json::from_value::<OpaqueParams>(value).unwrap();
|
||||
serde_json::to_value(MistralOCRConfig.map_ocr_params("model", ¶ms)).unwrap()
|
||||
}
|
||||
|
||||
fn document() -> OcrDocument {
|
||||
|
|
@ -168,6 +133,16 @@ mod mapping_tests {
|
|||
assert!(mapped.get("unsupported_param").is_none());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
fn map_ocr_params_preserves_unvalidated_values_and_explicit_null() {
|
||||
let mapped = mapped_params(json!({
|
||||
"pages":{"future":"shape"},
|
||||
"include_image_base64":null
|
||||
}));
|
||||
assert_eq!(mapped["pages"], json!({"future":"shape"}));
|
||||
assert!(mapped.get("include_image_base64").unwrap().is_null());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
#[case("table_format", json!("html"))]
|
||||
#[case("confidence_scores_granularity", json!("word"))]
|
||||
|
|
@ -205,8 +180,7 @@ mod mapping_tests {
|
|||
#[case("include_blocks", json!(true))]
|
||||
#[case("id", json!("req-123"))]
|
||||
fn request_mapping_matches_python(#[case] name: &str, #[case] value: Value) {
|
||||
let params: MistralOcrParams =
|
||||
serde_json::from_value(json!({name: value.clone()})).unwrap();
|
||||
let params: OpaqueParams = serde_json::from_value(json!({name: value.clone()})).unwrap();
|
||||
let result =
|
||||
serde_json::to_value(transform_ocr_request("model", document(), ¶ms).unwrap())
|
||||
.unwrap();
|
||||
|
|
@ -226,7 +200,7 @@ mod mapping_tests {
|
|||
#[case] name: &str,
|
||||
#[case] value: Value,
|
||||
) {
|
||||
let params: MistralOcrParams = serde_json::from_value(json!({name:value.clone()})).unwrap();
|
||||
let params: OpaqueParams = serde_json::from_value(json!({name:value.clone()})).unwrap();
|
||||
let result = serde_json::to_value(
|
||||
transform_ocr_request("mistral-ocr-latest", document(), ¶ms).unwrap(),
|
||||
)
|
||||
|
|
@ -237,7 +211,7 @@ mod mapping_tests {
|
|||
|
||||
#[rstest]
|
||||
fn transform_ocr_request_includes_multiple_new_params() {
|
||||
let params: MistralOcrParams = serde_json::from_value(json!({
|
||||
let params: OpaqueParams = serde_json::from_value(json!({
|
||||
"table_format":"html",
|
||||
"confidence_scores_granularity":"page",
|
||||
"extract_header":true
|
||||
|
|
@ -312,9 +286,7 @@ use crate::constants::MISTRAL_OCR_API_BASE;
|
|||
use crate::llms::base_llm::ocr::transformation::BaseOcrConfig;
|
||||
use crate::ocr::OcrClient;
|
||||
use crate::ocr::error::OcrError;
|
||||
use crate::ocr::prepare::{
|
||||
_prepare_ocr_request, ParsedProviderParams, credential_env, transform_request_body,
|
||||
};
|
||||
use crate::ocr::prepare::{credential_env, transform_request_body};
|
||||
use crate::ocr::types::{LiteLLMOcrRequest, OcrConnection};
|
||||
use crate::url_utils::ApiUrl;
|
||||
|
||||
|
|
@ -328,7 +300,7 @@ impl MistralOCRConfig {
|
|||
&self,
|
||||
model: &str,
|
||||
document: crate::ocr::types::OcrDocument,
|
||||
params: &MistralOcrParams,
|
||||
params: &OpaqueParams,
|
||||
) -> Result<MistralOcrRequest, OcrRequestError> {
|
||||
transform_ocr_request(model, document, params)
|
||||
}
|
||||
|
|
@ -337,15 +309,30 @@ impl MistralOCRConfig {
|
|||
impl BaseOcrConfig for MistralOCRConfig {
|
||||
type ProviderResponse = MistralOcrResponse;
|
||||
|
||||
fn get_supported_ocr_params(&self, _model: &str) -> &'static [&'static str] {
|
||||
&[
|
||||
"pages",
|
||||
"include_image_base64",
|
||||
"image_limit",
|
||||
"image_min_size",
|
||||
"bbox_annotation_format",
|
||||
"document_annotation_format",
|
||||
"document_annotation_prompt",
|
||||
"extract_header",
|
||||
"extract_footer",
|
||||
"table_format",
|
||||
"confidence_scores_granularity",
|
||||
"include_blocks",
|
||||
"id",
|
||||
]
|
||||
}
|
||||
|
||||
async fn prepare_request(
|
||||
&self,
|
||||
request: &LiteLLMOcrRequest,
|
||||
client: &OcrClient,
|
||||
) -> Result<reqwest::Request, OcrError> {
|
||||
let ParsedProviderParams {
|
||||
known: params,
|
||||
extra_params: _extra_params,
|
||||
} = _prepare_ocr_request::<MistralOcrParams>(request)?;
|
||||
let params = self.map_ocr_params(&request.model, &request.optional_params);
|
||||
let headers = validate_environment(&request.connection, &credential_env)?;
|
||||
let url = get_complete_url(request.connection.api_base.as_deref())?;
|
||||
let body = self.transform_ocr_request(&request.model, request.document.clone(), ¶ms)?;
|
||||
|
|
|
|||
|
|
@ -446,6 +446,10 @@ mod legacy {
|
|||
impl BaseOcrConfig for ReductoParseLegacyConfig {
|
||||
type ProviderResponse = ReductoResponse;
|
||||
|
||||
fn get_supported_ocr_params(&self, _model: &str) -> &'static [&'static str] {
|
||||
&["enhance"]
|
||||
}
|
||||
|
||||
async fn prepare_request(
|
||||
&self,
|
||||
request: &LiteLLMOcrRequest,
|
||||
|
|
@ -499,6 +503,10 @@ mod v3 {
|
|||
impl BaseOcrConfig for ReductoParseV3Config {
|
||||
type ProviderResponse = ReductoResponse;
|
||||
|
||||
fn get_supported_ocr_params(&self, _model: &str) -> &'static [&'static str] {
|
||||
&["formatting", "retrieval", "settings"]
|
||||
}
|
||||
|
||||
async fn prepare_request(
|
||||
&self,
|
||||
request: &LiteLLMOcrRequest,
|
||||
|
|
|
|||
|
|
@ -229,6 +229,10 @@ pub(crate) struct VertexAIDeepSeekOCRConfig;
|
|||
impl BaseOcrConfig for VertexAIDeepSeekOCRConfig {
|
||||
type ProviderResponse = DeepSeekOcrResponse;
|
||||
|
||||
fn get_supported_ocr_params(&self, _model: &str) -> &'static [&'static str] {
|
||||
&["stream", "temperature", "max_tokens", "top_p", "n", "stop"]
|
||||
}
|
||||
|
||||
async fn prepare_request(
|
||||
&self,
|
||||
request: &LiteLLMOcrRequest,
|
||||
|
|
|
|||
|
|
@ -2,14 +2,12 @@ use super::common_utils::validate_destination;
|
|||
use crate::Error;
|
||||
use crate::auth::vertex::{self, VertexConfig};
|
||||
use crate::llms::base_llm::ocr::transformation::BaseOcrConfig;
|
||||
use crate::llms::mistral::ocr::MistralOcrResponse;
|
||||
use crate::llms::mistral::ocr::transformation::MistralOCRConfig;
|
||||
use crate::llms::mistral::ocr::{MistralOcrParams, MistralOcrResponse};
|
||||
use crate::ocr::OcrClient;
|
||||
use crate::ocr::document::{inline_remote_document, validate_inline_document};
|
||||
use crate::ocr::error::{OcrError, OcrRequestError, OcrResponseError};
|
||||
use crate::ocr::prepare::{
|
||||
_prepare_ocr_request, ParsedProviderParams, credential_env, transform_request_body,
|
||||
};
|
||||
use crate::ocr::prepare::{credential_env, transform_request_body};
|
||||
use crate::ocr::types::{LiteLLMOcrRequest, LiteLLMOcrResponse};
|
||||
use crate::url_utils::ApiUrl;
|
||||
const DEFAULT_LOCATION: &str = "us-central1";
|
||||
|
|
@ -20,16 +18,17 @@ pub(crate) struct VertexAIOCRConfig;
|
|||
impl BaseOcrConfig for VertexAIOCRConfig {
|
||||
type ProviderResponse = MistralOcrResponse;
|
||||
|
||||
fn get_supported_ocr_params(&self, model: &str) -> &'static [&'static str] {
|
||||
MistralOCRConfig.get_supported_ocr_params(model)
|
||||
}
|
||||
|
||||
async fn prepare_request(
|
||||
&self,
|
||||
request: &LiteLLMOcrRequest,
|
||||
client: &OcrClient,
|
||||
) -> Result<reqwest::Request, OcrError> {
|
||||
validate_destination(&request.connection)?;
|
||||
let ParsedProviderParams {
|
||||
known: params,
|
||||
extra_params: _extra_params,
|
||||
} = _prepare_ocr_request::<MistralOcrParams>(request)?;
|
||||
let params = self.map_ocr_params(&request.model, &request.optional_params);
|
||||
let config = VertexConfig::from_sourced_optional_params(
|
||||
&request.optional_params,
|
||||
&request.input_sources,
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ impl CallLifecycleHooks<LiteLLMOcrRequest, LiteLLMOcrRequest, LiteLLMOcrResponse
|
|||
model: request.model.clone(),
|
||||
custom_llm_provider: self.provider_name.clone(),
|
||||
document: request.document,
|
||||
optional_params: Value::Object(request.optional_params),
|
||||
optional_params: Value::Object(request.optional_params.into()),
|
||||
})
|
||||
.await?;
|
||||
let Value::Object(optional_params) = changed.optional_params else {
|
||||
|
|
@ -111,7 +111,7 @@ impl CallLifecycleHooks<LiteLLMOcrRequest, LiteLLMOcrRequest, LiteLLMOcrResponse
|
|||
};
|
||||
Ok(LiteLLMOcrRequest {
|
||||
document: changed.document,
|
||||
optional_params,
|
||||
optional_params: optional_params.into(),
|
||||
..request
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub(crate) fn _prepare_ocr_request<T: DeserializeOwned>(
|
|||
request: &LiteLLMOcrRequest,
|
||||
) -> Result<ParsedProviderParams<T>, OcrRequestError> {
|
||||
super::wire::decode_request_value(
|
||||
Value::Object(request.optional_params.clone()),
|
||||
Value::Object(request.optional_params.clone().into()),
|
||||
"optional_params",
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,13 @@
|
|||
use crate::Error;
|
||||
use crate::llms::azure_ai::ocr::cohere_parse_transformation::AzureAICohereParseConfig;
|
||||
use crate::llms::azure_ai::ocr::document_intelligence::transformation::AzureDocumentIntelligenceOCRConfig;
|
||||
use crate::llms::azure_ai::ocr::transformation::AzureAIOCRConfig;
|
||||
use crate::llms::base_llm::ocr::transformation::BaseOcrConfig;
|
||||
use crate::llms::cohere::ocr::transformation::CohereParseConfig;
|
||||
use crate::llms::mistral::ocr::transformation::MistralOCRConfig;
|
||||
use crate::llms::reducto::ocr::transformation::{ReductoParseLegacyConfig, ReductoParseV3Config};
|
||||
use crate::llms::vertex_ai::ocr::deepseek_transformation::VertexAIDeepSeekOCRConfig;
|
||||
use crate::llms::vertex_ai::ocr::transformation::VertexAIOCRConfig;
|
||||
use crate::routing_utils::provider::{CustomLlmProvider, get_custom_llm_provider};
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
|
|
@ -26,6 +35,24 @@ impl OcrConfigKind {
|
|||
Self::VertexAi | Self::VertexDeepSeek => OcrProvider::VertexAi,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_supported_ocr_params(self, model: &str) -> &'static [&'static str] {
|
||||
match self {
|
||||
Self::Cohere => CohereParseConfig.get_supported_ocr_params(model),
|
||||
Self::Mistral => MistralOCRConfig.get_supported_ocr_params(model),
|
||||
Self::AzureAi => AzureAIOCRConfig::default().get_supported_ocr_params(model),
|
||||
Self::AzureCohere => {
|
||||
AzureAICohereParseConfig::default().get_supported_ocr_params(model)
|
||||
}
|
||||
Self::AzureDocumentIntelligence => {
|
||||
AzureDocumentIntelligenceOCRConfig.get_supported_ocr_params(model)
|
||||
}
|
||||
Self::ReductoLegacy => ReductoParseLegacyConfig.get_supported_ocr_params(model),
|
||||
Self::ReductoV3 => ReductoParseV3Config.get_supported_ocr_params(model),
|
||||
Self::VertexAi => VertexAIOCRConfig::default().get_supported_ocr_params(model),
|
||||
Self::VertexDeepSeek => VertexAIDeepSeekOCRConfig.get_supported_ocr_params(model),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ use super::provider_config::{OcrConfigKind, resolve_provider_config};
|
|||
use crate::Error;
|
||||
use crate::auth::{InputSource, TokenProviderHandle};
|
||||
use crate::constants::OCR_HTTP_TIMEOUT_SECS;
|
||||
use crate::params::OpaqueParams;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(tag = "type")]
|
||||
|
|
@ -95,7 +96,7 @@ pub struct LiteLLMOcrRequest {
|
|||
pub connection: OcrConnection,
|
||||
pub hooks: Arc<dyn OcrHooks>,
|
||||
pub litellm_call_id: Option<String>,
|
||||
pub optional_params: Map<String, Value>,
|
||||
pub optional_params: OpaqueParams,
|
||||
pub input_sources: BTreeMap<String, InputSource>,
|
||||
pub azure_ad_token_provider: Option<TokenProviderHandle>,
|
||||
pub(crate) config: OcrConfigKind,
|
||||
|
|
@ -106,7 +107,7 @@ impl LiteLLMOcrRequest {
|
|||
model: String,
|
||||
document: OcrDocument,
|
||||
custom_llm_provider: Option<&str>,
|
||||
optional_params: Map<String, Value>,
|
||||
optional_params: OpaqueParams,
|
||||
) -> Result<Self, Error> {
|
||||
let (model, config) = resolve_provider_config(&model, custom_llm_provider)?;
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use std::time::Duration;
|
|||
use super::types::{LiteLLMOcrRequest, OcrConnection, OcrDocument};
|
||||
use crate::Error;
|
||||
use crate::auth::InputSource;
|
||||
use crate::params::OpaqueParams;
|
||||
use serde::{
|
||||
Deserialize,
|
||||
de::{DeserializeOwned, IntoDeserializer},
|
||||
|
|
@ -13,26 +14,6 @@ use serde::{
|
|||
use serde_json::{Map, Value};
|
||||
|
||||
const COMMON_OPTION_FIELDS: &[&str] = &["req_format", "extra_body", "max_response_bytes"];
|
||||
const MISTRAL_OPTION_FIELDS: &[&str] = &[
|
||||
"pages",
|
||||
"include_image_base64",
|
||||
"image_limit",
|
||||
"image_min_size",
|
||||
"bbox_annotation_format",
|
||||
"document_annotation_format",
|
||||
"document_annotation_prompt",
|
||||
"extract_header",
|
||||
"extract_footer",
|
||||
"table_format",
|
||||
"confidence_scores_granularity",
|
||||
"include_blocks",
|
||||
"id",
|
||||
];
|
||||
const DEEPSEEK_OPTION_FIELDS: &[&str] =
|
||||
&["stream", "temperature", "max_tokens", "top_p", "n", "stop"];
|
||||
const DOCUMENT_INTELLIGENCE_OPTION_FIELDS: &[&str] = &["pages", "features"];
|
||||
const REDUCTO_V3_OPTION_FIELDS: &[&str] = &["formatting", "retrieval", "settings"];
|
||||
const REDUCTO_LEGACY_OPTION_FIELDS: &[&str] = &["enhance"];
|
||||
const AZURE_AUTH_OPTION_FIELDS: &[&str] = &[
|
||||
"azure_ad_token",
|
||||
"tenant_id",
|
||||
|
|
@ -76,7 +57,7 @@ pub struct OcrWireRequest {
|
|||
pub custom_llm_provider: Option<String>,
|
||||
pub extra_headers: Option<Map<String, Value>>,
|
||||
#[serde(default)]
|
||||
pub optional_params: Map<String, Value>,
|
||||
pub optional_params: OpaqueParams,
|
||||
#[serde(default)]
|
||||
pub input_sources: BTreeMap<String, InputSource>,
|
||||
pub timeout_seconds: Option<f64>,
|
||||
|
|
@ -92,17 +73,9 @@ pub fn consumed_optional_param_names(
|
|||
) -> Result<Vec<&'static str>, Error> {
|
||||
use super::provider_config::OcrConfigKind;
|
||||
|
||||
let (_, config) = super::provider_config::resolve_provider_config(model, custom_llm_provider)?;
|
||||
let provider_fields: &[&str] = match config {
|
||||
OcrConfigKind::Cohere | OcrConfigKind::AzureCohere => &["output_format"],
|
||||
OcrConfigKind::Mistral | OcrConfigKind::AzureAi | OcrConfigKind::VertexAi => {
|
||||
MISTRAL_OPTION_FIELDS
|
||||
}
|
||||
OcrConfigKind::AzureDocumentIntelligence => DOCUMENT_INTELLIGENCE_OPTION_FIELDS,
|
||||
OcrConfigKind::ReductoV3 => REDUCTO_V3_OPTION_FIELDS,
|
||||
OcrConfigKind::ReductoLegacy => REDUCTO_LEGACY_OPTION_FIELDS,
|
||||
OcrConfigKind::VertexDeepSeek => DEEPSEEK_OPTION_FIELDS,
|
||||
};
|
||||
let (provider_model, config) =
|
||||
super::provider_config::resolve_provider_config(model, custom_llm_provider)?;
|
||||
let provider_fields = config.get_supported_ocr_params(&provider_model);
|
||||
let auth_fields: &[&str] = match config {
|
||||
OcrConfigKind::AzureAi
|
||||
| OcrConfigKind::AzureDocumentIntelligence
|
||||
|
|
|
|||
87
litellm-rust/crates/core/src/params.rs
Normal file
87
litellm-rust/crates/core/src/params.rs
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
use std::ops::Deref;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct OpaqueParams(Map<String, Value>);
|
||||
|
||||
impl OpaqueParams {
|
||||
pub fn into_inner(self) -> Map<String, Value> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn retain_supported(&self, supported: &[&str]) -> Self {
|
||||
Self(
|
||||
self.iter()
|
||||
.filter(|(name, _)| supported.contains(&name.as_str()))
|
||||
.map(|(name, value)| (name.clone(), value.clone()))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for OpaqueParams {
|
||||
type Target = Map<String, Value>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Map<String, Value>> for OpaqueParams {
|
||||
fn from(value: Map<String, Value>) -> Self {
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OpaqueParams> for Map<String, Value> {
|
||||
fn from(value: OpaqueParams) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<(String, Value)> for OpaqueParams {
|
||||
fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
|
||||
Self(iter.into_iter().collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for OpaqueParams {
|
||||
type Item = (String, Value);
|
||||
type IntoIter = serde_json::map::IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.0.into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json::json;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn supported_keys_preserve_opaque_values() {
|
||||
let params: OpaqueParams = serde_json::from_value(json!({
|
||||
"object": {"future": [1, null]},
|
||||
"null": null,
|
||||
"unsupported": true
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
let retained = params.retain_supported(&["object", "null"]);
|
||||
|
||||
assert_eq!(
|
||||
serde_json::to_value(retained).unwrap(),
|
||||
json!({"object": {"future": [1, null]}, "null": null})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn outer_value_must_be_an_object() {
|
||||
assert!(serde_json::from_value::<OpaqueParams>(json!(["value"])).is_err());
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,9 @@ fn messages(value: Value) -> Vec<ChatMessage> {
|
|||
serde_json::from_value(value).expect("valid messages")
|
||||
}
|
||||
|
||||
fn params(value: Value) -> Map<String, Value> {
|
||||
fn params(value: Value) -> OpaqueParams {
|
||||
match value {
|
||||
Value::Object(map) => map,
|
||||
Value::Object(map) => map.into(),
|
||||
other => panic!("params must be an object, got {other}"),
|
||||
}
|
||||
}
|
||||
|
|
@ -419,13 +419,20 @@ fn resolves_the_messages_url_and_x_api_key_auth() {
|
|||
let config = &ANTHROPIC_CHAT_COMPLETIONS_CONFIG;
|
||||
assert_eq!(
|
||||
config
|
||||
.complete_url(None, "claude-sonnet-4-5", &Map::new(), &|_| None)
|
||||
.complete_url(None, "claude-sonnet-4-5", &OpaqueParams::default(), &|_| {
|
||||
None
|
||||
})
|
||||
.expect("url builds"),
|
||||
"https://api.anthropic.com/v1/messages"
|
||||
);
|
||||
assert_eq!(
|
||||
config
|
||||
.auth(Some("sk-x"), "claude-sonnet-4-5", &Map::new(), &|_| None)
|
||||
.auth(
|
||||
Some("sk-x"),
|
||||
"claude-sonnet-4-5",
|
||||
&OpaqueParams::default(),
|
||||
&|_| None,
|
||||
)
|
||||
.expect("auth resolves"),
|
||||
ChatCompletionsAuth::Header {
|
||||
name: "x-api-key",
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use crate::chat_completions::types::{
|
|||
};
|
||||
use crate::constants::ANTHROPIC_OAUTH_TOKEN_PREFIX;
|
||||
use crate::error::Error;
|
||||
use crate::params::OpaqueParams;
|
||||
use crate::providers::anthropic::messages::transformation::{
|
||||
complete_anthropic_url, resolve_anthropic_api_key,
|
||||
};
|
||||
|
|
@ -77,7 +78,7 @@ impl ChatCompletionsProviderConfig for AnthropicChatCompletionsConfig {
|
|||
&self,
|
||||
api_base: Option<&str>,
|
||||
_model: &str,
|
||||
_optional_params: &Map<String, Value>,
|
||||
_optional_params: &OpaqueParams,
|
||||
env_lookup: &dyn Fn(&str) -> Option<String>,
|
||||
) -> Result<String, Error> {
|
||||
Ok(complete_anthropic_url(api_base, env_lookup))
|
||||
|
|
@ -87,7 +88,7 @@ impl ChatCompletionsProviderConfig for AnthropicChatCompletionsConfig {
|
|||
&self,
|
||||
api_key: Option<&str>,
|
||||
_model: &str,
|
||||
_optional_params: &Map<String, Value>,
|
||||
_optional_params: &OpaqueParams,
|
||||
env_lookup: &dyn Fn(&str) -> Option<String>,
|
||||
) -> Result<ChatCompletionsAuth, Error> {
|
||||
Ok(ChatCompletionsAuth::Header {
|
||||
|
|
@ -124,7 +125,7 @@ impl ChatCompletionsProviderConfig for AnthropicChatCompletionsConfig {
|
|||
fn unsupported_reason(
|
||||
&self,
|
||||
messages: &[ChatMessage],
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
) -> Option<Unsupported> {
|
||||
unsupported_param(self.supported_openai_params(), &[], optional_params)
|
||||
.or_else(|| messages.iter().find_map(unsupported_message))
|
||||
|
|
@ -141,10 +142,14 @@ impl ChatCompletionsProviderConfig for AnthropicChatCompletionsConfig {
|
|||
&self,
|
||||
model: &str,
|
||||
messages: Vec<ChatMessage>,
|
||||
optional_params: Map<String, Value>,
|
||||
optional_params: OpaqueParams,
|
||||
) -> Result<ProviderChatRequestData, Error> {
|
||||
Ok(ProviderChatRequestData {
|
||||
body: anthropic_body(model, &build_conversation(&messages), optional_params),
|
||||
body: anthropic_body(
|
||||
model,
|
||||
&build_conversation(&messages),
|
||||
optional_params.into_inner(),
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use crate::audio_transcription::types::{
|
|||
AudioTranscriptionRequestData, AudioTranscriptionResponseData,
|
||||
};
|
||||
use crate::error::{Error, json_type_name};
|
||||
use crate::params::OpaqueParams;
|
||||
|
||||
pub use super::aws_base::{aws_auth_config, bedrock_model_id_and_region, resolve_bedrock_region};
|
||||
use super::constants::{BEDROCK_RUNTIME_ENDPOINT_TEMPLATE, BEDROCK_SERVICE};
|
||||
|
|
@ -54,7 +55,7 @@ impl AudioTranscriptionProviderConfig for BedrockAudioTranscriptionConfig {
|
|||
&self,
|
||||
_model: &str,
|
||||
audio: Value,
|
||||
optional_params: Map<String, Value>,
|
||||
optional_params: OpaqueParams,
|
||||
) -> Result<AudioTranscriptionRequestData, Error> {
|
||||
let (data, format) = audio_fields(audio)?;
|
||||
let mut instruction = "Transcribe the audio. Respond with only the transcript.".to_string();
|
||||
|
|
@ -109,7 +110,7 @@ impl AudioTranscriptionProviderConfig for BedrockAudioTranscriptionConfig {
|
|||
&self,
|
||||
api_base: Option<&str>,
|
||||
model: &str,
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
env_lookup: &dyn Fn(&str) -> Option<String>,
|
||||
) -> Result<String, Error> {
|
||||
let (model_id, model_region) = bedrock_model_id_and_region(model);
|
||||
|
|
@ -131,7 +132,7 @@ impl AudioTranscriptionProviderConfig for BedrockAudioTranscriptionConfig {
|
|||
fn auth_strategy(
|
||||
&self,
|
||||
model: &str,
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
env_lookup: &dyn Fn(&str) -> Option<String>,
|
||||
) -> Result<AudioTranscriptionAuth, Error> {
|
||||
let (_, model_region) = bedrock_model_id_and_region(model);
|
||||
|
|
@ -152,12 +153,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn request_matches_python_shape() {
|
||||
let params = Map::from_iter([
|
||||
let params = OpaqueParams::from(Map::from_iter([
|
||||
("language".to_string(), json!("en")),
|
||||
("prompt".to_string(), json!("Speaker names")),
|
||||
("temperature".to_string(), json!(0)),
|
||||
("timestamp_granularities".to_string(), json!(["word"])),
|
||||
]);
|
||||
]));
|
||||
let params = BEDROCK_AUDIO_TRANSCRIPTION_CONFIG.map_transcription_params(¶ms);
|
||||
let result = BEDROCK_AUDIO_TRANSCRIPTION_CONFIG
|
||||
.transform_transcription_request(
|
||||
|
|
@ -199,14 +200,17 @@ mod tests {
|
|||
let result = BEDROCK_AUDIO_TRANSCRIPTION_CONFIG.transform_transcription_request(
|
||||
"model",
|
||||
json!({"data": "AQI="}),
|
||||
Map::new(),
|
||||
OpaqueParams::default(),
|
||||
);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn region_and_url_precedence_match_python() {
|
||||
let params = Map::from_iter([("aws_region_name".to_string(), json!("eu-west-1"))]);
|
||||
let params = OpaqueParams::from(Map::from_iter([(
|
||||
"aws_region_name".to_string(),
|
||||
json!("eu-west-1"),
|
||||
)]));
|
||||
let url = BEDROCK_AUDIO_TRANSCRIPTION_CONFIG
|
||||
.complete_url(
|
||||
None,
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ fn messages(value: Value) -> Vec<ChatMessage> {
|
|||
serde_json::from_value(value).expect("valid messages")
|
||||
}
|
||||
|
||||
fn params(value: Value) -> Map<String, Value> {
|
||||
fn params(value: Value) -> OpaqueParams {
|
||||
match value {
|
||||
Value::Object(map) => map,
|
||||
Value::Object(map) => map.into(),
|
||||
other => panic!("params must be an object, got {other}"),
|
||||
}
|
||||
}
|
||||
|
|
@ -225,9 +225,12 @@ fn builds_the_converse_url_from_the_region_in_the_model_id() {
|
|||
let config = &BEDROCK_CHAT_COMPLETIONS_CONFIG;
|
||||
assert_eq!(
|
||||
config
|
||||
.complete_url(None, "us-east-1/anthropic.claude-v2", &Map::new(), &|_| {
|
||||
None
|
||||
})
|
||||
.complete_url(
|
||||
None,
|
||||
"us-east-1/anthropic.claude-v2",
|
||||
&OpaqueParams::default(),
|
||||
&|_| { None }
|
||||
)
|
||||
.expect("url builds"),
|
||||
"https://bedrock-runtime.us-east-1.amazonaws.com/model/anthropic.claude-v2/converse"
|
||||
);
|
||||
|
|
@ -239,13 +242,23 @@ fn falls_back_to_the_region_env_then_the_default_region() {
|
|||
let with_env = |key: &str| (key == "AWS_REGION_NAME").then(|| "eu-west-1".to_string());
|
||||
assert_eq!(
|
||||
config
|
||||
.complete_url(None, "anthropic.claude-v2", &Map::new(), &with_env)
|
||||
.complete_url(
|
||||
None,
|
||||
"anthropic.claude-v2",
|
||||
&OpaqueParams::default(),
|
||||
&with_env
|
||||
)
|
||||
.expect("url builds"),
|
||||
"https://bedrock-runtime.eu-west-1.amazonaws.com/model/anthropic.claude-v2/converse"
|
||||
);
|
||||
assert_eq!(
|
||||
config
|
||||
.complete_url(None, "anthropic.claude-v2", &Map::new(), &|_| None)
|
||||
.complete_url(
|
||||
None,
|
||||
"anthropic.claude-v2",
|
||||
&OpaqueParams::default(),
|
||||
&|_| None
|
||||
)
|
||||
.expect("url builds"),
|
||||
"https://bedrock-runtime.us-west-2.amazonaws.com/model/anthropic.claude-v2/converse"
|
||||
);
|
||||
|
|
@ -276,7 +289,7 @@ fn signs_with_sigv4_in_the_resolved_region() {
|
|||
.auth(
|
||||
None,
|
||||
"eu-central-1/anthropic.claude-v2",
|
||||
&Map::new(),
|
||||
&OpaqueParams::default(),
|
||||
&|_| None
|
||||
)
|
||||
.expect("auth resolves"),
|
||||
|
|
@ -300,7 +313,7 @@ fn a_bearer_token_outranks_sigv4_the_way_python_resolves_it() {
|
|||
.auth(
|
||||
api_key,
|
||||
"eu-central-1/anthropic.claude-v2",
|
||||
&Map::new(),
|
||||
&OpaqueParams::default(),
|
||||
env,
|
||||
)
|
||||
.expect("auth resolves")
|
||||
|
|
@ -542,7 +555,7 @@ fn leaves_a_complete_converse_url_untouched() {
|
|||
.complete_url(
|
||||
Some(already_built),
|
||||
"anthropic.claude-v2",
|
||||
&Map::new(),
|
||||
&OpaqueParams::default(),
|
||||
&|_| None
|
||||
)
|
||||
.expect("url builds"),
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ use crate::chat_completions::types::{
|
|||
ProviderChatResponseData,
|
||||
};
|
||||
use crate::error::Error;
|
||||
use crate::params::OpaqueParams;
|
||||
|
||||
use super::super::aws_base::{bedrock_model_id_and_region, resolve_bedrock_region};
|
||||
use super::super::constants::{AWS_BEARER_TOKEN_BEDROCK, BEDROCK_RUNTIME_ENDPOINT_TEMPLATE};
|
||||
|
|
@ -109,7 +110,7 @@ impl ChatCompletionsProviderConfig for BedrockChatCompletionsConfig {
|
|||
&self,
|
||||
api_base: Option<&str>,
|
||||
model: &str,
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
env_lookup: &dyn Fn(&str) -> Option<String>,
|
||||
) -> Result<String, Error> {
|
||||
let (model_id, model_region) = bedrock_model_id_and_region(model);
|
||||
|
|
@ -136,7 +137,7 @@ impl ChatCompletionsProviderConfig for BedrockChatCompletionsConfig {
|
|||
&self,
|
||||
api_key: Option<&str>,
|
||||
model: &str,
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
env_lookup: &dyn Fn(&str) -> Option<String>,
|
||||
) -> Result<ChatCompletionsAuth, Error> {
|
||||
// Python reads `api_key` as the Bedrock bearer token and consults the
|
||||
|
|
@ -174,7 +175,7 @@ impl ChatCompletionsProviderConfig for BedrockChatCompletionsConfig {
|
|||
fn unsupported_reason(
|
||||
&self,
|
||||
messages: &[ChatMessage],
|
||||
optional_params: &Map<String, Value>,
|
||||
optional_params: &OpaqueParams,
|
||||
) -> Option<Unsupported> {
|
||||
unsupported_param(
|
||||
self.supported_openai_params(),
|
||||
|
|
@ -212,7 +213,7 @@ impl ChatCompletionsProviderConfig for BedrockChatCompletionsConfig {
|
|||
&self,
|
||||
_model: &str,
|
||||
messages: Vec<ChatMessage>,
|
||||
optional_params: Map<String, Value>,
|
||||
optional_params: OpaqueParams,
|
||||
) -> Result<ProviderChatRequestData, Error> {
|
||||
Ok(ProviderChatRequestData {
|
||||
body: converse_body(&build_conversation(&messages), &optional_params),
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ async fn rejects_invalid_pages_features_and_format() {
|
|||
api_base: Some("http://127.0.0.1:1".into()),
|
||||
custom_llm_provider: None,
|
||||
extra_headers: None,
|
||||
optional_params: options.as_object().unwrap().clone(),
|
||||
optional_params: options.as_object().unwrap().clone().into(),
|
||||
input_sources: Default::default(),
|
||||
timeout_seconds: None,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ fn request_boundary_selects_mistral_and_rejects_unknown_providers() {
|
|||
optional_params: json!({"extract_header":true,"unknown":42})
|
||||
.as_object()
|
||||
.unwrap()
|
||||
.clone(),
|
||||
.clone()
|
||||
.into(),
|
||||
input_sources: Default::default(),
|
||||
timeout_seconds: None,
|
||||
};
|
||||
|
|
@ -40,7 +41,7 @@ fn request_boundary_selects_mistral_and_rejects_unknown_providers() {
|
|||
api_base: None,
|
||||
custom_llm_provider: Some("unknown".into()),
|
||||
extra_headers: None,
|
||||
optional_params: serde_json::Map::new(),
|
||||
optional_params: Default::default(),
|
||||
input_sources: Default::default(),
|
||||
timeout_seconds: None,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ pub(crate) fn wire_request(model: &str, base: &str, options: Value) -> LiteLLMOc
|
|||
api_base: Some(base.into()),
|
||||
custom_llm_provider: None,
|
||||
extra_headers: None,
|
||||
optional_params: options.as_object().unwrap().clone(),
|
||||
optional_params: options.as_object().unwrap().clone().into(),
|
||||
input_sources: Default::default(),
|
||||
timeout_seconds: Some(2.0),
|
||||
})
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ fn prepare_transcription(
|
|||
api_base: api_base.as_deref(),
|
||||
custom_llm_provider: custom_llm_provider.as_deref(),
|
||||
extra_headers,
|
||||
optional_params,
|
||||
optional_params: optional_params.into(),
|
||||
timeout,
|
||||
})
|
||||
.await
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ fn prepare_chat_completions(
|
|||
run_chat_completions(ChatCompletionsRequest {
|
||||
model: &model,
|
||||
messages: Value::Array(messages),
|
||||
optional_params,
|
||||
optional_params: optional_params.into(),
|
||||
api_key: api_key.as_deref(),
|
||||
api_base: api_base.as_deref(),
|
||||
custom_llm_provider: custom_llm_provider.as_deref(),
|
||||
|
|
@ -56,7 +56,7 @@ fn chat_completions_decline(
|
|||
#[pyo3(from_py_with = litellm_python_interop::from_py)] optional_params: Option<Value>,
|
||||
custom_llm_provider: Option<String>,
|
||||
) -> PyResult<Option<String>> {
|
||||
let optional_params = object_or_empty("optional_params", optional_params)?;
|
||||
let optional_params = object_or_empty("optional_params", optional_params)?.into();
|
||||
Ok(chat_completions_decline_reason(
|
||||
&model,
|
||||
custom_llm_provider.as_deref(),
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ pub(super) fn project_request(
|
|||
api_base: arguments.api_base()?,
|
||||
custom_llm_provider,
|
||||
extra_headers: arguments.extra_headers()?,
|
||||
optional_params,
|
||||
optional_params: optional_params.into(),
|
||||
input_sources,
|
||||
timeout_seconds: arguments.timeout_seconds()?,
|
||||
};
|
||||
|
|
@ -500,7 +500,7 @@ kwargs = {'api_key': key}
|
|||
api_base: None,
|
||||
custom_llm_provider: None,
|
||||
extra_headers: None,
|
||||
optional_params: Map::new(),
|
||||
optional_params: Default::default(),
|
||||
input_sources: Default::default(),
|
||||
timeout_seconds: None,
|
||||
}) {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ fn prepare_ocr(
|
|||
api_base,
|
||||
custom_llm_provider,
|
||||
extra_headers,
|
||||
optional_params,
|
||||
optional_params: optional_params.into(),
|
||||
input_sources,
|
||||
timeout_seconds: timeout.map(|value| value.as_secs_f64()),
|
||||
})?;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue