Module: Insika::Tools::AgentEnum

Defined in:
lib/insika/tools/agent_enum.rb

Overview

Names the parent's subagent allowlist INSIDE the delegation tools' schema.

The allowlist is per-turn data (profile.subagents), while a RubyLLM tool's schema is class-level, so the enum has to be injected per instance. Without it the model is told only that agent "must be one this agent may spawn" and is left to guess the ids; with it, the valid values are part of the contract the provider sees.

Tolerant by construction: JSON-schema hashes reach us with symbol keys (the class-level params(...) form) or string keys (anything that round-tripped through JSON), and a shape we do not recognize is returned UNTOUCHED — a schema we failed to annotate still works, one we corrupted would break every delegation.

Class Method Summary collapse

Class Method Details

.deep_dup(value) ⇒ Object



59
60
61
62
63
64
65
# File 'lib/insika/tools/agent_enum.rb', line 59

def deep_dup(value)
  case value
  when Hash  then value.each_with_object({}) { |(k, v), acc| acc[k] = deep_dup(v) }
  when Array then value.map { |v| deep_dup(v) }
  else value
  end
end

.fetch(node, key) ⇒ Object



47
48
49
50
51
# File 'lib/insika/tools/agent_enum.rb', line 47

def fetch(node, key)
  return nil unless node.is_a?(Hash)

  node[key] || node[key.to_s]
end

.inject(schema, allowed, path:) ⇒ Object

schema: the tool's JSON schema · allowed: [String] · path: the property chain leading to the agent property, e.g. %i or %i[tasks agent]. -> a COPY with enum set, or the original when there is nothing to do.



24
25
26
27
28
29
30
31
32
33
# File 'lib/insika/tools/agent_enum.rb', line 24

def inject(schema, allowed, path:)
  return schema if schema.nil? || Array(allowed).empty?

  copy = deep_dup(schema)
  target = resolve(copy, path)
  return schema if target.nil?

  target[key_for(target, :enum)] = Array(allowed).map(&:to_s)
  copy
end

.key_for(hash, key) ⇒ Object

Keep the hash's own key convention instead of mixing symbols into a string-keyed schema.



55
56
57
# File 'lib/insika/tools/agent_enum.rb', line 55

def key_for(hash, key)
  hash.keys.any? { |k| k.is_a?(String) } ? key.to_s : key
end

.resolve(node, path) ⇒ Object

Walks properties (and items for an array) down to the named property.



36
37
38
39
40
41
42
43
44
45
# File 'lib/insika/tools/agent_enum.rb', line 36

def resolve(node, path)
  path.reduce(node) do |current, segment|
    props = fetch(current, :properties)
    props = fetch(fetch(current, :items) || {}, :properties) if props.nil?
    prop = props && (props[segment] || props[segment.to_s])
    return nil unless prop.is_a?(Hash)

    prop
  end
end