ai_sdk_provider_utils/lib.rs
1//! # AI SDK Provider Utilities
2//!
3//! Common utility functions for implementing AI SDK providers.
4//!
5//! This crate provides shared functionality for provider implementations,
6//! including HTTP header management, request/response handling, and other
7//! cross-cutting concerns. These utilities promote code reuse and consistency
8//! across different provider implementations.
9
10#![warn(missing_docs)]
11#![warn(rustdoc::broken_intra_doc_links)]
12
13use std::collections::HashMap;
14
15/// Merges base headers with optional override headers.
16///
17/// This function combines default headers (typically from provider configuration) with
18/// user-supplied headers from the request. Override headers take precedence when keys
19/// conflict. The base map is consumed to avoid unnecessary allocations.
20///
21/// # Arguments
22///
23/// * `base` - Default headers from provider configuration (consumed)
24/// * `overrides` - Optional request-specific headers that override base headers
25///
26/// # Returns
27///
28/// A merged header map with override values taking precedence over base values
29pub fn merge_headers(
30 mut base: HashMap<String, String>,
31 overrides: Option<&HashMap<String, String>>,
32) -> HashMap<String, String> {
33 if let Some(headers) = overrides {
34 // Extend the base map. If keys exist in both, the 'overrides' value overwrites the 'base' value.
35 base.extend(headers.iter().map(|(k, v)| (k.clone(), v.clone())));
36 }
37 base
38}
39
40/// Merges headers and converts to reqwest HeaderMap.
41///
42/// Convenience function that merges base and override headers, then converts the
43/// result to a `reqwest::header::HeaderMap` suitable for direct use with reqwest
44/// HTTP clients. Invalid header names or values are silently dropped.
45///
46/// # Arguments
47///
48/// * `base` - Default headers from provider configuration (consumed)
49/// * `overrides` - Optional request-specific headers that override base headers
50///
51/// # Returns
52///
53/// A `reqwest::header::HeaderMap` ready for use with reqwest HTTP requests
54///
55/// # Example
56///
57/// ```rust,ignore
58/// use ai_sdk_provider_utils::merge_headers_reqwest;
59/// use std::collections::HashMap;
60///
61/// let mut base_headers = HashMap::new();
62/// base_headers.insert("Authorization".to_string(), "Bearer token".to_string());
63///
64/// let mut custom_headers = HashMap::new();
65/// custom_headers.insert("X-Custom-Header".to_string(), "value".to_string());
66///
67/// let headers = merge_headers_reqwest(base_headers, Some(&custom_headers));
68///
69/// let response = reqwest::Client::new()
70/// .post("https://api.example.com/generate")
71/// .headers(headers)
72/// .json(&request_body)
73/// .send()
74/// .await?;
75/// ```
76#[cfg(feature = "reqwest")]
77pub fn merge_headers_reqwest(
78 base: HashMap<String, String>,
79 overrides: Option<&HashMap<String, String>>,
80) -> reqwest::header::HeaderMap {
81 let merged = merge_headers(base, overrides);
82 let mut header_map = reqwest::header::HeaderMap::new();
83
84 for (key, value) in merged {
85 if let (Ok(name), Ok(val)) = (
86 reqwest::header::HeaderName::from_bytes(key.as_bytes()),
87 reqwest::header::HeaderValue::from_str(&value),
88 ) {
89 header_map.insert(name, val);
90 }
91 }
92
93 header_map
94}
95
96#[cfg(test)]
97mod tests {
98 #[test]
99 fn it_works() {
100 assert_eq!(2 + 2, 4);
101 }
102}