htm 0.0.1 → 0.0.10

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 (184) hide show
  1. checksums.yaml +4 -4
  2. data/.aigcm_msg +1 -0
  3. data/.architecture/reviews/comprehensive-codebase-review.md +577 -0
  4. data/.claude/settings.local.json +92 -0
  5. data/.envrc +1 -0
  6. data/.irbrc +283 -80
  7. data/.tbls.yml +31 -0
  8. data/CHANGELOG.md +314 -16
  9. data/CLAUDE.md +603 -0
  10. data/README.md +76 -5
  11. data/Rakefile +5 -0
  12. data/SETUP.md +132 -101
  13. data/db/migrate/{20250101000001_enable_extensions.rb → 00001_enable_extensions.rb} +0 -1
  14. data/db/migrate/00002_create_robots.rb +11 -0
  15. data/db/migrate/00003_create_file_sources.rb +20 -0
  16. data/db/migrate/00004_create_nodes.rb +65 -0
  17. data/db/migrate/00005_create_tags.rb +13 -0
  18. data/db/migrate/00006_create_node_tags.rb +18 -0
  19. data/db/migrate/00007_create_robot_nodes.rb +26 -0
  20. data/db/migrate/00009_add_working_memory_to_robot_nodes.rb +12 -0
  21. data/db/schema.sql +390 -36
  22. data/docs/api/database.md +19 -232
  23. data/docs/api/embedding-service.md +1 -7
  24. data/docs/api/htm.md +305 -364
  25. data/docs/api/index.md +1 -7
  26. data/docs/api/long-term-memory.md +342 -590
  27. data/docs/api/yard/HTM/ActiveRecordConfig.md +23 -0
  28. data/docs/api/yard/HTM/AuthorizationError.md +11 -0
  29. data/docs/api/yard/HTM/CircuitBreaker.md +92 -0
  30. data/docs/api/yard/HTM/CircuitBreakerOpenError.md +34 -0
  31. data/docs/api/yard/HTM/Configuration.md +175 -0
  32. data/docs/api/yard/HTM/Database.md +99 -0
  33. data/docs/api/yard/HTM/DatabaseError.md +14 -0
  34. data/docs/api/yard/HTM/EmbeddingError.md +18 -0
  35. data/docs/api/yard/HTM/EmbeddingService.md +58 -0
  36. data/docs/api/yard/HTM/Error.md +11 -0
  37. data/docs/api/yard/HTM/JobAdapter.md +39 -0
  38. data/docs/api/yard/HTM/LongTermMemory.md +342 -0
  39. data/docs/api/yard/HTM/NotFoundError.md +17 -0
  40. data/docs/api/yard/HTM/Observability.md +107 -0
  41. data/docs/api/yard/HTM/QueryTimeoutError.md +19 -0
  42. data/docs/api/yard/HTM/Railtie.md +27 -0
  43. data/docs/api/yard/HTM/ResourceExhaustedError.md +13 -0
  44. data/docs/api/yard/HTM/TagError.md +18 -0
  45. data/docs/api/yard/HTM/TagService.md +67 -0
  46. data/docs/api/yard/HTM/Timeframe/Result.md +24 -0
  47. data/docs/api/yard/HTM/Timeframe.md +40 -0
  48. data/docs/api/yard/HTM/TimeframeExtractor/Result.md +24 -0
  49. data/docs/api/yard/HTM/TimeframeExtractor.md +45 -0
  50. data/docs/api/yard/HTM/ValidationError.md +20 -0
  51. data/docs/api/yard/HTM/WorkingMemory.md +131 -0
  52. data/docs/api/yard/HTM.md +80 -0
  53. data/docs/api/yard/index.csv +179 -0
  54. data/docs/api/yard-reference.md +51 -0
  55. data/docs/architecture/adrs/001-postgresql-timescaledb.md +1 -1
  56. data/docs/architecture/adrs/003-ollama-embeddings.md +1 -1
  57. data/docs/architecture/adrs/010-redis-working-memory-rejected.md +2 -27
  58. data/docs/architecture/adrs/index.md +2 -13
  59. data/docs/architecture/hive-mind.md +165 -166
  60. data/docs/architecture/index.md +2 -2
  61. data/docs/architecture/overview.md +5 -171
  62. data/docs/architecture/two-tier-memory.md +1 -35
  63. data/docs/assets/images/adr-010-current-architecture.svg +37 -0
  64. data/docs/assets/images/adr-010-proposed-architecture.svg +48 -0
  65. data/docs/assets/images/adr-dependency-tree.svg +93 -0
  66. data/docs/assets/images/class-hierarchy.svg +55 -0
  67. data/docs/assets/images/exception-hierarchy.svg +45 -0
  68. data/docs/assets/images/htm-architecture-overview.svg +83 -0
  69. data/docs/assets/images/htm-complete-memory-flow.svg +160 -0
  70. data/docs/assets/images/htm-context-assembly-flow.svg +148 -0
  71. data/docs/assets/images/htm-eviction-process.svg +141 -0
  72. data/docs/assets/images/htm-memory-addition-flow.svg +138 -0
  73. data/docs/assets/images/htm-memory-recall-flow.svg +152 -0
  74. data/docs/assets/images/htm-node-states.svg +123 -0
  75. data/docs/assets/images/project-structure.svg +78 -0
  76. data/docs/assets/images/test-directory-structure.svg +38 -0
  77. data/{dbdoc → docs/database}/README.md +127 -125
  78. data/docs/database/public.file_sources.md +42 -0
  79. data/docs/database/public.file_sources.svg +211 -0
  80. data/{dbdoc → docs/database}/public.node_tags.md +7 -8
  81. data/docs/database/public.node_tags.svg +239 -0
  82. data/{dbdoc → docs/database}/public.nodes.md +22 -17
  83. data/docs/database/public.nodes.svg +271 -0
  84. data/docs/database/public.robot_nodes.md +46 -0
  85. data/docs/database/public.robot_nodes.svg +243 -0
  86. data/{dbdoc → docs/database}/public.robots.md +2 -3
  87. data/docs/database/public.robots.svg +161 -0
  88. data/docs/database/public.tags.svg +139 -0
  89. data/{dbdoc → docs/database}/schema.json +941 -630
  90. data/docs/database/schema.svg +282 -0
  91. data/docs/development/index.md +1 -29
  92. data/docs/development/schema.md +134 -309
  93. data/docs/development/testing.md +1 -9
  94. data/docs/getting-started/index.md +47 -0
  95. data/docs/{installation.md → getting-started/installation.md} +2 -2
  96. data/docs/{quick-start.md → getting-started/quick-start.md} +5 -5
  97. data/docs/guides/adding-memories.md +295 -643
  98. data/docs/guides/recalling-memories.md +36 -1
  99. data/docs/guides/search-strategies.md +85 -51
  100. data/docs/images/htm-er-diagram.svg +156 -0
  101. data/docs/index.md +16 -31
  102. data/docs/multi_framework_support.md +4 -4
  103. data/examples/README.md +280 -0
  104. data/examples/basic_usage.rb +18 -16
  105. data/examples/cli_app/htm_cli.rb +146 -8
  106. data/examples/cli_app/temp.log +93 -0
  107. data/examples/custom_llm_configuration.rb +1 -2
  108. data/examples/example_app/app.rb +11 -14
  109. data/examples/file_loader_usage.rb +177 -0
  110. data/examples/robot_groups/lib/robot_group.rb +419 -0
  111. data/examples/robot_groups/lib/working_memory_channel.rb +140 -0
  112. data/examples/robot_groups/multi_process.rb +286 -0
  113. data/examples/robot_groups/robot_worker.rb +136 -0
  114. data/examples/robot_groups/same_process.rb +229 -0
  115. data/examples/sinatra_app/Gemfile +1 -0
  116. data/examples/sinatra_app/Gemfile.lock +166 -0
  117. data/examples/sinatra_app/app.rb +219 -24
  118. data/examples/timeframe_demo.rb +276 -0
  119. data/lib/htm/active_record_config.rb +10 -3
  120. data/lib/htm/circuit_breaker.rb +202 -0
  121. data/lib/htm/configuration.rb +313 -80
  122. data/lib/htm/database.rb +67 -36
  123. data/lib/htm/embedding_service.rb +39 -2
  124. data/lib/htm/errors.rb +131 -11
  125. data/lib/htm/{sinatra.rb → integrations/sinatra.rb} +87 -12
  126. data/lib/htm/job_adapter.rb +10 -3
  127. data/lib/htm/jobs/generate_embedding_job.rb +5 -4
  128. data/lib/htm/jobs/generate_tags_job.rb +4 -0
  129. data/lib/htm/loaders/markdown_loader.rb +263 -0
  130. data/lib/htm/loaders/paragraph_chunker.rb +112 -0
  131. data/lib/htm/long_term_memory.rb +601 -321
  132. data/lib/htm/models/file_source.rb +99 -0
  133. data/lib/htm/models/node.rb +116 -12
  134. data/lib/htm/models/robot.rb +53 -4
  135. data/lib/htm/models/robot_node.rb +51 -0
  136. data/lib/htm/models/tag.rb +302 -0
  137. data/lib/htm/observability.rb +395 -0
  138. data/lib/htm/tag_service.rb +60 -3
  139. data/lib/htm/tasks.rb +29 -0
  140. data/lib/htm/timeframe.rb +194 -0
  141. data/lib/htm/timeframe_extractor.rb +307 -0
  142. data/lib/htm/version.rb +1 -1
  143. data/lib/htm/working_memory.rb +165 -70
  144. data/lib/htm.rb +352 -133
  145. data/lib/tasks/doc.rake +300 -0
  146. data/lib/tasks/files.rake +299 -0
  147. data/lib/tasks/htm.rake +188 -2
  148. data/lib/tasks/jobs.rake +10 -12
  149. data/lib/tasks/tags.rake +194 -0
  150. data/mkdocs.yml +91 -9
  151. data/notes/ARCHITECTURE_REVIEW.md +1167 -0
  152. data/notes/IMPLEMENTATION_SUMMARY.md +606 -0
  153. data/notes/MULTI_FRAMEWORK_IMPLEMENTATION.md +451 -0
  154. data/notes/next_steps.md +100 -0
  155. data/notes/plan.md +627 -0
  156. data/notes/tag_ontology_enhancement_ideas.md +222 -0
  157. data/notes/timescaledb_removal_summary.md +200 -0
  158. metadata +177 -37
  159. data/db/migrate/20250101000002_create_robots.rb +0 -14
  160. data/db/migrate/20250101000003_create_nodes.rb +0 -42
  161. data/db/migrate/20250101000005_create_tags.rb +0 -38
  162. data/db/migrate/20250101000007_add_node_vector_indexes.rb +0 -30
  163. data/dbdoc/public.node_tags.svg +0 -112
  164. data/dbdoc/public.nodes.svg +0 -118
  165. data/dbdoc/public.robots.svg +0 -90
  166. data/dbdoc/public.tags.svg +0 -60
  167. data/dbdoc/schema.svg +0 -154
  168. data/{dbdoc → docs/database}/public.node_stats.md +0 -0
  169. data/{dbdoc → docs/database}/public.node_stats.svg +0 -0
  170. data/{dbdoc → docs/database}/public.nodes_tags.md +0 -0
  171. data/{dbdoc → docs/database}/public.nodes_tags.svg +0 -0
  172. data/{dbdoc → docs/database}/public.ontology_structure.md +0 -0
  173. data/{dbdoc → docs/database}/public.ontology_structure.svg +0 -0
  174. data/{dbdoc → docs/database}/public.operations_log.md +0 -0
  175. data/{dbdoc → docs/database}/public.operations_log.svg +0 -0
  176. data/{dbdoc → docs/database}/public.relationships.md +0 -0
  177. data/{dbdoc → docs/database}/public.relationships.svg +0 -0
  178. data/{dbdoc → docs/database}/public.robot_activity.md +0 -0
  179. data/{dbdoc → docs/database}/public.robot_activity.svg +0 -0
  180. data/{dbdoc → docs/database}/public.schema_migrations.md +0 -0
  181. data/{dbdoc → docs/database}/public.schema_migrations.svg +0 -0
  182. data/{dbdoc → docs/database}/public.tags.md +3 -3
  183. /data/{dbdoc → docs/database}/public.topic_relationships.md +0 -0
  184. /data/{dbdoc → docs/database}/public.topic_relationships.svg +0 -0
