Module: SolidAgent::HasContext

Extended by:
ActiveSupport::Concern
Defined in:
lib/solid_agent/has_context.rb

Instance Method Summary collapse

Instance Method Details

#add_assistant_message(content, **attributes) ⇒ Object



411
412
413
# File 'lib/solid_agent/has_context.rb', line 411

def add_assistant_message(content, **attributes)
  add_message(role: "assistant", content: content, **attributes)
end

#add_message(role:, content:, **attributes) ⇒ Object



402
403
404
405
# File 'lib/solid_agent/has_context.rb', line 402

def add_message(role:, content:, **attributes)
  primary_context_name = self.class._context_configs.keys.first || :context
  send("add_#{primary_context_name}_message", role: role, content: content, **attributes)
end

#add_user_message(content, **attributes) ⇒ Object



407
408
409
# File 'lib/solid_agent/has_context.rb', line 407

def add_user_message(content, **attributes)
  add_message(role: "user", content: content, **attributes)
end

#agent_checksumString

Generate checksum for the agent class configuration

Class-level options are read defensively so provenance never raises inside the (rescued) persistence path and silently drops generations.

Returns:

  • (String)

    MD5 hex digest



519
520
521
522
523
524
525
526
# File 'lib/solid_agent/has_context.rb', line 519

def agent_checksum
  data = {
    class: self.class.name,
    prompt_options: class_options(:prompt_options),
    embed_options: class_options(:embed_options)
  }.compact
  Digest::MD5.hexdigest(data.to_json)
end

#contextObject

Backward compatibility methods ===

These delegate to the primary context (first or :context)



357
358
359
360
# File 'lib/solid_agent/has_context.rb', line 357

def context
  primary_context_name = self.class._context_configs.keys.first || :context
  send(primary_context_name)
end

#context=(value) ⇒ Object



362
363
364
365
# File 'lib/solid_agent/has_context.rb', line 362

def context=(value)
  primary_context_name = self.class._context_configs.keys.first || :context
  send("#{primary_context_name}=", value)
end

#context_checksumString?

Generate checksum for current context state

Returns:

  • (String, nil)

    MD5 hex digest or nil if no context



486
487
488
489
490
491
492
493
# File 'lib/solid_agent/has_context.rb', line 486

def context_checksum
  return nil unless context
  Digest::MD5.hexdigest({
    context_id: context.id,
    message_count: context.messages.size,
    last_message_id: context.messages.last&.id
  }.to_json)
end

#context_classObject



367
368
369
370
# File 'lib/solid_agent/has_context.rb', line 367

def context_class
  primary_context_name = self.class._context_configs.keys.first || :context
  send("#{primary_context_name}_class")
end

#context_messagesObject



392
393
394
395
# File 'lib/solid_agent/has_context.rb', line 392

def context_messages
  primary_context_name = self.class._context_configs.keys.first || :context
  send("#{primary_context_name}_messages")
end

#context_resultObject

Returns the last assistant message content from the primary context Useful for extracting the final result to pass back to a caller



417
418
419
420
# File 'lib/solid_agent/has_context.rb', line 417

def context_result
  primary_context_name = self.class._context_configs.keys.first || :context
  send("#{primary_context_name}_result")
end

#context_summaryObject

Returns a summary hash of the primary context Useful for passing structured results back to a parent context



430
431
432
433
# File 'lib/solid_agent/has_context.rb', line 430

def context_summary
  primary_context_name = self.class._context_configs.keys.first || :context
  send("#{primary_context_name}_summary")
end

#create_context(contextable: nil, **options) ⇒ Object



387
388
389
390
# File 'lib/solid_agent/has_context.rb', line 387

def create_context(contextable: nil, **options)
  primary_context_name = self.class._context_configs.keys.first || :context
  send("create_#{primary_context_name}", contextable: contextable, **options)
end

#current_provenanceHash

Generate provenance record for current agent state

Returns:

  • (Hash)

    Full provenance data for tracing



