1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
//! Rust SDK for Kagi with explicit protocol surfaces.
//!
//! ## Overview
//!
//! `kagi-sdk` models Kagi access as two distinct surfaces:
//!
//! 1. **Official API surface**
//! - Auth header: `Authorization: Bot <token>`
//! - Route families: `/api/v0/*` and `/api/v1/*` (in-scope subset)
//! - Response shape: JSON envelopes
//!
//! 2. **Session web surface**
//! - Cookie header: `Cookie: kagi_session=<token>`
//! - Routes: `/html/search`, `/mother/summary_labs`, `/mother/summary_labs/`
//! - Response shape:
//! - `search(...)`: HTML parsing
//! - `summarize(...)`: JSON parsing
//! - `summarize_stream(...)`: framed stream parsing (advanced)
//!
//! The SDK enforces auth/surface compatibility at runtime before request execution,
//! returning explicit errors for unsupported combinations.
//!
//! ## Quickstart: official API (bot token)
//!
//! ```no_run
//! use kagi_sdk::{BotToken, KagiClient};
//! use kagi_sdk::official_api::models::SearchRequest;
//!
//! # async fn run() -> Result<(), kagi_sdk::KagiError> {
//! let token = BotToken::new(std::env::var("KAGI_API_KEY").expect("set KAGI_API_KEY"))?;
//! let client = KagiClient::with_bot_token(token)?;
//! let api = client.official_api()?;
//!
//! let response = api.search(SearchRequest::new("rust error handling")?).await?;
//! println!("{:#?}", response.data);
//! # Ok(())
//! # }
//! # fn main() {}
//! ```
//!
//! ## Quickstart: session web (session token)
//!
//! ```no_run
//! use kagi_sdk::{KagiClient, SessionToken};
//! use kagi_sdk::session_web::models::{SearchRequest, SummarizeRequest};
//!
//! # async fn run() -> Result<(), kagi_sdk::KagiError> {
//! let token =
//! SessionToken::new(std::env::var("KAGI_SESSION_TOKEN").expect("set KAGI_SESSION_TOKEN"))?;
//! let client = KagiClient::with_session_token(token)?;
//! let web = client.session_web()?;
//!
//! let search = web.search(SearchRequest::new("kagi rust sdk")?).await?;
//! let summary = web
//! .summarize(SummarizeRequest::from_url("https://example.com")?)
//! .await?;
//! println!("{} {}", search.results.len(), summary.markdown.len());
//! # Ok(())
//! # }
//! # fn main() {}
//! ```
//!
//! ## Builder configuration example
//!
//! ```no_run
//! use std::time::Duration;
//! use kagi_sdk::{BotToken, ClientConfig, KagiClient};
//!
//! # fn run() -> Result<(), kagi_sdk::KagiError> {
//! let config = ClientConfig::default()
//! .with_timeout(Duration::from_secs(15))
//! .with_user_agent("my-app/0.1.0");
//!
//! let _client = KagiClient::builder()
//! .config(config)
//! .bot_token(BotToken::new("your-token")?)
//! .build()?;
//! # Ok(())
//! # }
//! # fn main() {}
//! ```
//!
//! ## Error handling
//!
//! Most fallible operations return [`KagiError`]. Prefer `?` propagation and map errors at your
//! application boundary.
//!
//! ## Module overview
//!
//! | Module | Purpose |
//! |---|---|
//! | [`auth`] | Credential types and header/cookie application |
//! | [`client`] | `KagiClient` construction and surface entrypoints |
//! | [`config`] | Client base URL, timeout, and user-agent configuration |
//! | [`official_api`] | Typed official API requests and envelope parsing |
//! | [`session_web`] | Typed session-web requests for HTML search, JSON summarize, and advanced framed streaming |
//! | [`parsing`] | Parser helpers for session HTML search, summarize JSON, and framed streaming |
//! | [`routing`] | Endpoint metadata (surface, version, parser shape) |
//! | [`error`] | Central typed error model |
//!
//! ## Safety
//!
//! This crate forbids `unsafe` Rust (`#![forbid(unsafe_code)]`).
//!
pub use ;
pub use ;
pub use ;
pub use ClientConfig;
pub use KagiError;