Class: Pikuri::Tool
- Inherits:
-
Object
- Object
- Pikuri::Tool
- Defined in:
- lib/pikuri/tool.rb,
lib/pikuri/tool/bash.rb,
lib/pikuri/tool/edit.rb,
lib/pikuri/tool/glob.rb,
lib/pikuri/tool/grep.rb,
lib/pikuri/tool/read.rb,
lib/pikuri/tool/fetch.rb,
lib/pikuri/tool/skill.rb,
lib/pikuri/tool/write.rb,
lib/pikuri/tool/confirmer.rb,
lib/pikuri/tool/sub_agent.rb,
lib/pikuri/tool/workspace.rb,
lib/pikuri/tool/calculator.rb,
lib/pikuri/tool/parameters.rb,
lib/pikuri/tool/search/exa.rb,
lib/pikuri/tool/web_scrape.rb,
lib/pikuri/tool/web_search.rb,
lib/pikuri/tool/scraper/pdf.rb,
lib/pikuri/tool/scraper/html.rb,
lib/pikuri/tool/search/brave.rb,
lib/pikuri/tool/search/result.rb,
lib/pikuri/tool/skill_catalog.rb,
lib/pikuri/tool/scraper/simple.rb,
lib/pikuri/tool/search/engines.rb,
lib/pikuri/tool/search/duckduckgo.rb,
lib/pikuri/tool/scraper/fetch_error.rb,
lib/pikuri/tool/search/rate_limiter.rb
Overview
Loaded after Tool itself is defined; the class Tool reopening below assumes that order.
Defined Under Namespace
Modules: Calculator, Fetch, Scraper, Search, WebScrape, WebSearch Classes: Bash, Confirmer, Edit, Glob, Grep, Parameters, Read, Skill, SkillCatalog, SubAgent, Workspace, Write
Constant Summary collapse
- FETCH =
Verbatim URL download tool. Thin wrapper over Pikuri::Tool::Fetch.fetch that exposes it to the agent loop in OpenAI tool-call shape. Use for raw textual payloads (JSON APIs, CSV files,
robots.txt, source files); use WEB_SCRAPE for rendered web pages or PDFs where readability extraction makes the result usable. new( name: 'fetch', description: <<~DESC, Downloads the given URL and returns its body verbatim. Usage: - Use for raw textual payloads: JSON APIs, CSV files, robots.txt, sitemaps, source files — anywhere a rendering pass would corrupt the data. - For rendered HTML pages or PDFs, use web_scrape — it extracts readable content; fetch returns the raw HTML/PDF bytes unchanged. - Accepts text/* and common textual application/* types (JSON, XML, JS, XHTML, RSS, Atom). Refuses PDFs, images, and other binaries. DESC parameters: Parameters.build { |p| p.required_string :url, 'Absolute URL to download, including the scheme, ' \ 'e.g. "https://example.com/data.json".' p.optional_integer :max_chars, 'Maximum number of characters of the body to ' \ 'return. Defaults to 5000; hard-capped at ' \ '100000. When the body is longer than this, ' \ 'output is cut and a marker reports the full ' \ 'length.' }, execute: ->(url:, max_chars: Fetch::DEFAULT_MAX_CHARS) { Fetch.fetch(url, max_chars: max_chars) } )
- CALCULATOR =
Arithmetic-evaluation tool backed by Pikuri::Tool::Calculator.calculate. Accepts Python-flavored operator syntax (+, -, *, /, ** for exponentiation, %, parentheses, decimals) so the model can emit the syntax it already knows.
new( name: 'calculator', description: <<~DESC, Evaluates a basic arithmetic expression and returns the numeric result. Usage: - Use this for any arithmetic beyond simple mental math — do not eyeball multi-digit work. - Operators supported: +, -, *, /, ** (exponentiation), %, parentheses, decimal numbers. - Decimal results are rounded to 3 places; integer results are exact. - Failures (parse error, division by zero) come back as "Error: ..." — read the message and re-call with a corrected expression. DESC parameters: Parameters.build { |p| p.required_string :expression, 'Arithmetic expression to evaluate, e.g. ' \ '"155 / (58 * 1000.0 / 3600)" or "2**10".' }, execute: ->(expression:) { Calculator.calculate(expression) } )
- WEB_SCRAPE =
Webpage download + Markdown conversion tool. Thin wrapper over Pikuri::Tool::WebScrape.visit that exposes it to the agent loop in OpenAI tool-call shape.
new( name: 'web_scrape', description: <<~DESC, Scrapes the rendered webpage, PDF, or text file at the given URL and returns its main content as Markdown. Usage: - Use for HTML pages or PDFs where you want readable content — readability extraction strips nav, sidebars, and boilerplate. - For raw textual payloads (JSON, CSV, robots.txt, source files), use fetch instead — it returns bytes verbatim, while web_scrape would corrupt them with a Markdown pass. - A Single Page App may return very little or no content. Do NOT retry with a larger max_chars; try a different URL instead. DESC parameters: Parameters.build { |p| p.required_string :url, 'Absolute URL of the webpage to scrape, including ' \ 'the scheme, e.g. "https://example.com/article".' p.optional_integer :max_chars, 'Maximum number of characters of Markdown to ' \ 'return. Defaults to 20000; hard-capped at ' \ '100000. When the page is longer than this, ' \ 'output is cut and a marker reports the full ' \ 'length.' }, execute: ->(url:, max_chars: WebScrape::DEFAULT_MAX_CHARS) { WebScrape.visit(url, max_chars: max_chars) } )
- WEB_SEARCH =
Web-search tool exposed to the agent loop in OpenAI tool-call shape. Calls Search::Engines.search, which cascades through whichever providers are configured (DuckDuckGo always, Brave when its API key is present) in random order, falling back on temporary-unavailability errors. Providers return structured Search::Result rows;
Engines.searchrenders the winning provider’s rows into the smolagents-style Markdown shape the LLM sees, so the format stays stable regardless of which provider ran. new( name: 'web_search', description: <<~DESC, Searches the web for a query and returns the top results as a Markdown list of titles, URLs, and short snippets. Usage: - Use this to find candidate URLs, then call web_scrape on the most promising one(s) for full content. Snippets alone rarely answer a question. DESC parameters: Parameters.build { |p| p.required_string :query, 'The search query, e.g. "BigDecimal precision Ruby".' p.optional_integer :max_results, 'Maximum number of result entries to return. ' \ 'Defaults to 10; most providers cap this at 20.' }, execute: ->(query:, max_results: 10) { Search::Engines.search(query, max_results: max_results) } )
Instance Attribute Summary collapse
-
#description ⇒ String
readonly
Human-readable description used by the LLM to decide when to call the tool.
-
#execute ⇒ Proc
readonly
Callable invoked once arguments have been validated; receives validated keyword arguments and returns a
Stringobservation. -
#name ⇒ String
readonly
Function name advertised to the LLM.
-
#parameters ⇒ Tool::Parameters
readonly
Declared schema; validates incoming arguments and serializes to the JSON Schema shape advertised to the LLM.
Instance Method Summary collapse
- #initialize(name:, description:, parameters:, execute:) ⇒ Tool constructor
-
#run(args) ⇒ String
Validate
argsagainst #parameters and forward them as keyword arguments to #execute. -
#to_ruby_llm_tool ⇒ Class
Build a synthetic
RubyLLM::Toolsubclass that wraps this Tool.
Constructor Details
#initialize(name:, description:, parameters:, execute:) ⇒ Tool
74 75 76 77 78 79 |
# File 'lib/pikuri/tool.rb', line 74 def initialize(name:, description:, parameters:, execute:) @name = name @description = description @parameters = parameters @execute = execute end |
Instance Attribute Details
#description ⇒ String (readonly)
Returns human-readable description used by the LLM to decide when to call the tool.
53 54 55 |
# File 'lib/pikuri/tool.rb', line 53 def description @description end |
#execute ⇒ Proc (readonly)
Returns callable invoked once arguments have been validated; receives validated keyword arguments and returns a String observation.
63 64 65 |
# File 'lib/pikuri/tool.rb', line 63 def execute @execute end |
#name ⇒ String (readonly)
Returns function name advertised to the LLM.
49 50 51 |
# File 'lib/pikuri/tool.rb', line 49 def name @name end |
#parameters ⇒ Tool::Parameters (readonly)
Returns declared schema; validates incoming arguments and serializes to the JSON Schema shape advertised to the LLM.
58 59 60 |
# File 'lib/pikuri/tool.rb', line 58 def parameters @parameters end |
Instance Method Details
#run(args) ⇒ String
Validate args against #parameters and forward them as keyword arguments to #execute. Validation failures are caught and rendered as “Error: <message>” Strings so the agent loop can feed them back to the LLM as the next observation; everything else bubbles up.
89 90 91 92 93 94 |
# File 'lib/pikuri/tool.rb', line 89 def run(args) validated = @parameters.validate(args) @execute.call(**validated) rescue Tool::Parameters::ValidationError => e "Error: #{e.}" end |
#to_ruby_llm_tool ⇒ Class
Build a synthetic RubyLLM::Tool subclass that wraps this Tool. The subclass is what RubyLLM::Chat#with_tool accepts: ruby_llm instantiates it (tool.new) and routes tool calls through instance.call(args), which lands in #execute(**args) and delegates back to #run on this instance.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
# File 'lib/pikuri/tool.rb', line 103 def to_ruby_llm_tool pikuri_tool = self schema = @parameters.to_h tool_name = @name tool_desc = @description Class.new(RubyLLM::Tool) do description(tool_desc) params(schema) define_singleton_method(:name) { tool_name } define_method(:execute) { |**args| pikuri_tool.run(args) } end end |