data/README.md CHANGED
@@ -27,7 +27,7 @@
27
27
 
28
28
  - **Client-Side Embeddings**
29
29
  - Automatic embedding generation before database insertion
30
- - Supports Ollama (local, default) and OpenAI
30
+ - Uses the [ruby_llm](https://ruby_llm.com) gem for LLM access
31
31
  - Configurable embedding providers and models
32
32
 
33
33
  - **Two-Tier Memory Architecture**
@@ -62,6 +62,12 @@
62
62
  - Tag-based categorization
63
63
  - Hierarchical tag structures
64
64
 
65
+ - **File Loading**
66
+ - Load markdown files into long-term memory
67
+ - Automatic paragraph-based chunking
68
+ - Source file tracking with re-sync support
69
+ - YAML frontmatter extraction as metadata
70
+
65
71
  ## Installation
66
72
 
67
73
  Add this line to your application's Gemfile:
@@ -264,6 +270,42 @@ memories = htm.recall(
264
270
  htm.forget(node_id, confirm: :confirmed)
265
271
  ```
266
272
 
273
+ ### Loading Files
274
+
275
+ HTM can load text-based files (currently markdown) into long-term memory with automatic chunking and source tracking.
276
+
277
+ ```ruby
278
+ htm = HTM.new(robot_name: "Document Loader")
279
+
280
+ # Load a single markdown file
281
+ result = htm.load_file("docs/guide.md")
282
+ # => { file_source_id: 1, chunks_created: 5, chunks_updated: 0, chunks_deleted: 0 }
283
+
284
+ # Load all markdown files in a directory
285
+ results = htm.load_directory("docs/", pattern: "**/*.md")
286
+
287
+ # Get nodes from a specific file
288
+ nodes = htm.nodes_from_file("docs/guide.md")
289
+
290
+ # Unload a file (soft deletes chunks)
291
+ htm.unload_file("docs/guide.md")
292
+ ```
293
+
294
+ **Features:**
295
+ - **Paragraph chunking**: Text split by blank lines, code blocks preserved
296
+ - **Source tracking**: Files tracked with mtime for automatic re-sync
297
+ - **YAML frontmatter**: Extracted and stored as metadata
298
+ - **Duplicate detection**: Content hash prevents duplicate nodes
299
+
300
+ **Rake tasks:**
301
+ ```bash
302
+ rake 'htm:files:load[docs/guide.md]' # Load a single file
303
+ rake 'htm:files:load_dir[docs/]' # Load all markdown files from directory
304
+ rake htm:files:list # List all loaded file sources
305
+ rake htm:files:sync # Sync all files (reload changed)
306
+ rake htm:files:stats # Show file loading statistics
307
+ ```
308
+
267
309
  ### Automatic Tag Extraction
268
310
 
269
311
  HTM automatically extracts hierarchical tags from content using LLM analysis. Tags are inferred from the content itself - you never specify them manually.
@@ -518,13 +560,14 @@ HTM provides a minimal, focused API with only 3 core instance methods for memory
518
560
 
519
561
  ### Core Memory Operations
520
562
 
521
- #### `remember(content, source: "")`
563
+ #### `remember(content, source: "", metadata: {})`
522
564
 
523
565
  Store information in memory. Embeddings and tags are automatically generated asynchronously.
524
566
 
525
567
  **Parameters:**
526
568
  - `content` (String, required) - The information to remember. Converted to string if nil. Returns ID of last node if empty.
527
569
  - `source` (String, optional) - Where the content came from (e.g., "user", "assistant", "system"). Defaults to empty string.
570
+ - `metadata` (Hash, optional) - Arbitrary key-value metadata stored as JSONB. Keys must be strings or symbols. Defaults to `{}`.
528
571
 
529
572
  **Returns:** Integer - The node ID of the stored memory
530
573
 
@@ -536,6 +579,12 @@ node_id = htm.remember("PostgreSQL is excellent for vector search with pgvector"
536
579
  # Store without source (uses default empty string)
537
580
  node_id = htm.remember("HTM uses two-tier memory architecture")
538
581
 
582
+ # Store with metadata
583
+ node_id = htm.remember(
584
+ "User prefers dark mode",
585
+ metadata: { category: "preference", priority: "high", version: 2 }
586
+ )
587
+
539
588
  # Nil/empty handling
540
589
  node_id = htm.remember(nil) # Returns ID of last node without creating duplicate
541
590
  node_id = htm.remember("") # Returns ID of last node without creating duplicate
@@ -543,7 +592,7 @@ node_id = htm.remember("") # Returns ID of last node without creating duplicat
543
592
 
544
593
  ---
545
594
 
546
- #### `recall(topic, timeframe: nil, limit: 20, strategy: :vector, with_relevance: false, query_tags: [])`
595
+ #### `recall(topic, timeframe: nil, limit: 20, strategy: :vector, with_relevance: false, query_tags: [], metadata: {})`
547
596
 
548
597
  Retrieve memories using temporal filtering and semantic/keyword search.
549
598
 
@@ -559,8 +608,9 @@ Retrieve memories using temporal filtering and semantic/keyword search.
559
608
  - `:hybrid` - Weighted combination (70% vector, 30% full-text)
560
609
  - `with_relevance` (Boolean, optional) - Include dynamic relevance scores. Default: false
561
610
  - `query_tags` (Array<String>, optional) - Filter results by tags. Default: []
611
+ - `metadata` (Hash, optional) - Filter results by metadata using JSONB containment (`@>`). Default: `{}`
562
612
 
563
- **Returns:** Array<Hash> - Matching memories with fields: `id`, `content`, `source`, `created_at`, `access_count`, (optionally `relevance`)
613
+ **Returns:** Array<Hash> - Matching memories with fields: `id`, `content`, `source`, `created_at`, `access_count`, `metadata`, (optionally `relevance`)
564
614
 
565
615
  **Example:**
566
616
  ```ruby
@@ -586,6 +636,21 @@ memories = htm.recall(
586
636
  query_tags: ["architecture"]
587
637
  )
588
638
  # => [{ "id" => 123, "content" => "...", "relevance" => 0.92, ... }, ...]
639
+
640
+ # Filter by metadata
641
+ memories = htm.recall(
642
+ "user preferences",
643
+ metadata: { category: "preference" }
644
+ )
645
+ # => Returns only nodes with metadata containing { category: "preference" }
646
+
647
+ # Combine metadata with other filters
648
+ memories = htm.recall(
649
+ "settings",
650
+ timeframe: "last month",
651
+ strategy: :hybrid,
652
+ metadata: { priority: "high", version: 2 }
653
+ )
589
654
  ```
590
655
 
591
656
  ---
@@ -1312,10 +1377,16 @@ See [htm_teamwork.md](htm_teamwork.md) for detailed design documentation and pla
1312
1377
  ### Database Schema
1313
1378
 
1314
1379
  - `robots`: Robot registry for all LLM agents using HTM
1315
- - `nodes`: Main memory storage with vector embeddings (pgvector), full-text search (tsvector), metadata
1380
+ - `nodes`: Main memory storage with vector embeddings (pgvector), full-text search (tsvector), JSONB metadata
1316
1381
  - `tags`: Hierarchical tag ontology (format: `root:level1:level2:level3`)
1317
1382
  - `node_tags`: Join table implementing many-to-many relationship between nodes and tags
1318
1383
 
1384
+ **Nodes Table Key Columns:**
1385
+ - `content`: The memory content
1386
+ - `embedding`: Vector embedding for semantic search (up to 2000 dimensions)
1387
+ - `metadata`: JSONB column for arbitrary key-value data (filterable via `@>` containment operator)
1388
+ - `content_hash`: SHA-256 hash for deduplication
1389
+
1319
1390
  ### Service Architecture
1320
1391
 
1321
1392
  HTM uses a layered architecture for LLM integration:
data/Rakefile CHANGED
@@ -28,6 +28,11 @@ task :example do
28
28
  ruby "examples/basic_usage.rb"
29
29
  end
30
30
 
31
+ desc "Run timeframe demo"
32
+ task :timeframe_demo do
33
+ ruby "examples/timeframe_demo.rb"
34
+ end
35
+
31
36
  desc "Show gem stats"
32
37
  task :stats do
33
38
  puts "\nHTM Gem Statistics:"
data/SETUP.md CHANGED
@@ -3,122 +3,165 @@
3
3
  ## Prerequisites
4
4
 
5
5
  1. **Ruby** (version 3.0 or higher)
6
- 2. **TimescaleDB Cloud Account** (already set up)
7
- 3. **Database Environment Variables** (already configured)
8
- 4. **Ollama** (for embeddings via RubyLLM)
6
+ 2. **PostgreSQL** (14+ with pgvector and pg_trgm extensions)
7
+ 3. **Ollama** (for embeddings via RubyLLM)
9
8
 
10
- ## Ollama Setup
9
+ ## PostgreSQL Setup
11
10
 
12
- HTM uses RubyLLM with the Ollama provider for generating embeddings. You need to install and run Ollama locally.
11
+ ### 1. Install PostgreSQL
13
12
 
14
- ### 1. Install Ollama
13
+ **macOS (via Homebrew):**
14
+ ```bash
15
+ brew install postgresql@17
16
+ brew services start postgresql@17
17
+ ```
15
18
 
16
- **macOS:**
19
+ **Ubuntu/Debian:**
17
20
  ```bash
18
- curl https://ollama.ai/install.sh | sh
21
+ sudo apt install postgresql postgresql-contrib
22
+ sudo systemctl start postgresql
19
23
  ```
20
24
 
21
- **Or download from:** https://ollama.ai/download
25
+ ### 2. Install pgvector Extension
22
26
 
23
- ### 2. Start Ollama Service
27
+ **macOS:**
28
+ ```bash
29
+ brew install pgvector
30
+ ```
24
31
 
32
+ **Ubuntu/Debian:**
25
33
  ```bash
26
- # Ollama typically starts automatically after installation
27
- # Verify it's running:
28
- curl http://localhost:11434/api/version
34
+ sudo apt install postgresql-17-pgvector
29
35
  ```
30
36
 
31
- ### 3. Pull the gpt-oss Model
37
+ **From source:**
38
+ ```bash
39
+ git clone https://github.com/pgvector/pgvector.git
40
+ cd pgvector
41
+ make
42
+ sudo make install
43
+ ```
44
+
45
+ ### 3. Create Database and Enable Extensions
32
46
 
33
47
  ```bash
34
- # Pull the default model used by HTM
35
- ollama pull gpt-oss
48
+ # Create the development database
49
+ createdb htm_development
36
50
 
37
- # Verify the model is available
38
- ollama list
51
+ # Enable required extensions
52
+ psql htm_development -c "CREATE EXTENSION IF NOT EXISTS vector;"
53
+ psql htm_development -c "CREATE EXTENSION IF NOT EXISTS pg_trgm;"
54
+
55
+ # Verify extensions
56
+ psql htm_development -c "SELECT extname, extversion FROM pg_extension;"
39
57
  ```
40
58
 
41
- ### 4. Test Embedding Generation
59
+ ### 4. Set Environment Variable
42
60
 
43
61
  ```bash
44
- # Test that embeddings work
45
- ollama run gpt-oss "Hello, world!"
46
- ```
62
+ # Add to your ~/.bashrc or ~/.zshrc
63
+ export HTM_DBURL="postgresql://postgres@localhost:5432/htm_development"
47
64
 
48
- ### Optional: Custom Ollama URL
65
+ # Or for a specific user with password
66
+ export HTM_DBURL="postgresql://username:password@localhost:5432/htm_development"
67
+ ```
49
68
 
50
- If Ollama is running on a different host/port, set the environment variable:
69
+ ### 5. Verify Connection
51
70
 
52
71
  ```bash
53
- export OLLAMA_URL="http://custom-host:11434"
72
+ cd /path/to/HTM
73
+ ruby test_connection.rb
74
+ ```
75
+
76
+ You should see:
77
+ ```
78
+ ✓ Connected successfully!
79
+ ✓ pgvector Extension: Version 0.8.x
80
+ ✓ pg_trgm Extension: Version 1.6
54
81
  ```
55
82
 
56
- ## Database Setup
83
+ ## Ollama Setup
57
84
 
58
- ### 1. Load Database Credentials
85
+ HTM uses RubyLLM with the Ollama provider for generating embeddings. You need to install and run Ollama locally.
59
86
 
60
- The HTM project uses environment variables to manage database credentials. These are defined in `~/.bashrc__tiger`.
87
+ ### 1. Install Ollama
61
88
 
89
+ **macOS:**
62
90
  ```bash
63
- # Load the Tiger database environment variables
64
- source ~/.bashrc__tiger
91
+ curl https://ollama.ai/install.sh | sh
65
92
  ```
66
93
 
67
- To make these variables available automatically in new shell sessions, ensure `~/.bashrc__tiger` is sourced in your `~/.bashrc` or `~/.bash_profile`.
68
-
69
- ### 2. Verify Connection
94
+ **Or download from:** https://ollama.ai/download
70
95
 
71
- Test the database connection:
96
+ ### 2. Start Ollama Service
72
97
 
73
98
  ```bash
74
- cd /path/to/HTM
75
- ruby test_connection.rb
99
+ # Ollama typically starts automatically after installation
100
+ # Verify it's running:
101
+ curl http://localhost:11434/api/version
76
102
  ```
77
103
 
78
- You should see:
104
+ ### 3. Pull Required Models
105
+
106
+ ```bash
107
+ # Pull the embedding model
108
+ ollama pull nomic-embed-text
109
+
110
+ # Pull the chat model (for tag extraction)
111
+ ollama pull llama3
112
+
113
+ # Verify models are available
114
+ ollama list
79
115
  ```
80
- ✓ Connected successfully!
81
- TimescaleDB Extension: Version 2.22.1
82
- ✓ pgvector Extension: Version 0.8.1
83
- ✓ pg_trgm Extension: Version 1.6
116
+
117
+ ### 4. Test Embedding Generation
118
+
119
+ ```bash
120
+ # Test that embeddings work
121
+ curl http://localhost:11434/api/embeddings -d '{
122
+ "model": "nomic-embed-text",
123
+ "prompt": "Hello, world!"
124
+ }'
84
125
  ```
85
126
 
86
- ### 3. Enable Extensions (One-time)
127
+ ### Optional: Custom Ollama URL
87
128
 
88
- Enable required PostgreSQL extensions (already done, but can be re-run safely):
129
+ If Ollama is running on a different host/port, set the environment variable:
89
130
 
90
131
  ```bash
91
- ruby enable_extensions.rb
132
+ export OLLAMA_URL="http://custom-host:11434"
92
133
  ```
93
134
 
94
135
  ## Environment Variables Reference
95
136
 
96
- After sourcing `~/.bashrc__tiger`, these variables are available:
97
-
98
137
  | Variable | Description | Example Value |
99
138
  |----------|-------------|---------------|
100
- | `HTM_SERVICE_NAME` | Service identifier | `db-67977` |
101
- | `HTM_DBNAME` | Database name | `tsdb` |
102
- | `HTM_DBUSER` | Database user | `tsdbadmin` |
103
- | `HTM_DBPASS` | Database password | `***` |
104
- | `HTM_DBURL` | Full connection URL (preferred) | `postgres://...` |
105
- | `HTM_DBPORT` | Database port | `37807` |
139
+ | `HTM_DBURL` | Full PostgreSQL connection URL (preferred) | `postgresql://postgres@localhost:5432/htm_development` |
140
+ | `HTM_DBNAME` | Database name (fallback) | `htm_development` |
141
+ | `HTM_DBUSER` | Database user (fallback) | `postgres` |
142
+ | `HTM_DBPASS` | Database password (fallback) | `` |
143
+ | `HTM_DBHOST` | Database host (fallback) | `localhost` |
144
+ | `HTM_DBPORT` | Database port (fallback) | `5432` |
145
+ | `OLLAMA_URL` | Ollama server URL | `http://localhost:11434` |
106
146
 
107
147
  ## Development Workflow
108
148
 
109
149
  ### Quick Start
110
150
 
111
151
  ```bash
112
- # 1. Source environment variables (if not in .bashrc)
113
- source ~/.bashrc__tiger
152
+ # 1. Set database URL (if not in shell config)
153
+ export HTM_DBURL="postgresql://postgres@localhost:5432/htm_development"
114
154
 
115
- # 2. Install dependencies (when gem is created)
155
+ # 2. Install dependencies
116
156
  bundle install
117
157
 
118
- # 3. Initialize database schema (when ready)
119
- ruby -r ./lib/htm -e "HTMDatabase.setup"
158
+ # 3. Initialize database schema
159
+ rake db_setup
160
+
161
+ # 4. Run tests
162
+ rake test
120
163
 
121
- # 4. Test HTM functionality (when implemented)
164
+ # 5. Try the basic example
122
165
  ruby examples/basic_usage.rb
123
166
  ```
124
167
 
@@ -130,10 +173,8 @@ HTM uses Minitest for testing:
130
173
  # Run all tests
131
174
  rake test
132
175
 
133
- # Or run directly with Ruby
134
- ruby test/htm_test.rb
135
-
136
176
  # Run specific test file
177
+ ruby test/htm_test.rb
137
178
  ruby test/embedding_service_test.rb
138
179
 
139
180
  # Run integration tests (requires database)
@@ -150,14 +191,18 @@ HTM/
150
191
  │ │ ├── database.rb # Database setup and schema
151
192
  │ │ ├── long_term_memory.rb # PostgreSQL-backed storage
152
193
  │ │ ├── working_memory.rb # In-memory active context
153
- │ │ ├── embedding_service.rb # RubyLLM embedding generation (Ollama/gpt-oss)
194
+ │ │ ├── embedding_service.rb # RubyLLM embedding generation
195
+ │ │ ├── tag_service.rb # Hierarchical tag extraction
196
+ │ │ ├── configuration.rb # Multi-provider LLM config
154
197
  │ │ └── version.rb # Version constant
155
- ├── sql/
198
+ ├── config/
199
+ │ └── database.yml # Database configuration
200
+ ├── db/
156
201
  │ └── schema.sql # Database schema
157
202
  ├── test/
158
203
  │ ├── test_helper.rb # Minitest configuration
159
204
  │ ├── htm_test.rb # Basic HTM tests
160
- │ ├── embedding_service_test.rb # Embedding tests (RubyLLM/Ollama)
205
+ │ ├── embedding_service_test.rb # Embedding tests
161
206
  │ └── integration_test.rb # Full integration tests
162
207
  ├── examples/
163
208
  │ └── basic_usage.rb # Basic usage example
@@ -165,24 +210,12 @@ HTM/
165
210
  ├── enable_extensions.rb # Enable PostgreSQL extensions
166
211
  ├── SETUP.md # This file
167
212
  ├── README.md # Project overview
168
- ├── htm_teamwork.md # Planning and design doc
213
+ ├── CLAUDE.md # AI assistant instructions
169
214
  ├── Gemfile
170
215
  ├── htm.gemspec
171
216
  └── Rakefile # Rake tasks
172
217
  ```
173
218
 
174
- ## Next Steps
175
-
176
- 1. **Phase 1**: Create basic gem structure
177
- 2. **Phase 2**: Implement database schema
178
- 3. **Phase 3**: Implement LongTermMemory class
179
- 4. **Phase 4**: Implement WorkingMemory class
180
- 5. **Phase 5**: Implement HTM main class
181
- 6. **Phase 6**: Add tests
182
- 7. **Phase 7**: Create examples
183
-
184
- See `htm_teamwork.md` for detailed roadmap.
185
-
186
219
  ## Troubleshooting
187
220
 
188
221
  ### Ollama Issues
@@ -193,24 +226,20 @@ If you encounter embedding errors:
193
226
  # Verify Ollama is running
194
227
  curl http://localhost:11434/api/version
195
228
 
196
- # Check if gpt-oss model is available
197
- ollama list | grep gpt-oss
229
+ # Check if models are available
230
+ ollama list
198
231
 
199
232
  # Test embedding generation
200
- ollama run gpt-oss "Test embedding"
233
+ curl http://localhost:11434/api/embeddings -d '{"model": "nomic-embed-text", "prompt": "Test"}'
201
234
 
202
- # View Ollama logs
203
- ollama logs
204
-
205
- # Restart Ollama service
206
- # On macOS, Ollama runs as a background service
207
- # Check Activity Monitor or restart from the menu bar
235
+ # View Ollama logs (macOS)
236
+ # Check Console.app or Activity Monitor
208
237
  ```
209
238
 
210
239
  **Common Ollama Errors:**
211
240
 
212
241
  - **"connection refused"**: Ollama service is not running. Start Ollama from Applications or via CLI.
213
- - **"model not found"**: Run `ollama pull gpt-oss` to download the model.
242
+ - **"model not found"**: Run `ollama pull nomic-embed-text` to download the model.
214
243
  - **Custom URL not working**: Ensure `OLLAMA_URL` environment variable is set correctly.
215
244
 
216
245
  ### Database Connection Issues
@@ -218,14 +247,15 @@ ollama logs
218
247
  If you get connection errors:
219
248
 
220
249
  ```bash
221
- # Verify environment variables are set
250
+ # Verify environment variable is set
222
251
  echo $HTM_DBURL
223
252
 
224
253
  # Test connection manually
225
- psql $HTM_DBURL
254
+ psql $HTM_DBURL -c "SELECT 1"
226
255
 
227
- # Check if ~/.bashrc__tiger is sourced
228
- grep "bashrc__tiger" ~/.bashrc
256
+ # Check PostgreSQL is running
257
+ brew services list | grep postgresql # macOS
258
+ systemctl status postgresql # Linux
229
259
  ```
230
260
 
231
261
  ### Extension Issues
@@ -233,31 +263,32 @@ grep "bashrc__tiger" ~/.bashrc
233
263
  If extensions aren't available:
234
264
 
235
265
  ```bash
266
+ # Check if pgvector is installed
267
+ psql htm_development -c "SELECT * FROM pg_available_extensions WHERE name = 'vector';"
268
+
236
269
  # Re-run extension setup
237
270
  ruby enable_extensions.rb
238
271
 
239
- # Check extension status manually
240
- psql $HTM_DBURL -c "SELECT extname, extversion FROM pg_extension ORDER BY extname"
272
+ # Check extension status
273
+ psql htm_development -c "SELECT extname, extversion FROM pg_extension ORDER BY extname"
241
274
  ```
242
275
 
243
- ### SSL Issues
276
+ ### Test Database
244
277
 
245
- The TimescaleDB Cloud instance requires SSL. If you see SSL errors:
278
+ For running tests, create a separate test database:
246
279
 
247
280
  ```bash
248
- # Ensure sslmode is set in connection URL
249
- echo $HTM_DBURL | grep sslmode
250
- # Should show: sslmode=require
281
+ createdb htm_development_test
282
+ psql htm_development_test -c "CREATE EXTENSION IF NOT EXISTS vector; CREATE EXTENSION IF NOT EXISTS pg_trgm;"
251
283
  ```
252
284
 
253
285
  ## Resources
254
286
 
255
287
  - **Ollama**: https://ollama.ai/
256
- - **RubyLLM**: https://github.com/madbomber/ruby_llm
257
- - **TimescaleDB Docs**: https://docs.timescale.com/
288
+ - **RubyLLM**: https://github.com/crmne/ruby_llm
258
289
  - **pgvector Docs**: https://github.com/pgvector/pgvector
259
- - **Planning Document**: `htm_teamwork.md`
260
290
  - **PostgreSQL Docs**: https://www.postgresql.org/docs/
291
+ - **Planning Document**: `htm_teamwork.md`
261
292
 
262
293
  ## Support
263
294
 
@@ -2,7 +2,6 @@
2
2
 
3
3
  class EnableExtensions < ActiveRecord::Migration[7.1]
4
4
  def up
5
- # Note: On TimescaleDB Cloud, the extension is named 'vector' not 'pgvector'
6
5
  enable_extension 'vector'
7
6
  enable_extension 'pg_trgm'
8
7
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateRobots < ActiveRecord::Migration[7.1]
4
+ def change
5
+ create_table :robots, comment: 'Registry of all LLM robots using the HTM system' do |t|
6
+ t.text :name, comment: 'Human-readable name for the robot'
7
+ t.timestamptz :created_at, default: -> { 'CURRENT_TIMESTAMP' }, comment: 'When the robot was first registered'
8
+ t.timestamptz :last_active, default: -> { 'CURRENT_TIMESTAMP' }, comment: 'Last time the robot accessed the system'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateFileSources < ActiveRecord::Migration[7.1]
4
+ def change
5
+ create_table :file_sources, comment: 'Source file metadata for loaded documents' do |t|
6
+ t.text :file_path, null: false, comment: 'Absolute path to source file'
7
+ t.string :file_hash, limit: 64, comment: 'SHA-256 hash of file content'
8
+ t.timestamptz :mtime, comment: 'File modification time'
9
+ t.integer :file_size, comment: 'File size in bytes'
10
+ t.jsonb :frontmatter, default: {}, comment: 'Parsed YAML frontmatter'
11
+ t.timestamptz :last_synced_at, comment: 'When file was last synced to HTM'
12
+ t.timestamptz :created_at, default: -> { 'CURRENT_TIMESTAMP' }
13
+ t.timestamptz :updated_at, default: -> { 'CURRENT_TIMESTAMP' }
14
+ end
15
+
16
+ add_index :file_sources, :file_path, unique: true, name: 'idx_file_sources_path_unique'
17
+ add_index :file_sources, :file_hash, name: 'idx_file_sources_hash'
18
+ add_index :file_sources, :last_synced_at, name: 'idx_file_sources_last_synced'
19
+ end
20
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateNodes < ActiveRecord::Migration[7.1]
4
+ def change
5
+ create_table :nodes, comment: 'Core memory storage for conversation messages and context' do |t|
6
+ t.text :content, null: false, comment: 'The conversation message/utterance content'
7
+ t.integer :access_count, default: 0, null: false, comment: 'Number of times this node has been accessed/retrieved'
8
+ t.timestamptz :created_at, default: -> { 'CURRENT_TIMESTAMP' }, comment: 'When this memory was created'
9
+ t.timestamptz :updated_at, default: -> { 'CURRENT_TIMESTAMP' }, comment: 'When this memory was last modified'
10
+ t.timestamptz :last_accessed, default: -> { 'CURRENT_TIMESTAMP' }, comment: 'When this memory was last accessed'
11
+ t.integer :token_count, comment: 'Number of tokens in the content (for context budget management)'
12
+ t.vector :embedding, limit: 2000, comment: 'Vector embedding (max 2000 dimensions) for semantic search'
13
+ t.integer :embedding_dimension, comment: 'Actual number of dimensions used in the embedding vector (max 2000)'
14
+ t.string :content_hash, limit: 64, comment: 'SHA-256 hash of content for deduplication'
15
+ t.timestamptz :deleted_at, comment: 'Soft delete timestamp - node is considered deleted when set'
16
+ t.bigint :source_id, comment: 'Reference to source file (for file-loaded nodes)'
17
+ t.integer :chunk_position, comment: 'Position within source file (0-indexed)'
18
+ t.jsonb :metadata, default: {}, null: false, comment: 'Flexible metadata storage (memory_type, importance, source, etc.)'
19
+ end
20
+
21
+ # Basic indexes for common queries
22
+ add_index :nodes, :created_at, name: 'idx_nodes_created_at'
23
+ add_index :nodes, :updated_at, name: 'idx_nodes_updated_at'
24
+ add_index :nodes, :last_accessed, name: 'idx_nodes_last_accessed'
25
+ add_index :nodes, :access_count, name: 'idx_nodes_access_count'
26
+ add_index :nodes, :content_hash, unique: true, name: 'idx_nodes_content_hash_unique'
27
+ add_index :nodes, :deleted_at, name: 'idx_nodes_deleted_at'
28
+ add_index :nodes, :source_id, name: 'idx_nodes_source_id'
29
+ add_index :nodes, [:source_id, :chunk_position], name: 'idx_nodes_source_chunk_position'
30
+
31
+ # Partial index for efficiently querying non-deleted nodes
32
+ add_index :nodes, :created_at, name: 'idx_nodes_not_deleted_created_at', where: 'deleted_at IS NULL'
33
+
34
+ # GIN index for JSONB metadata queries
35
+ add_index :nodes, :metadata, using: :gin, name: 'idx_nodes_metadata'
36
+
37
+ # Vector similarity search index (HNSW for better performance)
38
+ execute <<-SQL
39
+ CREATE INDEX idx_nodes_embedding ON nodes
40
+ USING hnsw (embedding vector_cosine_ops)
41
+ WITH (m = 16, ef_construction = 64)
42
+ SQL
43
+
44
+ # Full-text search on conversation content
45
+ execute <<-SQL
46
+ CREATE INDEX idx_nodes_content_gin ON nodes
47
+ USING gin(to_tsvector('english', content))
48
+ SQL
49
+
50
+ # Trigram indexes for fuzzy matching on conversation content
51
+ execute <<-SQL
52
+ CREATE INDEX idx_nodes_content_trgm ON nodes
53
+ USING gin(content gin_trgm_ops)
54
+ SQL
55
+
56
+ # Check constraint for embedding dimensions
57
+ execute <<-SQL
58
+ ALTER TABLE nodes ADD CONSTRAINT check_embedding_dimension
59
+ CHECK (embedding_dimension IS NULL OR (embedding_dimension > 0 AND embedding_dimension <= 2000))
60
+ SQL
61
+
62
+ # Foreign key to file_sources table
63
+ add_foreign_key :nodes, :file_sources, column: :source_id, on_delete: :nullify
64
+ end
65
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateTags < ActiveRecord::Migration[7.1]
4
+ def change
5
+ create_table :tags, comment: 'Unique tag names for categorization' do |t|
6
+ t.text :name, null: false, comment: 'Hierarchical tag in format: root:level1:level2 (e.g., database:postgresql:timescaledb)'
7
+ t.timestamptz :created_at, default: -> { 'CURRENT_TIMESTAMP' }, comment: 'When this tag was created'
8
+ end
9
+
10
+ add_index :tags, :name, unique: true, name: 'idx_tags_name_unique'
11
+ add_index :tags, :name, using: :btree, opclass: :text_pattern_ops, name: 'idx_tags_name_pattern'
12
+ end
13
+ end