ask-tools 0.2.6 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +82 -0
- data/README.md +35 -157
- data/lib/ask/tools/tool.rb +16 -0
- data/lib/ask/version.rb +1 -1
- data/lib/ask-tools.rb +1 -1
- metadata +17 -3
- data/lib/ask/tools/result.rb +0 -98
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 549044a60c0528c6cd72c66bdcada359b56e106a169237d17661841f751a090f
|
|
4
|
+
data.tar.gz: e315d06d73942a45c0cf0835069e403f221d89322ed2f456ea1ac7879db21ab8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4145513e16e503594071295d6e244ce6afad3fa55b83dbe33d88620226e0f4e152ec8837560fb85d89a5c992e19ce345fcc93775396af7e9471517cbc3c9ca91
|
|
7
|
+
data.tar.gz: 19a7e533b4d2614ff2842cbcac201393c79bc7d4fafe63e1b3c72592b63bddc887f6d517b726aabad51bbc19d916df4b06d688dd9818172c5f0ba40c4c525443
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,85 @@
|
|
|
1
|
+
## [0.5.0] — 2026-08-03
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
|
|
5
|
+
- **`Ask::Result` now comes from ask-core.** The duplicated `Ask::Result`
|
|
6
|
+
definition in this gem is removed; ask-tools depends on ask-core
|
|
7
|
+
(>= 0.9.0), which owns the single result type for the whole ecosystem with
|
|
8
|
+
both the foundational API (`success`/`failure`/`aborted`/`blocked`) and the
|
|
9
|
+
tool API (`ok`/`error`/`output`/`ok?`/`error_message`). Previously the two
|
|
10
|
+
gems' incompatible constructors meant whichever loaded last broke the
|
|
11
|
+
other's factories (`Ask::Result.success` raised `ArgumentError` in any app
|
|
12
|
+
loading both).
|
|
13
|
+
|
|
14
|
+
The tool API is unchanged:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
Ask::Result.ok(data: "hello").output # => "hello"
|
|
18
|
+
Ask::Result.error(message: "fail").error # => "fail"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Tested
|
|
22
|
+
|
|
23
|
+
- 71 tests, 159 assertions, 0 failures.
|
|
24
|
+
|
|
25
|
+
## [0.4.0] — 2026-07-26
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- **`name` class DSL for custom tool names** — tools can now declare a
|
|
30
|
+
custom name with `name "my_tool"` at the class level instead of
|
|
31
|
+
overriding `def name`. The class method safely shadows `Module#name`
|
|
32
|
+
by detecting arguments: `ClassName.name` returns the Ruby class path,
|
|
33
|
+
`name "foo"` sets the tool name. Instance `#name` returns the custom
|
|
34
|
+
name if set, otherwise auto-derives from the class name.
|
|
35
|
+
|
|
36
|
+
### Removed
|
|
37
|
+
|
|
38
|
+
- **`Ask::Tools::SubAgent`** — removed in favor of `Ask::Agent::SubAgent`
|
|
39
|
+
in ask-agent v0.18.0. Sub-agent delegation now lives in the agent
|
|
40
|
+
runtime where it can automatically build sessions.
|
|
41
|
+
|
|
42
|
+
## [0.3.0] — 2026-07-26
|
|
43
|
+
|
|
44
|
+
### Added
|
|
45
|
+
|
|
46
|
+
- **`Ask::Tools::SubAgent` — delegate tasks to a specialized sub-agent tool**.
|
|
47
|
+
An `Ask::Tool` subclass that wraps a runner callable. When the LLM calls it,
|
|
48
|
+
the sub-agent runs independently with its own model, tools, and instructions.
|
|
49
|
+
|
|
50
|
+
The tool supports per-instance `name:` and `description:` overrides so that
|
|
51
|
+
multiple sub-agents can coexist in the same tool list with distinct identities.
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
search = Ask::Tools::SubAgent.new(
|
|
55
|
+
name: "web_search",
|
|
56
|
+
description: "Search the web for current information",
|
|
57
|
+
runner: ->(task) {
|
|
58
|
+
Ask::Agent::Session.new(model: "gpt-4o-mini", tools: [Search])
|
|
59
|
+
.run(task).to_s
|
|
60
|
+
}
|
|
61
|
+
)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- **Tool discovery no longer breaks on tools with required constructor args**.
|
|
67
|
+
`Ask::Tools::SubAgent` makes its `runner:` parameter optional so that tool
|
|
68
|
+
discovery (which instantiates via `klass.new`) works without error. An
|
|
69
|
+
unconfigured SubAgent returns a clear error message at call time.
|
|
70
|
+
|
|
71
|
+
## [0.2.6] — 2026-07-18
|
|
72
|
+
|
|
73
|
+
### Fixed
|
|
74
|
+
|
|
75
|
+
- **`Tool#call` filters `_abort_controller` before passing to `execute`** — The internal `_abort_controller` key was being passed as a keyword argument to tool `execute` methods. Tools with explicit keyword arguments (e.g., `def execute(title:, extraction_scope:)`) crashed with `ArgumentError: unknown keyword` because they didn't accept `_abort_controller`. Now filtered before the `execute(**kwargs)` call.
|
|
76
|
+
|
|
77
|
+
## [0.2.5] — 2026-07-18
|
|
78
|
+
|
|
79
|
+
### Fixed
|
|
80
|
+
|
|
81
|
+
- **`Tool#normalize_args` parses JSON string arguments from LLMs** — Tool call arguments arrive as JSON strings from the LLM, but `normalize_args` only handled Hash arguments. JSON strings were silently ignored, returning an empty args hash and causing "missing required parameters" errors. Now parses JSON strings before normalizing keys to symbols.
|
|
82
|
+
|
|
1
83
|
## [0.2.4] - 2026-06-25
|
|
2
84
|
|
|
3
85
|
### Changed
|
data/README.md
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
# ask-tools
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://badge.fury.io/rb/ask-tools)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The tool framework for the ask-rb ecosystem. Defines `Ask::Tool` (the base class every tool inherits from), `Ask::Result` (standardized return value), and the `Ask::Tools` registry. This gem ships no executable tools; tool gems such as ask-tools-shell implement them.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
9
|
-
Add this line to your `Gemfile`:
|
|
10
|
-
|
|
11
9
|
```ruby
|
|
12
10
|
gem "ask-tools"
|
|
13
11
|
```
|
|
14
12
|
|
|
15
|
-
Or install it directly:
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
gem install ask-tools
|
|
19
|
-
```
|
|
20
|
-
|
|
21
13
|
## Quick Start
|
|
22
14
|
|
|
23
15
|
```ruby
|
|
@@ -32,174 +24,60 @@ class Greeter < Ask::Tool
|
|
|
32
24
|
end
|
|
33
25
|
end
|
|
34
26
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
tool.description # => "Greets a person by name"
|
|
39
|
-
|
|
40
|
-
result = tool.call(name: "World")
|
|
41
|
-
result.ok? # => true
|
|
42
|
-
result.output # => "Hello, World!"
|
|
27
|
+
result = Greeter.new.call(name: "World")
|
|
28
|
+
result.ok? # => true
|
|
29
|
+
result.output # => "Hello, World!"
|
|
43
30
|
```
|
|
44
31
|
|
|
45
|
-
## API
|
|
46
|
-
|
|
47
|
-
### `Ask::Tool` — Base Class
|
|
48
|
-
|
|
49
|
-
Subclass `Ask::Tool` to define a tool that an LLM can call.
|
|
50
|
-
|
|
51
|
-
#### Class DSL
|
|
52
|
-
|
|
53
|
-
| Method | Description |
|
|
54
|
-
|--------|-------------|
|
|
55
|
-
| `description(text)` | Sets or retrieves the tool's human-readable description. Alias: `desc` |
|
|
56
|
-
| `param(name, type:, desc:, required:)` | Declares a parameter. `type` must be a valid JSON Schema type (`:string`, `:integer`, `:number`, `:boolean`, `:array`, `:object`) |
|
|
57
|
-
|
|
58
|
-
#### Instance Methods
|
|
59
|
-
|
|
60
|
-
| Method | Returns | Description |
|
|
61
|
-
|--------|---------|-------------|
|
|
62
|
-
| `name` | `String` | Auto-derived from the class name: CamelCase → snake_case, strips `_tool` suffix |
|
|
63
|
-
| `description` | `String?, nil` | The tool's description |
|
|
64
|
-
| `parameters` | `Hash{Symbol => Parameter}` | Declared parameter definitions |
|
|
65
|
-
| `call(args = {})` | `Ask::Result` | Normalizes args (symbolizes keys), validates required params, delegates to `execute`. Catches `Halt` and `StandardError` |
|
|
66
|
-
| `execute(**args)` | `Ask::Result` | **Override this.** Implement the tool's logic. |
|
|
67
|
-
| `params_schema` | `Hash?, nil` | JSON Schema hash for LLM function-calling APIs. Returns `nil` when no params declared |
|
|
68
|
-
| `tool_definition` | `Hash` | Full tool definition hash with `:name`, `:description`, and `:input_schema` |
|
|
69
|
-
|
|
70
|
-
#### Error Handling
|
|
32
|
+
## Essential API
|
|
71
33
|
|
|
72
|
-
|
|
73
|
-
- **`StandardError`** — Any other exception raised in `execute` is caught by `call` and returned as an error `Ask::Result`.
|
|
34
|
+
### Ask::Tool
|
|
74
35
|
|
|
75
|
-
|
|
36
|
+
| Method | Purpose |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `description "..."` (alias `desc`) | Set the tool description |
|
|
39
|
+
| `param :name, type: :string, desc: "...", required: true` | Declare a parameter. `type` must be a JSON Schema type (`:string`, `:integer`, `:number`, `:boolean`, `:array`, `:object`) |
|
|
40
|
+
| `name "custom_tool"` | Set a custom tool name (default: derived from the class name, CamelCase to snake_case, `_tool` suffix stripped) |
|
|
41
|
+
| `params do ... end` | Declare parameters with the ask-schema DSL |
|
|
76
42
|
|
|
77
|
-
|
|
43
|
+
Override `execute(**args)` with the tool logic. `call(args)` normalizes input (JSON strings and hash keys), validates required parameters, and returns an `Ask::Result`. Raising `Ask::Tool::Halt` inside `execute` yields a success result with `metadata[:halted] = true`; any other exception becomes a failure result.
|
|
78
44
|
|
|
79
|
-
|
|
45
|
+
### Ask::Result
|
|
80
46
|
|
|
81
47
|
```ruby
|
|
82
|
-
# Successful result
|
|
83
48
|
Ask::Result.ok(data: "output", metadata: { key: "val" })
|
|
84
|
-
|
|
85
|
-
# Failed result
|
|
86
49
|
Ask::Result.error(message: "Something went wrong", metadata: { code: 500 })
|
|
50
|
+
Ask::Result.failure("Something went wrong")
|
|
51
|
+
|
|
52
|
+
result.ok? # => true
|
|
53
|
+
result.output # => "output"
|
|
54
|
+
result.error # => nil
|
|
55
|
+
result.metadata # => { key: "val" }
|
|
56
|
+
result.to_s # => "output"
|
|
57
|
+
result.to_h # => { ok: true, output: "output", error: nil, metadata: { key: "val" } }
|
|
87
58
|
```
|
|
88
59
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
| Attribute | Type | Description |
|
|
92
|
-
|-----------|------|-------------|
|
|
93
|
-
| `ok?` / `ok` | `Boolean` | Whether the tool completed successfully |
|
|
94
|
-
| `output` | `Object?, nil` | Output data (success) |
|
|
95
|
-
| `error` | `String?, nil` | Error message (failure) |
|
|
96
|
-
| `metadata` | `Hash` | Arbitrary metadata |
|
|
97
|
-
|
|
98
|
-
#### Instance Methods
|
|
99
|
-
|
|
100
|
-
| Method | Returns | Description |
|
|
101
|
-
|--------|---------|-------------|
|
|
102
|
-
| `to_s` | `String` | Returns `output.to_s` for success, `error` for failure |
|
|
103
|
-
| `to_h` | `Hash` | Serialized hash with `:ok`, `:output`, `:error`, `:metadata` |
|
|
104
|
-
| `inspect` | `String` | Human-readable representation |
|
|
105
|
-
|
|
106
|
-
### `Ask::Tool::Parameter` — Parameter Definition
|
|
107
|
-
|
|
108
|
-
Internal value object describing a declared parameter. Accessible via `Tool.parameters[name]`.
|
|
109
|
-
|
|
110
|
-
| Attribute | Type | Description |
|
|
111
|
-
|-----------|------|-------------|
|
|
112
|
-
| `name` | `Symbol` | Parameter name |
|
|
113
|
-
| `type` | `String` | JSON Schema type string |
|
|
114
|
-
| `description` | `String?, nil` | Human-readable description |
|
|
115
|
-
| `required` / `required?` | `Boolean` | Whether the parameter is mandatory |
|
|
116
|
-
|
|
117
|
-
### `Ask::Tools` — Registry & Discovery
|
|
118
|
-
|
|
119
|
-
Central registry for tool classes.
|
|
120
|
-
|
|
121
|
-
| Method | Returns | Description |
|
|
122
|
-
|--------|---------|-------------|
|
|
123
|
-
| `.register(tool_class)` | `void` | Manually register a tool class |
|
|
124
|
-
| `.all` | `Array<Tool>` | Instantiated list of all registered tools |
|
|
125
|
-
| `.discover` | `Array<Class>` | Auto-discover loaded `Ask::Tool` subclasses via `ObjectSpace` |
|
|
126
|
-
| `.[](name)` | `Tool?, nil` | Find a registered tool by its derived name |
|
|
127
|
-
| `.clear` | `void` | Remove all registered tools |
|
|
128
|
-
| `.count` | `Integer` | Number of registered tool classes |
|
|
129
|
-
|
|
130
|
-
```ruby
|
|
131
|
-
# Manual registration
|
|
132
|
-
Ask::Tools.register(MyTool)
|
|
133
|
-
|
|
134
|
-
# Auto-discover all loaded Ask::Tool subclasses
|
|
135
|
-
Ask::Tools.discover
|
|
136
|
-
|
|
137
|
-
# Find by name
|
|
138
|
-
tool = Ask::Tools["my_tool"]
|
|
139
|
-
tool.call(input: "hello")
|
|
140
|
-
|
|
141
|
-
# List all
|
|
142
|
-
Ask::Tools.all.each { |t| puts t.name }
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
## Defining a Custom Tool
|
|
60
|
+
### Ask::Tools registry
|
|
146
61
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
results = perform_search(query, limit)
|
|
155
|
-
Ask::Result.ok(data: results)
|
|
156
|
-
rescue SearchError => e
|
|
157
|
-
Ask::Result.error(message: e.message)
|
|
158
|
-
end
|
|
62
|
+
| Method | Purpose |
|
|
63
|
+
|---|---|
|
|
64
|
+
| `Ask::Tools.register(ToolClass)` | Register a tool class manually |
|
|
65
|
+
| `Ask::Tools.all` | Instances of all registered tools |
|
|
66
|
+
| `Ask::Tools.discover` | Auto-register loaded `Ask::Tool` subclasses via ObjectSpace |
|
|
67
|
+
| `Ask::Tools["name"]` | Find a tool instance by derived name |
|
|
68
|
+
| `Ask::Tools.clear` / `Ask::Tools.count` | Reset and count the registry |
|
|
159
69
|
|
|
160
|
-
|
|
70
|
+
## Full documentation
|
|
161
71
|
|
|
162
|
-
|
|
163
|
-
# ... implementation
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
```
|
|
72
|
+
The full ask-rb documentation lives at https://ask-rb.github.io/ask-docs. [ask-tools in depth](https://ask-rb.github.io/ask-docs/core/tools) covers the tool contract, parameter schemas, and custom tool examples. API reference: https://ask-rb.github.io/ask-docs/reference/api.
|
|
167
73
|
|
|
168
74
|
## Development
|
|
169
75
|
|
|
170
|
-
```bash
|
|
171
|
-
# Install dependencies
|
|
172
|
-
bundle install
|
|
173
|
-
|
|
174
|
-
# Run tests
|
|
175
|
-
bundle exec rake test
|
|
176
|
-
|
|
177
|
-
# Build the gem
|
|
178
|
-
gem build ask-tools.gemspec
|
|
179
76
|
```
|
|
180
|
-
|
|
181
|
-
## Testing
|
|
182
|
-
|
|
183
|
-
ask-tools uses **Minitest** with **Mocha** for mocking.
|
|
184
|
-
|
|
185
|
-
```bash
|
|
186
|
-
# Run the full test suite
|
|
77
|
+
bundle install
|
|
187
78
|
bundle exec rake test
|
|
188
79
|
```
|
|
189
80
|
|
|
190
|
-
## Release Process
|
|
191
|
-
|
|
192
|
-
1. Update `CHANGELOG.md`
|
|
193
|
-
2. Update `lib/ask/version.rb` if needed
|
|
194
|
-
3. Build the gem: `gem build ask-tools.gemspec`
|
|
195
|
-
4. Push to GitHub Packages: `gem push ask-tools-*.gem`
|
|
196
|
-
|
|
197
81
|
## License
|
|
198
82
|
|
|
199
|
-
MIT
|
|
200
|
-
|
|
201
|
-
## Links
|
|
202
|
-
|
|
203
|
-
- **Source:** https://github.com/ask-rb/ask-tools
|
|
204
|
-
- **Issues:** https://github.com/ask-rb/ask-tools/issues
|
|
205
|
-
- **Docs:** https://github.com/ask-rb/ask-docs
|
|
83
|
+
MIT
|
data/lib/ask/tools/tool.rb
CHANGED
|
@@ -20,6 +20,7 @@ module Ask
|
|
|
20
20
|
subclass.instance_variable_set(:@description, nil)
|
|
21
21
|
subclass.instance_variable_set(:@parameters, {})
|
|
22
22
|
subclass.instance_variable_set(:@params_schema_definition, nil)
|
|
23
|
+
subclass.instance_variable_set(:@tool_name, nil)
|
|
23
24
|
end
|
|
24
25
|
|
|
25
26
|
def description(text = nil)
|
|
@@ -28,6 +29,18 @@ module Ask
|
|
|
28
29
|
end
|
|
29
30
|
alias desc description
|
|
30
31
|
|
|
32
|
+
# Declare a custom tool name.
|
|
33
|
+
# Called with no argument returns the class name (via Module#name).
|
|
34
|
+
# Called with a string stores a custom name for the instance.
|
|
35
|
+
# Example: name "my_custom_tool"
|
|
36
|
+
def name(custom = :_no_arg_given)
|
|
37
|
+
if custom == :_no_arg_given
|
|
38
|
+
super() # Module#name, returns the Ruby class path
|
|
39
|
+
else
|
|
40
|
+
@tool_name = custom
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
31
44
|
def param(name, type:, desc: nil, description: nil, required: true)
|
|
32
45
|
type = type.to_s.downcase.to_sym
|
|
33
46
|
validate_param_type!(type, name)
|
|
@@ -115,6 +128,9 @@ module Ask
|
|
|
115
128
|
end
|
|
116
129
|
|
|
117
130
|
def name
|
|
131
|
+
custom = self.class.instance_variable_get(:@tool_name)
|
|
132
|
+
return custom if custom
|
|
133
|
+
|
|
118
134
|
klass_name = self.class.name.to_s.split("::").last || ""
|
|
119
135
|
normalized = klass_name.dup.force_encoding("UTF-8").unicode_normalize(:nfkd)
|
|
120
136
|
normalized.encode("ASCII", replace: "")
|
data/lib/ask/version.rb
CHANGED
data/lib/ask-tools.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ask-tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kaka Ruto
|
|
@@ -9,6 +9,20 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ask-core
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.9.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.9.0
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: ask-schema
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -65,7 +79,8 @@ dependencies:
|
|
|
65
79
|
- - "~>"
|
|
66
80
|
- !ruby/object:Gem::Version
|
|
67
81
|
version: '13.0'
|
|
68
|
-
description: Defines Ask::Tool (base class),
|
|
82
|
+
description: Defines Ask::Tool (base class), tool discovery, and the Ask::Result returned
|
|
83
|
+
by tools.
|
|
69
84
|
email:
|
|
70
85
|
- kaka@myrrlabs.com
|
|
71
86
|
executables: []
|
|
@@ -77,7 +92,6 @@ files:
|
|
|
77
92
|
- README.md
|
|
78
93
|
- lib/ask-tools.rb
|
|
79
94
|
- lib/ask/tools.rb
|
|
80
|
-
- lib/ask/tools/result.rb
|
|
81
95
|
- lib/ask/tools/tool.rb
|
|
82
96
|
- lib/ask/version.rb
|
|
83
97
|
homepage: https://github.com/ask-rb/ask-tools
|
data/lib/ask/tools/result.rb
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Ask
|
|
4
|
-
# Standardized return value for tool execution.
|
|
5
|
-
#
|
|
6
|
-
# Every tool's #execute method should return an Ask::Result.
|
|
7
|
-
# Use the factory methods +.ok+ and +.error+ for common cases.
|
|
8
|
-
#
|
|
9
|
-
# Ask::Result.ok(data: "hello world")
|
|
10
|
-
# Ask::Result.error(message: "something went wrong")
|
|
11
|
-
#
|
|
12
|
-
class Result
|
|
13
|
-
# @return [Boolean] whether the tool completed successfully
|
|
14
|
-
attr_reader :ok
|
|
15
|
-
|
|
16
|
-
# @return [Object, nil] the output data when the tool succeeded
|
|
17
|
-
attr_reader :output
|
|
18
|
-
|
|
19
|
-
# @return [String, nil] the error message when the tool failed
|
|
20
|
-
attr_reader :error
|
|
21
|
-
|
|
22
|
-
# @return [Hash] arbitrary metadata attached to the result
|
|
23
|
-
attr_reader :metadata
|
|
24
|
-
|
|
25
|
-
alias ok? ok
|
|
26
|
-
|
|
27
|
-
# @return [Boolean] whether the tool failed
|
|
28
|
-
def error?
|
|
29
|
-
!ok
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# @return [String, nil] the error message (alias for +error+)
|
|
33
|
-
alias error_message error
|
|
34
|
-
|
|
35
|
-
def initialize(ok:, output: nil, error: nil, metadata: {})
|
|
36
|
-
@ok = ok
|
|
37
|
-
@output = output
|
|
38
|
-
@error = error
|
|
39
|
-
@metadata = metadata
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Create a successful result.
|
|
43
|
-
#
|
|
44
|
-
# @param data [Object] the tool's output
|
|
45
|
-
# @param metadata [Hash] optional metadata
|
|
46
|
-
# @return [Ask::Result]
|
|
47
|
-
def self.ok(data:, metadata: {})
|
|
48
|
-
new(ok: true, output: data, error: nil, metadata: metadata)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Create a failed result (positional message form, used by Tool#call).
|
|
52
|
-
#
|
|
53
|
-
# @param message [String] description of the failure
|
|
54
|
-
# @param metadata [Hash] optional metadata
|
|
55
|
-
# @return [Ask::Result]
|
|
56
|
-
def self.failure(message, metadata: {})
|
|
57
|
-
new(ok: false, output: nil, error: message, metadata: metadata)
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Create a failed result.
|
|
61
|
-
#
|
|
62
|
-
# @param message [String] description of the failure
|
|
63
|
-
# @param metadata [Hash] optional metadata
|
|
64
|
-
# @return [Ask::Result]
|
|
65
|
-
def self.error(message:, metadata: {})
|
|
66
|
-
new(ok: false, output: nil, error: message, metadata: metadata)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Human-readable representation.
|
|
70
|
-
# Returns the output for success or the error message for failure.
|
|
71
|
-
#
|
|
72
|
-
# @return [String]
|
|
73
|
-
def to_s
|
|
74
|
-
ok? ? output.to_s : error.to_s
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Hash representation suitable for serialization.
|
|
78
|
-
#
|
|
79
|
-
# @return [Hash]
|
|
80
|
-
def to_h
|
|
81
|
-
{
|
|
82
|
-
ok: ok,
|
|
83
|
-
output: output,
|
|
84
|
-
error: error,
|
|
85
|
-
metadata: metadata
|
|
86
|
-
}
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# @return [String] inspect string
|
|
90
|
-
def inspect
|
|
91
|
-
if ok?
|
|
92
|
-
"#<Ask::Result ok=true output=#{output.inspect}>"
|
|
93
|
-
else
|
|
94
|
-
"#<Ask::Result ok=false error=#{error.inspect}>"
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|