aia 0.9.8 → 0.9.10

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.version +1 -1
  3. data/CHANGELOG.md +15 -0
  4. data/README.md +62 -5
  5. data/Rakefile +16 -8
  6. data/examples/directives/ask.rb +21 -0
  7. data/examples/tools/edit_file.rb +2 -0
  8. data/examples/tools/incomplete/calculator_tool.rb +70 -0
  9. data/examples/tools/incomplete/composite_analysis_tool.rb +89 -0
  10. data/examples/tools/incomplete/data_science_kit.rb +128 -0
  11. data/examples/tools/incomplete/database_query_tool.rb +100 -0
  12. data/examples/tools/incomplete/devops_toolkit.rb +112 -0
  13. data/examples/tools/incomplete/error_handling_tool.rb +109 -0
  14. data/examples/tools/{pdf_page_reader.rb → incomplete/pdf_page_reader.rb} +2 -0
  15. data/examples/tools/incomplete/secure_tool_template.rb +117 -0
  16. data/examples/tools/incomplete/weather_tool.rb +110 -0
  17. data/examples/tools/incomplete/workflow_manager_tool.rb +145 -0
  18. data/examples/tools/list_files.rb +2 -0
  19. data/examples/tools/mcp/README.md +1 -0
  20. data/examples/tools/mcp/github_mcp_server.rb +41 -0
  21. data/examples/tools/mcp/imcp.rb +15 -0
  22. data/examples/tools/read_file.rb +2 -0
  23. data/examples/tools/run_shell_command.rb +2 -0
  24. data/justfile +3 -25
  25. data/lib/aia/chat_processor_service.rb +0 -3
  26. data/lib/aia/config.rb +542 -436
  27. data/lib/aia/context_manager.rb +3 -8
  28. data/lib/aia/directive_processor.rb +21 -10
  29. data/lib/aia/ruby_llm_adapter.rb +78 -10
  30. data/lib/aia/session.rb +187 -138
  31. data/lib/aia/ui_presenter.rb +7 -5
  32. data/lib/aia/utility.rb +26 -6
  33. data/lib/aia.rb +5 -1
  34. data/main.just +3 -25
  35. metadata +31 -12
  36. data/lib/aia/shell_command_executor.rb +0 -109
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3411bc073c7fadee9f625917b13b06fb11aee61c234f61c57958be1000f261c6
4
- data.tar.gz: 1228b68648536d5c8a4caeb2e6cf6eb91400647c96163b1ab7b6f499b1001d99
3
+ metadata.gz: 0c3bcf7e4888283a1ce66733f83d4d3ec1dc95420a7898462cc571ff6ccb134c
4
+ data.tar.gz: 07733fe911886e651569e1310a078474abf61f7f9cdeb16cfd9154758d936af5
5
5
  SHA512:
6
- metadata.gz: cbd94570f820e5007a7a9ba94b908a0f36407ec9b9f11074283e67374b24261db18309f32c2e079521c93322ce6fe4c2223181d61f3dce99c4a0d39bc5a26e48
7
- data.tar.gz: fdd5647498501805e598c5e7d319193928954e1e2e639e473440c323e49abee46c09aa665130679e790db15c49d81f0ab5bf09deb5ac0f4b6b67eba6a9da625c
6
+ metadata.gz: 94beb5af35dfcd044c7932eca574ab3c5c7a8451c303a99786e84096458927090a1886aefc83e02bf530e5bf6bcee0cba7ea7cbb5177efca1f3042670b8df8ff
7
+ data.tar.gz: 99e1f1ea2a751176d828f9c0cc18224875866977a21b91a8adceff6d8b339ac09632b55b8858ff3b4231e4d0e83180f9adfc340d3e0d76144f284288831317f3
data/.version CHANGED
@@ -1 +1 @@
1
- 0.9.8
1
+ 0.9.10
data/CHANGELOG.md CHANGED
@@ -3,10 +3,25 @@
3
3
 
4
4
  ## Released
5
5
 
