! re-refactor of Error - back to genai::Error (as adapter::Error was unnecessarily exposing internal responsibility)

This commit is contained in:
Jeremy Chone
2024-07-19 10:44:32 -07:00
parent 8de900c237
commit b87c76f62a
20 changed files with 115 additions and 105 deletions

View File

@ -1,8 +1,8 @@
use crate::adapter::Result;
use crate::adapter::{AdapterConfig, AdapterKind};
use crate::chat::{ChatRequest, ChatRequestOptionsSet, ChatResponse, ChatStreamResponse};
use crate::webc::WebResponse;
use crate::ConfigSet;
use crate::Result;
use reqwest::RequestBuilder;
use serde_json::Value;

View File

@ -1,6 +1,6 @@
use crate::adapter::anthropic::AnthropicStreamer;
use crate::adapter::support::get_api_key_resolver;
use crate::adapter::Result;
use crate::Result;
use crate::adapter::{Adapter, AdapterConfig, AdapterKind, ServiceType, WebRequestData};
use crate::chat::{
ChatRequest, ChatRequestOptionsSet, ChatResponse, ChatRole, ChatStream, ChatStreamResponse, MessageContent,

View File

@ -1,6 +1,6 @@
use crate::adapter::adapters::support::{StreamerCapturedData, StreamerOptions};
use crate::adapter::inter_stream::{InterStreamEnd, InterStreamEvent};
use crate::adapter::{Error, Result};
use crate::{Error, Result};
use crate::chat::{ChatRequestOptionsSet, MetaUsage};
use crate::support::value_ext::ValueExt;
use reqwest_eventsource::{Event, EventSource};

View File

@ -1,7 +1,6 @@
use crate::adapter::cohere::CohereStreamer;
use crate::adapter::support::get_api_key_resolver;
use crate::adapter::{Adapter, AdapterConfig, AdapterKind, ServiceType, WebRequestData};
use crate::adapter::{Error, Result};
use crate::chat::{
ChatRequest, ChatRequestOptionsSet, ChatResponse, ChatRole, ChatStream, ChatStreamResponse, MessageContent,
MetaUsage,
@ -9,6 +8,7 @@ use crate::chat::{
use crate::support::value_ext::ValueExt;
use crate::webc::{WebResponse, WebStream};
use crate::ConfigSet;
use crate::{Error, Result};
use reqwest::RequestBuilder;
use serde_json::{json, Value};
use std::sync::OnceLock;
@ -96,15 +96,17 @@ impl Adapter for CohereAdapter {
Ok(WebRequestData { url, headers, payload })
}
fn to_chat_response(_kind: AdapterKind, web_response: WebResponse) -> Result<ChatResponse> {
fn to_chat_response(adapter_kind: AdapterKind, web_response: WebResponse) -> Result<ChatResponse> {
let WebResponse { mut body, .. } = web_response;
// -- Get usage
let usage = body.x_take("/meta/tokens").map(Self::into_usage).unwrap_or_default();
// -- Get response
let mut last_chat_history_item =
body.x_take::<Vec<Value>>("chat_history")?.pop().ok_or(Error::NoChatResponse)?;
let mut last_chat_history_item = body
.x_take::<Vec<Value>>("chat_history")?
.pop()
.ok_or(Error::NoChatResponse { adapter_kind })?;
let content: Option<MessageContent> = last_chat_history_item
.x_take::<Option<String>>("message")?
@ -173,7 +175,7 @@ impl CohereAdapter {
}
// -- Build extract the last user message
let last_chat_msg = chat_req.messages.pop().ok_or(Error::ChatReqHasNoMessages)?;
let last_chat_msg = chat_req.messages.pop().ok_or(Error::ChatReqHasNoMessages { adapter_kind })?;
if !matches!(last_chat_msg.role, ChatRole::User) {
return Err(Error::LastChatMessageIsNoUser {
actual_role: last_chat_msg.role,
@ -193,7 +195,7 @@ impl CohereAdapter {
ChatRole::User => chat_history.push(json! ({"role": "USER", "content": content})),
ChatRole::Assistant => chat_history.push(json! ({"role": "CHATBOT", "content": content})),
ChatRole::Tool => {
return Err(Error::MessageRoleNotSupport {
return Err(Error::MessageRoleNotSupported {
adapter_kind,
role: ChatRole::Tool,
})

View File

@ -1,10 +1,11 @@
use crate::adapter::adapters::support::{StreamerCapturedData, StreamerOptions};
use crate::adapter::cohere::CohereAdapter;
use crate::adapter::inter_stream::{InterStreamEnd, InterStreamEvent};
use crate::adapter::{Error, Result};
use crate::adapter::AdapterKind;
use crate::chat::ChatRequestOptionsSet;
use crate::support::value_ext::ValueExt;
use crate::webc::WebStream;
use crate::{Error, Result};
use serde::Deserialize;
use serde_json::Value;
use std::pin::Pin;
@ -118,7 +119,10 @@ impl futures::Stream for CohereStreamer {
}
Some(Err(err)) => {
println!("Cohere Adapter Stream Error: {}", err);
return Poll::Ready(Some(Err(Error::WebStream)));
return Poll::Ready(Some(Err(Error::WebStream {
adapter_kind: AdapterKind::Cohere,
cause: err.to_string(),
})));
}
None => {
self.done = true;

View File

@ -1,7 +1,6 @@
use crate::adapter::gemini::GeminiStreamer;
use crate::adapter::support::get_api_key_resolver;
use crate::adapter::{Adapter, AdapterConfig, AdapterKind, ServiceType, WebRequestData};
use crate::adapter::{Error, Result};
use crate::chat::{
ChatRequest, ChatRequestOptionsSet, ChatResponse, ChatRole, ChatStream, ChatStreamResponse, MessageContent,
MetaUsage,
@ -9,6 +8,7 @@ use crate::chat::{
use crate::support::value_ext::ValueExt;
use crate::webc::{WebResponse, WebStream};
use crate::ConfigSet;
use crate::{Error, Result};
use reqwest::RequestBuilder;
use serde_json::{json, Value};
use std::sync::OnceLock;
@ -177,7 +177,7 @@ impl GeminiAdapter {
ChatRole::User => contents.push(json! ({"role": "user", "parts": [{"text": content}]})),
ChatRole::Assistant => contents.push(json! ({"role": "model", "parts": [{"text": content}]})),
ChatRole::Tool => {
return Err(Error::MessageRoleNotSupport {
return Err(Error::MessageRoleNotSupported {
adapter_kind,
role: ChatRole::Tool,
})

View File

@ -1,9 +1,10 @@
use crate::adapter::adapters::support::{StreamerCapturedData, StreamerOptions};
use crate::adapter::gemini::{GeminiAdapter, GeminiChatResponse};
use crate::adapter::inter_stream::{InterStreamEnd, InterStreamEvent};
use crate::adapter::{Error, Result};
use crate::adapter::AdapterKind;
use crate::chat::ChatRequestOptionsSet;
use crate::webc::WebStream;
use crate::{Error, Result};
use serde_json::Value;
use std::pin::Pin;
use std::task::{Context, Poll};
@ -106,7 +107,10 @@ impl futures::Stream for GeminiStreamer {
}
Some(Err(err)) => {
println!("Gemini Adapter Stream Error: {}", err);
return Poll::Ready(Some(Err(Error::WebStream)));
return Poll::Ready(Some(Err(Error::WebStream {
adapter_kind: AdapterKind::Gemini,
cause: err.to_string(),
})));
}
None => {
self.done = true;

View File

@ -1,6 +1,6 @@
use crate::adapter::openai::OpenAIAdapter;
use crate::adapter::support::get_api_key_resolver;
use crate::adapter::Result;
use crate::Result;
use crate::adapter::{Adapter, AdapterConfig, AdapterKind, ServiceType, WebRequestData};
use crate::chat::{ChatRequest, ChatRequestOptionsSet, ChatResponse, ChatStreamResponse};
use crate::webc::WebResponse;

View File

@ -1,12 +1,12 @@
//! API DOC: https://github.com/ollama/ollama/blob/main/docs/openai.md
use crate::adapter::openai::OpenAIAdapter;
use crate::adapter::Result;
use crate::adapter::{Adapter, AdapterConfig, AdapterKind, ServiceType, WebRequestData};
use crate::chat::{ChatRequest, ChatRequestOptionsSet, ChatResponse, ChatStreamResponse};
use crate::support::value_ext::ValueExt;
use crate::webc::WebResponse;
use crate::ConfigSet;
use crate::{Error, Result};
use reqwest::RequestBuilder;
use serde_json::Value;
use std::sync::OnceLock;
@ -22,12 +22,15 @@ const OLLAMA_BASE_URL: &str = "http://localhost:11434/api/";
/// Since the base ollama API supports `application/x-ndjson` for streaming whereas others support `text/event-stream`
impl Adapter for OllamaAdapter {
/// Note: For now returns empty as it should probably do a request to the ollama server
async fn all_model_names(_kind: AdapterKind) -> Result<Vec<String>> {
async fn all_model_names(adapter_kind: AdapterKind) -> Result<Vec<String>> {
let url = format!("{OLLAMA_BASE_URL}tags");
// TODO: need to get the WebClient from the client.
let web_c = crate::webc::WebClient::default();
let mut res = web_c.do_get(&url, &[]).await?;
let mut res = web_c.do_get(&url, &[]).await.map_err(|webc_error| Error::WebCall {
adapter_kind,
webc_error,
})?;
let mut models: Vec<String> = Vec::new();

View File

@ -1,7 +1,6 @@
use crate::adapter::openai::OpenAIStreamer;
use crate::adapter::support::get_api_key_resolver;
use crate::adapter::{Adapter, AdapterConfig, AdapterKind, ServiceType, WebRequestData};
use crate::adapter::{Error, Result};
use crate::chat::{
ChatRequest, ChatRequestOptionsSet, ChatResponse, ChatRole, ChatStream, ChatStreamResponse, MessageContent,
MetaUsage,
@ -9,6 +8,7 @@ use crate::chat::{
use crate::support::value_ext::ValueExt;
use crate::webc::WebResponse;
use crate::ConfigSet;
use crate::{Error, Result};
use reqwest::RequestBuilder;
use reqwest_eventsource::EventSource;
use serde_json::{json, Value};
@ -194,7 +194,7 @@ impl OpenAIAdapter {
ChatRole::User => messages.push(json! ({"role": "user", "content": content})),
ChatRole::Assistant => messages.push(json! ({"role": "assistant", "content": content})),
ChatRole::Tool => {
return Err(Error::MessageRoleNotSupport {
return Err(Error::MessageRoleNotSupported {
adapter_kind,
role: ChatRole::Tool,
})

View File

@ -2,9 +2,9 @@ use crate::adapter::adapters::support::{StreamerCapturedData, StreamerOptions};
use crate::adapter::inter_stream::{InterStreamEnd, InterStreamEvent};
use crate::adapter::openai::OpenAIAdapter;
use crate::adapter::AdapterKind;
use crate::adapter::{Error, Result};
use crate::chat::ChatRequestOptionsSet;
use crate::support::value_ext::ValueExt;
use crate::{Error, Result};
use reqwest_eventsource::{Event, EventSource};
use serde_json::Value;
use std::pin::Pin;

View File

@ -3,11 +3,11 @@ use crate::adapter::cohere::CohereAdapter;
use crate::adapter::gemini::GeminiAdapter;
use crate::adapter::ollama::OllamaAdapter;
use crate::adapter::openai::OpenAIAdapter;
use crate::adapter::Result;
use crate::adapter::{Adapter, AdapterConfig, AdapterKind, ServiceType, WebRequestData};
use crate::chat::{ChatRequest, ChatRequestOptionsSet, ChatResponse, ChatStreamResponse};
use crate::webc::WebResponse;
use crate::ConfigSet;
use crate::Result;
use reqwest::RequestBuilder;
use super::groq::GroqAdapter;

View File

@ -1,66 +0,0 @@
use crate::adapter::AdapterKind;
use crate::chat::ChatRole;
use crate::{resolver, webc};
use derive_more::From;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, From)]
pub enum Error {
// -- Adapter Chat Request
ChatReqHasNoMessages,
LastChatMessageIsNoUser {
actual_role: ChatRole,
},
// -- Adapter Chat Response
NoChatResponse,
// -- Adapter
RequiresApiKey {
adapter_kind: AdapterKind,
},
MessageRoleNotSupport {
adapter_kind: AdapterKind,
role: ChatRole,
},
HasNoDefaultApiKeyEnvName,
NoAuthResolver {
adapter_kind: AdapterKind,
},
AuthResolverNoAuthData {
adapter_kind: AdapterKind,
},
// -- Stream
StreamParse(serde_json::Error),
StreamEventError(serde_json::Value),
WebStream,
// -- Modules
#[from]
Webc(webc::Error),
#[from]
Resolver(resolver::Error),
// -- Utils
#[from]
XValue(crate::support::value_ext::Error),
// -- Externals
#[from]
EventSourceClone(reqwest_eventsource::CannotCloneRequestError),
ReqwestEventSource(reqwest_eventsource::Error),
}
// region: --- Error Boilerplate
impl core::fmt::Display for Error {
fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::result::Result<(), core::fmt::Error> {
write!(fmt, "{self:?}")
}
}
impl std::error::Error for Error {}
// endregion: --- Error Boilerplate

View File

@ -14,7 +14,6 @@ mod adapter_kind;
mod adapter_types;
mod adapters;
mod dispatcher;
mod error;
mod support;
// -- Flatten (private, crate, public)
@ -23,7 +22,6 @@ use adapters::*;
pub(crate) use adapter_types::*;
pub(crate) use dispatcher::*;
pub use self::error::{Error, Result};
pub use adapter_config::*;
pub use adapter_kind::*;

View File

@ -1,6 +1,6 @@
use crate::adapter::AdapterKind;
use crate::adapter::{Error, Result};
use crate::ConfigSet;
use crate::{Error, Result};
/// Returns the `api_key` value from the config_set auth_resolver
/// This function should be called if the adapter must have a api_key

View File

@ -5,7 +5,7 @@ use futures::Stream;
use std::pin::Pin;
use std::task::{Context, Poll};
type InterStreamType = Pin<Box<dyn Stream<Item = crate::adapter::Result<InterStreamEvent>>>>;
type InterStreamType = Pin<Box<dyn Stream<Item = crate::Result<InterStreamEvent>>>>;
pub struct ChatStream {
inter_stream: InterStreamType,
@ -18,7 +18,7 @@ impl ChatStream {
pub fn from_inter_stream<T>(inter_stream: T) -> Self
where
T: Stream<Item = crate::adapter::Result<InterStreamEvent>> + Unpin + 'static,
T: Stream<Item = crate::Result<InterStreamEvent>> + Unpin + 'static,
{
let boxed_stream: InterStreamType = Box::pin(inter_stream);
ChatStream::new(boxed_stream)
@ -28,7 +28,7 @@ impl ChatStream {
// region: --- Stream Impl
impl Stream for ChatStream {
type Item = crate::adapter::Result<ChatStreamEvent>;
type Item = crate::Result<ChatStreamEvent>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();

View File

@ -1,7 +1,7 @@
use crate::adapter::{Adapter, AdapterDispatcher, AdapterKind, ServiceType, WebRequestData};
use crate::chat::{ChatRequest, ChatRequestOptions, ChatRequestOptionsSet, ChatResponse, ChatStreamResponse};
use crate::client::Client;
use crate::{ConfigSet, Result};
use crate::{ConfigSet, Error, Result};
/// Public AI Functions
impl Client {
@ -66,7 +66,14 @@ impl Client {
options_set,
)?;
let web_res = self.web_client().do_post(&url, &headers, payload).await?;
let web_res =
self.web_client()
.do_post(&url, &headers, payload)
.await
.map_err(|webc_error| Error::WebCall {
adapter_kind,
webc_error,
})?;
let chat_res = AdapterDispatcher::to_chat_response(adapter_kind, web_res)?;
@ -100,7 +107,13 @@ impl Client {
options_set.clone(),
)?;
let reqwest_builder = self.web_client().new_req_builder(&url, &headers, payload)?;
let reqwest_builder = self
.web_client()
.new_req_builder(&url, &headers, payload)
.map_err(|webc_error| Error::WebCall {
adapter_kind,
webc_error,
})?;
let res = AdapterDispatcher::to_chat_stream(adapter_kind, reqwest_builder, options_set)?;

View File

@ -1,16 +1,68 @@
use crate::adapter::{self};
use crate::webc;
use crate::adapter::AdapterKind;
use crate::chat::ChatRole;
use crate::{resolver, webc};
use derive_more::From;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, From)]
pub enum Error {
#[from]
Webc(webc::Error),
// -- Chat Input
ChatReqHasNoMessages {
adapter_kind: AdapterKind,
},
LastChatMessageIsNoUser {
actual_role: ChatRole,
},
MessageRoleNotSupported {
adapter_kind: AdapterKind,
role: ChatRole,
},
// -- Chat Output
NoChatResponse {
adapter_kind: AdapterKind,
},
// -- Auth
RequiresApiKey {
adapter_kind: AdapterKind,
},
NoAuthResolver {
adapter_kind: AdapterKind,
},
AuthResolverNoAuthData {
adapter_kind: AdapterKind,
},
// -- Web Call error
WebCall {
adapter_kind: AdapterKind,
webc_error: webc::Error,
},
// -- Chat Stream
StreamParse(serde_json::Error),
StreamEventError(serde_json::Value),
WebStream {
adapter_kind: AdapterKind,
cause: String,
},
// -- Modules
// #[from]
// Webc(webc::Error),
#[from]
Adapter(adapter::Error),
Resolver(resolver::Error),
// -- Utils
#[from]
XValue(crate::support::value_ext::Error),
// -- Externals
#[from]
EventSourceClone(reqwest_eventsource::CannotCloneRequestError),
ReqwestEventSource(reqwest_eventsource::Error),
}
// region: --- Error Boilerplate

View File

@ -9,8 +9,8 @@ mod adapter_kind_resolver;
mod auth_resolver;
mod error;
pub use self::error::{Error, Result};
pub use adapter_kind_resolver::*;
pub use auth_resolver::*;
pub use error::{Error, Result};
// endregion: --- Modules

View File

@ -5,7 +5,7 @@ mod web_client;
// for when not `text/event-stream`
mod web_stream;
pub use self::error::{Error, Result};
pub use error::{Error, Result};
pub use web_client::*;
pub use web_stream::*;