cogito_openai/lib.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright (C) 2025 Michael Dippery <michael@monkey-robot.com>
3
4//! An implementation of a client for the OpenAI API.
5//!
6//! This provider implements various traits from [cogito] to provide a uniform
7//! way to access the OpenAI API. This makes it easy to swap out other
8//! providers for OpenAI in your application, or vice versa.
9//!
10//! This library assumes you pass authentication tokens for the OpenAI API
11//! using [`cogito::service::Auth`]. **This means that you are solely
12//! responsible for paying the costs of API access; the Cogito developers
13//! are not responsible for costs you incur while using this library.**
14//!
15//! # Cost
16//!
17//! There's no such thing as a free lunch, and there's no such thing as free OpenAI access,
18//! even if OpenAI is a "non-profit" that is building its technology for the betterment of
19//! humanity (and not Sam Altman's bank account). When you create an OpenAI API client,
20//! you will need to select an [`OpenAIModel`]. Models are billed on a per-token basis, where
21//! a token is the smallest unit of text that the model reads and processes. There are three
22//! types of tokens: input tokens, cached input tokens, and output tokens.
23//!
24//! - **Input tokens** are the token used in any _requests_ made to the OpenAPI AI. This is
25//! the "prompt" that users of this library send to OpenAI for summarization.
26//! - **Cached input tokens** are input tokens that have been reused by GPT. Input tokens are
27//! reused by prompts that have a common prefix, as described
28//! [here](https://openai.com/index/api-prompt-caching/).
29//! - **Output tokens** are tokens generated in the output that is sent back to a client in
30//! response to a request.
31//!
32//! Prices are expressed in US dollars per $1 million tokens. As of 17 July 2025, the prices
33//! for each model are as follows.
34//!
35//! For the latest pricing, see OpenAI's [pricing][OpenAI's platform pricing documentation]
36//! docs.
37//!
38//! | Model | Designation | Input | Cached Input | Output |
39//! |------------|--------------------|---------:|-------------:|--------:|
40//! | Gpt5 | gpt-5 | $1.25 | $0.125 | $10.00 |
41//! | Gpt5mini | gpt-5-mini | $0.25 | $0.025 | $2.00 |
42//! | Gpt5nano | gpt-5-nano | $0.05 | $0.005 | $0.40 |
43//! | Gpt4_1nano | gpt-4.1-nano | $0.10 | $0.025 | $0.40 |
44//! | Gpt4omini | gpt-4o-mini | $0.15 | $0.075 | $0.60 |
45//! | Gpt4_1mini | gpt-4.1-mini | $0.40 | $0.10 | $1.60 |
46//! | O4mini | o4-mini | $1.10 | $0.275 | $4.40 |
47//! | O3mini | o3-mini | $1.10 | $0.55 | $4.40 |
48//! | Gpt4_1 | gpt-4.1 | $2.00 | $0.50 | $8.00 |
49//! | O3 | o3 | $2.00 | $0.50 | $8.00 |
50//! | Gpt4o | gpt-4o | $2.50 | $1.25 | $10.00 |
51//! | ChatGpt4o | chatgpt-4o-latest | $5.00 | - | $15.00 |
52//! | O1 | o1 | $15.00 | $7.50 | $60.00 |
53//! | O3pro | o3-pro | $20.00 | - | $80.00 |
54//! | 01pro | o1-pro | $150.00 | - | $600.00 |
55//!
56//! [cogito]: https://docs.rs/cogito
57//! [`OpenAIClient::new()`]: client::OpenAIClient::new
58//! [`cogito::service::Auth`]: https://docs.rs/cogito/latest/cogito/service/struct.Auth.html
59//! [OpenAI's platform pricing documentation]: https://platform.openai.com/docs/pricing
60
61pub mod client;
62
63use cogito::AiModel;
64use serde::{Deserialize, Serialize};
65use std::fmt;
66
67/// Available OpenAI GPT models.
68///
69/// For more information on the differences between each model, see the
70/// [OpenAI model documentation].
71///
72/// The [default](OpenAIModel::default()) is [gpt-4o](OpenAIModel::Gpt4o),
73/// which OpenAI describes as "the best model to use for most tasks".
74/// [According to its docs][1], [gpt-4.1](OpenAIModel::Gpt4_1) "offers a solid
75/// combination of intelligence, speed, and cost effectiveness". If you are on
76/// a budget, consider using [gpt-4.1-nano](OpenAIModel::Gpt4_1nano), the
77/// [least expensive](OpenAIModel::cheapest()) model.
78///
79/// # Cost
80///
81/// OpenAI API usage has a cost, and the cost of each model differs;
82/// naturally, the more powerful models cost more to use. See the
83/// [cost breakdown] in the `cogito_openai` module documentation
84/// for more details, or visit OpenAI's [pricing] docs for the latest prices.
85///
86/// [1]: https://platform.openai.com/docs/guides/text?api-mode=responses#choosing-a-model
87/// [cost breakdown]: self#Cost
88/// [OpenAI model documentation]: https://platform.openai.com/docs/models
89/// [pricing]: https://platform.openai.com/docs/pricing
90#[derive(Clone, Copy, Debug, Default, PartialEq, Deserialize, Serialize)]
91pub enum OpenAIModel {
92 /// OpenAI's flagship model for coding, reasoning, and agentic tasks
93 /// across domains.
94 #[default]
95 #[serde(rename = "gpt-5")]
96 Gpt5,
97
98 /// A faster, more cost-efficient version of [`GPT-5`](OpenAIModel::Gpt5).
99 ///
100 /// It's great for well-defined tasks and precise prompts.
101 #[serde(rename = "gpt-5-mini")]
102 Gpt5mini,
103
104 /// Fastest, cheapest version of [`GPT-5`](OpenAIModel::Gpt5).
105 ///
106 /// It's great for summarization and classification tasks.
107 #[serde(rename = "gpt-5-nano")]
108 Gpt5nano,
109
110 /// Versatile, high-intelligence flagship model.
111 #[serde(rename = "gpt-4o")]
112 Gpt4o,
113
114 /// A fast, affordable model for focused tasks.
115 #[serde(rename = "gpt-4o-mini")]
116 Gpt4omini,
117
118 /// The flagship model for complex tasks.
119 ///
120 /// It is well-suited for problem-solving across domains.
121 #[serde(rename = "gpt-4.1")]
122 Gpt4_1,
123
124 /// Provides a balance between intelligence, speed, and cost.
125 ///
126 /// An attractive model for many use cases.
127 #[serde(rename = "gpt-4.1-mini")]
128 Gpt4_1mini,
129
130 /// The fastest, most cost-effective 4.1 model.
131 #[serde(rename = "gpt-4.1-nano")]
132 Gpt4_1nano,
133
134 /// Optimized for fast, effective reasoning with exceptionally efficient
135 /// performance in coding and visual tasks.
136 #[serde(rename = "o4-mini")]
137 O4mini,
138
139 /// A well-rounded and powerful reasoning model across domains.
140 ///
141 /// It sets a new standard for math, science, coding, and visual
142 /// reasoning tasks, and excels at technical writing and following
143 /// instructions.
144 #[serde(rename = "o3")]
145 O3,
146
147 /// A mini version of the o3 model, providing high intelligence with
148 /// the same cost and latency targets of o1-mini.
149 #[serde(rename = "o3-mini")]
150 O3mini,
151
152 /// Like the o3 model, but it uses more compute to think even harder.
153 #[serde(rename = "o3-pro")]
154 O3pro,
155
156 /// A model trained with reinforcement learning that thinks before it
157 /// answers and produces a long chain of thought before responding to
158 /// the user.
159 #[serde(rename = "o1")]
160 O1,
161
162 /// A version of the [`o1`](OpenAIModel::O1) model that thinks even harder
163 /// before responding.
164 #[serde(rename = "o1-pro")]
165 O1pro,
166}
167
168impl AiModel for OpenAIModel {
169 /// OpenAI's standard model.
170 fn flagship() -> Self {
171 OpenAIModel::default()
172 }
173
174 /// The "best" GPT model as defined by OpenAI.
175 fn best() -> Self {
176 OpenAIModel::default()
177 }
178
179 fn cheapest() -> Self {
180 OpenAIModel::Gpt5nano
181 }
182
183 fn fastest() -> Self {
184 // GPT 4.1-nano is noticeably faster than GPT 5-nano.
185 OpenAIModel::Gpt4_1nano
186 }
187}
188
189impl fmt::Display for OpenAIModel {
190 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
191 let s = serde_json::to_string(&self)
192 .unwrap_or_else(|_| panic!("could not serialize {:?}", self));
193 let s = s.trim_matches('"');
194 f.write_fmt(format_args!("{}", s))
195 }
196}
197
198/// Convenience module for splat imports.
199///
200/// You can import the most common traits and data structures into your
201/// project using
202///
203/// ```
204/// use cogito_openai::prelude::*;
205/// ```
206pub mod prelude {
207 pub use crate::OpenAIModel;
208 pub use crate::client::{OpenAIClient, OpenAIRequest, OpenAIResponse};
209 pub use cogito::AiModel;
210 pub use cogito::client::{AiClient, AiRequest, AiResponse};
211 pub use cogito::service::Service;
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn it_returns_a_valid_display_string() {
220 let test_cases = vec![
221 (OpenAIModel::Gpt5, "gpt-5"),
222 (OpenAIModel::Gpt5mini, "gpt-5-mini"),
223 (OpenAIModel::Gpt5nano, "gpt-5-nano"),
224 (OpenAIModel::Gpt4o, "gpt-4o"),
225 (OpenAIModel::Gpt4omini, "gpt-4o-mini"),
226 (OpenAIModel::Gpt4_1, "gpt-4.1"),
227 (OpenAIModel::Gpt4_1mini, "gpt-4.1-mini"),
228 (OpenAIModel::Gpt4_1nano, "gpt-4.1-nano"),
229 (OpenAIModel::O4mini, "o4-mini"),
230 (OpenAIModel::O3, "o3"),
231 (OpenAIModel::O3mini, "o3-mini"),
232 (OpenAIModel::O3pro, "o3-pro"),
233 (OpenAIModel::O1, "o1"),
234 (OpenAIModel::O1pro, "o1-pro"),
235 ];
236
237 for (model, descriptor) in test_cases {
238 assert_eq!(model.to_string(), descriptor, "OpenAIModel::{:?}", model);
239 }
240 }
241}