6
+ ### [0.9.10] 2025-07-18
7
+ - updated ruby_llm-mcp to version 0.6.1 which solves problems with MCP tools not being installed
8
+
9
+ ### [0.9.9] 2025-07-10
10
+ - refactored the Session and Config classes into more testable method_missing
11
+ - updated the test suire for both the Session and Config classes
12
+ - added support for MCP servers coming into AIA via the shared_tools gem
13
+ - added +RubyLLM::MCP.support_complex_parameters! to patch ruby_llm gem until such time as it supports the more complex optional parameters in tool calls
14
+ - added an examples/tools/mcp directory with 2 MCP client definitions
15
+ - updated to ruby_llm-mcp gem version 0.5.1
16
+ - //model directive now dumps full model details
17
+ - //available_models now has context window size and capabilities for each model returned
18
+
19
+
6
20
  ### [0.9.8] 2025-06-25
7
21
  - fixing an issue with pipelined prompts
8
22
  - now showing the complete modality of the model on the processing line.
9
23
  - changed -p option from prompts_dir to pipeline
24
+ - found problem with simple cov and deep cov w/r/t their reported test coverage; they have problems with heredoc and complex conditionals.
10
25
 
11
26
  ### [0.9.7] 2025-06-20
12
27
 
data/README.md CHANGED
@@ -6,9 +6,15 @@
6
6
 
7
7
  AIA is a command-line utility that facilitates interaction with AI models through dynamic prompt management. It automates the management of pre-compositional prompts and executes generative AI commands with enhanced features including embedded directives, shell integration, embedded Ruby, history management, interactive chat, and prompt workflows.
8
8
 
