1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! `openkind-client`: an async Rust client for the `openkindd`
//! daemon and the TypeSafe Jev SystemOne HTTP API, including OpenRouter's
//! System One route. Cloudflare Workers AI uses the same question types via
//! [`ClientBuilder::cloudflare_account`].
//!
//! Built directly on the `openkind-core` wire types. Successful evaluation
//! responses are validated against their originating requests, including
//! question types and Choice keys. Wire-compatible with
//! `https://api.typesafe.ai` and the `typesafe_sdk` Python SDK: the same
//! retry taxonomy, `Retry-After` handling, error envelope parsing, request
//! ids, and env-var resolution ā with defaults mirroring the Python
//! `RetryPolicy` (2 retries, 0.5sā5s exponential backoff, 25% jitter,
//! 408/429/5xx retried, 30s total budget).
//!
//! # Quick start
//! ```no_run
//! use openkind_client::{question, Client};
//!
//! # async fn demo() -> Result<(), openkind_client::Error> {
//! let client = Client::builder()
//! .api_key("dev-key")
//! .base_url("http://127.0.0.1:18080")
//! .build()?;
//!
//! let response = client
//! .system_one(
//! "I was charged twice. Please help.",
//! [
//! ("billing", question::noul("Is this about billing?")),
//! (
//! "tone",
//! question::choice("What is the tone?", [("calm", None), ("angry", None)]),
//! ),
//! ],
//! )
//! .await?;
//!
//! let billing = match &response.answers["billing"] {
//! openkind_client::Answer::Noul(answer) => answer.noul,
//! _ => unreachable!(),
//! };
//! println!("billing probability: {billing}");
//! # Ok(())
//! # }
//! ```
//!
//! # Rate limiting, overload, and retries
//!
//! 429 (`rate_limited`) and 529 (`overloaded`) responses carry
//! `retry-after-ms` / `Retry-After` headers. The client reads them and waits
//! exactly as long as the server asked, falling back to exponential backoff
//! with jitter for other transient failures (408, 5xx, connection errors,
//! timeouts). See [`RetryPolicy`] to tune or disable.
//!
//! # Errors
//!
//! All failures surface as [`Error`]; server rejections as
//! [`Error::Api`](`Error::Api`) with the status, error `code`, `message`,
//! `x-typesafe-request-id`, and parsed `retry_after`. See [`error`].
//!
//! # Environment variables
//!
//! | Builder option | Primary | Python-SDK fallback |
//! |------------------|-------------------------|-----------------------|
//! | `api_key` | `OPENKIND_API_KEY` | `TYPESAFE_API_KEY` |
//! | `base_url` | `OPENKIND_BASE_URL` | `TYPESAFE_BASE_URL` |
//! | `default_model` | `OPENKIND_DEFAULT_MODEL` | `TYPESAFE_DEFAULT_MODEL` |
//!
//! Explicit builder values win; empty/whitespace env values are ignored.
/// Async HTTP client implementation, builder, and request options.
/// Client error types, envelope extraction, and status classifications.
/// Question construction convenience helpers.
/// Retry policy, exponential backoff, and jitter strategies.
pub use ;
pub use ;
pub use RetryPolicy;
// Wire types re-exported from `openkind-core` ā the single source of
// truth for the Jev protocol. Client and server share these structs, which
// is what guarantees schema conformance (`jev-v1-request.json` /
// `jev-v1-response.json`).
pub use ;