intr_providers/types.rs
1//! Shared request/response types for all providers.
2
3use std::collections::HashMap;
4
5use secrecy::SecretString;
6use serde::{Deserialize, Serialize};
7
8// ---------------------------------------------------------------------------
9// Messages
10// ---------------------------------------------------------------------------
11
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
13#[serde(rename_all = "lowercase")]
14pub enum Role {
15 System,
16 User,
17 Assistant,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct Message {
22 pub role: Role,
23 pub content: String,
24}
25
26// ---------------------------------------------------------------------------
27// API key
28// ---------------------------------------------------------------------------
29
30/// Represents the API key used for a model call.
31///
32/// Uses [`SecretString`] from the `secrecy` crate which:
33/// - Never exposes the secret in `Debug` output
34/// - Zeroes the memory on drop
35#[derive(Clone)]
36pub enum ApiKey {
37 /// Caller-supplied key - passed per request, never persisted by Intentry.
38 UserSupplied(SecretString),
39 /// Intentry-owned key - resolved from environment at startup.
40 /// (Used when Intentry runs model calls on behalf of the user.)
41 IntentryOwned,
42}
43
44impl std::fmt::Debug for ApiKey {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 match self {
47 ApiKey::UserSupplied(_) => write!(f, "ApiKey::UserSupplied([REDACTED])"),
48 ApiKey::IntentryOwned => write!(f, "ApiKey::IntentryOwned"),
49 }
50 }
51}
52
53// ---------------------------------------------------------------------------
54// Request
55// ---------------------------------------------------------------------------
56
57/// A model generation request, normalised across providers.
58#[derive(Debug, Clone)]
59pub struct GenerateRequest {
60 /// Model identifier, e.g. `"claude-sonnet-4-6"` or `"gpt-4o"`.
61 pub model: String,
62 /// Conversation turn history.
63 pub messages: Vec<Message>,
64 /// Sampling temperature (0.0–1.0). `None` → provider default.
65 pub temperature: Option<f32>,
66 /// Maximum output tokens. `None` → provider default.
67 pub max_tokens: Option<u32>,
68 /// Request JSON-mode output from the provider when supported.
69 pub json_mode: bool,
70 /// Provider-specific extra parameters (passed through verbatim).
71 pub extra: HashMap<String, serde_json::Value>,
72 /// API key to use for this call.
73 pub api_key: ApiKey,
74 /// Per-request timeout in milliseconds. Default: 30 000 ms.
75 pub timeout_ms: u32,
76}
77
78impl Default for GenerateRequest {
79 fn default() -> Self {
80 Self {
81 model: String::new(),
82 messages: Vec::new(),
83 temperature: None,
84 max_tokens: None,
85 json_mode: false,
86 extra: HashMap::new(),
87 api_key: ApiKey::IntentryOwned,
88 timeout_ms: 30_000,
89 }
90 }
91}
92
93// ---------------------------------------------------------------------------
94// Response
95// ---------------------------------------------------------------------------
96
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub enum FinishReason {
99 Stop,
100 MaxTokens,
101 ContentFilter,
102 Other(String),
103}
104
105/// A normalised generation response.
106#[derive(Debug, Clone)]
107pub struct GenerateResponse {
108 /// Model output text (the assistant turn).
109 pub text: String,
110 /// Why the generation stopped.
111 pub finish_reason: FinishReason,
112 /// Input tokens consumed.
113 pub tokens_in: u32,
114 /// Output tokens generated.
115 pub tokens_out: u32,
116 /// Exact model ID that handled the request (provider may route).
117 pub model_used: String,
118 /// End-to-end wall-clock latency in milliseconds.
119 pub latency_ms: u32,
120 /// Raw provider JSON response (opaque; useful for debugging).
121 pub raw_response: serde_json::Value,
122}