498
499
500
501
502
503
504
505
506
507
508
509
510
511
# File 'lib/solid_agent/has_context.rb', line 498

def current_provenance
  {
    agent_class: self.class.name,
    agent_checksum: agent_checksum,
    prompt_checksum: prompt_checksum,
    context_checksum: context_checksum,
    context_id: context&.id,
    action_name: action_name,
    trace_id: prompt_options[:trace_id],
    timestamp: Time.now.iso8601,
    manifest_fingerprint: manifest_fingerprint,
    tools: prompt_tool_roster.presence
  }.compact
end

#generation_classObject



377
378
379
380
# File 'lib/solid_agent/has_context.rb', line 377

def generation_class
  primary_context_name = self.class._context_configs.keys.first || :context
  send("#{primary_context_name}_generation_class")
end

#last_generationObject

Returns the last generation record from the primary context



423
424
425
426
# File 'lib/solid_agent/has_context.rb', line 423

def last_generation
  primary_context_name = self.class._context_configs.keys.first || :context
  send("#{primary_context_name}_last_generation")
end

#load_context(contextable: nil, context_id: nil, **options) ⇒ Object



382
383
384
385
# File 'lib/solid_agent/has_context.rb', line 382

def load_context(contextable: nil, context_id: nil, **options)
  primary_context_name = self.class._context_configs.keys.first || :context
  send("load_#{primary_context_name}", contextable: contextable, context_id: context_id, **options)
end

#manifest_fingerprintString?

Get manifest fingerprint if agent was built from manifest

Returns:

  • (String, nil)

    Fingerprint or nil



531
532
533
534
# File 'lib/solid_agent/has_context.rb', line 531

def manifest_fingerprint
  return nil unless self.class.respond_to?(:_manifest) && self.class._manifest
  self.class._manifest.fingerprint
end

#message_classObject



372
373
374
375
# File 'lib/solid_agent/has_context.rb', line 372

def message_class
  primary_context_name = self.class._context_configs.keys.first || :context
  send("#{primary_context_name}_message_class")
end

#prompt_checksumString

Generate checksum for current prompt configuration

Returns:

  • (String)

    MD5 hex digest



442
443
444
445
446
447
448
449
450
# File 'lib/solid_agent/has_context.rb', line 442

def prompt_checksum
  data = {
    instructions: prompt_options[:instructions],
    model: prompt_options[:model],
    temperature: prompt_options[:temperature],
    tools: prompt_tool_roster.map { |tool| tool[:name] }.presence
  }.compact
  Digest::MD5.hexdigest(data.to_json)
end

#prompt_tool_rosterArray<Hash>

The tool schemas this generation actually offered the provider, as a compact roster.

The full schemas are too heavy to persist on every generation, and the checksum above only proves the roster changed — it can't say what the agent could do. Recording names, descriptions and parameter keys makes the tool surface auditable straight from the generation records, without requiring telemetry to be switched on.

Shape matches ActiveAgent's prompt.input.tools span attribute so a dashboard parses one format from both sources.

Returns:

  • (Array<Hash>)

    entries with :name, :description, :parameters



465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
# File 'lib/solid_agent/has_context.rb', line 465

def prompt_tool_roster
  Array(prompt_options[:tools]).filter_map do |tool|
    next unless tool.respond_to?(:[])

    name = tool[:name] || tool["name"]
    next if name.blank?

    parameters = tool[:parameters] || tool["parameters"] || tool[:input_schema] || tool["input_schema"]
    properties = parameters.is_a?(Hash) ? (parameters[:properties] || parameters["properties"]) : nil

    {
      name: name.to_s,
      description: (tool[:description] || tool["description"]).to_s.presence,
      parameters: properties.is_a?(Hash) ? properties.keys.map(&:to_s) : []
    }.compact
  end
end

#with_context_messagesObject



397
398
399
400
# File 'lib/solid_agent/has_context.rb', line 397

def with_context_messages
  primary_context_name = self.class._context_configs.keys.first || :context
  send("with_#{primary_context_name}_messages")
end