htm 0.0.18 → 0.0.30

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 (216) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +119 -1
  3. data/README.md +12 -0
  4. data/Rakefile +104 -18
  5. data/db/migrate/00001_enable_extensions.rb +9 -5
  6. data/db/migrate/00002_create_robots.rb +18 -6
  7. data/db/migrate/00003_create_file_sources.rb +30 -17
  8. data/db/migrate/00004_create_nodes.rb +60 -48
  9. data/db/migrate/00005_create_tags.rb +24 -12
  10. data/db/migrate/00006_create_node_tags.rb +28 -13
  11. data/db/migrate/00007_create_robot_nodes.rb +40 -26
  12. data/db/schema.sql +17 -1
  13. data/db/seeds.rb +34 -34
  14. data/docs/api/embedding-service.md +140 -110
  15. data/docs/api/yard/HTM/ActiveRecordConfig.md +6 -0
  16. data/docs/api/yard/HTM/Config.md +173 -0
  17. data/docs/api/yard/HTM/ConfigSection.md +28 -0
  18. data/docs/api/yard/HTM/Database.md +1 -1
  19. data/docs/api/yard/HTM/Railtie.md +2 -2
  20. data/docs/api/yard/HTM.md +0 -57
  21. data/docs/api/yard/index.csv +76 -61
  22. data/docs/api/yard-reference.md +2 -1
  23. data/docs/architecture/adrs/003-ollama-embeddings.md +45 -36
  24. data/docs/architecture/adrs/004-hive-mind.md +1 -1
  25. data/docs/architecture/adrs/008-robot-identification.md +1 -1
  26. data/docs/architecture/index.md +11 -9
  27. data/docs/architecture/overview.md +11 -7
  28. data/docs/assets/images/balanced-strategy-decay.svg +41 -0
  29. data/docs/assets/images/class-hierarchy.svg +1 -1
  30. data/docs/assets/images/eviction-priority.svg +43 -0
  31. data/docs/assets/images/exception-hierarchy.svg +2 -2
  32. data/docs/assets/images/hive-mind-shared-memory.svg +52 -0
  33. data/docs/assets/images/htm-architecture-overview.svg +3 -3
  34. data/docs/assets/images/htm-core-components.svg +4 -4
  35. data/docs/assets/images/htm-layered-architecture.svg +1 -1
  36. data/docs/assets/images/htm-memory-addition-flow.svg +2 -2
  37. data/docs/assets/images/htm-memory-recall-flow.svg +2 -2
  38. data/docs/assets/images/memory-topology.svg +53 -0
  39. data/docs/assets/images/two-tier-memory-architecture.svg +55 -0
  40. data/docs/database/naming-convention.md +244 -0
  41. data/docs/database_rake_tasks.md +31 -0
  42. data/docs/development/rake-tasks.md +80 -35
  43. data/docs/development/setup.md +76 -44
  44. data/docs/examples/basic-usage.md +133 -0
  45. data/docs/examples/config-files.md +170 -0
  46. data/docs/examples/file-loading.md +208 -0
  47. data/docs/examples/index.md +116 -0
  48. data/docs/examples/llm-configuration.md +168 -0
  49. data/docs/examples/mcp-client.md +172 -0
  50. data/docs/examples/rails-integration.md +173 -0
  51. data/docs/examples/robot-groups.md +210 -0
  52. data/docs/examples/sinatra-integration.md +218 -0
  53. data/docs/examples/standalone-app.md +216 -0
  54. data/docs/examples/telemetry.md +224 -0
  55. data/docs/examples/timeframes.md +143 -0
  56. data/docs/getting-started/installation.md +97 -40
  57. data/docs/getting-started/quick-start.md +28 -11
  58. data/docs/guides/configuration.md +515 -0
  59. data/docs/guides/file-loading.md +322 -0
  60. data/docs/guides/getting-started.md +40 -9
  61. data/docs/guides/index.md +3 -3
  62. data/docs/guides/mcp-server.md +100 -13
  63. data/docs/guides/propositions.md +264 -0
  64. data/docs/guides/recalling-memories.md +4 -4
  65. data/docs/guides/search-strategies.md +3 -3
  66. data/docs/guides/tags.md +318 -0
  67. data/docs/guides/telemetry.md +229 -0
  68. data/docs/index.md +8 -16
  69. data/docs/{architecture → robots}/hive-mind.md +8 -111
  70. data/docs/robots/index.md +73 -0
  71. data/docs/{guides → robots}/multi-robot.md +3 -3
  72. data/docs/{guides → robots}/robot-groups.md +8 -7
  73. data/docs/{architecture → robots}/two-tier-memory.md +13 -149
  74. data/docs/robots/why-robots.md +85 -0
  75. data/examples/.envrc +6 -0
  76. data/examples/.gitignore +2 -0
  77. data/examples/00_create_examples_db.rb +94 -0
  78. data/examples/{basic_usage.rb → 01_basic_usage.rb} +12 -16
  79. data/examples/{custom_llm_configuration.rb → 03_custom_llm_configuration.rb} +13 -3
  80. data/examples/{file_loader_usage.rb → 04_file_loader_usage.rb} +11 -14
  81. data/examples/{timeframe_demo.rb → 05_timeframe_demo.rb} +10 -3
  82. data/examples/{example_app → 06_example_app}/app.rb +15 -15
  83. data/examples/{cli_app → 07_cli_app}/htm_cli.rb +15 -22
  84. data/examples/08_sinatra_app/Gemfile.lock +241 -0
  85. data/examples/{sinatra_app → 08_sinatra_app}/app.rb +19 -18
  86. data/examples/{mcp_client.rb → 09_mcp_client.rb} +5 -8
  87. data/examples/{telemetry → 10_telemetry}/SETUP_README.md +1 -1
  88. data/examples/{telemetry → 10_telemetry}/demo.rb +14 -10
  89. data/examples/11_robot_groups/README.md +335 -0
  90. data/examples/{robot_groups → 11_robot_groups/lib}/robot_worker.rb +17 -3
  91. data/examples/{robot_groups → 11_robot_groups}/multi_process.rb +9 -9
  92. data/examples/{robot_groups → 11_robot_groups}/same_process.rb +9 -12
  93. data/examples/{rails_app → 12_rails_app}/Gemfile +3 -0
  94. data/examples/{rails_app → 12_rails_app}/Gemfile.lock +87 -58
  95. data/examples/{rails_app → 12_rails_app}/app/controllers/dashboard_controller.rb +10 -6
  96. data/examples/{rails_app → 12_rails_app}/app/controllers/files_controller.rb +5 -5
  97. data/examples/{rails_app → 12_rails_app}/app/controllers/memories_controller.rb +11 -7
  98. data/examples/{rails_app → 12_rails_app}/app/controllers/robots_controller.rb +8 -8
  99. data/examples/12_rails_app/app/controllers/tags_controller.rb +36 -0
  100. data/examples/{rails_app → 12_rails_app}/app/views/dashboard/index.html.erb +2 -2
  101. data/examples/{rails_app → 12_rails_app}/app/views/files/new.html.erb +5 -2
  102. data/examples/{rails_app → 12_rails_app}/app/views/memories/_memory_card.html.erb +3 -3
  103. data/examples/{rails_app → 12_rails_app}/app/views/memories/deleted.html.erb +3 -3
  104. data/examples/{rails_app → 12_rails_app}/app/views/memories/edit.html.erb +3 -3
  105. data/examples/{rails_app → 12_rails_app}/app/views/memories/show.html.erb +4 -4
  106. data/examples/{rails_app → 12_rails_app}/app/views/robots/index.html.erb +2 -2
  107. data/examples/{rails_app → 12_rails_app}/app/views/robots/show.html.erb +4 -4
  108. data/examples/{rails_app → 12_rails_app}/app/views/search/index.html.erb +1 -1
  109. data/examples/{rails_app → 12_rails_app}/app/views/tags/index.html.erb +2 -2
  110. data/examples/{rails_app → 12_rails_app}/app/views/tags/show.html.erb +1 -1
  111. data/examples/12_rails_app/config/initializers/htm.rb +7 -0
  112. data/examples/12_rails_app/config/initializers/rack.rb +5 -0
  113. data/examples/README.md +230 -211
  114. data/examples/examples_helper.rb +138 -0
  115. data/lib/htm/config/builder.rb +167 -0
  116. data/lib/htm/config/database.rb +317 -0
  117. data/lib/htm/config/defaults.yml +41 -13
  118. data/lib/htm/config/section.rb +74 -0
  119. data/lib/htm/config/validator.rb +83 -0
  120. data/lib/htm/config.rb +65 -361
  121. data/lib/htm/database.rb +85 -127
  122. data/lib/htm/errors.rb +14 -0
  123. data/lib/htm/integrations/sinatra.rb +13 -44
  124. data/lib/htm/job_adapter.rb +75 -1
  125. data/lib/htm/jobs/generate_embedding_job.rb +3 -4
  126. data/lib/htm/jobs/generate_propositions_job.rb +4 -5
  127. data/lib/htm/jobs/generate_tags_job.rb +16 -15
  128. data/lib/htm/loaders/defaults_loader.rb +23 -0
  129. data/lib/htm/loaders/markdown_loader.rb +17 -15
  130. data/lib/htm/loaders/xdg_config_loader.rb +9 -9
  131. data/lib/htm/long_term_memory/fulltext_search.rb +14 -14
  132. data/lib/htm/long_term_memory/hybrid_search.rb +396 -229
  133. data/lib/htm/long_term_memory/node_operations.rb +24 -23
  134. data/lib/htm/long_term_memory/relevance_scorer.rb +23 -20
  135. data/lib/htm/long_term_memory/robot_operations.rb +4 -4
  136. data/lib/htm/long_term_memory/tag_operations.rb +91 -77
  137. data/lib/htm/long_term_memory/vector_search.rb +4 -5
  138. data/lib/htm/long_term_memory.rb +13 -13
  139. data/lib/htm/mcp/cli.rb +115 -8
  140. data/lib/htm/mcp/resources.rb +4 -3
  141. data/lib/htm/mcp/server.rb +5 -4
  142. data/lib/htm/mcp/tools.rb +37 -28
  143. data/lib/htm/migration.rb +72 -0
  144. data/lib/htm/models/file_source.rb +52 -31
  145. data/lib/htm/models/node.rb +224 -108
  146. data/lib/htm/models/node_tag.rb +49 -28
  147. data/lib/htm/models/robot.rb +38 -27
  148. data/lib/htm/models/robot_node.rb +63 -35
  149. data/lib/htm/models/tag.rb +126 -123
  150. data/lib/htm/observability.rb +45 -41
  151. data/lib/htm/proposition_service.rb +76 -7
  152. data/lib/htm/railtie.rb +2 -2
  153. data/lib/htm/robot_group.rb +30 -18
  154. data/lib/htm/sequel_config.rb +215 -0
  155. data/lib/htm/sql_builder.rb +14 -16
  156. data/lib/htm/tag_service.rb +78 -0
  157. data/lib/htm/tasks.rb +3 -0
  158. data/lib/htm/version.rb +1 -1
  159. data/lib/htm/workflows/remember_workflow.rb +213 -0
  160. data/lib/htm.rb +27 -22
  161. data/lib/tasks/db.rake +0 -2
  162. data/lib/tasks/doc.rake +2 -2
  163. data/lib/tasks/files.rake +11 -18
  164. data/lib/tasks/htm.rake +190 -62
  165. data/lib/tasks/jobs.rake +179 -54
  166. data/lib/tasks/tags.rake +8 -13
  167. data/mkdocs.yml +33 -8
  168. data/scripts/backfill_parent_tags.rb +376 -0
  169. data/scripts/normalize_plural_tags.rb +335 -0
  170. metadata +168 -86
  171. data/docs/api/yard/HTM/Configuration.md +0 -240
  172. data/docs/telemetry.md +0 -391
  173. data/examples/rails_app/app/controllers/tags_controller.rb +0 -30
  174. data/examples/sinatra_app/Gemfile.lock +0 -166
  175. data/lib/htm/active_record_config.rb +0 -104
  176. /data/examples/{config_file_example → 02_config_file_example}/README.md +0 -0
  177. /data/examples/{config_file_example → 02_config_file_example}/config/htm.local.yml +0 -0
  178. /data/examples/{config_file_example → 02_config_file_example}/custom_config.yml +0 -0
  179. /data/examples/{config_file_example → 02_config_file_example}/show_config.rb +0 -0
  180. /data/examples/{example_app → 06_example_app}/Rakefile +0 -0
  181. /data/examples/{cli_app → 07_cli_app}/README.md +0 -0
  182. /data/examples/{sinatra_app → 08_sinatra_app}/Gemfile +0 -0
  183. /data/examples/{telemetry → 10_telemetry}/README.md +0 -0
  184. /data/examples/{telemetry → 10_telemetry}/grafana/dashboards/htm-metrics.json +0 -0
  185. /data/examples/{rails_app → 12_rails_app}/.gitignore +0 -0
  186. /data/examples/{rails_app → 12_rails_app}/Procfile.dev +0 -0
  187. /data/examples/{rails_app → 12_rails_app}/README.md +0 -0
  188. /data/examples/{rails_app → 12_rails_app}/Rakefile +0 -0
  189. /data/examples/{rails_app → 12_rails_app}/app/assets/stylesheets/application.css +0 -0
  190. /data/examples/{rails_app → 12_rails_app}/app/assets/stylesheets/inter-font.css +0 -0
  191. /data/examples/{rails_app → 12_rails_app}/app/controllers/application_controller.rb +0 -0
  192. /data/examples/{rails_app → 12_rails_app}/app/controllers/search_controller.rb +0 -0
  193. /data/examples/{rails_app → 12_rails_app}/app/javascript/application.js +0 -0
  194. /data/examples/{rails_app → 12_rails_app}/app/javascript/controllers/application.js +0 -0
  195. /data/examples/{rails_app → 12_rails_app}/app/javascript/controllers/index.js +0 -0
  196. /data/examples/{rails_app → 12_rails_app}/app/views/files/index.html.erb +0 -0
  197. /data/examples/{rails_app → 12_rails_app}/app/views/files/show.html.erb +0 -0
  198. /data/examples/{rails_app → 12_rails_app}/app/views/layouts/application.html.erb +0 -0
  199. /data/examples/{rails_app → 12_rails_app}/app/views/memories/index.html.erb +0 -0
  200. /data/examples/{rails_app → 12_rails_app}/app/views/memories/new.html.erb +0 -0
  201. /data/examples/{rails_app → 12_rails_app}/app/views/robots/new.html.erb +0 -0
  202. /data/examples/{rails_app → 12_rails_app}/app/views/shared/_navbar.html.erb +0 -0
  203. /data/examples/{rails_app → 12_rails_app}/app/views/shared/_stat_card.html.erb +0 -0
  204. /data/examples/{rails_app → 12_rails_app}/bin/dev +0 -0
  205. /data/examples/{rails_app → 12_rails_app}/bin/rails +0 -0
  206. /data/examples/{rails_app → 12_rails_app}/bin/rake +0 -0
  207. /data/examples/{rails_app → 12_rails_app}/config/application.rb +0 -0
  208. /data/examples/{rails_app → 12_rails_app}/config/boot.rb +0 -0
  209. /data/examples/{rails_app → 12_rails_app}/config/database.yml +0 -0
  210. /data/examples/{rails_app → 12_rails_app}/config/environment.rb +0 -0
  211. /data/examples/{rails_app → 12_rails_app}/config/importmap.rb +0 -0
  212. /data/examples/{rails_app → 12_rails_app}/config/routes.rb +0 -0
  213. /data/examples/{rails_app → 12_rails_app}/config/tailwind.config.js +0 -0
  214. /data/examples/{rails_app → 12_rails_app}/config.ru +0 -0
  215. /data/examples/{rails_app → 12_rails_app}/log/.keep +0 -0
  216. /data/examples/{rails_app → 12_rails_app}/tmp/local_secret.txt +0 -0
