liter_llm 1.0.0.pre.rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +239 -0
  3. data/ext/liter_llm_rb/extconf.rb +65 -0
  4. data/ext/liter_llm_rb/native/.cargo/config.toml +23 -0
  5. data/ext/liter_llm_rb/native/Cargo.lock +3713 -0
  6. data/ext/liter_llm_rb/native/Cargo.toml +32 -0
  7. data/ext/liter_llm_rb/native/build.rs +15 -0
  8. data/ext/liter_llm_rb/native/src/lib.rs +1079 -0
  9. data/lib/liter_llm.rb +8 -0
  10. data/sig/liter_llm.rbs +416 -0
  11. data/vendor/Cargo.toml +54 -0
  12. data/vendor/liter-llm/Cargo.toml +92 -0
  13. data/vendor/liter-llm/README.md +252 -0
  14. data/vendor/liter-llm/schemas/pricing.json +40 -0
  15. data/vendor/liter-llm/schemas/providers.json +1662 -0
  16. data/vendor/liter-llm/src/auth/azure_ad.rs +264 -0
  17. data/vendor/liter-llm/src/auth/bedrock_sts.rs +353 -0
  18. data/vendor/liter-llm/src/auth/mod.rs +68 -0
  19. data/vendor/liter-llm/src/auth/vertex_oauth.rs +353 -0
  20. data/vendor/liter-llm/src/client/config.rs +351 -0
  21. data/vendor/liter-llm/src/client/managed.rs +622 -0
  22. data/vendor/liter-llm/src/client/mod.rs +864 -0
  23. data/vendor/liter-llm/src/cost.rs +212 -0
  24. data/vendor/liter-llm/src/error.rs +190 -0
  25. data/vendor/liter-llm/src/http/eventstream.rs +860 -0
  26. data/vendor/liter-llm/src/http/mod.rs +12 -0
  27. data/vendor/liter-llm/src/http/request.rs +438 -0
  28. data/vendor/liter-llm/src/http/retry.rs +72 -0
  29. data/vendor/liter-llm/src/http/streaming.rs +289 -0
  30. data/vendor/liter-llm/src/lib.rs +37 -0
  31. data/vendor/liter-llm/src/provider/anthropic.rs +2250 -0
  32. data/vendor/liter-llm/src/provider/azure.rs +579 -0
  33. data/vendor/liter-llm/src/provider/bedrock.rs +1543 -0
  34. data/vendor/liter-llm/src/provider/cohere.rs +654 -0
  35. data/vendor/liter-llm/src/provider/custom.rs +404 -0
  36. data/vendor/liter-llm/src/provider/google_ai.rs +281 -0
  37. data/vendor/liter-llm/src/provider/mistral.rs +188 -0
  38. data/vendor/liter-llm/src/provider/mod.rs +616 -0
  39. data/vendor/liter-llm/src/provider/vertex.rs +1504 -0
  40. data/vendor/liter-llm/src/tests.rs +1425 -0
  41. data/vendor/liter-llm/src/tokenizer.rs +281 -0
  42. data/vendor/liter-llm/src/tower/budget.rs +599 -0
  43. data/vendor/liter-llm/src/tower/cache.rs +502 -0
  44. data/vendor/liter-llm/src/tower/cache_opendal.rs +270 -0
  45. data/vendor/liter-llm/src/tower/cooldown.rs +231 -0
  46. data/vendor/liter-llm/src/tower/cost.rs +404 -0
  47. data/vendor/liter-llm/src/tower/fallback.rs +121 -0
  48. data/vendor/liter-llm/src/tower/health.rs +219 -0
  49. data/vendor/liter-llm/src/tower/hooks.rs +369 -0
  50. data/vendor/liter-llm/src/tower/mod.rs +77 -0
  51. data/vendor/liter-llm/src/tower/rate_limit.rs +300 -0
  52. data/vendor/liter-llm/src/tower/router.rs +436 -0
  53. data/vendor/liter-llm/src/tower/service.rs +181 -0
  54. data/vendor/liter-llm/src/tower/tests.rs +539 -0
  55. data/vendor/liter-llm/src/tower/tests_common.rs +252 -0
  56. data/vendor/liter-llm/src/tower/tracing.rs +209 -0
  57. data/vendor/liter-llm/src/tower/types.rs +170 -0
  58. data/vendor/liter-llm/src/types/audio.rs +52 -0
  59. data/vendor/liter-llm/src/types/batch.rs +77 -0
  60. data/vendor/liter-llm/src/types/chat.rs +214 -0
  61. data/vendor/liter-llm/src/types/common.rs +244 -0
  62. data/vendor/liter-llm/src/types/embedding.rs +84 -0
  63. data/vendor/liter-llm/src/types/files.rs +58 -0
  64. data/vendor/liter-llm/src/types/image.rs +40 -0
  65. data/vendor/liter-llm/src/types/mod.rs +27 -0
  66. data/vendor/liter-llm/src/types/models.rs +21 -0
  67. data/vendor/liter-llm/src/types/moderation.rs +80 -0
  68. data/vendor/liter-llm/src/types/ocr.rs +87 -0
  69. data/vendor/liter-llm/src/types/rerank.rs +46 -0
  70. data/vendor/liter-llm/src/types/responses.rs +55 -0
  71. data/vendor/liter-llm/src/types/search.rs +45 -0
  72. data/vendor/liter-llm/tests/contract.rs +332 -0
  73. data/vendor/liter-llm-ffi/Cargo.toml +30 -0
  74. data/vendor/liter-llm-ffi/build.rs +66 -0
  75. data/vendor/liter-llm-ffi/cbindgen.toml +60 -0
  76. data/vendor/liter-llm-ffi/liter_llm.h +850 -0
  77. data/vendor/liter-llm-ffi/src/lib.rs +2488 -0
  78. metadata +286 -0
