1use crate::error::LingerError;
2use crate::RequestId;
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5use std::collections::BTreeMap;
6
7#[derive(Clone, Debug, Serialize, PartialEq)]
10#[non_exhaustive]
11pub struct CreateEvalRequest {
12 pub data_source_config: Value,
15 pub testing_criteria: Vec<Value>,
18 #[serde(skip_serializing_if = "Option::is_none")]
21 pub name: Option<String>,
22 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
25 pub metadata: BTreeMap<String, String>,
26 #[serde(flatten)]
29 pub extra: BTreeMap<String, Value>,
30}
31
32impl CreateEvalRequest {
33 pub fn builder() -> CreateEvalRequestBuilder {
36 CreateEvalRequestBuilder::default()
37 }
38}
39
40#[derive(Clone, Debug, Default)]
43#[non_exhaustive]
44pub struct CreateEvalRequestBuilder {
45 data_source_config: Option<Value>,
46 testing_criteria: Vec<Value>,
47 name: Option<String>,
48 metadata: BTreeMap<String, String>,
49 extra: BTreeMap<String, Value>,
50}
51
52impl CreateEvalRequestBuilder {
53 pub fn data_source_config(mut self, data_source_config: Value) -> Self {
56 self.data_source_config = Some(data_source_config);
57 self
58 }
59
60 pub fn testing_criterion(mut self, testing_criterion: Value) -> Self {
63 self.testing_criteria.push(testing_criterion);
64 self
65 }
66
67 pub fn testing_criteria(mut self, testing_criteria: impl IntoIterator<Item = Value>) -> Self {
70 self.testing_criteria = testing_criteria.into_iter().collect();
71 self
72 }
73
74 pub fn name(mut self, name: impl Into<String>) -> Self {
77 self.name = Some(name.into());
78 self
79 }
80
81 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
84 self.metadata.insert(key.into(), value.into());
85 self
86 }
87
88 pub fn extra(mut self, name: impl Into<String>, value: Value) -> Self {
91 self.extra.insert(name.into(), value);
92 self
93 }
94
95 pub fn build(self) -> Result<CreateEvalRequest, LingerError> {
98 validate_optional_string("name", self.name.as_deref())?;
99 validate_metadata(&self.metadata)?;
100 validate_json_value("data_source_config", self.data_source_config.as_ref(), true)?;
101 validate_json_values("testing_criteria", &self.testing_criteria, true)?;
102 validate_extra_fields(&self.extra)?;
103 Ok(CreateEvalRequest {
104 data_source_config: self
105 .data_source_config
106 .expect("validated data_source_config"),
107 testing_criteria: self.testing_criteria,
108 name: self.name,
109 metadata: self.metadata,
110 extra: self.extra,
111 })
112 }
113}
114
115#[derive(Clone, Debug, Default, Serialize, PartialEq, Eq)]
118#[non_exhaustive]
119pub struct ModifyEvalRequest {
120 #[serde(skip_serializing_if = "Option::is_none")]
123 pub name: Option<String>,
124 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
127 pub metadata: BTreeMap<String, String>,
128}
129
130impl ModifyEvalRequest {
131 pub fn builder() -> ModifyEvalRequestBuilder {
134 ModifyEvalRequestBuilder::default()
135 }
136}
137
138#[derive(Clone, Debug, Default)]
141#[non_exhaustive]
142pub struct ModifyEvalRequestBuilder {
143 name: Option<String>,
144 metadata: BTreeMap<String, String>,
145}
146
147impl ModifyEvalRequestBuilder {
148 pub fn name(mut self, name: impl Into<String>) -> Self {
151 self.name = Some(name.into());
152 self
153 }
154
155 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
158 self.metadata.insert(key.into(), value.into());
159 self
160 }
161
162 pub fn build(self) -> Result<ModifyEvalRequest, LingerError> {
165 validate_optional_string("name", self.name.as_deref())?;
166 validate_metadata(&self.metadata)?;
167 Ok(ModifyEvalRequest {
168 name: self.name,
169 metadata: self.metadata,
170 })
171 }
172}
173
174#[derive(Clone, Debug, Serialize, PartialEq)]
177#[non_exhaustive]
178pub struct CreateEvalRunRequest {
179 pub data_source: Value,
182 #[serde(skip_serializing_if = "Option::is_none")]
185 pub name: Option<String>,
186 #[serde(skip_serializing_if = "BTreeMap::is_empty")]
189 pub metadata: BTreeMap<String, String>,
190 #[serde(flatten)]
193 pub extra: BTreeMap<String, Value>,
194}
195
196impl CreateEvalRunRequest {
197 pub fn builder() -> CreateEvalRunRequestBuilder {
200 CreateEvalRunRequestBuilder::default()
201 }
202}
203
204#[derive(Clone, Debug, Default)]
207#[non_exhaustive]
208pub struct CreateEvalRunRequestBuilder {
209 data_source: Option<Value>,
210 name: Option<String>,
211 metadata: BTreeMap<String, String>,
212 extra: BTreeMap<String, Value>,
213}
214
215impl CreateEvalRunRequestBuilder {
216 pub fn data_source(mut self, data_source: Value) -> Self {
219 self.data_source = Some(data_source);
220 self
221 }
222
223 pub fn name(mut self, name: impl Into<String>) -> Self {
226 self.name = Some(name.into());
227 self
228 }
229
230 pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
233 self.metadata.insert(key.into(), value.into());
234 self
235 }
236
237 pub fn extra(mut self, name: impl Into<String>, value: Value) -> Self {
240 self.extra.insert(name.into(), value);
241 self
242 }
243
244 pub fn build(self) -> Result<CreateEvalRunRequest, LingerError> {
247 validate_optional_string("name", self.name.as_deref())?;
248 validate_metadata(&self.metadata)?;
249 validate_json_value("data_source", self.data_source.as_ref(), true)?;
250 validate_extra_fields(&self.extra)?;
251 Ok(CreateEvalRunRequest {
252 data_source: self.data_source.expect("validated data_source"),
253 name: self.name,
254 metadata: self.metadata,
255 extra: self.extra,
256 })
257 }
258}
259
260#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
263#[non_exhaustive]
264pub struct Eval {
265 pub id: String,
268 pub object: String,
271 pub created_at: u64,
274 #[serde(default)]
277 pub name: String,
278 #[serde(default)]
281 pub metadata: BTreeMap<String, String>,
282 #[serde(default)]
285 pub data_source_config: Value,
286 #[serde(default)]
289 pub testing_criteria: Vec<Value>,
290 #[serde(flatten)]
293 pub extra: BTreeMap<String, Value>,
294 #[serde(skip)]
297 request_id: Option<RequestId>,
298}
299
300impl Eval {
301 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
302 self.request_id = request_id;
303 self
304 }
305
306 pub fn request_id(&self) -> Option<&RequestId> {
309 self.request_id.as_ref()
310 }
311}
312
313#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
316#[non_exhaustive]
317pub struct EvalRun {
318 pub id: String,
321 pub object: String,
324 pub created_at: u64,
327 pub eval_id: String,
330 pub status: String,
333 #[serde(default)]
336 pub name: Option<String>,
337 #[serde(default)]
340 pub model: Option<String>,
341 #[serde(default)]
344 pub metadata: BTreeMap<String, String>,
345 #[serde(default)]
348 pub data_source: Value,
349 #[serde(default)]
352 pub error: Option<Value>,
353 #[serde(default)]
356 pub per_model_usage: Vec<Value>,
357 #[serde(default)]
360 pub per_testing_criteria_results: Vec<Value>,
361 #[serde(default)]
364 pub report_url: Option<String>,
365 #[serde(default)]
368 pub result_counts: Value,
369 #[serde(flatten)]
372 pub extra: BTreeMap<String, Value>,
373 #[serde(skip)]
376 request_id: Option<RequestId>,
377}
378
379impl EvalRun {
380 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
381 self.request_id = request_id;
382 self
383 }
384
385 pub fn request_id(&self) -> Option<&RequestId> {
388 self.request_id.as_ref()
389 }
390}
391
392#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
395#[non_exhaustive]
396pub struct EvalRunPage {
397 pub object: String,
400 #[serde(default)]
403 pub data: Vec<EvalRun>,
404 #[serde(default)]
407 pub first_id: Option<String>,
408 #[serde(default)]
411 pub last_id: Option<String>,
412 pub has_more: bool,
415 #[serde(skip)]
418 request_id: Option<RequestId>,
419}
420
421impl EvalRunPage {
422 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
423 self.request_id = request_id;
424 self
425 }
426
427 pub fn request_id(&self) -> Option<&RequestId> {
430 self.request_id.as_ref()
431 }
432}
433
434#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
437#[non_exhaustive]
438pub struct EvalRunDeletion {
439 pub run_id: String,
442 pub object: String,
445 pub deleted: bool,
448 #[serde(skip)]
451 request_id: Option<RequestId>,
452}
453
454impl EvalRunDeletion {
455 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
456 self.request_id = request_id;
457 self
458 }
459
460 pub fn request_id(&self) -> Option<&RequestId> {
463 self.request_id.as_ref()
464 }
465}
466
467#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
470#[non_exhaustive]
471pub struct EvalRunOutputItem {
472 pub id: String,
475 pub object: String,
478 pub created_at: u64,
481 pub eval_id: String,
484 pub run_id: String,
487 pub status: String,
490 pub datasource_item_id: u64,
493 #[serde(default)]
496 pub datasource_item: Value,
497 #[serde(default)]
500 pub results: Vec<Value>,
501 #[serde(default)]
504 pub sample: Value,
505 #[serde(flatten)]
508 pub extra: BTreeMap<String, Value>,
509 #[serde(skip)]
512 request_id: Option<RequestId>,
513}
514
515impl EvalRunOutputItem {
516 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
517 self.request_id = request_id;
518 self
519 }
520
521 pub fn request_id(&self) -> Option<&RequestId> {
524 self.request_id.as_ref()
525 }
526}
527
528#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
531#[non_exhaustive]
532pub struct EvalRunOutputItemPage {
533 pub object: String,
536 #[serde(default)]
539 pub data: Vec<EvalRunOutputItem>,
540 #[serde(default)]
543 pub first_id: Option<String>,
544 #[serde(default)]
547 pub last_id: Option<String>,
548 pub has_more: bool,
551 #[serde(skip)]
554 request_id: Option<RequestId>,
555}
556
557impl EvalRunOutputItemPage {
558 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
559 self.request_id = request_id;
560 self
561 }
562
563 pub fn request_id(&self) -> Option<&RequestId> {
566 self.request_id.as_ref()
567 }
568}
569
570#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
573#[non_exhaustive]
574pub struct EvalPage {
575 pub object: String,
578 #[serde(default)]
581 pub data: Vec<Eval>,
582 #[serde(default)]
585 pub first_id: Option<String>,
586 #[serde(default)]
589 pub last_id: Option<String>,
590 pub has_more: bool,
593 #[serde(skip)]
596 request_id: Option<RequestId>,
597}
598
599impl EvalPage {
600 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
601 self.request_id = request_id;
602 self
603 }
604
605 pub fn request_id(&self) -> Option<&RequestId> {
608 self.request_id.as_ref()
609 }
610}
611
612#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
615#[non_exhaustive]
616pub struct EvalDeletion {
617 pub eval_id: String,
620 pub object: String,
623 pub deleted: bool,
626 #[serde(skip)]
629 request_id: Option<RequestId>,
630}
631
632impl EvalDeletion {
633 pub(crate) fn with_request_id(mut self, request_id: Option<RequestId>) -> Self {
634 self.request_id = request_id;
635 self
636 }
637
638 pub fn request_id(&self) -> Option<&RequestId> {
641 self.request_id.as_ref()
642 }
643}
644
645fn validate_json_value(
646 name: &str,
647 value: Option<&Value>,
648 require_present: bool,
649) -> Result<(), LingerError> {
650 match value {
651 Some(value) if value.is_null() => Err(LingerError::invalid_config(format!(
652 "{name} must not be null"
653 ))),
654 Some(_) => Ok(()),
655 None if require_present => Err(LingerError::invalid_config(format!("{name} is required"))),
656 None => Ok(()),
657 }
658}
659
660fn validate_json_values(
661 name: &str,
662 values: &[Value],
663 require_non_empty: bool,
664) -> Result<(), LingerError> {
665 if require_non_empty && values.is_empty() {
666 return Err(LingerError::invalid_config(format!("{name} is required")));
667 }
668 if values.iter().any(Value::is_null) {
669 return Err(LingerError::invalid_config(format!(
670 "{name} must not contain null values"
671 )));
672 }
673 Ok(())
674}
675
676fn validate_metadata(metadata: &BTreeMap<String, String>) -> Result<(), LingerError> {
677 for key in metadata.keys() {
678 if key.trim().is_empty() {
679 return Err(LingerError::invalid_config(
680 "metadata keys must not be empty",
681 ));
682 }
683 }
684 Ok(())
685}
686
687fn validate_optional_string(name: &str, value: Option<&str>) -> Result<(), LingerError> {
688 if value.is_some_and(|value| value.trim().is_empty()) {
689 return Err(LingerError::invalid_config(format!(
690 "{name} must not be empty"
691 )));
692 }
693 Ok(())
694}
695
696fn validate_extra_fields(extra: &BTreeMap<String, Value>) -> Result<(), LingerError> {
697 for (key, value) in extra {
698 if key.trim().is_empty() {
699 return Err(LingerError::invalid_config(
700 "extra field names must not be empty",
701 ));
702 }
703 if value.is_null() {
704 return Err(LingerError::invalid_config(format!(
705 "extra field {key} must not be null"
706 )));
707 }
708 }
709 Ok(())
710}