openai-ergonomic 0.5.2

Ergonomic Rust wrapper for OpenAI API
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
#![allow(clippy::uninlined_format_args)]
//! Basic Assistants API example demonstrating assistant creation and thread management.
//!
//! This example showcases the Assistants API, which provides a way to build AI assistants
//! with persistent conversation threads, custom instructions, and tool capabilities.
//!
//! ## Features Demonstrated
//!
//! - Creating and configuring assistants
//! - Thread creation and management
//! - Message handling within threads
//! - Run creation and polling for responses
//! - Tool integration (code interpreter, function calling)
//! - Comprehensive error handling patterns
//!
//! ## Prerequisites
//!
//! Set your `OpenAI` API key:
//! ```bash
//! export OPENAI_API_KEY="your-key-here"
//! ```
//!
//! ## Usage
//!
//! ```bash
//! cargo run --example assistants_basic
//! ```
//!
//! ## Note on Implementation Status
//!
//! **Important**: The Assistants API is not yet fully implemented in openai-ergonomic.
//! This example demonstrates the intended API design and serves as a template for
//! future implementation. Current code shows simulated functionality.

use openai_ergonomic::{Client, Error};
use std::io::{self, Write};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!(" OpenAI Ergonomic - Basic Assistants API Example\n");

    // Initialize client from environment variables
    let client = match Client::from_env() {
        Ok(client_builder) => {
            println!(" Client initialized successfully");
            client_builder.build()
        }
        Err(e) => {
            eprintln!(" Failed to initialize client: {e}");
            eprintln!(" Make sure OPENAI_API_KEY is set in your environment");
            return Err(e.into());
        }
    };

    // Check current implementation status
    check_implementation_status();

    // Example 1: Creating an Assistant
    println!("\n Example 1: Creating an Assistant");
    println!("==================================");

    create_assistant_example(&client);
    println!(" Assistant creation example completed");

    // Example 2: Managing Threads
    println!("\n Example 2: Thread Management");
    println!("==============================");

    thread_management_example(&client);
    println!(" Thread management example completed");

    // Example 3: Message Handling
    println!("\n Example 3: Message Handling");
    println!("=============================");

    match message_handling_example(&client).await {
        Ok(()) => println!(" Message handling example completed"),
        Err(e) => {
            eprintln!(" Message handling example failed: {e}");
            handle_api_error(&e);
        }
    }

    // Example 4: Tool Integration
    println!("\n Example 4: Tool Integration");
    println!("=============================");

    match tool_integration_example(&client).await {
        Ok(()) => println!(" Tool integration example completed"),
        Err(e) => {
            eprintln!(" Tool integration example failed: {e}");
            handle_api_error(&e);
        }
    }

    // Example 5: Conversation Simulation
    println!("\n Example 5: Complete Conversation Flow");
    println!("========================================");

    match conversation_flow_example(&client).await {
        Ok(()) => println!(" Conversation flow example completed"),
        Err(e) => {
            eprintln!(" Conversation flow example failed: {e}");
            handle_api_error(&e);
        }
    }

    println!("\n All examples completed!");
    println!("\n This example demonstrated the intended Assistants API usage:");
    println!("  • Assistant creation with custom instructions");
    println!("  • Thread lifecycle management");
    println!("  • Message sending and retrieval");
    println!("  • Run creation and status polling");
    println!("  • Tool integration patterns");
    println!("  • Error handling strategies");

    println!("\n Implementation Status:");
    println!("  • The Assistants API builders and response types are placeholders");
    println!("  • This example serves as a design template for future implementation");
    println!("  • Actual API calls will be available once the builders are implemented");

    Ok(())
}

/// Check and display the current implementation status
fn check_implementation_status() {
    println!(" Implementation Status Check");
    println!("=============================");
    println!(" Client infrastructure: Ready");
    println!(" Assistants builders: Not yet implemented");
    println!(" Threads builders: Not yet implemented");
    println!(" Assistants responses: Not yet implemented");
    println!();
    println!(" This example demonstrates the intended API design");
    println!("   and will work once the builders are implemented.");
}

