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.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.dictate.toml +46 -0
  3. data/.envrc +2 -0
  4. data/CHANGELOG.md +85 -2
  5. data/README.md +348 -79
  6. data/Rakefile +14 -2
  7. data/bin/htm_mcp.rb +94 -0
  8. data/config/database.yml +20 -13
  9. data/db/migrate/00003_create_file_sources.rb +5 -0
  10. data/db/migrate/00004_create_nodes.rb +17 -0
  11. data/db/migrate/00005_create_tags.rb +7 -0
  12. data/db/migrate/00006_create_node_tags.rb +2 -0
  13. data/db/migrate/00007_create_robot_nodes.rb +7 -0
  14. data/db/schema.sql +69 -100
  15. data/docs/api/index.md +1 -1
  16. data/docs/api/yard/HTM/Configuration.md +54 -0
  17. data/docs/api/yard/HTM/Database.md +13 -10
  18. data/docs/api/yard/HTM/EmbeddingService.md +5 -1
  19. data/docs/api/yard/HTM/LongTermMemory.md +18 -277
  20. data/docs/api/yard/HTM/PropositionError.md +18 -0
  21. data/docs/api/yard/HTM/PropositionService.md +66 -0
  22. data/docs/api/yard/HTM/QueryCache.md +88 -0
  23. data/docs/api/yard/HTM/RobotGroup.md +481 -0
  24. data/docs/api/yard/HTM/SqlBuilder.md +108 -0
  25. data/docs/api/yard/HTM/TagService.md +4 -0
  26. data/docs/api/yard/HTM/Telemetry/NullInstrument.md +13 -0
  27. data/docs/api/yard/HTM/Telemetry/NullMeter.md +15 -0
  28. data/docs/api/yard/HTM/Telemetry.md +109 -0
  29. data/docs/api/yard/HTM/WorkingMemoryChannel.md +176 -0
  30. data/docs/api/yard/HTM.md +8 -22
  31. data/docs/api/yard/index.csv +102 -25
  32. data/docs/api/yard-reference.md +8 -0
  33. data/docs/architecture/index.md +1 -1
  34. data/docs/assets/images/multi-provider-failover.svg +51 -0
  35. data/docs/assets/images/robot-group-architecture.svg +65 -0
  36. data/docs/database/README.md +3 -3
  37. data/docs/database/public.file_sources.svg +29 -21
  38. data/docs/database/public.node_tags.md +2 -0
  39. data/docs/database/public.node_tags.svg +53 -41
  40. data/docs/database/public.nodes.md +2 -0
  41. data/docs/database/public.nodes.svg +52 -40
  42. data/docs/database/public.robot_nodes.md +2 -0
  43. data/docs/database/public.robot_nodes.svg +30 -22
  44. data/docs/database/public.robots.svg +16 -12
  45. data/docs/database/public.tags.md +3 -0
  46. data/docs/database/public.tags.svg +41 -33
  47. data/docs/database/schema.json +66 -0
  48. data/docs/database/schema.svg +60 -48
  49. data/docs/development/index.md +14 -1
  50. data/docs/development/rake-tasks.md +1068 -0
  51. data/docs/getting-started/index.md +1 -1
  52. data/docs/getting-started/quick-start.md +144 -155
  53. data/docs/guides/adding-memories.md +2 -3
  54. data/docs/guides/context-assembly.md +185 -184
  55. data/docs/guides/getting-started.md +154 -148
  56. data/docs/guides/index.md +8 -1
  57. data/docs/guides/long-term-memory.md +60 -92
  58. data/docs/guides/mcp-server.md +617 -0
  59. data/docs/guides/multi-robot.md +249 -345
  60. data/docs/guides/recalling-memories.md +153 -163
  61. data/docs/guides/robot-groups.md +604 -0
  62. data/docs/guides/search-strategies.md +61 -58
  63. data/docs/guides/working-memory.md +103 -136
  64. data/docs/images/telemetry-architecture.svg +153 -0
  65. data/docs/index.md +30 -26
  66. data/docs/telemetry.md +391 -0
  67. data/examples/README.md +46 -1
  68. data/examples/cli_app/README.md +1 -1
  69. data/examples/cli_app/htm_cli.rb +1 -1
  70. data/examples/robot_groups/robot_worker.rb +1 -2
  71. data/examples/robot_groups/same_process.rb +1 -4
  72. data/examples/sinatra_app/app.rb +1 -1
  73. data/examples/telemetry/README.md +147 -0
  74. data/examples/telemetry/SETUP_README.md +169 -0
  75. data/examples/telemetry/demo.rb +498 -0
  76. data/examples/telemetry/grafana/dashboards/htm-metrics.json +457 -0
  77. data/lib/htm/configuration.rb +261 -70
  78. data/lib/htm/database.rb +46 -22
  79. data/lib/htm/embedding_service.rb +24 -14
  80. data/lib/htm/errors.rb +15 -1
  81. data/lib/htm/jobs/generate_embedding_job.rb +19 -0
  82. data/lib/htm/jobs/generate_propositions_job.rb +103 -0
  83. data/lib/htm/jobs/generate_tags_job.rb +24 -0
  84. data/lib/htm/loaders/markdown_chunker.rb +79 -0
  85. data/lib/htm/loaders/markdown_loader.rb +41 -15
  86. data/lib/htm/long_term_memory/fulltext_search.rb +138 -0
  87. data/lib/htm/long_term_memory/hybrid_search.rb +324 -0
  88. data/lib/htm/long_term_memory/node_operations.rb +209 -0
  89. data/lib/htm/long_term_memory/relevance_scorer.rb +355 -0
  90. data/lib/htm/long_term_memory/robot_operations.rb +34 -0
  91. data/lib/htm/long_term_memory/tag_operations.rb +428 -0
  92. data/lib/htm/long_term_memory/vector_search.rb +109 -0
  93. data/lib/htm/long_term_memory.rb +51 -1153
  94. data/lib/htm/models/node.rb +35 -2
  95. data/lib/htm/models/node_tag.rb +31 -0
  96. data/lib/htm/models/robot_node.rb +31 -0
  97. data/lib/htm/models/tag.rb +44 -0
  98. data/lib/htm/proposition_service.rb +169 -0
  99. data/lib/htm/query_cache.rb +214 -0
  100. data/lib/htm/robot_group.rb +721 -0
  101. data/lib/htm/sql_builder.rb +178 -0
  102. data/lib/htm/tag_service.rb +16 -6
  103. data/lib/htm/tasks.rb +8 -2
  104. data/lib/htm/telemetry.rb +224 -0
  105. data/lib/htm/version.rb +1 -1
  106. data/lib/htm/working_memory_channel.rb +250 -0
  107. data/lib/htm.rb +66 -3
  108. data/lib/tasks/doc.rake +1 -1
  109. data/lib/tasks/htm.rake +259 -13
  110. data/mkdocs.yml +98 -96
  111. metadata +55 -20
  112. data/.aigcm_msg +0 -1
  113. data/.claude/settings.local.json +0 -95
  114. data/CLAUDE.md +0 -603
  115. data/db/migrate/00009_add_working_memory_to_robot_nodes.rb +0 -12
  116. data/examples/cli_app/temp.log +0 -93
  117. data/examples/robot_groups/lib/robot_group.rb +0 -419
  118. data/examples/robot_groups/lib/working_memory_channel.rb +0 -140
  119. data/lib/htm/loaders/paragraph_chunker.rb +0 -112
  120. data/notes/ARCHITECTURE_REVIEW.md +0 -1167
  121. data/notes/IMPLEMENTATION_SUMMARY.md +0 -606
  122. data/notes/MULTI_FRAMEWORK_IMPLEMENTATION.md +0 -451
  123. data/notes/next_steps.md +0 -100
  124. data/notes/plan.md +0 -627
  125. data/notes/tag_ontology_enhancement_ideas.md +0 -222
  126. data/notes/timescaledb_removal_summary.md +0 -200