@@ -4,15 +4,21 @@ Client-side embedding generation service for HTM.
4
4
 
5
5
  ## Overview
6
6
 
7
- `HTM::EmbeddingService` generates vector embeddings for text content before database insertion. It supports multiple embedding providers:
7
+ `HTM::EmbeddingService` generates vector embeddings for text content before database insertion. It uses RubyLLM to support multiple embedding providers:
8
8
 
9
- - **Ollama** - Local embedding server (default, via `nomic-embed-text` model)
10
- - **OpenAI** - OpenAI's `text-embedding-3-small` model
9
+ - **Ollama** - Local embedding server (default for development)
10
+ - **OpenAI** - OpenAI's embedding models
11
+ - **Anthropic** - For tag extraction (via chat models)
12
+ - **Gemini** - Google's embedding models
13
+ - **Azure** - Azure OpenAI deployments
14
+ - **Bedrock** - AWS Bedrock models
15
+ - **DeepSeek** - DeepSeek embeddings
11
16
 
12
17
  The service also provides token counting for working memory management.
13
18
 
14
19
  **Architecture:**
15
- - Ruby application generates embeddings via HTTP call to Ollama/OpenAI
20
+ - Ruby application generates embeddings via RubyLLM
21
+ - RubyLLM handles provider-specific API calls
16
22
  - Embeddings are passed to PostgreSQL during INSERT
