Skip to content

Commit dcccac5

Browse files
authored
Merge pull request #89 from windoze/main
Add `extra_body` field to the request
2 parents c0bce9c + 39377bd commit dcccac5

File tree

8 files changed

+43
-0
lines changed

8 files changed

+43
-0
lines changed

src/backends/cohere.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ impl Cohere {
4848
top_k: Option<u32>,
4949
tools: Option<Vec<Tool>>,
5050
tool_choice: Option<ToolChoice>,
51+
extra_body: Option<serde_json::Value>,
5152
embedding_encoding_format: Option<String>,
5253
embedding_dimensions: Option<u32>,
5354
reasoning_effort: Option<String>,
@@ -70,6 +71,7 @@ impl Cohere {
7071
reasoning_effort,
7172
json_schema,
7273
None, // voice - not supported by Cohere
74+
extra_body,
7375
parallel_tool_calls,
7476
normalize_response,
7577
embedding_encoding_format,

src/backends/groq.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ impl Groq {
5959
top_k: Option<u32>,
6060
tools: Option<Vec<Tool>>,
6161
tool_choice: Option<ToolChoice>,
62+
extra_body: Option<serde_json::Value>,
6263
_embedding_encoding_format: Option<String>,
6364
_embedding_dimensions: Option<u32>,
6465
reasoning_effort: Option<String>,
@@ -81,6 +82,7 @@ impl Groq {
8182
reasoning_effort,
8283
json_schema,
8384
None, // voice - not supported by Groq
85+
extra_body,
8486
parallel_tool_calls,
8587
normalize_response,
8688
None, // embedding_encoding_format - not supported by Groq

src/backends/huggingface.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ impl HuggingFace {
4545
top_k: Option<u32>,
4646
tools: Option<Vec<Tool>>,
4747
tool_choice: Option<ToolChoice>,
48+
extra_body: Option<serde_json::Value>,
4849
_embedding_encoding_format: Option<String>,
4950
_embedding_dimensions: Option<u32>,
5051
reasoning_effort: Option<String>,
@@ -67,6 +68,7 @@ impl HuggingFace {
6768
reasoning_effort,
6869
json_schema,
6970
None, // voice
71+
extra_body,
7072
parallel_tool_calls,
7173
normalize_response,
7274
None, // embedding_encoding_format

src/backends/mistral.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ impl Mistral {
4848
top_k: Option<u32>,
4949
tools: Option<Vec<Tool>>,
5050
tool_choice: Option<ToolChoice>,
51+
extra_body: Option<serde_json::Value>,
5152
embedding_encoding_format: Option<String>,
5253
embedding_dimensions: Option<u32>,
5354
reasoning_effort: Option<String>,
@@ -70,6 +71,7 @@ impl Mistral {
7071
reasoning_effort,
7172
json_schema,
7273
None, // voice - not supported by Mistral
74+
extra_body,
7375
parallel_tool_calls,
7476
normalize_response,
7577
embedding_encoding_format,

src/backends/openai.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ pub struct OpenAIAPIChatRequest<'a> {
166166
pub response_format: Option<OpenAIResponseFormat>,
167167
#[serde(skip_serializing_if = "Option::is_none")]
168168
pub stream_options: Option<OpenAIStreamOptions>,
169+
#[serde(flatten)]
170+
pub extra_body: serde_json::Map<String, serde_json::Value>,
169171
}
170172

171173
impl OpenAI {
@@ -207,6 +209,7 @@ impl OpenAI {
207209
reasoning_effort: Option<String>,
208210
json_schema: Option<StructuredOutputFormat>,
209211
voice: Option<String>,
212+
extra_body: Option<serde_json::Value>,
210213
enable_web_search: Option<bool>,
211214
web_search_context_size: Option<String>,
212215
web_search_user_location_type: Option<String>,
@@ -234,6 +237,7 @@ impl OpenAI {
234237
reasoning_effort,
235238
json_schema,
236239
voice,
240+
extra_body,
237241
None, // parallel_tool_calls
238242
normalize_response,
239243
embedding_encoding_format,
@@ -323,6 +327,7 @@ impl ChatProvider for OpenAI {
323327
reasoning_effort: self.provider.reasoning_effort.clone(),
324328
response_format,
325329
stream_options: None,
330+
extra_body: self.provider.extra_body.clone(),
326331
};
327332
let url = self
328333
.provider
@@ -457,6 +462,7 @@ impl ChatProvider for OpenAI {
457462
stream_options: Some(OpenAIStreamOptions {
458463
include_usage: true,
459464
}),
465+
extra_body: self.provider.extra_body.clone(),
460466
};
461467
let url = self
462468
.provider
@@ -659,6 +665,7 @@ impl OpenAI {
659665
reasoning_effort: self.provider.reasoning_effort.clone(),
660666
response_format: None, // Hosted tools don't use structured output
661667
stream_options: None,
668+
extra_body: self.provider.extra_body.clone(),
662669
};
663670

664671
let url = self

src/backends/openrouter.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ impl OpenRouter {
4545
top_k: Option<u32>,
4646
tools: Option<Vec<Tool>>,
4747
tool_choice: Option<ToolChoice>,
48+
extra_body: Option<serde_json::Value>,
4849
_embedding_encoding_format: Option<String>,
4950
_embedding_dimensions: Option<u32>,
5051
reasoning_effort: Option<String>,
@@ -67,6 +68,7 @@ impl OpenRouter {
6768
reasoning_effort,
6869
json_schema,
6970
None, // voice - not supported by OpenRouter
71+
extra_body,
7072
parallel_tool_calls,
7173
normalize_response,
7274
None, // embedding_encoding_format - not supported by OpenRouter

src/builder.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ pub struct LLMBuilder {
161161
deployment_id: Option<String>,
162162
/// Voice
163163
voice: Option<String>,
164+
/// ExtraBody
165+
extra_body: Option<serde_json::Value>,
164166
/// Search parameters for providers that support search functionality
165167
xai_search_mode: Option<String>,
166168
/// XAI search source type
@@ -394,6 +396,13 @@ impl LLMBuilder {
394396
self
395397
}
396398

399+
/// Set the extra body.
400+
pub fn extra_body(mut self, extra_body: impl serde::Serialize) -> Self {
401+
let value = serde_json::to_value(extra_body).ok();
402+
self.extra_body = value;
403+
self
404+
}
405+
397406
/// Enable web search
398407
pub fn openai_enable_web_search(mut self, enable: bool) -> Self {
399408
self.openai_enable_web_search = Some(enable);
@@ -667,6 +676,7 @@ impl LLMBuilder {
667676
self.reasoning_effort,
668677
self.json_schema,
669678
self.voice,
679+
self.extra_body,
670680
self.openai_enable_web_search,
671681
self.openai_web_search_context_size,
672682
self.openai_web_search_user_location_type,
@@ -876,6 +886,7 @@ impl LLMBuilder {
876886
self.top_k,
877887
self.tools,
878888
self.tool_choice,
889+
self.extra_body,
879890
None, // embedding_encoding_format
880891
None, // embedding_dimensions
881892
None, // reasoning_effort
@@ -910,6 +921,7 @@ impl LLMBuilder {
910921
self.top_k,
911922
self.tools,
912923
self.tool_choice,
924+
self.extra_body,
913925
None, // embedding_encoding_format
914926
None, // embedding_dimensions
915927
None, // reasoning_effort
@@ -946,6 +958,7 @@ impl LLMBuilder {
946958
self.reasoning_effort,
947959
self.json_schema,
948960
None,
961+
self.extra_body,
949962
self.enable_parallel_tool_use,
950963
self.normalize_response,
951964
self.embedding_encoding_format,
@@ -980,6 +993,7 @@ impl LLMBuilder {
980993
self.top_k,
981994
self.tools,
982995
self.tool_choice,
996+
self.extra_body,
983997
None, // embedding_encoding_format
984998
None, // embedding_dimensions
985999
None, // reasoning_effort
@@ -1012,6 +1026,7 @@ impl LLMBuilder {
10121026
self.top_k,
10131027
tools,
10141028
tool_choice,
1029+
self.extra_body,
10151030
self.embedding_encoding_format,
10161031
self.embedding_dimensions,
10171032
self.reasoning_effort,

src/providers/openai_compatible.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ pub struct OpenAICompatibleProvider<T: OpenAIProviderConfig> {
4040
pub reasoning_effort: Option<String>,
4141
pub json_schema: Option<StructuredOutputFormat>,
4242
pub voice: Option<String>,
43+
pub extra_body: serde_json::Map<String, serde_json::Value>,
4344
pub parallel_tool_calls: bool,
4445
pub embedding_encoding_format: Option<String>,
4546
pub embedding_dimensions: Option<u32>,
@@ -135,6 +136,8 @@ pub struct OpenAIChatRequest<'a> {
135136
pub stream_options: Option<OpenAIStreamOptions>,
136137
#[serde(skip_serializing_if = "Option::is_none")]
137138
pub parallel_tool_calls: Option<bool>,
139+
#[serde(flatten)]
140+
pub extra_body: serde_json::Map<String, serde_json::Value>,
138141
}
139142

140143
/// Generic OpenAI-compatible chat response
@@ -306,6 +309,7 @@ impl<T: OpenAIProviderConfig> OpenAICompatibleProvider<T> {
306309
reasoning_effort: Option<String>,
307310
json_schema: Option<StructuredOutputFormat>,
308311
voice: Option<String>,
312+
extra_body: Option<serde_json::Value>,
309313
parallel_tool_calls: Option<bool>,
310314
normalize_response: Option<bool>,
311315
embedding_encoding_format: Option<String>,
@@ -315,6 +319,10 @@ impl<T: OpenAIProviderConfig> OpenAICompatibleProvider<T> {
315319
if let Some(sec) = timeout_seconds {
316320
builder = builder.timeout(std::time::Duration::from_secs(sec));
317321
}
322+
let extra_body = match extra_body {
323+
Some(serde_json::Value::Object(map)) => map,
324+
_ => serde_json::Map::new(), // Should we panic here?
325+
};
318326
Self {
319327
api_key: api_key.into(),
320328
base_url: Url::parse(&base_url.unwrap_or_else(|| T::DEFAULT_BASE_URL.to_owned()))
@@ -331,6 +339,7 @@ impl<T: OpenAIProviderConfig> OpenAICompatibleProvider<T> {
331339
reasoning_effort,
332340
json_schema,
333341
voice,
342+
extra_body,
334343
parallel_tool_calls: parallel_tool_calls.unwrap_or(false),
335344
normalize_response: normalize_response.unwrap_or(true),
336345
embedding_encoding_format,
@@ -432,6 +441,7 @@ impl<T: OpenAIProviderConfig> ChatProvider for OpenAICompatibleProvider<T> {
432441
response_format,
433442
stream_options: None,
434443
parallel_tool_calls,
444+
extra_body: self.extra_body.clone(),
435445
};
436446
let url = self
437447
.base_url
@@ -547,6 +557,7 @@ impl<T: OpenAIProviderConfig> ChatProvider for OpenAICompatibleProvider<T> {
547557
} else {
548558
None
549559
},
560+
extra_body: self.extra_body.clone(),
550561
};
551562
let url = self
552563
.base_url

0 commit comments

Comments
 (0)