. move internal value_ext to jc crate value-ext (for internal code only)

This commit is contained in:
Jeremy Chone
2024-09-04 09:23:33 -07:00
parent 616c53b8c8
commit 43d4eb2be0
14 changed files with 14 additions and 281 deletions

View File

@ -28,3 +28,4 @@ eventsource-stream = "0.2"
bytes = "1.6"
# -- Others
derive_more = { version = "1.0.0-beta", features = ["from", "display"] }
value-ext = "0.0.1" # JC Authored. Early release (API might change). Be cautious when using in other projects.

View File

@ -4,13 +4,13 @@ use crate::adapter::{Adapter, AdapterKind, ServiceType, WebRequestData};
use crate::chat::{
ChatOptionsSet, ChatRequest, ChatResponse, ChatRole, ChatStream, ChatStreamResponse, MessageContent, MetaUsage,
};
use crate::support::value_ext::ValueExt;
use crate::webc::WebResponse;
use crate::Result;
use crate::{ClientConfig, ModelIden};
use reqwest::RequestBuilder;
use reqwest_eventsource::EventSource;
use serde_json::{json, Value};
use value_ext::JsonValueExt;
pub struct AnthropicAdapter;

View File

@ -1,12 +1,12 @@
use crate::adapter::adapters::support::{StreamerCapturedData, StreamerOptions};
use crate::adapter::inter_stream::{InterStreamEnd, InterStreamEvent};
use crate::chat::{ChatOptionsSet, MetaUsage};
use crate::support::value_ext::ValueExt;
use crate::{Error, ModelIden, Result};
use reqwest_eventsource::{Event, EventSource};
use serde_json::Value;
use std::pin::Pin;
use std::task::{Context, Poll};
use value_ext::JsonValueExt;
pub struct AnthropicStreamer {
inner: EventSource,

View File

@ -4,12 +4,12 @@ use crate::adapter::{Adapter, AdapterKind, ServiceType, WebRequestData};
use crate::chat::{
ChatOptionsSet, ChatRequest, ChatResponse, ChatRole, ChatStream, ChatStreamResponse, MessageContent, MetaUsage,
};
use crate::support::value_ext::ValueExt;
use crate::webc::{WebResponse, WebStream};
use crate::{ClientConfig, ModelIden};
use crate::{Error, Result};
use reqwest::RequestBuilder;
use serde_json::{json, Value};
use value_ext::JsonValueExt;
pub struct CohereAdapter;

View File

@ -2,13 +2,13 @@ use crate::adapter::adapters::support::{StreamerCapturedData, StreamerOptions};
use crate::adapter::cohere::CohereAdapter;
use crate::adapter::inter_stream::{InterStreamEnd, InterStreamEvent};
use crate::chat::ChatOptionsSet;
use crate::support::value_ext::ValueExt;
use crate::webc::WebStream;
use crate::{Error, ModelIden, Result};
use serde::Deserialize;
use serde_json::Value;
use std::pin::Pin;
use std::task::{Context, Poll};
use value_ext::JsonValueExt;
pub struct CohereStreamer {
inner: WebStream,

View File

@ -4,12 +4,12 @@ use crate::adapter::{Adapter, AdapterKind, ServiceType, WebRequestData};
use crate::chat::{
ChatOptionsSet, ChatRequest, ChatResponse, ChatRole, ChatStream, ChatStreamResponse, MessageContent, MetaUsage,
};
use crate::support::value_ext::ValueExt;
use crate::webc::{WebResponse, WebStream};
use crate::{ClientConfig, ModelIden};
use crate::{Error, Result};
use reqwest::RequestBuilder;
use serde_json::{json, Value};
use value_ext::JsonValueExt;
pub struct GeminiAdapter;

View File

@ -3,12 +3,12 @@
use crate::adapter::openai::OpenAIAdapter;
use crate::adapter::{Adapter, AdapterKind, ServiceType, WebRequestData};
use crate::chat::{ChatOptionsSet, ChatRequest, ChatResponse, ChatStreamResponse};
use crate::support::value_ext::ValueExt;
use crate::webc::WebResponse;
use crate::{ClientConfig, ModelIden};
use crate::{Error, Result};
use reqwest::RequestBuilder;
use serde_json::Value;
use value_ext::JsonValueExt;
pub struct OllamaAdapter;

View File

@ -5,13 +5,13 @@ use crate::chat::{
ChatOptionsSet, ChatRequest, ChatResponse, ChatResponseFormat, ChatRole, ChatStream, ChatStreamResponse,
MessageContent, MetaUsage,
};
use crate::support::value_ext::ValueExt;
use crate::webc::WebResponse;
use crate::{ClientConfig, ModelIden};
use crate::{Error, Result};
use reqwest::RequestBuilder;
use reqwest_eventsource::EventSource;
use serde_json::{json, Value};
use value_ext::JsonValueExt;
pub struct OpenAIAdapter;

View File

@ -3,12 +3,12 @@ use crate::adapter::inter_stream::{InterStreamEnd, InterStreamEvent};
use crate::adapter::openai::OpenAIAdapter;
use crate::adapter::AdapterKind;
use crate::chat::ChatOptionsSet;
use crate::support::value_ext::ValueExt;
use crate::{Error, ModelIden, Result};
use reqwest_eventsource::{Event, EventSource};
use serde_json::Value;
use std::pin::Pin;
use std::task::{Context, Poll};
use value_ext::JsonValueExt;
pub struct OpenAIStreamer {
inner: EventSource,

View File

@ -2,6 +2,7 @@ use crate::adapter::AdapterKind;
use crate::chat::ChatRole;
use crate::{resolver, webc, ModelIden};
use derive_more::From;
use value_ext::JsonValueExtError;
pub type Result<T> = core::result::Result<T, Error>;
@ -74,12 +75,12 @@ pub enum Error {
},
// -- Utils
#[from]
XValue(crate::support::value_ext::Error),
// -- Externals
#[from]
EventSourceClone(reqwest_eventsource::CannotCloneRequestError),
#[from]
JsonValueExt(JsonValueExtError),
ReqwestEventSource(reqwest_eventsource::Error),
}

View File

@ -1,7 +1,5 @@
// region: --- Modules
mod support;
mod client;
mod common;
mod error;

View File

@ -1,5 +0,0 @@
// region: --- Modules
pub mod value_ext;
// endregion: --- Modules

View File

@ -1,263 +0,0 @@
use serde::de::DeserializeOwned;
use serde::Serialize;
use serde_json::{json, Map, Value};
use std::collections::VecDeque;
pub trait ValueExt {
/// Will return the value T for a given name/pointer path.
/// - `name` - can be the direct name, or pointer path if starts with '/'
fn x_get<T: DeserializeOwned>(&self, name_or_pointer: &str) -> Result<T>;
/// Take the value at the name or pointer path, and replaces it with Null.
/// - `name` - can be the direct name, or pointer path if starts with '/'
fn x_take<T: DeserializeOwned>(&mut self, name_or_pointer: &str) -> Result<T>;
/// Insert a new Value of type T at the specified name or pointer path.
/// This method will create missing Value::Object as needed.
/// - `name` - can be the direct name, or pointer path if starts with '/'
fn x_insert<T: Serialize>(&mut self, name_or_pointer: &str, value: T) -> Result<()>;
#[allow(unused)]
fn x_walk<F>(&mut self, callback: F) -> bool
where
F: FnMut(&mut Map<String, Value>, &str) -> bool;
/// Return the pretty_print string for this json value
#[allow(unused)]
fn x_pretty(&self) -> Result<String>;
}
impl ValueExt for Value {
fn x_get<T: DeserializeOwned>(&self, name_or_pointer: &str) -> Result<T> {
let value = if name_or_pointer.starts_with('/') {
self.pointer(name_or_pointer)
.ok_or_else(|| Error::PropertyNotFound(name_or_pointer.to_string()))?
} else {
self.get(name_or_pointer)
.ok_or_else(|| Error::PropertyNotFound(name_or_pointer.to_string()))?
};
let value: T = serde_json::from_value(value.clone())?;
Ok(value)
}
fn x_take<T: DeserializeOwned>(&mut self, name_or_pointer: &str) -> Result<T> {
let value = if name_or_pointer.starts_with('/') {
self.pointer_mut(name_or_pointer)
.map(Value::take)
.ok_or_else(|| Error::PropertyNotFound(name_or_pointer.to_string()))?
} else {
self.get_mut(name_or_pointer)
.map(Value::take)
.ok_or_else(|| Error::PropertyNotFound(name_or_pointer.to_string()))?
};
let value: T = serde_json::from_value(value)?;
Ok(value)
}
fn x_insert<T: Serialize>(&mut self, name_or_pointer: &str, value: T) -> Result<()> {
let new_value = serde_json::to_value(value)?;
if !name_or_pointer.starts_with('/') {
match self {
Value::Object(map) => {
map.insert(name_or_pointer.to_string(), new_value);
Ok(())
}
_ => Err(Error::custom("Value is not an Object, cannot x_insert")),
}
} else {
let parts: Vec<&str> = name_or_pointer.split('/').skip(1).collect();
let mut current = self;
// -- Add the eventual missing parents
for &part in &parts[..parts.len() - 1] {
match current {
Value::Object(map) => {
current = map.entry(part).or_insert_with(|| json!({}));
}
_ => return Err(Error::custom("Path does not point to an Object")),
}
}
// -- Set the value at the last element
if let Some(&last_part) = parts.last() {
match current {
Value::Object(map) => {
map.insert(last_part.to_string(), new_value);
Ok(())
}
_ => Err(Error::custom("Path does not point to an Object")),
}
} else {
Err(Error::custom("Invalid path"))
}
}
}
fn x_pretty(&self) -> Result<String> {
let content = serde_json::to_string_pretty(self)?;
Ok(content)
}
/// Walks through all properties of a JSON value tree and calls the callback function on each property.
///
/// - The callback signature is `(parent_map, property_name) -> bool`.
/// - Return `false` from the callback to stop the traversal; return `true` to continue.
///
/// Returns:
/// - `true` if the traversal completed to the end without being stopped early.
/// - `false` if the traversal was stopped early because the callback returned `false`.
fn x_walk<F>(&mut self, mut callback: F) -> bool
where
F: FnMut(&mut Map<String, Value>, &str) -> bool,
{
let mut queue = VecDeque::new();
queue.push_back(self);
while let Some(current) = queue.pop_front() {
if let Value::Object(map) = current {
// Call the callback for each property name in the current map
for key in map.keys().cloned().collect::<Vec<_>>() {
let res = callback(map, &key);
if !res {
return false;
}
}
// Add all nested objects and arrays to the queue for further processing
for value in map.values_mut() {
if value.is_object() || value.is_array() {
queue.push_back(value);
}
}
} else if let Value::Array(arr) = current {
// If current value is an array, add its elements to the queue
for value in arr.iter_mut() {
if value.is_object() || value.is_array() {
queue.push_back(value);
}
}
}
}
true
}
}
// region: --- Error
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, derive_more::From)]
pub enum Error {
Custom(String),
PropertyNotFound(String),
#[from]
SerdeJson(serde_json::Error),
}
impl Error {
fn custom(val: impl std::fmt::Display) -> Self {
Self::Custom(val.to_string())
}
}
// 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
// endregion: --- Error
// region: --- Tests
#[cfg(test)]
mod tests {
type Error = Box<dyn std::error::Error>;
type Result<T> = core::result::Result<T, Error>; // For tests.
use super::*;
#[test]
fn test_value_insert_ok() -> Result<()> {
// -- Setup & Fixtures
let mut value = json!({"tokens": 3});
let fx_node_value = "hello";
// -- Exec
value.x_insert("/happy/word", fx_node_value)?;
// -- Check
let actual_value: String = value.x_get("/happy/word")?;
assert_eq!(actual_value.as_str(), fx_node_value);
Ok(())
}
#[test]
fn test_value_walk_ok() -> Result<()> {
// -- Setup & Fixtures
let mut root_value = json!(
{
"tokens": 3,
"schema": {
"type": "object",
"additionalProperties": false,
"properties": {
"all_models": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"maker": { "type": "string" },
"model_name": { "type": "string" }
},
"required": ["maker", "model_name"]
}
}
},
"required": ["all_models"]
}
});
// -- Exec
// Will remove "additionalProperties" (only the frist one, because return false)
root_value.x_walk(|parent_map, property_name| {
// --
if property_name == "type" {
let val = parent_map.get(property_name).and_then(|val| val.as_str());
if let Some("object") = val {
parent_map.remove("additionalProperties");
return false; // will stop early
}
}
true
});
// -- Check
// the number of "additionalProperties" left
let mut marker_count = 0;
// Will remove "additionalProperties"
root_value.x_walk(|_parent_map, property_name| {
if property_name == "additionalProperties" {
marker_count += 1;
}
true
});
assert_eq!(1, marker_count); // only 1 was removed, as callback returned false
Ok(())
}
}
// endregion: --- Tests

View File

@ -1,5 +1,6 @@
use derive_more::From;
use reqwest::StatusCode;
use value_ext::JsonValueExtError;
pub type Result<T> = core::result::Result<T, Error>;
@ -16,7 +17,7 @@ pub enum Error {
// -- Utils
#[from]
XValue(crate::support::value_ext::Error),
JsonValueExt(JsonValueExtError),
// -- Externals
#[from]