Skip to main content

openai_interface/fine_tuning/jobs/
events.rs

1//! List the events of a fine-tuning job via
2//! `GET /fine_tuning/jobs/{job_id}/events`.
3//!
4//! > ![warn] This module is untested!
5//! > If you encounter any issues, please report them on the repository.
6
7use url::Url;
8
9use crate::{
10    errors::OapiError,
11    rest::get::{Get, GetNoStream},
12};
13
14/// Lists the fine-grained status updates of a fine-tuning job.
15#[derive(Debug, Clone, Default)]
16pub struct ListFineTuningEventsRequest<'a> {
17    /// The ID of the job whose events to list, e.g. `ftjob_...`.
18    pub job_id: &'a str,
19    /// A cursor for pagination: return events recorded *after* this
20    /// event ID.
21    pub after: Option<&'a str>,
22    /// A limit on the number of events to be returned (1-100, default
23    /// 20).
24    pub limit: Option<u32>,
25    /// Additional query parameters appended verbatim to the URL.
26    pub extra_query: Option<std::collections::HashMap<String, String>>,
27}
28
29impl Get for ListFineTuningEventsRequest<'_> {
30    /// Builds the URL for the request.
31    ///
32    /// `base_url` should be like <https://api.openai.com/v1>
33    fn build_url(&self, base_url: &str) -> Result<String, OapiError> {
34        let mut url = Url::parse(base_url.trim_end_matches('/')).map_err(OapiError::UrlError)?;
35        url.path_segments_mut()
36            .map_err(|_| OapiError::UrlCannotBeBase(base_url.to_string()))?
37            .push("fine_tuning")
38            .push("jobs")
39            .push(self.job_id)
40            .push("events");
41
42        let mut touched = false;
43        {
44            let mut pairs = url.query_pairs_mut();
45            if let Some(after) = self.after {
46                pairs.append_pair("after", after);
47                touched = true;
48            }
49            if let Some(limit) = self.limit {
50                pairs.append_pair("limit", &limit.to_string());
51                touched = true;
52            }
53            if let Some(extra_query) = &self.extra_query {
54                for (key, value) in extra_query {
55                    pairs.append_pair(key, value);
56                }
57                touched = true;
58            }
59        }
60        if !touched {
61            url.set_query(None);
62        }
63
64        Ok(url.to_string())
65    }
66}
67
68impl GetNoStream for ListFineTuningEventsRequest<'_> {
69    type Response = ListFineTuningEventsResponse;
70}
71
72/// A fine-tuning job event.
73#[derive(Debug, Clone, serde::Deserialize)]
74pub struct FineTuningEvent {
75    /// The event ID, e.g. `ftevent_...`.
76    pub id: String,
77    /// The object type, always `fine_tuning.job.event`.
78    #[serde(default)]
79    pub object: Option<String>,
80    /// Unix timestamp (seconds) of when the event happened.
81    pub created_at: u64,
82    /// The log level of the event (`info`, `warn`, `error`).
83    pub level: String,
84    /// A human-readable message describing the event.
85    pub message: String,
86    /// The event data, as raw JSON.
87    #[serde(default)]
88    pub data: Option<serde_json::Value>,
89}
90
91/// The response of listing a job's events.
92#[derive(Debug, Clone, serde::Deserialize)]
93pub struct ListFineTuningEventsResponse {
94    /// The events on this page.
95    #[serde(default)]
96    pub data: Vec<FineTuningEvent>,
97    /// Whether more events exist after this page.
98    #[serde(default)]
99    pub has_more: Option<bool>,
100    /// The object type (`list`), if the provider sends it.
101    #[serde(default)]
102    pub object: Option<String>,
103}
104
105crate::impl_from_str!(ListFineTuningEventsResponse);