Skip to main content

neuron_types/
types.rs

1//! Core message and request/response types.
2
3use std::collections::HashMap;
4use std::path::PathBuf;
5use std::sync::Arc;
6
7use serde::{Deserialize, Serialize};
8use tokio_util::sync::CancellationToken;
9
10use crate::wasm::{WasmCompatSend, WasmCompatSync};
11
12/// The role of a message participant.
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub enum Role {
15    /// A human user.
16    User,
17    /// An AI assistant.
18    Assistant,
19    /// A system message.
20    System,
21}
22
23/// A content block within a message.
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub enum ContentBlock {
26    /// Plain text content.
27    Text(String),
28    /// Extended thinking from reasoning models.
29    Thinking {
30        /// The thinking text.
31        thinking: String,
32        /// Cryptographic signature for verification.
33        signature: String,
34    },
35    /// Redacted thinking (not visible to user).
36    RedactedThinking {
37        /// Opaque data blob.
38        data: String,
39    },
40    /// A tool invocation request from the assistant.
41    ToolUse {
42        /// Unique identifier for this tool call.
43        id: String,
44        /// Name of the tool to invoke.
45        name: String,
46        /// JSON input arguments.
47        input: serde_json::Value,
48    },
49    /// Result of a tool invocation.
50    ToolResult {
51        /// References the `id` from the corresponding `ToolUse`.
52        tool_use_id: String,
53        /// Content items in the result.
54        content: Vec<ContentItem>,
55        /// Whether this result represents an error.
56        is_error: bool,
57    },
58    /// An image content block.
59    Image {
60        /// The image source.
61        source: ImageSource,
62    },
63    /// A document content block.
64    Document {
65        /// The document source.
66        source: DocumentSource,
67    },
68    /// Server-side context compaction summary.
69    Compaction {
70        /// The compacted context summary.
71        content: String,
72    },
73}
74
75/// A content item within a tool result.
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub enum ContentItem {
78    /// Plain text content.
79    Text(String),
80    /// An image.
81    Image {
82        /// The image source.
83        source: ImageSource,
84    },
85}
86
87/// Source of an image.
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub enum ImageSource {
90    /// Base64-encoded image data.
91    Base64 {
92        /// MIME type (e.g. "image/png").
93        media_type: String,
94        /// Base64-encoded data.
95        data: String,
96    },
97    /// URL to an image.
98    Url {
99        /// The image URL.
100        url: String,
101    },
102}
103
104/// Source of a document.
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub enum DocumentSource {
107    /// Base64-encoded PDF.
108    Base64Pdf {
109        /// Base64-encoded PDF data.
110        data: String,
111    },
112    /// Plain text document.
113    PlainText {
114        /// The text content.
115        data: String,
116    },
117    /// URL to a document.
118    Url {
119        /// The document URL.
120        url: String,
121    },
122}
123
124/// A message in a conversation.
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct Message {
127    /// The role of the message author.
128    pub role: Role,
129    /// The content blocks of this message.
130    pub content: Vec<ContentBlock>,
131}
132
133impl Message {
134    /// Create a user message with a single text content block.
135    ///
136    /// # Example
137    ///
138    /// ```
139    /// use neuron_types::Message;
140    /// let msg = Message::user("What is Rust?");
141    /// ```
142    #[must_use]
143    pub fn user(text: impl Into<String>) -> Self {
144        Self {
145            role: Role::User,
146            content: vec![ContentBlock::Text(text.into())],
147        }
148    }
149
150    /// Create an assistant message with a single text content block.
151    ///
152    /// # Example
153    ///
154    /// ```
155    /// use neuron_types::Message;
156    /// let msg = Message::assistant("Rust is a systems programming language.");
157    /// ```
158    #[must_use]
159    pub fn assistant(text: impl Into<String>) -> Self {
160        Self {
161            role: Role::Assistant,
162            content: vec![ContentBlock::Text(text.into())],
163        }
164    }
165
166    /// Create a system message with a single text content block.
167    ///
168    /// # Example
169    ///
170    /// ```
171    /// use neuron_types::Message;
172    /// let msg = Message::system("You are a helpful assistant.");
173    /// ```
174    #[must_use]
175    pub fn system(text: impl Into<String>) -> Self {
176        Self {
177            role: Role::System,
178            content: vec![ContentBlock::Text(text.into())],
179        }
180    }
181}
182
183// --- Completion request/response types ---
184
185/// System prompt configuration.
186#[derive(Debug, Clone, Serialize, Deserialize)]
187pub enum SystemPrompt {
188    /// A simple text system prompt.
189    Text(String),
190    /// Structured system prompt blocks with optional cache control.
191    Blocks(Vec<SystemBlock>),
192}
193
194/// A block within a structured system prompt.
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct SystemBlock {
197    /// The text content of this block.
198    pub text: String,
199    /// Optional cache control for this block.
200    pub cache_control: Option<CacheControl>,
201}
202
203/// Cache control configuration for prompt caching.
204#[derive(Debug, Clone, Serialize, Deserialize)]
205pub struct CacheControl {
206    /// Time-to-live for the cached content.
207    pub ttl: Option<CacheTtl>,
208}
209
210/// Cache time-to-live options.
211#[derive(Debug, Clone, Serialize, Deserialize)]
212pub enum CacheTtl {
213    /// Cache for 5 minutes.
214    FiveMinutes,
215    /// Cache for 1 hour.
216    OneHour,
217}
218
219/// Tool selection strategy for the model.
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub enum ToolChoice {
222    /// Model decides whether to use tools.
223    Auto,
224    /// Model must not use tools.
225    None,
226    /// Model must use at least one tool.
227    Required,
228    /// Model must use the specified tool.
229    Specific {
230        /// Name of the required tool.
231        name: String,
232    },
233}
234
235/// Response format configuration.
236#[derive(Debug, Clone, Serialize, Deserialize)]
237pub enum ResponseFormat {
238    /// Plain text response.
239    Text,
240    /// JSON object response.
241    JsonObject,
242    /// Structured JSON response with schema validation.
243    JsonSchema {
244        /// Name of the schema.
245        name: String,
246        /// The JSON Schema.
247        schema: serde_json::Value,
248        /// Whether to enforce strict schema adherence.
249        strict: bool,
250    },
251}
252
253/// Extended thinking configuration for reasoning models.
254#[derive(Debug, Clone, Serialize, Deserialize)]
255pub enum ThinkingConfig {
256    /// Enable thinking with a token budget.
257    Enabled {
258        /// Maximum tokens for thinking.
259        budget_tokens: usize,
260    },
261    /// Disable thinking.
262    Disabled,
263    /// Let the model decide.
264    Adaptive,
265}
266
267/// Reasoning effort level for reasoning models.
268#[derive(Debug, Clone, Serialize, Deserialize)]
269pub enum ReasoningEffort {
270    /// No reasoning.
271    None,
272    /// Minimal reasoning.
273    Low,
274    /// Moderate reasoning.
275    Medium,
276    /// Maximum reasoning.
277    High,
278}
279
280/// Server-side context management configuration.
281///
282/// Instructs the provider to manage context window size automatically,
283/// e.g. by compacting conversation history when it grows too large.
284#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
285pub struct ContextManagement {
286    /// The context edits to apply.
287    pub edits: Vec<ContextEdit>,
288}
289
290/// A context editing operation.
291#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
292pub enum ContextEdit {
293    /// Compact conversation history using the named strategy.
294    Compact {
295        /// Strategy identifier (e.g. `"compact_20260112"`).
296        strategy: String,
297    },
298}
299
300/// Per-iteration token usage breakdown (returned during server-side compaction).
301#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
302pub struct UsageIteration {
303    /// Tokens in the input for this iteration.
304    pub input_tokens: usize,
305    /// Tokens in the output for this iteration.
306    pub output_tokens: usize,
307    /// Tokens read from cache in this iteration.
308    pub cache_read_tokens: Option<usize>,
309    /// Tokens written to cache in this iteration.
310    pub cache_creation_tokens: Option<usize>,
311}
312
313// --- Embedding types ---
314
315/// A request to an embedding model.
316#[derive(Debug, Clone, Default, PartialEq)]
317pub struct EmbeddingRequest {
318    /// The embedding model to use (e.g. `"text-embedding-3-small"`).
319    pub model: String,
320    /// The text inputs to embed.
321    pub input: Vec<String>,
322    /// Optional number of dimensions for the output embeddings.
323    pub dimensions: Option<usize>,
324    /// Provider-specific extra fields forwarded verbatim.
325    pub extra: HashMap<String, serde_json::Value>,
326}
327
328/// Response from an embedding request.
329#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
330pub struct EmbeddingResponse {
331    /// The embedding vectors, one per input string.
332    pub embeddings: Vec<Vec<f32>>,
333    /// The model that generated the embeddings.
334    pub model: String,
335    /// Token usage statistics.
336    pub usage: EmbeddingUsage,
337}
338
339/// Token usage statistics for an embedding request.
340#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
341pub struct EmbeddingUsage {
342    /// Number of tokens in the input.
343    pub prompt_tokens: usize,
344    /// Total tokens consumed.
345    pub total_tokens: usize,
346}
347
348/// A completion request to an LLM provider.
349#[derive(Debug, Clone, Default, Serialize, Deserialize)]
350pub struct CompletionRequest {
351    /// The model identifier.
352    pub model: String,
353    /// The conversation messages.
354    pub messages: Vec<Message>,
355    /// Optional system prompt.
356    pub system: Option<SystemPrompt>,
357    /// Tool definitions available to the model.
358    pub tools: Vec<ToolDefinition>,
359    /// Maximum tokens to generate.
360    pub max_tokens: Option<usize>,
361    /// Sampling temperature (0.0 to 1.0).
362    pub temperature: Option<f32>,
363    /// Nucleus sampling parameter.
364    pub top_p: Option<f32>,
365    /// Sequences that cause generation to stop.
366    pub stop_sequences: Vec<String>,
367    /// Tool selection strategy.
368    pub tool_choice: Option<ToolChoice>,
369    /// Response format constraint.
370    pub response_format: Option<ResponseFormat>,
371    /// Extended thinking configuration.
372    pub thinking: Option<ThinkingConfig>,
373    /// Reasoning effort level.
374    pub reasoning_effort: Option<ReasoningEffort>,
375    /// Provider-specific extra fields forwarded verbatim.
376    pub extra: Option<serde_json::Value>,
377    /// Server-side context management configuration.
378    pub context_management: Option<ContextManagement>,
379}
380
381/// A completion response from an LLM provider.
382#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct CompletionResponse {
384    /// Provider-assigned message ID.
385    pub id: String,
386    /// The model that generated this response.
387    pub model: String,
388    /// The response message.
389    pub message: Message,
390    /// Token usage statistics.
391    pub usage: TokenUsage,
392    /// Why the model stopped generating.
393    pub stop_reason: StopReason,
394}
395
396/// Reason the model stopped generating.
397#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
398pub enum StopReason {
399    /// Model reached a natural end.
400    EndTurn,
401    /// Model wants to use a tool.
402    ToolUse,
403    /// Hit the max token limit.
404    MaxTokens,
405    /// Hit a stop sequence.
406    StopSequence,
407    /// Content was filtered.
408    ContentFilter,
409    /// Server paused to compact context.
410    Compaction,
411}
412
413/// Token usage statistics for a completion.
414#[derive(Debug, Clone, Default, Serialize, Deserialize)]
415pub struct TokenUsage {
416    /// Tokens in the input/prompt.
417    pub input_tokens: usize,
418    /// Tokens in the output/completion.
419    pub output_tokens: usize,
420    /// Tokens read from cache.
421    pub cache_read_tokens: Option<usize>,
422    /// Tokens written to cache.
423    pub cache_creation_tokens: Option<usize>,
424    /// Tokens used for reasoning/thinking.
425    pub reasoning_tokens: Option<usize>,
426    /// Per-iteration token breakdown (for server-side compaction).
427    pub iterations: Option<Vec<UsageIteration>>,
428}
429
430// --- Tool definition types (needed by CompletionRequest) ---
431
432/// Definition of a tool available to the model.
433#[derive(Debug, Clone, Serialize, Deserialize)]
434pub struct ToolDefinition {
435    /// The tool name (unique identifier).
436    pub name: String,
437    /// Optional human-readable title.
438    pub title: Option<String>,
439    /// Description of what the tool does.
440    pub description: String,
441    /// JSON Schema for the tool's input parameters.
442    pub input_schema: serde_json::Value,
443    /// Optional JSON Schema for the tool's output.
444    pub output_schema: Option<serde_json::Value>,
445    /// Optional behavioral annotations (MCP spec).
446    pub annotations: Option<ToolAnnotations>,
447    /// Optional cache control for this tool definition.
448    pub cache_control: Option<CacheControl>,
449}
450
451/// Behavioral annotations for a tool (from MCP spec).
452#[derive(Debug, Clone, Serialize, Deserialize)]
453pub struct ToolAnnotations {
454    /// Whether the tool only reads data.
455    pub read_only_hint: Option<bool>,
456    /// Whether the tool performs destructive operations.
457    pub destructive_hint: Option<bool>,
458    /// Whether repeated calls with same args produce same result.
459    pub idempotent_hint: Option<bool>,
460    /// Whether the tool interacts with external systems.
461    pub open_world_hint: Option<bool>,
462}
463
464/// Output from a tool execution.
465#[derive(Debug, Clone, Serialize, Deserialize)]
466pub struct ToolOutput {
467    /// Human-readable content items.
468    pub content: Vec<ContentItem>,
469    /// Optional structured JSON output for programmatic consumption.
470    pub structured_content: Option<serde_json::Value>,
471    /// Whether this output represents an error.
472    pub is_error: bool,
473}
474
475/// Runtime context provided to tools during execution.
476pub struct ToolContext {
477    /// Current working directory.
478    pub cwd: PathBuf,
479    /// Session identifier.
480    pub session_id: String,
481    /// Environment variables available to the tool.
482    pub environment: HashMap<String, String>,
483    /// Token for cooperative cancellation.
484    pub cancellation_token: CancellationToken,
485    /// Optional progress reporter for long-running tools.
486    pub progress_reporter: Option<Arc<dyn ProgressReporter>>,
487}
488
489impl Default for ToolContext {
490    /// Creates a ToolContext with sensible defaults:
491    /// - `cwd`: current directory (falls back to `/tmp` if unavailable)
492    /// - `session_id`: empty string
493    /// - `environment`: empty
494    /// - `cancellation_token`: new token
495    /// - `progress_reporter`: None
496    fn default() -> Self {
497        Self {
498            cwd: std::env::current_dir().unwrap_or_else(|_| PathBuf::from("/tmp")),
499            session_id: String::new(),
500            environment: HashMap::new(),
501            cancellation_token: CancellationToken::new(),
502            progress_reporter: None,
503        }
504    }
505}
506
507/// Resource usage limits for the agentic loop.
508///
509/// Controls how many requests, tool calls, and tokens the loop may consume
510/// before terminating with `LoopError::UsageLimitExceeded`.
511/// All limits are optional — only set limits are enforced.
512///
513/// # Example
514///
515/// ```
516/// use neuron_types::UsageLimits;
517///
518/// let limits = UsageLimits::default()
519///     .with_request_limit(50)
520///     .with_total_tokens_limit(100_000);
521/// ```
522#[derive(Debug, Clone, Default, PartialEq, Eq)]
523pub struct UsageLimits {
524    /// Maximum number of LLM requests (provider calls) allowed.
525    pub request_limit: Option<usize>,
526    /// Maximum number of tool calls allowed across all turns.
527    pub tool_calls_limit: Option<usize>,
528    /// Maximum input tokens allowed across all turns.
529    pub input_tokens_limit: Option<usize>,
530    /// Maximum output tokens allowed across all turns.
531    pub output_tokens_limit: Option<usize>,
532    /// Maximum total tokens (input + output) allowed across all turns.
533    pub total_tokens_limit: Option<usize>,
534}
535
536impl UsageLimits {
537    /// Set the maximum number of LLM requests.
538    #[must_use]
539    pub fn with_request_limit(mut self, limit: usize) -> Self {
540        self.request_limit = Some(limit);
541        self
542    }
543
544    /// Set the maximum number of tool calls.
545    #[must_use]
546    pub fn with_tool_calls_limit(mut self, limit: usize) -> Self {
547        self.tool_calls_limit = Some(limit);
548        self
549    }
550
551    /// Set the maximum input tokens.
552    #[must_use]
553    pub fn with_input_tokens_limit(mut self, limit: usize) -> Self {
554        self.input_tokens_limit = Some(limit);
555        self
556    }
557
558    /// Set the maximum output tokens.
559    #[must_use]
560    pub fn with_output_tokens_limit(mut self, limit: usize) -> Self {
561        self.output_tokens_limit = Some(limit);
562        self
563    }
564
565    /// Set the maximum total tokens (input + output).
566    #[must_use]
567    pub fn with_total_tokens_limit(mut self, limit: usize) -> Self {
568        self.total_tokens_limit = Some(limit);
569        self
570    }
571}
572
573/// Reports progress for long-running tool operations.
574pub trait ProgressReporter: WasmCompatSend + WasmCompatSync {
575    /// Report progress.
576    ///
577    /// # Arguments
578    /// * `progress` - Current progress value.
579    /// * `total` - Optional total value (for percentage calculation).
580    /// * `message` - Optional status message.
581    fn report(&self, progress: f64, total: Option<f64>, message: Option<&str>);
582}
583
584impl From<String> for SystemPrompt {
585    fn from(s: String) -> Self {
586        SystemPrompt::Text(s)
587    }
588}
589
590impl From<&str> for SystemPrompt {
591    fn from(s: &str) -> Self {
592        SystemPrompt::Text(s.to_string())
593    }
594}