llmsdk_provider/
files_model.rs1use async_trait::async_trait;
14use serde::{Deserialize, Serialize};
15
16use crate::error::Result;
17use crate::shared::{FileBytes, ProviderMetadata, ProviderOptions, ProviderReference, Warning};
18
19#[async_trait]
23pub trait FilesModel: Send + Sync + std::fmt::Debug {
24 fn provider(&self) -> &str;
26
27 fn specification_version(&self) -> &'static str {
29 "v4"
30 }
31
32 async fn upload_file(&self, options: UploadFileOptions) -> Result<UploadFileResult>;
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
47pub struct UploadFileOptions {
48 pub data: UploadFileData,
50 #[serde(rename = "mediaType")]
52 pub media_type: String,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub filename: Option<String>,
56 #[serde(
58 default,
59 rename = "providerOptions",
60 skip_serializing_if = "Option::is_none"
61 )]
62 pub provider_options: Option<ProviderOptions>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
71#[serde(tag = "type", rename_all = "kebab-case")]
72pub enum UploadFileData {
73 Data {
75 data: FileBytes,
77 },
78 Text {
80 text: String,
82 },
83}
84
85#[derive(Debug, Clone)]
89pub struct UploadFileResult {
90 pub provider_reference: ProviderReference,
92 pub media_type: Option<String>,
94 pub filename: Option<String>,
96 pub provider_metadata: Option<ProviderMetadata>,
98 pub warnings: Vec<Warning>,
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105 use serde_json::json;
106
107 #[test]
108 fn options_serde_roundtrip_data() {
109 let opts = UploadFileOptions {
110 data: UploadFileData::Data {
111 data: FileBytes::Base64("aGVsbG8=".into()),
112 },
113 media_type: "text/plain".into(),
114 filename: Some("hi.txt".into()),
115 provider_options: None,
116 };
117 let json = serde_json::to_value(&opts).unwrap();
118 assert_eq!(json["mediaType"], "text/plain");
119 assert_eq!(json["data"]["type"], "data");
120 assert_eq!(json["data"]["data"], "aGVsbG8=");
121 let back: UploadFileOptions = serde_json::from_value(json).unwrap();
122 assert_eq!(back.media_type, "text/plain");
123 }
124
125 #[test]
126 fn options_serde_roundtrip_text() {
127 let opts = UploadFileOptions {
128 data: UploadFileData::Text {
129 text: "hello".into(),
130 },
131 media_type: "text/plain".into(),
132 filename: None,
133 provider_options: None,
134 };
135 let json = serde_json::to_value(&opts).unwrap();
136 assert_eq!(json["data"], json!({ "type": "text", "text": "hello" }));
137 }
138
139 #[test]
140 fn upload_data_tagged_correctly() {
141 let v = serde_json::to_value(UploadFileData::Data {
142 data: FileBytes::Bytes(vec![1, 2, 3]),
143 })
144 .unwrap();
145 assert_eq!(v["type"], "data");
146 }
147}