htm 0.0.11 → 0.0.15
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.
- checksums.yaml +4 -4
- data/.dictate.toml +46 -0
- data/.envrc +2 -0
- data/CHANGELOG.md +85 -2
- data/README.md +348 -79
- data/Rakefile +14 -2
- data/bin/htm_mcp.rb +94 -0
- data/config/database.yml +20 -13
- data/db/migrate/00003_create_file_sources.rb +5 -0
- data/db/migrate/00004_create_nodes.rb +17 -0
- data/db/migrate/00005_create_tags.rb +7 -0
- data/db/migrate/00006_create_node_tags.rb +2 -0
- data/db/migrate/00007_create_robot_nodes.rb +7 -0
- data/db/schema.sql +69 -100
- data/docs/api/index.md +1 -1
- data/docs/api/yard/HTM/Configuration.md +54 -0
- data/docs/api/yard/HTM/Database.md +13 -10
- data/docs/api/yard/HTM/EmbeddingService.md +5 -1
- data/docs/api/yard/HTM/LongTermMemory.md +18 -277
- data/docs/api/yard/HTM/PropositionError.md +18 -0
- data/docs/api/yard/HTM/PropositionService.md +66 -0
- data/docs/api/yard/HTM/QueryCache.md +88 -0
- data/docs/api/yard/HTM/RobotGroup.md +481 -0
- data/docs/api/yard/HTM/SqlBuilder.md +108 -0
- data/docs/api/yard/HTM/TagService.md +4 -0
- data/docs/api/yard/HTM/Telemetry/NullInstrument.md +13 -0
- data/docs/api/yard/HTM/Telemetry/NullMeter.md +15 -0
- data/docs/api/yard/HTM/Telemetry.md +109 -0
- data/docs/api/yard/HTM/WorkingMemoryChannel.md +176 -0
- data/docs/api/yard/HTM.md +8 -22
- data/docs/api/yard/index.csv +102 -25
- data/docs/api/yard-reference.md +8 -0
- data/docs/architecture/index.md +1 -1
- data/docs/assets/images/multi-provider-failover.svg +51 -0
- data/docs/assets/images/robot-group-architecture.svg +65 -0
- data/docs/database/README.md +3 -3
- data/docs/database/public.file_sources.svg +29 -21
- data/docs/database/public.node_tags.md +2 -0
- data/docs/database/public.node_tags.svg +53 -41
- data/docs/database/public.nodes.md +2 -0
- data/docs/database/public.nodes.svg +52 -40
- data/docs/database/public.robot_nodes.md +2 -0
- data/docs/database/public.robot_nodes.svg +30 -22
- data/docs/database/public.robots.svg +16 -12
- data/docs/database/public.tags.md +3 -0
- data/docs/database/public.tags.svg +41 -33
- data/docs/database/schema.json +66 -0
- data/docs/database/schema.svg +60 -48
- data/docs/development/index.md +14 -1
- data/docs/development/rake-tasks.md +1068 -0
- data/docs/getting-started/index.md +1 -1
- data/docs/getting-started/quick-start.md +144 -155
- data/docs/guides/adding-memories.md +2 -3
- data/docs/guides/context-assembly.md +185 -184
- data/docs/guides/getting-started.md +154 -148
- data/docs/guides/index.md +8 -1
- data/docs/guides/long-term-memory.md +60 -92
- data/docs/guides/mcp-server.md +617 -0
- data/docs/guides/multi-robot.md +249 -345
- data/docs/guides/recalling-memories.md +153 -163
- data/docs/guides/robot-groups.md +604 -0
- data/docs/guides/search-strategies.md +61 -58
- data/docs/guides/working-memory.md +103 -136
- data/docs/images/telemetry-architecture.svg +153 -0
- data/docs/index.md +30 -26
- data/docs/telemetry.md +391 -0
- data/examples/README.md +46 -1
- data/examples/cli_app/README.md +1 -1
- data/examples/cli_app/htm_cli.rb +1 -1
- data/examples/robot_groups/robot_worker.rb +1 -2
- data/examples/robot_groups/same_process.rb +1 -4
- data/examples/sinatra_app/app.rb +1 -1
- data/examples/telemetry/README.md +147 -0
- data/examples/telemetry/SETUP_README.md +169 -0
- data/examples/telemetry/demo.rb +498 -0
- data/examples/telemetry/grafana/dashboards/htm-metrics.json +457 -0
- data/lib/htm/configuration.rb +261 -70
- data/lib/htm/database.rb +46 -22
- data/lib/htm/embedding_service.rb +24 -14
- data/lib/htm/errors.rb +15 -1
- data/lib/htm/jobs/generate_embedding_job.rb +19 -0
- data/lib/htm/jobs/generate_propositions_job.rb +103 -0
- data/lib/htm/jobs/generate_tags_job.rb +24 -0
- data/lib/htm/loaders/markdown_chunker.rb +79 -0
- data/lib/htm/loaders/markdown_loader.rb +41 -15
- data/lib/htm/long_term_memory/fulltext_search.rb +138 -0
- data/lib/htm/long_term_memory/hybrid_search.rb +324 -0
- data/lib/htm/long_term_memory/node_operations.rb +209 -0
- data/lib/htm/long_term_memory/relevance_scorer.rb +355 -0
- data/lib/htm/long_term_memory/robot_operations.rb +34 -0
- data/lib/htm/long_term_memory/tag_operations.rb +428 -0
- data/lib/htm/long_term_memory/vector_search.rb +109 -0
- data/lib/htm/long_term_memory.rb +51 -1153
- data/lib/htm/models/node.rb +35 -2
- data/lib/htm/models/node_tag.rb +31 -0
- data/lib/htm/models/robot_node.rb +31 -0
- data/lib/htm/models/tag.rb +44 -0
- data/lib/htm/proposition_service.rb +169 -0
- data/lib/htm/query_cache.rb +214 -0
- data/lib/htm/robot_group.rb +721 -0
- data/lib/htm/sql_builder.rb +178 -0
- data/lib/htm/tag_service.rb +16 -6
- data/lib/htm/tasks.rb +8 -2
- data/lib/htm/telemetry.rb +224 -0
- data/lib/htm/version.rb +1 -1
- data/lib/htm/working_memory_channel.rb +250 -0
- data/lib/htm.rb +66 -3
- data/lib/tasks/doc.rake +1 -1
- data/lib/tasks/htm.rake +259 -13
- data/mkdocs.yml +98 -96
- metadata +55 -20
- data/.aigcm_msg +0 -1
- data/.claude/settings.local.json +0 -95
- data/CLAUDE.md +0 -603
- data/db/migrate/00009_add_working_memory_to_robot_nodes.rb +0 -12
- data/examples/cli_app/temp.log +0 -93
- data/examples/robot_groups/lib/robot_group.rb +0 -419
- data/examples/robot_groups/lib/working_memory_channel.rb +0 -140
- data/lib/htm/loaders/paragraph_chunker.rb +0 -112
- data/notes/ARCHITECTURE_REVIEW.md +0 -1167
- data/notes/IMPLEMENTATION_SUMMARY.md +0 -606
- data/notes/MULTI_FRAMEWORK_IMPLEMENTATION.md +0 -451
- data/notes/next_steps.md +0 -100
- data/notes/plan.md +0 -627
- data/notes/tag_ontology_enhancement_ideas.md +0 -222
- data/notes/timescaledb_removal_summary.md +0 -200
|
@@ -81,6 +81,21 @@ Returns the value of attribute bedrock_region.
|
|
|
81
81
|
## bedrock_secret_key[RW] {: #attribute-i-bedrock_secret_key }
|
|
82
82
|
Returns the value of attribute bedrock_secret_key.
|
|
83
83
|
|
|
84
|
+
## chunk_overlap[RW] {: #attribute-i-chunk_overlap }
|
|
85
|
+
Character overlap between chunks (default: 64)
|
|
86
|
+
|
|
87
|
+
## chunk_size[RW] {: #attribute-i-chunk_size }
|
|
88
|
+
Chunking configuration (for file loading)
|
|
89
|
+
|
|
90
|
+
## circuit_breaker_failure_threshold[RW] {: #attribute-i-circuit_breaker_failure_threshold }
|
|
91
|
+
Circuit breaker configuration
|
|
92
|
+
|
|
93
|
+
## circuit_breaker_half_open_max_calls[RW] {: #attribute-i-circuit_breaker_half_open_max_calls }
|
|
94
|
+
Successes to close (default: 3)
|
|
95
|
+
|
|
96
|
+
## circuit_breaker_reset_timeout[RW] {: #attribute-i-circuit_breaker_reset_timeout }
|
|
97
|
+
Seconds before half-open (default: 60)
|
|
98
|
+
|
|
84
99
|
## connection_timeout[RW] {: #attribute-i-connection_timeout }
|
|
85
100
|
Returns the value of attribute connection_timeout.
|
|
86
101
|
|
|
@@ -102,6 +117,9 @@ Returns the value of attribute embedding_provider.
|
|
|
102
117
|
## embedding_timeout[RW] {: #attribute-i-embedding_timeout }
|
|
103
118
|
Returns the value of attribute embedding_timeout.
|
|
104
119
|
|
|
120
|
+
## extract_propositions[RW] {: #attribute-i-extract_propositions }
|
|
121
|
+
Returns the value of attribute extract_propositions.
|
|
122
|
+
|
|
105
123
|
## gemini_api_key[RW] {: #attribute-i-gemini_api_key }
|
|
106
124
|
Returns the value of attribute gemini_api_key.
|
|
107
125
|
|
|
@@ -114,6 +132,12 @@ Returns the value of attribute job_backend.
|
|
|
114
132
|
## logger[RW] {: #attribute-i-logger }
|
|
115
133
|
Returns the value of attribute logger.
|
|
116
134
|
|
|
135
|
+
## max_embedding_dimension[RW] {: #attribute-i-max_embedding_dimension }
|
|
136
|
+
Limit configuration
|
|
137
|
+
|
|
138
|
+
## max_tag_depth[RW] {: #attribute-i-max_tag_depth }
|
|
139
|
+
Max tag hierarchy depth (default: 4)
|
|
140
|
+
|
|
117
141
|
## ollama_url[RW] {: #attribute-i-ollama_url }
|
|
118
142
|
Returns the value of attribute ollama_url.
|
|
119
143
|
|
|
@@ -129,6 +153,33 @@ Provider-specific API keys and endpoints
|
|
|
129
153
|
## openrouter_api_key[RW] {: #attribute-i-openrouter_api_key }
|
|
130
154
|
Returns the value of attribute openrouter_api_key.
|
|
131
155
|
|
|
156
|
+
## proposition_extractor[RW] {: #attribute-i-proposition_extractor }
|
|
157
|
+
Returns the value of attribute proposition_extractor.
|
|
158
|
+
|
|
159
|
+
## proposition_model[RW] {: #attribute-i-proposition_model }
|
|
160
|
+
Returns the value of attribute proposition_model.
|
|
161
|
+
|
|
162
|
+
## proposition_provider[RW] {: #attribute-i-proposition_provider }
|
|
163
|
+
Returns the value of attribute proposition_provider.
|
|
164
|
+
|
|
165
|
+
## proposition_timeout[RW] {: #attribute-i-proposition_timeout }
|
|
166
|
+
Returns the value of attribute proposition_timeout.
|
|
167
|
+
|
|
168
|
+
## relevance_access_weight[RW] {: #attribute-i-relevance_access_weight }
|
|
169
|
+
Access frequency weight (default: 0.1)
|
|
170
|
+
|
|
171
|
+
## relevance_recency_half_life_hours[RW] {: #attribute-i-relevance_recency_half_life_hours }
|
|
172
|
+
Decay half-life in hours (default: 168 = 1 week)
|
|
173
|
+
|
|
174
|
+
## relevance_recency_weight[RW] {: #attribute-i-relevance_recency_weight }
|
|
175
|
+
Temporal freshness weight (default: 0.1)
|
|
176
|
+
|
|
177
|
+
## relevance_semantic_weight[RW] {: #attribute-i-relevance_semantic_weight }
|
|
178
|
+
Relevance scoring weights (must sum to 1.0)
|
|
179
|
+
|
|
180
|
+
## relevance_tag_weight[RW] {: #attribute-i-relevance_tag_weight }
|
|
181
|
+
Tag overlap weight (default: 0.3)
|
|
182
|
+
|
|
132
183
|
## tag_extractor[RW] {: #attribute-i-tag_extractor }
|
|
133
184
|
Returns the value of attribute tag_extractor.
|
|
134
185
|
|
|
@@ -141,6 +192,9 @@ Returns the value of attribute tag_provider.
|
|
|
141
192
|
## tag_timeout[RW] {: #attribute-i-tag_timeout }
|
|
142
193
|
Returns the value of attribute tag_timeout.
|
|
143
194
|
|
|
195
|
+
## telemetry_enabled[RW] {: #attribute-i-telemetry_enabled }
|
|
196
|
+
Enable OpenTelemetry metrics (default: false)
|
|
197
|
+
|
|
144
198
|
## token_counter[RW] {: #attribute-i-token_counter }
|
|
145
199
|
Returns the value of attribute token_counter.
|
|
146
200
|
|
|
@@ -8,20 +8,23 @@ initialization
|
|
|
8
8
|
|
|
9
9
|
# Class Methods
|
|
10
10
|
## default_config() {: #method-c-default_config }
|
|
11
|
-
Get default database configuration
|
|
12
|
-
|
|
11
|
+
Get default database configuration (respects RAILS_ENV)
|
|
12
|
+
|
|
13
|
+
Uses ActiveRecordConfig which reads from config/database.yml and respects
|
|
14
|
+
RAILS_ENV for environment-specific database selection.
|
|
15
|
+
**`@return`** [Hash, nil] Connection configuration hash with PG-style keys
|
|
13
16
|
|
|
14
17
|
## drop(db_url nil) {: #method-c-drop }
|
|
15
|
-
Drop all HTM tables
|
|
16
|
-
**`@param`** [String] Database connection URL (uses
|
|
18
|
+
Drop all HTM tables (respects RAILS_ENV)
|
|
19
|
+
**`@param`** [String] Database connection URL (uses default_config if not provided)
|
|
17
20
|
|
|
18
21
|
**`@return`** [void]
|
|
19
22
|
|
|
20
23
|
## dump_schema(db_url nil) {: #method-c-dump_schema }
|
|
21
|
-
Dump current database schema to db/schema.sql
|
|
24
|
+
Dump current database schema to db/schema.sql (respects RAILS_ENV)
|
|
22
25
|
|
|
23
26
|
Uses pg_dump to create a clean SQL schema file without data
|
|
24
|
-
**`@param`** [String] Database connection URL (uses
|
|
27
|
+
**`@param`** [String] Database connection URL (uses default_config if not provided)
|
|
25
28
|
|
|
26
29
|
**`@return`** [void]
|
|
27
30
|
|
|
@@ -39,16 +42,16 @@ comprehensive database documentation including:
|
|
|
39
42
|
**`@return`** [void]
|
|
40
43
|
|
|
41
44
|
## info(db_url nil) {: #method-c-info }
|
|
42
|
-
Show database info
|
|
43
|
-
**`@param`** [String] Database connection URL (uses
|
|
45
|
+
Show database info (respects RAILS_ENV)
|
|
46
|
+
**`@param`** [String] Database connection URL (uses default_config if not provided)
|
|
44
47
|
|
|
45
48
|
**`@return`** [void]
|
|
46
49
|
|
|
47
50
|
## load_schema(db_url nil) {: #method-c-load_schema }
|
|
48
|
-
Load schema from db/schema.sql
|
|
51
|
+
Load schema from db/schema.sql (respects RAILS_ENV)
|
|
49
52
|
|
|
50
53
|
Uses psql to load the schema file
|
|
51
|
-
**`@param`** [String] Database connection URL (uses
|
|
54
|
+
**`@param`** [String] Database connection URL (uses default_config if not provided)
|
|
52
55
|
|
|
53
56
|
**`@return`** [void]
|
|
54
57
|
|
|
@@ -39,8 +39,12 @@ Generate embedding with validation and processing
|
|
|
39
39
|
storage_dimension: Integer # Padded dimension (2000)
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
## max_dimension() {: #method-c-max_dimension }
|
|
43
|
+
Maximum embedding dimension (configurable, default 2000)
|
|
44
|
+
**`@return`** [Integer] Max dimensions for pgvector HNSW index
|
|
45
|
+
|
|
42
46
|
## pad_embedding(embedding ) {: #method-c-pad_embedding }
|
|
43
|
-
Pad embedding to
|
|
47
|
+
Pad embedding to max_dimension with zeros
|
|
44
48
|
**`@param`** [Array<Float>] Original embedding
|
|
45
49
|
|
|
46
50
|
**`@return`** [Array<Float>] Padded embedding
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Class: HTM::LongTermMemory
|
|
2
2
|
**Inherits:** Object
|
|
3
3
|
|
|
4
|
+
**Includes:** FulltextSearch, HybridSearch, NodeOperations, RelevanceScorer, RobotOperations, TagOperations, VectorSearch
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
Long-term Memory - PostgreSQL/TimescaleDB-backed permanent storage
|
|
6
8
|
|
|
@@ -13,6 +15,21 @@ LongTermMemory provides durable storage for all memory nodes with:
|
|
|
13
15
|
* ActiveRecord ORM for data access
|
|
14
16
|
* Query result caching for efficiency
|
|
15
17
|
|
|
18
|
+
This class uses standalone utility classes and modules:
|
|
19
|
+
|
|
20
|
+
Standalone classes (used via class methods or instances):
|
|
21
|
+
* HTM::SqlBuilder: SQL condition building helpers (class methods)
|
|
22
|
+
* HTM::QueryCache: Query result caching (instantiated as @cache)
|
|
23
|
+
|
|
24
|
+
Included modules:
|
|
25
|
+
* RelevanceScorer: Dynamic relevance scoring
|
|
26
|
+
* NodeOperations: Node CRUD operations
|
|
27
|
+
* RobotOperations: Robot registration and activity
|
|
28
|
+
* TagOperations: Tag management
|
|
29
|
+
* VectorSearch: Vector similarity search
|
|
30
|
+
* FulltextSearch: Full-text search
|
|
31
|
+
* HybridSearch: Combined search strategies
|
|
32
|
+
|
|
16
33
|
|
|
17
34
|
# Attributes
|
|
18
35
|
## query_timeout[RW] {: #attribute-i-query_timeout }
|
|
@@ -20,104 +37,11 @@ Returns the value of attribute query_timeout.
|
|
|
20
37
|
|
|
21
38
|
|
|
22
39
|
# Instance Methods
|
|
23
|
-
## add(content:, token_count:0, robot_id:, embedding:nil, metadata:{}) {: #method-i-add }
|
|
24
|
-
Add a node to long-term memory (with deduplication)
|
|
25
|
-
|
|
26
|
-
If content already exists (by content_hash), links the robot to the existing
|
|
27
|
-
node and updates timestamps. Otherwise creates a new node.
|
|
28
|
-
|
|
29
|
-
**`@param`** [String] Conversation message/utterance
|
|
30
|
-
|
|
31
|
-
**`@param`** [Integer] Token count
|
|
32
|
-
|
|
33
|
-
**`@param`** [Integer] Robot identifier
|
|
34
|
-
|
|
35
|
-
**`@param`** [Array<Float>, nil] Pre-generated embedding vector
|
|
36
|
-
|
|
37
|
-
**`@param`** [Hash] Flexible metadata for the node (default: {})
|
|
38
|
-
|
|
39
|
-
**`@return`** [Hash] { node_id:, is_new:, robot_node: }
|
|
40
|
-
|
|
41
|
-
## add_tag(node_id:, tag:) {: #method-i-add_tag }
|
|
42
|
-
Add a tag to a node
|
|
43
|
-
|
|
44
|
-
**`@param`** [Integer] Node database ID
|
|
45
|
-
|
|
46
|
-
**`@param`** [String] Tag name
|
|
47
|
-
|
|
48
|
-
**`@return`** [void]
|
|
49
|
-
|
|
50
|
-
## batch_load_node_tags(node_ids) {: #method-i-batch_load_node_tags }
|
|
51
|
-
Batch load tags for multiple nodes (avoids N+1 queries)
|
|
52
|
-
|
|
53
|
-
**`@param`** [Array<Integer>] Node database IDs
|
|
54
|
-
|
|
55
|
-
**`@return`** [Hash<Integer, Array<String>>] Map of node_id to array of tag names
|
|
56
|
-
|
|
57
|
-
## calculate_relevance(node:, query_tags:[], vector_similarity:nil, node_tags:nil) {: #method-i-calculate_relevance }
|
|
58
|
-
Calculate dynamic relevance score for a node given query context
|
|
59
|
-
|
|
60
|
-
Combines multiple signals:
|
|
61
|
-
* Vector similarity (semantic match)
|
|
62
|
-
* Tag overlap (categorical match)
|
|
63
|
-
* Recency (freshness)
|
|
64
|
-
* Access frequency (popularity/utility)
|
|
65
|
-
|
|
66
|
-
**`@param`** [Hash] Node data with similarity, tags, created_at, access_count
|
|
67
|
-
|
|
68
|
-
**`@param`** [Array<String>] Tags associated with the query
|
|
69
|
-
|
|
70
|
-
**`@param`** [Float, nil] Pre-computed vector similarity (0-1)
|
|
71
|
-
|
|
72
|
-
**`@param`** [Array<String>, nil] Pre-loaded tags for this node (avoids N+1 query)
|
|
73
|
-
|
|
74
|
-
**`@return`** [Float] Composite relevance score (0-10)
|
|
75
|
-
|
|
76
40
|
## clear_cache!() {: #method-i-clear_cache! }
|
|
77
|
-
Clear the query cache
|
|
78
|
-
|
|
79
|
-
Call this after any operation that modifies data (soft delete, restore, etc.)
|
|
80
|
-
to ensure subsequent queries see fresh results.
|
|
81
|
-
|
|
82
|
-
**`@return`** [void]
|
|
83
|
-
|
|
84
|
-
## delete(node_id) {: #method-i-delete }
|
|
85
|
-
Delete a node
|
|
86
|
-
|
|
87
|
-
**`@param`** [Integer] Node database ID
|
|
41
|
+
Clear the query result cache
|
|
88
42
|
|
|
89
43
|
**`@return`** [void]
|
|
90
44
|
|
|
91
|
-
## exists?(node_id) {: #method-i-exists? }
|
|
92
|
-
Check if a node exists
|
|
93
|
-
|
|
94
|
-
**`@param`** [Integer] Node database ID
|
|
95
|
-
|
|
96
|
-
**`@return`** [Boolean] True if node exists
|
|
97
|
-
|
|
98
|
-
## find_query_matching_tags(query, include_extracted:false) {: #method-i-find_query_matching_tags }
|
|
99
|
-
Find tags that match terms in the query
|
|
100
|
-
|
|
101
|
-
Searches the tags table for tags where any hierarchy level matches query
|
|
102
|
-
words. For example, query "PostgreSQL database" would match tags like
|
|
103
|
-
"database:postgresql", "database:sql", etc. Find tags matching a query using
|
|
104
|
-
semantic extraction
|
|
105
|
-
|
|
106
|
-
**`@param`** [String] Search query
|
|
107
|
-
|
|
108
|
-
**`@param`** [Boolean] If true, returns hash with :extracted and :matched keys
|
|
109
|
-
|
|
110
|
-
**`@return`** [Array<String>] Matching tag names (default)
|
|
111
|
-
|
|
112
|
-
**`@return`** [Hash] If include_extracted: { extracted: [...], matched: [...] }
|
|
113
|
-
|
|
114
|
-
## get_node_tags(node_id) {: #method-i-get_node_tags }
|
|
115
|
-
Get tags for a specific node
|
|
116
|
-
|
|
117
|
-
**`@param`** [Integer] Node database ID
|
|
118
|
-
|
|
119
|
-
**`@return`** [Array<String>] Tag names
|
|
120
|
-
|
|
121
45
|
## initialize(config, pool_size:nil, query_timeout:DEFAULT_QUERY_TIMEOUT, cache_size:DEFAULT_CACHE_SIZE, cache_ttl:DEFAULT_CACHE_TTL) {: #method-i-initialize }
|
|
122
46
|
Initialize long-term memory storage
|
|
123
47
|
|
|
@@ -146,160 +70,9 @@ ltm = LongTermMemory.new(config, cache_size: 500, cache_ttl: 600)
|
|
|
146
70
|
```ruby
|
|
147
71
|
ltm = LongTermMemory.new(config, cache_size: 0)
|
|
148
72
|
```
|
|
149
|
-
## link_robot_to_node(robot_id:, node:, working_memory:false) {: #method-i-link_robot_to_node }
|
|
150
|
-
Link a robot to a node (create or update robot_node record)
|
|
151
|
-
|
|
152
|
-
**`@param`** [Integer] Robot ID
|
|
153
|
-
|
|
154
|
-
**`@param`** [HTM::Models::Node] Node to link
|
|
155
|
-
|
|
156
|
-
**`@param`** [Boolean] Whether node is in working memory (default: false)
|
|
157
|
-
|
|
158
|
-
**`@return`** [HTM::Models::RobotNode] The robot_node link record
|
|
159
|
-
|
|
160
|
-
## mark_evicted(robot_id:, node_ids:) {: #method-i-mark_evicted }
|
|
161
|
-
Mark nodes as evicted from working memory
|
|
162
|
-
|
|
163
|
-
Sets working_memory = false on the robot_nodes join table for the specified
|
|
164
|
-
robot and node IDs.
|
|
165
|
-
|
|
166
|
-
**`@param`** [Integer] Robot ID whose working memory is being evicted
|
|
167
|
-
|
|
168
|
-
**`@param`** [Array<Integer>] Node IDs to mark as evicted
|
|
169
|
-
|
|
170
|
-
**`@return`** [void]
|
|
171
|
-
|
|
172
|
-
## node_topics(node_id) {: #method-i-node_topics }
|
|
173
|
-
Get topics for a specific node
|
|
174
|
-
|
|
175
|
-
**`@param`** [Integer] Node database ID
|
|
176
|
-
|
|
177
|
-
**`@return`** [Array<String>] Topic paths
|
|
178
|
-
|
|
179
|
-
## nodes_by_topic(topic_path, exact:false, limit:50) {: #method-i-nodes_by_topic }
|
|
180
|
-
Retrieve nodes by ontological topic
|
|
181
|
-
|
|
182
|
-
**`@param`** [String] Topic hierarchy path
|
|
183
|
-
|
|
184
|
-
**`@param`** [Boolean] Exact match or prefix match
|
|
185
|
-
|
|
186
|
-
**`@param`** [Integer] Maximum results
|
|
187
|
-
|
|
188
|
-
**`@return`** [Array<Hash>] Matching nodes
|
|
189
|
-
|
|
190
|
-
## ontology_structure() {: #method-i-ontology_structure }
|
|
191
|
-
Get ontology structure view
|
|
192
|
-
|
|
193
|
-
**`@return`** [Array<Hash>] Ontology structure
|
|
194
|
-
|
|
195
73
|
## pool_size() {: #method-i-pool_size }
|
|
196
74
|
For backwards compatibility with tests/code that expect pool_size
|
|
197
75
|
|
|
198
|
-
## popular_tags(limit:20, timeframe:nil) {: #method-i-popular_tags }
|
|
199
|
-
Get most popular tags
|
|
200
|
-
|
|
201
|
-
**`@param`** [Integer] Number of tags to return
|
|
202
|
-
|
|
203
|
-
**`@param`** [Range, nil] Optional time range filter
|
|
204
|
-
|
|
205
|
-
**`@return`** [Array<Hash>] Tags with usage counts
|
|
206
|
-
|
|
207
|
-
## register_robot(robot_name) {: #method-i-register_robot }
|
|
208
|
-
Register a robot
|
|
209
|
-
|
|
210
|
-
**`@param`** [String] Robot identifier
|
|
211
|
-
|
|
212
|
-
**`@param`** [String] Robot name
|
|
213
|
-
|
|
214
|
-
**`@return`** [void]
|
|
215
|
-
|
|
216
|
-
## retrieve(node_id) {: #method-i-retrieve }
|
|
217
|
-
Retrieve a node by ID
|
|
218
|
-
|
|
219
|
-
Automatically tracks access by incrementing access_count and updating
|
|
220
|
-
last_accessed
|
|
221
|
-
|
|
222
|
-
**`@param`** [Integer] Node database ID
|
|
223
|
-
|
|
224
|
-
**`@return`** [Hash, nil] Node data or nil
|
|
225
|
-
|
|
226
|
-
## search(timeframe:, query:, limit:, embedding_service:, metadata:{}) {: #method-i-search }
|
|
227
|
-
Vector similarity search
|
|
228
|
-
|
|
229
|
-
**`@param`** [nil, Range, Array<Range>] Time range(s) to search (nil = no filter)
|
|
230
|
-
|
|
231
|
-
**`@param`** [String] Search query
|
|
232
|
-
|
|
233
|
-
**`@param`** [Integer] Maximum results
|
|
234
|
-
|
|
235
|
-
**`@param`** [Object] Service to generate embeddings
|
|
236
|
-
|
|
237
|
-
**`@param`** [Hash] Filter by metadata fields (default: {})
|
|
238
|
-
|
|
239
|
-
**`@return`** [Array<Hash>] Matching nodes
|
|
240
|
-
|
|
241
|
-
## search_by_tags(tags:, match_all:false, timeframe:nil, limit:20) {: #method-i-search_by_tags }
|
|
242
|
-
Search nodes by tags
|
|
243
|
-
|
|
244
|
-
**`@param`** [Array<String>] Tags to search for
|
|
245
|
-
|
|
246
|
-
**`@param`** [Boolean] If true, match ALL tags; if false, match ANY tag
|
|
247
|
-
|
|
248
|
-
**`@param`** [Range, nil] Optional time range filter
|
|
249
|
-
|
|
250
|
-
**`@param`** [Integer] Maximum results
|
|
251
|
-
|
|
252
|
-
**`@return`** [Array<Hash>] Matching nodes with relevance scores
|
|
253
|
-
|
|
254
|
-
## search_fulltext(timeframe:, query:, limit:, metadata:{}) {: #method-i-search_fulltext }
|
|
255
|
-
Full-text search
|
|
256
|
-
|
|
257
|
-
**`@param`** [Range] Time range to search
|
|
258
|
-
|
|
259
|
-
**`@param`** [String] Search query
|
|
260
|
-
|
|
261
|
-
**`@param`** [Integer] Maximum results
|
|
262
|
-
|
|
263
|
-
**`@param`** [Hash] Filter by metadata fields (default: {})
|
|
264
|
-
|
|
265
|
-
**`@return`** [Array<Hash>] Matching nodes
|
|
266
|
-
|
|
267
|
-
## search_hybrid(timeframe:, query:, limit:, embedding_service:, prefilter_limit:100, metadata:{}) {: #method-i-search_hybrid }
|
|
268
|
-
Hybrid search (full-text + vector)
|
|
269
|
-
|
|
270
|
-
**`@param`** [Range] Time range to search
|
|
271
|
-
|
|
272
|
-
**`@param`** [String] Search query
|
|
273
|
-
|
|
274
|
-
**`@param`** [Integer] Maximum results
|
|
275
|
-
|
|
276
|
-
**`@param`** [Object] Service to generate embeddings
|
|
277
|
-
|
|
278
|
-
**`@param`** [Integer] Candidates to consider (default: 100)
|
|
279
|
-
|
|
280
|
-
**`@param`** [Hash] Filter by metadata fields (default: {})
|
|
281
|
-
|
|
282
|
-
**`@return`** [Array<Hash>] Matching nodes
|
|
283
|
-
|
|
284
|
-
## search_with_relevance(timeframe:, query:nil, query_tags:[], limit:20, embedding_service:nil, metadata:{}) {: #method-i-search_with_relevance }
|
|
285
|
-
Search with dynamic relevance scoring
|
|
286
|
-
|
|
287
|
-
Returns nodes with calculated relevance scores based on query context
|
|
288
|
-
|
|
289
|
-
**`@param`** [nil, Range, Array<Range>] Time range(s) to search (nil = no filter)
|
|
290
|
-
|
|
291
|
-
**`@param`** [String, nil] Search query
|
|
292
|
-
|
|
293
|
-
**`@param`** [Array<String>] Tags to match
|
|
294
|
-
|
|
295
|
-
**`@param`** [Integer] Maximum results
|
|
296
|
-
|
|
297
|
-
**`@param`** [Object, nil] Service to generate embeddings
|
|
298
|
-
|
|
299
|
-
**`@param`** [Hash] Filter by metadata fields (default: {})
|
|
300
|
-
|
|
301
|
-
**`@return`** [Array<Hash>] Nodes with relevance scores
|
|
302
|
-
|
|
303
76
|
## shutdown() {: #method-i-shutdown }
|
|
304
77
|
Shutdown - no-op with ActiveRecord (connection pool managed by ActiveRecord)
|
|
305
78
|
|
|
@@ -308,35 +81,3 @@ Get memory statistics
|
|
|
308
81
|
|
|
309
82
|
**`@return`** [Hash] Statistics
|
|
310
83
|
|
|
311
|
-
## topic_relationships(min_shared_nodes:2, limit:50) {: #method-i-topic_relationships }
|
|
312
|
-
Get topic relationships (co-occurrence)
|
|
313
|
-
|
|
314
|
-
**`@param`** [Integer] Minimum shared nodes
|
|
315
|
-
|
|
316
|
-
**`@param`** [Integer] Maximum relationships
|
|
317
|
-
|
|
318
|
-
**`@return`** [Array<Hash>] Topic relationships
|
|
319
|
-
|
|
320
|
-
## track_access(node_ids) {: #method-i-track_access }
|
|
321
|
-
Track access for multiple nodes (bulk operation)
|
|
322
|
-
|
|
323
|
-
Updates access_count and last_accessed for all nodes in the array
|
|
324
|
-
|
|
325
|
-
**`@param`** [Array<Integer>] Node IDs that were accessed
|
|
326
|
-
|
|
327
|
-
**`@return`** [void]
|
|
328
|
-
|
|
329
|
-
## update_last_accessed(node_id) {: #method-i-update_last_accessed }
|
|
330
|
-
Update last_accessed timestamp
|
|
331
|
-
|
|
332
|
-
**`@param`** [Integer] Node database ID
|
|
333
|
-
|
|
334
|
-
**`@return`** [void]
|
|
335
|
-
|
|
336
|
-
## update_robot_activity(robot_id) {: #method-i-update_robot_activity }
|
|
337
|
-
Update robot activity timestamp
|
|
338
|
-
|
|
339
|
-
**`@param`** [String] Robot identifier
|
|
340
|
-
|
|
341
|
-
**`@return`** [void]
|
|
342
|
-
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Exception: HTM::PropositionError
|
|
2
|
+
**Inherits:** HTM::Error
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Raised when proposition extraction fails
|
|
6
|
+
|
|
7
|
+
Common causes:
|
|
8
|
+
* LLM provider API errors
|
|
9
|
+
* Invalid proposition response format
|
|
10
|
+
* Network connectivity issues
|
|
11
|
+
* Model not available
|
|
12
|
+
|
|
13
|
+
Note: This error is distinct from CircuitBreakerOpenError. PropositionError
|
|
14
|
+
indicates a single failure, while CircuitBreakerOpenError indicates repeated
|
|
15
|
+
failures have triggered protective circuit breaking.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# Class: HTM::PropositionService
|
|
2
|
+
**Inherits:** Object
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Proposition Service - Extracts atomic factual propositions from text
|
|
6
|
+
|
|
7
|
+
This service breaks complex text into simple, self-contained factual
|
|
8
|
+
statements that can be stored as independent memory nodes. Each proposition:
|
|
9
|
+
* Expresses a single fact
|
|
10
|
+
* Is understandable without context
|
|
11
|
+
* Uses full names, not pronouns
|
|
12
|
+
* Includes relevant dates/qualifiers
|
|
13
|
+
* Contains one subject-predicate relationship
|
|
14
|
+
|
|
15
|
+
The actual LLM call is delegated to HTM.configuration.proposition_extractor
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
**`@example`**
|
|
19
|
+
```ruby
|
|
20
|
+
propositions = HTM::PropositionService.extract(
|
|
21
|
+
"In 1969, Neil Armstrong became the first person to walk on the Moon during Apollo 11."
|
|
22
|
+
)
|
|
23
|
+
# => ["Neil Armstrong was an astronaut.",
|
|
24
|
+
# "Neil Armstrong walked on the Moon in 1969.",
|
|
25
|
+
# "Neil Armstrong was the first person to walk on the Moon.",
|
|
26
|
+
# "Neil Armstrong walked on the Moon during the Apollo 11 mission.",
|
|
27
|
+
# "The Apollo 11 mission occurred in 1969."]
|
|
28
|
+
```
|
|
29
|
+
# Class Methods
|
|
30
|
+
## circuit_breaker() {: #method-c-circuit_breaker }
|
|
31
|
+
Get or create the circuit breaker for proposition service
|
|
32
|
+
**`@return`** [HTM::CircuitBreaker] The circuit breaker instance
|
|
33
|
+
|
|
34
|
+
## extract(content ) {: #method-c-extract }
|
|
35
|
+
Extract propositions from text content
|
|
36
|
+
**`@param`** [String] Text to analyze
|
|
37
|
+
|
|
38
|
+
**`@raise`** [CircuitBreakerOpenError] If circuit breaker is open
|
|
39
|
+
|
|
40
|
+
**`@raise`** [PropositionError] If extraction fails
|
|
41
|
+
|
|
42
|
+
**`@return`** [Array<String>] Array of atomic propositions
|
|
43
|
+
|
|
44
|
+
## parse_propositions(raw_propositions ) {: #method-c-parse_propositions }
|
|
45
|
+
Parse proposition response (handles string or array input)
|
|
46
|
+
**`@param`** [String, Array] Raw response from extractor
|
|
47
|
+
|
|
48
|
+
**`@return`** [Array<String>] Parsed proposition strings
|
|
49
|
+
|
|
50
|
+
## reset_circuit_breaker!() {: #method-c-reset_circuit_breaker! }
|
|
51
|
+
Reset the circuit breaker (useful for testing)
|
|
52
|
+
**`@return`** [void]
|
|
53
|
+
|
|
54
|
+
## valid_proposition?(proposition ) {: #method-c-valid_proposition? }
|
|
55
|
+
Validate single proposition
|
|
56
|
+
**`@param`** [String] Proposition to validate
|
|
57
|
+
|
|
58
|
+
**`@return`** [Boolean] True if valid
|
|
59
|
+
|
|
60
|
+
## validate_and_filter_propositions(propositions ) {: #method-c-validate_and_filter_propositions }
|
|
61
|
+
Validate and filter propositions
|
|
62
|
+
**`@param`** [Array<String>] Parsed propositions
|
|
63
|
+
|
|
64
|
+
**`@return`** [Array<String>] Valid propositions only
|
|
65
|
+
|
|
66
|
+
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Class: HTM::QueryCache
|
|
2
|
+
**Inherits:** Object
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
Thread-safe query result cache with TTL and statistics
|
|
6
|
+
|
|
7
|
+
Provides LRU caching for expensive query results with:
|
|
8
|
+
* Configurable size and TTL
|
|
9
|
+
* Thread-safe statistics tracking
|
|
10
|
+
* Fast cache key generation (using Ruby's built-in hash)
|
|
11
|
+
* Selective cache invalidation by method type
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
**`@example`**
|
|
15
|
+
```ruby
|
|
16
|
+
cache = HTM::QueryCache.new(size: 1000, ttl: 300)
|
|
17
|
+
```
|
|
18
|
+
**`@example`**
|
|
19
|
+
```ruby
|
|
20
|
+
result = cache.fetch(:search, timeframe, query, limit) do
|
|
21
|
+
expensive_search_operation
|
|
22
|
+
end
|
|
23
|
+
```
|
|
24
|
+
**`@example`**
|
|
25
|
+
```ruby
|
|
26
|
+
cache.stats
|
|
27
|
+
# => { hits: 42, misses: 10, hit_rate: 80.77, size: 52 }
|
|
28
|
+
```
|
|
29
|
+
**`@example`**
|
|
30
|
+
```ruby
|
|
31
|
+
cache.invalidate_methods!(:search, :hybrid) # Only invalidate search-related entries
|
|
32
|
+
```
|
|
33
|
+
# Attributes
|
|
34
|
+
## enabled[RW] {: #attribute-i-enabled }
|
|
35
|
+
Returns the value of attribute enabled.
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Instance Methods
|
|
39
|
+
## clear!() {: #method-i-clear! }
|
|
40
|
+
Clear all cached entries
|
|
41
|
+
|
|
42
|
+
**`@return`** [void]
|
|
43
|
+
|
|
44
|
+
## enabled?() {: #method-i-enabled? }
|
|
45
|
+
Check if cache is enabled
|
|
46
|
+
|
|
47
|
+
**`@return`** [Boolean]
|
|
48
|
+
|
|
49
|
+
## fetch(method, *args, &block) {: #method-i-fetch }
|
|
50
|
+
Fetch a value from cache or execute block
|
|
51
|
+
|
|
52
|
+
**`@param`** [Symbol] Method name for cache key
|
|
53
|
+
|
|
54
|
+
**`@param`** [Array] Arguments for cache key
|
|
55
|
+
|
|
56
|
+
**`@return`** [Object] Cached or computed value
|
|
57
|
+
|
|
58
|
+
**`@yield`** [] Block that computes the value if not cached
|
|
59
|
+
|
|
60
|
+
## initialize(size:1000, ttl:300) {: #method-i-initialize }
|
|
61
|
+
Initialize a new query cache
|
|
62
|
+
|
|
63
|
+
**`@param`** [Integer] Maximum number of entries (default: 1000, use 0 to disable)
|
|
64
|
+
|
|
65
|
+
**`@param`** [Integer] Time-to-live in seconds (default: 300)
|
|
66
|
+
|
|
67
|
+
**`@return`** [QueryCache] a new instance of QueryCache
|
|
68
|
+
|
|
69
|
+
## invalidate!() {: #method-i-invalidate! }
|
|
70
|
+
Invalidate cache (alias for clear!)
|
|
71
|
+
|
|
72
|
+
**`@return`** [void]
|
|
73
|
+
|
|
74
|
+
## invalidate_methods!(*methods) {: #method-i-invalidate_methods! }
|
|
75
|
+
Invalidate cache entries for specific methods only
|
|
76
|
+
|
|
77
|
+
More efficient than full invalidation when only certain types of cached data
|
|
78
|
+
need to be refreshed.
|
|
79
|
+
|
|
80
|
+
**`@param`** [Array<Symbol>] Method names to invalidate
|
|
81
|
+
|
|
82
|
+
**`@return`** [Integer] Number of entries invalidated
|
|
83
|
+
|
|
84
|
+
## stats() {: #method-i-stats }
|
|
85
|
+
Get cache statistics
|
|
86
|
+
|
|
87
|
+
**`@return`** [Hash, nil] Statistics hash or nil if disabled
|
|
88
|
+
|