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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0e0b16a246eee9282788e5ec82210cacc6a507d83591645aca2ce87d17e0e129
4
- data.tar.gz: 546cf192269daacbbf48e7e1fe4a188ad42d3e5f8e9e50ccaeb3d96ca24803be
3
+ metadata.gz: 549044a60c0528c6cd72c66bdcada359b56e106a169237d17661841f751a090f
4
+ data.tar.gz: e315d06d73942a45c0cf0835069e403f221d89322ed2f456ea1ac7879db21ab8
5
5
  SHA512:
6
- metadata.gz: 9b6cab4332f8f76e36ccfe1024976578d739258125cdad49567cc9eb0ab21334bff1b28a5c61aee3bdaf21d375e9ac5d78ae2f248f717b1d6c942a0d75b8a51c
7
- data.tar.gz: 9a08ac4432817ceb9917f1490a2e1b5c0905609feac8181b9de4404097c1b366b185cd653315e116cf658a655ee48089dec662ce1e5b611024feb0fcde4f2660
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
- The foundational gem for the ask-rb ecosystem. Defines `Ask::Tool` — the base class every tool inherits from — along with `Ask::Result` (standardized return value), tool discovery/registration, and a scaffold generator.
3
+ [![Gem Version](https://badge.fury.io/rb/ask-tools.svg)](https://badge.fury.io/rb/ask-tools)
4
4
 
5
- This gem does **not** ship any executable tools. It only provides the contract that tool gems (e.g., `ask-tools-shell`, `ask-tools-filesystem`) implement.
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
- # Use it
36
- tool = Greeter.new
37
- tool.name # => "greeter"
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 Reference
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
- - **`Ask::Tool::Halt`** — Raise this inside `execute` to signal the conversation loop should stop after this tool's result. `call` returns an `Ask::Result` with `metadata[:halted] = true`.
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
- ### `Ask::Result` Return Value
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
- A value object representing the outcome of a tool execution.
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
- #### Factory Methods
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
- #### Attributes
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
- ```ruby
148
- class SearchTool < Ask::Tool
149
- description "Searches a knowledge base"
150
- param :query, type: :string, desc: "Search query", required: true
151
- param :limit, type: :integer, desc: "Max results", required: false
152
-
153
- def execute(query:, limit: 10)
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
- private
70
+ ## Full documentation
161
71
 
162
- def perform_search(query, limit)
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 — see [LICENSE](LICENSE).
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ask
4
4
  module Tools
5
- VERSION = "0.4.0"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
data/lib/ask-tools.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  require_relative "ask/version"
4
4
  require_relative "ask/tools"
5
- require_relative "ask/tools/result"
5
+ require "ask" # ask-core: the shared Ask::Result lives here
6
6
  require_relative "ask/tools/tool"
7
7
 
8
8
  module Ask
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.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), Ask::Result, and tool discovery.
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
@@ -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