use std::fmt;
use bytes::Bytes;
use http::{HeaderMap, StatusCode};
use serde::{
Serialize, Serializer,
ser::{SerializeMap, SerializeStruct},
};
use crate::{constants::request_id, content::Content, name::Name};
#[derive(Debug, Clone, PartialEq)]
pub struct SystemOneResponse<A = Answers> {
model: Name,
usage: Usage,
answers: A,
meta: ResponseMeta,
}
impl<A> SystemOneResponse<A> {
pub(crate) fn from_parts(model: Name, usage: Usage, answers: A, meta: ResponseMeta) -> Self {
Self { model, usage, answers, meta }
}
#[must_use]
pub fn model(&self) -> &str {
self.model.as_str()
}
#[must_use]
pub fn usage(&self) -> &Usage {
&self.usage
}
#[must_use]
pub fn answers(&self) -> &A {
&self.answers
}
#[must_use]
pub fn meta(&self) -> &ResponseMeta {
&self.meta
}
#[must_use]
pub fn into_answers(self) -> A {
self.answers
}
}
impl<A> Serialize for SystemOneResponse<A>
where
A: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut out = serializer.serialize_struct("SystemOneResponse", 3)?;
out.serialize_field("model", &self.model)?;
out.serialize_field("usage", &self.usage)?;
out.serialize_field("answers", &self.answers)?;
out.end()
}
}
#[derive(Clone, PartialEq)]
pub struct ResponseMeta {
status: StatusCode,
headers: HeaderMap,
body: Bytes,
}
impl ResponseMeta {
pub(crate) fn new(status: StatusCode, headers: HeaderMap, body: Bytes) -> Self {
Self { status, headers, body }
}
pub(crate) fn into_parts(self) -> (StatusCode, HeaderMap, Bytes) {
(self.status, self.headers, self.body)
}
#[must_use]
pub fn status(&self) -> StatusCode {
self.status
}
#[must_use]
pub fn headers(&self) -> &HeaderMap {
&self.headers
}
#[must_use]
pub fn request_id(&self) -> Option<&str> {
request_id(&self.headers)
}
#[must_use]
pub fn raw_body(&self) -> &Bytes {
&self.body
}
}
impl fmt::Debug for ResponseMeta {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ResponseMeta")
.field("status", &self.status.as_u16())
.field("request_id", &self.request_id())
.field("headers", &format_args!("<{} headers>", self.headers.len()))
.field("body", &format_args!("<{} bytes>", self.body.len()))
.finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize)]
pub struct Usage {
input_tokens: Option<u64>,
output_tokens: Option<u64>,
}
impl Usage {
#[must_use]
pub fn new(input_tokens: Option<u64>, output_tokens: Option<u64>) -> Self {
Self { input_tokens, output_tokens }
}
#[must_use]
pub fn input_tokens(&self) -> Option<u64> {
self.input_tokens
}
#[must_use]
pub fn output_tokens(&self) -> Option<u64> {
self.output_tokens
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Answers {
entries: Vec<(Name, Answer)>,
}
impl Answers {
pub(crate) fn with_capacity(capacity: usize) -> Self {
Self { entries: Vec::with_capacity(capacity) }
}
pub(crate) fn push(&mut self, name: Name, answer: Answer) {
self.entries.push((name, answer));
}
#[cfg(test)]
pub(crate) fn capacity(&self) -> usize {
self.entries.capacity()
}
#[must_use]
pub fn len(&self) -> usize {
self.entries.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
#[must_use]
pub fn get(&self, name: &str) -> Option<&Answer> {
self.entries.iter().find(|(key, _)| key.as_str() == name).map(|(_, answer)| answer)
}
#[must_use]
pub fn noul(&self, name: &str) -> Option<&NoulAnswer> {
self.get(name).and_then(Answer::as_noul)
}
#[must_use]
pub fn choice(&self, name: &str) -> Option<&ChoiceAnswer> {
self.get(name).and_then(Answer::as_choice)
}
#[must_use]
pub fn score(&self, name: &str) -> Option<&ScoreAnswer> {
self.get(name).and_then(Answer::as_score)
}
pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Answer)> + DoubleEndedIterator {
self.entries.iter().map(|(name, answer)| (name.as_str(), answer))
}
pub fn names(&self) -> impl ExactSizeIterator<Item = &str> + DoubleEndedIterator {
self.entries.iter().map(|(name, _)| name.as_str())
}
pub fn nouls(&self) -> impl DoubleEndedIterator<Item = (&str, &NoulAnswer)> {
self.iter().filter_map(|(name, answer)| answer.as_noul().map(|noul| (name, noul)))
}
pub fn choices(&self) -> impl DoubleEndedIterator<Item = (&str, &ChoiceAnswer)> {
self.iter().filter_map(|(name, answer)| answer.as_choice().map(|choice| (name, choice)))
}
pub fn scores(&self) -> impl DoubleEndedIterator<Item = (&str, &ScoreAnswer)> {
self.iter().filter_map(|(name, answer)| answer.as_score().map(|score| (name, score)))
}
}
impl<S> FromIterator<(S, Answer)> for Answers
where
S: Into<String>,
{
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = (S, Answer)>,
{
Self {
entries: iter
.into_iter()
.map(|(name, answer)| (Name::from(name.into()), answer))
.collect(),
}
}
}
impl Serialize for Answers {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
Pairs(&self.entries).serialize(serializer)
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub enum Answer {
Noul(NoulAnswer),
Choice(ChoiceAnswer),
Score(ScoreAnswer),
}
impl Answer {
#[must_use]
pub fn as_noul(&self) -> Option<&NoulAnswer> {
match self {
Self::Noul(answer) => Some(answer),
Self::Choice(_) | Self::Score(_) => None,
}
}
#[must_use]
pub fn as_choice(&self) -> Option<&ChoiceAnswer> {
match self {
Self::Choice(answer) => Some(answer),
Self::Noul(_) | Self::Score(_) => None,
}
}
#[must_use]
pub fn as_score(&self) -> Option<&ScoreAnswer> {
match self {
Self::Score(answer) => Some(answer),
Self::Noul(_) | Self::Choice(_) => None,
}
}
}
impl From<NoulAnswer> for Answer {
fn from(answer: NoulAnswer) -> Self {
Self::Noul(answer)
}
}
impl From<ChoiceAnswer> for Answer {
fn from(answer: ChoiceAnswer) -> Self {
Self::Choice(answer)
}
}
impl From<ScoreAnswer> for Answer {
fn from(answer: ScoreAnswer) -> Self {
Self::Score(answer)
}
}
impl Serialize for Answer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Self::Noul(answer) => answer.serialize(serializer),
Self::Choice(answer) => answer.serialize(serializer),
Self::Score(answer) => answer.serialize(serializer),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct NoulAnswer {
noul: f64,
}
impl NoulAnswer {
#[must_use]
pub fn new(noul: f64) -> Self {
Self { noul }
}
#[must_use]
pub fn noul(&self) -> f64 {
self.noul
}
}
impl Serialize for NoulAnswer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut out = serializer.serialize_struct("NoulAnswer", 2)?;
out.serialize_field("type", "noul")?;
out.serialize_field("noul", &self.noul)?;
out.end()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ChoiceAnswer {
choice: Name,
confidence: f64,
probabilities: Vec<(Name, f64)>,
}
impl ChoiceAnswer {
#[must_use]
pub fn new<C, I, S>(choice: C, confidence: f64, probabilities: I) -> Self
where
C: Into<String>,
I: IntoIterator<Item = (S, f64)>,
S: Into<String>,
{
Self::from_parts(
Name::from(choice.into()),
confidence,
probabilities
.into_iter()
.map(|(name, probability)| (Name::from(name.into()), probability))
.collect(),
)
}
pub(crate) fn from_parts(
choice: Name,
confidence: f64,
probabilities: Vec<(Name, f64)>,
) -> Self {
Self { choice, confidence, probabilities }
}
#[must_use]
pub fn choice(&self) -> &str {
self.choice.as_str()
}
#[must_use]
pub fn confidence(&self) -> f64 {
self.confidence
}
pub fn probabilities(
&self,
) -> impl ExactSizeIterator<Item = (&str, f64)> + DoubleEndedIterator {
self.probabilities.iter().map(|(name, probability)| (name.as_str(), *probability))
}
#[must_use]
pub fn probability(&self, name: &str) -> Option<f64> {
self.probabilities
.iter()
.find(|(key, _)| key.as_str() == name)
.map(|(_, probability)| *probability)
}
}
impl Serialize for ChoiceAnswer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut out = serializer.serialize_struct("ChoiceAnswer", 4)?;
out.serialize_field("type", "choice")?;
out.serialize_field("choice", &self.choice)?;
out.serialize_field("confidence", &self.confidence)?;
out.serialize_field("probabilities", &Pairs(&self.probabilities))?;
out.end()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ScoreAnswer {
score: f64,
confidence: f64,
legend: Vec<(u32, Content<'static>)>,
probabilities: Vec<(u32, f64)>,
}
impl ScoreAnswer {
#[must_use]
pub fn new<L, P>(score: f64, confidence: f64, legend: L, probabilities: P) -> Self
where
L: IntoIterator<Item = (u32, Content<'static>)>,
P: IntoIterator<Item = (u32, f64)>,
{
let mut sorted_legend: Vec<_> = legend.into_iter().collect();
sort_by_level(&mut sorted_legend);
let mut sorted_probabilities: Vec<_> = probabilities.into_iter().collect();
sort_by_level(&mut sorted_probabilities);
Self::from_sorted(score, confidence, sorted_legend, sorted_probabilities)
}
pub(crate) fn from_sorted(
score: f64,
confidence: f64,
legend: Vec<(u32, Content<'static>)>,
probabilities: Vec<(u32, f64)>,
) -> Self {
debug_assert!(legend.is_sorted_by_key(|(level, _)| *level), "the legend is sorted");
debug_assert!(
probabilities.is_sorted_by_key(|(level, _)| *level),
"the probabilities are sorted"
);
Self { score, confidence, legend, probabilities }
}
#[must_use]
pub fn score(&self) -> f64 {
self.score
}
#[must_use]
pub fn confidence(&self) -> f64 {
self.confidence
}
pub fn legend(
&self,
) -> impl ExactSizeIterator<Item = (u32, &Content<'static>)> + DoubleEndedIterator {
self.legend.iter().map(|(level, description)| (*level, description))
}
#[must_use]
pub fn description(&self, level: u32) -> Option<&Content<'static>> {
self.legend.binary_search_by_key(&level, |(key, _)| *key).ok().map(|at| &self.legend[at].1)
}
pub fn probabilities(&self) -> impl ExactSizeIterator<Item = (u32, f64)> + DoubleEndedIterator {
self.probabilities.iter().copied()
}
#[must_use]
pub fn probability(&self, level: u32) -> Option<f64> {
self.probabilities
.binary_search_by_key(&level, |(key, _)| *key)
.ok()
.map(|at| self.probabilities[at].1)
}
}
impl Serialize for ScoreAnswer {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut out = serializer.serialize_struct("ScoreAnswer", 5)?;
out.serialize_field("type", "score")?;
out.serialize_field("score", &self.score)?;
out.serialize_field("confidence", &self.confidence)?;
out.serialize_field("legend", &Pairs(&self.legend))?;
out.serialize_field("probabilities", &Pairs(&self.probabilities))?;
out.end()
}
}
struct Pairs<'a, K, V>(&'a [(K, V)]);
impl<K, V> Serialize for Pairs<'_, K, V>
where
K: Serialize,
V: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut out = serializer.serialize_map(Some(self.0.len()))?;
for (key, value) in self.0 {
out.serialize_entry(key, value)?;
}
out.end()
}
}
pub(crate) fn push_by_level<T>(
entries: &mut Vec<(u32, T)>,
in_order: &mut bool,
level: u32,
value: T,
) {
*in_order &= entries.last().is_none_or(|(last, _)| *last <= level);
entries.push((level, value));
}
pub(crate) fn sort_by_level<T>(entries: &mut [(u32, T)]) {
entries.sort_by_key(|(level, _)| *level);
}
#[cfg(test)]
#[path = "response_tests.rs"]
mod tests;