17
23
  - Simple, reliable, cross-platform operation
18
24
 
@@ -34,7 +40,6 @@ Create a new embedding service instance.
34
40
  HTM::EmbeddingService.new(
35
41
  provider = :ollama,
36
42
  model: 'nomic-embed-text',
37
- ollama_url: nil,
38
43
  dimensions: nil
39
44
  )
40
45
  ```
@@ -43,11 +48,12 @@ HTM::EmbeddingService.new(
43
48
 
44
49
  | Parameter | Type | Default | Description |
45
50
  |-----------|------|---------|-------------|
46
- | `provider` | Symbol | `:ollama` | Embedding provider (`:ollama`, `:openai`) |
47
- | `model` | String | `'nomic-embed-text'` | Model name for the provider |
48
- | `ollama_url` | String, nil | `ENV['OLLAMA_URL']` or `'http://localhost:11434'` | Ollama server URL |
51
+ | `provider` | Symbol | `:ollama` | Embedding provider (`:ollama`, `:openai`, `:gemini`, `:azure`, `:bedrock`, `:deepseek`) |
52
+ | `model` | String | Provider-dependent | Model name for the provider |
49
53
  | `dimensions` | Integer, nil | Auto-detected | Expected embedding dimensions |
50
54
 
55
+ **Provider-specific configuration** is handled via environment variables (see RubyLLM documentation).
56
+
51
57
  #### Returns
52
58
 
53
59
  `HTM::EmbeddingService` - Configured embedding service instance
@@ -58,20 +64,19 @@ HTM::EmbeddingService.new(
58
64
 
59
65
  #### Examples
60
66
 
61
- **Default Ollama configuration:**
67
+ **Default configuration (uses Ollama):**
62
68
 
63
69
  ```ruby