@@ -0,0 +1,77 @@
1
+ use serde::{Deserialize, Serialize};
2
+
3
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
4
+ #[serde(deny_unknown_fields)]
5
+ pub struct CreateBatchRequest {
6
+ pub input_file_id: String,
7
+ pub endpoint: String,
8
+ pub completion_window: String,
9
+ #[serde(default, skip_serializing_if = "Option::is_none")]
10
+ pub metadata: Option<serde_json::Value>,
11
+ }
12
+
13
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14
+ #[serde(rename_all = "snake_case")]
15
+ pub enum BatchStatus {
16
+ Validating,
17
+ Failed,
18
+ InProgress,
19
+ Finalizing,
20
+ Completed,
21
+ Expired,
22
+ Cancelling,
23
+ Cancelled,
24
+ }
25
+
26
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
27
+ pub struct BatchObject {
28
+ pub id: String,
29
+ pub object: String,
30
+ pub endpoint: String,
31
+ pub input_file_id: String,
32
+ pub completion_window: String,
33
+ pub status: BatchStatus,
34
+ #[serde(default, skip_serializing_if = "Option::is_none")]
35
+ pub output_file_id: Option<String>,
36
+ #[serde(default, skip_serializing_if = "Option::is_none")]
37
+ pub error_file_id: Option<String>,
38
+ pub created_at: u64,
39
+ #[serde(default, skip_serializing_if = "Option::is_none")]
40
+ pub completed_at: Option<u64>,
41
+ #[serde(default, skip_serializing_if = "Option::is_none")]
42
+ pub failed_at: Option<u64>,
43
+ #[serde(default, skip_serializing_if = "Option::is_none")]
44
+ pub expired_at: Option<u64>,
45
+ #[serde(default, skip_serializing_if = "Option::is_none")]
46
+ pub request_counts: Option<BatchRequestCounts>,
47
+ #[serde(default, skip_serializing_if = "Option::is_none")]
48
+ pub metadata: Option<serde_json::Value>,
49
+ }
50
+
51
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
52
+ pub struct BatchRequestCounts {
53
+ pub total: u64,
54
+ pub completed: u64,
55
+ pub failed: u64,
56
+ }
57
+
58
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59
+ pub struct BatchListResponse {
60
+ pub object: String,
61
+ pub data: Vec<BatchObject>,
62
+ #[serde(default, skip_serializing_if = "Option::is_none")]
63
+ pub has_more: Option<bool>,
64
+ #[serde(default, skip_serializing_if = "Option::is_none")]
65
+ pub first_id: Option<String>,
66
+ #[serde(default, skip_serializing_if = "Option::is_none")]
67
+ pub last_id: Option<String>,
68
+ }
69
+
70
+ #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
71
+ #[serde(deny_unknown_fields)]
72
+ pub struct BatchListQuery {
73
+ #[serde(default, skip_serializing_if = "Option::is_none")]
74
+ pub limit: Option<u32>,
75
+ #[serde(default, skip_serializing_if = "Option::is_none")]
76
+ pub after: Option<String>,
77
+ }
@@ -0,0 +1,214 @@
1
+ use std::collections::BTreeMap;
2
+
3
+ use serde::{Deserialize, Serialize};
4
+
5
+ use super::common::{
6
+ AssistantMessage, ChatCompletionTool, Message, ResponseFormat, StopSequence, ToolChoice, ToolType, Usage,
7
+ };
8
+ use crate::cost;
9
+
10
+ // ─── Finish Reason ────────────────────────────────────────────────────────────
11
+
12
+ /// Why a choice stopped generating tokens.
13
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14
+ #[serde(rename_all = "snake_case")]
15
+ pub enum FinishReason {
16
+ Stop,
17
+ Length,
18
+ ToolCalls,
19
+ ContentFilter,
20
+ /// Deprecated legacy finish reason; retained for API compatibility.
21
+ #[serde(rename = "function_call")]
22
+ FunctionCall,
23
+ /// Catch-all for unknown finish reasons returned by non-OpenAI providers.
24
+ ///
25
+ /// Note: this intentionally does **not** carry the original string (e.g.
26
+ /// `Other(String)`). Using `#[serde(other)]` requires a unit variant, and
27
+ /// switching to `#[serde(untagged)]` would change deserialization semantics
28
+ /// for all variants. The original value can be recovered by inspecting the
29
+ /// raw JSON if needed.
30
+ #[serde(other)]
31
+ Other,
32
+ }
33
+
34
+ // ─── Reasoning Effort ────────────────────────────────────────────────────────
35
+
36
+ /// Controls how much reasoning effort the model should use.
37
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38
+ #[serde(rename_all = "lowercase")]
39
+ pub enum ReasoningEffort {
40
+ Low,
41
+ Medium,
42
+ High,
43
+ }
44
+
45
+ // ─── Request ─────────────────────────────────────────────────────────────────
46
+
47
+ #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
48
+ #[serde(deny_unknown_fields)]
49
+ pub struct ChatCompletionRequest {
50
+ pub model: String,
51
+ pub messages: Vec<Message>,
52
+ #[serde(default, skip_serializing_if = "Option::is_none")]
53
+ pub temperature: Option<f64>,
54
+ #[serde(default, skip_serializing_if = "Option::is_none")]
55
+ pub top_p: Option<f64>,
56
+ #[serde(default, skip_serializing_if = "Option::is_none")]
57
+ pub n: Option<u32>,
58
+ /// Whether to stream the response.
59
+ ///
60
+ /// This field is managed by the client layer (`prepare_request`) and should
61
+ /// not be set directly by callers — use `chat` for non-streaming and
62
+ /// `chat_stream` for streaming. Making it `pub(crate)` prevents callers
63
+ /// from setting it explicitly, which would conflict with the client's own
64
+ /// stream flag injection.
65
+ #[serde(default, skip_serializing_if = "Option::is_none")]
66
+ pub(crate) stream: Option<bool>,
67
+ #[serde(default, skip_serializing_if = "Option::is_none")]
68
+ pub stop: Option<StopSequence>,
69
+ #[serde(default, skip_serializing_if = "Option::is_none")]
70
+ pub max_tokens: Option<u64>,
71
+ #[serde(default, skip_serializing_if = "Option::is_none")]
72
+ pub presence_penalty: Option<f64>,
73
+ #[serde(default, skip_serializing_if = "Option::is_none")]
74
+ pub frequency_penalty: Option<f64>,
75
+ #[serde(default, skip_serializing_if = "Option::is_none")]
76
+ /// Token bias map. Uses `BTreeMap` (sorted keys) for deterministic
77
+ /// serialization order — important when hashing or signing requests.
78
+ pub logit_bias: Option<BTreeMap<String, f64>>,
79
+ #[serde(default, skip_serializing_if = "Option::is_none")]
80
+ pub user: Option<String>,
81
+ #[serde(default, skip_serializing_if = "Option::is_none")]
82
+ pub tools: Option<Vec<ChatCompletionTool>>,
83
+ #[serde(default, skip_serializing_if = "Option::is_none")]
84
+ pub tool_choice: Option<ToolChoice>,
85
+ #[serde(default, skip_serializing_if = "Option::is_none")]
86
+ pub parallel_tool_calls: Option<bool>,
87
+ #[serde(default, skip_serializing_if = "Option::is_none")]
88
+ pub response_format: Option<ResponseFormat>,
89
+ #[serde(default, skip_serializing_if = "Option::is_none")]
90
+ pub stream_options: Option<StreamOptions>,
91
+ #[serde(default, skip_serializing_if = "Option::is_none")]
92
+ pub seed: Option<i64>,
93
+ #[serde(default, skip_serializing_if = "Option::is_none")]
94
+ pub reasoning_effort: Option<ReasoningEffort>,
95
+ /// Provider-specific extra parameters merged into the request body.
96
+ /// Use for guardrails, safety settings, grounding config, etc.
97
+ #[serde(default, skip_serializing_if = "Option::is_none")]
98
+ pub extra_body: Option<serde_json::Value>,
99
+ }
100
+
101
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
102
+ #[serde(deny_unknown_fields)]
103
+ pub struct StreamOptions {
104
+ #[serde(default, skip_serializing_if = "Option::is_none")]
105
+ pub include_usage: Option<bool>,
106
+ }
107
+
108
+ // ─── Response ────────────────────────────────────────────────────────────────
109
+
110
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
111
+ pub struct ChatCompletionResponse {
112
+ pub id: String,
113
+ /// Always `"chat.completion"` from OpenAI-compatible APIs. Stored as a
114
+ /// plain `String` so non-standard provider values do not break deserialization.
115
+ pub object: String,
116
+ pub created: u64,
117
+ pub model: String,
118
+ pub choices: Vec<Choice>,
119
+ #[serde(default, skip_serializing_if = "Option::is_none")]
120
+ pub usage: Option<Usage>,
121
+ #[serde(default, skip_serializing_if = "Option::is_none")]
122
+ pub system_fingerprint: Option<String>,
123
+ #[serde(default, skip_serializing_if = "Option::is_none")]
124
+ pub service_tier: Option<String>,
125
+ }
126
+
127
+ impl ChatCompletionResponse {
128
+ /// Estimate the cost of this response based on embedded pricing data.
129
+ ///
130
+ /// Returns `None` if:
131
+ /// - the `model` field is not present in the embedded pricing registry, or
132
+ /// - the `usage` field is absent from the response.
133
+ ///
134
+ /// # Example
135
+ ///
136
+ /// ```rust,ignore
137
+ /// let cost = response.estimated_cost();
138
+ /// if let Some(usd) = cost {
139
+ /// println!("Request cost: ${usd:.6}");
140
+ /// }
141
+ /// ```
142
+ #[must_use]
143
+ pub fn estimated_cost(&self) -> Option<f64> {
144
+ let usage = self.usage.as_ref()?;
145
+ cost::completion_cost(&self.model, usage.prompt_tokens, usage.completion_tokens)
146
+ }
147
+ }
148
+
149
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
150
+ pub struct Choice {
151
+ pub index: u32,
152
+ pub message: AssistantMessage,
153
+ pub finish_reason: Option<FinishReason>,
154
+ }
155
+
156
+ // ─── Stream Chunk ────────────────────────────────────────────────────────────
157
+
158
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
159
+ pub struct ChatCompletionChunk {
160
+ pub id: String,
161
+ /// Always `"chat.completion.chunk"` from OpenAI-compatible APIs. Stored
162
+ /// as a plain `String` so non-standard provider values do not fail parsing.
163
+ pub object: String,
164
+ pub created: u64,
165
+ pub model: String,
166
+ pub choices: Vec<StreamChoice>,
167
+ #[serde(default, skip_serializing_if = "Option::is_none")]
168
+ pub usage: Option<Usage>,
169
+ #[serde(default, skip_serializing_if = "Option::is_none")]
170
+ pub system_fingerprint: Option<String>,
171
+ #[serde(default, skip_serializing_if = "Option::is_none")]
172
+ pub service_tier: Option<String>,
173
+ }
174
+
175
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
176
+ pub struct StreamChoice {
177
+ pub index: u32,
178
+ pub delta: StreamDelta,
179
+ pub finish_reason: Option<FinishReason>,
180
+ }
181
+
182
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
183
+ pub struct StreamDelta {
184
+ #[serde(default, skip_serializing_if = "Option::is_none")]
185
+ pub role: Option<String>,
186
+ #[serde(default, skip_serializing_if = "Option::is_none")]
187
+ pub content: Option<String>,
188
+ #[serde(default, skip_serializing_if = "Option::is_none")]
189
+ pub tool_calls: Option<Vec<StreamToolCall>>,
190
+ /// Deprecated legacy function_call delta; retained for API compatibility.
191
+ #[serde(default, skip_serializing_if = "Option::is_none")]
192
+ pub function_call: Option<StreamFunctionCall>,
193
+ #[serde(default, skip_serializing_if = "Option::is_none")]
194
+ pub refusal: Option<String>,
195
+ }
196
+
197
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
198
+ pub struct StreamToolCall {
199
+ pub index: u32,
200
+ #[serde(default, skip_serializing_if = "Option::is_none")]
201
+ pub id: Option<String>,
202
+ #[serde(default, skip_serializing_if = "Option::is_none", rename = "type")]
203
+ pub call_type: Option<ToolType>,
204
+ #[serde(default, skip_serializing_if = "Option::is_none")]
205
+ pub function: Option<StreamFunctionCall>,
206
+ }
207
+
208
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
209
+ pub struct StreamFunctionCall {
210
+ #[serde(default, skip_serializing_if = "Option::is_none")]
211
+ pub name: Option<String>,
212
+ #[serde(default, skip_serializing_if = "Option::is_none")]
213
+ pub arguments: Option<String>,
214
+ }
@@ -0,0 +1,244 @@
1
+ use serde::{Deserialize, Serialize};
2
+
3
+ // ─── Messages ────────────────────────────────────────────────────────────────
4
+
5
+ /// A chat message in a conversation.
6
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7
+ #[serde(tag = "role")]
8
+ pub enum Message {
9
+ #[serde(rename = "system")]
10
+ System(SystemMessage),
11
+ #[serde(rename = "user")]
12
+ User(UserMessage),
13
+ #[serde(rename = "assistant")]
14
+ Assistant(AssistantMessage),
15
+ #[serde(rename = "tool")]
16
+ Tool(ToolMessage),
17
+ #[serde(rename = "developer")]
18
+ Developer(DeveloperMessage),
19
+ /// Deprecated legacy function-role message; retained for API compatibility.
20
+ #[serde(rename = "function")]
21
+ Function(FunctionMessage),
22
+ }
23
+
24
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
25
+ pub struct SystemMessage {
26
+ pub content: String,
27
+ #[serde(default, skip_serializing_if = "Option::is_none")]
28
+ pub name: Option<String>,
29
+ }
30
+
31
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32
+ pub struct UserMessage {
33
+ pub content: UserContent,
34
+ #[serde(default, skip_serializing_if = "Option::is_none")]
35
+ pub name: Option<String>,
36
+ }
37
+
38
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
39
+ #[serde(untagged)]
40
+ pub enum UserContent {
41
+ Text(String),
42
+ Parts(Vec<ContentPart>),
43
+ }
44
+
45
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
46
+ #[serde(tag = "type")]
47
+ pub enum ContentPart {
48
+ #[serde(rename = "text")]
49
+ Text { text: String },
50
+ #[serde(rename = "image_url")]
51
+ ImageUrl { image_url: ImageUrl },
52
+ #[serde(rename = "document")]
53
+ Document { document: DocumentContent },
54
+ #[serde(rename = "input_audio")]
55
+ InputAudio { input_audio: AudioContent },
56
+ }
57
+
58
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59
+ #[serde(deny_unknown_fields)]
60
+ pub struct ImageUrl {
61
+ pub url: String,
62
+ #[serde(default, skip_serializing_if = "Option::is_none")]
63
+ pub detail: Option<ImageDetail>,
64
+ }
65
+
66
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
67
+ #[serde(rename_all = "lowercase")]
68
+ pub enum ImageDetail {
69
+ Low,
70
+ High,
71
+ Auto,
72
+ }
73
+
74
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
75
+ #[serde(deny_unknown_fields)]
76
+ pub struct DocumentContent {
77
+ /// Base64-encoded document data or URL.
78
+ pub data: String,
79
+ /// MIME type (e.g., "application/pdf", "text/csv").
80
+ pub media_type: String,
81
+ }
82
+
83
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
84
+ #[serde(deny_unknown_fields)]
85
+ pub struct AudioContent {
86
+ /// Base64-encoded audio data.
87
+ pub data: String,
88
+ /// Audio format (e.g., "wav", "mp3", "ogg").
89
+ pub format: String,
90
+ }
91
+
92
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
93
+ pub struct AssistantMessage {
94
+ #[serde(default, skip_serializing_if = "Option::is_none")]
95
+ pub content: Option<String>,
96
+ #[serde(default, skip_serializing_if = "Option::is_none")]
97
+ pub name: Option<String>,
98
+ #[serde(default, skip_serializing_if = "Option::is_none")]
99
+ pub tool_calls: Option<Vec<ToolCall>>,
100
+ #[serde(default, skip_serializing_if = "Option::is_none")]
101
+ pub refusal: Option<String>,
102
+ /// Deprecated legacy function_call field; retained for API compatibility.
103
+ #[serde(default, skip_serializing_if = "Option::is_none")]
104
+ pub function_call: Option<FunctionCall>,
105
+ }
106
+
107
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
108
+ pub struct ToolMessage {
109
+ pub content: String,
110
+ pub tool_call_id: String,
111
+ #[serde(default, skip_serializing_if = "Option::is_none")]
112
+ pub name: Option<String>,
113
+ }
114
+
115
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
116
+ pub struct DeveloperMessage {
117
+ pub content: String,
118
+ #[serde(default, skip_serializing_if = "Option::is_none")]
119
+ pub name: Option<String>,
120
+ }
121
+
122
+ /// Deprecated legacy function-role message body.
123
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
124
+ pub struct FunctionMessage {
125
+ pub content: String,
126
+ pub name: String,
127
+ }
128
+
129
+ // ─── Tools ───────────────────────────────────────────────────────────────────
130
+
131
+ /// The type discriminator for tool/tool-call objects. Per the OpenAI spec this
132
+ /// is always `"function"`. Using an enum enforces that constraint at the type
133
+ /// level and rejects any other value on deserialization.
134
+ #[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
135
+ pub enum ToolType {
136
+ #[default]
137
+ #[serde(rename = "function")]
138
+ Function,
139
+ }
140
+
141
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
142
+ #[serde(deny_unknown_fields)]
143
+ pub struct ChatCompletionTool {
144
+ #[serde(rename = "type")]
145
+ pub tool_type: ToolType,
146
+ pub function: FunctionDefinition,
147
+ }
148
+
149
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
150
+ #[serde(deny_unknown_fields)]
151
+ pub struct FunctionDefinition {
152
+ pub name: String,
153
+ #[serde(default, skip_serializing_if = "Option::is_none")]
154
+ pub description: Option<String>,
155
+ #[serde(default, skip_serializing_if = "Option::is_none")]
156
+ pub parameters: Option<serde_json::Value>,
157
+ #[serde(default, skip_serializing_if = "Option::is_none")]
158
+ pub strict: Option<bool>,
159
+ }
160
+
161
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
162
+ pub struct ToolCall {
163
+ pub id: String,
164
+ #[serde(rename = "type")]
165
+ pub call_type: ToolType,
166
+ pub function: FunctionCall,
167
+ }
168
+
169
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
170
+ pub struct FunctionCall {
171
+ pub name: String,
172
+ pub arguments: String,
173
+ }
174
+
175
+ // ─── Tool Choice ─────────────────────────────────────────────────────────────
176
+
177
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
178
+ #[serde(untagged)]
179
+ pub enum ToolChoice {
180
+ Mode(ToolChoiceMode),
181
+ Specific(SpecificToolChoice),
182
+ }
183
+
184
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
185
+ #[serde(rename_all = "lowercase")]
186
+ pub enum ToolChoiceMode {
187
+ Auto,
188
+ Required,
189
+ None,
190
+ }
191
+
192
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
193
+ pub struct SpecificToolChoice {
194
+ #[serde(rename = "type")]
195
+ pub choice_type: ToolType,
196
+ pub function: SpecificFunction,
197
+ }
198
+
199
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
200
+ pub struct SpecificFunction {
201
+ pub name: String,
202
+ }
203
+
204
+ // ─── Response Format ─────────────────────────────────────────────────────────
205
+
206
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
207
+ #[serde(tag = "type")]
208
+ pub enum ResponseFormat {
209
+ #[serde(rename = "text")]
210
+ Text,
211
+ #[serde(rename = "json_object")]
212
+ JsonObject,
213
+ #[serde(rename = "json_schema")]
214
+ JsonSchema { json_schema: JsonSchemaFormat },
215
+ }
216
+
217
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
218
+ #[serde(deny_unknown_fields)]
219
+ pub struct JsonSchemaFormat {
220
+ pub name: String,
221
+ #[serde(default, skip_serializing_if = "Option::is_none")]
222
+ pub description: Option<String>,
223
+ pub schema: serde_json::Value,
224
+ #[serde(default, skip_serializing_if = "Option::is_none")]
225
+ pub strict: Option<bool>,
226
+ }
227
+
228
+ // ─── Usage ───────────────────────────────────────────────────────────────────
229
+
230
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
231
+ pub struct Usage {
232
+ pub prompt_tokens: u64,
233
+ pub completion_tokens: u64,
234
+ pub total_tokens: u64,
235
+ }
236
+
237
+ // ─── Stop Sequence ───────────────────────────────────────────────────────────
238
+
239
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240
+ #[serde(untagged)]
241
+ pub enum StopSequence {
242
+ Single(String),
243
+ Multiple(Vec<String>),
244
+ }
@@ -0,0 +1,84 @@
1
+ use serde::{Deserialize, Serialize};
2
+
3
+ use super::common::Usage;
4
+ use crate::cost;
5
+
6
+ // ─── Encoding format ──────────────────────────────────────────────────────────
7
+
8
+ /// The format in which the embedding vectors are returned.
9
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
10
+ #[serde(rename_all = "lowercase")]
11
+ pub enum EmbeddingFormat {
12
+ /// 32-bit floating-point numbers (default).
13
+ Float,
14
+ /// Base64-encoded string representation of the floats.
15
+ Base64,
16
+ }
17
+
18
+ // ─── Request ──────────────────────────────────────────────────────────────────
19
+
20
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
21
+ #[serde(deny_unknown_fields)]
22
+ pub struct EmbeddingRequest {
23
+ pub model: String,
24
+ pub input: EmbeddingInput,
25
+ #[serde(default, skip_serializing_if = "Option::is_none")]
26
+ pub encoding_format: Option<EmbeddingFormat>,
27
+ #[serde(default, skip_serializing_if = "Option::is_none")]
28
+ pub dimensions: Option<u32>,
29
+ #[serde(default, skip_serializing_if = "Option::is_none")]
30
+ pub user: Option<String>,
31
+ }
32
+
33
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
34
+ #[serde(untagged)]
35
+ pub enum EmbeddingInput {
36
+ Single(String),
37
+ Multiple(Vec<String>),
38
+ }
39
+
40
+ // ─── Response ─────────────────────────────────────────────────────────────────
41
+
42
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
43
+ pub struct EmbeddingResponse {
44
+ /// Always `"list"` from OpenAI-compatible APIs. Stored as a plain
45
+ /// `String` so non-standard provider values do not break deserialization.
46
+ pub object: String,
47
+ pub data: Vec<EmbeddingObject>,
48
+ pub model: String,
49
+ #[serde(default, skip_serializing_if = "Option::is_none")]
50
+ pub usage: Option<Usage>,
51
+ }
52
+
53
+ impl EmbeddingResponse {
54
+ /// Estimate the cost of this embedding request based on embedded pricing data.
55
+ ///
56
+ /// Returns `None` if:
57
+ /// - the `model` field is not present in the embedded pricing registry, or
58
+ /// - the `usage` field is absent from the response.
59
+ ///
60
+ /// Embedding models only charge for input tokens; output cost is zero.
61
+ ///
62
+ /// # Example
63
+ ///
64
+ /// ```rust,ignore
65
+ /// let cost = response.estimated_cost();
66
+ /// if let Some(usd) = cost {
67
+ /// println!("Embedding cost: ${usd:.8}");
68
+ /// }
69
+ /// ```
70
+ #[must_use]
71
+ pub fn estimated_cost(&self) -> Option<f64> {
72
+ let usage = self.usage.as_ref()?;
73
+ cost::completion_cost(&self.model, usage.prompt_tokens, usage.completion_tokens)
74
+ }
75
+ }
76
+
77
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
78
+ pub struct EmbeddingObject {
79
+ /// Always `"embedding"` from OpenAI-compatible APIs. Stored as a plain
80
+ /// `String` so non-standard provider values do not break deserialization.
81
+ pub object: String,
82
+ pub embedding: Vec<f64>,
83
+ pub index: u32,
84
+ }
@@ -0,0 +1,58 @@
1
+ use serde::{Deserialize, Serialize};
2
+
3
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
4
+ #[serde(rename_all = "snake_case")]
5
+ pub enum FilePurpose {
6
+ Assistants,
7
+ Batch,
8
+ FineTune,
9
+ Vision,
10
+ }
11
+
12
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13
+ #[serde(deny_unknown_fields)]
14
+ pub struct CreateFileRequest {
15
+ /// Base64-encoded file data.
16
+ pub file: String,
17
+ pub purpose: FilePurpose,
18
+ #[serde(default, skip_serializing_if = "Option::is_none")]
19
+ pub filename: Option<String>,
20
+ }
21
+
22
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23
+ pub struct FileObject {
24
+ pub id: String,
25
+ pub object: String,
26
+ pub bytes: u64,
27
+ pub created_at: u64,
28
+ pub filename: String,
29
+ pub purpose: String,
30
+ #[serde(default, skip_serializing_if = "Option::is_none")]
31
+ pub status: Option<String>,
32
+ }
33
+
34
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
35
+ pub struct FileListResponse {
36
+ pub object: String,
37
+ pub data: Vec<FileObject>,
38
+ #[serde(default, skip_serializing_if = "Option::is_none")]
39
+ pub has_more: Option<bool>,
40
+ }
41
+
42
+ #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
43
+ #[serde(deny_unknown_fields)]
44
+ pub struct FileListQuery {
45
+ #[serde(default, skip_serializing_if = "Option::is_none")]
46
+ pub purpose: Option<String>,
47
+ #[serde(default, skip_serializing_if = "Option::is_none")]
48
+ pub limit: Option<u32>,
49
+ #[serde(default, skip_serializing_if = "Option::is_none")]
50
+ pub after: Option<String>,
51
+ }
52
+
53
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
54
+ pub struct DeleteResponse {
55
+ pub id: String,
56
+ pub object: String,
57
+ pub deleted: bool,
58
+ }