Class: HTM::PropositionService
- Inherits:
-
Object
- Object
- HTM::PropositionService
- Defined in:
- lib/htm/proposition_service.rb
Overview
Proposition Service - Extracts atomic factual propositions from text
This service breaks complex text into simple, self-contained factual statements that can be stored as independent memory nodes. Each proposition:
-
Expresses a single fact
-
Is understandable without context
-
Uses full names, not pronouns
-
Includes relevant dates/qualifiers
-
Contains one subject-predicate relationship
The actual LLM call is delegated to HTM.configuration.proposition_extractor
Constant Summary collapse
- META_RESPONSE_PATTERNS =
Patterns that indicate meta-responses (LLM asking for input instead of extracting)
[ /please provide/i, /provide the text/i, /provide me with/i, /I need the text/i, /I am ready/i, /waiting for/i, /send me the/i, /what text would you/i, /what would you like/i, /cannot extract.*without/i, /no text provided/i ].freeze
Class Method Summary collapse
-
.circuit_breaker ⇒ HTM::CircuitBreaker
Get or create the circuit breaker for proposition service.
-
.extract(content) ⇒ Array<String>
Extract propositions from text content.
-
.max_length ⇒ Integer
Get maximum character length from config.
-
.meta_response?(proposition) ⇒ Boolean
Check if proposition is a meta-response (LLM asking for input).
-
.min_length ⇒ Integer
Get minimum character length from config.
-
.min_words ⇒ Integer
Get minimum words from config.
-
.parse_propositions(raw_propositions) ⇒ Array<String>
Parse proposition response (handles string or array input).
-
.reset_circuit_breaker! ⇒ void
Reset the circuit breaker (useful for testing).
-
.valid_proposition?(proposition) ⇒ Boolean
Validate single proposition.
-
.validate_and_filter_propositions(propositions) ⇒ Array<String>
Validate and filter propositions.
Class Method Details
.circuit_breaker ⇒ HTM::CircuitBreaker
Get or create the circuit breaker for proposition service
53 54 55 56 57 58 59 60 61 |
# File 'lib/htm/proposition_service.rb', line 53 def circuit_breaker @circuit_breaker_mutex.synchronize do @circuit_breaker ||= HTM::CircuitBreaker.new( name: 'proposition_service', failure_threshold: 5, reset_timeout: 60 ) end end |
.extract(content) ⇒ Array<String>
Extract propositions from text content
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/htm/proposition_service.rb', line 81 def self.extract(content) # Use circuit breaker to protect against cascading failures raw_propositions = circuit_breaker.call do HTM.configuration.proposition_extractor.call(content) end # Parse response (may be string or array) parsed_propositions = parse_propositions(raw_propositions) # Validate and filter propositions validate_and_filter_propositions(parsed_propositions) rescue HTM::CircuitBreakerOpenError, HTM::PropositionError raise rescue StandardError => e HTM.logger.error "PropositionService: Failed to extract propositions: #{e.}" raise HTM::PropositionError, "Proposition extraction failed: #{e.}" end |
.max_length ⇒ Integer
Get maximum character length from config
137 138 139 140 141 |
# File 'lib/htm/proposition_service.rb', line 137 def self.max_length HTM.config.proposition.max_length || 1000 rescue 1000 end |
.meta_response?(proposition) ⇒ Boolean
Check if proposition is a meta-response (LLM asking for input)
158 159 160 |
# File 'lib/htm/proposition_service.rb', line 158 def self.(proposition) META_RESPONSE_PATTERNS.any? { |pattern| proposition.match?(pattern) } end |
.min_length ⇒ Integer
Get minimum character length from config
127 128 129 130 131 |
# File 'lib/htm/proposition_service.rb', line 127 def self.min_length HTM.config.proposition.min_length || 10 rescue 10 end |
.min_words ⇒ Integer
Get minimum words from config
147 148 149 150 151 |
# File 'lib/htm/proposition_service.rb', line 147 def self.min_words HTM.config.proposition.min_words || 3 rescue 3 end |
.parse_propositions(raw_propositions) ⇒ Array<String>
Parse proposition response (handles string or array input)
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/htm/proposition_service.rb', line 104 def self.parse_propositions(raw_propositions) case raw_propositions when Array # Already an array, return as-is raw_propositions.map(&:to_s).map(&:strip).reject(&:empty?) when String # String response - split by newlines, remove list markers raw_propositions .split("\n") .map(&:strip) .map { |line| line.sub(/^[-*•]\s*/, '') } # Remove bullet points .map { |line| line.sub(/^\d+\.\s*/, '') } # Remove numbered lists .map(&:strip) .reject(&:empty?) else raise HTM::PropositionError, "Proposition response must be Array or String, got #{raw_propositions.class}" end end |
.reset_circuit_breaker! ⇒ void
This method returns an undefined value.
Reset the circuit breaker (useful for testing)
67 68 69 70 71 |
# File 'lib/htm/proposition_service.rb', line 67 def reset_circuit_breaker! @circuit_breaker_mutex.synchronize do @circuit_breaker&.reset! end end |
.valid_proposition?(proposition) ⇒ Boolean
Validate single proposition
213 214 215 216 217 218 219 220 221 222 |
# File 'lib/htm/proposition_service.rb', line 213 def self.valid_proposition?(proposition) return false unless proposition.is_a?(String) return false if proposition.length < min_length return false if proposition.length > max_length return false unless proposition.match?(/[a-zA-Z]{3,}/) return false if proposition.split.size < min_words return false if (proposition) true end |
.validate_and_filter_propositions(propositions) ⇒ Array<String>
Validate and filter propositions
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
# File 'lib/htm/proposition_service.rb', line 167 def self.validate_and_filter_propositions(propositions) valid_propositions = [] min_char_length = min_length max_char_length = max_length min_word_count = min_words propositions.each do |proposition| # Check minimum length (characters) next if proposition.length < min_char_length # Check maximum length if proposition.length > max_char_length HTM.logger.warn "PropositionService: Proposition too long, skipping: #{proposition[0..50]}..." next end # Check for actual content (not just punctuation/whitespace) unless proposition.match?(/[a-zA-Z]{3,}/) next end # Check minimum word count word_count = proposition.split.size if word_count < min_word_count HTM.logger.debug "PropositionService: Proposition too short (#{word_count} words), skipping: #{proposition}" next end # Filter out meta-responses (LLM asking for more input) if (proposition) HTM.logger.warn "PropositionService: Filtered meta-response: #{proposition[0..50]}..." next end # Proposition is valid valid_propositions << proposition end valid_propositions.uniq end |