htm 0.0.30 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. checksums.yaml +4 -4
  2. data/.irbrc +2 -3
  3. data/.rubocop.yml +184 -0
  4. data/CHANGELOG.md +46 -0
  5. data/README.md +2 -0
  6. data/Rakefile +93 -12
  7. data/db/migrate/00008_create_node_relationships.rb +54 -0
  8. data/db/migrate/00009_fix_node_relationships_column_types.rb +17 -0
  9. data/db/schema.sql +124 -1
  10. data/docs/api/database.md +35 -57
  11. data/docs/api/embedding-service.md +1 -1
  12. data/docs/api/index.md +26 -15
  13. data/docs/api/working-memory.md +8 -8
  14. data/docs/architecture/index.md +5 -7
  15. data/docs/architecture/overview.md +5 -8
  16. data/docs/assets/images/htm-architecture-overview.svg +1 -1
  17. data/docs/assets/images/htm-context-assembly-flow.svg +2 -2
  18. data/docs/assets/images/htm-layered-architecture.svg +3 -3
  19. data/docs/assets/images/two-tier-memory-architecture.svg +1 -1
  20. data/docs/database/README.md +1 -0
  21. data/docs/database_rake_tasks.md +20 -28
  22. data/docs/development/contributing.md +5 -5
  23. data/docs/development/index.md +4 -7
  24. data/docs/development/schema.md +71 -1
  25. data/docs/development/setup.md +40 -82
  26. data/docs/development/testing.md +1 -1
  27. data/docs/examples/file-loading.md +4 -4
  28. data/docs/examples/mcp-client.md +1 -1
  29. data/docs/getting-started/quick-start.md +4 -4
  30. data/docs/guides/adding-memories.md +14 -1
  31. data/docs/guides/configuration.md +5 -5
  32. data/docs/guides/context-assembly.md +4 -4
  33. data/docs/guides/file-loading.md +12 -12
  34. data/docs/guides/getting-started.md +2 -2
  35. data/docs/guides/long-term-memory.md +7 -27
  36. data/docs/guides/propositions.md +20 -19
  37. data/docs/guides/recalling-memories.md +5 -5
  38. data/docs/guides/tags.md +18 -13
  39. data/docs/multi_framework_support.md +1 -1
  40. data/docs/robots/hive-mind.md +1 -1
  41. data/docs/robots/multi-robot.md +2 -2
  42. data/docs/robots/robot-groups.md +1 -1
  43. data/docs/robots/two-tier-memory.md +72 -94
  44. data/docs/setup_local_database.md +8 -54
  45. data/docs/using_rake_tasks_in_your_app.md +6 -6
  46. data/examples/01_basic_usage.rb +1 -0
  47. data/examples/03_custom_llm_configuration.rb +1 -0
  48. data/examples/04_file_loader_usage.rb +1 -0
  49. data/examples/05_timeframe_demo.rb +1 -0
  50. data/examples/06_example_app/app.rb +1 -0
  51. data/examples/07_cli_app/htm_cli.rb +1 -0
  52. data/examples/09_mcp_client.rb +1 -0
  53. data/examples/10_telemetry/demo.rb +1 -0
  54. data/examples/11_robot_groups/multi_process.rb +1 -0
  55. data/examples/11_robot_groups/same_process.rb +1 -0
  56. data/examples/12_rails_app/.envrc +12 -0
  57. data/examples/12_rails_app/Gemfile +8 -3
  58. data/examples/12_rails_app/Gemfile.lock +94 -89
  59. data/examples/12_rails_app/README.md +70 -19
  60. data/examples/12_rails_app/app/controllers/application_controller.rb +6 -0
  61. data/examples/12_rails_app/app/controllers/chats_controller.rb +305 -0
  62. data/examples/12_rails_app/app/controllers/dashboard_controller.rb +3 -0
  63. data/examples/12_rails_app/app/controllers/files_controller.rb +17 -2
  64. data/examples/12_rails_app/app/controllers/home_controller.rb +8 -0
  65. data/examples/12_rails_app/app/controllers/memories_controller.rb +9 -4
  66. data/examples/12_rails_app/app/controllers/messages_controller.rb +214 -0
  67. data/examples/12_rails_app/app/controllers/robots_controller.rb +11 -1
  68. data/examples/12_rails_app/app/controllers/tags_controller.rb +14 -1
  69. data/examples/12_rails_app/app/javascript/application.js +1 -1
  70. data/examples/12_rails_app/app/models/application_record.rb +5 -0
  71. data/examples/12_rails_app/app/models/chat.rb +36 -0
  72. data/examples/12_rails_app/app/models/message.rb +5 -0
  73. data/examples/12_rails_app/app/models/model.rb +5 -0
  74. data/examples/12_rails_app/app/models/tool_call.rb +5 -0
  75. data/examples/12_rails_app/app/views/chats/index.html.erb +61 -0
  76. data/examples/12_rails_app/app/views/chats/show.html.erb +213 -0
  77. data/examples/12_rails_app/app/views/dashboard/index.html.erb +3 -0
  78. data/examples/12_rails_app/app/views/files/index.html.erb +10 -5
  79. data/examples/12_rails_app/app/views/files/new.html.erb +4 -2
  80. data/examples/12_rails_app/app/views/files/show.html.erb +19 -3
  81. data/examples/12_rails_app/app/views/home/index.html.erb +45 -0
  82. data/examples/12_rails_app/app/views/layouts/application.html.erb +20 -18
  83. data/examples/12_rails_app/app/views/memories/_memory_card.html.erb +1 -1
  84. data/examples/12_rails_app/app/views/memories/deleted.html.erb +3 -1
  85. data/examples/12_rails_app/app/views/memories/edit.html.erb +2 -0
  86. data/examples/12_rails_app/app/views/memories/index.html.erb +2 -0
  87. data/examples/12_rails_app/app/views/memories/new.html.erb +2 -0
  88. data/examples/12_rails_app/app/views/memories/show.html.erb +4 -2
  89. data/examples/12_rails_app/app/views/messages/_message.html.erb +20 -0
  90. data/examples/12_rails_app/app/views/robots/index.html.erb +2 -0
  91. data/examples/12_rails_app/app/views/robots/new.html.erb +2 -0
  92. data/examples/12_rails_app/app/views/robots/show.html.erb +2 -0
  93. data/examples/12_rails_app/app/views/search/index.html.erb +59 -8
  94. data/examples/12_rails_app/app/views/shared/_navbar.html.erb +75 -29
  95. data/examples/12_rails_app/app/views/tags/index.html.erb +2 -0
  96. data/examples/12_rails_app/app/views/tags/show.html.erb +3 -1
  97. data/examples/12_rails_app/config/application.rb +1 -1
  98. data/examples/12_rails_app/config/database.yml +9 -5
  99. data/examples/12_rails_app/config/importmap.rb +1 -1
  100. data/examples/12_rails_app/config/initializers/htm.rb +9 -2
  101. data/examples/12_rails_app/config/initializers/ruby_llm.rb +33 -0
  102. data/examples/12_rails_app/config/routes.rb +39 -23
  103. data/examples/12_rails_app/db/migrate/20250124000001_create_ruby_llm_tables.rb +34 -0
  104. data/examples/12_rails_app/db/migrate/20250124000002_create_models_table.rb +28 -0
  105. data/examples/12_rails_app/db/schema.rb +67 -0
  106. data/examples/examples_helper.rb +25 -0
  107. data/lib/htm/circuit_breaker.rb +5 -6
  108. data/lib/htm/config/builder.rb +12 -12
  109. data/lib/htm/config/database.rb +21 -27
  110. data/lib/htm/config/defaults.yml +25 -13
  111. data/lib/htm/config/validator.rb +12 -18
  112. data/lib/htm/config.rb +93 -173
  113. data/lib/htm/database.rb +193 -199
  114. data/lib/htm/embedding_service.rb +4 -9
  115. data/lib/htm/integrations/sinatra.rb +7 -7
  116. data/lib/htm/job_adapter.rb +14 -21
  117. data/lib/htm/jobs/generate_embedding_job.rb +28 -44
  118. data/lib/htm/jobs/generate_propositions_job.rb +29 -55
  119. data/lib/htm/jobs/generate_relationships_job.rb +137 -0
  120. data/lib/htm/jobs/generate_tags_job.rb +45 -67
  121. data/lib/htm/loaders/markdown_loader.rb +65 -112
  122. data/lib/htm/long_term_memory/fulltext_search.rb +1 -1
  123. data/lib/htm/long_term_memory/hybrid_search.rb +300 -128
  124. data/lib/htm/long_term_memory/node_operations.rb +2 -2
  125. data/lib/htm/long_term_memory/relevance_scorer.rb +100 -68
  126. data/lib/htm/long_term_memory/tag_operations.rb +87 -120
  127. data/lib/htm/long_term_memory/vector_search.rb +1 -1
  128. data/lib/htm/long_term_memory.rb +2 -1
  129. data/lib/htm/mcp/cli.rb +59 -58
  130. data/lib/htm/mcp/server.rb +5 -6
  131. data/lib/htm/mcp/tools.rb +30 -36
  132. data/lib/htm/migration.rb +10 -10
  133. data/lib/htm/models/node.rb +2 -3
  134. data/lib/htm/models/node_relationship.rb +72 -0
  135. data/lib/htm/models/node_tag.rb +2 -2
  136. data/lib/htm/models/robot_node.rb +2 -2
  137. data/lib/htm/models/tag.rb +41 -28
  138. data/lib/htm/observability.rb +45 -51
  139. data/lib/htm/proposition_service.rb +3 -7
  140. data/lib/htm/query_cache.rb +13 -15
  141. data/lib/htm/railtie.rb +1 -2
  142. data/lib/htm/robot_group.rb +9 -9
  143. data/lib/htm/sequel_config.rb +1 -0
  144. data/lib/htm/sql_builder.rb +1 -1
  145. data/lib/htm/tag_service.rb +2 -6
  146. data/lib/htm/timeframe.rb +4 -5
  147. data/lib/htm/timeframe_extractor.rb +42 -83
  148. data/lib/htm/version.rb +1 -1
  149. data/lib/htm/workflows/remember_workflow.rb +112 -115
  150. data/lib/htm/working_memory.rb +21 -26
  151. data/lib/htm.rb +103 -116
  152. data/lib/tasks/db.rake +0 -2
  153. data/lib/tasks/doc.rake +14 -13
  154. data/lib/tasks/files.rake +5 -12
  155. data/lib/tasks/htm.rake +70 -71
  156. data/lib/tasks/jobs.rake +41 -47
  157. data/lib/tasks/tags.rake +3 -8
  158. metadata +28 -106
  159. data/lib/htm/config/section.rb +0 -74
  160. data/lib/htm/loaders/defaults_loader.rb +0 -166
  161. data/lib/htm/loaders/xdg_config_loader.rb +0 -116
