klieo_llm_gemini/lib.rs
1#![deny(missing_docs)]
2#![deny(rust_2018_idioms)]
3#![deny(rustdoc::broken_intra_doc_links)]
4
5//! Google Gemini (Generative Language API) client implementing
6//! [`klieo_core::LlmClient`].
7//!
8//! Wraps Google's `:generateContent` / `:streamGenerateContent` /
9//! `:embedContent` / `:batchEmbedContents` endpoints. Capability
10//! declaration (`tool_calling`, `streaming`, `structured_output`,
11//! `embeddings`, `max_context_tokens`, `vision`) is computed at
12//! construction time from the configured chat-model name; see
13//! [`capabilities::model_capabilities`].
14//!
15//! Sibling to `klieo-llm-ollama`, `klieo-llm-openai`, and
16//! `klieo-llm-anthropic` — the four crates implement the same
17//! `LlmClient` trait so a `Box<dyn LlmClient>` consumer can swap
18//! between them without other code changes.
19//!
20//! # Differences from OpenAI / Anthropic
21//!
22//! - **Auth**: `x-goog-api-key: <key>` header (NOT `Authorization:
23//! Bearer`, NOT `?key=` query parameter). Vertex AI's OAuth bearer
24//! variant is out of scope for v0.1.0.
25//! - **Endpoints are model-scoped**: `/v1beta/models/{model}:generateContent`
26//! etc. The model name is part of the URL path — the request body
27//! does NOT carry a top-level `model` field.
28//! - **Roles are `user` and `model`** (NOT `assistant` / `system`).
29//! The system prompt is extracted into the top-level
30//! `systemInstruction` request field.
31//! - **Tool input is a JSON object** on the wire (`functionCall.args`
32//! is a JSON object, like Anthropic, opposite of OpenAI). No
33//! string-parse boundary needed.
34//! - **Content shape**: `Content { role, parts: [Part] }` where each
35//! `Part` is one of `{text}`, `{functionCall}`, `{functionResponse}`,
36//! or `{inlineData}` (vision).
37//! - **Tool definitions** are wrapped in a protobuf-style group:
38//! `tools: [{functionDeclarations: [...]}]`.
39//! - **Streaming** is SSE (`?alt=sse`) where each `data:` line carries
40//! a partial `Candidate`. **Function-calls arrive complete in a
41//! single chunk** (not fragment-by-fragment like OpenAI / Anthropic),
42//! so the parser does NOT need a per-call accumulator.
43//! - **Embeddings ARE supported** via `:embedContent` (single input)
44//! and `:batchEmbedContents` (multi-input). Single vs batch is chosen
45//! internally based on `texts.len()`.
46//!
47//! # Example
48//!
49//! ```no_run
50//! use klieo_llm_gemini::GeminiClient;
51//! let client = GeminiClient::new("AIza...", "gemini-2.0-flash");
52//! # let _ = client;
53//! ```
54//!
55//! # Limitations
56//!
57//! - Vision input is declared in `Capabilities::vision` but the wire
58//! encoding for `inlineData` parts is not implemented in v0.1.0;
59//! see the carryovers section of the implementation plan.
60//! - Vertex AI deployment (different endpoint + OAuth bearer auth) is
61//! not yet a first-class target; override the base URL via
62//! [`GeminiClient::with_base_url`] for self-hosted proxies.
63//! - Built-in Gemini tools (code execution, Google Search grounding)
64//! and `cachedContents` are deferred to Wave 9 follow-ups.
65
66pub mod capabilities;
67pub use capabilities::model_capabilities;
68
69pub(crate) mod error;
70pub(crate) mod stream;
71pub(crate) mod wire;
72
73pub mod client;
74pub use client::GeminiClient;