64
70
  service = HTM::EmbeddingService.new
65
- # Uses Ollama at http://localhost:11434 with nomic-embed-text (768 dimensions)
71
+ # Uses Ollama with nomic-embed-text (768 dimensions)
66
72
  ```
67
73
 
68
- **Custom Ollama model:**
74
+ **Ollama with custom model:**
69
75
 
70
76
  ```ruby
71
77
  service = HTM::EmbeddingService.new(
72
78
  :ollama,
73
79
  model: 'mxbai-embed-large',
74
- ollama_url: 'http://localhost:11434',
75
80
  dimensions: 1024
76
81
  )
77
82
  ```
@@ -87,15 +92,27 @@ service = HTM::EmbeddingService.new(
87
92
  )
88
93
  ```
89
94
 
90
- **HTM automatically initializes EmbeddingService:**
95
+ **Gemini configuration:**
91
96
 
92
97
  ```ruby
93
- htm = HTM.new(
94
- robot_name: "Assistant",
95
- embedding_provider: :ollama,
96
- embedding_model: 'nomic-embed-text'
98
+ # Requires GEMINI_API_KEY environment variable
99
+ service = HTM::EmbeddingService.new(
100
+ :gemini,
101
+ model: 'text-embedding-004',
102
+ dimensions: 768
97
103
  )
98
- # EmbeddingService configured automatically
104
+ ```
105
+
106
+ **HTM global configuration (recommended):**
107
+
108
+ ```ruby
109
+ HTM.configure do |config|
110
+ config.embedding.provider = :openai # or :ollama, :gemini, etc.
111
+ config.embedding.model = 'text-embedding-3-small'
112
+ end
113
+
114
+ htm = HTM.new(robot_name: "Assistant")
115
+ # EmbeddingService configured automatically from global config
99
116
  ```
100
117
 
101
118
  ---
@@ -144,21 +161,24 @@ begin
144
161
  embedding = service.embed("some text")
145
162
  rescue HTM::EmbeddingError => e
146
163
  puts "Embedding failed: #{e.message}"
147
- # Check Ollama is running: curl http://localhost:11434/api/tags
164
+ # For Ollama: Check if running with `curl http://localhost:11434/api/tags`
165
+ # For cloud providers: Check API key is set correctly
148
166
  end
149
167
  ```
150
168
 
151
169
  #### Implementation Details
152
170
 
153
- **Ollama provider:**
154
- - Makes HTTP POST to `/api/embeddings`
155
- - Returns dense vector representation
156
- - Requires Ollama server running locally
171
+ All providers are handled through RubyLLM, which provides a consistent interface across providers.
172
+
173
+ **Ollama:** Local HTTP calls, requires Ollama server running
157
174
 
158
- **OpenAI provider:**
159
- - Makes HTTP POST to OpenAI API
160
- - Requires `OPENAI_API_KEY` environment variable
161
- - API costs: $0.0001 per 1K tokens
175
+ **OpenAI:** Cloud API calls, requires `OPENAI_API_KEY`
176
+
177
+ **Gemini:** Cloud API calls, requires `GEMINI_API_KEY`
178
+
179
+ **Azure:** Cloud API calls, requires Azure credentials
180
+
181
+ **Bedrock:** AWS API calls, requires AWS credentials
162
182
 
163
183
  ---
164
184
 
@@ -207,11 +227,13 @@ htm.add_message(
207
227
 
208
228
  ## Embedding Providers
209
229
 
210
- ### Ollama (Default)
230
+ HTM uses RubyLLM which supports multiple providers. Choose based on your requirements for privacy, cost, and quality.
231
+
232
+ ### Ollama (Default for Development)
211
233
 
212
234
  **Status**: ✅ Fully implemented
213
235
 
214
- Local embedding server with various models, accessed via HTTP.
236
+ Local embedding server with various models.
215
237
 
216
238
  **Installation:**
217
239
 
@@ -234,25 +256,10 @@ ollama pull nomic-embed-text
234
256
  **Configuration:**
235
257
 
236
258
  ```ruby
