agent_types/tool.rs
1//! Tool-related pure types: Content, ToolMetadata, ToolExposure, ActivationContext.
2
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6use crate::session::SessionId;
7
8/// Structured content returned by a tool, aligned with the MCP `content`
9/// array shape (no envelope, no orchestration/failure/truncation semantics).
10///
11/// Only `Text` is consumed by the first LLM adapter; `Image` is shape-reserved
12/// and the adapter reports "not supported" rather than silently dropping it.
13#[derive(Clone, Debug, Serialize, Deserialize)]
14#[serde(tag = "type", rename_all = "snake_case")]
15pub enum Content {
16 Text {
17 text: String,
18 },
19 /// Base64-encoded image payload.
20 Image {
21 data: String,
22 mime_type: String,
23 },
24}
25
26impl Content {
27 pub fn text(s: impl Into<String>) -> Self {
28 Content::Text { text: s.into() }
29 }
30
31 pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
32 Content::Image {
33 data: data.into(),
34 mime_type: mime_type.into(),
35 }
36 }
37}
38
39impl From<Content> for Vec<Content> {
40 fn from(c: Content) -> Self {
41 vec![c]
42 }
43}
44
45/// Join the textual portion of tool output into a single string for display
46/// and session history. Non-text variants (e.g. `Image`) are skipped.
47pub fn content_text(contents: &[Content]) -> String {
48 contents
49 .iter()
50 .filter_map(|c| match c {
51 Content::Text { text } => Some(text.as_str()),
52 Content::Image { .. } => None,
53 })
54 .collect::<Vec<_>>()
55 .join("\n")
56}
57
58/// Machine-readable metadata for a registered tool — origin, version, and
59/// runtime requirements in a stable shape consumers can inspect without
60/// parsing the LLM-facing definition JSON.
61#[derive(Clone, Debug, Serialize, Deserialize)]
62pub struct ToolMetadata {
63 /// Tool name (matches `Tool::name`).
64 pub name: String,
65 /// Human-readable description.
66 pub description: String,
67 /// Where this tool comes from: a crate name (e.g. `"phi-tools"`), a
68 /// framework identifier (`"agent-base"`, `"agent-works"`), or
69 /// `"custom"` for user-defined tools.
70 pub origin: String,
71 /// Crate / package version, or `"unknown"` when built outside a crate.
72 pub version: String,
73 /// Optional runtime requirements or capabilities this tool depends on.
74 pub requirements: Vec<String>,
75}
76
77/// Visibility level of a tool to the LLM model.
78///
79/// Controls whether a tool appears in the tool definitions sent to the model
80/// each turn. Defaults to `Direct` for backward compatibility.
81#[derive(Clone, Debug, PartialEq, Eq)]
82pub enum ToolExposure {
83 /// Always visible to the model. This is the default.
84 Direct,
85 /// Conditionally visible — the tool decides via `Tool::should_activate`.
86 Deferred,
87 /// Never visible to the model (internal/framework tools).
88 Hidden,
89}
90
91/// Context passed to `Tool::should_activate` for Deferred tools.
92///
93/// Built once per react-loop iteration; tools inspect it to decide
94/// whether they should be exposed to the model this turn.
95#[derive(Clone, Debug)]
96pub struct ActivationContext {
97 /// Current session ID.
98 pub session_id: SessionId,
99 /// Names of tools already activated (Direct + activated Deferred) this turn.
100 pub current_tools: Vec<String>,
101 /// Workspace / working directory path.
102 pub workspace: PathBuf,
103}