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}