237
- service = HTM::EmbeddingService.new(
238
- :ollama,
239
- model: 'nomic-embed-text',
240
- ollama_url: 'http://localhost:11434'
241
- )
242
-
243
- embedding = service.embed("test text")
244
- ```
245
-
246
- **Troubleshooting:**
247
-
248
- If Ollama is unavailable, embedding generation will fail:
249
-
250
- ```ruby
251
- # Check Ollama is running
252
- system("curl http://localhost:11434/api/tags")
253
-
254
- # Start Ollama if needed
255
- system("ollama serve")
259
+ HTM.configure do |config|
260
+ config.embedding.provider = :ollama
261
+ config.embedding.model = 'nomic-embed-text'
262
+ end
256
263
  ```
257
264
 
258
265
  **Advantages:**
@@ -264,15 +271,14 @@ system("ollama serve")
264
271
  **Disadvantages:**
265
272
  - ❌ Requires local installation
266
273
  - ❌ Uses local compute resources
267
- - ❌ Slightly lower quality than OpenAI
268
274
 
269
275
  ---
270
276
 
271
- ### OpenAI
277
+ ### OpenAI (Recommended for Production)
272
278
 
273
279
  **Status**: ✅ Fully implemented
274
280
 
275
- Uses OpenAI's embedding API, accessed via HTTP.
281
+ Uses OpenAI's embedding API.
276
282
 
277
283
  **Configuration:**
278
284
 
@@ -281,13 +287,10 @@ export OPENAI_API_KEY="sk-..."
281
287
  ```
282
288
 
283
289
  ```ruby
284
- service = HTM::EmbeddingService.new(
285
- :openai,
286
- model: 'text-embedding-3-small'
287
- )
288
-
289
- # Add message - embedding generated via OpenAI API
290
- embedding = service.embed("test text")
290
+ HTM.configure do |config|
291
+ config.embedding.provider = :openai
292
+ config.embedding.model = 'text-embedding-3-small'
293
+ end
291
294
  ```
292
295
 
293
296
  **Models:**
@@ -295,20 +298,7 @@ embedding = service.embed("test text")
295
298
  | Model | Dimensions | Speed | Cost |
296
299
  |-------|------------|-------|------|
297
300
  | `text-embedding-3-small` | 1536 | Fast | $0.0001/1K tokens |
298
- | `text-embedding-ada-002` | 1536 | Fast | $0.0001/1K tokens |
299
-
300
- **Error Handling:**
301
-
302
- ```ruby
303
- begin
304
- service = HTM::EmbeddingService.new(:openai)
305
- embedding = service.embed("test")
306
- rescue HTM::EmbeddingError => e
307
- if e.message.include?("API key")
308
- puts "Set OPENAI_API_KEY environment variable"
309
- end
310
- end
311
- ```
301
+ | `text-embedding-3-large` | 3072 | Fast | $0.00013/1K tokens |
312
302
 
313
303
  **Advantages:**
314
304
  - ✅ High quality embeddings
@@ -316,10 +306,44 @@ end
316
306
  - ✅ Managed service
317
307
 
318
308
  **Disadvantages:**
319
- - ❌ API costs ($0.0001 per 1K tokens)
309
+ - ❌ API costs
320
310
  - ❌ Requires internet connection
