kagi_api/v0/
universal_summarizer.rs

1use async_trait::async_trait;
2use serde::{Deserialize, Serialize};
3
4use crate::v0::Meta;
5use crate::{KagiClient, KagiError, KagiResult};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
8pub struct Summary {
9    pub meta: Meta,
10    pub data: Data,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Data {
15    pub output: String,
16    pub tokens: usize,
17}
18
19/// Parameters url and text are exclusive. You must pass one or the other.
20///
21/// Total request size is limited to 1MB.
22#[derive(Debug, Clone)]
23pub enum Subject {
24    /// A [url::Url] to a document to summarize.
25    Url(url::Url),
26
27    /// Text to summarize.
28    Text(String),
29}
30
31impl Subject {
32    pub fn url_or_text(input: String) -> Self {
33        url::Url::parse(&input)
34            .map(Subject::Url)
35            .unwrap_or_else(|_| Subject::Text(input))
36    }
37}
38
39/// Several options are provided to control the output the summarizer produces.
40///
41/// - Summary Types
42/// - Summarization Engines
43/// - Target Language Codes
44/// - Cache
45#[derive(Debug, Default)]
46pub struct SummaryOptions {
47    /// Different summary types are provided that control the structure of the summary output.
48    pub summary_type: Option<SummaryType>,
49
50    /// Different summarization engines give you choices over the "flavor" of the summary.
51    pub engine: Option<Engine>,
52
53    /// The summarizer can translate the output into a desired language
54    pub target_language: Option<Language>,
55
56    /// Whether to allow cached requests & responses. (default is true)
57    pub cache: Option<bool>,
58}
59
60/// Different summary types are provided that control the structure of the summary output.
61#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
62#[serde(rename_all = "lowercase")]
63pub enum SummaryType {
64    /// Paragraph(s) of summary prose
65    #[default]
66    Summary,
67
68    /// Bulleted list of key points
69    Takeaway,
70}
71
72/// Different summarization engines are provided that will give you choices over the "flavor"
73/// of the summarization text.
74#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
75#[serde(rename_all = "lowercase")]
76pub enum Engine {
77    /// Friendly, descriptive, fast summary
78    #[default]
79    Cecil,
80
81    /// Formal, technical, analytical summary
82    Agnes,
83
84    /// Informal, creative, friendly summary
85    Daphne,
86
87    /// Best-in-class summary using Kagi's enterprise-grade model
88    Muriel,
89}
90
91/// The summarizer can translate the output into a desired language, using the table of supported
92/// language codes below.
93///
94/// If no language is specified, the document's original language is allowed to influence the
95/// summarizer's output. Specifying a language will add a an explicit translation step, to
96/// translate the summary to the desired language.
97///
98/// For example, if a document is mostly written in Spanish, the summary output may itself be in
99/// Spanish or contain Spanish passages. Specifying "EN" will ensure all passages are translated
100/// as English.
101#[derive(Debug, Default, Copy, Clone, Serialize, Deserialize)]
102pub enum Language {
103    /// Bulgarian
104    BG,
105    /// Czech
106    CS,
107    /// Danish
108    DA,
109    /// German
110    DE,
111    /// Greek
112    EL,
113    /// English
114    #[default]
115    EN,
116    /// Spanish
117    ES,
118    /// Estonian
119    ET,
120    /// Finnish
121    FI,
122    /// French
123    FR,
124    /// Hungarian
125    HU,
126    /// Indonesian
127    ID,
128    /// Italian
129    IT,
130    /// Japanese
131    JA,
132    /// Korean
133    KO,
134    /// Lithuanian
135    LT,
136    /// Latvian
137    LV,
138    /// Norwegian
139    NB,
140    /// Dutch
141    NL,
142    /// Polish
143    PL,
144    /// Portuguese
145    PT,
146    /// Romanian
147    RO,
148    /// Russian
149    RU,
150    /// Slovak
151    SK,
152    /// Slovenian
153    SL,
154    /// Swedish
155    SV,
156    /// Turkish
157    TR,
158    /// Ukrainian
159    UK,
160    /// Chinese (simplified)
161    ZH,
162}
163
164/// The Universal Summarizer is an API using powerful LLMs to summarize content on the web, or
165/// your own documents, of any length.
166#[async_trait]
167pub trait UniversalSummarizer {
168    async fn summarize(&self, subject: Subject, options: SummaryOptions) -> KagiResult<Summary>;
169
170    async fn summarize_url(&self, url: url::Url, options: SummaryOptions) -> KagiResult<Summary> {
171        self.summarize(Subject::Url(url), options).await
172    }
173
174    async fn summarize_text(&self, text: String, options: SummaryOptions) -> KagiResult<Summary> {
175        self.summarize(Subject::Text(text), options).await
176    }
177}
178
179#[async_trait]
180impl UniversalSummarizer for KagiClient {
181    async fn summarize(&self, subject: Subject, options: SummaryOptions) -> KagiResult<Summary> {
182        let request_body = build_request_body(subject, options);
183        let response = self.query("/v0/summarize", request_body).await?;
184        response
185            .json::<Summary>()
186            .await
187            .map_err(KagiError::ReqwestError)
188    }
189}
190
191fn build_request_body(subject: Subject, options: SummaryOptions) -> serde_json::Value {
192    let mut request_body = match subject {
193        Subject::Url(url) => serde_json::json!({ "url": url }),
194        Subject::Text(text) => serde_json::json!({ "text": text }),
195    };
196
197    if let Some(summary_type) = options.summary_type {
198        request_body["summary_type"] = serde_json::to_value(summary_type).unwrap();
199    }
200
201    if let Some(engine) = options.engine {
202        request_body["engine"] = serde_json::to_value(engine).unwrap();
203    }
204
205    if let Some(target_language) = options.target_language {
206        request_body["target_language"] = serde_json::to_value(target_language).unwrap();
207    }
208
209    if let Some(cache) = options.cache {
210        request_body["cache"] = serde_json::to_value(cache).unwrap();
211    }
212
213    request_body
214}
215
216impl std::fmt::Display for SummaryType {
217    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
218        match self {
219            SummaryType::Summary => write!(f, "summary"),
220            SummaryType::Takeaway => write!(f, "takeaway"),
221        }
222    }
223}
224
225impl std::str::FromStr for SummaryType {
226    type Err = String;
227
228    fn from_str(s: &str) -> Result<Self, Self::Err> {
229        match s {
230            "summary" => Ok(SummaryType::Summary),
231            "takeaway" => Ok(SummaryType::Takeaway),
232            _ => Err(format!("Unknown summary type '{s}'")),
233        }
234    }
235}
236
237impl std::fmt::Display for Language {
238    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239        let language = match self {
240            Language::BG => "bg",
241            Language::CS => "cs",
242            Language::DA => "da",
243            Language::DE => "de",
244            Language::EL => "el",
245            Language::EN => "en",
246            Language::ES => "es",
247            Language::ET => "et",
248            Language::FI => "fi",
249            Language::FR => "fr",
250            Language::HU => "hu",
251            Language::ID => "id",
252            Language::IT => "it",
253            Language::JA => "ja",
254            Language::KO => "ko",
255            Language::LT => "lt",
256            Language::LV => "lv",
257            Language::NB => "nb",
258            Language::NL => "nl",
259            Language::PL => "pl",
260            Language::PT => "pt",
261            Language::RO => "ro",
262            Language::RU => "ru",
263            Language::SK => "sk",
264            Language::SL => "sl",
265            Language::SV => "sv",
266            Language::TR => "tr",
267            Language::UK => "uk",
268            Language::ZH => "zh",
269        };
270
271        write!(f, "{}", language)
272    }
273}
274
275impl std::str::FromStr for Language {
276    type Err = String;
277
278    fn from_str(s: &str) -> Result<Self, Self::Err> {
279        let language = match s.to_lowercase().as_ref() {
280            "bg" => Language::BG,
281            "cs" => Language::CS,
282            "da" => Language::DA,
283            "de" => Language::DE,
284            "el" => Language::EL,
285            "en" => Language::EN,
286            "es" => Language::ES,
287            "et" => Language::ET,
288            "fi" => Language::FI,
289            "fr" => Language::FR,
290            "hu" => Language::HU,
291            "id" => Language::ID,
292            "it" => Language::IT,
293            "ja" => Language::JA,
294            "ko" => Language::KO,
295            "lt" => Language::LT,
296            "lv" => Language::LV,
297            "nb" => Language::NB,
298            "nl" => Language::NL,
299            "pl" => Language::PL,
300            "pt" => Language::PT,
301            "ro" => Language::RO,
302            "ru" => Language::RU,
303            "sk" => Language::SK,
304            "sl" => Language::SL,
305            "sv" => Language::SV,
306            "tr" => Language::TR,
307            "uk" => Language::UK,
308            "zh" => Language::ZH,
309            _ => return Err(format!("Unknown language '{s}'")),
310        };
311
312        Ok(language)
313    }
314}
315
316impl std::fmt::Display for Engine {
317    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318        let engine = match self {
319            Engine::Cecil => "cecil",
320            Engine::Agnes => "agnes",
321            Engine::Daphne => "daphne",
322            Engine::Muriel => "muriel",
323        };
324
325        write!(f, "{}", engine)
326    }
327}
328
329impl std::str::FromStr for Engine {
330    type Err = String;
331
332    fn from_str(s: &str) -> Result<Self, Self::Err> {
333        let engine = match s.to_lowercase().as_ref() {
334            "cecil" => Engine::Cecil,
335            "agnes" => Engine::Agnes,
336            "daphne" => Engine::Daphne,
337            "muriel" => Engine::Muriel,
338            _ => return Err(format!("Unknown engine '{s}")),
339        };
340
341        Ok(engine)
342    }
343}