chatgpt/
client.rs

1use std::path::Path;
2use std::str::FromStr;
3use ureq::{Agent, AgentBuilder};
4use chrono::Local;
5use reqwest::header::AUTHORIZATION;
6use reqwest::{
7    header::{HeaderMap, HeaderValue},
8    Url,
9};
10
11use crate::converse::Conversation;
12use crate::types::{ChatMessage, CompletionRequest, CompletionResponse, Role, ServerResponse};
13
14/// The client that operates the ChatGPT API
15#[derive(Debug, Clone)]
16pub struct ChatGPT {
17    client: ureq::Agent,
18    token: String,
19}
20
21impl ChatGPT {
22    /// Constructs a new ChatGPT API client with provided API Key
23    pub fn new<S: Into<String>>(api_key: S) -> crate::Result<Self> {
24        let api_key = api_key.into();
25        let token = format!("Bearer {api_key}");
26        let client = AgentBuilder::new().build();
27        Ok(Self { client, token})
28    }
29
30    /// Starts a new conversation with a default starting message.
31    ///
32    /// Conversations record message history.
33    pub fn new_conversation(&self) -> Conversation {
34        self.new_conversation_directed(format!("You are ChatGPT, an AI model developed by OpenAI. Answer as concisely as possible. Today is: {0}", Local::now().format("%d/%m/%Y %H:%M")))
35    }
36
37    /// Starts a new conversation with a specified starting message.
38    ///
39    /// Conversations record message history.
40    pub fn new_conversation_directed<S: Into<String>>(&self, direction_message: S) -> Conversation {
41        Conversation::new(self.clone(), direction_message.into())
42    }
43
44    /// Explicitly sends whole message history to the API.
45    ///
46    /// In most cases, if you would like to store message history, you should be looking at the [`Conversation`] struct, and
47    /// [`Self::new_conversation()`] and [`Self::new_conversation_directed()`]
48    pub fn send_history(
49        &self,
50        history: &Vec<ChatMessage>,
51    ) -> String {
52        let response = self
53            .client
54            .post("https://api.openai.com/v1/chat/completions")
55            .set("Authorization", &self.token)
56            .send_json(ureq::json!({
57                "model": "gpt-3.5-turbo",
58                "rust": true,
59                "messages": history
60            }))
61            .unwrap()
62            .into_string();
63        response.unwrap_or_default()
64    }
65
66    /// Sends a single message to the API without preserving message history.
67    pub fn send_message<S: Into<String>>(
68        &self,
69        message: S,
70    ) -> Result<CompletionResponse, ureq::Error> {
71        let response: CompletionResponse = self
72            .client
73            .post("https://api.openai.com/v1/chat/completions")
74            .set("Authorization", &self.token)
75            .send_json(ureq::json!({
76                "model": "gpt-3.5-turbo",
77                "messages": &vec![ChatMessage {
78                    role: Role::User,
79                    content: message.into(),
80                }],
81            }))
82            .unwrap()
83            .into_json()?;
84        Ok(response)
85    }
86}