/// Example 1: Creating and configuring an assistant
fn create_assistant_example(_client: &Client) {
    println!("Creating a new assistant with custom instructions...");

    // Future API design - this will work once assistants builders are implemented
    /*
    let assistant_builder = client
        .assistants()
        .create()
        .model("gpt-4")
        .name("Math Tutor")
        .description("A helpful assistant that helps with math problems")
        .instructions(
            "You are a patient math tutor. Help students understand mathematical concepts \
             by breaking down problems step by step. Always encourage learning and provide \
             clear explanations."
        )
        .tools(vec![
            tool_code_interpreter(),
            tool_function(
                "calculate_fibonacci",
                "Calculate the nth Fibonacci number",
                json!({
                    "type": "object",
                    "properties": {
                        "n": {
                            "type": "integer",
                            "description": "The position in the Fibonacci sequence"
                        }
                    },
                    "required": ["n"]
                })
            )
        ])
        .metadata(json!({
            "example": "basic_assistants",
            "created_by": "openai-ergonomic"
        }));

    let assistant = client.send_assistants(assistant_builder).await?;

    println!(" Assistant created successfully!");
    println!("   ID: {}", assistant.id());
    println!("   Name: {}", assistant.name().unwrap_or("Unnamed"));
    println!("   Model: {}", assistant.model());
    println!("   Tools: {} configured", assistant.tools().len());
    */

    // Simulated output for now
    println!(" [Simulated] Assistant created successfully!");
    println!("   ID: asst_abc123def456");
    println!("   Name: Math Tutor");
    println!("   Model: gpt-4");
    println!("   Tools: 2 configured (code_interpreter, calculate_fibonacci)");
    println!("   Instructions: Custom math tutoring instructions set");

    println!("\n Assistant Configuration:");
    println!("   • Model: GPT-4 for advanced reasoning");
    println!("   • Code Interpreter: Enabled for calculations");
    println!("   • Custom Function: Fibonacci calculator");
    println!("   • Metadata: Tagged for tracking");
}

/// Example 2: Thread creation and management
fn thread_management_example(_client: &Client) {
    println!("Creating and managing conversation threads...");

    // Future API design for thread management
    /*
    // Create a new thread
    let thread_builder = client
        .threads()
        .create()
        .metadata(json!({
            "user_id": "user_123",
            "session": "math_help_session"
        }));

    let thread = client.send_threads(thread_builder).await?;

    println!(" Thread created: {}", thread.id());

    // Retrieve thread information
    let thread_info = client
        .threads()
        .retrieve(thread.id())
        .await?;

    println!(" Thread info retrieved:");
    println!("   Created: {}", thread_info.created_at());
    println!("   Metadata: {}", thread_info.metadata());

    // List threads (if supported)
    let threads = client
        .threads()
        .list()
        .limit(10)
        .await?;

    println!(" Found {} threads", threads.data().len());
    */

    // Simulated output
    println!(" [Simulated] Thread created: thread_abc123xyz789");
    println!(" Thread information:");
    println!("   Status: Active");
    println!("   Created: 2024-01-15T10:30:00Z");
    println!("   Messages: 0 (new thread)");
    println!("   Metadata: {{\"user_id\": \"user_123\", \"session\": \"math_help_session\"}}");

    println!("\n Thread Management Features:");
    println!("   • Unique thread ID for session tracking");
    println!("   • Metadata for context preservation");
    println!("   • Message history maintained automatically");
    println!("   • Thread retrieval and listing capabilities");
}

