ask-tools 0.4.0 → 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 +24 -0
- data/README.md +35 -157
- 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,27 @@
|
|
|
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
|
+
|
|
1
25
|
## [0.4.0] — 2026-07-26
|
|
2
26
|
|
|
3
27
|
### Added
|
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/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
|