@@ -0,0 +1,109 @@
1
+ # Module: HTM::Telemetry
2
+
3
+
4
+ OpenTelemetry-based observability for HTM
5
+
6
+ Provides opt-in metrics collection with zero overhead when disabled. Uses the
7
+ null object pattern - when telemetry is disabled or the SDK is not available,
8
+ all metric operations are no-ops.
9
+
10
+ **`@see`** [] for full implementation details
11
+
12
+
13
+ **`@example`**
14
+ ```ruby
15
+ HTM.configure do |config|
16
+ config.telemetry_enabled = true
17
+ end
18
+ ```
19
+ **`@example`**
20
+ ```ruby
21
+ # Export to OTLP endpoint
22
+ ENV['OTEL_METRICS_EXPORTER'] = 'otlp'
23
+ ENV['OTEL_EXPORTER_OTLP_ENDPOINT'] = 'http://localhost:4318'
24
+ ```
25
+ # Class Methods
26
+ ## cache_operations() {: #method-c-cache_operations }
27
+ Counter for cache operations (hits, misses)
28
+ **`@return`** [OpenTelemetry::Metrics::Counter, NullInstrument]
29
+
30
+
31
+ **`@example`**
32
+ ```ruby
33
+ Telemetry.cache_operations.add(1, attributes: { 'operation' => 'hit' })
34
+ ```
35
+ ## embedding_latency() {: #method-c-embedding_latency }
36
+ Histogram for embedding generation latency
37
+ **`@return`** [OpenTelemetry::Metrics::Histogram, NullInstrument]
38
+
39
+
40
+ **`@example`**
41
+ ```ruby
42
+ Telemetry.embedding_latency.record(145, attributes: { 'provider' => 'ollama', 'status' => 'success' })
43
+ ```
44
+ ## enabled?() {: #method-c-enabled? }
45
+ Check if telemetry is enabled and SDK is available
46
+ **`@return`** [Boolean] true if telemetry should be active
47
+
48
+ ## job_counter() {: #method-c-job_counter }
49
+ Counter for job execution (enqueued, completed, failed)
50
+ **`@return`** [OpenTelemetry::Metrics::Counter, NullInstrument]
51
+
52
+
53
+ **`@example`**
54
+ ```ruby
55
+ Telemetry.job_counter.add(1, attributes: { 'job' => 'embedding', 'status' => 'success' })
56
+ ```
57
+ ## measure(histogram , attributes {}) {: #method-c-measure }
58
+ Measure execution time of a block and record to a histogram
59
+ **`@param`** [OpenTelemetry::Metrics::Histogram, NullInstrument] The histogram to record to
60
+
61
+ **`@param`** [Hash] Attributes to attach to the measurement
62
+
63
+ **`@return`** [Object] The result of the block
64
+
65
+ **`@yield`** [] The block to measure
66
+
67
+
68
+ **`@example`**
69
+ ```ruby
70
+ result = Telemetry.measure(Telemetry.embedding_latency, 'provider' => 'ollama') do
71
+ generate_embedding(text)
72
+ end
73
+ ```
74
+ ## meter() {: #method-c-meter }
75
+ Get the meter for creating instruments
76
+ **`@return`** [OpenTelemetry::Metrics::Meter, NullMeter] Real or null meter
77
+
78
+ ## reset!() {: #method-c-reset! }
79
+ Reset telemetry state (for testing)
80
+ **`@return`** [void]
81
+
82
+ ## sdk_available?() {: #method-c-sdk_available? }
83
+ Check if OpenTelemetry SDK is installed
84
+ **`@return`** [Boolean] true if SDK can be loaded
85
+
86
+ ## search_latency() {: #method-c-search_latency }
87
+ Histogram for search operation latency
88
+ **`@return`** [OpenTelemetry::Metrics::Histogram, NullInstrument]
89
+
90
+
91
+ **`@example`**
92
+ ```ruby
93
+ Telemetry.search_latency.record(50, attributes: { 'strategy' => 'vector' })
94
+ ```
95
+ ## setup() {: #method-c-setup }
96
+ Initialize OpenTelemetry SDK
97
+
98
+ Called automatically when telemetry is enabled. Safe to call multiple times.
99
+ **`@return`** [void]
100
+
101
+ ## tag_latency() {: #method-c-tag_latency }
102
+ Histogram for tag extraction latency
103
+ **`@return`** [OpenTelemetry::Metrics::Histogram, NullInstrument]
104
+
105
+
106
+ **`@example`**
107
+ ```ruby
108
+ Telemetry.tag_latency.record(250, attributes: { 'provider' => 'ollama', 'status' => 'success' })
109
+ ```
@@ -0,0 +1,176 @@
1
+ # Class: HTM::WorkingMemoryChannel
2
+ **Inherits:** Object
3
+
4
+
5
+ Provides real-time synchronization of working memory changes across multiple
6
+ robots using PostgreSQL LISTEN/NOTIFY pub/sub mechanism.
7
+
8
+ This class enables distributed robots to maintain synchronized working memory
9
+ by broadcasting change notifications through PostgreSQL channels. When one
10
+ robot adds, evicts, or clears working memory, all other robots in the group
11
+ receive immediate notification.
12
+
13
+ **`@see`** [] Higher-level coordination using this channel
14
+
15
+
16
+ **`@example`**
17
+ ```ruby
18
+ channel = HTM::WorkingMemoryChannel.new('support-team', db_config)
19
+
20
+ # Subscribe to changes
21
+ channel.on_change do |event, node_id, robot_id|
22
+ case event
23
+ when :added then puts "Node #{node_id} added by robot #{robot_id}"
24
+ when :evicted then puts "Node #{node_id} evicted by robot #{robot_id}"
25
+ when :cleared then puts "Working memory cleared by robot #{robot_id}"
26
+ end
27
+ end
28
+
29
+ # Start listening in background thread
30
+ channel.start_listening
31
+
32
+ # Publish a change
33
+ channel.notify(:added, node_id: 123, robot_id: 456)
34
+
35
+ # Cleanup when done
36
+ channel.stop_listening
37
+ ```
38
+ # Attributes
39
+ ## notifications_received[RW] {: #attribute-i-notifications_received }
40
+ Number of notifications received since channel was created
41
+
42
+ **`@return`** [Integer]
43
+
44
+
45
+ # Instance Methods
46
+ ## channel_name() {: #method-i-channel_name }
47
+ Returns the PostgreSQL channel name used for notifications.
48
+
49
+ The channel name is derived from the group name with a prefix and sanitization
50
+ of special characters.
51
+
52
+ **`@return`** [String] The PostgreSQL LISTEN/NOTIFY channel name
53
+
54
+
55
+ **`@example`**
56
+ ```ruby
57
+ channel = HTM::WorkingMemoryChannel.new('my-group', db_config)
58
+ channel.channel_name # => "htm_wm_my_group"
59
+ ```
60
+ ## initialize(group_name, db_config) {: #method-i-initialize }
61
+ Creates a new working memory channel for a robot group.
62
+
63
+ The channel name is derived from the group name with non-alphanumeric
64
+ characters replaced by underscores to ensure PostgreSQL compatibility.
65
+
66
+ **`@option`** []
67
+
68
+ **`@option`** []
69
+
70
+ **`@option`** []
71
+
72
+ **`@option`** []
73
+
74
+ **`@option`** []
75
+
76
+ **`@param`** [String] Name of the robot group (used to create unique channel)
77
+
78
+ **`@param`** [Hash] PostgreSQL connection configuration hash
79
+
80
+ **`@return`** [WorkingMemoryChannel] a new instance of WorkingMemoryChannel
81
+
82
+
83
+ **`@example`**
84
+ ```ruby
85
+ db_config = { host: 'localhost', port: 5432, dbname: 'htm_dev', user: 'postgres' }
86
+ channel = HTM::WorkingMemoryChannel.new('customer-support', db_config)
87
+ ```
88
+ ## listening?() {: #method-i-listening? }
89
+ Checks if the listener thread is currently active.
90
+
91
+ **`@return`** [Boolean] true if listening for notifications, false otherwise
92
+
93
+
94
+ **`@example`**
95
+ ```ruby
96
+ channel.start_listening
97
+ channel.listening? # => true
98
+ channel.stop_listening
99
+ channel.listening? # => false
100
+ ```
101
+ ## notify(event, node_id:, robot_id:) {: #method-i-notify }
102
+ Broadcasts a working memory change notification to all listeners.
103
+
104
+ Uses PostgreSQL's pg_notify function to send a JSON payload containing the
105
+ event type, affected node ID, originating robot ID, and timestamp.
106
+
107
+ **`@param`** [Symbol] Type of change (:added, :evicted, or :cleared)
108
+
109
+ **`@param`** [Integer, nil] ID of the affected node (nil for :cleared events)
110
+
111
+ **`@param`** [Integer] ID of the robot that triggered the change
112
+
113
+ **`@return`** [void]
114
+
115
+
116
+ **`@example`**
117
+ ```ruby
118
+ channel.notify(:added, node_id: 123, robot_id: 1)
119
+ ```
120
+ **`@example`**
121
+ ```ruby
122
+ channel.notify(:cleared, node_id: nil, robot_id: 1)
123
+ ```
124
+ ## on_change(&callback) {: #method-i-on_change }
125
+ Registers a callback to be invoked when working memory changes occur.
126
+
127
+ Multiple callbacks can be registered; all will be called for each event.
128
+ Callbacks are invoked synchronously within the listener thread.
129
+
130
+ **`@return`** [void]
131
+
132
+ **`@yield`** [event, node_id, robot_id] Block called for each notification
133
+
134
+ **`@yieldparam`** [Symbol] Type of change (:added, :evicted, or :cleared)
135
+
136
+ **`@yieldparam`** [Integer, nil] ID of the affected node
137
+
138
+ **`@yieldparam`** [Integer] ID of the robot that triggered the change
139
+
140
+
141
+ **`@example`**
142
+ ```ruby
143
+ channel.on_change do |event, node_id, robot_id|
144
+ puts "Received #{event} event for node #{node_id}"
145
+ end
146
+ ```
147
+ ## start_listening() {: #method-i-start_listening }
148
+ Starts listening for notifications in a background thread.
149
+
150
+ Creates a dedicated PostgreSQL connection that uses LISTEN to receive
151
+ notifications. The thread polls every 0.5 seconds, allowing for clean shutdown
152
+ via {#stop_listening}.
153
+
154
+ **`@return`** [Thread] The background listener thread
155
+
156
+
157
+ **`@example`**
158
+ ```ruby
159
+ thread = channel.start_listening
160
+ puts "Listening: #{channel.listening?}" # => true
161
+ ```
162
+ ## stop_listening() {: #method-i-stop_listening }
163
+ Stops the background listener thread.
164
+
165
+ Signals the listener to stop, waits up to 0.5 seconds for clean exit, then
166
+ forcefully terminates if still running. The PostgreSQL connection is closed
167
+ automatically.
168
+
169
+ **`@return`** [void]
170
+
171
+
172
+ **`@example`**
173
+ ```ruby
174
+ channel.stop_listening
175
+ puts "Listening: #{channel.listening?}" # => false
176
+ ```
data/docs/api/yard/HTM.md CHANGED
@@ -2,30 +2,10 @@
2
2
  **Inherits:** Object
3
3
 
4
4
 
5
- HTM (Hierarchical Temporary Memory) error classes
5
+ examples/robot_groups/lib/htm/working_memory_channel.rb frozen_string_literal:
6
+ true
6
7
 
7
- All HTM errors inherit from HTM::Error, allowing you to catch all HTM-related
8
- errors with a single rescue clause.
9
8
 
10
-
11
- **`@example`**
12
- ```ruby
13
- begin
14
- htm.remember("some content")
15
- rescue HTM::Error => e
16
- logger.error "HTM error: #{e.message}"
17
- end
18
- ```
19
- **`@example`**
20
- ```ruby
21
- begin
22
- htm.forget(node_id, soft: false)
23
- rescue HTM::NotFoundError
24
- puts "Node not found"
25
- rescue HTM::ValidationError
26
- puts "Invalid input"
27
- end
28
- ```
29
9
  # Class Methods
30
10
  ## configure() {: #method-c-configure }
31
11
  Configure HTM
@@ -57,6 +37,12 @@ Generate embedding using EmbeddingService
57
37
 
58
38
  **`@return`** [Array<Float>] Embedding vector (original, not padded)
59
39
 
40
+ ## extract_propositions(text ) {: #method-c-extract_propositions }
41
+ Extract propositions using PropositionService
42
+ **`@param`** [String] Text to analyze
43
+
44
+ **`@return`** [Array<String>] Extracted atomic propositions
45
+
60
46
  ## extract_tags(text , existing_ontology: []) {: #method-c-extract_tags }
61
47
  Extract tags using TagService
62
48
  **`@param`** [String] Text to analyze
@@ -15,10 +15,23 @@ HTM::Observability.record_query_timing,Method,HTM/Observability.md#method-c-reco
15
15
  HTM::Observability.record_tag_timing,Method,HTM/Observability.md#method-c-record_tag_timing
16
16
  HTM::Observability.reset_metrics!,Method,HTM/Observability.md#method-c-reset_metrics!
17
17
  HTM::Observability.service_timing_stats,Method,HTM/Observability.md#method-c-service_timing_stats
18
+ HTM::Telemetry,Module,HTM/Telemetry.md
19
+ HTM::Telemetry.cache_operations,Method,HTM/Telemetry.md#method-c-cache_operations
20
+ HTM::Telemetry.embedding_latency,Method,HTM/Telemetry.md#method-c-embedding_latency
21
+ HTM::Telemetry.enabled?,Method,HTM/Telemetry.md#method-c-enabled?
22
+ HTM::Telemetry.job_counter,Method,HTM/Telemetry.md#method-c-job_counter
23
+ HTM::Telemetry.measure,Method,HTM/Telemetry.md#method-c-measure
24
+ HTM::Telemetry.meter,Method,HTM/Telemetry.md#method-c-meter
25
+ HTM::Telemetry.reset!,Method,HTM/Telemetry.md#method-c-reset!
26
+ HTM::Telemetry.sdk_available?,Method,HTM/Telemetry.md#method-c-sdk_available?
27
+ HTM::Telemetry.search_latency,Method,HTM/Telemetry.md#method-c-search_latency
28
+ HTM::Telemetry.setup,Method,HTM/Telemetry.md#method-c-setup
29
+ HTM::Telemetry.tag_latency,Method,HTM/Telemetry.md#method-c-tag_latency
18
30
  HTM,Class,HTM.md
19
31
  HTM.configure,Method,HTM.md#method-c-configure
20
32
  HTM.count_tokens,Method,HTM.md#method-c-count_tokens
21
33
  HTM.embed,Method,HTM.md#method-c-embed
34
+ HTM.extract_propositions,Method,HTM.md#method-c-extract_propositions
22
35
  HTM.extract_tags,Method,HTM.md#method-c-extract_tags
23
36
  HTM.logger,Method,HTM.md#method-c-logger
24
37
  HTM.reset_configuration!,Method,HTM.md#method-c-reset_configuration!
@@ -55,6 +68,11 @@ azure_endpoint,Attribute,HTM/Configuration.md#attribute-i-azure_endpoint
55
68
  bedrock_access_key,Attribute,HTM/Configuration.md#attribute-i-bedrock_access_key
56
69
  bedrock_region,Attribute,HTM/Configuration.md#attribute-i-bedrock_region
57
70
  bedrock_secret_key,Attribute,HTM/Configuration.md#attribute-i-bedrock_secret_key
71
+ chunk_overlap,Attribute,HTM/Configuration.md#attribute-i-chunk_overlap
72
+ chunk_size,Attribute,HTM/Configuration.md#attribute-i-chunk_size
73
+ circuit_breaker_failure_threshold,Attribute,HTM/Configuration.md#attribute-i-circuit_breaker_failure_threshold
74
+ circuit_breaker_half_open_max_calls,Attribute,HTM/Configuration.md#attribute-i-circuit_breaker_half_open_max_calls
75
+ circuit_breaker_reset_timeout,Attribute,HTM/Configuration.md#attribute-i-circuit_breaker_reset_timeout
58
76
  connection_timeout,Attribute,HTM/Configuration.md#attribute-i-connection_timeout
59
77
  deepseek_api_key,Attribute,HTM/Configuration.md#attribute-i-deepseek_api_key
60
78
  embedding_dimensions,Attribute,HTM/Configuration.md#attribute-i-embedding_dimensions
@@ -62,19 +80,32 @@ embedding_generator,Attribute,HTM/Configuration.md#attribute-i-embedding_generat
62
80
  embedding_model,Attribute,HTM/Configuration.md#attribute-i-embedding_model
63
81
  embedding_provider,Attribute,HTM/Configuration.md#attribute-i-embedding_provider
64
82
  embedding_timeout,Attribute,HTM/Configuration.md#attribute-i-embedding_timeout
83
+ extract_propositions,Attribute,HTM/Configuration.md#attribute-i-extract_propositions
65
84
  gemini_api_key,Attribute,HTM/Configuration.md#attribute-i-gemini_api_key
66
85
  huggingface_api_key,Attribute,HTM/Configuration.md#attribute-i-huggingface_api_key
67
86
  job_backend,Attribute,HTM/Configuration.md#attribute-i-job_backend
68
87
  logger,Attribute,HTM/Configuration.md#attribute-i-logger
88
+ max_embedding_dimension,Attribute,HTM/Configuration.md#attribute-i-max_embedding_dimension
89
+ max_tag_depth,Attribute,HTM/Configuration.md#attribute-i-max_tag_depth
69
90
  ollama_url,Attribute,HTM/Configuration.md#attribute-i-ollama_url
70
91
  openai_api_key,Attribute,HTM/Configuration.md#attribute-i-openai_api_key
71
92
  openai_organization,Attribute,HTM/Configuration.md#attribute-i-openai_organization
72
93
  openai_project,Attribute,HTM/Configuration.md#attribute-i-openai_project
73
94
  openrouter_api_key,Attribute,HTM/Configuration.md#attribute-i-openrouter_api_key
95
+ proposition_extractor,Attribute,HTM/Configuration.md#attribute-i-proposition_extractor
96
+ proposition_model,Attribute,HTM/Configuration.md#attribute-i-proposition_model
97
+ proposition_provider,Attribute,HTM/Configuration.md#attribute-i-proposition_provider
98
+ proposition_timeout,Attribute,HTM/Configuration.md#attribute-i-proposition_timeout
99
+ relevance_access_weight,Attribute,HTM/Configuration.md#attribute-i-relevance_access_weight
100
+ relevance_recency_half_life_hours,Attribute,HTM/Configuration.md#attribute-i-relevance_recency_half_life_hours
101
+ relevance_recency_weight,Attribute,HTM/Configuration.md#attribute-i-relevance_recency_weight
102
+ relevance_semantic_weight,Attribute,HTM/Configuration.md#attribute-i-relevance_semantic_weight
103
+ relevance_tag_weight,Attribute,HTM/Configuration.md#attribute-i-relevance_tag_weight
74
104
  tag_extractor,Attribute,HTM/Configuration.md#attribute-i-tag_extractor
75
105
  tag_model,Attribute,HTM/Configuration.md#attribute-i-tag_model
76
106
  tag_provider,Attribute,HTM/Configuration.md#attribute-i-tag_provider
77
107
  tag_timeout,Attribute,HTM/Configuration.md#attribute-i-tag_timeout
108
+ telemetry_enabled,Attribute,HTM/Configuration.md#attribute-i-telemetry_enabled
78
109
  token_counter,Attribute,HTM/Configuration.md#attribute-i-token_counter
79
110
  week_start,Attribute,HTM/Configuration.md#attribute-i-week_start
80
111
  HTM::Database,Class,HTM/Database.md
@@ -94,6 +125,7 @@ HTM::EmbeddingService,Class,HTM/EmbeddingService.md
94
125
  HTM::EmbeddingService.circuit_breaker,Method,HTM/EmbeddingService.md#method-c-circuit_breaker
95
126
  HTM::EmbeddingService.format_for_storage,Method,HTM/EmbeddingService.md#method-c-format_for_storage
96
127
  HTM::EmbeddingService.generate,Method,HTM/EmbeddingService.md#method-c-generate
128
+ HTM::EmbeddingService.max_dimension,Method,HTM/EmbeddingService.md#method-c-max_dimension
97
129
  HTM::EmbeddingService.pad_embedding,Method,HTM/EmbeddingService.md#method-c-pad_embedding
98
130
  HTM::EmbeddingService.reset_circuit_breaker!,Method,HTM/EmbeddingService.md#method-c-reset_circuit_breaker!
99
131
  HTM::EmbeddingService.validate_embedding!,Method,HTM/EmbeddingService.md#method-c-validate_embedding!
@@ -103,51 +135,87 @@ HTM::ResourceExhaustedError,Class,HTM/ResourceExhaustedError.md
103
135
  HTM::NotFoundError,Class,HTM/NotFoundError.md
104
136
  HTM::EmbeddingError,Class,HTM/EmbeddingError.md
105
137
  HTM::TagError,Class,HTM/TagError.md
138
+ HTM::PropositionError,Class,HTM/PropositionError.md
106
139
  HTM::DatabaseError,Class,HTM/DatabaseError.md
107
140
  HTM::QueryTimeoutError,Class,HTM/QueryTimeoutError.md
108
141
  HTM::AuthorizationError,Class,HTM/AuthorizationError.md
109
142
  HTM::CircuitBreakerOpenError,Class,HTM/CircuitBreakerOpenError.md
110
143
  HTM::LongTermMemory,Class,HTM/LongTermMemory.md
111
- HTM::LongTermMemory.add,Method,HTM/LongTermMemory.md#method-i-add
112
- HTM::LongTermMemory.add_tag,Method,HTM/LongTermMemory.md#method-i-add_tag
113
- HTM::LongTermMemory.batch_load_node_tags,Method,HTM/LongTermMemory.md#method-i-batch_load_node_tags
114
- HTM::LongTermMemory.calculate_relevance,Method,HTM/LongTermMemory.md#method-i-calculate_relevance
115
144
  HTM::LongTermMemory.clear_cache!,Method,HTM/LongTermMemory.md#method-i-clear_cache!
116
- HTM::LongTermMemory.delete,Method,HTM/LongTermMemory.md#method-i-delete
117
- HTM::LongTermMemory.exists?,Method,HTM/LongTermMemory.md#method-i-exists?
118
- HTM::LongTermMemory.find_query_matching_tags,Method,HTM/LongTermMemory.md#method-i-find_query_matching_tags
119
- HTM::LongTermMemory.get_node_tags,Method,HTM/LongTermMemory.md#method-i-get_node_tags
120
145
  HTM::LongTermMemory.initialize,Method,HTM/LongTermMemory.md#method-i-initialize
121
- HTM::LongTermMemory.link_robot_to_node,Method,HTM/LongTermMemory.md#method-i-link_robot_to_node
122
- HTM::LongTermMemory.mark_evicted,Method,HTM/LongTermMemory.md#method-i-mark_evicted
123
- HTM::LongTermMemory.node_topics,Method,HTM/LongTermMemory.md#method-i-node_topics
124
- HTM::LongTermMemory.nodes_by_topic,Method,HTM/LongTermMemory.md#method-i-nodes_by_topic
125
- HTM::LongTermMemory.ontology_structure,Method,HTM/LongTermMemory.md#method-i-ontology_structure
126
146
  HTM::LongTermMemory.pool_size,Method,HTM/LongTermMemory.md#method-i-pool_size
127
- HTM::LongTermMemory.popular_tags,Method,HTM/LongTermMemory.md#method-i-popular_tags
128
- HTM::LongTermMemory.register_robot,Method,HTM/LongTermMemory.md#method-i-register_robot
129
- HTM::LongTermMemory.retrieve,Method,HTM/LongTermMemory.md#method-i-retrieve
130
- HTM::LongTermMemory.search,Method,HTM/LongTermMemory.md#method-i-search
131
- HTM::LongTermMemory.search_by_tags,Method,HTM/LongTermMemory.md#method-i-search_by_tags
132
- HTM::LongTermMemory.search_fulltext,Method,HTM/LongTermMemory.md#method-i-search_fulltext
133
- HTM::LongTermMemory.search_hybrid,Method,HTM/LongTermMemory.md#method-i-search_hybrid
134
- HTM::LongTermMemory.search_with_relevance,Method,HTM/LongTermMemory.md#method-i-search_with_relevance
135
147
  HTM::LongTermMemory.shutdown,Method,HTM/LongTermMemory.md#method-i-shutdown
136
148
  HTM::LongTermMemory.stats,Method,HTM/LongTermMemory.md#method-i-stats
137
- HTM::LongTermMemory.topic_relationships,Method,HTM/LongTermMemory.md#method-i-topic_relationships
138
- HTM::LongTermMemory.track_access,Method,HTM/LongTermMemory.md#method-i-track_access
139
- HTM::LongTermMemory.update_last_accessed,Method,HTM/LongTermMemory.md#method-i-update_last_accessed
140
- HTM::LongTermMemory.update_robot_activity,Method,HTM/LongTermMemory.md#method-i-update_robot_activity
141
149
  query_timeout,Attribute,HTM/LongTermMemory.md#attribute-i-query_timeout
150
+ HTM::PropositionService,Class,HTM/PropositionService.md
151
+ HTM::PropositionService.circuit_breaker,Method,HTM/PropositionService.md#method-c-circuit_breaker
152
+ HTM::PropositionService.extract,Method,HTM/PropositionService.md#method-c-extract
153
+ HTM::PropositionService.parse_propositions,Method,HTM/PropositionService.md#method-c-parse_propositions
154
+ HTM::PropositionService.reset_circuit_breaker!,Method,HTM/PropositionService.md#method-c-reset_circuit_breaker!
155
+ HTM::PropositionService.valid_proposition?,Method,HTM/PropositionService.md#method-c-valid_proposition?
156
+ HTM::PropositionService.validate_and_filter_propositions,Method,HTM/PropositionService.md#method-c-validate_and_filter_propositions
157
+ HTM::QueryCache,Class,HTM/QueryCache.md
158
+ HTM::QueryCache.clear!,Method,HTM/QueryCache.md#method-i-clear!
159
+ HTM::QueryCache.enabled?,Method,HTM/QueryCache.md#method-i-enabled?
160
+ HTM::QueryCache.fetch,Method,HTM/QueryCache.md#method-i-fetch
161
+ HTM::QueryCache.initialize,Method,HTM/QueryCache.md#method-i-initialize
162
+ HTM::QueryCache.invalidate!,Method,HTM/QueryCache.md#method-i-invalidate!
163
+ HTM::QueryCache.invalidate_methods!,Method,HTM/QueryCache.md#method-i-invalidate_methods!
164
+ HTM::QueryCache.stats,Method,HTM/QueryCache.md#method-i-stats
165
+ enabled,Attribute,HTM/QueryCache.md#attribute-i-enabled
142
166
  HTM::Railtie,Class,HTM/Railtie.md
167
+ HTM::RobotGroup,Class,HTM/RobotGroup.md
168
+ HTM::RobotGroup.active?,Method,HTM/RobotGroup.md#method-i-active?
169
+ HTM::RobotGroup.active_robot_names,Method,HTM/RobotGroup.md#method-i-active_robot_names
170
+ HTM::RobotGroup.add_active,Method,HTM/RobotGroup.md#method-i-add_active
171
+ HTM::RobotGroup.add_passive,Method,HTM/RobotGroup.md#method-i-add_passive
172
+ HTM::RobotGroup.clear_working_memory,Method,HTM/RobotGroup.md#method-i-clear_working_memory
173
+ HTM::RobotGroup.demote,Method,HTM/RobotGroup.md#method-i-demote
174
+ HTM::RobotGroup.failover!,Method,HTM/RobotGroup.md#method-i-failover!
175
+ HTM::RobotGroup.in_sync?,Method,HTM/RobotGroup.md#method-i-in_sync?
176
+ HTM::RobotGroup.initialize,Method,HTM/RobotGroup.md#method-i-initialize
177
+ HTM::RobotGroup.member?,Method,HTM/RobotGroup.md#method-i-member?
178
+ HTM::RobotGroup.member_ids,Method,HTM/RobotGroup.md#method-i-member_ids
179
+ HTM::RobotGroup.passive?,Method,HTM/RobotGroup.md#method-i-passive?
180
+ HTM::RobotGroup.passive_robot_names,Method,HTM/RobotGroup.md#method-i-passive_robot_names
181
+ HTM::RobotGroup.promote,Method,HTM/RobotGroup.md#method-i-promote
182
+ HTM::RobotGroup.recall,Method,HTM/RobotGroup.md#method-i-recall
183
+ HTM::RobotGroup.remember,Method,HTM/RobotGroup.md#method-i-remember
184
+ HTM::RobotGroup.remove,Method,HTM/RobotGroup.md#method-i-remove
185
+ HTM::RobotGroup.shutdown,Method,HTM/RobotGroup.md#method-i-shutdown
186
+ HTM::RobotGroup.status,Method,HTM/RobotGroup.md#method-i-status
187
+ HTM::RobotGroup.sync_all,Method,HTM/RobotGroup.md#method-i-sync_all
188
+ HTM::RobotGroup.sync_robot,Method,HTM/RobotGroup.md#method-i-sync_robot
189
+ HTM::RobotGroup.sync_stats,Method,HTM/RobotGroup.md#method-i-sync_stats
190
+ HTM::RobotGroup.transfer_working_memory,Method,HTM/RobotGroup.md#method-i-transfer_working_memory
191
+ HTM::RobotGroup.working_memory_contents,Method,HTM/RobotGroup.md#method-i-working_memory_contents
192
+ channel,Attribute,HTM/RobotGroup.md#attribute-i-channel
193
+ max_tokens,Attribute,HTM/RobotGroup.md#attribute-i-max_tokens
194
+ name,Attribute,HTM/RobotGroup.md#attribute-i-name
195
+ HTM::SqlBuilder,Class,HTM/SqlBuilder.md
196
+ HTM::SqlBuilder.apply_metadata,Method,HTM/SqlBuilder.md#method-c-apply_metadata
197
+ HTM::SqlBuilder.apply_timeframe,Method,HTM/SqlBuilder.md#method-c-apply_timeframe
198
+ HTM::SqlBuilder.metadata_condition,Method,HTM/SqlBuilder.md#method-c-metadata_condition
199
+ HTM::SqlBuilder.pad_embedding,Method,HTM/SqlBuilder.md#method-c-pad_embedding
200
+ HTM::SqlBuilder.sanitize_embedding,Method,HTM/SqlBuilder.md#method-c-sanitize_embedding
201
+ HTM::SqlBuilder.sanitize_like_pattern,Method,HTM/SqlBuilder.md#method-c-sanitize_like_pattern
202
+ HTM::SqlBuilder.timeframe_condition,Method,HTM/SqlBuilder.md#method-c-timeframe_condition
143
203
  HTM::TagService,Class,HTM/TagService.md
144
204
  HTM::TagService.circuit_breaker,Method,HTM/TagService.md#method-c-circuit_breaker
145
205
  HTM::TagService.extract,Method,HTM/TagService.md#method-c-extract
206
+ HTM::TagService.max_depth,Method,HTM/TagService.md#method-c-max_depth
146
207
  HTM::TagService.parse_hierarchy,Method,HTM/TagService.md#method-c-parse_hierarchy
147
208
  HTM::TagService.parse_tags,Method,HTM/TagService.md#method-c-parse_tags
148
209
  HTM::TagService.reset_circuit_breaker!,Method,HTM/TagService.md#method-c-reset_circuit_breaker!
149
210
  HTM::TagService.valid_tag?,Method,HTM/TagService.md#method-c-valid_tag?
150
211
  HTM::TagService.validate_and_filter_tags,Method,HTM/TagService.md#method-c-validate_and_filter_tags
212
+ HTM::Telemetry::NullMeter,Class,HTM/Telemetry/NullMeter.md
213
+ HTM::Telemetry::NullMeter.create_counter,Method,HTM/Telemetry/NullMeter.md#method-i-create_counter
214
+ HTM::Telemetry::NullMeter.create_histogram,Method,HTM/Telemetry/NullMeter.md#method-i-create_histogram
215
+ HTM::Telemetry::NullMeter.create_up_down_counter,Method,HTM/Telemetry/NullMeter.md#method-i-create_up_down_counter
216
+ HTM::Telemetry::NullInstrument,Class,HTM/Telemetry/NullInstrument.md
217
+ HTM::Telemetry::NullInstrument.add,Method,HTM/Telemetry/NullInstrument.md#method-i-add
218
+ HTM::Telemetry::NullInstrument.record,Method,HTM/Telemetry/NullInstrument.md#method-i-record
151
219
  HTM::TimeframeExtractor,Class,HTM/TimeframeExtractor.md
152
220
  HTM::TimeframeExtractor.extract,Method,HTM/TimeframeExtractor.md#method-c-extract
153
221
  HTM::TimeframeExtractor.temporal?,Method,HTM/TimeframeExtractor.md#method-c-temporal?
@@ -162,6 +230,15 @@ HTM::Timeframe::Result,Class,HTM/Timeframe/Result.md
162
230
  extracted,Attribute,HTM/Timeframe/Result.md#attribute-i-extracted
163
231
  query,Attribute,HTM/Timeframe/Result.md#attribute-i-query
164
232
  timeframe,Attribute,HTM/Timeframe/Result.md#attribute-i-timeframe
233
+ HTM::WorkingMemoryChannel,Class,HTM/WorkingMemoryChannel.md
234
+ HTM::WorkingMemoryChannel.channel_name,Method,HTM/WorkingMemoryChannel.md#method-i-channel_name
235
+ HTM::WorkingMemoryChannel.initialize,Method,HTM/WorkingMemoryChannel.md#method-i-initialize
236
+ HTM::WorkingMemoryChannel.listening?,Method,HTM/WorkingMemoryChannel.md#method-i-listening?
237
+ HTM::WorkingMemoryChannel.notify,Method,HTM/WorkingMemoryChannel.md#method-i-notify
238
+ HTM::WorkingMemoryChannel.on_change,Method,HTM/WorkingMemoryChannel.md#method-i-on_change
239
+ HTM::WorkingMemoryChannel.start_listening,Method,HTM/WorkingMemoryChannel.md#method-i-start_listening
240
+ HTM::WorkingMemoryChannel.stop_listening,Method,HTM/WorkingMemoryChannel.md#method-i-stop_listening
241
+ notifications_received,Attribute,HTM/WorkingMemoryChannel.md#attribute-i-notifications_received
165
242
  HTM::WorkingMemory,Class,HTM/WorkingMemory.md
166
243
  HTM::WorkingMemory.add,Method,HTM/WorkingMemory.md#method-i-add
167
244
  HTM::WorkingMemory.add_from_sync,Method,HTM/WorkingMemory.md#method-i-add_from_sync
@@ -30,12 +30,20 @@ Complete API documentation generated by [YARD](https://yardoc.org/) with [yard-m
30
30
  | [HTM::JobAdapter](yard/HTM/JobAdapter.md) | Background job abstraction layer |
31
31
  | [HTM::LongTermMemory](yard/HTM/LongTermMemory.md) | PostgreSQL-backed permanent storage |
32
32
  | [HTM::Observability](yard/HTM/Observability.md) | Metrics and logging instrumentation |
33
+ | [HTM::PropositionService](yard/HTM/PropositionService.md) | HTM::PropositionService class |
34
+ | [HTM::QueryCache](yard/HTM/QueryCache.md) | HTM::QueryCache class |
35
+ | [HTM::RobotGroup](yard/HTM/RobotGroup.md) | HTM::RobotGroup class |
36
+ | [HTM::SqlBuilder](yard/HTM/SqlBuilder.md) | HTM::SqlBuilder class |
33
37
  | [HTM::TagService](yard/HTM/TagService.md) | Hierarchical tag extraction service |
38
+ | [HTM::Telemetry](yard/HTM/Telemetry.md) | HTM::Telemetry class |
39
+ | [HTM::Telemetry::NullInstrument](yard/HTM/Telemetry/NullInstrument.md) | HTM::Telemetry::NullInstrument class |
40
+ | [HTM::Telemetry::NullMeter](yard/HTM/Telemetry/NullMeter.md) | HTM::Telemetry::NullMeter class |
34
41
  | [HTM::Timeframe](yard/HTM/Timeframe.md) | Time-based query filtering |
35
42
  | [HTM::Timeframe::Result](yard/HTM/Timeframe/Result.md) | HTM::Timeframe::Result class |
36
43
  | [HTM::TimeframeExtractor](yard/HTM/TimeframeExtractor.md) | Natural language time parsing |
37
44
  | [HTM::TimeframeExtractor::Result](yard/HTM/TimeframeExtractor/Result.md) | HTM::TimeframeExtractor::Result class |
38
45
  | [HTM::WorkingMemory](yard/HTM/WorkingMemory.md) | Token-limited in-memory cache |
46
+ | [HTM::WorkingMemoryChannel](yard/HTM/WorkingMemoryChannel.md) | HTM::WorkingMemoryChannel class |
39
47
 
40
48
  ## Generating Documentation
41
49
 
@@ -1,6 +1,6 @@
1
1
  # Architecture Overview
2
2
 
3
- HTM (Hierarchical Temporary Memory) implements a sophisticated two-tier memory system designed specifically for LLM-based applications ("robots"). This architecture enables robots to maintain long-term context across sessions while managing token budgets efficiently.
3
+ HTM (Hierarchical Temporal Memory) implements a sophisticated two-tier memory system designed specifically for LLM-based applications ("robots"). This architecture enables robots to maintain long-term context across sessions while managing token budgets efficiently.
4
4
 
5
5
  ## System Overview
6
6
 
@@ -0,0 +1,51 @@
1
+ <svg viewBox="0 0 800 400" xmlns="http://www.w3.org/2000/svg" style="background: transparent;">
2
+ <!-- Title -->
3
+ <text x="400" y="30" text-anchor="middle" fill="#E0E0E0" font-size="16" font-weight="bold">Multi-Provider Failover Strategy</text>
4
+
5
+ <!-- Primary -->
6
+ <rect x="50" y="80" width="200" height="100" fill="rgba(76, 175, 80, 0.2)" stroke="#4CAF50" stroke-width="2" rx="5"/>
7
+ <text x="150" y="105" text-anchor="middle" fill="#4CAF50" font-size="12" font-weight="bold">PRIMARY</text>
8
+ <text x="150" y="130" text-anchor="middle" fill="#E0E0E0" font-size="14">OpenAI GPT-4</text>
9
+ <text x="150" y="155" text-anchor="middle" fill="#B0B0B0" font-size="10">Fast, cost-effective</text>
10
+ <text x="150" y="170" text-anchor="middle" fill="#B0B0B0" font-size="10">JSON output format</text>
11
+
12
+ <!-- Standby 1 -->
13
+ <rect x="300" y="80" width="200" height="100" fill="rgba(255, 152, 0, 0.2)" stroke="#FF9800" stroke-width="2" rx="5"/>
14
+ <text x="400" y="105" text-anchor="middle" fill="#FF9800" font-size="12" font-weight="bold">STANDBY 1</text>
15
+ <text x="400" y="130" text-anchor="middle" fill="#E0E0E0" font-size="14">Anthropic Claude</text>
16
+ <text x="400" y="155" text-anchor="middle" fill="#B0B0B0" font-size="10">Different provider</text>
17
+ <text x="400" y="170" text-anchor="middle" fill="#B0B0B0" font-size="10">XML output format</text>
18
+
19
+ <!-- Standby 2 -->
20
+ <rect x="550" y="80" width="200" height="100" fill="rgba(255, 152, 0, 0.2)" stroke="#FF9800" stroke-width="2" rx="5"/>
21
+ <text x="650" y="105" text-anchor="middle" fill="#FF9800" font-size="12" font-weight="bold">STANDBY 2</text>
22
+ <text x="650" y="130" text-anchor="middle" fill="#E0E0E0" font-size="14">Google Gemini</text>
23
+ <text x="650" y="155" text-anchor="middle" fill="#B0B0B0" font-size="10">Third provider option</text>
24
+ <text x="650" y="170" text-anchor="middle" fill="#B0B0B0" font-size="10">Markdown output format</text>
25
+
26
+ <!-- Arrows -->
27
+ <path d="M 250 130 L 290 130" stroke="#4CAF50" stroke-width="2" marker-end="url(#arrow-fail)"/>
28
+ <path d="M 500 130 L 540 130" stroke="#FF9800" stroke-width="2" marker-end="url(#arrow-fail)"/>
29
+ <text x="270" y="120" fill="#F44336" font-size="10">failover</text>
30
+ <text x="520" y="120" fill="#F44336" font-size="10">failover</text>
31
+
32
+ <!-- Shared Context Box -->
33
+ <rect x="150" y="230" width="500" height="80" fill="rgba(33, 150, 243, 0.15)" stroke="#2196F3" stroke-width="2" rx="5"/>
34
+ <text x="400" y="260" text-anchor="middle" fill="#E0E0E0" font-size="14" font-weight="bold">Shared Working Memory</text>
35
+ <text x="400" y="285" text-anchor="middle" fill="#2196F3" font-size="12">Same context across all providers</text>
36
+
37
+ <!-- Connections to shared -->
38
+ <line x1="150" y1="180" x2="200" y2="230" stroke="#4CAF50" stroke-width="1.5"/>
39
+ <line x1="400" y1="180" x2="400" y2="230" stroke="#FF9800" stroke-width="1.5"/>
40
+ <line x1="650" y1="180" x2="600" y2="230" stroke="#FF9800" stroke-width="1.5"/>
41
+
42
+ <!-- Key points -->
43
+ <text x="400" y="350" text-anchor="middle" fill="#4CAF50" font-size="12">Context is shared</text>
44
+ <text x="400" y="370" text-anchor="middle" fill="#F44336" font-size="12">Prompts are provider-specific</text>
45
+
46
+ <defs>
47
+ <marker id="arrow-fail" markerWidth="10" markerHeight="10" refX="9" refY="3" orient="auto">
48
+ <polygon points="0 0, 10 3, 0 6" fill="#F44336"/>
49
+ </marker>
50
+ </defs>
51
+ </svg>