9
- AIA leverages the [prompt_manager gem](https://github.com/madbomber/prompt_manager) to manage prompts, utilizes the [CLI tool fzf](https://github.com/junegunn/fzf) for prompt selection, and can use the [shared_tools gem](https://github.com/madbomber/shared_tools) which provides a collection of common ready-to-use functions for use with LLMs that support tools.
9
+ AIA leverages the following Ruby gems:
10
10
 
11
- **Wiki**: [Checkout the AIA Wiki](https://github.com/MadBomber/aia/wiki)
11
+ - **[prompt_manager](https://github.com/madbomber/prompt_manager)** to manage prompts,
12
+ - **[ruby_llm](https://rubyllm.com)** to access LLM providers,
13
+ - **[ruby_llm-mcp](https://www.rubyllm-mcp.com)** for Model Context Protocol (MCP) support,
14
+ - and can use the **[shared_tools gem](https://github.com/madbomber/shared_tools)** which provides a collection of common ready-to-use MCP clients and functions for use with LLMs that support tools.
15
+
16
+ **Wiki**: [Checkout the AIA Wiki](https://github.com/MadBomber/aia/wiki)<br />
17
+ **BLOG**: [Series on AIA](https://madbomber.github.io/blog/engineering/AIA-Philosophy/)
12
18
 
13
19
  ## Quick Start
14
20
 
@@ -74,6 +80,7 @@ AIA leverages the [prompt_manager gem](https://github.com/madbomber/prompt_manag
74
80
  - [Prompt Directives](#prompt-directives)
75
81
  - [Configuration Directive Examples](#configuration-directive-examples)
76
82
  - [Dynamic Content Examples](#dynamic-content-examples)
83
+ - [Custom Directive Examples](#custom-directive-examples)
77
84
  - [Shell Integration](#shell-integration)
78
85
  - [Embedded Ruby (ERB)](#embedded-ruby-erb)
79
86
  - [Prompt Sequences](#prompt-sequences)
@@ -112,6 +119,7 @@ AIA leverages the [prompt_manager gem](https://github.com/madbomber/prompt_manag
112
119
  - [Areas for Improvement](#areas-for-improvement)
113
120
  - [Roadmap](#roadmap)
114
121
  - [License](#license)
122
+ - [Articles on AIA](#articles-on-aia)
115
123
 
116
124
  <!-- Tocer[finish]: Auto-generated, don't remove. -->
117
125
 
@@ -358,6 +366,33 @@ Your prompt content here...
358
366
  Analyze the above information and provide insights.
359
367
  ```
360
368
 
369
+ #### Custom Directive Examples
370
+
371
+ You can extend AIA with custom directives by creating Ruby files that define new directive methods:
372
+
373
+ ```ruby
374
+ # examples/directives/ask.rb
375
+ module AIA
376
+ class DirectiveProcessor
377
+ private
378
+ desc "A meta-prompt to LLM making its response available as part of the primary prompt"
379
+ def ask(args, context_manager=nil)
380
+ meta_prompt = args.empty? ? "What is meta-prompting?" : args.join(' ')
381
+ AIA.config.client.chat(meta_prompt)
382
+ end
383
+ end
384
+ end
385
+ ```
386
+
387
+ **Usage:** Use the --tools option to specific a specific directive file or a directory full of files
388
+ ```bash
389
+ # Load custom directive
390
+ aia --tools examples/directives/ask.rb --chat
391
+
392
+ # Use the results of the custom directive as input to a prompt
393
+ //ask gather the latest closing data for the DOW, NASDAQ, and S&P 500
394
+ ```
395
+
361
396
  ### Shell Integration
362
397
 
363
398
  AIA automatically processes shell patterns in prompts:
@@ -499,6 +534,22 @@ aia --tools ~/tools/ --rejected_tools deprecated
499
534
  - API integrations
500
535
  - Data processing utilities
501
536
 
537
+ **MCP Client Examples** (see `examples/tools/mcp/` directory):
538
+
539
+ AIA supports Model Context Protocol (MCP) clients for extended functionality:
540
+
541
+ ```bash
542
+ # GitHub MCP Server (requires: brew install github-mcp-server)
543
+ # Set GITHUB_PERSONAL_ACCESS_TOKEN environment variable
544
+ aia --tools examples/tools/mcp/github_mcp_server.rb --chat
545
+
546
+ # iMCP for macOS (requires: brew install --cask loopwork/tap/iMCP)
547
+ # Provides access to Notes, Calendar, Contacts, etc.
548
+ aia --tools examples/tools/mcp/imcp.rb --chat
549
+ ```
550
+
551
+ These MCP clients require the `ruby_llm-mcp` gem and provide access to external services and data sources through the Model Context Protocol.
552
+
502
553
  **Shared Tools Collection:**
503
554
  AIA can use the [shared_tools gem](https://github.com/madbomber/shared_tools) which provides a curated collection of commonly-used tools (aka functions) via the --require option.
504
555
 
@@ -806,11 +857,17 @@ rake test
806
857
  ## Roadmap
807
858
 
808
859
  - **Enhanced Search**: Restore full-text search within prompt files
809
- - **Model Context Protocol**: Continue integration with ruby_llm gem
810
860
  - **UI Improvements**: Better configuration management for fzf and rg tools
811
- - **Performance**: Optimize prompt loading and processing
812
- - **Security**: Enhanced sandboxing for shell command execution
861
+ - **Logging**: Enhanced logging using Ruby Logger class; integration with RubyLLM and RubyLLM::MCP logging
813
862
 
814
863
  ## License
815
864
 
816
865
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
866
+
867
+ ## Articles on AIA
868
+
869
+ 1. [The Philosophy of Prompt-Driven Development with AIA](https://madbomber.github.io/blog/engineering/AIA-Philosophy/)
870
+ 2. [Mastering AIA's Batch Mode: From Simple Questions to Complex Workflows](https://madbomber.github.io/blog/engineering/AIA-Batch-Mode/)
871
+ 3. [Building AI Workflows: AIA's Prompt Sequencing and Pipelines](https://madbomber.github.io/blog/engineering/AIA-Workflows/)
872
+ 4. [Interactive AI Sessions: Mastering AIA's Chat Mode](https://madbomber.github.io/blog/engineering/AIA-Chat-Mode/)
873
+ 5. [From Dynamic Prompts to Advanced Tool Integration](https://madbomber.github.io/blog/engineering/AIA-Advanced-Tool-Integration/)
data/Rakefile CHANGED
@@ -2,23 +2,31 @@
2
2
 
3
3
  begin
4
4
  require "tocer/rake/register"
5
- rescue LoadError => error
6
- puts error.message
5
+ Tocer::Rake::Register.call
6
+ rescue LoadError, StandardError => e
7
+ warn "Skipping tocer tasks: #{e.message}"
7
8
  end
8
9
 
9
- Tocer::Rake::Register.call
10
-
11
- require 'kramdown/man/task'
12
- Kramdown::Man::Task.new
10
+ begin
11
+ require 'kramdown/man/task'
12
+ Kramdown::Man::Task.new
13
+ rescue LoadError, StandardError => e
14
+ warn "Skipping kramdown man task: #{e.message}"
15
+ end
13
16
 
14
- require "bundler/gem_tasks"
17
+ begin
18
+ require "bundler/gem_tasks"
19
+ rescue LoadError, StandardError => e
20
+ warn "Skipping bundler/gem_tasks: #{e.message}"
21
+ end
15
22
  require "minitest/test_task"
16
23
 
17
24
  Minitest::TestTask.create(:test) do |t|
18
25
  t.libs << "test"
19
26
  t.libs << "lib"
20
27
  t.warning = false
21
- t.test_globs = ["test/aia/*_test.rb", "test/aia_test.rb", "!test/integration/**/*_test.rb"]
28
+ # Include all unit tests under test/, excluding integration tests
29
+ t.test_globs = ["test/**/*_test.rb", "!test/integration/**/*_test.rb"]
22
30
  end
23
31
 
24
32
  Minitest::TestTask.create(:integration) do |t|
@@ -0,0 +1,21 @@
1
+ # ~/examples/directives/ask.rb
2
+ # Desc: An example of how to extend the AIA directives
3
+ # Usage: aia <options> --require path/to/ask.rb
4
+ #
5
+ # A directive is just a private method of the AIA::DirectiveProcessor class. its
6
+ # definition is preceeded by the `desc` method which has a single String parameter
7
+ # that is a description of the directive. This discription is shown with the
8
+ # directive's name in the --chat mode with the //help directive is used.
9
+
10
+ module AIA
11
+ class DirectiveProcessor
12
+ private
13
+ desc "A meta-prompt to LLM making its response available as part of the primary prompt"
14
+ # args is an Array of Strings
15
+ # context_manager is an optional parameter TBD
16
+ def ask(args, context_manager=nil)
17
+ meta_prompt = args.empty? ? "What is meta-prompting?" : args.join(' ')
18
+ AIA.config.client.chat(meta_prompt)
19
+ end
20
+ end
21
+ end
@@ -4,6 +4,8 @@ require "ruby_llm/tool"
4
4
 
5
5
  module Tools
6
6
  class EditFile < RubyLLM::Tool
7
+ def self.name = "edit_file"
8
+
7
9
  description <<~DESCRIPTION
8
10
  Make edits to a text file.
9
11
 
@@ -0,0 +1,70 @@
1
+ # calculator_tool.rb - Simple custom tool example
2
+ require 'ruby_llm/tool'
3
+
4
+ module Tools
5
+ class Calculator < RubyLLM::Tool
6
+ def self.name = "calculator"
7
+
8
+ description <<~DESCRIPTION
9
+ Perform advanced mathematical calculations with comprehensive error handling and validation.
10
+ This tool supports basic arithmetic operations, parentheses, and common mathematical functions.
11
+ It provides safe evaluation of mathematical expressions without executing arbitrary code,
12
+ making it suitable for use in AI-assisted calculations where security is important.
13
+ The tool returns formatted results with configurable precision and helpful error messages
14
+ when invalid expressions are provided.
15
+ DESCRIPTION
16
+
17
+ param :expression,
18
+ desc: <<~DESC,
19
+ Mathematical expression to evaluate using standard arithmetic operators and parentheses.
20
+ Supported operations include: addition (+), subtraction (-), multiplication (*), division (/),
21
+ and parentheses for grouping. Examples: '2 + 2', '(10 * 5) / 2', '15.5 - 3.2'.
22
+ Only numeric characters, operators, parentheses, decimal points, and spaces are allowed
23
+ for security reasons. Complex mathematical functions are not supported in this version.
24
+ DESC
25
+ type: :string,
26
+ required: true
27
+
28
+ param :precision,
29
+ desc: <<~DESC,
30
+ Number of decimal places to display in the result. Must be a non-negative integer.
31
+ Set to 0 for whole numbers only, or higher values for more precise decimal results.
32
+ Default is 2 decimal places, which works well for most financial and general calculations.
33
+ Maximum precision is limited to 10 decimal places to prevent excessive output.
34
+ DESC
35
+ type: :integer,
36
+ default: 2
37
+
38
+ def execute(expression:, precision: 2)
39
+ begin
40
+ # Use safe evaluation instead of raw eval
41
+ result = safe_eval(expression)
42
+ formatted_result = result.round(precision)
43
+
44
+ {
45
+ success: true,
46
+ result: formatted_result,
47
+ expression: expression,
48
+ precision: precision
49
+ }
50
+ rescue => e
51
+ {
52
+ success: false,
53
+ error: "Invalid expression: #{e.message}",
54
+ expression: expression,
55
+ suggestion: "Try expressions like '2 + 2' or '10 * 5'"
56
+ }
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def safe_eval(expression)
63
+ # Implement safe mathematical evaluation
64
+ # This is a simplified example - use a proper math parser in production
65
+ allowed_chars = /\A[0-9+\-*\/\(\)\.\s]+\z/
66
+ raise "Invalid characters in expression" unless expression.match?(allowed_chars)
67
+ eval(expression)
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,89 @@
1
+ # composite_analysis_tool.rb - Tool that uses other tools
2
+ require 'ruby_llm/tool'
3
+
4
+ module Tools
5
+ class CompositeAnalysis < RubyLLM::Tool
6
+ def self.name = "composite_analysis"
7
+
8
+ description <<~DESCRIPTION
9
+ Perform comprehensive multi-stage data analysis by orchestrating multiple specialized tools
10
+ to provide complete insights from various data sources. This composite tool automatically
11
+ determines the appropriate data fetching method (web scraping for URLs, file reading for
12
+ local paths), analyzes data structure and content, generates statistical insights,
13
+ and suggests appropriate visualizations based on the data characteristics.
14
+ Ideal for exploratory data analysis workflows where you need a complete picture
15
+ from initial data loading through final insights.
16
+ DESCRIPTION
17
+
18
+ param :data_source,
19
+ desc: <<~DESC,
20
+ Primary data source to analyze. Can be either a local file path or a web URL.
21
+ For files: Use relative or absolute paths to CSV, JSON, XML, or text files.
22
+ For URLs: Use complete HTTP/HTTPS URLs to accessible data endpoints or web pages.
23
+ The tool automatically detects the source type and uses appropriate fetching methods.
24
+ Examples: './data/sales.csv', '/home/user/data.json', 'https://api.example.com/data'
25
+ DESC
26
+ type: :string,
27
+ required: true
28
+
29
+ def execute(data_source:)
30
+ results = {}
31
+
32
+ begin
33
+ # Step 1: Fetch data using appropriate tool
34
+ if data_source.start_with?('http')
35
+ results[:data] = fetch_web_data(data_source)
36
+ else
37
+ results[:data] = read_file_data(data_source)
38
+ end
39
+
40
+ # Step 2: Analyze data structure
41
+ results[:structure] = analyze_data_structure(results[:data])
42
+
43
+ # Step 3: Generate insights
44
+ results[:insights] = generate_insights(results[:data], results[:structure])
45
+
46
+ # Step 4: Create visualizations if applicable
47
+ if results[:structure][:numeric_columns]&.any?
48
+ results[:visualizations] = suggest_visualizations(results[:structure])
49
+ end
50
+
51
+ {
52
+ success: true,
53
+ analysis: results,
54
+ data_source: data_source,
55
+ analyzed_at: Time.now.iso8601
56
+ }
57
+ rescue => e
58
+ {
59
+ success: false,
60
+ error: e.message,
61
+ data_source: data_source,
62
+ partial_results: results
63
+ }
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def fetch_web_data(url)
70
+ # TODO: Use shared web tools or custom HTTP client
71
+ end
72
+
73
+ def read_file_data(file_path)
74
+ # TODO: Use shared file tools
75
+ end
76
+
77
+ def analyze_data_structure(data)
78
+ # TODO: Implementation for data structure analysis
79
+ end
80
+
81
+ def generate_insights(data, structure)
82
+ # TODO: Implementation for insight generation
83
+ end
84
+
85
+ def suggest_visualizations(structure)
86
+ # TODO:Implementation for visualization suggestions
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,128 @@
1
+ # data_science_kit.rb - Analytics and ML tools
2
+ require 'ruby_llm/tool'
3
+
4
+ module Tools
5
+ class DataScienceKit < RubyLLM::Tool
6
+ def self.name = "data_science_kit"
7
+
8
+ description <<~DESCRIPTION
9
+ Comprehensive data science and analytics toolkit for performing statistical analysis,
10
+ machine learning tasks, and data exploration on various data sources. This tool provides
11
+ a unified interface for common data science operations including descriptive statistics,
12
+ correlation analysis, time series analysis, clustering algorithms, and predictive modeling.
13
+ It automatically handles data loading, validation, preprocessing, and result formatting.
14
+ Supports multiple data formats and provides detailed analysis results with visualizations
15
+ recommendations and statistical significance testing where applicable.
16
+ DESCRIPTION
17
+
18
+ param :analysis_type,
19
+ desc: <<~DESC,
20
+ Type of data science analysis to perform:
21
+ - 'statistical_summary': Descriptive statistics, distributions, outlier detection
22
+ - 'correlation_analysis': Correlation matrices, feature relationships, dependency analysis
23
+ - 'time_series': Trend analysis, seasonality detection, forecasting
24
+ - 'clustering': K-means, hierarchical clustering, cluster analysis
25
+ - 'prediction': Regression analysis, classification, predictive modeling
26
+ Each analysis type requires specific data formats and optional parameters.
27
+ DESC
28
+ type: :string,
29
+ required: true,
30
+ enum: ["statistical_summary", "correlation_analysis", "time_series", "clustering", "prediction"]
31
+
32
+ param :data_source,
33
+ desc: <<~DESC,
34
+ Data source specification for analysis. Can be:
35
+ - File path: Relative or absolute path to CSV, JSON, Excel, or Parquet files
36
+ - Database query: SQL SELECT statement for database-sourced data
37
+ - API endpoint: HTTP URL for REST API data sources
38
+ The tool automatically detects the format and applies appropriate parsing.
39
+ Examples: './sales_data.csv', 'SELECT * FROM transactions', 'https://api.company.com/data'
40
+ DESC
41
+ type: :string,
42
+ required: true
43
+
44
+ param :parameters,
45
+ desc: <<~DESC,
46
+ Hash of analysis-specific parameters and configuration options:
47
+ - statistical_summary: confidence_level, include_quartiles, outlier_method
48
+ - correlation_analysis: method (pearson/spearman), significance_level
49
+ - time_series: date_column, value_column, frequency, forecast_periods
50
+ - clustering: n_clusters, algorithm (kmeans/hierarchical), distance_metric
51
+ - prediction: target_column, feature_columns, model_type, validation_split
52
+ Default empty hash uses standard parameters for each analysis type.
53
+ DESC
54
+ type: :hash,
55
+ default: {}
56
+
57
+ def execute(analysis_type:, data_source:, parameters: {})
58
+ begin
59
+ # Load and validate data
60
+ data = load_data(data_source)
61
+ validate_data_for_analysis(data, analysis_type)
62
+
63
+ # Perform analysis
64
+ result = case analysis_type
65
+ when "statistical_summary"
66
+ generate_statistical_summary(data, parameters)
67
+ when "correlation_analysis"
68
+ perform_correlation_analysis(data, parameters)
69
+ when "time_series"
70
+ analyze_time_series(data, parameters)
71
+ when "clustering"
72
+ perform_clustering(data, parameters)
73
+ when "prediction"
74
+ generate_predictions(data, parameters)
75
+ end
76
+
77
+ {
78
+ success: true,
79
+ analysis_type: analysis_type,
80
+ result: result,
81
+ data_summary: summarize_data(data),
82
+ analyzed_at: Time.now.iso8601
83
+ }
84
+ rescue => e
85
+ {
86
+ success: false,
87
+ error: e.message,
88
+ analysis_type: analysis_type,
89
+ data_source: data_source
90
+ }
91
+ end
92
+ end
93
+
94
+ private
95
+
96
+ def load_data(source)
97
+ # TODO: Implementation for data loading from various sources
98
+ end
99
+
100
+ def validate_data_for_analysis(data, analysis_type)
101
+ # TODO: Implementation for data validation
102
+ end
103
+
104
+ def generate_statistical_summary(data, parameters)
105
+ # TODO: Implementation for statistical summary
106
+ end
107
+
108
+ def perform_correlation_analysis(data, parameters)
109
+ # TODO: Implementation for correlation analysis
110
+ end
111
+
112
+ def analyze_time_series(data, parameters)
113
+ # TODO: Implementation for time series analysis
114
+ end
115
+
116
+ def perform_clustering(data, parameters)
117
+ # TODO: Implementation for clustering
118
+ end
119
+
120
+ def generate_predictions(data, parameters)
121
+ # TODO: Implementation for prediction
122
+ end
123
+
124
+ def summarize_data(data)
125
+ # TODO: Implementation for data summary
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,100 @@
1
+ # database_query_tool.rb - Database interaction example
2
+ require 'ruby_llm/tool'
3
+ require 'sequel'
4
+
5
+ module Tools
6
+ class DatabaseQuery < RubyLLM::Tool
7
+ def self.name = "database_query"
8
+
9
+ description <<~DESCRIPTION
10
+ Execute safe, read-only database queries with automatic connection management and security controls.
11
+ This tool is designed for secure data retrieval operations only, restricting access to SELECT statements
12
+ to prevent any data modification. It includes automatic connection pooling, query result limiting,
13
+ and comprehensive error handling. The tool supports multiple database configurations through
14
+ environment variables and ensures all connections are properly closed after use.
15
+ Perfect for AI-assisted data analysis and reporting workflows where read-only access is required.
16
+ DESCRIPTION
17
+
18
+ param :query,
19
+ desc: <<~DESC,
20
+ SQL SELECT query to execute against the database. Only SELECT statements are permitted
21
+ for security reasons - INSERT, UPDATE, DELETE, and DDL statements will be rejected.
22
+ The query should be well-formed SQL appropriate for the target database system.
23
+ Examples: 'SELECT * FROM users WHERE active = true', 'SELECT COUNT(*) FROM orders'.
24
+ Table and column names should match the database schema exactly.
25
+ DESC
26
+ type: :string,
27
+ required: true
28
+
29
+ param :database,
30
+ desc: <<~DESC,
31
+ Database configuration name to use for the connection. This corresponds to environment
32
+ variables like DATABASE_URL, STAGING_DATABASE_URL, etc. The tool will look for
33
+ an environment variable named {DATABASE_NAME}_DATABASE_URL (uppercase).
34
+ Default is 'default' which looks for DEFAULT_DATABASE_URL environment variable.
35
+ Common values: 'default', 'staging', 'analytics', 'reporting'.
36
+ DESC
37
+ type: :string,
38
+ default: "default"
39
+
40
+ param :limit,
41
+ desc: <<~DESC,
42
+ Maximum number of rows to return from the query to prevent excessive memory usage
43
+ and long response times. The tool automatically adds a LIMIT clause if one is not
44
+ present in the original query. Set to a reasonable value based on expected data size.
45
+ Minimum: 1, Maximum: 10000, Default: 100. For large datasets, consider using
46
+ pagination or more specific WHERE clauses.
47
+ DESC
48
+ type: :integer,
49
+ default: 100
50
+
51
+ def execute(query:, database: "default", limit: 100)
52
+ begin
53
+ # Security: Only allow SELECT queries
54
+ normalized_query = query.strip.downcase
55
+ unless normalized_query.start_with?('select')
56
+ raise "Only SELECT queries are allowed for security"
57
+ end
58
+
59
+ db = connect_to_database(database)
60
+ limited_query = add_limit_to_query(query, limit)
61
+
62
+ results = db[limited_query].all
63
+
64
+ {
65
+ success: true,
66
+ query: limited_query,
67
+ row_count: results.length,
68
+ data: results,
69
+ database: database,
70
+ executed_at: Time.now.iso8601
71
+ }
72
+ rescue => e
73
+ {
74
+ success: false,
75
+ error: e.message,
76
+ query: query,
77
+ database: database
78
+ }
79
+ ensure
80
+ db&.disconnect
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def connect_to_database(database_name)
87
+ # Implementation depends on your database setup
88
+ connection_string = ENV["#{database_name.upcase}_DATABASE_URL"]
89
+ raise "Database connection not configured for #{database_name}" unless connection_string
90
+
91
+ Sequel.connect(connection_string)
92
+ end
93
+
94
+ def add_limit_to_query(query, limit)
95
+ # Add LIMIT clause if not present
96
+ query += " LIMIT #{limit}" unless query.downcase.include?('limit')
97
+ query
98
+ end
99
+ end
100
+ end