Skip to main content

golem_rust/tool/
mod.rs

1// Copyright 2024-2026 Golem Cloud
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#[cfg(not(feature = "export_golem_agentic"))]
16pub use crate::bindings::golem::agent::common::Principal;
17#[cfg(not(feature = "export_golem_agentic"))]
18pub use crate::bindings::golem::tool::streams::ToolOutputWriter;
19#[cfg(feature = "export_golem_agentic")]
20pub use crate::golem_agentic::golem::agent::common::Principal;
21#[cfg(feature = "export_golem_agentic")]
22pub use crate::golem_agentic::golem::tool::streams::ToolOutputWriter;
23pub use crate::schema::tool::Tool;
24pub use crate::schema::tool::{
25    MonomorphicToolMiddlewareScope, ProcessExitStatus, ToolMiddleware, ToolMiddlewareScope,
26};
27pub use tool_middleware::{
28    InputStream, InvocationResult, RawCustomToolError, ToolInvokeError, TypedUnderlyingInvocation,
29    UnderlyingInvocation, UnderlyingTool, decode_result_empty, decode_result_outputs_only,
30    decode_result_stderr_only, decode_result_stdout_only, decode_result_value,
31    decode_result_with_outputs, decode_result_with_stderr, decode_result_with_stdout,
32};
33
34#[doc(hidden)]
35#[derive(crate::IntoSchema, crate::FromSchema)]
36pub struct EmptyMiddlewareParameters {}
37
38#[doc(hidden)]
39pub fn decode_middleware_parameters<T>(
40    parameters: crate::TypedSchemaValue,
41) -> Result<T, ToolInvokeError<RawCustomToolError>>
42where
43    T: crate::IntoSchema + crate::FromSchema,
44{
45    let expected = crate::schema::try_into_schema_graph::<T>()
46        .map_err(|error| ToolInvokeError::InvalidInput(error.to_string()))?;
47    if parameters.graph() != &expected {
48        return Err(ToolInvokeError::InvalidInput(
49            "tool middleware installation parameters do not match the declared schema".to_string(),
50        ));
51    }
52    T::from_value(parameters.value())
53        .map_err(|error| ToolInvokeError::InvalidInput(error.to_string()))
54}
55pub use tool_middleware::OutputStream;
56#[doc(hidden)]
57pub use tool_middleware::{ToolMiddlewareInvokeFuture, ToolMiddlewareInvokeFutureFor};
58#[doc(hidden)]
59pub use tool_middleware_registry::{
60    ToolMiddlewareInvoker, get_all_tool_middlewares, get_tool_middleware_by_name,
61    get_tool_middleware_invoker_by_name, register_tool_middleware,
62};
63
64#[cfg(any(test, feature = "export_golem_agentic"))]
65pub(crate) use crate::schema::tool::wit::wire;
66
67#[cfg(any(test, feature = "export_golem_agentic"))]
68pub(crate) mod invocation_result;
69mod tool_middleware;
70#[cfg(feature = "export_golem_agentic")]
71mod tool_middleware_impl;
72#[cfg(feature = "export_golem_agentic")]
73#[doc(hidden)]
74pub use tool_middleware_impl::install_middleware_exports;
75mod tool_middleware_registry;
76
77#[doc(hidden)]
78pub trait ToolUnderlying: Sized {
79    fn __golem_from_underlying(underlying: UnderlyingTool) -> Self;
80
81    fn __golem_tool_descriptor() -> Tool;
82}
83
84#[cfg(test)]
85mod parameter_tests {
86    use super::*;
87    use crate::{FromSchema, IntoSchema, IntoTypedSchemaValue};
88    use test_r::test;
89
90    #[derive(Debug, PartialEq, IntoSchema, FromSchema)]
91    struct NestedParameters {
92        prefix: String,
93        rules: Vec<Rule>,
94    }
95
96    #[derive(Debug, PartialEq, IntoSchema, FromSchema)]
97    struct Rule {
98        label: String,
99        enabled: bool,
100    }
101
102    #[test]
103    fn decodes_distinct_invocation_local_parameter_values() {
104        for prefix in ["first", "second"] {
105            let value = NestedParameters {
106                prefix: prefix.to_string(),
107                rules: vec![Rule {
108                    label: "nested".to_string(),
109                    enabled: true,
110                }],
111            };
112            let decoded = decode_middleware_parameters::<NestedParameters>(
113                value.into_typed_schema_value().unwrap(),
114            )
115            .unwrap();
116            assert_eq!(decoded, value);
117        }
118    }
119
120    #[test]
121    fn rejects_parameter_value_with_wrong_declared_type() {
122        let error = decode_middleware_parameters::<NestedParameters>(
123            "not parameters"
124                .to_string()
125                .into_typed_schema_value()
126                .unwrap(),
127        )
128        .unwrap_err();
129        assert!(matches!(error, ToolInvokeError::InvalidInput(_)));
130    }
131
132    #[test]
133    fn omitted_parameters_use_an_empty_record_schema() {
134        let schema = crate::schema::try_into_schema_graph::<EmptyMiddlewareParameters>().unwrap();
135        assert!(matches!(
136            schema.resolve_ref(&schema.root).unwrap(),
137            crate::schema::SchemaType::Record { fields, .. } if fields.is_empty()
138        ));
139    }
140}