/// Example 3: Adding messages to threads and getting responses
async fn message_handling_example(_client: &Client) -> Result<(), Error> {
    println!("Adding messages to thread and getting assistant responses...");

    // Future API design for message handling
    /*
    // Add a user message to the thread
    let message_builder = client
        .threads()
        .messages(thread_id)
        .create()
        .role("user")
        .content("Can you help me solve this equation: 2x + 5 = 13?")
        .metadata(json!({
            "message_type": "math_problem",
            "difficulty": "beginner"
        }));

    let message = client.send_thread_message(message_builder).await?;

    println!(" Message added: {}", message.id());

    // Create a run to get assistant response
    let run_builder = client
        .threads()
        .runs(thread_id)
        .create()
        .assistant_id("asst_abc123def456")
        .instructions("Focus on step-by-step explanation")
        .additional_instructions("Show your work clearly");

    let run = client.send_thread_run(run_builder).await?;

    println!(" Run created: {}", run.id());

    // Poll for completion
    let completed_run = poll_run_completion(client, thread_id, run.id()).await?;

    if completed_run.status() == "completed" {
        // Retrieve messages
        let messages = client
            .threads()
            .messages(thread_id)
            .list()
            .limit(10)
            .await?;

        for message in messages.data().iter().rev() {
            println!(" {}: {}", message.role(), message.content());
        }
    }
    */

    let thread_id = "thread_abc123xyz789"; // From previous example

    // Simulated conversation flow
    println!(" [Simulated] Adding user message to thread: {thread_id}");
    println!("   Message: 'Can you help me solve this equation: 2x + 5 = 13?'");
    println!("   Message ID: msg_user123abc");

    println!("\n [Simulated] Creating run for assistant response...");
    print!("   Status: ");
    io::stdout().flush()?;

    // Simulate run status progression
    let statuses = ["queued", "in_progress", "completed"];
    for (i, status) in statuses.iter().enumerate() {
        if i > 0 {
            print!("");
        }
        print!("{status}");
        io::stdout().flush()?;
        tokio::time::sleep(tokio::time::Duration::from_millis(800)).await;
    }
    println!();

    println!("\n [Simulated] Assistant Response:");
    println!("   I'd be happy to help you solve the equation 2x + 5 = 13!");
    println!();
    println!("   Let's solve this step by step:");
    println!("   1. Start with: 2x + 5 = 13");
    println!("   2. Subtract 5 from both sides: 2x = 13 - 5");
    println!("   3. Simplify: 2x = 8");
    println!("   4. Divide both sides by 2: x = 8/2");
    println!("   5. Final answer: x = 4");
    println!();
    println!("   To verify: 2(4) + 5 = 8 + 5 = 13 ");

    println!("\n Message Flow Summary:");
    println!("   • User message successfully added to thread");
    println!("   • Run created and executed with assistant");
    println!("   • Step-by-step mathematical solution provided");
    println!("   • Response includes verification of the answer");

    Ok(())
}

/// Example 4: Tool integration with code interpreter and custom functions
async fn tool_integration_example(_client: &Client) -> Result<(), Error> {
    println!("Demonstrating tool integration with code interpreter and custom functions...");

    // Future API design for tool integration
    /*
    // Add a message that would trigger tool usage
    let message_builder = client
        .threads()
        .messages(thread_id)
        .create()
        .role("user")
        .content("Calculate the 10th Fibonacci number and create a graph showing the first 10 numbers in the sequence");

    let message = client.send_thread_message(message_builder).await?;

    // Create a run that can use tools
    let run_builder = client
        .threads()
        .runs(thread_id)
        .create()
        .assistant_id(assistant_id)
        .tools_enabled(true)
        .instructions("Use the fibonacci function and code interpreter to provide a complete answer");

    let run = client.send_thread_run(run_builder).await?;

    // Poll and handle tool calls
    let completed_run = poll_run_with_tool_handling(client, thread_id, run.id()).await?;
    */

    let thread_id = "thread_abc123xyz789";
    let assistant_id = "asst_abc123def456";

    // Simulated tool integration
    println!(" [Simulated] User request: Calculate 10th Fibonacci number with visualization");
    println!("   Thread: {thread_id}, Assistant: {assistant_id}");

    println!("\n [Simulated] Tool Execution Flow:");
    println!("   1. Custom Function Call: calculate_fibonacci(n=10)");
    print!("      Result: ");
    io::stdout().flush()?;
    tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
    println!("55");

    println!("\n   2. Code Interpreter: Generating Fibonacci sequence visualization");
    print!("      Status: ");
    io::stdout().flush()?;
    let code_steps = [
        "importing libraries",
        "generating sequence",
        "creating plot",
        "complete",
    ];
    for (i, step) in code_steps.iter().enumerate() {
        if i > 0 {
            print!("");
        }
        print!("{step}");
        io::stdout().flush()?;
        tokio::time::sleep(tokio::time::Duration::from_millis(600)).await;
    }
    println!();

    println!("\n [Simulated] Assistant Response with Tool Results:");
    println!("   I've calculated the 10th Fibonacci number and created a visualization for you!");
    println!();
    println!("    Results:");
    println!("   • The 10th Fibonacci number is: 55");
    println!("   • Complete sequence (1-10): [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]");
    println!();
    println!("    I've also generated a graph showing the exponential growth pattern");
    println!("   of the Fibonacci sequence. The visualization clearly shows how each");
    println!("   number rapidly increases as the sequence progresses.");
    println!();
    println!("    Tools Used:");
    println!("   1. Custom fibonacci function for precise calculation");
    println!("   2. Code interpreter for data visualization");

    println!("\n Tool Integration Benefits:");
    println!("   • Custom functions provide domain-specific capabilities");
    println!("   • Code interpreter enables dynamic computation and visualization");
    println!("   • Seamless integration in conversation flow");
    println!("   • Automatic tool selection based on user needs");

    Ok(())
}

