kunobi_decision/lib.rs
1//! Typed decision clients for TypeSafe Jev, Cloudflare Clef, Liquid d1, Vercel and OpenRouter.
2//! Select an access service with [`ClientBuilder::provider`].
3//!
4//! Send state and typed questions to a decision model; get structured answers with
5//! probabilities and confidence that code can act on directly.
6//!
7//! - [`noul`]: a yes/no question, answered with the probability of yes.
8//! - [`choice`]: pick one label, answered with probabilities and confidence.
9//! - [`score`]: rate against ordered levels, answered with an expected score.
10//!
11//! ```no_run
12//! # async fn example() -> kunobi_decision::Result<()> {
13//! use kunobi_decision::{Client, Questions, SystemOneRequest, choice_labels, noul, score};
14//!
15//! let client = Client::new()?; // reads TYPESAFE_API_KEY
16//!
17//! let mut questions = Questions::new();
18//! let billing = questions.add("billing", noul("Is this ticket about billing?"));
19//! let tone = questions.add("tone", choice_labels("What is the tone?", ["calm", "frustrated", "angry"]));
20//! let urgency = questions.add("urgency", score("How urgent?", ["can wait", "today", "right now"]));
21//!
22//! let result = client
23//! .system_one(SystemOneRequest::new("I was charged twice. Fix this ASAP.", questions))
24//! .await?;
25//!
26//! let tone = result.answer(&tone)?;
27//! if tone.confidence > 0.6 {
28//! println!("tone: {}", tone.choice);
29//! }
30//! println!("billing: {:.2}", result.answer(&billing)?.noul);
31//! println!("urgency: {:.2}", result.answer(&urgency)?.score);
32//! # Ok(()) }
33//! ```
34//!
35//! Calls retry 408, 429 and 5xx responses, connection errors and timeouts with
36//! capped exponential backoff, honoring `Retry-After`. See [`RetryPolicy`].
37//! Requests log through [`tracing`]: summaries at `info` and redacted headers at
38//! `debug`. Bodies are logged only with [`ClientBuilder::log_bodies`].
39//!
40//! Authenticate with an API key, or with a [`CredentialProvider`] when the client
41//! runs on user machines and must not hold a TypeSafe key. See [`credentials`].
42
43#![cfg_attr(docsrs, feature(doc_cfg))]
44#![forbid(unsafe_code)]
45#![warn(missing_docs)]
46
47pub mod answers;
48#[cfg(feature = "blocking")]
49#[cfg_attr(docsrs, doc(cfg(feature = "blocking")))]
50pub mod blocking;
51pub mod client;
52pub mod credentials;
53pub mod error;
54pub mod labels;
55pub mod questions;
56pub mod request;
57pub mod retry;
58pub mod system_one;
59#[cfg(feature = "testing")]
60#[cfg_attr(docsrs, doc(cfg(feature = "testing")))]
61pub mod testing;
62pub mod types;
63
64mod redact;
65
66pub use answers::{
67 Answer, AnswerKind, ChoiceAnswer, NoulAnswer, ScoreAnswer, SystemOneResult, TypedChoiceAnswer,
68};
69pub use client::{
70 Call, Client, ClientBuilder, DEFAULT_BASE_URL, DEFAULT_MODEL, ENV_API_KEY, ENV_BASE_URL,
71 ENV_DEFAULT_MODEL, Models, Provider, RawResponse, WithResponse,
72};
73pub use credentials::{BoxError, CredentialProvider, ExposeSecret, SecretString, TokenFuture};
74pub use error::{ApiError, ApiErrorKind, Error, ErrorBody, REQUEST_ID_HEADER, Result};
75pub use labels::Labels;
76pub use questions::{
77 AnswerKey, ChoiceQuestion, NoulCriteria, NoulQuestion, Question, QuestionKind, Questions,
78 ScoreQuestion, TypedChoice, choice, choice_labels, choice_of, noul, score,
79};
80pub use request::SystemOneRequest;
81pub use retry::{DEFAULT_TIMEOUT, DEFAULT_TOTAL_TIMEOUT, RetryPolicy, parse_retry_after};
82pub use system_one::{AskFuture, SystemOne};
83pub use types::{Entry, ModelCard, Usage};
84
85/// Re-exported so callers can build headers and HTTP clients with matching versions.
86pub use reqwest;
87
88/// This crate's version.
89pub const VERSION: &str = env!("CARGO_PKG_VERSION");