Module: Legate::GlobalDefinitionRegistry

Defined in:
lib/legate/global_definition_registry.rb

Overview

In-memory registry for AgentDefinition instances. Serves as both the runtime definition registry (used by agents) and as a drop-in replacement for the Redis-backed DefinitionStore used by the Web UI.

Internal structure:

@registry = { name_symbol => { definition: AgentDefinition, metadata: {} } }

The ‘register`, `find`, `all`, and `clear!` methods maintain backward compatibility with the original API. The new methods (`get_definition`, `save_definition`, `update_definition`, `delete_definition`, `list_definitions`, `check_connection`, `definition_exists?`) provide the DefinitionStore interface the Web UI routes expect.

Constant Summary collapse

DEFINITION_FIELDS =

Checks if a key corresponds to a field on AgentDefinition.

%i[
  description instruction tool_names model_name temperature
  fallback_mode mcp_servers webhook_enabled webhook_secret
  agent_type planning_strategy sub_agent_names output_key
  sequential_sub_agent_names parallel_sub_agent_names loop_sub_agent_names
  delegation_targets loop_max_iterations loop_condition_state_key
  loop_condition_expected_value auth_credential_names auth_url_mappings
  auth_scheme_assignments auth_credential_assignments
].freeze
WEB_TO_DEFINITION_MAP =

Web UI uses different field names; map them to definition ivars.

{
  tools: :tool_names,
  model: :model_name,
  mcp_servers_json: :mcp_servers
}.freeze

Class Method Summary collapse

Class Method Details

.allHash{Symbol => Legate::AgentDefinition}

Returns the current registry hash mapping names to AgentDefinition objects.

Returns:



71
72
73
74
75
# File 'lib/legate/global_definition_registry.rb', line 71

def self.all
  @mutex.synchronize do
    @registry.transform_values { |entry| entry[:definition] }.dup
  end
end

.check_connectionBoolean

Always returns true for the in-memory store (no external connection to check).

Returns:

  • (Boolean)

    true



198
199
200
# File 'lib/legate/global_definition_registry.rb', line 198

def self.check_connection
  true
end

.clear!Object

Clears the registry (primarily for testing).



64
65
66
67
# File 'lib/legate/global_definition_registry.rb', line 64

def self.clear!
  @mutex.synchronize { @registry = {} }
  Legate.logger.debug('GlobalDefinitionRegistry: Cleared.')
end

.definition_exists?(name) ⇒ Boolean

Checks if an agent definition with the given name exists.

Parameters:

  • name (String, Symbol)

    The agent name.

Returns:

  • (Boolean)


205
206
207
208
209
210
# File 'lib/legate/global_definition_registry.rb', line 205

def self.definition_exists?(name)
  sym_name = normalize_name(name)
  return false unless sym_name

  @mutex.synchronize { @registry.key?(sym_name) }
end

.delete_definition(name) ⇒ Boolean

Deletes an agent definition from the registry.

Parameters:

  • name (String, Symbol)

    The agent name.

Returns:

  • (Boolean)

    true if deleted (or didn’t exist), false on error.



178
179
180
181
182
183
184
185
# File 'lib/legate/global_definition_registry.rb', line 178

def self.delete_definition(name)
  sym_name = normalize_name(name)
  return true unless sym_name # Nothing to delete

  @mutex.synchronize { @registry.delete(sym_name) }
  Legate.logger.info("GlobalDefinitionRegistry: Deleted definition for :#{sym_name}")
  true
end

.find(name) ⇒ Legate::AgentDefinition?

Finds an AgentDefinition instance by name.

Parameters:

  • name (Symbol)

    The name of the agent definition.

Returns:



54
55
56
57
58
59
60
61
# File 'lib/legate/global_definition_registry.rb', line 54

def self.find(name)
  unless name.is_a?(Symbol)
    Legate.logger.warn("GlobalDefinitionRegistry: Find called with non-symbol key: #{name.inspect}")
    return nil
  end
  entry = @mutex.synchronize { @registry[name] }
  entry&.[](:definition)
end

.get_definition(name) ⇒ Hash?

Retrieves a single agent definition as a hash with Web UI field names.

Field name mapping from AgentDefinition#to_h:

:tool_names  -> :tools       (Array of Symbols)
:model_name  -> :model       (Symbol or nil)
:mcp_servers -> :mcp_servers_json (JSON String)

Metadata fields (e.g. :persistent_status, :last_run_at) are merged in.

Parameters:

  • name (String, Symbol)

    The agent name.

Returns:

  • (Hash, nil)

    A hash with symbol keys in Web UI format, or nil if not found.



92
93
94
95
96
97
98
99
100
# File 'lib/legate/global_definition_registry.rb', line 92

def self.get_definition(name)
  sym_name = normalize_name(name)
  return nil unless sym_name

  entry = @mutex.synchronize { @registry[sym_name] }
  return nil unless entry

  build_web_hash(entry)
end

.list_definitionsArray<Hash>

Returns an array of hashes, each in the same format as get_definition output.

Returns:

  • (Array<Hash>)


189
190
191
192
193
194
# File 'lib/legate/global_definition_registry.rb', line 189

def self.list_definitions
  entries = @mutex.synchronize { @registry.dup }
  entries.map { |_name, entry| build_web_hash(entry) }
         .compact
         .sort_by { |d| d[:name].to_s }
end

.register(definition) ⇒ Boolean

Registers an AgentDefinition instance.

Parameters:

Returns:

  • (Boolean)

    true if registered successfully, false otherwise.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/legate/global_definition_registry.rb', line 30

def self.register(definition)
  unless definition.is_a?(Legate::AgentDefinition) && definition.name.is_a?(Symbol)
    Legate.logger.error("GlobalDefinitionRegistry: Invalid object passed to register: #{definition.inspect}")
    return false
  end

  name = definition.name
  @mutex.synchronize do
    if @registry.key?(name)
      Legate.logger.warn("GlobalDefinitionRegistry: Overwriting existing definition for agent :#{name}")
      # Preserve existing metadata when re-registering
       = @registry[name][:metadata] || {}
      @registry[name] = { definition: definition, metadata:  }
    else
      @registry[name] = { definition: definition, metadata: {} }
    end
  end
  Legate.logger.debug("GlobalDefinitionRegistry: Registered definition for :#{name}")
  true
end

.save_definition(*args, **kwargs) ⇒ Boolean

Saves a new agent definition. Supports two call signatures:

  1. Keyword splat (from the create form):

    save_definition(name:, description:, tools:, model:, ...)
    
  2. Two positional args (from the duplicate route):

    save_definition(new_name, definition_hash)
    

Returns:

  • (Boolean)

    true on success.



111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/legate/global_definition_registry.rb', line 111

def self.save_definition(*args, **kwargs)
  if args.length == 2
    # Positional form: save_definition(new_name, definition_hash)
    new_name = args[0]
    definition_hash = args[1]
    _save_from_hash(new_name, definition_hash)
  elsif args.empty? && !kwargs.empty?
    # Keyword form: save_definition(name:, description:, tools:, model:, ...)
    _save_from_keywords(**kwargs)
  else
    raise ArgumentError, 'save_definition expects either (name, hash) or keyword arguments'
  end
end

.update_definition(name, updates) ⇒ Boolean

Updates specific fields of an existing agent definition’s metadata. This is used for things like persistent_status, last_run_at, and also for updating definition fields via the Web UI edit forms.

Parameters:

  • name (String, Symbol)

    The agent name.

  • updates (Hash)

    A hash of field names to new values.

Returns:

  • (Boolean)

    true if the agent was found and updated, false otherwise.



132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/legate/global_definition_registry.rb', line 132

def self.update_definition(name, updates)
  sym_name = normalize_name(name)
  return false unless sym_name

  @mutex.synchronize do
    entry = @registry[sym_name]
    return false unless entry

    definition = entry[:definition]
    # Snapshot for atomic rollback: a web edit that left the definition in a
    # state the constructor would reject (e.g. cleared instruction) used to
    # persist silently. We apply the batch, then validate! once and restore
    # the prior state on failure. (update_definition_field reassigns ivars
    # rather than mutating in place, so a shallow snapshot is a faithful
    # rollback.)
    ivar_snapshot = definition&.instance_variables&.to_h { |iv| [iv, definition.instance_variable_get(iv)] }
     = entry[:metadata].dup

    updates.each do |key, value|
      key_sym = key.to_sym
      # Check if this is a field that should update the AgentDefinition itself
      update_definition_field(definition, key_sym, value) if definition_field?(key_sym) && definition
      # Always store in metadata as well (for fields like persistent_status,
      # last_run_at, and as a cache for definition field overrides)
      entry[:metadata][key_sym] = value
    end

    if definition
      begin
        definition.validate!
      rescue StandardError => e
        ivar_snapshot.each { |iv, val| definition.instance_variable_set(iv, val) }
        entry[:metadata].replace()
        Legate.logger.error("GlobalDefinitionRegistry: Rejected update for :#{sym_name} (would leave the definition invalid): #{e.message}")
        return false
      end
    end
  end

  Legate.logger.debug("GlobalDefinitionRegistry: Updated definition for :#{sym_name} with keys: #{updates.keys.join(', ')}")
  true
end