Skip to main content

openkind_client/client/
options.rs

1//! Request configuration options, default constants, and state conversion helpers for the client.
2
3use std::time::Duration;
4
5use openkind_core::State;
6use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
7use serde::{Deserialize, Serialize};
8
9use crate::retry::RetryPolicy;
10
11/// Default API base URL (the TypeSafe hosted service). Override with
12/// `base_url` for a local `openkindd` daemon.
13pub const DEFAULT_BASE_URL: &str = "https://api.typesafe.ai";
14
15/// Default model alias when none is set explicitly or via the environment.
16pub const DEFAULT_MODEL: &str = "jev-latest";
17
18/// Default per-attempt HTTP timeout (matches the Python SDK's 10s default).
19pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
20
21pub(crate) const SYSTEM_ONE_PATH: &str = "/v1/systemone";
22pub(crate) const CLOUDFLARE_RUN_PATH: &str = "/ai/run";
23pub(crate) const MODELS_PATH: &str = "/v1/models";
24pub(crate) const HEALTH_PATH: &str = "/health";
25
26pub(crate) const SDK_NAME: &str = "openkind-client";
27
28pub(crate) const USER_AGENT_HEADER: &str = "user-agent";
29pub(crate) const SDK_HEADER: &str = "x-typesafe-sdk";
30pub(crate) const RUNTIME_HEADER: &str = "x-typesafe-runtime";
31/// Sent on retry attempts (value = retry number), mirroring the Python
32/// SDK's `X-TypeSafe-Retry-Count` so servers can observe client retries.
33pub(crate) const RETRY_COUNT_HEADER: &str = "x-typesafe-retry-count";
34
35/// Response of `GET /health` — liveness probe, no authentication required.
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
37pub struct Health {
38    /// Service status string (currently `"ok"`).
39    pub status: String,
40}
41
42/// Conversion into a Jev [`State`], letting [`crate::Client::system_one`] accept
43/// plain strings, structured objects, arrays, or a ready-made `State` —
44/// mirroring the Python SDK's `str | dict | list` state parameter.
45///
46/// (A blanket `From<&str> for State` cannot be provided because both types
47/// are foreign to this crate.)
48pub trait IntoState {
49    /// Convert into the wire [`State`] type.
50    fn into_state(self) -> State;
51}
52
53impl IntoState for State {
54    fn into_state(self) -> State {
55        self
56    }
57}
58
59impl IntoState for &str {
60    fn into_state(self) -> State {
61        State::Text(self.to_owned())
62    }
63}
64
65impl IntoState for String {
66    fn into_state(self) -> State {
67        State::Text(self)
68    }
69}
70
71impl IntoState for &String {
72    fn into_state(self) -> State {
73        State::Text(self.clone())
74    }
75}
76
77impl IntoState for serde_json::Map<String, serde_json::Value> {
78    fn into_state(self) -> State {
79        State::Object(self)
80    }
81}
82
83impl IntoState for Vec<serde_json::Value> {
84    fn into_state(self) -> State {
85        State::Array(self)
86    }
87}
88
89/// Per-call overrides applied on top of the client's defaults.
90///
91/// Passed to the `*_with` method variants:
92///
93/// ```
94/// use std::time::Duration;
95/// use openkind_client::{Client, RequestOptions, RetryPolicy};
96///
97/// # async fn demo(client: &Client, request: openkind_client::SystemRequest) {
98/// let opts = RequestOptions::new()
99///     .timeout(Duration::from_secs(2))
100///     .retry(RetryPolicy::new().max_retries(0));
101/// let _ = client.evaluate_with(request, &opts).await;
102/// # }
103/// ```
104#[derive(Debug, Clone, Default)]
105pub struct RequestOptions {
106    pub(crate) timeout: Option<Duration>,
107    pub(crate) retry: Option<RetryPolicy>,
108    pub(crate) extra_headers: Option<HeaderMap>,
109}
110
111impl RequestOptions {
112    /// An empty set of overrides (all client defaults apply).
113    pub fn new() -> Self {
114        Self::default()
115    }
116
117    /// Per-attempt timeout, overriding the client default.
118    pub fn timeout(mut self, timeout: Duration) -> Self {
119        self.timeout = Some(timeout);
120        self
121    }
122
123    /// Retry policy for this call, overriding the client default.
124    pub fn retry(mut self, retry: RetryPolicy) -> Self {
125        self.retry = Some(retry);
126        self
127    }
128
129    /// Extra header sent with this request only.
130    pub fn header(mut self, name: HeaderName, value: HeaderValue) -> Self {
131        self.extra_headers
132            .get_or_insert_with(HeaderMap::new)
133            .insert(name, value);
134        self
135    }
136}