enhance the uniffi API layer (#10427)
This commit is contained in:
Generated
+8
-6
@@ -5108,7 +5108,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "goose-download-manager"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"once_cell",
|
||||
@@ -5120,7 +5120,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "goose-local-inference"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -5186,7 +5186,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "goose-provider-types"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -5215,7 +5215,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "goose-providers"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-stream",
|
||||
@@ -5247,14 +5247,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "goose-sdk"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
dependencies = [
|
||||
"agent-client-protocol",
|
||||
"agent-client-protocol-schema",
|
||||
"anyhow",
|
||||
"base64 0.22.1",
|
||||
"futures",
|
||||
"goose-providers",
|
||||
"goose-sdk-types",
|
||||
"rmcp",
|
||||
"serde_json",
|
||||
"thiserror 2.0.18",
|
||||
"tokio",
|
||||
@@ -5264,7 +5266,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "goose-sdk-types"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
dependencies = [
|
||||
"agent-client-protocol",
|
||||
"agent-client-protocol-schema",
|
||||
|
||||
@@ -2584,6 +2584,7 @@ mod tests {
|
||||
serde_json::json!(["output-128k-2025-02-19"]),
|
||||
)])),
|
||||
reasoning: Some(false),
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let switched =
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "goose-download-manager"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "goose-local-inference"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
authors.workspace = true
|
||||
@@ -27,9 +27,9 @@ etcetera = { workspace = true }
|
||||
encoding_rs = { version = "0.8.35", default-features = false }
|
||||
fs2 = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
goose-download-manager = { version = "0.1.0-alpha.0", path = "../goose-download-manager" }
|
||||
goose-provider-types = { version = "0.1.0-alpha.0", path = "../goose-provider-types", default-features = false }
|
||||
goose-sdk-types = { version = "0.1.0-alpha.0", path = "../goose-sdk-types", default-features = false }
|
||||
goose-download-manager = { version = "0.1.0-alpha.5", path = "../goose-download-manager" }
|
||||
goose-provider-types = { version = "0.1.0-alpha.5", path = "../goose-provider-types", default-features = false }
|
||||
goose-sdk-types = { version = "0.1.0-alpha.5", path = "../goose-sdk-types", default-features = false }
|
||||
hf-hub = { version = "1.0.0-rc.1", default-features = false }
|
||||
include_dir = { workspace = true }
|
||||
llama-cpp-2 = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "goose-provider-types"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@@ -358,13 +358,21 @@ pub async fn collect_stream(
|
||||
}
|
||||
}
|
||||
|
||||
match final_message {
|
||||
Some(msg) => {
|
||||
let usage = final_usage
|
||||
match (final_message, final_usage) {
|
||||
(Some(msg), usage) => {
|
||||
let usage = usage
|
||||
.unwrap_or_else(|| ProviderUsage::new("unknown".to_string(), Usage::default()));
|
||||
Ok((msg, usage))
|
||||
}
|
||||
None => Err(ProviderError::ExecutionError(
|
||||
(None, Some(usage)) => Ok((
|
||||
Message::new(
|
||||
rmcp::model::Role::Assistant,
|
||||
chrono::Utc::now().timestamp(),
|
||||
Vec::new(),
|
||||
),
|
||||
usage,
|
||||
)),
|
||||
(None, None) => Err(ProviderError::ExecutionError(
|
||||
"Stream yielded no message".to_string(),
|
||||
)),
|
||||
}
|
||||
@@ -662,6 +670,22 @@ mod tests {
|
||||
assert_eq!(usage.model, "unknown");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_collect_stream_usage_only_yields_empty_message() {
|
||||
let usage = ProviderUsage::new("claude-sonnet-4".to_string(), Usage::default());
|
||||
let stream = futures::stream::once(async move { Ok((None, Some(usage))) });
|
||||
let (msg, usage) = collect_stream(Box::pin(stream)).await.unwrap();
|
||||
assert!(msg.content.is_empty());
|
||||
assert_eq!(usage.model, "claude-sonnet-4");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_collect_stream_no_message_no_usage_errors() {
|
||||
let stream = futures::stream::empty();
|
||||
let result = collect_stream(Box::pin(stream)).await;
|
||||
assert!(matches!(result, Err(ProviderError::ExecutionError(_))));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_collect_stream_preserves_text_audience_boundaries() {
|
||||
use futures::stream;
|
||||
|
||||
@@ -1071,6 +1071,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
let request = create_request(&model_config, "system", &[], &[], &ImageFormat::OpenAi)?;
|
||||
let obj = request.as_object().unwrap();
|
||||
@@ -1105,6 +1106,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: Some(params),
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
let request = create_request(&model_config, "system", &[], &[], &ImageFormat::OpenAi)?;
|
||||
assert_eq!(request["reasoning_effort"], "high");
|
||||
@@ -1124,6 +1126,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: Some(params),
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
let request = create_request(&model_config, "system", &[], &[], &ImageFormat::OpenAi)?;
|
||||
assert_eq!(request["reasoning_effort"], "low");
|
||||
@@ -1144,6 +1147,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: Some(params),
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
let request = create_request(&model_config, "system", &[], &[], &ImageFormat::OpenAi)?;
|
||||
assert_eq!(request["reasoning_effort"], "high");
|
||||
@@ -1162,6 +1166,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
let request = create_request(&model_config, "system", &[], &[], &ImageFormat::OpenAi)?;
|
||||
assert_eq!(request["model"], "o3");
|
||||
@@ -1180,6 +1185,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
let request = create_request(&model_config, "system", &[], &[], &ImageFormat::OpenAi)?;
|
||||
assert_eq!(request["model"], "o3");
|
||||
@@ -1198,6 +1204,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
let request = create_request(&model_config, "system", &[], &[], &ImageFormat::OpenAi)?;
|
||||
assert_eq!(request["model"], "databricks-gpt-5.4");
|
||||
@@ -1687,6 +1694,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let messages = vec![
|
||||
@@ -1739,6 +1747,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let messages = vec![Message::user().with_text("Hello")];
|
||||
|
||||
@@ -1330,6 +1330,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let messages = vec![
|
||||
@@ -1421,6 +1422,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let tool = Tool::new(
|
||||
@@ -1464,6 +1466,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result =
|
||||
@@ -1544,6 +1547,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result =
|
||||
@@ -1594,6 +1598,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "You are helpful.", &[], &[]).unwrap();
|
||||
@@ -1619,6 +1624,7 @@ mod tests {
|
||||
serde_json::json!(true),
|
||||
)])),
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &[], &[]).unwrap();
|
||||
@@ -1643,6 +1649,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result =
|
||||
@@ -1689,6 +1696,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -1725,6 +1733,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -1756,6 +1765,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -1786,6 +1796,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -1820,6 +1831,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -1856,6 +1868,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -1881,6 +1894,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -1912,6 +1926,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -1943,6 +1958,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -2085,6 +2101,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -2125,6 +2142,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -2162,6 +2180,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
@@ -2199,6 +2218,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
|
||||
let result = create_responses_request(&model_config, "", &messages, &[]).unwrap();
|
||||
|
||||
@@ -33,6 +33,10 @@ pub struct ModelConfig {
|
||||
pub request_params: Option<HashMap<String, Value>>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub reasoning: Option<bool>,
|
||||
/// Per-request HTTP headers attached to outgoing provider calls.
|
||||
/// Never serialized into request bodies.
|
||||
#[serde(skip)]
|
||||
pub request_headers: Option<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ModelConfig {
|
||||
@@ -64,6 +68,7 @@ impl<'de> Deserialize<'de> for ModelConfig {
|
||||
toolshim_model: raw.toolshim_model,
|
||||
request_params: raw.request_params,
|
||||
reasoning: raw.reasoning,
|
||||
request_headers: None,
|
||||
};
|
||||
config.normalize_effort_suffix();
|
||||
Ok(config)
|
||||
@@ -81,6 +86,7 @@ impl ModelConfig {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
config.normalize_effort_suffix();
|
||||
config
|
||||
@@ -162,6 +168,11 @@ impl ModelConfig {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_request_headers(mut self, headers: Option<HashMap<String, String>>) -> Self {
|
||||
self.request_headers = headers;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_merged_request_params(mut self, params: HashMap<String, Value>) -> Self {
|
||||
match self.request_params.as_mut() {
|
||||
Some(existing) => {
|
||||
@@ -300,6 +311,25 @@ impl ModelConfig {
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn request_headers_never_serialize_into_bodies() {
|
||||
let config = ModelConfig::new("test-model").with_request_headers(Some(HashMap::from([(
|
||||
"queue_threshold".to_string(),
|
||||
"500".to_string(),
|
||||
)])));
|
||||
|
||||
let serialized = serde_json::to_value(&config).unwrap();
|
||||
assert!(serialized.get("request_headers").is_none());
|
||||
assert_eq!(
|
||||
config
|
||||
.request_headers
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get("queue_threshold"),
|
||||
Some(&"500".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
mod thinking_effort_tests {
|
||||
use super::*;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "goose-providers"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
authors.workspace = true
|
||||
@@ -35,8 +35,8 @@ anyhow = { workspace = true }
|
||||
async-stream = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
futures = { workspace = true }
|
||||
goose-provider-types = { version = "0.1.0-alpha.0", path = "../goose-provider-types", default-features = false }
|
||||
goose-local-inference = { version = "0.1.0-alpha.0", path = "../goose-local-inference", default-features = false, optional = true }
|
||||
goose-provider-types = { version = "0.1.0-alpha.5", path = "../goose-provider-types", default-features = false }
|
||||
goose-local-inference = { version = "0.1.0-alpha.5", path = "../goose-local-inference", default-features = false, optional = true }
|
||||
reqwest = { workspace = true }
|
||||
rmcp = { workspace = true, features = ["server", "macros"] }
|
||||
serde = { workspace = true }
|
||||
|
||||
@@ -282,7 +282,10 @@ impl Provider for AnthropicProvider {
|
||||
|
||||
let response = self
|
||||
.with_retry(|| async {
|
||||
let request = self.api_client.request("v1/messages");
|
||||
let request = self
|
||||
.api_client
|
||||
.request("v1/messages")
|
||||
.model_headers(model_config)?;
|
||||
let resp = request.response_post(&payload).await?;
|
||||
handle_status(resp).await
|
||||
})
|
||||
|
||||
@@ -408,6 +408,17 @@ impl<'a> ApiRequestBuilder<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
/// Apply per-request headers from a model config, overriding any static
|
||||
/// client headers on key collision.
|
||||
pub fn model_headers(self, model_config: &crate::model::ModelConfig) -> Result<Self> {
|
||||
match &model_config.request_headers {
|
||||
Some(headers) => headers
|
||||
.iter()
|
||||
.try_fold(self, |builder, (key, value)| builder.header(key, value)),
|
||||
None => Ok(self),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn api_post(self, payload: &Value) -> Result<ApiResponse> {
|
||||
let response = self.response_post(payload).await?;
|
||||
ApiResponse::from_response(response).await
|
||||
@@ -598,6 +609,67 @@ ShGoCNbfNS+COlPMRAujyDlATZcLs9p4tA==
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_model_headers_applied_and_override_static_headers() {
|
||||
let runtime = tokio::runtime::Runtime::new().unwrap();
|
||||
runtime.block_on(async {
|
||||
let client = ApiClient::new_with_tls(
|
||||
"http://localhost:8080".to_string(),
|
||||
AuthMethod::NoAuth,
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
.with_header("x-static", "static-value")
|
||||
.unwrap()
|
||||
.with_header("queue_threshold", "1000")
|
||||
.unwrap();
|
||||
|
||||
let model_config = crate::model::ModelConfig::new("test-model").with_request_headers(
|
||||
Some(std::collections::HashMap::from([
|
||||
("queue_threshold".to_string(), "500".to_string()),
|
||||
("Idempotency-Key".to_string(), "abc-123".to_string()),
|
||||
])),
|
||||
);
|
||||
|
||||
let request = client
|
||||
.request("/test")
|
||||
.model_headers(&model_config)
|
||||
.unwrap()
|
||||
.send_request(|url, client| client.get(url))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let headers = request.build().unwrap().headers().clone();
|
||||
let get = |name: &str| {
|
||||
headers
|
||||
.get(name)
|
||||
.and_then(|value| value.to_str().ok())
|
||||
.map(str::to_string)
|
||||
};
|
||||
assert_eq!(get("queue_threshold"), Some("500".to_string()));
|
||||
assert_eq!(get("Idempotency-Key"), Some("abc-123".to_string()));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_model_headers_rejects_invalid_header_name() {
|
||||
let client = ApiClient::new_with_tls(
|
||||
"http://localhost:8080".to_string(),
|
||||
AuthMethod::NoAuth,
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let model_config = crate::model::ModelConfig::new("test-model").with_request_headers(Some(
|
||||
std::collections::HashMap::from([("bad header name".to_string(), "value".to_string())]),
|
||||
));
|
||||
|
||||
assert!(client
|
||||
.request("/test")
|
||||
.model_headers(&model_config)
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_request_builder_decorator() {
|
||||
let runtime = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
@@ -595,7 +595,12 @@ impl Provider for DatabricksProvider {
|
||||
let response = self
|
||||
.with_retry(|| async {
|
||||
let payload_clone = payload.clone();
|
||||
let resp = self.api_client.response_post(&path, &payload_clone).await?;
|
||||
let resp = self
|
||||
.api_client
|
||||
.request(&path)
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload_clone)
|
||||
.await?;
|
||||
handle_status(resp).await
|
||||
})
|
||||
.await
|
||||
@@ -655,7 +660,12 @@ impl Provider for DatabricksProvider {
|
||||
let mut log = start_log(model_config, &payload)?;
|
||||
let response = self
|
||||
.with_retry(|| async {
|
||||
let resp = self.api_client.response_post(&path, &payload).await?;
|
||||
let resp = self
|
||||
.api_client
|
||||
.request(&path)
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload)
|
||||
.await?;
|
||||
if !resp.status().is_success() {
|
||||
let status = resp.status();
|
||||
let url = sanitize_url(resp.url().as_str());
|
||||
@@ -672,7 +682,12 @@ impl Provider for DatabricksProvider {
|
||||
Err(e) if e.to_string().contains("stream_options") => {
|
||||
payload.as_object_mut().unwrap().remove("stream_options");
|
||||
self.with_retry(|| async {
|
||||
let resp = self.api_client.response_post(&path, &payload).await?;
|
||||
let resp = self
|
||||
.api_client
|
||||
.request(&path)
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload)
|
||||
.await?;
|
||||
if !resp.status().is_success() {
|
||||
let status = resp.status();
|
||||
let url = sanitize_url(resp.url().as_str());
|
||||
|
||||
@@ -205,7 +205,9 @@ impl DatabricksV2Provider {
|
||||
.with_retry(|| async {
|
||||
let resp = self
|
||||
.api_client
|
||||
.response_post("ai-gateway/openai/v1/responses", &payload)
|
||||
.request("ai-gateway/openai/v1/responses")
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload)
|
||||
.await?;
|
||||
handle_status(resp).await
|
||||
})
|
||||
@@ -241,7 +243,9 @@ impl DatabricksV2Provider {
|
||||
.with_retry(|| async {
|
||||
let resp = self
|
||||
.api_client
|
||||
.response_post("ai-gateway/mlflow/v1/chat/completions", &payload)
|
||||
.request("ai-gateway/mlflow/v1/chat/completions")
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload)
|
||||
.await?;
|
||||
handle_status(resp).await
|
||||
})
|
||||
@@ -275,7 +279,9 @@ impl DatabricksV2Provider {
|
||||
.with_retry(|| async {
|
||||
let resp = self
|
||||
.api_client
|
||||
.response_post("ai-gateway/anthropic/v1/messages", &payload)
|
||||
.request("ai-gateway/anthropic/v1/messages")
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload)
|
||||
.await?;
|
||||
handle_status(resp).await
|
||||
})
|
||||
|
||||
@@ -93,11 +93,19 @@ impl GoogleProvider {
|
||||
|
||||
async fn post_stream(
|
||||
&self,
|
||||
model_name: &str,
|
||||
model_config: &ModelConfig,
|
||||
payload: &Value,
|
||||
) -> Result<reqwest::Response, ProviderError> {
|
||||
let path = format!("v1beta/models/{}:streamGenerateContent?alt=sse", model_name);
|
||||
let response = self.api_client.response_post(&path, payload).await?;
|
||||
let path = format!(
|
||||
"v1beta/models/{}:streamGenerateContent?alt=sse",
|
||||
model_config.model_name
|
||||
);
|
||||
let response = self
|
||||
.api_client
|
||||
.request(&path)
|
||||
.model_headers(model_config)?
|
||||
.response_post(payload)
|
||||
.await?;
|
||||
handle_status(response).await
|
||||
}
|
||||
}
|
||||
@@ -181,7 +189,7 @@ impl Provider for GoogleProvider {
|
||||
let mut log = start_log(model_config, &payload)?;
|
||||
|
||||
let response = self
|
||||
.with_retry(|| async { self.post_stream(&model_config.model_name, &payload).await })
|
||||
.with_retry(|| async { self.post_stream(model_config, &payload).await })
|
||||
.await
|
||||
.inspect_err(|e| {
|
||||
let _ = log.error(e);
|
||||
|
||||
@@ -426,7 +426,9 @@ impl Provider for OllamaProvider {
|
||||
.with_retry(|| async {
|
||||
let resp = self
|
||||
.api_client
|
||||
.response_post("v1/chat/completions", &payload)
|
||||
.request("v1/chat/completions")
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload)
|
||||
.await?;
|
||||
handle_status(resp).await
|
||||
})
|
||||
|
||||
@@ -656,14 +656,13 @@ impl Provider for OpenAiProvider {
|
||||
let payload_clone = payload.clone();
|
||||
let resp = self
|
||||
.api_client
|
||||
.response_post(
|
||||
&Self::map_base_path(
|
||||
&self.base_path,
|
||||
"responses",
|
||||
OPEN_AI_DEFAULT_RESPONSES_PATH,
|
||||
),
|
||||
&payload_clone,
|
||||
)
|
||||
.request(&Self::map_base_path(
|
||||
&self.base_path,
|
||||
"responses",
|
||||
OPEN_AI_DEFAULT_RESPONSES_PATH,
|
||||
))
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload_clone)
|
||||
.await?;
|
||||
handle_status(resp).await
|
||||
})
|
||||
@@ -721,7 +720,9 @@ impl Provider for OpenAiProvider {
|
||||
.with_retry(|| async {
|
||||
let resp = self
|
||||
.api_client
|
||||
.response_post(&self.base_path, &payload)
|
||||
.request(&self.base_path)
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload)
|
||||
.await?;
|
||||
handle_status(resp).await
|
||||
})
|
||||
|
||||
@@ -123,7 +123,9 @@ impl Provider for OpenAiCompatibleProvider {
|
||||
.with_retry(|| async {
|
||||
let resp = self
|
||||
.api_client
|
||||
.response_post(&completions_path, &payload)
|
||||
.request(&completions_path)
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload)
|
||||
.await?;
|
||||
handle_status(resp).await
|
||||
})
|
||||
|
||||
@@ -91,10 +91,16 @@ impl SnowflakeProvider {
|
||||
})
|
||||
}
|
||||
|
||||
async fn post(&self, payload: &Value) -> Result<Value, ProviderError> {
|
||||
async fn post(
|
||||
&self,
|
||||
model_config: &ModelConfig,
|
||||
payload: &Value,
|
||||
) -> Result<Value, ProviderError> {
|
||||
let response = self
|
||||
.api_client
|
||||
.response_post("api/v2/cortex/inference:complete", payload)
|
||||
.request("api/v2/cortex/inference:complete")
|
||||
.model_headers(model_config)?
|
||||
.response_post(payload)
|
||||
.await?;
|
||||
|
||||
let status = response.status();
|
||||
@@ -317,7 +323,7 @@ impl Provider for SnowflakeProvider {
|
||||
let response = self
|
||||
.with_retry(|| async {
|
||||
let payload_clone = payload.clone();
|
||||
self.post(&payload_clone).await
|
||||
self.post(model_config, &payload_clone).await
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "goose-sdk-types"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "goose-sdk"
|
||||
version = "0.1.0-alpha.0"
|
||||
version = "0.1.0-alpha.5"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
authors.workspace = true
|
||||
@@ -27,20 +27,24 @@ uniffi = [
|
||||
"dep:futures",
|
||||
"dep:serde_json",
|
||||
"dep:tokio",
|
||||
"dep:rmcp",
|
||||
"dep:base64",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
goose-sdk-types = { version = "0.1.0-alpha.0", path = "../goose-sdk-types" }
|
||||
goose-sdk-types = { version = "0.1.0-alpha.5", path = "../goose-sdk-types" }
|
||||
agent-client-protocol = { workspace = true, features = ["unstable"] }
|
||||
agent-client-protocol-schema = { workspace = true }
|
||||
|
||||
uniffi = { version = "0.32", features = ["cli"], optional = true }
|
||||
thiserror = { version = "2", optional = true }
|
||||
goose-providers = { version = "0.1.0-alpha.0", path = "../goose-providers", features = ["rustls-tls"], optional = true }
|
||||
goose-providers = { version = "0.1.0-alpha.5", path = "../goose-providers", features = ["rustls-tls"], optional = true }
|
||||
futures = { workspace = true, optional = true }
|
||||
serde_json = { workspace = true, optional = true }
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "sync"], optional = true }
|
||||
anyhow = { workspace = true, optional = true }
|
||||
rmcp = { workspace = true, optional = true }
|
||||
base64 = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "process", "io-std", "io-util"] }
|
||||
|
||||
@@ -4,9 +4,9 @@ The bindings layer for Goose. It houses the shared types used for both ACP and
|
||||
SDK access, and exposes a cross-language version of the Goose API.
|
||||
|
||||
With `--features uniffi` the crate compiles to native bindings for Python and
|
||||
Kotlin (namespace `goose` / `io.github.aaif_goose`). The UniFFI surface currently lets
|
||||
callers construct declarative providers from JSON and stream provider
|
||||
completions.
|
||||
Kotlin (namespace `goose` / `io.github.aaif_goose`). The UniFFI surface lets
|
||||
callers construct providers, stream provider completions, perform non-streaming
|
||||
completion, and pass rich message/tool content across the FFI boundary.
|
||||
|
||||
```bash
|
||||
just python # build bindings + run examples/uniffi/provider.py
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import io.github.aaif_goose.MessageContent
|
||||
import io.github.aaif_goose.MessageRole
|
||||
import io.github.aaif_goose.ProviderMessage
|
||||
import io.github.aaif_goose.ProviderModelConfig
|
||||
import io.github.aaif_goose.StreamChunk
|
||||
import io.github.aaif_goose.streamFlow
|
||||
import io.github.aaif_goose.providers.openai.defaultModel
|
||||
import io.github.aaif_goose.providers.openai.provider as openAiProvider
|
||||
@@ -17,7 +19,11 @@ fun main() = runBlocking {
|
||||
val messages = listOf(
|
||||
ProviderMessage(
|
||||
role = MessageRole.USER,
|
||||
text = "What is the capital of France? Answer in one sentence.",
|
||||
content = listOf(
|
||||
MessageContent.Text(
|
||||
text = "What is the capital of France? Answer in one sentence.",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -28,8 +34,12 @@ fun main() = runBlocking {
|
||||
messages,
|
||||
)
|
||||
.collect { chunk ->
|
||||
chunk.text?.let { print(it) }
|
||||
chunk.usageJson?.let { println("\nusage: $it") }
|
||||
when (chunk) {
|
||||
is StreamChunk.TextChunk -> print(chunk.text)
|
||||
is StreamChunk.EndChunk -> chunk.usage?.let { println("\nusage: $it") }
|
||||
is StreamChunk.ErrorChunk -> System.err.println("\nerror: ${chunk.error.message}")
|
||||
is StreamChunk.ToolChunk -> Unit
|
||||
}
|
||||
}
|
||||
println()
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#!/usr/bin/env -S uv run --script
|
||||
"""Goose SDK demo: build a declarative provider and stream a completion."""
|
||||
import asyncio
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
@@ -9,9 +8,11 @@ HERE = Path(__file__).resolve().parent
|
||||
sys.path.insert(0, str(HERE.parent.parent / "generated"))
|
||||
|
||||
from goose import ( # noqa: E402
|
||||
MessageContent,
|
||||
MessageRole,
|
||||
ProviderMessage,
|
||||
ProviderModelConfig,
|
||||
StreamChunk,
|
||||
declarative_provider_from_json,
|
||||
)
|
||||
|
||||
@@ -19,19 +20,26 @@ from goose import ( # noqa: E402
|
||||
async def main() -> None:
|
||||
provider = declarative_provider_from_json((HERE.parent / "deepseek.json").read_text())
|
||||
model = ProviderModelConfig(model_name="deepseek-v4-flash")
|
||||
messages = [ProviderMessage(role=MessageRole.USER, text="what is the capital of France?")]
|
||||
messages = [
|
||||
ProviderMessage(
|
||||
role=MessageRole.USER,
|
||||
content=[MessageContent.Text(text="what is the capital of France?")],
|
||||
)
|
||||
]
|
||||
stream = await provider.stream(
|
||||
model,
|
||||
"You are a knowledgable geography expert",
|
||||
messages,
|
||||
[],
|
||||
)
|
||||
|
||||
while chunk := await stream.next():
|
||||
if chunk.text:
|
||||
while chunk := await stream.next_chunk():
|
||||
if isinstance(chunk, StreamChunk.TextChunk):
|
||||
print(chunk.text, end="")
|
||||
if chunk.usage_json:
|
||||
usage = json.loads(chunk.usage_json)
|
||||
print(f"\nusage: {usage}")
|
||||
elif isinstance(chunk, StreamChunk.EndChunk) and chunk.usage:
|
||||
print(f"\nusage: {chunk.usage}")
|
||||
elif isinstance(chunk, StreamChunk.ErrorChunk):
|
||||
print(f"\nerror: {chunk.error.message}", file=sys.stderr)
|
||||
print()
|
||||
|
||||
|
||||
|
||||
@@ -77,9 +77,15 @@ maven-bindings profile="release":
|
||||
esac; \
|
||||
crates/goose-sdk/scripts/prepare-maven-package.sh "$lib_path"
|
||||
|
||||
maven-clean:
|
||||
rm -rf {{maven_dir}}/build {{maven_kotlin_dir}} {{maven_resources_dir}}
|
||||
|
||||
maven-package: (maven-bindings "release")
|
||||
cd {{maven_dir}} && ./gradlew --no-daemon publishToMavenLocal
|
||||
|
||||
maven-package-clean: maven-clean (maven-bindings "release")
|
||||
cd {{maven_dir}} && ./gradlew --no-daemon publishToMavenLocal
|
||||
|
||||
maven-publish: (maven-bindings "release")
|
||||
cd {{maven_dir}} && ./gradlew --no-daemon publishAndReleaseToMavenCentral
|
||||
|
||||
|
||||
@@ -5,7 +5,10 @@ as the Maven artifact `io.github.aaif-goose:gdk`.
|
||||
|
||||
The artifact version is read from `crates/goose-sdk/Cargo.toml`, so it stays in
|
||||
lockstep with the Rust crate version. The jar includes the generated Kotlin API
|
||||
and native libraries under JNA platform resource directories.
|
||||
and native libraries under JNA platform resource directories. Packaging supports
|
||||
`darwin-aarch64`, `darwin-x86-64`, `linux-x86-64`, `linux-aarch64`, and
|
||||
`win32-x86-64` resource prefixes; CI is responsible for assembling every native
|
||||
library into the final published jar.
|
||||
|
||||
Build locally from the repository root:
|
||||
|
||||
|
||||
@@ -42,10 +42,6 @@ tasks.jar {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<GenerateModuleMetadata>().configureEach {
|
||||
dependsOn(tasks.named("plainJavadocJar"))
|
||||
}
|
||||
|
||||
mavenPublishing {
|
||||
publishToMavenCentral(automaticRelease = true)
|
||||
if (providers.gradleProperty("signingInMemoryKey").isPresent) {
|
||||
|
||||
@@ -7,9 +7,16 @@ public fun Provider.streamFlow(
|
||||
model: ProviderModelConfig,
|
||||
system: String,
|
||||
messages: List<ProviderMessage>,
|
||||
): Flow<ProviderStreamChunk> = flow {
|
||||
val stream = stream(model, system, messages)
|
||||
tools: List<ProviderTool> = emptyList(),
|
||||
): Flow<StreamChunk> = flow {
|
||||
val stream = stream(model, system, messages, tools)
|
||||
while (true) {
|
||||
emit(stream.next() ?: break)
|
||||
emit(stream.nextChunk() ?: break)
|
||||
}
|
||||
}
|
||||
|
||||
public suspend fun Provider.complete(
|
||||
model: ProviderModelConfig,
|
||||
system: String,
|
||||
messages: List<ProviderMessage>,
|
||||
): ProviderCompletion = complete(model, system, messages, emptyList())
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package io.github.aaif_goose.providers.anthropic
|
||||
|
||||
public fun provider(
|
||||
apiKey: String,
|
||||
baseUrl: String? = null,
|
||||
betaHeaders: List<String> = emptyList(),
|
||||
): io.github.aaif_goose.Provider = io.github.aaif_goose.anthropicProvider(apiKey, baseUrl, betaHeaders)
|
||||
|
||||
public fun defaultModel(): String = io.github.aaif_goose.anthropicDefaultModel()
|
||||
@@ -0,0 +1,5 @@
|
||||
package io.github.aaif_goose.providers.groq
|
||||
|
||||
public fun provider(apiKey: String): io.github.aaif_goose.Provider = io.github.aaif_goose.groqProvider(apiKey)
|
||||
|
||||
public fun defaultModel(): String = io.github.aaif_goose.groqDefaultModel()
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "goose-sdk"
|
||||
version = "0.1.0a0"
|
||||
version = "0.1.0a5"
|
||||
description = "Python bindings for the Goose SDK"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.9"
|
||||
|
||||
@@ -5,6 +5,7 @@ case "$(uname -s)-$(uname -m)" in
|
||||
Darwin-arm64) echo "darwin-aarch64" ;;
|
||||
Darwin-x86_64) echo "darwin-x86-64" ;;
|
||||
Linux-x86_64) echo "linux-x86-64" ;;
|
||||
Linux-aarch64|Linux-arm64) echo "linux-aarch64" ;;
|
||||
MINGW64_NT-*-x86_64|MSYS_NT-*-x86_64|CYGWIN_NT-*-x86_64) echo "win32-x86-64" ;;
|
||||
*) echo "unsupported platform: $(uname -s)-$(uname -m)" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
@@ -10,7 +10,7 @@ native_lib="$1"
|
||||
resource_prefix="${2:-$(crates/goose-sdk/scripts/maven-resource-prefix.sh)}"
|
||||
|
||||
case "$resource_prefix" in
|
||||
darwin-aarch64|linux-x86-64|win32-x86-64) ;;
|
||||
darwin-aarch64|darwin-x86-64|linux-x86-64|linux-aarch64|win32-x86-64) ;;
|
||||
*) echo "unsupported JNA resource prefix: $resource_prefix" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -710,6 +710,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
},
|
||||
max_tool_responses: None,
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ pub async fn get_fast_model(
|
||||
match fast_model_name {
|
||||
Some(name) if name != model_config.model_name => {
|
||||
model_config_from_user_config(provider_name, name)
|
||||
.map(|config| config.with_request_headers(model_config.request_headers.clone()))
|
||||
}
|
||||
_ => Ok(model_config.clone()),
|
||||
}
|
||||
@@ -179,6 +180,7 @@ fn base_model_config_from_user_config(model_name: &str) -> Result<ModelConfig> {
|
||||
toolshim_model: get_goose_toolshim_model(config)?,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
};
|
||||
model.normalize_effort_suffix();
|
||||
Ok(model)
|
||||
|
||||
@@ -941,6 +941,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -279,6 +279,7 @@ impl GcpVertexAIProvider {
|
||||
|
||||
async fn send_request_with_retry(
|
||||
&self,
|
||||
model: &ModelConfig,
|
||||
url: Url,
|
||||
payload: &Value,
|
||||
) -> Result<reqwest::Response, ProviderError> {
|
||||
@@ -314,12 +315,18 @@ impl GcpVertexAIProvider {
|
||||
}
|
||||
};
|
||||
|
||||
let request = self
|
||||
let mut request = self
|
||||
.client
|
||||
.post(url.clone())
|
||||
.json(payload)
|
||||
.header("Authorization", auth_header);
|
||||
|
||||
if let Some(headers) = &model.request_headers {
|
||||
for (key, value) in headers {
|
||||
request = request.header(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
let response = (self.request_builder)(request)
|
||||
.map_err(|e| ProviderError::ExecutionError(e.to_string()))?
|
||||
.send()
|
||||
@@ -400,7 +407,7 @@ impl GcpVertexAIProvider {
|
||||
.build_request_url(model, context.provider(), location, true)
|
||||
.map_err(|e| ProviderError::RequestFailed(e.to_string()))?;
|
||||
|
||||
self.send_request_with_retry(url, payload).await
|
||||
self.send_request_with_retry(model, url, payload).await
|
||||
}
|
||||
|
||||
async fn post_stream(
|
||||
|
||||
@@ -261,6 +261,7 @@ impl GithubCopilotProvider {
|
||||
|
||||
async fn post(
|
||||
&self,
|
||||
model_config: &ModelConfig,
|
||||
path: &str,
|
||||
is_user_initiated: bool,
|
||||
payload: &mut Value,
|
||||
@@ -279,7 +280,9 @@ impl GithubCopilotProvider {
|
||||
.with_headers(headers)?;
|
||||
|
||||
api_client
|
||||
.response_post(path, payload)
|
||||
.request(path)
|
||||
.model_headers(model_config)?
|
||||
.response_post(payload)
|
||||
.await
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
@@ -413,6 +416,7 @@ impl GithubCopilotProvider {
|
||||
let mut payload_clone = payload.clone();
|
||||
let resp = self
|
||||
.post(
|
||||
model_config,
|
||||
"responses",
|
||||
is_user_initiated,
|
||||
&mut payload_clone,
|
||||
@@ -459,6 +463,7 @@ impl GithubCopilotProvider {
|
||||
let mut payload_clone = payload.clone();
|
||||
let resp = self
|
||||
.post(
|
||||
model_config,
|
||||
"chat/completions",
|
||||
is_user_initiated,
|
||||
&mut payload_clone,
|
||||
@@ -488,6 +493,7 @@ impl GithubCopilotProvider {
|
||||
.with_retry(|| async {
|
||||
let mut payload_clone = payload.clone();
|
||||
self.post(
|
||||
model_config,
|
||||
"chat/completions",
|
||||
is_user_initiated,
|
||||
&mut payload_clone,
|
||||
|
||||
@@ -136,10 +136,16 @@ impl LiteLLMProvider {
|
||||
Ok(models)
|
||||
}
|
||||
|
||||
async fn post(&self, payload: &Value) -> Result<Value, ProviderError> {
|
||||
async fn post(
|
||||
&self,
|
||||
model_config: &ModelConfig,
|
||||
payload: &Value,
|
||||
) -> Result<Value, ProviderError> {
|
||||
let response = self
|
||||
.api_client
|
||||
.response_post(&self.base_path, payload)
|
||||
.request(&self.base_path)
|
||||
.model_headers(model_config)?
|
||||
.response_post(payload)
|
||||
.await?;
|
||||
handle_response_openai_compat(response).await
|
||||
}
|
||||
@@ -248,7 +254,7 @@ impl Provider for LiteLLMProvider {
|
||||
let response = self
|
||||
.with_retry(|| async {
|
||||
let payload_clone = payload.clone();
|
||||
self.post(&payload_clone).await
|
||||
self.post(model_config, &payload_clone).await
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -196,7 +196,9 @@ impl Provider for NanoGptProvider {
|
||||
.with_retry(|| async {
|
||||
let resp = self
|
||||
.api_client
|
||||
.response_post("chat/completions", &payload)
|
||||
.request("chat/completions")
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload)
|
||||
.await?;
|
||||
handle_status(resp).await
|
||||
})
|
||||
|
||||
@@ -335,7 +335,9 @@ impl Provider for OpenRouterProvider {
|
||||
.with_retry(|| async {
|
||||
let resp = self
|
||||
.api_client
|
||||
.response_post("api/v1/chat/completions", &payload)
|
||||
.request("api/v1/chat/completions")
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload)
|
||||
.await?;
|
||||
handle_status(resp).await
|
||||
})
|
||||
@@ -369,6 +371,7 @@ mod tests {
|
||||
toolshim_model: None,
|
||||
request_params: None,
|
||||
reasoning: None,
|
||||
request_headers: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -152,7 +152,9 @@ impl Provider for TetrateProvider {
|
||||
.with_retry(|| async {
|
||||
let resp = self
|
||||
.api_client
|
||||
.response_post("v1/chat/completions", &payload)
|
||||
.request("v1/chat/completions")
|
||||
.model_headers(model_config)?
|
||||
.response_post(&payload)
|
||||
.await?;
|
||||
let resp = handle_status(resp)
|
||||
.await
|
||||
|
||||
Reference in New Issue
Block a user