@@ -1,166 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'anyway_config'
4
- require 'yaml'
5
-
6
- class HTM
7
- module Loaders
8
- # Bundled Defaults Loader for Anyway Config
9
- #
10
- # Loads default configuration values from a YAML file bundled with the gem.
11
- # This ensures defaults are always available regardless of where HTM is installed.
12
- #
13
- # The defaults.yml file has this structure:
14
- # defaults: # Base values for all environments
15
- # database:
16
- # host: localhost
17
- # port: 5432
18
- # development: # Overrides for development
19
- # database:
20
- # name: htm_development
21
- # test: # Overrides for test
22
- # database:
23
- # name: htm_test
24
- # production: # Overrides for production
25
- # database:
26
- # sslmode: require
27
- #
28
- # This loader deep-merges `defaults` with the current environment's overrides.
29
- #
30
- # This loader runs at LOWEST priority (before XDG), so all other sources
31
- # can override these bundled defaults:
32
- # 1. Bundled defaults (this loader)
33
- # 2. XDG user config (~/.config/htm/htm.yml)
34
- # 3. Project config (./config/htm.yml)
35
- # 4. Local overrides (./config/htm.local.yml)
36
- # 5. Environment variables (HTM_*)
37
- # 6. Programmatic (configure block)
38
- #
39
- class DefaultsLoader < Anyway::Loaders::Base
40
- DEFAULTS_PATH = File.expand_path('../config/defaults.yml', __dir__).freeze
41
-
42
- class << self
43
- # Returns the path to the bundled defaults file
44
- #
45
- # @return [String] path to defaults.yml
46
- def defaults_path
47
- DEFAULTS_PATH
48
- end
49
-
50
- # Check if defaults file exists
51
- #
52
- # @return [Boolean]
53
- def defaults_exist?
54
- File.exist?(DEFAULTS_PATH)
55
- end
56
-
57
- # Load and parse the raw YAML content
58
- #
59
- # @return [Hash] parsed YAML with symbolized keys
60
- def load_raw_yaml
61
- return {} unless defaults_exist?
62
-
63
- content = File.read(defaults_path)
64
- YAML.safe_load(
65
- content,
66
- permitted_classes: [Symbol],
67
- symbolize_names: true,
68
- aliases: true
69
- ) || {}
70
- rescue Psych::SyntaxError => e
71
- warn "HTM: Failed to parse bundled defaults #{defaults_path}: #{e.message}"
72
- {}
73
- end
74
-
75
- # Extract the schema (attribute names) from the defaults section
76
- #
77
- # @return [Hash] the defaults section containing all attribute definitions
78
- def schema
79
- raw = load_raw_yaml
80
- raw[:defaults] || {}
81
- end
82
-
83
- # Returns valid environment names from the config file
84
- #
85
- # Valid environments are top-level keys in defaults.yml excluding 'defaults'.
86
- # For example, if defaults.yml has keys: defaults, development, test, production
87
- # this returns [:development, :test, :production]
88
- #
89
- # @return [Array<Symbol>] list of valid environment names
90
- def valid_environments
91
- raw = load_raw_yaml
92
- raw.keys.reject { |k| k == :defaults }.sort
93
- end
94
-
95
- # Check if a given environment name is valid
96
- #
97
- # @param env [String, Symbol] environment name to check
98
- # @return [Boolean] true if environment is valid
99
- def valid_environment?(env)
100
- return false if env.nil? || env.to_s.empty?
101
- return false if env.to_s == 'defaults'
102
-
103
- valid_environments.include?(env.to_sym)
104
- end
105
- end
106
-
107
- def call(name:, **_options)
108
- return {} unless self.class.defaults_exist?
109
-
110
- trace!(:bundled_defaults, path: self.class.defaults_path) do
111
- load_and_merge_for_environment
112
- end
113
- end
114
-
115
- private
116
-
117
- # Load defaults and deep merge with environment-specific overrides
118
- #
119
- # @return [Hash] merged configuration for current environment
120
- def load_and_merge_for_environment
121
- raw = self.class.load_raw_yaml
122
- return {} if raw.empty?
123
-
124
- # Start with the defaults section
125
- defaults = raw[:defaults] || {}
126
-
127
- # Deep merge with environment-specific overrides
128
- env = current_environment
129
- env_overrides = raw[env.to_sym] || {}
130
-
131
- deep_merge(defaults, env_overrides)
132
- end
133
-
134
- # Deep merge two hashes, with overlay taking precedence
135
- #
136
- # @param base [Hash] base configuration
137
- # @param overlay [Hash] overlay configuration (takes precedence)
138
- # @return [Hash] merged result
139
- def deep_merge(base, overlay)
140
- base.merge(overlay) do |_key, old_val, new_val|
141
- if old_val.is_a?(Hash) && new_val.is_a?(Hash)
142
- deep_merge(old_val, new_val)
143
- else
144
- new_val
145
- end
146
- end
147
- end
148
-
149
- # Determine the current environment
150
- #
151
- # Priority: HTM_ENV > RAILS_ENV > RACK_ENV > 'development'
152
- #
153
- # @return [String] current environment name
154
- def current_environment
155
- ENV['HTM_ENV'] || ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
156
- end
157
- end
158
- end
159
- end
160
-
161
- # Register the defaults loader at LOWEST priority (before :yml loader)
162
- # This ensures bundled defaults are overridden by all other sources:
163
- # - XDG user config (registered after this, also before :yml)
164
- # - Project config (:yml loader)
165
- # - Environment variables (:env loader)
166
- Anyway.loaders.insert_before :yml, :bundled_defaults, HTM::Loaders::DefaultsLoader
@@ -1,116 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'anyway_config'
4
- require 'yaml'
5
-
6
- class HTM
7
- module Loaders
8
- # XDG Base Directory Specification loader for Anyway Config
9
- #
10
- # Loads configuration from XDG-compliant paths:
11
- # 1. $XDG_CONFIG_HOME/htm/htm.yml (if XDG_CONFIG_HOME is set)
12
- # 2. ~/.config/htm/htm.yml (XDG default fallback)
13
- #
14
- # On macOS, also checks:
15
- # 3. ~/Library/Application Support/htm/htm.yml
16
- #
17
- # This loader runs BEFORE the project-local config loader,
18
- # so project configs take precedence over user-global configs.
19
- #
20
- # @example XDG config file location
21
- # ~/.config/htm/htm.yml
22
- #
23
- # @example Custom XDG_CONFIG_HOME
24
- # export XDG_CONFIG_HOME=/my/config
25
- # # Looks for /my/config/htm/htm.yml
26
- #
27
- class XdgConfigLoader < Anyway::Loaders::Base
28
- class << self
29
- # Returns all XDG config paths to check, in order of priority (lowest first)
30
- #
31
- # Per XDG spec: If $XDG_CONFIG_HOME is set, use it; otherwise use ~/.config
32
- #
33
- # @return [Array<String>] list of potential config file paths
34
- def config_paths
35
- paths = []
36
-
37
- # macOS Application Support (lowest priority, only when XDG_CONFIG_HOME is not set)
38
- if macos? && (!ENV['XDG_CONFIG_HOME'] || ENV['XDG_CONFIG_HOME'].empty?)
39
- macos_path = File.expand_path('~/Library/Application Support/htm')
40
- paths << macos_path if Dir.exist?(File.dirname(macos_path))
41
- end
42
-
43
- # XDG_CONFIG_HOME takes precedence over default
44
- if ENV['XDG_CONFIG_HOME'] && !ENV['XDG_CONFIG_HOME'].empty?
45
- paths << File.join(ENV['XDG_CONFIG_HOME'], 'htm')
46
- else
47
- # XDG default: ~/.config/htm (only when XDG_CONFIG_HOME is not set)
48
- paths << File.expand_path('~/.config/htm')
49
- end
50
-
51
- paths
52
- end
53
-
54
- # Find the first existing config file
55
- #
56
- # @param name [String] config name (e.g., 'htm')
57
- # @return [String, nil] path to config file or nil if not found
58
- def find_config_file(name)
59
- config_paths.reverse_each do |dir|
60
- file = File.join(dir, "#{name}.yml")
61
- return file if File.exist?(file)
62
- end
63
- nil
64
- end
65
-
66
- private
67
-
68
- def macos?
69
- RUBY_PLATFORM.include?('darwin')
70
- end
71
- end
72
-
73
- def call(name:, **_options)
74
- config_file = self.class.find_config_file(name)
75
- return {} unless config_file
76
-
77
- trace!(:xdg, path: config_file) do
78
- load_yaml(config_file, name)
79
- end
80
- end
81
-
82
- private
83
-
84
- def load_yaml(path, name)
85
- return {} unless File.exist?(path)
86
-
87
- content = File.read(path)
88
- parsed = YAML.safe_load(content, permitted_classes: [Symbol], symbolize_names: true, aliases: true) || {}
89
-
90
- # Support environment-specific configs
91
- env = Anyway::Settings.current_environment ||
92
- ENV['HTM_ENV'] ||
93
- ENV['RAILS_ENV'] ||
94
- ENV['RACK_ENV'] ||
95
- 'development'
96
-
97
- # Check for environment key first, fall back to root level
98
- if parsed.key?(env.to_sym)
99
- parsed[env.to_sym] || {}
100
- elsif parsed.key?(env.to_s)
101
- parsed[env.to_s] || {}
102
- else
103
- # No environment key, treat as flat config
104
- parsed
105
- end
106
- rescue Psych::SyntaxError => e
107
- warn "HTM: Failed to parse XDG config #{path}: #{e.message}"
108
- {}
109
- end
110
- end
111
- end
112
- end
113
-
114
- # Register the XDG loader with Anyway Config
115
- # Insert before :yml so project-local config takes precedence
116
- Anyway.loaders.insert_before :yml, :xdg, HTM::Loaders::XdgConfigLoader