shared_tools 0.3.1 → 0.4.1
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 +46 -16
- data/README.md +257 -262
- data/lib/shared_tools/browser_tool.rb +5 -0
- data/lib/shared_tools/calculator_tool.rb +4 -0
- data/lib/shared_tools/clipboard_tool.rb +4 -0
- data/lib/shared_tools/composite_analysis_tool.rb +4 -0
- data/lib/shared_tools/computer_tool.rb +5 -0
- data/lib/shared_tools/cron_tool.rb +4 -0
- data/lib/shared_tools/current_date_time_tool.rb +4 -0
- data/lib/shared_tools/data_science_kit.rb +4 -0
- data/lib/shared_tools/database.rb +4 -0
- data/lib/shared_tools/database_query_tool.rb +4 -0
- data/lib/shared_tools/database_tool.rb +5 -0
- data/lib/shared_tools/disk_tool.rb +5 -0
- data/lib/shared_tools/dns_tool.rb +4 -0
- data/lib/shared_tools/doc_tool.rb +5 -0
- data/lib/shared_tools/error_handling_tool.rb +4 -0
- data/lib/shared_tools/eval_tool.rb +5 -0
- data/lib/shared_tools/mcp/brave_search_client.rb +37 -0
- data/lib/shared_tools/mcp/chart_client.rb +32 -0
- data/lib/shared_tools/mcp/github_client.rb +38 -0
- data/lib/shared_tools/mcp/hugging_face_client.rb +43 -0
- data/lib/shared_tools/mcp/memory_client.rb +33 -0
- data/lib/shared_tools/mcp/notion_client.rb +40 -0
- data/lib/shared_tools/mcp/sequential_thinking_client.rb +33 -0
- data/lib/shared_tools/mcp/slack_client.rb +54 -0
- data/lib/shared_tools/mcp/streamable_http_patch.rb +42 -0
- data/lib/shared_tools/mcp/tavily_client.rb +41 -0
- data/lib/shared_tools/mcp.rb +45 -16
- data/lib/shared_tools/system_info_tool.rb +4 -0
- data/lib/shared_tools/tools/browser/base_tool.rb +8 -12
- data/lib/shared_tools/tools/browser/click_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/ferrum_driver.rb +119 -0
- data/lib/shared_tools/tools/browser/inspect_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/page_inspect_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/page_screenshot_tool.rb +19 -7
- data/lib/shared_tools/tools/browser/selector_inspect_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/text_field_area_set_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/visit_tool.rb +4 -2
- data/lib/shared_tools/tools/browser.rb +31 -2
- data/lib/shared_tools/tools/browser_tool.rb +6 -0
- data/lib/shared_tools/tools/clipboard_tool.rb +69 -144
- data/lib/shared_tools/tools/composite_analysis_tool.rb +60 -4
- data/lib/shared_tools/tools/computer/mac_driver.rb +37 -4
- data/lib/shared_tools/tools/cron_tool.rb +237 -379
- data/lib/shared_tools/tools/current_date_time_tool.rb +54 -120
- data/lib/shared_tools/tools/data_science_kit.rb +63 -13
- data/lib/shared_tools/tools/dns_tool.rb +335 -269
- data/lib/shared_tools/tools/doc/docx_reader_tool.rb +107 -0
- data/lib/shared_tools/tools/doc/spreadsheet_reader_tool.rb +171 -0
- data/lib/shared_tools/tools/doc/text_reader_tool.rb +57 -0
- data/lib/shared_tools/tools/doc.rb +3 -0
- data/lib/shared_tools/tools/doc_tool.rb +101 -6
- data/lib/shared_tools/tools/docker/compose_run_tool.rb +1 -1
- data/lib/shared_tools/tools/enabler.rb +42 -0
- data/lib/shared_tools/tools/error_handling_tool.rb +3 -1
- data/lib/shared_tools/tools/notification/base_driver.rb +51 -0
- data/lib/shared_tools/tools/notification/linux_driver.rb +115 -0
- data/lib/shared_tools/tools/notification/mac_driver.rb +66 -0
- data/lib/shared_tools/tools/notification/null_driver.rb +29 -0
- data/lib/shared_tools/tools/notification.rb +12 -0
- data/lib/shared_tools/tools/notification_tool.rb +99 -0
- data/lib/shared_tools/tools/system_info_tool.rb +130 -343
- data/lib/shared_tools/tools/workflow_manager_tool.rb +32 -0
- data/lib/shared_tools/utilities.rb +193 -0
- data/lib/shared_tools/version.rb +1 -1
- data/lib/shared_tools/weather_tool.rb +4 -0
- data/lib/shared_tools/workflow_manager_tool.rb +4 -0
- data/lib/shared_tools.rb +28 -38
- metadata +74 -9
- data/lib/shared_tools/mcp/github_mcp_server.rb +0 -58
- data/lib/shared_tools/mcp/imcp.rb +0 -28
- data/lib/shared_tools/mcp/tavily_mcp_server.rb +0 -44
- data/lib/shared_tools/tools/devops_toolkit.rb +0 -420
|
@@ -1,48 +1,32 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'ruby_llm/tool'
|
|
4
3
|
require 'time'
|
|
4
|
+
require_relative '../../shared_tools'
|
|
5
5
|
|
|
6
6
|
module SharedTools
|
|
7
7
|
module Tools
|
|
8
|
-
#
|
|
9
|
-
# Useful for AI assistants that need to know the current time context.
|
|
8
|
+
# Returns the current date, time, and timezone from the local system.
|
|
10
9
|
#
|
|
11
10
|
# @example
|
|
12
11
|
# tool = SharedTools::Tools::CurrentDateTimeTool.new
|
|
13
|
-
#
|
|
14
|
-
#
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class CurrentDateTimeTool < RubyLLM::Tool
|
|
18
|
-
def self.name = 'current_date_time'
|
|
12
|
+
# tool.execute # full output
|
|
13
|
+
# tool.execute(format: 'date') # date fields only
|
|
14
|
+
class CurrentDateTimeTool < ::RubyLLM::Tool
|
|
15
|
+
def self.name = 'current_date_time_tool'
|
|
19
16
|
|
|
20
|
-
description <<~
|
|
21
|
-
Returns the current date, time, and
|
|
22
|
-
This tool provides accurate temporal context for AI assistants that need
|
|
23
|
-
to reason about time-sensitive information or schedule-related queries.
|
|
17
|
+
description <<~DESC
|
|
18
|
+
Returns the current date, time, timezone, and calendar metadata from the system clock.
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Example usage:
|
|
33
|
-
tool = SharedTools::Tools::CurrentDateTimeTool.new
|
|
34
|
-
result = tool.execute
|
|
35
|
-
puts "Today is #{result[:day_of_week]}, #{result[:date]}"
|
|
36
|
-
puts "Current time: #{result[:time]} #{result[:timezone]}"
|
|
37
|
-
DESCRIPTION
|
|
20
|
+
Supported formats:
|
|
21
|
+
- 'full' (default) — all fields: date, time, timezone, ISO 8601, unix timestamp, DST flag
|
|
22
|
+
- 'date' — year, month, day, day_of_week, week_of_year, quarter, ordinal_day
|
|
23
|
+
- 'time' — hour, minute, second, timezone, utc_offset
|
|
24
|
+
- 'iso8601' — iso8601, iso8601_utc, unix_timestamp
|
|
25
|
+
DESC
|
|
38
26
|
|
|
39
27
|
params do
|
|
40
|
-
string :format, description: <<~DESC.strip
|
|
41
|
-
Output format
|
|
42
|
-
- 'full' (default): Returns all date/time information
|
|
43
|
-
- 'date_only': Returns only date-related fields
|
|
44
|
-
- 'time_only': Returns only time-related fields
|
|
45
|
-
- 'iso8601': Returns a single ISO 8601 formatted datetime string
|
|
28
|
+
string :format, required: false, description: <<~DESC.strip
|
|
29
|
+
Output format. Options: 'full' (default), 'date', 'time', 'iso8601'.
|
|
46
30
|
DESC
|
|
47
31
|
end
|
|
48
32
|
|
|
@@ -51,103 +35,53 @@ module SharedTools
|
|
|
51
35
|
@logger = logger || RubyLLM.logger
|
|
52
36
|
end
|
|
53
37
|
|
|
54
|
-
#
|
|
55
|
-
#
|
|
56
|
-
# @param format [String] Output format ('full', 'date_only', 'time_only', 'iso8601')
|
|
57
|
-
# @return [Hash] Current date/time information
|
|
38
|
+
# @param format [String] output format
|
|
39
|
+
# @return [Hash] date/time information
|
|
58
40
|
def execute(format: 'full')
|
|
59
|
-
@logger.info("
|
|
41
|
+
@logger.info("CurrentDateTimeTool#execute format=#{format}")
|
|
60
42
|
|
|
61
43
|
now = Time.now
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
private
|
|
76
|
-
|
|
77
|
-
# Full response with all date/time information
|
|
78
|
-
def full_response(now)
|
|
79
|
-
{
|
|
80
|
-
success: true,
|
|
81
|
-
date: now.strftime('%Y-%m-%d'),
|
|
82
|
-
time: now.strftime('%H:%M:%S'),
|
|
83
|
-
datetime: now.iso8601,
|
|
84
|
-
timezone: now.zone,
|
|
85
|
-
timezone_name: timezone_name(now),
|
|
86
|
-
utc_offset: formatted_utc_offset(now),
|
|
87
|
-
unix_timestamp: now.to_i,
|
|
88
|
-
day_of_week: now.strftime('%A'),
|
|
89
|
-
day_of_year: now.yday,
|
|
90
|
-
week_number: now.strftime('%V').to_i,
|
|
91
|
-
is_dst: now.dst?,
|
|
92
|
-
quarter: ((now.month - 1) / 3) + 1
|
|
44
|
+
utc = now.utc
|
|
45
|
+
|
|
46
|
+
date_info = {
|
|
47
|
+
year: now.year,
|
|
48
|
+
month: now.month,
|
|
49
|
+
day: now.day,
|
|
50
|
+
day_of_week: now.strftime('%A'),
|
|
51
|
+
day_of_week_num: now.wday,
|
|
52
|
+
week_of_year: now.strftime('%U').to_i,
|
|
53
|
+
quarter: ((now.month - 1) / 3) + 1,
|
|
54
|
+
ordinal_day: now.yday
|
|
93
55
|
}
|
|
94
|
-
end
|
|
95
56
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
month_name: now.strftime('%B'),
|
|
104
|
-
day: now.day,
|
|
105
|
-
day_of_week: now.strftime('%A'),
|
|
106
|
-
day_of_year: now.yday,
|
|
107
|
-
week_number: now.strftime('%V').to_i,
|
|
108
|
-
quarter: ((now.month - 1) / 3) + 1
|
|
57
|
+
time_info = {
|
|
58
|
+
hour: now.hour,
|
|
59
|
+
minute: now.min,
|
|
60
|
+
second: now.sec,
|
|
61
|
+
timezone: now.zone,
|
|
62
|
+
utc_offset: now.strftime('%z'),
|
|
63
|
+
utc_offset_hours: (now.utc_offset / 3600.0).round(2)
|
|
109
64
|
}
|
|
110
|
-
end
|
|
111
65
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
time: now.strftime('%H:%M:%S'),
|
|
117
|
-
time_12h: now.strftime('%I:%M:%S %p'),
|
|
118
|
-
hour: now.hour,
|
|
119
|
-
minute: now.min,
|
|
120
|
-
second: now.sec,
|
|
121
|
-
timezone: now.zone,
|
|
122
|
-
timezone_name: timezone_name(now),
|
|
123
|
-
utc_offset: formatted_utc_offset(now),
|
|
124
|
-
unix_timestamp: now.to_i,
|
|
125
|
-
is_dst: now.dst?
|
|
66
|
+
iso_info = {
|
|
67
|
+
iso8601: now.iso8601,
|
|
68
|
+
iso8601_utc: utc.iso8601,
|
|
69
|
+
unix_timestamp: now.to_i
|
|
126
70
|
}
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# ISO 8601 formatted response
|
|
130
|
-
def iso8601_response(now)
|
|
131
|
-
{
|
|
132
|
-
success: true,
|
|
133
|
-
datetime: now.iso8601,
|
|
134
|
-
utc: now.utc.iso8601
|
|
135
|
-
}
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# Get the IANA timezone name if available
|
|
139
|
-
def timezone_name(time)
|
|
140
|
-
# Try to get IANA timezone from TZ environment variable
|
|
141
|
-
ENV['TZ'] || time.zone
|
|
142
|
-
end
|
|
143
71
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
72
|
+
case format.to_s.downcase
|
|
73
|
+
when 'date'
|
|
74
|
+
{ success: true }.merge(date_info)
|
|
75
|
+
when 'time'
|
|
76
|
+
{ success: true }.merge(time_info)
|
|
77
|
+
when 'iso8601'
|
|
78
|
+
{ success: true }.merge(iso_info)
|
|
79
|
+
else
|
|
80
|
+
{ success: true, dst: now.dst? }
|
|
81
|
+
.merge(date_info)
|
|
82
|
+
.merge(time_info)
|
|
83
|
+
.merge(iso_info)
|
|
84
|
+
end
|
|
151
85
|
end
|
|
152
86
|
end
|
|
153
87
|
end
|
|
@@ -28,13 +28,22 @@ module SharedTools
|
|
|
28
28
|
Each analysis type requires specific data formats and optional parameters.
|
|
29
29
|
DESC
|
|
30
30
|
|
|
31
|
-
string :data_source, description: <<~DESC.strip, required:
|
|
31
|
+
string :data_source, description: <<~DESC.strip, required: false
|
|
32
32
|
Data source specification for analysis. Can be:
|
|
33
33
|
- File path: Relative or absolute path to CSV, JSON, Excel, or Parquet files
|
|
34
34
|
- Database query: SQL SELECT statement for database-sourced data
|
|
35
35
|
- API endpoint: HTTP URL for REST API data sources
|
|
36
36
|
The tool automatically detects the format and applies appropriate parsing.
|
|
37
37
|
Examples: './sales_data.csv', 'SELECT * FROM transactions', 'https://api.company.com/data'
|
|
38
|
+
Either data_source or data must be provided.
|
|
39
|
+
DESC
|
|
40
|
+
|
|
41
|
+
string :data, description: <<~DESC.strip, required: false
|
|
42
|
+
Inline data to analyse, provided directly as a JSON string. Accepted formats:
|
|
43
|
+
- Array of hashes: '[{"month":"Jan","value":42},{"month":"Feb","value":45}]'
|
|
44
|
+
- Pipe-delimited table string: "Col A | Col B\n1 | 2\n3 | 4"
|
|
45
|
+
- Comma-separated numbers (single series): "42,45,51,48,55" — parsed as [{value: n}]
|
|
46
|
+
Either data or data_source must be provided.
|
|
38
47
|
DESC
|
|
39
48
|
|
|
40
49
|
object :parameters, description: <<~DESC.strip, required: false do
|
|
@@ -82,11 +91,18 @@ module SharedTools
|
|
|
82
91
|
@logger = logger || RubyLLM.logger
|
|
83
92
|
end
|
|
84
93
|
|
|
85
|
-
def execute(analysis_type:, data_source
|
|
94
|
+
def execute(analysis_type:, data_source: nil, data: nil, **parameters)
|
|
86
95
|
analysis_start = Time.now
|
|
87
96
|
|
|
88
97
|
begin
|
|
89
|
-
|
|
98
|
+
if data_source.nil? && data.nil?
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: "Either data_source or data must be provided."
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
@logger.info("DataScienceKit#execute analysis_type=#{analysis_type}")
|
|
90
106
|
|
|
91
107
|
# Validate analysis type
|
|
92
108
|
unless VALID_ANALYSIS_TYPES.include?(analysis_type)
|
|
@@ -99,21 +115,21 @@ module SharedTools
|
|
|
99
115
|
end
|
|
100
116
|
|
|
101
117
|
# Load and validate data
|
|
102
|
-
|
|
103
|
-
validate_data_for_analysis(
|
|
118
|
+
loaded_data = data ? parse_inline_data(data) : load_data(data_source)
|
|
119
|
+
validate_data_for_analysis(loaded_data, analysis_type, parameters)
|
|
104
120
|
|
|
105
121
|
# Perform analysis
|
|
106
122
|
result = case analysis_type
|
|
107
123
|
when "statistical_summary"
|
|
108
|
-
generate_statistical_summary(
|
|
124
|
+
generate_statistical_summary(loaded_data, parameters)
|
|
109
125
|
when "correlation_analysis"
|
|
110
|
-
perform_correlation_analysis(
|
|
126
|
+
perform_correlation_analysis(loaded_data, parameters)
|
|
111
127
|
when "time_series"
|
|
112
|
-
analyze_time_series(
|
|
128
|
+
analyze_time_series(loaded_data, parameters)
|
|
113
129
|
when "clustering"
|
|
114
|
-
perform_clustering(
|
|
130
|
+
perform_clustering(loaded_data, parameters)
|
|
115
131
|
when "prediction"
|
|
116
|
-
generate_predictions(
|
|
132
|
+
generate_predictions(loaded_data, parameters)
|
|
117
133
|
end
|
|
118
134
|
|
|
119
135
|
analysis_duration = (Time.now - analysis_start).round(3)
|
|
@@ -123,7 +139,7 @@ module SharedTools
|
|
|
123
139
|
success: true,
|
|
124
140
|
analysis_type: analysis_type,
|
|
125
141
|
result: result,
|
|
126
|
-
data_summary: summarize_data(
|
|
142
|
+
data_summary: summarize_data(loaded_data),
|
|
127
143
|
analyzed_at: Time.now.iso8601,
|
|
128
144
|
duration_seconds: analysis_duration
|
|
129
145
|
}
|
|
@@ -133,8 +149,7 @@ module SharedTools
|
|
|
133
149
|
success: false,
|
|
134
150
|
error: e.message,
|
|
135
151
|
error_type: e.class.name,
|
|
136
|
-
analysis_type: analysis_type
|
|
137
|
-
data_source: data_source
|
|
152
|
+
analysis_type: analysis_type
|
|
138
153
|
}
|
|
139
154
|
end
|
|
140
155
|
end
|
|
@@ -189,6 +204,41 @@ module SharedTools
|
|
|
189
204
|
end
|
|
190
205
|
end
|
|
191
206
|
|
|
207
|
+
def parse_inline_data(raw)
|
|
208
|
+
raw = raw.strip
|
|
209
|
+
# JSON array or object
|
|
210
|
+
if raw.start_with?('{', '[')
|
|
211
|
+
begin
|
|
212
|
+
return JSON.parse(raw)
|
|
213
|
+
rescue JSON::ParserError
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
lines = raw.lines.map(&:strip).reject(&:empty?)
|
|
217
|
+
# Pipe-delimited table
|
|
218
|
+
if lines.first&.include?('|')
|
|
219
|
+
headers = lines.first.split('|').map(&:strip).reject(&:empty?)
|
|
220
|
+
data_lines = lines.drop(1).reject { |l| l.match?(/^\|?[-:\s|]+$/) }
|
|
221
|
+
return data_lines.map do |line|
|
|
222
|
+
values = line.split('|').map(&:strip).reject(&:empty?)
|
|
223
|
+
headers.zip(values).to_h
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
# CSV header row
|
|
227
|
+
if lines.size > 1 && lines.first.include?(',') && lines.first.match?(/[a-zA-Z]/)
|
|
228
|
+
headers = lines.first.split(',').map(&:strip)
|
|
229
|
+
return lines.drop(1).map do |line|
|
|
230
|
+
values = line.split(',').map(&:strip)
|
|
231
|
+
headers.zip(values).to_h
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
# Comma-separated numbers — single series
|
|
235
|
+
if lines.size == 1 && lines.first.match?(/^[\d.,\s]+$/)
|
|
236
|
+
return lines.first.split(',').map { |v| { "value" => v.strip.to_f } }
|
|
237
|
+
end
|
|
238
|
+
# Plain lines
|
|
239
|
+
lines.map { |l| { "value" => l } }
|
|
240
|
+
end
|
|
241
|
+
|
|
192
242
|
# Generate sample data for testing
|
|
193
243
|
def generate_sample_data(size = 30)
|
|
194
244
|
(1..size).map do |i|
|