refactor: refactoring api error handling (#382)

* refactor: refactoring api error handling

* chore: update release notes

* chore: merge from main
This commit is contained in:
Medcl
2025-04-17 18:42:28 +08:00
committed by GitHub
parent 0bf6686494
commit bbb517237f
15 changed files with 274 additions and 287 deletions

View File

@@ -36,7 +36,8 @@ Information about release notes of Coco Server is provided here.
- refactor: refactoring login callback, receive access_token from coco-server
- chore: adjust web component styles #362
- style: modify the style #370
- style: search list details display #378 #380
- style: search list details display #378
- refactor: refactoring api error handling #382
## 0.3.0 (2025-03-31)

View File

@@ -1,7 +1,7 @@
use crate::common;
use crate::common::assistant::ChatRequestMessage;
use crate::common::http::GetResponse;
use crate::server::http_client::HttpClient;
use reqwest::Response;
use serde_json::Value;
use std::collections::HashMap;
use tauri::{AppHandle, Runtime};
@@ -24,8 +24,8 @@ pub async fn chat_history<R: Runtime>(
if let Some(query) = query {
if !query.is_empty() {
query_params.insert("query".to_string(), query.into());
}
query_params.insert("query".to_string(), query.into());
}
}
let response = HttpClient::get(&server_id, "/chat/_history", Some(query_params))
@@ -35,21 +35,8 @@ pub async fn chat_history<R: Runtime>(
format!("Error get history: {}", e)
})?;
handle_raw_response(response).await?
}
async fn handle_raw_response(response: Response) -> Result<Result<String, String>, String> {
Ok(
if response.status().as_u16() < 200 || response.status().as_u16() >= 400 {
Err("Failed to send message".to_string())
} else {
let body = response
.text()
.await
.map_err(|e| format!("Failed to parse response JSON: {}", e))?;
Ok(body)
},
)
common::http::get_response_body_text(response).await
}
#[tauri::command]
@@ -74,7 +61,7 @@ pub async fn session_chat_history<R: Runtime>(
.await
.map_err(|e| format!("Error get session message: {}", e))?;
handle_raw_response(response).await?
common::http::get_response_body_text(response).await
}
#[tauri::command]
@@ -90,7 +77,7 @@ pub async fn open_session_chat<R: Runtime>(
.await
.map_err(|e| format!("Error open session: {}", e))?;
handle_raw_response(response).await?
common::http::get_response_body_text(response).await
}
#[tauri::command]
@@ -106,7 +93,7 @@ pub async fn close_session_chat<R: Runtime>(
.await
.map_err(|e| format!("Error close session: {}", e))?;
handle_raw_response(response).await?
common::http::get_response_body_text(response).await
}
#[tauri::command]
pub async fn cancel_session_chat<R: Runtime>(
@@ -121,7 +108,7 @@ pub async fn cancel_session_chat<R: Runtime>(
.await
.map_err(|e| format!("Error cancel session: {}", e))?;
handle_raw_response(response).await?
common::http::get_response_body_text(response).await
}
#[tauri::command]
@@ -130,14 +117,17 @@ pub async fn new_chat<R: Runtime>(
server_id: String,
websocket_id: String,
message: String,
query_params: Option<HashMap<String, Value>>, //search,deep_thinking
query_params: Option<HashMap<String, Value>>,
) -> Result<GetResponse, String> {
let body = if !message.is_empty() {
let message = ChatRequestMessage {
message: Some(message),
};
let body = reqwest::Body::from(serde_json::to_string(&message).unwrap());
Some(body)
Some(
serde_json::to_string(&message)
.map_err(|e| format!("Failed to serialize message: {}", e))?
.into(),
)
} else {
None
};
@@ -145,21 +135,18 @@ pub async fn new_chat<R: Runtime>(
let mut headers = HashMap::new();
headers.insert("WEBSOCKET-SESSION-ID".to_string(), websocket_id.into());
let response =
HttpClient::advanced_post(&server_id, "/chat/_new", Some(headers), query_params, body)
.await
.map_err(|e| format!("Error sending message: {}", e))?;
if response.status().as_u16() < 200 || response.status().as_u16() >= 400 {
return Err("Failed to send message".to_string());
}
let chat_response: GetResponse = response
.json()
let response = HttpClient::advanced_post(&server_id, "/chat/_new", Some(headers), query_params, body)
.await
.map_err(|e| format!("Error sending message: {}", e))?;
let text = response
.text()
.await
.map_err(|e| format!("Failed to read response body: {}", e))?;
let chat_response: GetResponse = serde_json::from_str(&text)
.map_err(|e| format!("Failed to parse response JSON: {}", e))?;
// Check the result and status fields
if chat_response.result != "created" {
return Err(format!("Unexpected result: {}", chat_response.result));
}
@@ -192,10 +179,10 @@ pub async fn send_message<R: Runtime>(
query_params,
Some(body),
)
.await
.map_err(|e| format!("Error cancel session: {}", e))?;
.await
.map_err(|e| format!("Error cancel session: {}", e))?;
handle_raw_response(response).await?
common::http::get_response_body_text(response).await
}
#[tauri::command]
@@ -235,8 +222,8 @@ pub async fn update_session_chat(
None,
Some(reqwest::Body::from(serde_json::to_string(&body).unwrap())),
)
.await
.map_err(|e| format!("Error updating session: {}", e))?;
.await
.map_err(|e| format!("Error updating session: {}", e))?;
Ok(response.status().is_success())
}

View File

@@ -0,0 +1,45 @@
use serde::{Deserialize, Serialize};
use thiserror::Error;
#[derive(Debug, Deserialize)]
pub struct ErrorDetail {
pub reason: String,
pub status: u16,
}
#[derive(Debug, Deserialize)]
pub struct ErrorResponse {
pub error: ErrorDetail,
}
#[derive(Debug, Error, Serialize)]
pub enum SearchError {
#[error("HTTP request failed: {0}")]
HttpError(String),
#[error("Invalid response format: {0}")]
ParseError(String),
#[error("Timeout occurred")]
Timeout,
#[error("Unknown error: {0}")]
#[allow(dead_code)]
Unknown(String),
#[error("InternalError error: {0}")]
#[allow(dead_code)]
InternalError(String),
}
impl From<reqwest::Error> for SearchError {
fn from(err: reqwest::Error) -> Self {
if err.is_timeout() {
SearchError::Timeout
} else if err.is_decode() {
SearchError::ParseError(err.to_string())
} else {
SearchError::HttpError(err.to_string())
}
}
}

View File

@@ -1,3 +1,5 @@
use crate::common;
use reqwest::Response;
use serde::{Deserialize, Serialize};
use serde_json::Value;
@@ -15,4 +17,34 @@ pub struct Source {
pub created: String,
pub updated: String,
pub status: String,
}
pub async fn get_response_body_text(response: Response) -> Result<String, String> {
let status = response.status().as_u16();
let body = response
.text()
.await
.map_err(|e| format!("Failed to read response body: {}", e))?;
if status < 200 || status >= 400 {
// Try to parse the error body
let fallback_error = "Failed to send message".to_string();
if body.trim().is_empty() {
return Err(fallback_error);
}
match serde_json::from_str::<common::error::ErrorResponse>(&body) {
Ok(parsed_error) => {
dbg!(&parsed_error);
Err(format!(
"Server error ({}): {}",
parsed_error.error.status, parsed_error.error.reason
))
}
Err(_) => Err(fallback_error),
}
} else {
Ok(body)
}
}

View File

@@ -10,6 +10,7 @@ pub mod traits;
pub mod register;
pub mod assistant;
pub mod http;
pub mod error;
pub static MAIN_WINDOW_LABEL: &str = "main";
pub static SETTINGS_WINDOW_LABEL: &str = "settings";

View File

@@ -1,9 +1,10 @@
use crate::common::document::Document;
use crate::common::http::get_response_body_text;
use reqwest::Response;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use std::error::Error;
#[derive(Debug, Serialize, Deserialize)]
pub struct SearchResponse<T> {
pub took: u64,
@@ -47,14 +48,11 @@ pub async fn parse_search_response<T>(
where
T: for<'de> Deserialize<'de> + std::fmt::Debug,
{
let body = response
.json::<Value>()
.await
.map_err(|e| format!("Failed to parse JSON: {}", e))?;
let body_text = get_response_body_text(response).await?;
// dbg!(&body);
dbg!(&body_text);
let search_response: SearchResponse<T> = serde_json::from_value(body)
let search_response: SearchResponse<T> = serde_json::from_str(&body_text)
.map_err(|e| format!("Failed to deserialize search response: {}", e))?;
Ok(search_response)

View File

@@ -1,10 +1,8 @@
use crate::common::search::{QueryResponse, QuerySource};
use thiserror::Error;
use async_trait::async_trait;
use crate::common::error::SearchError;
// use std::{future::Future, pin::Pin};
use crate::common::search::SearchQuery;
use serde::Serialize;
use crate::common::search::{QueryResponse, QuerySource};
use async_trait::async_trait;
#[async_trait]
pub trait SearchSource: Send + Sync {
@@ -13,34 +11,3 @@ pub trait SearchSource: Send + Sync {
async fn search(&self, query: SearchQuery) -> Result<QueryResponse, SearchError>;
}
#[derive(Debug, Error, Serialize)]
pub enum SearchError {
#[error("HTTP request failed: {0}")]
HttpError(String),
#[error("Invalid response format: {0}")]
ParseError(String),
#[error("Timeout occurred")]
Timeout,
#[error("Unknown error: {0}")]
#[allow(dead_code)]
Unknown(String),
#[error("InternalError error: {0}")]
#[allow(dead_code)]
InternalError(String),
}
impl From<reqwest::Error> for SearchError {
fn from(err: reqwest::Error) -> Self {
if err.is_timeout() {
SearchError::Timeout
} else if err.is_decode() {
SearchError::ParseError(err.to_string())
} else {
SearchError::HttpError(err.to_string())
}
}
}

View File

@@ -1,6 +1,7 @@
use crate::common::document::{DataSourceReference, Document};
use crate::common::error::SearchError;
use crate::common::search::{QueryResponse, QuerySource, SearchQuery};
use crate::common::traits::{SearchError, SearchSource};
use crate::common::traits::SearchSource;
use crate::local::LOCAL_QUERY_SOURCE_TYPE;
use applications::{App, AppInfo, AppInfoContext};
use async_trait::async_trait;

View File

@@ -1,8 +1,8 @@
use crate::common::error::SearchError;
use crate::common::register::SearchSourceRegistry;
use crate::common::search::{
FailedRequest, MultiSourceQueryResponse, QueryHits, QuerySource, SearchQuery,
};
use crate::common::traits::SearchError;
use futures::stream::FuturesUnordered;
use futures::StreamExt;
use std::collections::HashMap;
@@ -41,7 +41,7 @@ pub async fn query_coco_fusion<R: Runtime>(
timeout(timeout_duration, async {
query_source_clone.search(query).await
})
.await
.await
}));
}

View File

@@ -1,4 +1,5 @@
use super::servers::{get_server_by_id, get_server_token};
use crate::common::http::get_response_body_text;
use crate::server::http_client::HttpClient;
use reqwest::multipart::{Form, Part};
use serde::{Deserialize, Serialize};
@@ -99,16 +100,10 @@ pub async fn upload_attachment(
.await
.map_err(|err| err.to_string())?;
if response.status().is_success() {
let result = response
.json::<UploadAttachmentResponse>()
.await
.map_err(|err| err.to_string())?;
let body = get_response_body_text(response).await?;
Ok(result)
} else {
Err(format!("Upload failed with status: {}", response.status()))
}
serde_json::from_str::<UploadAttachmentResponse>(&body)
.map_err(|e| format!("Failed to parse upload response: {}", e))
}
#[command]
@@ -119,33 +114,30 @@ pub async fn get_attachment(
let mut query_params = HashMap::new();
query_params.insert("session".to_string(), serde_json::Value::String(session_id));
let response = HttpClient::get(&server_id, "/attachment/_search", Some(query_params)).await?;
let response = HttpClient::get(&server_id, "/attachment/_search", Some(query_params))
.await
.map_err(|e| format!("Request error: {}", e))?;
if response.status().is_success() {
response
.json::<GetAttachmentResponse>()
.await
.map_err(|e| e.to_string())
} else {
Err(format!("Request failed with status: {}", response.status()))
}
let body = get_response_body_text(response).await?;
serde_json::from_str::<GetAttachmentResponse>(&body)
.map_err(|e| format!("Failed to parse attachment response: {}", e))
}
#[command]
pub async fn delete_attachment(server_id: String, id: String) -> Result<bool, String> {
let response =
HttpClient::delete(&server_id, &format!("/attachment/{}", id), None, None).await?;
let response = HttpClient::delete(&server_id, &format!("/attachment/{}", id), None, None)
.await
.map_err(|e| format!("Request error: {}", e))?;
if response.status().is_success() {
response
.json::<DeleteAttachmentResponse>()
.await
.map_err(|e| e.to_string())?
.result
.eq("deleted")
.then_some(true)
.ok_or("Delete operation was not successful".to_string())
} else {
Err(format!("Delete failed with status: {}", response.status()))
}
let body = get_response_body_text(response).await?;
let parsed: DeleteAttachmentResponse = serde_json::from_str(&body)
.map_err(|e| format!("Failed to parse delete response: {}", e))?;
parsed
.result
.eq("deleted")
.then_some(true)
.ok_or_else(|| "Delete operation was not successful".to_string())
}

View File

@@ -58,6 +58,7 @@ impl HttpClient {
) -> RequestBuilder {
let client = HTTP_CLIENT.lock().await; // Acquire the lock on HTTP_CLIENT
// Build the request
let mut request_builder = client.request(method.clone(), url);

View File

@@ -1,3 +1,4 @@
use crate::common::http::get_response_body_text;
use crate::common::profile::UserProfile;
use crate::server::http_client::HttpClient;
use tauri::{AppHandle, Runtime};
@@ -12,14 +13,16 @@ pub async fn get_user_profiles<R: Runtime>(
.await
.map_err(|e| format!("Error fetching profile: {}", e))?;
if let Some(content_length) = response.content_length() {
if content_length > 0 {
let profile: UserProfile = response
.json()
.await
.map_err(|e| format!("Failed to parse response: {}", e))?;
return Ok(profile);
}
// Use get_response_body_text to extract the body content
let response_body = get_response_body_text(response)
.await
.map_err(|e| format!("Failed to read response body: {}", e))?;
// Check if the response body is not empty before deserializing
if !response_body.is_empty() {
let profile: UserProfile = serde_json::from_str(&response_body)
.map_err(|e| format!("Failed to parse response: {}", e))?;
return Ok(profile);
}
Err("Profile not found or empty response".to_string())

View File

@@ -1,9 +1,9 @@
use crate::common::document::Document;
use crate::common::search::{
parse_search_response, QueryHits, QueryResponse, QuerySource, SearchQuery,
};
use crate::common::error::SearchError;
use crate::common::http::get_response_body_text;
use crate::common::search::{QueryHits, QueryResponse, QuerySource, SearchQuery, SearchResponse};
use crate::common::server::Server;
use crate::common::traits::{SearchError, SearchSource};
use crate::common::traits::SearchSource;
use crate::server::http_client::HttpClient;
use crate::server::servers::get_server_token;
use async_trait::async_trait;
@@ -46,7 +46,7 @@ impl DocumentsSizedCollector {
}
}
fn documents(self) -> impl ExactSizeIterator<Item = Document> {
fn documents(self) -> impl ExactSizeIterator<Item=Document> {
self.docs.into_iter().map(|(_, doc, _)| doc)
}
@@ -126,58 +126,42 @@ impl SearchSource for CocoSearchSource {
}
}
// Directly return Result<QueryResponse, SearchError> instead of Future
async fn search(&self, query: SearchQuery) -> Result<QueryResponse, SearchError> {
let _server_id = self.server.id.clone();
let _server_name = self.server.name.clone();
let request_builder = self.build_request_from_query(&query).await.unwrap();
// Build the request from the provided query
let request_builder = self
.build_request_from_query(&query)
.await
.map_err(|e| SearchError::InternalError(e.to_string()))?;
// Send the HTTP request asynchronously
let response = request_builder.send().await;
// Send the HTTP request and handle errors
let response = request_builder
.send()
.await
.map_err(|e| SearchError::HttpError(format!("Failed to send search request: {}", e)))?;
match response {
Ok(response) => {
let status_code = response.status().as_u16();
// Use the helper function to parse the response body
let response_body = get_response_body_text(response)
.await
.map_err(|e| SearchError::ParseError(format!("Failed to read response body: {}", e)))?;
if status_code >= 200 && status_code < 400 {
// Parse the response only if the status code is successful
match parse_search_response(response).await {
Ok(response) => {
let total_hits = response.hits.total.value as usize;
let hits: Vec<(Document, f64)> = response
.hits
.hits
.into_iter()
.map(|hit| {
// Handling Option<f64> in hit._score by defaulting to 0.0 if None
(hit._source, hit._score.unwrap_or(0.0)) // Use 0.0 if _score is None
})
.collect();
// Parse the search response from the body text
let parsed: SearchResponse<Document> = serde_json::from_str(&response_body)
.map_err(|e| SearchError::ParseError(format!("Failed to parse search response: {}", e)))?;
// Return the QueryResponse with hits and total hits
Ok(QueryResponse {
source: self.get_type(),
hits,
total_hits,
})
}
Err(err) => {
// Parse error when response parsing fails
Err(SearchError::ParseError(err.to_string()))
}
}
} else {
// Handle unsuccessful HTTP status codes (e.g., 4xx, 5xx)
Err(SearchError::HttpError(format!(
"Request failed with status code: {}",
status_code
)))
}
}
Err(err) => {
// Handle error from the request itself
Err(SearchError::HttpError(err.to_string()))
}
}
// Process the parsed response
let total_hits = parsed.hits.total.value as usize;
let hits: Vec<(Document, f64)> = parsed
.hits
.hits
.into_iter()
.map(|hit| (hit._source, hit._score.unwrap_or(0.0))) // Default _score to 0.0 if None
.collect();
// Return the final result
Ok(QueryResponse {
source: self.get_type(),
hits,
total_hits,
})
}
}

View File

@@ -1,3 +1,4 @@
use crate::common::http::get_response_body_text;
use crate::common::register::SearchSourceRegistry;
use crate::common::server::{AuthProvider, Provider, Server, ServerAccessToken, Sso, Version};
use crate::server::connector::fetch_connectors_by_server;
@@ -6,7 +7,7 @@ use crate::server::http_client::HttpClient;
use crate::server::search::CocoSearchSource;
use crate::COCO_TAURI_STORE;
use lazy_static::lazy_static;
use reqwest::{Client, Method, StatusCode};
use reqwest::{Client, Method};
use serde_json::from_value;
use serde_json::Value as JsonValue;
use std::collections::HashMap;
@@ -298,61 +299,57 @@ pub async fn refresh_coco_server_info<R: Runtime>(
id: String,
) -> Result<Server, String> {
// Retrieve the server from the cache
let server = {
let cached_server = {
let cache = SERVER_CACHE.read().unwrap();
cache.get(&id).cloned()
};
if let Some(server) = server {
let is_enabled = server.enabled;
let is_builtin = server.builtin;
let profile = server.profile;
let mut server = match cached_server {
Some(server) => server,
None => return Err("Server not found.".into()),
};
// Use the HttpClient to send the request
let response = HttpClient::get(&id, "/provider/_info", None) // Assuming "/provider-info" is the endpoint
.await
.map_err(|e| format!("Failed to send request to the server: {}", e))?;
// Preserve important local state
let is_enabled = server.enabled;
let is_builtin = server.builtin;
let profile = server.profile;
if response.status() == StatusCode::OK {
if let Some(content_length) = response.content_length() {
if content_length > 0 {
let new_coco_server: Result<Server, _> = response.json().await;
match new_coco_server {
Ok(mut server) => {
server.id = id.clone();
server.builtin = is_builtin;
server.enabled = is_enabled;
server.available = true;
server.profile = profile;
trim_endpoint_last_forward_slash(&mut server);
save_server(&server);
persist_servers(&app_handle)
.await
.expect("Failed to persist coco servers.");
// Send request to fetch updated server info
let response = HttpClient::get(&id, "/provider/_info", None)
.await
.map_err(|e| format!("Failed to contact the server: {}", e))?;
//refresh connectors and datasources
let _ = fetch_connectors_by_server(&id).await;
let _ = get_datasources_by_server(&id, None).await;
Ok(server)
}
Err(e) => Err(format!("Failed to deserialize the response: {:?}", e)),
}
} else {
Err("Received empty response body.".to_string())
}
} else {
mark_server_as_offline(id.as_str()).await;
Err("Could not determine the content length.".to_string())
}
} else {
mark_server_as_offline(id.as_str()).await;
Err(format!("Request failed with status: {}", response.status()))
}
} else {
Err("Server not found.".to_string())
if !response.status().is_success() {
mark_server_as_offline(&id).await;
return Err(format!("Request failed with status: {}", response.status()));
}
// Get body text via helper
let body = get_response_body_text(response).await?;
// Deserialize server
let mut updated_server: Server = serde_json::from_str(&body)
.map_err(|e| format!("Failed to deserialize the response: {}", e))?;
// Restore local state
updated_server.id = id.clone();
updated_server.builtin = is_builtin;
updated_server.enabled = is_enabled;
updated_server.available = true;
updated_server.profile = profile;
trim_endpoint_last_forward_slash(&mut updated_server);
// Save and persist
save_server(&updated_server);
persist_servers(&app_handle)
.await
.map_err(|e| format!("Failed to persist servers: {}", e))?;
// Refresh connectors and datasources (best effort)
let _ = fetch_connectors_by_server(&id).await;
let _ = get_datasources_by_server(&id, None).await;
Ok(updated_server)
}
#[tauri::command]
@@ -362,12 +359,10 @@ pub async fn add_coco_server<R: Runtime>(
) -> Result<Server, String> {
load_or_insert_default_server(&app_handle)
.await
.expect("Failed to load default servers");
.map_err(|e| format!("Failed to load default servers: {}", e))?;
// Remove the trailing '/' from the endpoint to ensure correct URL construction
let endpoint = endpoint.trim_end_matches('/');
// Check if the server with this endpoint already exists
if check_endpoint_exists(endpoint) {
dbg!(format!(
"This Coco server has already been registered: {:?}",
@@ -376,59 +371,37 @@ pub async fn add_coco_server<R: Runtime>(
return Err("This Coco server has already been registered.".into());
}
let url = provider_info_url(&endpoint);
// Use the HttpClient to fetch provider information
let url = provider_info_url(endpoint);
let response = HttpClient::send_raw_request(Method::GET, url.as_str(), None, None, None)
.await
.map_err(|e| format!("Failed to send request to the server: {}", e))?;
dbg!(format!("Get provider info response: {:?}", &response));
// Check if the response status is OK (200)
if response.status() == StatusCode::OK {
if let Some(content_length) = response.content_length() {
if content_length > 0 {
let new_coco_server: Result<Server, _> = response.json().await;
let body = get_response_body_text(response).await?;
match new_coco_server {
Ok(mut server) => {
// Perform necessary checks and adjustments on the server data
trim_endpoint_last_forward_slash(&mut server);
let mut server: Server = serde_json::from_str(&body)
.map_err(|e| format!("Failed to deserialize the response: {}", e))?;
if server.id.is_empty() {
server.id = pizza_common::utils::uuid::Uuid::new().to_string();
}
trim_endpoint_last_forward_slash(&mut server);
if server.name.is_empty() {
server.name = "Coco Cloud".to_string();
}
// Save the new server to the cache
save_server(&server);
// Register the server to the search source
try_register_server_to_search_source(app_handle.clone(), &server).await;
// Persist the servers to the store
persist_servers(&app_handle)
.await
.expect("Failed to persist Coco servers.");
dbg!(format!("Successfully registered server: {:?}", &endpoint));
Ok(server)
}
Err(e) => Err(format!("Failed to deserialize the response: {}", e)),
}
} else {
Err("Received empty response body.".to_string())
}
} else {
Err("Could not determine the content length.".to_string())
}
} else {
Err(format!("Request failed with status: {}", response.status()))
if server.id.is_empty() {
server.id = pizza_common::utils::uuid::Uuid::new().to_string();
}
if server.name.is_empty() {
server.name = "Coco Server".to_string();
}
save_server(&server);
try_register_server_to_search_source(app_handle.clone(), &server).await;
persist_servers(&app_handle)
.await
.map_err(|e| format!("Failed to persist Coco servers: {}", e))?;
dbg!(format!("Successfully registered server: {:?}", &endpoint));
Ok(server)
}
#[tauri::command]

View File

@@ -1,3 +1,4 @@
use crate::common::http::get_response_body_text;
use crate::server::http_client::HttpClient;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
@@ -19,23 +20,24 @@ pub async fn transcription(
query_params.insert("type".to_string(), JsonValue::String(audio_type));
query_params.insert("content".to_string(), JsonValue::String(audio_content));
// Send the HTTP POST request
let response = HttpClient::post(
&server_id,
"/services/audio/transcription",
Some(query_params),
None,
)
.await?;
.await
.map_err(|e| format!("Error sending transcription request: {}", e))?;
if response.status().is_success() {
response
.json::<TranscriptionResponse>()
.await
.map_err(|e| e.to_string())
} else {
Err(format!(
"Transcription failed with status: {}",
response.status()
))
}
// Use get_response_body_text to extract the response body as text
let response_body = get_response_body_text(response)
.await
.map_err(|e| format!("Failed to read response body: {}", e))?;
// Deserialize the response body into TranscriptionResponse
let transcription_response: TranscriptionResponse = serde_json::from_str(&response_body)
.map_err(|e| format!("Failed to parse transcription response: {}", e))?;
Ok(transcription_response)
}