/// Example 5: Complete conversation flow simulation
async fn conversation_flow_example(_client: &Client) -> Result<(), Error> {
    println!("Demonstrating a complete conversation flow with an assistant...");

    // Simulate creating a new thread for a fresh conversation
    println!(" [Simulated] Creating new thread for complete conversation...");
    let thread_id = "thread_conversation_demo";
    println!("   Thread ID: {thread_id}");

    // Conversation turns
    let conversation = [
        ("user", "Hi! I'm working on a project about mathematical sequences. Can you help?"),
        ("assistant", "Hello! I'd be delighted to help you with mathematical sequences. What specific aspect of sequences are you working on? Are you interested in arithmetic sequences, geometric sequences, Fibonacci numbers, or perhaps something else?"),
        ("user", "I'm particularly interested in the golden ratio and how it relates to Fibonacci numbers."),
        ("assistant", "Excellent topic! The golden ratio (φ ≈ 1.618) has a beautiful relationship with Fibonacci numbers. Let me explain and demonstrate this connection."),
        ("user", "Could you calculate the ratio between consecutive Fibonacci numbers to show this?"),
    ];

    println!("\n [Simulated] Conversation Flow:");
    println!("================================");

    for (i, (role, message)) in conversation.iter().enumerate() {
        println!(
            "\n{}. {}: {}",
            i + 1,
            if *role == "user" { "User" } else { "Assistant" },
            message
        );

        if *role == "user" && i < conversation.len() - 1 {
            // Simulate processing time for assistant responses
            print!("   [Processing");
            for _ in 0..3 {
                print!(".");
                io::stdout().flush()?;
                tokio::time::sleep(tokio::time::Duration::from_millis(400)).await;
            }
            println!("]");
        }
    }

    // Demonstrate tool usage in the final response
    println!("\n [Simulated] Final Response with Tool Integration:");
    println!("Assistant: I'll calculate the ratios between consecutive Fibonacci numbers to demonstrate the golden ratio convergence!");

    print!("\n   [Using fibonacci function and code interpreter");
    for _ in 0..4 {
        print!(".");
        io::stdout().flush()?;
        tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
    }
    println!("]");

    println!("\n   Here are the ratios of consecutive Fibonacci numbers:");
    let fib_ratios = [
        (1, 1, 1.0),
        (1, 2, 2.0),
        (2, 3, 1.5),
        (3, 5, 1.667),
        (5, 8, 1.6),
        (8, 13, 1.625),
        (13, 21, 1.615),
        (21, 34, 1.619),
        (34, 55, 1.618),
        (55, 89, 1.618),
    ];

    for (a, b, ratio) in fib_ratios {
        println!(
            "   F({}) / F({}) = {} / {} = {:.3}",
            fib_ratios.iter().position(|(x, _, _)| *x == a).unwrap() + 1,
            fib_ratios.iter().position(|(x, _, _)| *x == b).unwrap() + 2,
            b,
            a,
            ratio
        );
    }

    println!("\n    As you can see, the ratio converges to φ ≈ 1.618 (the golden ratio)!");
    println!("   This demonstrates the beautiful mathematical relationship between");
    println!("   the Fibonacci sequence and the golden ratio.");

    println!("\n Complete Conversation Summary:");
    println!("   • Natural conversation flow with context preservation");
    println!("   • Assistant understanding of complex mathematical concepts");
    println!("   • Seamless tool integration for calculations and demonstrations");
    println!("   • Educational explanations with concrete examples");
    println!("   • Multi-turn conversation maintaining context throughout");

    Ok(())
}