321
- - ❌ Data sent to OpenAI servers
322
- - ❌ Requires API key management
311
+ - ❌ Data sent to cloud
312
+
313
+ ---
314
+
315
+ ### Other Providers
316
+
317
+ **Gemini:**
318
+ ```bash
319
+ export GEMINI_API_KEY="..."
320
+ ```
321
+ ```ruby
322
+ HTM.configure do |config|
323
+ config.embedding.provider = :gemini
324
+ config.embedding.model = 'text-embedding-004'
325
+ end
326
+ ```
327
+
328
+ **Azure OpenAI:**
329
+ ```bash
330
+ export AZURE_OPENAI_API_KEY="..."
331
+ export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com"
332
+ ```
333
+
334
+ **AWS Bedrock:**
335
+ ```bash
336
+ export AWS_ACCESS_KEY_ID="..."
337
+ export AWS_SECRET_ACCESS_KEY="..."
338
+ export AWS_REGION="us-east-1"
339
+ ```
340
+
341
+ **DeepSeek:**
342
+ ```bash
343
+ export DEEPSEEK_API_KEY="..."
344
+ ```
345
+
346
+ See the [RubyLLM documentation](https://rubyllm.com/) for complete provider configuration.
323
347
 
324
348
  ---
325
349
 
@@ -335,15 +359,17 @@ end
335
359
  system("ollama serve")
336
360
  ```
337
361
 
338
- **OpenAI API key missing:**
362
+ **API key missing (cloud providers):**
339
363
 
340
364
  ```ruby
341
- # Error: OPENAI_API_KEY not set
365
+ # Error: API key not set
342
366
  # Solution: Set environment variable
343
- ENV['OPENAI_API_KEY'] = 'sk-...'
367
+ ENV['OPENAI_API_KEY'] = 'sk-...' # For OpenAI
368
+ ENV['ANTHROPIC_API_KEY'] = 'sk-...' # For Anthropic
369
+ ENV['GEMINI_API_KEY'] = '...' # For Gemini
344
370
  ```
345
371
 
346
- **Invalid model:**
372
+ **Invalid model (Ollama):**
347
373
 
348
374
  ```ruby
349
375
  # Error: Model not found
@@ -368,9 +394,10 @@ Based on typical production workloads:
368
394
  | Ollama | nomic-embed-text | 20ms | 40ms | Free |
369
395
  | Ollama | mxbai-embed-large | 30ms | 60ms | Free |
370
396
  | OpenAI | text-embedding-3-small | 40ms | 80ms | $0.10 |
397
+ | Gemini | text-embedding-004 | 50ms | 90ms | Varies |
371
398
 
372
399
  **Factors affecting latency:**
373
- - Network latency (Ollama local vs OpenAI remote)
400
+ - Network latency (local providers vs cloud)
374
401
  - Text length (longer text = more tokens = slower)
375
402
  - Model size (larger models = slower)
376
403
  - System load (CPU/GPU utilization)
@@ -380,14 +407,17 @@ Based on typical production workloads:
380
407
  **Use appropriate model size:**
381
408
 
382
409
  ```ruby
383
- # Fast but lower quality
384
- service = HTM::EmbeddingService.new(:ollama, model: 'all-minilm')
410
+ # Fast but lower quality (Ollama)
411
+ HTM.configure { |c| c.embedding.model = 'all-minilm' }
385
412
 
386
- # Balanced (recommended)
387
- service = HTM::EmbeddingService.new(:ollama, model: 'nomic-embed-text')
413
+ # Balanced - Ollama (recommended for development)
414
+ HTM.configure { |c| c.embedding.model = 'nomic-embed-text' }
388
415
 
389
- # Slower but higher quality
390
- service = HTM::EmbeddingService.new(:ollama, model: 'mxbai-embed-large')
416
+ # High quality - OpenAI (recommended for production)
417
+ HTM.configure do |c|
418
+ c.embedding.provider = :openai
419
+ c.embedding.model = 'text-embedding-3-small'
420
+ end
391
421
  ```
392
422
 
393
423
  **Batch operations:**
@@ -410,12 +440,12 @@ end
410
440
  HTM initializes `EmbeddingService` automatically:
411
441
 
412
442
  ```ruby
413
- htm = HTM.new(
414
- robot_name: "Assistant",
415
- embedding_provider: :ollama, # Optional, default
416
- embedding_model: 'nomic-embed-text' # Optional, default
417
- )
443
+ HTM.configure do |config|
444
+ config.embedding.provider = :ollama # or :openai, :gemini, etc.
445
+ config.embedding.model = 'nomic-embed-text'
446
+ end
418
447
 
448
+ htm = HTM.new(robot_name: "Assistant")
419
449
  # EmbeddingService is ready to use internally
420
450
  ```
421
451
 
@@ -426,13 +456,13 @@ sequenceDiagram
426
456
  participant App as Application
427
457
  participant HTM as HTM
428
458
  participant ES as EmbeddingService
429
- participant Ollama as Ollama/OpenAI
459
+ participant LLM as LLM Provider (via RubyLLM)
430
460
  participant DB as PostgreSQL
431
461
 
432
462
  App->>HTM: add_message(content)
433
463
  HTM->>ES: embed(content)
434
- ES->>Ollama: HTTP POST /api/embeddings
435
- Ollama->>ES: embedding vector
464
+ ES->>LLM: Generate embedding
465
+ LLM->>ES: embedding vector
436
466
  ES->>HTM: Array<Float>
437
467
  HTM->>DB: INSERT with embedding
438
468
  DB->>HTM: node_id
@@ -484,21 +514,20 @@ puts "Token count: #{tokens}"
484
514
  ### Multiple Providers
485
515
 
486
516
  ```ruby
487
- # Ollama for development
488
- dev_service = HTM::EmbeddingService.new(
489
- :ollama,
490
- model: 'nomic-embed-text'
491
- )
517
+ # Configure for development (Ollama)
518
+ HTM.configure do |config|
519
+ config.embedding.provider = :ollama
520
+ config.embedding.model = 'nomic-embed-text'
521
+ end
492
522
 
493
- # OpenAI for production
494
- prod_service = HTM::EmbeddingService.new(
495
- :openai,
496
- model: 'text-embedding-3-small'
497
- )
523
+ # Configure for production (OpenAI)
524
+ HTM.configure do |config|
525
+ config.embedding.provider = :openai
526
+ config.embedding.model = 'text-embedding-3-small'
527
+ end
498
528
 
499
- # Same interface
500
- dev_embedding = dev_service.embed("test")
501
- prod_embedding = prod_service.embed("test")
529
+ # Same interface regardless of provider
530
+ embedding = HTM::EmbeddingService.new.embed("test")
502
531
  ```
503
532
 
504
533
  ### Custom Model Dimensions
@@ -521,6 +550,7 @@ embedding = service.embed("text")
521
550
 
522
551
  - [HTM API](htm.md) - Main HTM class
523
552
  - [LongTermMemory API](long-term-memory.md) - Storage layer
524
- - [ADR-003: Ollama Embeddings](../architecture/adrs/003-ollama-embeddings.md) - Architecture decision
525
- - [Ollama Documentation](https://ollama.ai/docs) - Ollama setup guide
526
- - [OpenAI Embeddings](https://platform.openai.com/docs/guides/embeddings) - OpenAI API docs
553
+ - [ADR-003: Default Embedding Provider](../architecture/adrs/003-ollama-embeddings.md) - Architecture decision for defaults
554
+ - [RubyLLM Documentation](https://rubyllm.com/) - Multi-provider LLM interface
555
+ - [Ollama Documentation](https://ollama.ai/docs) - Local LLM provider
556
+ - [OpenAI Embeddings](https://platform.openai.com/docs/guides/embeddings) - Cloud embeddings
@@ -4,6 +4,10 @@
4
4
 
5
5
  ActiveRecord database configuration and model loading
6
6
 
7
+ Uses HTM::Config for database settings. Configuration can come from:
8
+ * Environment variables (HTM_DATABASE__URL, HTM_DATABASE__HOST, etc.)
9
+ * Programmatic configuration via HTM.configure
10
+
7
11
 
8
12
  # Class Methods
9
13
  ## connected?() {: #method-c-connected? }
@@ -18,6 +22,8 @@ Close all database connections
18
22
  Establish database connection from HTM::Config
19
23
  ## load_database_config() {: #method-c-load_database_config }
20
24
  Load database configuration from HTM::Config
25
+ **`@return`** [Hash] ActiveRecord-compatible configuration hash
26
+
21
27
  ## verify_extensions!() {: #method-c-verify_extensions! }
22
28
  Verify required extensions are available
23
29
 
@@ -0,0 +1,173 @@
1
+ # Class: HTM::Config
2
+ **Inherits:** Anyway::Config
3
+
4
+
5
+ HTM Configuration using Anyway Config
6
+
7
+ Schema is defined in lib/htm/config/defaults.yml (single source of truth)
8
+ Configuration uses nested sections for better organization:
9
+ - HTM.config.database.host
10
+ - HTM.config.embedding.provider
11
+ - HTM.config.providers.openai.api_key
12
+
13
+ Configuration sources (lowest to highest priority):
14
+ 1. Bundled defaults: lib/htm/config/defaults.yml (ships with gem)
15
+ 2. XDG user config:
16
+ * ~/Library/Application Support/htm/htm.yml (macOS only)
17
+ * ~/.config/htm/htm.yml (XDG default)
18
+ * $XDG_CONFIG_HOME/htm/htm.yml (if XDG_CONFIG_HOME is set)
19
+ 3. Project config: ./config/htm.yml (environment-specific)
20
+ 4. Local overrides: ./config/htm.local.yml (gitignored)
21
+ 5. Environment variables (HTM_*)
22
+ 6. Explicit values passed to configure block
23
+
24
+
25
+ **`@example`**
26
+ ```ruby
27
+ export HTM_EMBEDDING__PROVIDER=openai
28
+ export HTM_EMBEDDING__MODEL=text-embedding-3-small
29
+ export HTM_PROVIDERS__OPENAI__API_KEY=sk-xxx
30
+ ```
31
+ **`@example`**
32
+ ```ruby
33
+ embedding:
34
+ provider: ollama
35
+ model: nomic-embed-text:latest
36
+ providers:
37
+ ollama:
38
+ url: http://localhost:11434
39
+ ```
40
+ **`@example`**
41
+ ```ruby
42
+ HTM.configure do |config|
43
+ config.embedding.provider = :openai
44
+ config.embedding.model = 'text-embedding-3-small'
45
+ end
46
+ ```
47
+ # Class Methods
48
+ ## active_xdg_config_file() {: #method-c-active_xdg_config_file }
49
+ ## config_section_with_defaults(section_key ) {: #method-c-config_section_with_defaults }
50
+ Create a coercion that merges incoming value with SCHEMA defaults for a
51
+ section. This ensures env vars like HTM_DATABASE__URL don't lose other
52
+ defaults.
53
+ ## deep_merge_hashes(base , overlay ) {: #method-c-deep_merge_hashes }
54
+ Deep merge helper for coercion
55
+ ## env() {: #method-c-env }
56
+ ## xdg_config_file() {: #method-c-xdg_config_file }
57
+ ## xdg_config_paths() {: #method-c-xdg_config_paths }
58
+ XDG Config Path Helpers
59
+
60
+ # Attributes
61
+ ## embedding_generator[RW] {: #attribute-i-embedding_generator }
62
+ Callable Accessors (not loaded from config sources)
63
+
64
+
65
+ ## logger[RW] {: #attribute-i-logger }
66
+ Returns the value of attribute logger.
67
+
68
+ ## proposition_extractor[RW] {: #attribute-i-proposition_extractor }
69
+ Callable Accessors (not loaded from config sources)
70
+
71
+
72
+ ## tag_extractor[RW] {: #attribute-i-tag_extractor }
73
+ Callable Accessors (not loaded from config sources)
74
+
75
+
76
+ ## token_counter[RW] {: #attribute-i-token_counter }
77
+ Returns the value of attribute token_counter.
78
+
79
+
80
+ # Instance Methods
81
+ ## anthropic_api_key() {: #method-i-anthropic_api_key }
82
+ ## azure_api_key() {: #method-i-azure_api_key }
83
+ ## azure_api_version() {: #method-i-azure_api_version }
84
+ ## azure_endpoint() {: #method-i-azure_endpoint }
85
+ ## bedrock_access_key() {: #method-i-bedrock_access_key }
86
+ ## bedrock_region() {: #method-i-bedrock_region }
87
+ ## bedrock_secret_key() {: #method-i-bedrock_secret_key }
88
+ ## chunk_overlap() {: #method-i-chunk_overlap }
89
+ ## chunk_size() {: #method-i-chunk_size }
90
+ Chunking convenience accessors
91
+
92
+ ## circuit_breaker_failure_threshold() {: #method-i-circuit_breaker_failure_threshold }
93
+ Circuit breaker convenience accessors
94
+
95
+ ## circuit_breaker_half_open_max_calls() {: #method-i-circuit_breaker_half_open_max_calls }
96
+ ## circuit_breaker_reset_timeout() {: #method-i-circuit_breaker_reset_timeout }
97
+ ## configure_ruby_llm(providernil) {: #method-i-configure_ruby_llm }
98
+ ## database_config() {: #method-i-database_config }
99
+ ## database_configured?() {: #method-i-database_configured? }
100
+ **`@return`** [Boolean]
101
+
102
+ ## database_url() {: #method-i-database_url }
103
+ Database convenience methods
104
+
105
+ ## deepseek_api_key() {: #method-i-deepseek_api_key }
106
+ ## development?() {: #method-i-development? }
107
+ **`@return`** [Boolean]
108
+
109
+ ## embedding_dimensions() {: #method-i-embedding_dimensions }
110
+ ## embedding_model() {: #method-i-embedding_model }
111
+ ## embedding_provider() {: #method-i-embedding_provider }
112
+ Embedding convenience accessors
113
+
114
+ ## embedding_timeout() {: #method-i-embedding_timeout }
115
+ ## environment() {: #method-i-environment }
116
+ ## extract_propositions() {: #method-i-extract_propositions }
117
+ ## gemini_api_key() {: #method-i-gemini_api_key }
118
+ ## huggingface_api_key() {: #method-i-huggingface_api_key }
119
+ ## initialize() {: #method-i-initialize }
120
+ Instance Methods
121
+
122
+
123
+ **`@return`** [Config] a new instance of Config
124
+
125
+ ## job_backend() {: #method-i-job_backend }
126
+ Job backend convenience accessor
127
+
128
+ ## max_embedding_dimension() {: #method-i-max_embedding_dimension }
129
+ ## max_tag_depth() {: #method-i-max_tag_depth }
130
+ ## normalize_ollama_model(model_name) {: #method-i-normalize_ollama_model }
131
+ Ollama Helpers
132
+
133
+
134
+ ## ollama_url() {: #method-i-ollama_url }
135
+ ## openai_api_key() {: #method-i-openai_api_key }
136
+ Provider credential convenience accessors
137
+
138
+ ## openai_organization() {: #method-i-openai_organization }
139
+ ## openai_project() {: #method-i-openai_project }
140
+ ## openrouter_api_key() {: #method-i-openrouter_api_key }
141
+ ## production?() {: #method-i-production? }
142
+ **`@return`** [Boolean]
143
+
144
+ ## proposition_model() {: #method-i-proposition_model }
145
+ ## proposition_provider() {: #method-i-proposition_provider }
146
+ Proposition convenience accessors
147
+
148
+ ## proposition_timeout() {: #method-i-proposition_timeout }
149
+ ## refresh_ollama_models!() {: #method-i-refresh_ollama_models! }
150
+ ## relevance_access_weight() {: #method-i-relevance_access_weight }
151
+ ## relevance_recency_half_life_hours() {: #method-i-relevance_recency_half_life_hours }
152
+ ## relevance_recency_weight() {: #method-i-relevance_recency_weight }
153
+ ## relevance_semantic_weight() {: #method-i-relevance_semantic_weight }
154
+ Relevance scoring convenience accessors
155
+
156
+ ## relevance_tag_weight() {: #method-i-relevance_tag_weight }
157
+ ## reset_to_defaults() {: #method-i-reset_to_defaults }
158
+ ## service_name() {: #method-i-service_name }
159
+ Service name convenience accessor
160
+
161
+ ## tag_model() {: #method-i-tag_model }
162
+ ## tag_provider() {: #method-i-tag_provider }
163
+ Tag convenience accessors
164
+
165
+ ## tag_timeout() {: #method-i-tag_timeout }
166
+ ## test?() {: #method-i-test? }
167
+ Environment Helpers
168
+
169
+
170
+ **`@return`** [Boolean]
171
+
172
+ ## validate!() {: #method-i-validate! }
173
+ ## validate_settings!() {: #method-i-validate_settings! }
@@ -0,0 +1,28 @@
1
+ # Class: HTM::ConfigSection
2
+ **Inherits:** Object
3
+
4
+
5
+ ConfigSection provides method access to nested configuration hashes
6
+
7
+
8
+ **`@example`**
9
+ ```ruby
10
+ section = ConfigSection.new(host: 'localhost', port: 5432)
11
+ section.host # => 'localhost'
12
+ section.port # => 5432
13
+ ```
14
+
15
+ # Instance Methods
16
+ ## `[](key)` {: #method-i-[] }
17
+ ## `[]=(key, value)` {: #method-i-[]= }
18
+ ## `each(&block)` {: #method-i-each }
19
+ ## `initialize(hash = {})` {: #method-i-initialize }
20
+ **`@return`** [ConfigSection] a new instance of ConfigSection
21
+
22
+ ## `keys()` {: #method-i-keys }
23
+ ## `merge(other)` {: #method-i-merge }
24
+ ## `method_missing(method, *args, &block)` {: #method-i-method_missing }
25
+ ## `respond_to_missing?(method, include_private = false)` {: #method-i-respond_to_missing? }
26
+ **`@return`** [Boolean]
27
+
28
+ ## `to_h()` {: #method-i-to_h }
@@ -8,7 +8,7 @@ initialization
8
8
 
9
9
  # Class Methods
10
10
  ## default_config() {: #method-c-default_config }
11
- Get default database configuration (respects HTM_ENV)
11
+ Get default database configuration
12
12
 
13
13
  Uses HTM::Config for database settings.
14
14
  **`@return`** [Hash, nil] Connection configuration hash with PG-style keys
@@ -20,8 +20,8 @@ This railtie automatically configures HTM when Rails boots:
20
20
  ```ruby
21
21
  # config/initializers/htm.rb
22
22
  HTM.configure do |config|
23
- config.embedding.model = 'custom-model'
24
- config.tag.model = 'custom-tag-model'
23
+ config.embedding_model = 'custom-model'
24
+ config.tag_model = 'custom-tag-model'
25
25
  end
26
26
  ```
27
27