1use serde::{Deserialize, Serialize};
3
4#[derive(Debug, Clone)]
6pub struct ClientConfig {
7 pub directory_url: String,
8 pub timeout_ms: u64,
10 pub region: Option<String>,
11 pub node_token: Option<String>,
12 pub use_confidentiality: bool,
14}
15
16impl Default for ClientConfig {
17 fn default() -> Self {
18 Self {
19 directory_url: "https://iicp.network/api".into(),
20 timeout_ms: 30_000,
21 region: None,
22 node_token: None,
23 use_confidentiality: false,
24 }
25 }
26}
27
28#[derive(Debug, Default, Clone)]
30pub struct DiscoverOptions {
31 pub region: Option<String>,
32 pub model: Option<String>,
33 pub min_reputation: Option<f64>,
34 pub limit: Option<u32>,
35}
36
37#[derive(Debug, Clone, Deserialize)]
39pub struct CxPublicKey {
40 pub algorithm: String,
41 pub key: String,
43 pub key_id: String,
45}
46
47#[derive(Debug, Clone, Deserialize)]
49pub struct Node {
50 pub node_id: String,
51 pub endpoint: String,
52 pub score: f64,
53 pub available: bool,
54 pub region: String,
55 pub models: Option<Vec<String>>,
56 pub cip_policy: Option<CipPolicy>,
57 #[serde(default)]
60 pub health_label: Option<String>,
61 #[serde(default)]
63 pub exposure_mode: Option<String>,
64 #[serde(default)]
67 pub cx_public_key: Option<CxPublicKey>,
68}
69
70#[derive(Debug, Clone, Deserialize)]
72pub struct CipPolicy {
73 pub allow_remote_inference: bool,
74}
75
76#[derive(Debug, Clone, Deserialize)]
78pub struct NodeList {
79 pub nodes: Vec<Node>,
80 pub count: u32,
81}
82
83#[derive(Debug, Clone, Serialize)]
85pub struct TaskRequest {
86 pub task_id: String,
87 pub intent: String,
88 pub payload: serde_json::Value,
89 #[serde(skip_serializing_if = "Option::is_none")]
90 pub constraints: Option<TaskConstraints>,
91 #[serde(skip_serializing_if = "Option::is_none")]
92 pub auth: Option<TaskAuth>,
93}
94
95#[derive(Debug, Clone, Serialize)]
97pub struct TaskConstraints {
98 #[serde(skip_serializing_if = "Option::is_none")]
99 pub timeout_ms: Option<u64>,
100 #[serde(skip_serializing_if = "Option::is_none")]
101 pub max_tokens: Option<u32>,
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub model: Option<String>,
104}
105
106#[derive(Debug, Clone, Serialize)]
108pub struct TaskAuth {
109 pub token: String,
110}
111
112#[derive(Debug, Clone, Deserialize)]
114pub struct TaskResponse {
115 pub task_id: String,
116 pub status: String,
117 pub result: Option<serde_json::Value>,
118 pub metrics: Option<TaskMetrics>,
119}
120
121#[derive(Debug, Clone, Deserialize)]
123pub struct TaskMetrics {
124 pub latency_ms: Option<f64>,
125 pub tokens_used: Option<u32>,
126 pub node_id: Option<String>,
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct ChatMessage {
132 pub role: String,
133 pub content: String,
134}
135
136#[derive(Debug, Default, Clone)]
138pub struct ChatOptions {
139 pub model: Option<String>,
140 pub max_tokens: Option<u32>,
141 pub timeout_ms: Option<u64>,
142 pub temperature: Option<f64>,
143}
144
145#[derive(Debug, Clone, Deserialize, Default)]
147pub struct ChatResponse {
148 pub choices: Vec<ChatChoice>,
149 pub usage: Option<ChatUsage>,
150 #[serde(default)]
152 pub task_id: String,
153 #[serde(default)]
155 pub node_id: Option<String>,
156}
157
158#[derive(Debug, Clone, Deserialize)]
160pub struct ChatChoice {
161 pub message: ChatMessage,
162 pub finish_reason: Option<String>,
163}
164
165#[derive(Debug, Clone, Deserialize)]
167pub struct ChatUsage {
168 pub total_tokens: Option<u32>,
169 pub prompt_tokens: Option<u32>,
170 pub completion_tokens: Option<u32>,
171}
172
173#[cfg(test)]
174mod tests {
175 use super::Node;
176
177 #[test]
179 fn node_parses_health_label_and_exposure_mode() {
180 let json = r#"{"node_id":"n1","endpoint":"https://x","score":0.9,"available":true,"region":"eu","health_label":"healthy","exposure_mode":"ipv4_public_direct"}"#;
181 let n: Node = serde_json::from_str(json).unwrap();
182 assert_eq!(n.health_label.as_deref(), Some("healthy"));
183 assert_eq!(n.exposure_mode.as_deref(), Some("ipv4_public_direct"));
184 }
185
186 #[test]
188 fn node_health_fields_default_none_for_old_directory() {
189 let json =
190 r#"{"node_id":"n1","endpoint":"https://x","score":0.5,"available":true,"region":"eu"}"#;
191 let n: Node = serde_json::from_str(json).unwrap();
192 assert!(n.health_label.is_none());
193 assert!(n.exposure_mode.is_none());
194 }
195}