tokitai_core/lib.rs
1//! # Tokitai Core
2//!
3//! **Core types for Tokitai - Compile-time tool definitions with zero runtime dependencies**
4//!
5//! This crate provides the fundamental types and traits for the Tokitai AI tool integration system.
6//! All tool information is generated at compile time, ensuring zero runtime overhead and maximum
7//! type safety.
8//!
9//! ## 🎯 Key Features
10//!
11//! - **Zero Runtime Dependencies** - Core types have minimal dependencies
12//! - **`no_std` Support** - Works in embedded environments (when `serde` feature is disabled)
13//! - **Type Safety** - Compile-time tool definitions prevent runtime errors
14//! - **Serde Integration** - Optional serialization support via the `serde` feature
15//!
16//! ## Core Types
17//!
18//! - [`ToolDefinition`] - Tool definition containing name, description, and input schema
19//! - [`ToolParameter`] - Parameter definition for tools
20//! - [`ParamType`] - JSON Schema type enumeration
21//! - [`ToolError`] - Error type for tool invocation failures
22//! - [`ToolErrorKind`] - Classification of tool errors
23//! - [`ToolProvider`] - Trait for tool providers (auto-implemented by `#[tool]` macro)
24//!
25//! ## Usage Example
26//!
27//! ```rust
28//! use tokitai_core::ToolDefinition;
29//!
30//! // Create a tool definition
31//! let tool = ToolDefinition::new(
32//! "add",
33//! "Add two numbers together",
34//! r#"{"type":"object","properties":{"a":{"type":"integer"},"b":{"type":"integer"}},"required":["a","b"]}"#
35//! );
36//!
37//! assert_eq!(tool.name, "add");
38//! assert_eq!(tool.description, "Add two numbers together");
39//!
40//! // With serde feature enabled, convert to JSON
41//! #[cfg(feature = "serde")]
42//! {
43//! let json = tool.to_json().unwrap();
44//! assert!(json.contains("\"name\":\"add\""));
45//! }
46//! ```
47//!
48//! ## No-Std Support
49//!
50//! This crate supports `no_std` environments when the `serde` feature is disabled:
51//!
52//! ```toml
53//! [dependencies]
54//! tokitai-core = { version = "0.4.0", default-features = false }
55//! ```
56//!
57//! ## Type Mapping
58//!
59//! The [`ParamType`] enum maps Rust types to JSON Schema types:
60//!
61//! | Rust Type | JSON Schema Type | `ParamType` Variant |
62//! |-----------|------------------|---------------------|
63//! | `String`, `&str` | `string` | `ParamType::String` |
64//! | `i8`, `i16`, `i32`, `i64`, `u8`, `u16`, `u32`, `u64` | `integer` | `ParamType::Integer` |
65//! | `f32`, `f64` | `number` | `ParamType::Number` |
66//! | `bool` | `boolean` | `ParamType::Boolean` |
67//! | `Vec<T>` | `array` | `ParamType::Array` |
68//! | Custom structs | `object` | `ParamType::Object` |
69//!
70//! ```rust
71//! use tokitai_core::ParamType;
72//!
73//! assert_eq!(ParamType::from_rust_type("String"), Some(ParamType::String));
74//! assert_eq!(ParamType::from_rust_type("i32"), Some(ParamType::Integer));
75//! assert_eq!(ParamType::from_rust_type("f64"), Some(ParamType::Number));
76//! assert_eq!(ParamType::from_rust_type("bool"), Some(ParamType::Boolean));
77//! assert_eq!(ParamType::from_rust_type("Vec<i32>"), Some(ParamType::Array));
78//! ```
79//!
80//! ## Error Handling
81//!
82//! The [`ToolError`] type provides structured error handling for tool invocations:
83//!
84//! ```rust
85//! use tokitai_core::{ToolError, ToolErrorKind};
86//!
87//! // Create different error types
88//! let validation_err = ToolError::validation_error("Missing required parameter 'city'");
89//! assert_eq!(validation_err.kind, ToolErrorKind::ValidationError);
90//!
91//! let not_found_err = ToolError::not_found("Tool 'unknown_tool' not found");
92//! assert_eq!(not_found_err.kind, ToolErrorKind::NotFound);
93//!
94//! let internal_err = ToolError::internal_error("Connection timeout");
95//! assert_eq!(internal_err.kind, ToolErrorKind::InternalError);
96//! ```
97//!
98//! ## Tool Provider Trait
99//!
100//! The [`ToolProvider`] trait is automatically implemented by the `#[tool]` macro:
101//!
102//! ```rust
103//! use tokitai_core::ToolProvider;
104//!
105//! // After using #[tool] macro on your type:
106//! // struct Calculator;
107//! // #[tool] impl Calculator { ... }
108//!
109//! // Get all tool definitions
110//! // let tools = Calculator::tool_definitions();
111//!
112//! // Get tool count
113//! // let count = Calculator::tool_count();
114//!
115//! // Find a specific tool
116//! // let tool = Calculator::find_tool("add");
117//! ```
118//!
119//! ## JSON Schema Macro
120//!
121//! The `json_schema!` macro helps generate JSON Schema strings at compile time:
122//!
123//! ```rust,ignore
124//! use tokitai_core::json_schema;
125//!
126//! const SCHEMA: &str = json_schema!({
127//! "city": {
128//! type: String,
129//! description: "Name of the city",
130//! required: true,
131//! }
132//! });
133//! ```
134//!
135//! ## Features
136//!
137//! | Feature | Description |
138//! |---------|-------------|
139//! | `serde` (default) | Enable serde serialization and JSON support |
140//!
141//! ## Requirements
142//!
143//! - **Rust Version**: 1.70+
144//! - **Edition**: 2021
145//!
146//! ## License
147//!
148//! Licensed under either of:
149//!
150//! - Apache License, Version 2.0 ([LICENSE-APACHE](https://github.com/silverenternal/tokitai/blob/main/LICENSE))
151//! - MIT License ([LICENSE-MIT](https://github.com/silverenternal/tokitai/blob/main/LICENSE))
152//!
153//! at your option.
154//!
155//! ## Contributing
156//!
157//! Unless you explicitly state otherwise, any contribution intentionally submitted
158//! for inclusion in this crate by you, as defined in the Apache-2.0 license, shall be
159//! dual licensed as above, without any additional terms or conditions.
160//!
161//! ## See Also
162//!
163//! - [`tokitai`](https://crates.io/crates/tokitai) - Main crate with runtime support
164//! - [`tokitai-macros`](https://crates.io/crates/tokitai-macros) - Procedural macros
165
166#![cfg_attr(not(feature = "serde"), no_std)]
167#![allow(dead_code)]
168
169#[cfg(feature = "serde")]
170extern crate serde;
171
172#[cfg(feature = "serde")]
173extern crate alloc;
174
175#[cfg(feature = "serde")]
176pub use serde_types::*;
177
178#[cfg(feature = "serde")]
179pub use config::{ToolConfig, ToolConfigRegistry, GLOBAL_CONFIG_REGISTRY};
180
181/// # Tool Definition
182///
183/// Represents a tool that can be called by an AI system.
184///
185/// This struct is typically generated automatically by the `#[tool]` macro,
186/// so manual creation is rarely needed.
187///
188/// ## Fields
189///
190/// - `name` - The tool identifier used for AI recognition
191/// - `description` - Human-readable description helping AI understand the tool's purpose
192/// - `input_schema` - JSON Schema string for parameter validation
193///
194/// ## Example
195///
196/// ```rust
197/// use tokitai_core::ToolDefinition;
198///
199/// let tool = ToolDefinition::new("add", "Add two numbers", r#"{"type":"object"}"#);
200/// assert_eq!(tool.name, "add");
201/// ```
202#[derive(Debug, Clone)]
203#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
204pub struct ToolDefinition {
205 /// Tool name used for identification during AI calls
206 #[cfg(feature = "serde")]
207 pub name: alloc::string::String,
208 #[cfg(not(feature = "serde"))]
209 pub name: &'static str,
210 /// Tool description helping AI understand its purpose
211 #[cfg(feature = "serde")]
212 pub description: alloc::string::String,
213 #[cfg(not(feature = "serde"))]
214 pub description: &'static str,
215 /// Input parameter JSON Schema (compile-time generated string)
216 #[cfg(feature = "serde")]
217 pub input_schema: alloc::string::String,
218 #[cfg(not(feature = "serde"))]
219 pub input_schema: &'static str,
220 /// Tool version (optional)
221 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
222 #[cfg(feature = "serde")]
223 pub version: Option<alloc::string::String>,
224 #[cfg(not(feature = "serde"))]
225 pub version: Option<&'static str>,
226 /// Version since when the tool is deprecated (optional)
227 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
228 #[cfg(feature = "serde")]
229 pub deprecated_since: Option<alloc::string::String>,
230 #[cfg(not(feature = "serde"))]
231 pub deprecated_since: Option<&'static str>,
232 /// Version when the tool will be removed (optional)
233 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
234 #[cfg(feature = "serde")]
235 pub remove_in: Option<alloc::string::String>,
236 #[cfg(not(feature = "serde"))]
237 pub remove_in: Option<&'static str>,
238 /// Tool that replaces this deprecated tool (optional)
239 #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
240 #[cfg(feature = "serde")]
241 pub replaced_by: Option<alloc::string::String>,
242 #[cfg(not(feature = "serde"))]
243 pub replaced_by: Option<&'static str>,
244}
245
246/// Internal struct for compile-time tool definition data
247///
248/// This is used to store tool definitions as `&'static str` at compile time,
249/// then convert to `ToolDefinition` at runtime with zero allocation.
250#[doc(hidden)]
251pub struct ToolDefinitionConst {
252 pub name: &'static str,
253 pub description: &'static str,
254 pub input_schema: &'static str,
255}
256
257impl ToolDefinition {
258 /// Create a new tool definition from compile-time constants
259 ///
260 /// This is optimized for compile-time generated code where all strings
261 /// are `'static`. The conversion to `ToolDefinition` happens at runtime
262 /// but with zero allocation since we're just copying references.
263 ///
264 /// # Parameters
265 ///
266 /// - `name` - Tool name
267 /// - `description` - Tool description
268 /// - `input_schema` - JSON Schema string
269 ///
270 /// # Example
271 ///
272 /// ```rust
273 /// use tokitai_core::{ToolDefinition, ToolDefinitionConst};
274 ///
275 /// const TOOL_DATA: ToolDefinitionConst = ToolDefinitionConst {
276 /// name: "get_weather",
277 /// description: "Get weather information for a specified city",
278 /// input_schema: r#"{"type":"object"}"#,
279 /// };
280 ///
281 /// let tool = ToolDefinition::from_const(TOOL_DATA);
282 /// ```
283 #[inline(always)]
284 pub fn from_const(data: ToolDefinitionConst) -> Self {
285 Self {
286 #[cfg(feature = "serde")]
287 name: data.name.into(),
288 #[cfg(not(feature = "serde"))]
289 name: data.name,
290 #[cfg(feature = "serde")]
291 description: data.description.into(),
292 #[cfg(not(feature = "serde"))]
293 description: data.description,
294 #[cfg(feature = "serde")]
295 input_schema: data.input_schema.into(),
296 #[cfg(not(feature = "serde"))]
297 input_schema: data.input_schema,
298 version: None,
299 deprecated_since: None,
300 remove_in: None,
301 replaced_by: None,
302 }
303 }
304
305 /// Create a new tool definition (runtime version)
306 ///
307 /// # Parameters
308 ///
309 /// - `name` - Tool name
310 /// - `description` - Tool description
311 /// - `input_schema` - JSON Schema string
312 ///
313 /// # Example
314 ///
315 /// ```rust
316 /// use tokitai_core::ToolDefinition;
317 ///
318 /// let tool = ToolDefinition::new(
319 /// "get_weather",
320 /// "Get weather information for a specified city",
321 /// r#"{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}"#
322 /// );
323 /// ```
324 #[cfg(feature = "serde")]
325 pub fn new(
326 name: impl Into<alloc::string::String>,
327 description: impl Into<alloc::string::String>,
328 input_schema: impl Into<alloc::string::String>,
329 ) -> Self {
330 Self {
331 name: name.into(),
332 description: description.into(),
333 input_schema: input_schema.into(),
334 version: None,
335 deprecated_since: None,
336 remove_in: None,
337 replaced_by: None,
338 }
339 }
340
341 /// Create a new tool definition (no_std version)
342 #[cfg(not(feature = "serde"))]
343 pub fn new(
344 name: &'static str,
345 description: &'static str,
346 input_schema: &'static str,
347 ) -> Self {
348 Self {
349 name,
350 description,
351 input_schema,
352 version: None,
353 deprecated_since: None,
354 remove_in: None,
355 replaced_by: None,
356 }
357 }
358
359 /// Set the tool version
360 #[cfg(feature = "serde")]
361 pub fn with_version(mut self, version: impl Into<alloc::string::String>) -> Self {
362 self.version = Some(version.into());
363 self
364 }
365
366 /// Set the tool version (no_std version)
367 #[cfg(not(feature = "serde"))]
368 pub fn with_version(mut self, version: &'static str) -> Self {
369 self.version = Some(version);
370 self
371 }
372
373 /// Set deprecation information
374 #[cfg(feature = "serde")]
375 pub fn with_deprecated(
376 mut self,
377 deprecated_since: impl Into<alloc::string::String>,
378 remove_in: impl Into<alloc::string::String>,
379 replaced_by: impl Into<alloc::string::String>,
380 ) -> Self {
381 self.deprecated_since = Some(deprecated_since.into());
382 self.remove_in = Some(remove_in.into());
383 self.replaced_by = Some(replaced_by.into());
384 self
385 }
386
387 /// Set deprecation information (no_std version)
388 #[cfg(not(feature = "serde"))]
389 pub fn with_deprecated(
390 mut self,
391 deprecated_since: &'static str,
392 remove_in: &'static str,
393 replaced_by: &'static str,
394 ) -> Self {
395 self.deprecated_since = Some(deprecated_since);
396 self.remove_in = Some(remove_in);
397 self.replaced_by = Some(replaced_by);
398 self
399 }
400
401 /// Convert to JSON string (requires `serde` feature)
402 #[cfg(feature = "serde")]
403 pub fn to_json(&self) -> Result<String, serde_json::Error> {
404 serde_json::to_string(self)
405 }
406
407 /// Convert to JSON Value (requires `serde` feature)
408 #[cfg(feature = "serde")]
409 pub fn to_value(&self) -> Result<serde_json::Value, serde_json::Error> {
410 serde_json::to_value(self)
411 }
412
413 /// Get input schema as pretty-printed JSON string (requires `serde` feature)
414 #[cfg(feature = "serde")]
415 pub fn input_schema_pretty(&self) -> Result<String, serde_json::Error> {
416 let value: serde_json::Value = serde_json::from_str(&self.input_schema)?;
417 serde_json::to_string_pretty(&value)
418 }
419
420 /// Get input schema as JSON Value (requires `serde` feature)
421 #[cfg(feature = "serde")]
422 pub fn input_schema_value(&self) -> Result<serde_json::Value, serde_json::Error> {
423 serde_json::from_str(&self.input_schema)
424 }
425
426 /// Apply configurations to this tool definition.
427 ///
428 /// This method is used by the configuration system to apply runtime
429 /// configurations to tool definitions generated at compile time.
430 ///
431 /// # Parameters
432 ///
433 /// - `configs` - Slice of configuration items to apply
434 ///
435 /// # Example
436 ///
437 /// ```rust
438 /// use tokitai_core::{ToolDefinition, ToolConfig};
439 ///
440 /// let mut tool = ToolDefinition::new("test", "Original description", "{}");
441 /// tool.apply_configs(&[
442 /// ToolConfig::Desc("Overridden description".to_string()),
443 /// ]);
444 /// assert_eq!(tool.description, "Overridden description");
445 /// ```
446 #[cfg(feature = "serde")]
447 pub fn apply_configs(&mut self, configs: &[ToolConfig]) {
448 for config in configs {
449 match config {
450 ToolConfig::Desc(desc) => {
451 self.description = desc.clone();
452 }
453 ToolConfig::Tags(tags) => {
454 // Add tags to the schema
455 if let Ok(mut schema) =
456 serde_json::from_str::<serde_json::Value>(&self.input_schema)
457 {
458 if let Some(obj) = schema.as_object_mut() {
459 obj.insert("tags".to_string(), serde_json::json!(tags));
460 }
461 self.input_schema = schema.to_string();
462 }
463 }
464 ToolConfig::ParamDesc { name, desc } => {
465 self.apply_param_desc(name, desc);
466 }
467 ToolConfig::ParamExample { name, example } => {
468 self.apply_param_example(name, example);
469 }
470 ToolConfig::ParamDefault { name, default } => {
471 self.apply_param_default(name, default);
472 }
473 ToolConfig::ParamRequired { name, required } => {
474 self.apply_param_required(name, *required);
475 }
476 ToolConfig::ParamMin { name, min } => {
477 self.apply_param_constraint(name, "minimum", serde_json::json!(min));
478 }
479 ToolConfig::ParamMax { name, max } => {
480 self.apply_param_constraint(name, "maximum", serde_json::json!(max));
481 }
482 ToolConfig::ParamMinLength { name, min_length } => {
483 self.apply_param_constraint(name, "minLength", serde_json::json!(min_length));
484 }
485 ToolConfig::ParamMaxLength { name, max_length } => {
486 self.apply_param_constraint(name, "maxLength", serde_json::json!(max_length));
487 }
488 ToolConfig::ParamPattern { name, pattern } => {
489 self.apply_param_constraint(name, "pattern", serde_json::json!(pattern));
490 }
491 ToolConfig::ParamMinItems { name, min_items } => {
492 self.apply_param_constraint(name, "minItems", serde_json::json!(min_items));
493 }
494 ToolConfig::ParamMaxItems { name, max_items } => {
495 self.apply_param_constraint(name, "maxItems", serde_json::json!(max_items));
496 }
497 ToolConfig::ParamMultipleOf { name, multiple_of } => {
498 self.apply_param_constraint(name, "multipleOf", serde_json::json!(multiple_of));
499 }
500 }
501 }
502 }
503
504 /// Apply parameter description to the schema.
505 #[cfg(feature = "serde")]
506 fn apply_param_desc(&mut self, name: &str, desc: &str) {
507 if let Ok(mut schema) = serde_json::from_str::<serde_json::Value>(&self.input_schema) {
508 if let Some(obj) = schema.as_object_mut() {
509 if let Some(props) = obj.get_mut("properties").and_then(|v| v.as_object_mut()) {
510 if let Some(param) = props.get_mut(name).and_then(|v| v.as_object_mut()) {
511 param.insert("description".to_string(), serde_json::json!(desc));
512 }
513 }
514 }
515 self.input_schema = schema.to_string();
516 }
517 }
518
519 /// Apply parameter example to the schema.
520 #[cfg(feature = "serde")]
521 fn apply_param_example(&mut self, name: &str, example: &serde_json::Value) {
522 if let Ok(mut schema) = serde_json::from_str::<serde_json::Value>(&self.input_schema) {
523 if let Some(obj) = schema.as_object_mut() {
524 if let Some(props) = obj.get_mut("properties").and_then(|v| v.as_object_mut()) {
525 if let Some(param) = props.get_mut(name).and_then(|v| v.as_object_mut()) {
526 param.insert("example".to_string(), example.clone());
527 }
528 }
529 }
530 self.input_schema = schema.to_string();
531 }
532 }
533
534 /// Apply parameter default to the schema.
535 #[cfg(feature = "serde")]
536 fn apply_param_default(&mut self, name: &str, default: &serde_json::Value) {
537 if let Ok(mut schema) = serde_json::from_str::<serde_json::Value>(&self.input_schema) {
538 if let Some(obj) = schema.as_object_mut() {
539 if let Some(props) = obj.get_mut("properties").and_then(|v| v.as_object_mut()) {
540 if let Some(param) = props.get_mut(name).and_then(|v| v.as_object_mut()) {
541 param.insert("default".to_string(), default.clone());
542 }
543 }
544 }
545 self.input_schema = schema.to_string();
546 }
547 }
548
549 /// Apply parameter required flag to the schema.
550 #[cfg(feature = "serde")]
551 fn apply_param_required(&mut self, name: &str, required: bool) {
552 if let Ok(mut schema) = serde_json::from_str::<serde_json::Value>(&self.input_schema) {
553 if let Some(obj) = schema.as_object_mut() {
554 // Update required array
555 let required_arr = obj
556 .entry("required".to_string())
557 .or_insert_with(|| serde_json::json!([]))
558 .as_array_mut();
559
560 if let Some(req_arr) = required_arr {
561 let name_json = serde_json::json!(name);
562 if required && !req_arr.contains(&name_json) {
563 req_arr.push(name_json);
564 } else if !required {
565 req_arr.retain(|v| v != &name_json);
566 }
567 }
568
569 // Also update parameter schema
570 if let Some(props) = obj.get_mut("properties").and_then(|v| v.as_object_mut()) {
571 if let Some(param) = props.get_mut(name).and_then(|v| v.as_object_mut()) {
572 // Note: individual param "required" is not standard JSON Schema
573 // but we can add it for documentation purposes
574 param.insert("required".to_string(), serde_json::json!(required));
575 }
576 }
577 }
578 self.input_schema = schema.to_string();
579 }
580 }
581
582 /// Apply a constraint to a parameter in the schema.
583 #[cfg(feature = "serde")]
584 fn apply_param_constraint(
585 &mut self,
586 name: &str,
587 constraint_key: &str,
588 value: serde_json::Value,
589 ) {
590 if let Ok(mut schema) = serde_json::from_str::<serde_json::Value>(&self.input_schema) {
591 if let Some(obj) = schema.as_object_mut() {
592 if let Some(props) = obj.get_mut("properties").and_then(|v| v.as_object_mut()) {
593 if let Some(param) = props.get_mut(name).and_then(|v| v.as_object_mut()) {
594 param.insert(constraint_key.to_string(), value);
595 }
596 }
597 }
598 self.input_schema = schema.to_string();
599 }
600 }
601}
602
603impl core::fmt::Display for ToolDefinition {
604 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
605 write!(f, "{}: {}", self.name, self.description)
606 }
607}
608
609/// # Parameter Type
610///
611/// Represents JSON Schema types for tool parameters.
612///
613/// ## Example
614///
615/// ```rust
616/// use tokitai_core::ParamType;
617///
618/// assert_eq!(ParamType::from_rust_type("String"), Some(ParamType::String));
619/// assert_eq!(ParamType::from_rust_type("i32"), Some(ParamType::Integer));
620/// assert_eq!(ParamType::Integer.as_str(), "integer");
621/// ```
622#[derive(Debug, Clone, Copy, PartialEq, Eq)]
623#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
624#[repr(u8)]
625pub enum ParamType {
626 /// String type
627 String = 0,
628 /// Integer type
629 Integer = 1,
630 /// Number type (floating point)
631 Number = 2,
632 /// Boolean type
633 Boolean = 3,
634 /// Array type
635 Array = 4,
636 /// Object type
637 Object = 5,
638}
639
640impl ParamType {
641 /// Get the JSON Schema type string
642 ///
643 /// # Example
644 ///
645 /// ```rust
646 /// use tokitai_core::ParamType;
647 ///
648 /// assert_eq!(ParamType::String.as_str(), "string");
649 /// assert_eq!(ParamType::Integer.as_str(), "integer");
650 /// ```
651 pub fn as_str(&self) -> &'static str {
652 match self {
653 ParamType::String => "string",
654 ParamType::Integer => "integer",
655 ParamType::Number => "number",
656 ParamType::Boolean => "boolean",
657 ParamType::Array => "array",
658 ParamType::Object => "object",
659 }
660 }
661
662 /// Infer parameter type from Rust type name
663 ///
664 /// # Parameters
665 ///
666 /// - `type_name` - Rust type name (e.g., `"String"`, `"i32"`, `"Vec<i32>"`)
667 ///
668 /// # Example
669 ///
670 /// ```rust
671 /// use tokitai_core::ParamType;
672 ///
673 /// assert_eq!(ParamType::from_rust_type("String"), Some(ParamType::String));
674 /// assert_eq!(ParamType::from_rust_type("i32"), Some(ParamType::Integer));
675 /// assert_eq!(ParamType::from_rust_type("f64"), Some(ParamType::Number));
676 /// assert_eq!(ParamType::from_rust_type("bool"), Some(ParamType::Boolean));
677 /// assert_eq!(ParamType::from_rust_type("Vec<i32>"), Some(ParamType::Array));
678 /// ```
679 pub fn from_rust_type(type_name: &str) -> Option<Self> {
680 match type_name {
681 "String" | "str" => Some(ParamType::String),
682 "i8" | "i16" | "i32" | "i64" | "i128" | "u8" | "u16" | "u32" | "u64" | "u128"
683 | "usize" | "isize" => Some(ParamType::Integer),
684 "f32" | "f64" => Some(ParamType::Number),
685 "bool" => Some(ParamType::Boolean),
686 _ => {
687 if type_name.starts_with("Vec<") {
688 Some(ParamType::Array)
689 } else if type_name.starts_with("Option<") {
690 None
691 } else {
692 Some(ParamType::Object)
693 }
694 }
695 }
696 }
697}
698
699/// # Tool Parameter
700///
701/// Represents a single parameter definition for a tool.
702///
703/// ## Example
704///
705/// ```rust
706/// use tokitai_core::{ToolParameter, ParamType};
707///
708/// let param = ToolParameter::new(
709/// "city",
710/// ParamType::String,
711/// "Name of the city",
712/// true, // Required parameter
713/// );
714/// ```
715#[derive(Debug, Clone)]
716#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
717pub struct ToolParameter {
718 /// Parameter name
719 pub name: &'static str,
720 /// Parameter type
721 #[cfg_attr(feature = "serde", serde(rename = "type"))]
722 pub param_type: ParamType,
723 /// Parameter description
724 pub description: &'static str,
725 /// Whether the parameter is required
726 pub required: bool,
727}
728
729impl ToolParameter {
730 /// Create a new parameter definition
731 ///
732 /// # Parameters
733 ///
734 /// - `name` - Parameter name
735 /// - `param_type` - Parameter type
736 /// - `description` - Parameter description
737 /// - `required` - Whether the parameter is required
738 ///
739 /// # Example
740 ///
741 /// ```rust
742 /// use tokitai_core::{ToolParameter, ParamType};
743 ///
744 /// let param = ToolParameter::new("limit", ParamType::Integer, "Number of results to return", false);
745 /// ```
746 pub fn new(
747 name: &'static str,
748 param_type: ParamType,
749 description: &'static str,
750 required: bool,
751 ) -> Self {
752 Self {
753 name,
754 param_type,
755 description,
756 required,
757 }
758 }
759}
760
761/// # Tool Error
762///
763/// Represents errors that can occur during tool invocation.
764///
765/// ## Example
766///
767/// ```rust
768/// use tokitai_core::{ToolError, ToolErrorKind};
769///
770/// let error = ToolError::validation_error("Missing required parameter 'city'");
771/// assert_eq!(error.kind, ToolErrorKind::ValidationError);
772/// ```
773#[derive(Debug, Clone)]
774#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
775pub struct ToolError {
776 /// Error type classification
777 pub kind: ToolErrorKind,
778 /// Error message
779 #[cfg(feature = "serde")]
780 pub message: crate::serde_types::String,
781 #[cfg(not(feature = "serde"))]
782 pub message: &'static str,
783}
784
785#[cfg(feature = "serde")]
786impl std::error::Error for ToolError {}
787
788#[cfg(feature = "serde")]
789impl std::fmt::Display for ToolError {
790 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
791 write!(f, "ToolError: {:?} - {}", self.kind, self.message)
792 }
793}
794
795#[cfg(not(feature = "serde"))]
796impl ToolError {
797 /// Create a new error
798 pub fn new(kind: ToolErrorKind, message: &'static str) -> Self {
799 Self { kind, message }
800 }
801
802 /// Create a validation error
803 pub fn validation_error(message: &'static str) -> Self {
804 Self {
805 kind: ToolErrorKind::ValidationError,
806 message,
807 }
808 }
809
810 /// Create a not found error
811 pub fn not_found(message: &'static str) -> Self {
812 Self {
813 kind: ToolErrorKind::NotFound,
814 message,
815 }
816 }
817
818 /// Create an internal error
819 pub fn internal_error(message: &'static str) -> Self {
820 Self {
821 kind: ToolErrorKind::InternalError,
822 message,
823 }
824 }
825}
826
827#[cfg(feature = "serde")]
828impl ToolError {
829 /// Create a new error
830 pub fn new(kind: ToolErrorKind, message: impl Into<crate::serde_types::String>) -> Self {
831 Self {
832 kind,
833 message: message.into(),
834 }
835 }
836
837 /// Create a validation error
838 pub fn validation_error(message: impl Into<crate::serde_types::String>) -> Self {
839 Self {
840 kind: ToolErrorKind::ValidationError,
841 message: message.into(),
842 }
843 }
844
845 /// Create a not found error
846 pub fn not_found(message: impl Into<crate::serde_types::String>) -> Self {
847 Self {
848 kind: ToolErrorKind::NotFound,
849 message: message.into(),
850 }
851 }
852
853 /// Create an internal error
854 pub fn internal_error(message: impl Into<crate::serde_types::String>) -> Self {
855 Self {
856 kind: ToolErrorKind::InternalError,
857 message: message.into(),
858 }
859 }
860}
861
862/// # Error Kind
863///
864/// Classification of tool errors for structured error handling.
865#[derive(Debug, Clone, Copy, PartialEq, Eq)]
866#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
867#[repr(u8)]
868pub enum ToolErrorKind {
869 /// Validation error - parameter validation failed
870 ValidationError = 0,
871 /// Not found - requested tool does not exist
872 NotFound = 1,
873 /// Internal error - tool execution failed
874 InternalError = 2,
875 /// Type error - parameter type mismatch
876 TypeError = 3,
877}
878
879/// # Compile-time Tool Registry Trait
880///
881/// Automatically implemented by the `#[tool]` macro, providing tool definitions
882/// and invocation interface.
883///
884/// ## Example
885///
886/// ```rust
887/// use tokitai_core::ToolProvider;
888///
889/// // After using #[tool] macro on your type:
890/// // struct Calculator;
891/// // #[tool] impl Calculator { ... }
892///
893/// // Get all tool definitions
894/// // let tools = Calculator::tool_definitions();
895///
896/// // Get tool count
897/// // let count = Calculator::tool_count();
898///
899/// // Find a specific tool
900/// // let tool = Calculator::find_tool("add");
901/// ```
902pub trait ToolProvider {
903 /// Get all tool definitions
904 fn tool_definitions() -> &'static [ToolDefinition];
905
906 /// Get the number of tools
907 fn tool_count() -> usize {
908 Self::tool_definitions().len()
909 }
910
911 /// Find a tool definition by name
912 fn find_tool(name: &str) -> Option<&'static ToolDefinition> {
913 Self::tool_definitions().iter().find(|t| t.name == name)
914 }
915}
916
917/// # Tool Caller Trait
918///
919/// Provides runtime tool invocation capability.
920/// Automatically implemented by the `#[tool]` macro for all tool types.
921///
922/// ## Example
923///
924/// ```rust,ignore
925/// use tokitai_core::{ToolProvider, ToolCaller};
926/// use serde_json::json;
927///
928/// // After using #[tool] macro on your type:
929/// // struct Calculator;
930/// // #[tool] impl Calculator { ... }
931///
932/// let calc = Calculator;
933/// let result = calc.call_tool("add", &json!({"a": 10, "b": 20})).unwrap();
934/// assert_eq!(result, json!(30));
935/// ```
936#[cfg(feature = "serde")]
937pub trait ToolCaller {
938 /// Call a tool by name with JSON arguments
939 ///
940 /// # Parameters
941 ///
942 /// - `name` - Tool name to call
943 /// - `args` - JSON arguments for the tool
944 ///
945 /// # Returns
946 ///
947 /// - `Ok(Value)` - Tool execution result
948 /// - `Err(ToolError)` - Tool execution failed
949 fn call_tool(
950 &self,
951 name: &str,
952 args: &crate::serde_types::Value,
953 ) -> Result<crate::serde_types::Value, ToolError>;
954}
955
956/// # From Json Value Trait (P0 优化)
957///
958/// Trait for parsing JSON values into Rust types.
959/// This trait is implemented for common types and used by the `#[tool]` macro
960/// to parse tool parameters from JSON arguments.
961///
962/// ## Design Goals
963///
964/// - **Zero code duplication**: Implemented once per type, not per tool method
965/// - **Compile-time monomorphization**: Generic over types for optimal performance
966/// - **Clear error messages**: Type-specific error handling
967///
968/// ## Example
969///
970/// ```rust
971/// use tokitai_core::{FromJsonValue, ToolError};
972/// use serde_json::json;
973///
974/// let args = json!({"count": 42, "name": "test"});
975/// let count = i64::from_json_value(&args, "count").unwrap();
976/// let name = String::from_json_value(&args, "name").unwrap();
977/// assert_eq!(count, 42);
978/// assert_eq!(name, "test");
979/// ```
980#[cfg(feature = "serde")]
981pub trait FromJsonValue: Sized {
982 /// Parse a value from JSON arguments
983 ///
984 /// # Parameters
985 ///
986 /// - `args` - JSON arguments object
987 /// - `key` - Parameter name to extract
988 ///
989 /// # Returns
990 ///
991 /// - `Ok(Self)` - Successfully parsed value
992 /// - `Err(ToolError)` - Parsing failed (missing key or type mismatch)
993 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError>;
994
995 /// Parse an optional value from JSON arguments
996 ///
997 /// # Parameters
998 ///
999 /// - `args` - JSON arguments object
1000 /// - `key` - Parameter name to extract
1001 ///
1002 /// # Returns
1003 ///
1004 /// - `Some(Self)` - Successfully parsed value
1005 /// - `None` - Key does not exist or type mismatch
1006 fn from_json_value_opt(args: &crate::serde_types::Value, key: &str) -> Option<Self> {
1007 Self::from_json_value(args, key).ok()
1008 }
1009}
1010
1011// ============== 基本类型实现 ==============
1012
1013#[cfg(feature = "serde")]
1014impl FromJsonValue for i64 {
1015 #[inline(always)]
1016 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1017 args.get(key)
1018 .ok_or_else(|| ToolError::validation_error(format!("缺少必需参数 '{}'", key)))?
1019 .as_i64()
1020 .ok_or_else(|| ToolError::validation_error(format!("参数 '{}' 类型错误,期望 integer", key)))
1021 }
1022}
1023
1024#[cfg(feature = "serde")]
1025impl FromJsonValue for i32 {
1026 #[inline(always)]
1027 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1028 args.get(key)
1029 .ok_or_else(|| ToolError::validation_error(format!("缺少必需参数 '{}'", key)))?
1030 .as_i64()
1031 .map(|v| v as i32)
1032 .ok_or_else(|| ToolError::validation_error(format!("参数 '{}' 类型错误,期望 integer", key)))
1033 }
1034}
1035
1036#[cfg(feature = "serde")]
1037impl FromJsonValue for u64 {
1038 #[inline(always)]
1039 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1040 args.get(key)
1041 .ok_or_else(|| ToolError::validation_error(format!("缺少必需参数 '{}'", key)))?
1042 .as_u64()
1043 .ok_or_else(|| ToolError::validation_error(format!("参数 '{}' 类型错误,期望 unsigned integer", key)))
1044 }
1045}
1046
1047#[cfg(feature = "serde")]
1048impl FromJsonValue for u32 {
1049 #[inline(always)]
1050 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1051 args.get(key)
1052 .ok_or_else(|| ToolError::validation_error(format!("缺少必需参数 '{}'", key)))?
1053 .as_u64()
1054 .map(|v| v as u32)
1055 .ok_or_else(|| ToolError::validation_error(format!("参数 '{}' 类型错误,期望 unsigned integer", key)))
1056 }
1057}
1058
1059#[cfg(feature = "serde")]
1060impl FromJsonValue for f64 {
1061 #[inline(always)]
1062 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1063 args.get(key)
1064 .ok_or_else(|| ToolError::validation_error(format!("缺少必需参数 '{}'", key)))?
1065 .as_f64()
1066 .ok_or_else(|| ToolError::validation_error(format!("参数 '{}' 类型错误,期望 number", key)))
1067 }
1068}
1069
1070#[cfg(feature = "serde")]
1071impl FromJsonValue for f32 {
1072 #[inline(always)]
1073 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1074 args.get(key)
1075 .ok_or_else(|| ToolError::validation_error(format!("缺少必需参数 '{}'", key)))?
1076 .as_f64()
1077 .map(|v| v as f32)
1078 .ok_or_else(|| ToolError::validation_error(format!("参数 '{}' 类型错误,期望 number", key)))
1079 }
1080}
1081
1082#[cfg(feature = "serde")]
1083impl FromJsonValue for bool {
1084 #[inline(always)]
1085 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1086 args.get(key)
1087 .ok_or_else(|| ToolError::validation_error(format!("缺少必需参数 '{}'", key)))?
1088 .as_bool()
1089 .ok_or_else(|| ToolError::validation_error(format!("参数 '{}' 类型错误,期望 boolean", key)))
1090 }
1091}
1092
1093#[cfg(feature = "serde")]
1094impl FromJsonValue for String {
1095 #[inline(always)]
1096 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1097 args.get(key)
1098 .ok_or_else(|| ToolError::validation_error(format!("缺少必需参数 '{}'", key)))?
1099 .as_str()
1100 .map(|s| s.to_string())
1101 .ok_or_else(|| ToolError::validation_error(format!("参数 '{}' 类型错误,期望 string", key)))
1102 }
1103}
1104
1105// ============== &str 零拷贝支持 ==============
1106// 特殊处理:需要生命周期,使用单独函数
1107#[cfg(feature = "serde")]
1108#[inline(always)]
1109pub fn from_json_value_str<'a>(
1110 args: &'a crate::serde_types::Value,
1111 key: &str,
1112) -> Result<&'a str, ToolError> {
1113 args.get(key)
1114 .ok_or_else(|| ToolError::validation_error(format!("Missing required parameter '{}'", key)))?
1115 .as_str()
1116 .ok_or_else(|| ToolError::validation_error(format!("Parameter '{}' type error, expected string", key)))
1117}
1118
1119// ============== Option 实现 ==============
1120
1121#[cfg(feature = "serde")]
1122impl<T: FromJsonValue> FromJsonValue for Option<T> {
1123 #[inline(always)]
1124 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1125 Ok(T::from_json_value_opt(args, key))
1126 }
1127}
1128
1129// ============== Vec 实现 ==============
1130
1131#[cfg(feature = "serde")]
1132impl<T: serde::de::DeserializeOwned> FromJsonValue for Vec<T> {
1133 #[inline(always)]
1134 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1135 let value = args.get(key)
1136 .ok_or_else(|| ToolError::validation_error(format!("缺少必需参数 '{}'", key)))?;
1137 serde_json::from_value(value.clone())
1138 .map_err(|e| ToolError::validation_error(format!("参数 '{}' 类型错误:{}", key, e)))
1139 }
1140}
1141
1142// ============== 辅助函数:解析任意 DeserializeOwned 类型 ==============
1143// 对于不支持的自定义类型,用户可以在方法内部手动反序列化
1144
1145#[cfg(feature = "serde")]
1146#[inline(always)]
1147pub fn from_json_value_generic<T: serde::de::DeserializeOwned>(
1148 args: &crate::serde_types::Value,
1149 key: &str,
1150) -> Result<T, ToolError> {
1151 let value = args.get(key)
1152 .ok_or_else(|| ToolError::validation_error(format!("Missing required parameter '{}'", key)))?;
1153 serde_json::from_value(value.clone())
1154 .map_err(|e| ToolError::validation_error(format!("Parameter '{}' type error: {}", key, e)))
1155}
1156
1157// ============== HashMap 实现 ==============
1158#[cfg(feature = "serde")]
1159impl<V: serde::de::DeserializeOwned> FromJsonValue for std::collections::HashMap<String, V> {
1160 #[inline(always)]
1161 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1162 let value = args.get(key)
1163 .ok_or_else(|| ToolError::validation_error(format!("Missing required parameter '{}'", key)))?;
1164 serde_json::from_value(value.clone())
1165 .map_err(|e| ToolError::validation_error(format!("Parameter '{}' type error: {}", key, e)))
1166 }
1167}
1168
1169// ============== BTreeMap 实现 ==============
1170#[cfg(feature = "serde")]
1171impl<V: serde::de::DeserializeOwned> FromJsonValue for std::collections::BTreeMap<String, V> {
1172 #[inline(always)]
1173 fn from_json_value(args: &crate::serde_types::Value, key: &str) -> Result<Self, ToolError> {
1174 let value = args.get(key)
1175 .ok_or_else(|| ToolError::validation_error(format!("Missing required parameter '{}'", key)))?;
1176 serde_json::from_value(value.clone())
1177 .map_err(|e| ToolError::validation_error(format!("Parameter '{}' type error: {}", key, e)))
1178 }
1179}
1180
1181/// Tool configuration types for runtime customization.
1182#[cfg(feature = "serde")]
1183pub mod config;
1184
1185#[cfg(feature = "serde")]
1186pub mod serde_types {
1187 //! Serde type aliases
1188 //!
1189 //! This module is available when the `serde` feature is enabled.
1190
1191 pub use alloc::string::String;
1192 pub use serde_json::Value;
1193}
1194
1195/// # JSON Schema Macro (Compile-time)
1196///
1197/// Helper macro for generating JSON Schema strings at compile time,
1198/// avoiding runtime overhead.
1199///
1200/// ## Example
1201///
1202/// ```rust,ignore
1203/// // Note: This macro generates strings at compile time, syntax is special
1204/// use tokitai_core::json_schema;
1205///
1206/// const SCHEMA: &str = json_schema!({
1207/// "city": {
1208/// type: String,
1209/// description: "Name of the city",
1210/// required: true,
1211/// }
1212/// });
1213/// ```
1214#[macro_export]
1215macro_rules! json_schema {
1216 (
1217 {
1218 $($param_name:literal: {
1219 type: $param_type:ident,
1220 description: $description:literal,
1221 required: $required:literal $(,)?
1222 }),*
1223 $(,)?
1224 }
1225 ) => {{
1226 const SCHEMA: &str = concat!(
1227 "{\"type\":\"object\",\"properties\":{",
1228 $({
1229 concat!(
1230 "\"", $param_name, "\":",
1231 "{\"type\":\"", $crate::ParamType::$param_type.as_str(), "\",\"description\":\"", $description, "\"}"
1232 )
1233 },)*
1234 "},\"required\":[",
1235 $({
1236 if $required { concat!("\"", $param_name, "\"") } else { "" }
1237 },)*
1238 "]}"
1239 );
1240 SCHEMA
1241 }};
1242}
1243
1244#[cfg(test)]
1245mod tests {
1246 use super::*;
1247
1248 #[test]
1249 fn test_param_type_from_rust_type() {
1250 assert_eq!(ParamType::from_rust_type("String"), Some(ParamType::String));
1251 assert_eq!(ParamType::from_rust_type("i32"), Some(ParamType::Integer));
1252 assert_eq!(ParamType::from_rust_type("f64"), Some(ParamType::Number));
1253 assert_eq!(ParamType::from_rust_type("bool"), Some(ParamType::Boolean));
1254 assert_eq!(
1255 ParamType::from_rust_type("Vec<i32>"),
1256 Some(ParamType::Array)
1257 );
1258 }
1259
1260 #[test]
1261 fn test_tool_definition_const() {
1262 let tool = ToolDefinition::new("test", "A test tool", "{}");
1263 assert_eq!(tool.name, "test");
1264 assert_eq!(tool.description, "A test tool");
1265 }
1266
1267 #[cfg(feature = "serde")]
1268 #[test]
1269 fn test_tool_definition_to_json() {
1270 let tool = ToolDefinition::new("test", "A test tool", r#"{"type":"object"}"#);
1271 let json = tool.to_json().unwrap();
1272 assert!(json.contains(r#""name":"test""#));
1273 }
1274}