/// Comprehensive error handling helper for assistants API
fn handle_api_error(error: &Error) {
    match error {
        Error::Api {
            status,
            message,
            error_type,
            error_code,
        } => {
            eprintln!(" API Error [{status}]: {message}");
            if let Some(error_type) = error_type {
                eprintln!("   Type: {error_type}");
            }
            if let Some(error_code) = error_code {
                eprintln!("   Code: {error_code}");
            }

            // Provide specific guidance for assistants API errors
            match *status {
                400 => {
                    eprintln!(" Check your request parameters (assistant ID, thread ID, etc.)");
                }
                401 => eprintln!(" Check your API key: export OPENAI_API_KEY=\"your-key\""),
                404 => eprintln!(" Assistant or thread not found - verify IDs are correct"),
                429 => eprintln!(" Rate limited - assistants API has specific rate limits"),
                500..=599 => eprintln!(" Server error - try again later"),
                _ => {}
            }
        }
        Error::InvalidRequest(msg) => {
            eprintln!(" Invalid Request: {msg}");
            eprintln!(" Check your assistant/thread/message parameters");
        }
        Error::Config(msg) => {
            eprintln!(" Configuration Error: {msg}");
            eprintln!(" Check your client configuration");
        }
        Error::Http(err) => {
            eprintln!(" HTTP Error: {err}");
            eprintln!(" Check your network connection");
        }
        Error::HttpMiddleware(err) => {
            eprintln!(" HTTP Middleware Error: {err}");
            eprintln!(" Check your network connection and middleware configuration");
        }
        Error::Json(err) => {
            eprintln!(" JSON Error: {err}");
            eprintln!(" Response parsing failed - may be a temporary issue");
        }
        Error::Authentication(msg) => {
            eprintln!(" Authentication Error: {msg}");
            eprintln!(" Check your API key and organization ID");
        }
        Error::RateLimit(msg) => {
            eprintln!(" Rate Limit Error: {msg}");
            eprintln!(" Assistants API has specific rate limits - wait before retrying");
        }
        Error::Builder(msg) => {
            eprintln!(" Builder Error: {msg}");
            eprintln!(" Check your assistant/thread builder configuration");
        }
        _ => {
            eprintln!(" Unexpected Error: {error}");
            eprintln!(" This may be a bug, please report it");
        }
    }
}

// Helper functions that would be used once the API is implemented

/// Poll run status until completion (future implementation)
#[allow(dead_code)]
fn poll_run_completion(_client: &Client, _thread_id: &str, _run_id: &str) -> MockRun {
    // This would poll the actual API in a real implementation
    // For now, return a simulated completed run
    MockRun {
        id: "run_completed123".to_string(),
        status: "completed".to_string(),
    }
}

/// Mock run type for simulation
#[allow(dead_code)]
struct MockRun {
    id: String,
    status: String,
}

#[allow(dead_code)]
impl MockRun {
    fn id(&self) -> &str {
        &self.id
    }

    fn status(&self) -> &str {
        &self.status
    }
}