Commit 7d519da

mo khan <mo@mokhan.ca>
2026-01-21 20:12:46
feat: add json schema validation for tool call args
1 parent df5321f
lib/elelem/tool.rb
@@ -11,21 +11,38 @@ module Elelem
       @required = required
       @aliases = aliases
       @fn = fn
+      @schema = JSONSchemer.schema(schema_hash)
     end
 
     def call(args)
       @fn.call(args)
     end
 
+    def validate(args)
+      @schema.validate(args || {}).map do |error|
+        error["error"]
+      end
+    end
+
     def to_h
       {
         type: "function",
         function: {
           name: name,
           description: description,
-          parameters: { type: "object", properties: params, required: required }
+          parameters: schema_hash
         }
       }
     end
+
+    private
+
+    def schema_hash
+      {
+        type: "object",
+        properties: params,
+        required: required
+      }
+    end
   end
 end
lib/elelem/toolbox.rb
@@ -34,8 +34,8 @@ module Elelem
       tool = tools[name]
       return { error: "unknown tool: #{name}" } unless tool
 
-      missing = tool.required - (args&.keys || [])
-      return { error: "missing required args: #{missing.join(', ')}" } if missing.any?
+      errors = tool.validate(args)
+      return { error: errors.join(", ") } if errors.any?
 
       @hooks[:before][name].each { |h| h.call(args) }
       result = tool.call(args)
lib/elelem.rb
@@ -3,6 +3,7 @@
 require "date"
 require "fileutils"
 require "json"
+require "json_schemer"
 require "open3"
 require "pathname"
 require "reline"
elelem.gemspec
@@ -51,6 +51,7 @@ Gem::Specification.new do |spec|
   spec.add_dependency "date", "~> 3.0"
   spec.add_dependency "fileutils", "~> 1.0"
   spec.add_dependency "json", "~> 2.0"
+  spec.add_dependency "json_schemer", "~> 2.0"
   spec.add_dependency "net-hippie", "~> 1.0"
   spec.add_dependency "open3", "~> 0.1"
   spec.add_dependency "optparse", "~> 0.1"
Gemfile.lock
@@ -5,6 +5,7 @@ PATH
       date (~> 3.0)
       fileutils (~> 1.0)
       json (~> 2.0)
+      json_schemer (~> 2.0)
       net-hippie (~> 1.0)
       open3 (~> 0.1)
       optparse (~> 0.1)
@@ -18,16 +19,23 @@ GEM
   remote: https://rubygems.org/
   specs:
     base64 (0.3.0)
+    bigdecimal (4.0.1)
     date (3.5.1)
     diff-lcs (1.6.2)
     erb (6.0.1)
     fileutils (1.8.0)
+    hana (1.3.7)
     io-console (0.8.2)
     irb (1.16.0)
       pp (>= 0.6.0)
       rdoc (>= 4.0.0)
       reline (>= 0.4.2)
     json (2.18.0)
+    json_schemer (2.5.0)
+      bigdecimal
+      hana (~> 1.3)
+      regexp_parser (~> 2.0)
+      simpleidn (~> 0.2)
     logger (1.7.0)
     net-hippie (1.4.0)
       base64 (~> 0.1)
@@ -52,6 +60,7 @@ GEM
       erb
       psych (>= 4.0.0)
       tsort
+    regexp_parser (2.11.3)
     reline (0.6.3)
       io-console (~> 0.5)
     rspec (3.13.2)
@@ -67,6 +76,7 @@ GEM
       diff-lcs (>= 1.2.0, < 2.0)
       rspec-support (~> 3.13.0)
     rspec-support (3.13.6)
+    simpleidn (0.2.3)
     stringio (3.2.0)
     tempfile (0.3.1)
     tsort (0.2.0)