mirror of
https://github.com/infinilabs/coco-app.git
synced 2026-02-24 12:10:28 +01:00
refactor: refactoring api error handling (#382)
* refactor: refactoring api error handling * chore: update release notes * chore: merge from main
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
45
src-tauri/src/common/error.rs
Normal file
45
src-tauri/src/common/error.rs
Normal 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())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user