fine_tuning/
fine_tuning.rs

1//! Comprehensive fine-tuning example demonstrating model customization with OpenAI.
2//!
3//! This example showcases the OpenAI Fine-tuning API, including:
4//! - Creating fine-tuning jobs with training data
5//! - Listing fine-tuning jobs with pagination
6//! - Retrieving job status and monitoring progress
7//! - Listing events for jobs to track training progress
8//! - Listing checkpoints for jobs
9//! - Cancelling running jobs
10//!
11//! ## Features Demonstrated
12//!
13//! - **Job Creation**: Create fine-tuning jobs with various configurations
14//! - **Job Listing**: List all fine-tuning jobs with filtering
15//! - **Job Monitoring**: Track job progress and view events
16//! - **Checkpoint Management**: View and manage training checkpoints
17//! - **Job Cancellation**: Cancel running jobs when needed
18//! - **Error Handling**: Robust error handling for various scenarios
19//!
20//! ## Prerequisites
21//!
22//! Set your OpenAI API key:
23//! ```bash
24//! export OPENAI_API_KEY="your-key-here"
25//! ```
26//!
27//! ## Usage
28//!
29//! ```bash
30//! cargo run --example fine_tuning
31//! ```
32//!
33//! Note: This example demonstrates the API structure. Fine-tuning requires
34//! properly formatted training data files uploaded to OpenAI.
35
36#![allow(clippy::uninlined_format_args)]
37#![allow(clippy::no_effect_underscore_binding)]
38#![allow(clippy::doc_markdown)]
39#![allow(clippy::cast_possible_wrap)]
40#![allow(clippy::too_many_lines)]
41#![allow(clippy::missing_docs_in_private_items)]
42#![allow(clippy::cast_possible_truncation)]
43#![allow(clippy::cast_lossless)]
44#![allow(unused_variables)]
45#![allow(missing_docs)]
46#![allow(dead_code)]
47
48use openai_ergonomic::{builders::fine_tuning::FineTuningJobBuilder, Client};
49
50/// Fine-tuning job metadata for demonstration
51#[derive(Debug, Clone)]
52pub struct JobInfo {
53    pub id: String,
54    pub model: String,
55    pub status: String,
56    pub training_file: String,
57    pub created_at: i64,
58}
59
60impl JobInfo {
61    pub fn new(
62        id: impl Into<String>,
63        model: impl Into<String>,
64        status: impl Into<String>,
65        training_file: impl Into<String>,
66    ) -> Self {
67        Self {
68            id: id.into(),
69            model: model.into(),
70            status: status.into(),
71            training_file: training_file.into(),
72            created_at: std::time::SystemTime::now()
73                .duration_since(std::time::UNIX_EPOCH)
74                .unwrap()
75                .as_secs() as i64,
76        }
77    }
78
79    pub fn display(&self) {
80        println!("  ID: {}", self.id);
81        println!("  Model: {}", self.model);
82        println!("  Status: {}", self.status);
83        println!("  Training File: {}", self.training_file);
84        println!("  Created At: {}", self.created_at);
85    }
86}
87
88#[tokio::main]
89async fn main() -> Result<(), Box<dyn std::error::Error>> {
90    println!("šŸš€ OpenAI Ergonomic - Comprehensive Fine-tuning Example\n");
91
92    // Initialize client from environment variables
93    println!("šŸ“ Initializing OpenAI client...");
94    let client = match Client::from_env() {
95        Ok(c) => {
96            println!("āœ… Client initialized successfully\n");
97            c.build()
98        }
99        Err(e) => {
100            eprintln!("āŒ Failed to initialize client: {}", e);
101            eprintln!("šŸ’” Make sure OPENAI_API_KEY is set");
102            return Ok(());
103        }
104    };
105
106    // Example 1: Create a fine-tuning job
107    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
108    println!("šŸ“Œ Example 1: Create Fine-tuning Job");
109    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
110
111    // Note: You need to upload a training file first
112    // For demonstration purposes, we'll use a placeholder file ID
113    let training_file_id = "file-training-data";
114
115    println!("Creating fine-tuning job...");
116    println!("  Base Model: gpt-3.5-turbo");
117    println!("  Training File: {}", training_file_id);
118    println!("  Suffix: my-custom-model");
119
120    let builder = FineTuningJobBuilder::new("gpt-3.5-turbo", training_file_id)
121        .suffix("my-custom-model")
122        .epochs(3);
123
124    println!("\nšŸ’” Note: This would create a real fine-tuning job with your API key.");
125    println!("   Commented out to avoid accidental charges.\n");
126
127    // Uncomment to actually create the job:
128    // match client.fine_tuning().create_job(builder).await {
129    //     Ok(job) => {
130    //         println!("āœ… Fine-tuning job created successfully!");
131    //         println!("  Job ID: {}", job.id);
132    //         println!("  Status: {}", job.status);
133    //         println!("  Model: {}", job.model);
134    //     }
135    //     Err(e) => {
136    //         eprintln!("āŒ Failed to create fine-tuning job: {}", e);
137    //     }
138    // }
139
140    // Simulate job creation for demonstration
141    let demo_job = JobInfo::new(
142        "ftjob-demo123",
143        "gpt-3.5-turbo",
144        "validating",
145        training_file_id,
146    );
147    println!("šŸ“Š Demo Job Created:");
148    demo_job.display();
149
150    // Example 2: List fine-tuning jobs
151    println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
152    println!("šŸ“Œ Example 2: List Fine-tuning Jobs");
153    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
154
155    println!("Listing fine-tuning jobs (limit: 5)...\n");
156
157    // Uncomment to actually list jobs:
158    // match client.fine_tuning().list_jobs(None, Some(5)).await {
159    //     Ok(response) => {
160    //         println!("āœ… Found {} fine-tuning jobs", response.data.len());
161    //         for (i, job) in response.data.iter().enumerate() {
162    //             println!("\nšŸ“ Job {}:", i + 1);
163    //             println!("  ID: {}", job.id);
164    //             println!("  Model: {}", job.model);
165    //             println!("  Status: {}", job.status);
166    //             println!("  Created At: {}", job.created_at);
167    //         }
168    //     }
169    //     Err(e) => {
170    //         eprintln!("āŒ Failed to list fine-tuning jobs: {}", e);
171    //     }
172    // }
173
174    println!("šŸ’” Demo: Would list your fine-tuning jobs here");
175
176    // Example 3: Get specific fine-tuning job
177    println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
178    println!("šŸ“Œ Example 3: Get Fine-tuning Job Details");
179    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
180
181    let job_id = "ftjob-demo123";
182    println!("Retrieving job: {}\n", job_id);
183
184    // Uncomment to actually get job:
185    // match client.fine_tuning().get_job(job_id).await {
186    //     Ok(job) => {
187    //         println!("āœ… Job retrieved successfully!");
188    //         println!("  ID: {}", job.id);
189    //         println!("  Model: {}", job.model);
190    //         println!("  Status: {}", job.status);
191    //         println!("  Created At: {}", job.created_at);
192    //         if let Some(finished_at) = job.finished_at {
193    //             println!("  Finished At: {}", finished_at);
194    //         }
195    //     }
196    //     Err(e) => {
197    //         eprintln!("āŒ Failed to get fine-tuning job: {}", e);
198    //     }
199    // }
200
201    println!("šŸ’” Demo: Would show detailed job information");
202
203    // Example 4: List job events
204    println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
205    println!("šŸ“Œ Example 4: List Fine-tuning Job Events");
206    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
207
208    println!("Listing events for job: {}\n", job_id);
209
210    // Uncomment to actually list events:
211    // match client.fine_tuning().list_events(job_id, None, Some(10)).await {
212    //     Ok(response) => {
213    //         println!("āœ… Found {} events", response.data.len());
214    //         for (i, event) in response.data.iter().enumerate() {
215    //             println!("\nšŸ“‹ Event {}:", i + 1);
216    //             println!("  Message: {}", event.message);
217    //             println!("  Created At: {}", event.created_at);
218    //             if let Some(level) = &event.level {
219    //                 println!("  Level: {}", level);
220    //             }
221    //         }
222    //     }
223    //     Err(e) => {
224    //         eprintln!("āŒ Failed to list events: {}", e);
225    //     }
226    // }
227
228    println!("šŸ’” Demo: Would show training events like:");
229    println!("  - Job started");
230    println!("  - Training step 1/100 complete");
231    println!("  - Validation loss: 0.452");
232    println!("  - Training complete");
233
234    // Example 5: List job checkpoints
235    println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
236    println!("šŸ“Œ Example 5: List Fine-tuning Job Checkpoints");
237    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
238
239    println!("Listing checkpoints for job: {}\n", job_id);
240
241    // Uncomment to actually list checkpoints:
242    // match client.fine_tuning().list_checkpoints(job_id, None, Some(5)).await {
243    //     Ok(response) => {
244    //         println!("āœ… Found {} checkpoints", response.data.len());
245    //         for (i, checkpoint) in response.data.iter().enumerate() {
246    //             println!("\nšŸ’¾ Checkpoint {}:", i + 1);
247    //             println!("  ID: {}", checkpoint.id);
248    //             println!("  Created At: {}", checkpoint.created_at);
249    //             println!("  Step Number: {}", checkpoint.step_number);
250    //         }
251    //     }
252    //     Err(e) => {
253    //         eprintln!("āŒ Failed to list checkpoints: {}", e);
254    //     }
255    // }
256
257    println!("šŸ’” Demo: Would show model checkpoints from training");
258
259    // Example 6: Cancel fine-tuning job
260    println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
261    println!("šŸ“Œ Example 6: Cancel Fine-tuning Job");
262    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
263
264    println!("Cancelling job: {}\n", job_id);
265
266    // Uncomment to actually cancel job:
267    // match client.fine_tuning().cancel_job(job_id).await {
268    //     Ok(job) => {
269    //         println!("āœ… Job cancelled successfully!");
270    //         println!("  Job ID: {}", job.id);
271    //         println!("  Status: {}", job.status);
272    //     }
273    //     Err(e) => {
274    //         eprintln!("āŒ Failed to cancel job: {}", e);
275    //     }
276    // }
277
278    println!("šŸ’” Demo: Would cancel the running fine-tuning job");
279
280    // Example 7: Create job with validation file
281    println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
282    println!("šŸ“Œ Example 7: Create Job with Validation File");
283    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
284
285    let validation_file_id = "file-validation-data";
286
287    println!("Creating fine-tuning job with validation...");
288    println!("  Base Model: gpt-3.5-turbo");
289    println!("  Training File: {}", training_file_id);
290    println!("  Validation File: {}", validation_file_id);
291    println!("  Epochs: 5");
292    println!("  Learning Rate Multiplier: 0.1");
293
294    let builder_with_validation = FineTuningJobBuilder::new("gpt-3.5-turbo", training_file_id)
295        .validation_file(validation_file_id)
296        .epochs(5)
297        .learning_rate_multiplier(0.1);
298
299    println!("\nšŸ’” Note: Validation files help monitor overfitting during training");
300
301    // Example 8: Create job with Weights & Biases integration
302    println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
303    println!("šŸ“Œ Example 8: Create Job with W&B Integration");
304    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
305
306    println!("Creating fine-tuning job with W&B...");
307    println!("  Base Model: gpt-3.5-turbo");
308    println!("  Training File: {}", training_file_id);
309    println!("  W&B Project: my-finetuning-project");
310
311    let builder_with_wandb = FineTuningJobBuilder::new("gpt-3.5-turbo", training_file_id)
312        .with_wandb("my-finetuning-project");
313
314    println!("\nšŸ’” Note: W&B integration provides detailed training metrics visualization");
315
316    // Summary
317    println!("\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━");
318    println!("šŸ“Š Summary");
319    println!("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
320
321    println!("āœ… Fine-tuning API examples completed!");
322    println!("\nšŸ“š Key Takeaways:");
323    println!("  • Fine-tuning allows customizing models for specific tasks");
324    println!("  • Jobs can be created with various hyperparameters");
325    println!("  • Progress can be monitored through events and checkpoints");
326    println!("  • Validation files help prevent overfitting");
327    println!("  • Integrations like W&B provide detailed metrics");
328    println!("  • Jobs can be cancelled if needed");
329
330    println!("\nšŸ’” Next Steps:");
331    println!("  1. Prepare your training data in JSONL format");
332    println!("  2. Upload training data using the Files API");
333    println!("  3. Create a fine-tuning job with appropriate parameters");
334    println!("  4. Monitor progress through events");
335    println!("  5. Use the fine-tuned model in your applications");
336
337    println!("\nšŸŽ‰ Example completed successfully!");
338
339    Ok(())
340}