Class: OllamaChat::Database::Models::AppState

Inherits:
Object
  • Object
show all
Includes:
Feedback
Defined in:
lib/ollama_chat/database/models/app_state.rb

Overview

Represents a key/value pair of persistent application state stored in the database.

This model provides a generic, JSON-backed store for small bits of runtime state that need to survive across sessions—such as the XOR-SHA256 fingerprint of the shipped default prompts used for boot drift detection.

Constant Summary

Constants included from Feedback

Feedback::PREFIXES

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Feedback

#feedback

Class Method Details

.acknowledge(chat) ⇒ Object

Acknowledges the current shipped-prompt state, suppressing the boot drift notification. Called after /prompt sync so the user is not nagged again on the next launch.

Parameters:



105
106
107
108
109
# File 'lib/ollama_chat/database/models/app_state.rb', line 105

def self.acknowledge(chat)
  hashes      = compute_hashes(chat.config.prompts)
  fingerprint = fingerprint_from(hashes)
  store_fingerprint(fingerprint, hashes)
end

.compute_hashes(prompts_config) ⇒ Hash{String => String}

Builds a per-prompt SHA256 hash map from a prompts config object.

Parameters:

  • prompts_config (OllamaChatConfig::Prompts) —

    the config prompts

Returns:

  • (Hash{String => String}) —

    mapping "context/name" → SHA256 hex



132
133
134
135
136
137
138
139
# File 'lib/ollama_chat/database/models/app_state.rb', line 132

def self.compute_hashes(prompts_config)
  prompts_config.to_h.each_with_object({}) do |(context, prompts), hashes|
    prompts.each do |name, content|
      hashes["#{context}/#{name}"] =
        Digest::SHA256.hexdigest(content.to_s)
    end
  end
end

.fingerprint_from(hashes) ⇒ String

Computes an XOR fingerprint from a hash map.

Parameters:

  • hashes (Hash{String => String}) —

    the per-prompt SHA256 hex values

Returns:

  • (String) —

    the XOR-combined fingerprint as a hex string



145
146
147
# File 'lib/ollama_chat/database/models/app_state.rb', line 145

def self.fingerprint_from(hashes)
  hashes.values.reduce(0) { |sum, hex| sum ^ hex.to_i(16) }.to_s(16)
end

.get(key) ⇒ Hash?

Retrieves the JSON payload stored under the given key.

Parameters:

  • key (String, Symbol) —

    the state key to look up

Returns:

  • (Hash, nil) —

    the deserialized JSON payload, or nil if no record exists for the key



31
32
33
# File 'lib/ollama_chat/database/models/app_state.rb', line 31

def self.get(key)
  where(key: key.to_s).first&.value
end

.seed(chat) ⇒ Object

Computes per-prompt SHA256 hashes and an XOR fingerprint of all shipped default prompts.

At boot, if the stored fingerprint differs from the current one, a detail report (modified / added / removed) is printed to STDERR and the user is prompted to accept the new fingerprint. If declined, the old fingerprint is retained and the notice will reappear on the next boot.

This method is invoked automatically by the model seeding loop in OllamaChat::Database.setup_models.

Parameters:

  • chat (OllamaChat::Chat) —

    the chat instance providing the config and confirm?



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/ollama_chat/database/models/app_state.rb', line 62

def self.seed(chat)
  stored = get(:prompt_defaults_fingerprint)

  stored_fingerprint = stored&.dig('fingerprint')

  hashes    = compute_hashes(chat.config.prompts)
  fingerprint = fingerprint_from(hashes)

  return true if fingerprint == stored_fingerprint

  if stored&.dig('hashes')
    old     = stored['hashes']
    added   = hashes.keys - old.keys
    removed = old.keys - hashes.keys
    changed = hashes.select { |k, v| old[k] && old[k] != v }.keys

    chat.feedback("Shipped prompts changed since last boot:", type: :warn)
    changed.each { |k| chat.feedback "  ~ #{k} (modified)" }
    added.each   { |k| chat.feedback "  + #{k} (new)" }
    removed.each { |k| chat.feedback "  - #{k} (removed)" }
  elsif !OllamaChat.test_mode?
    chat.feedback("First run — storing prompt fingerprints.", type: :warn)
    store_fingerprint(fingerprint, hashes)
    return true
  end

  c = Term::ANSIColor
  prompt = <<~EOT.chomp << ' '
    #{c.bold{'Tip'}}: /prompt sync adopts shipped defaults into local config.
    Seen these changes? (y = stop nagging, n = ask again next boot) %s
  EOT
  if OllamaChat.test_mode? || chat.confirm?(prompt:, yes: /\Ay/i, timeout: 5)
    store_fingerprint(fingerprint, hashes)
  end

  true
end

.set(key, value) ⇒ OllamaChat::Database::Models::AppState

Creates or updates the JSON payload stored under the given key.

Parameters:

  • key (String, Symbol) —

    the state key to write

  • value (Hash, nil) —

    the JSON-serializable payload to store

Returns:



40
41
42
43
44
45
46
47
# File 'lib/ollama_chat/database/models/app_state.rb', line 40

def self.set(key, value)
  if found = where(key: key.to_s).first
    found.update(value:)
    found
  else
    create(key: key.to_s, value:)
  end
end

.store_fingerprint(fingerprint, hashes) ⇒ Object

Stores the fingerprint and hash map under the standard state key.

Parameters:

  • fingerprint (String) —

    the XOR fingerprint

  • hashes (Hash{String => String}) —

    the per-prompt hash map



153
154
155
156
# File 'lib/ollama_chat/database/models/app_state.rb', line 153

def self.store_fingerprint(fingerprint, hashes)
  set(:prompt_defaults_fingerprint,
      { fingerprint:, hashes:, computed_at: Time.now.iso8601 })
end

Instance Method Details

#validate ⇒ Object

Validates the application state entry.

Ensures that the key is present and unique.



20
21
22
23
24
# File 'lib/ollama_chat/database/models/app_state.rb', line 20

def validate
  super
  validates_presence :key
  validates_unique :key
end