Skip to content
This repository was archived by the owner on Jul 22, 2025. It is now read-only.

Commit 2d6f4ea

Browse files
committed
rubocop
1 parent 5500b1c commit 2d6f4ea

File tree

9 files changed

+244
-204
lines changed

9 files changed

+244
-204
lines changed

app/jobs/regular/generate_inferred_concepts.rb

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,18 @@ def process_batch(item_ids, item_type, match_only)
5151

5252
def process_item(item, item_type, match_only)
5353
# Use the Manager method that handles both identifying and creating concepts
54+
manager = DiscourseAi::InferredConcepts::Manager.new
5455
if match_only
5556
if item_type == "topics"
56-
DiscourseAi::InferredConcepts::Manager.match_topic_to_concepts(item)
57+
manager.match_topic_to_concepts(item)
5758
else # posts
58-
DiscourseAi::InferredConcepts::Manager.match_post_to_concepts(item)
59+
manager.match_post_to_concepts(item)
5960
end
6061
else
6162
if item_type == "topics"
62-
DiscourseAi::InferredConcepts::Manager.generate_concepts_from_topic(item)
63+
manager.generate_concepts_from_topic(item)
6364
else # posts
64-
DiscourseAi::InferredConcepts::Manager.generate_concepts_from_post(item)
65+
manager.generate_concepts_from_post(item)
6566
end
6667
end
6768
end

app/jobs/scheduled/generate_concepts_from_popular_items.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,9 @@ def execute(_args)
1717

1818
def process_popular_topics
1919
# Find candidate topics that are popular and don't have concepts yet
20+
manager = DiscourseAi::InferredConcepts::Manager.new
2021
candidates =
21-
DiscourseAi::InferredConcepts::Manager.find_candidate_topics(
22+
manager.find_candidate_topics(
2223
limit: SiteSetting.inferred_concepts_daily_topics_limit || 20,
2324
min_posts: SiteSetting.inferred_concepts_min_posts || 5,
2425
min_likes: SiteSetting.inferred_concepts_min_likes || 10,
@@ -51,8 +52,9 @@ def process_popular_topics
5152

5253
def process_popular_posts
5354
# Find candidate posts that are popular and don't have concepts yet
55+
manager = DiscourseAi::InferredConcepts::Manager.new
5456
candidates =
55-
DiscourseAi::InferredConcepts::Manager.find_candidate_posts(
57+
manager.find_candidate_posts(
5658
limit: SiteSetting.inferred_concepts_daily_posts_limit || 30,
5759
min_likes: SiteSetting.inferred_concepts_post_min_likes || 5,
5860
exclude_first_posts: true,

lib/inferred_concepts/applier.rb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class Applier
66
# Associates the provided concepts with a topic
77
# topic: a Topic instance
88
# concepts: an array of InferredConcept instances
9-
def self.apply_to_topic(topic, concepts)
9+
def apply_to_topic(topic, concepts)
1010
return if topic.blank? || concepts.blank?
1111

1212
topic.inferred_concepts << concepts
@@ -15,15 +15,15 @@ def self.apply_to_topic(topic, concepts)
1515
# Associates the provided concepts with a post
1616
# post: a Post instance
1717
# concepts: an array of InferredConcept instances
18-
def self.apply_to_post(post, concepts)
18+
def apply_to_post(post, concepts)
1919
return if post.blank? || concepts.blank?
2020

2121
post.inferred_concepts << concepts
2222
end
2323

2424
# Extracts content from a topic for concept analysis
2525
# Returns a string with the topic title and first few posts
26-
def self.topic_content_for_analysis(topic)
26+
def topic_content_for_analysis(topic)
2727
return "" if topic.blank?
2828

2929
# Combine title and first few posts for analysis
@@ -37,7 +37,7 @@ def self.topic_content_for_analysis(topic)
3737

3838
# Extracts content from a post for concept analysis
3939
# Returns a string with the post content
40-
def self.post_content_for_analysis(post)
40+
def post_content_for_analysis(post)
4141
return "" if post.blank?
4242

4343
# Get the topic title for context
@@ -50,14 +50,14 @@ def self.post_content_for_analysis(post)
5050
end
5151

5252
# Match a topic with existing concepts
53-
def self.match_existing_concepts(topic)
53+
def match_existing_concepts(topic)
5454
return [] if topic.blank?
5555

5656
# Get content to analyze
5757
content = topic_content_for_analysis(topic)
5858

5959
# Get all existing concepts
60-
existing_concepts = DiscourseAi::InferredConcepts::Manager.list_concepts
60+
existing_concepts = DiscourseAi::InferredConcepts::Manager.new.list_concepts
6161
return [] if existing_concepts.empty?
6262

6363
# Use the ConceptMatcher persona to match concepts
@@ -73,14 +73,14 @@ def self.match_existing_concepts(topic)
7373
end
7474

7575
# Match a post with existing concepts
76-
def self.match_existing_concepts_for_post(post)
76+
def match_existing_concepts_for_post(post)
7777
return [] if post.blank?
7878

7979
# Get content to analyze
8080
content = post_content_for_analysis(post)
8181

8282
# Get all existing concepts
83-
existing_concepts = DiscourseAi::InferredConcepts::Manager.list_concepts
83+
existing_concepts = DiscourseAi::InferredConcepts::Manager.new.list_concepts
8484
return [] if existing_concepts.empty?
8585

8686
# Use the ConceptMatcher persona to match concepts
@@ -96,7 +96,7 @@ def self.match_existing_concepts_for_post(post)
9696
end
9797

9898
# Use ConceptMatcher persona to match content against provided concepts
99-
def self.match_concepts_to_content(content, concept_list)
99+
def match_concepts_to_content(content, concept_list)
100100
return [] if content.blank? || concept_list.blank?
101101

102102
# Prepare user message with only the content

lib/inferred_concepts/finder.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ module InferredConcepts
55
class Finder
66
# Identifies potential concepts from provided content
77
# Returns an array of concept names (strings)
8-
def self.identify_concepts(content)
8+
def identify_concepts(content)
99
return [] if content.blank?
1010

1111
# Use the ConceptFinder persona to identify concepts
@@ -20,7 +20,7 @@ def self.identify_concepts(content)
2020
DiscourseAi::Personas::BotContext.new(
2121
messages: [{ type: :user, content: content }],
2222
user: Discourse.system_user,
23-
inferred_concepts: DiscourseAi::InferredConcepts::Manager.list_concepts,
23+
inferred_concepts: DiscourseAi::InferredConcepts::Manager.new.list_concepts,
2424
)
2525

2626
bot = DiscourseAi::Personas::Bot.as(Discourse.system_user, persona: persona, model: llm)
@@ -35,7 +35,7 @@ def self.identify_concepts(content)
3535

3636
# Creates or finds concepts in the database from provided names
3737
# Returns an array of InferredConcept instances
38-
def self.create_or_find_concepts(concept_names)
38+
def create_or_find_concepts(concept_names)
3939
return [] if concept_names.blank?
4040

4141
concept_names.map { |name| InferredConcept.find_or_create_by(name: name) }
@@ -51,7 +51,7 @@ def self.create_or_find_concepts(concept_names)
5151
# @param category_ids [Array<Integer>] Only include topics from these categories (optional)
5252
# @param created_after [DateTime] Only include topics created after this time (optional)
5353
# @return [Array<Topic>] Array of Topic objects that are good candidates
54-
def self.find_candidate_topics(
54+
def find_candidate_topics(
5555
limit: 100,
5656
min_posts: 5,
5757
min_likes: 10,
@@ -104,7 +104,7 @@ def self.find_candidate_topics(
104104
# @param category_ids [Array<Integer>] Only include posts from topics in these categories
105105
# @param created_after [DateTime] Only include posts created after this time
106106
# @return [Array<Post>] Array of Post objects that are good candidates
107-
def self.find_candidate_posts(
107+
def find_candidate_posts(
108108
limit: 100,
109109
min_likes: 5,
110110
exclude_first_posts: true,
@@ -144,7 +144,7 @@ def self.find_candidate_posts(
144144
# Deduplicate and standardize a list of concepts
145145
# @param concept_names [Array<String>] List of concept names to deduplicate
146146
# @return [Hash] Hash with deduplicated concepts and mapping
147-
def self.deduplicate_concepts(concept_names)
147+
def deduplicate_concepts(concept_names)
148148
return { deduplicated_concepts: [], mapping: {} } if concept_names.blank?
149149

150150
# Use the ConceptDeduplicator persona to deduplicate concepts

lib/inferred_concepts/manager.rb

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class Manager
66
# Get a list of existing concepts
77
# @param limit [Integer, nil] Optional maximum number of concepts to return
88
# @return [Array<InferredConcept>] Array of InferredConcept objects
9-
def self.list_concepts(limit: nil)
9+
def list_concepts(limit: nil)
1010
query = InferredConcept.all.order("name ASC")
1111

1212
# Apply limit if provided
@@ -21,7 +21,7 @@ def self.list_concepts(limit: nil)
2121
# 2. Process each letter group separately through the deduplicator
2222
# 3. Do a final pass with all deduplicated concepts
2323
# @return [Hash] Statistics about the deduplication process
24-
def self.deduplicate_concepts_by_letter(per_letter_batch: 50, full_pass_batch: 150)
24+
def deduplicate_concepts_by_letter(per_letter_batch: 50, full_pass_batch: 150)
2525
# Get all concepts
2626
all_concepts = list_concepts
2727
return if all_concepts.empty?
@@ -42,14 +42,15 @@ def self.deduplicate_concepts_by_letter(per_letter_batch: 50, full_pass_batch: 1
4242

4343
# Process each letter group
4444
letter_deduplicated_concepts = []
45+
finder = DiscourseAi::InferredConcepts::Finder.new
4546

4647
letter_groups.each do |letter, concepts|
4748
next if concepts.empty?
4849

4950
batches = concepts.each_slice(per_letter_batch).to_a
5051

5152
batches.each do |batch|
52-
result = Finder.deduplicate_concepts(batch)
53+
result = finder.deduplicate_concepts(batch)
5354
letter_deduplicated_concepts.concat(result)
5455
end
5556
end
@@ -60,7 +61,7 @@ def self.deduplicate_concepts_by_letter(per_letter_batch: 50, full_pass_batch: 1
6061

6162
batches = letter_deduplicated_concepts.each_slice(full_pass_batch).to_a
6263
batches.each do |batch|
63-
dedups = Finder.deduplicate_concepts(batch)
64+
dedups = finder.deduplicate_concepts(batch)
6465
final_result.concat(dedups)
6566
end
6667

@@ -76,32 +77,34 @@ def self.deduplicate_concepts_by_letter(per_letter_batch: 50, full_pass_batch: 1
7677
# Extract new concepts from arbitrary content
7778
# @param content [String] The content to analyze
7879
# @return [Array<String>] The identified concept names
79-
def self.identify_concepts(content)
80-
Finder.identify_concepts(content)
80+
def identify_concepts(content)
81+
DiscourseAi::InferredConcepts::Finder.new.identify_concepts(content)
8182
end
8283

8384
# Identify and create concepts from content without applying them to any topic
8485
# @param content [String] The content to analyze
8586
# @return [Array<InferredConcept>] The created or found concepts
86-
def self.generate_concepts_from_content(content)
87+
def generate_concepts_from_content(content)
8788
return [] if content.blank?
8889

8990
# Identify concepts
90-
concept_names = Finder.identify_concepts(content)
91+
finder = DiscourseAi::InferredConcepts::Finder.new
92+
concept_names = finder.identify_concepts(content)
9193
return [] if concept_names.blank?
9294

9395
# Create or find concepts in the database
94-
Finder.create_or_find_concepts(concept_names)
96+
finder.create_or_find_concepts(concept_names)
9597
end
9698

9799
# Generate concepts from a topic's content without applying them to the topic
98100
# @param topic [Topic] A Topic instance
99101
# @return [Array<InferredConcept>] The created or found concepts
100-
def self.generate_concepts_from_topic(topic)
102+
def generate_concepts_from_topic(topic)
101103
return [] if topic.blank?
102104

103105
# Get content to analyze
104-
content = Applier.topic_content_for_analysis(topic)
106+
applier = DiscourseAi::InferredConcepts::Applier.new
107+
content = applier.topic_content_for_analysis(topic)
105108
return [] if content.blank?
106109

107110
# Generate concepts from the content
@@ -111,11 +114,12 @@ def self.generate_concepts_from_topic(topic)
111114
# Generate concepts from a post's content without applying them to the post
112115
# @param post [Post] A Post instance
113116
# @return [Array<InferredConcept>] The created or found concepts
114-
def self.generate_concepts_from_post(post)
117+
def generate_concepts_from_post(post)
115118
return [] if post.blank?
116119

117120
# Get content to analyze
118-
content = Applier.post_content_for_analysis(post)
121+
applier = DiscourseAi::InferredConcepts::Applier.new
122+
content = applier.post_content_for_analysis(post)
119123
return [] if content.blank?
120124

121125
# Generate concepts from the content
@@ -125,25 +129,25 @@ def self.generate_concepts_from_post(post)
125129
# Match a topic against existing concepts
126130
# @param topic [Topic] A Topic instance
127131
# @return [Array<InferredConcept>] The concepts that were applied
128-
def self.match_topic_to_concepts(topic)
132+
def match_topic_to_concepts(topic)
129133
return [] if topic.blank?
130134

131-
Applier.match_existing_concepts(topic)
135+
DiscourseAi::InferredConcepts::Applier.new.match_existing_concepts(topic)
132136
end
133137

134138
# Match a post against existing concepts
135139
# @param post [Post] A Post instance
136140
# @return [Array<InferredConcept>] The concepts that were applied
137-
def self.match_post_to_concepts(post)
141+
def match_post_to_concepts(post)
138142
return [] if post.blank?
139143

140-
Applier.match_existing_concepts_for_post(post)
144+
DiscourseAi::InferredConcepts::Applier.new.match_existing_concepts_for_post(post)
141145
end
142146

143147
# Find topics that have a specific concept
144148
# @param concept_name [String] The name of the concept to search for
145149
# @return [Array<Topic>] Topics that have the specified concept
146-
def self.search_topics_by_concept(concept_name)
150+
def search_topics_by_concept(concept_name)
147151
concept = ::InferredConcept.find_by(name: concept_name)
148152
return [] unless concept
149153
concept.topics
@@ -152,7 +156,7 @@ def self.search_topics_by_concept(concept_name)
152156
# Find posts that have a specific concept
153157
# @param concept_name [String] The name of the concept to search for
154158
# @return [Array<Post>] Posts that have the specified concept
155-
def self.search_posts_by_concept(concept_name)
159+
def search_posts_by_concept(concept_name)
156160
concept = ::InferredConcept.find_by(name: concept_name)
157161
return [] unless concept
158162
concept.posts
@@ -161,11 +165,11 @@ def self.search_posts_by_concept(concept_name)
161165
# Match arbitrary content against existing concepts
162166
# @param content [String] The content to analyze
163167
# @return [Array<String>] Names of matching concepts
164-
def self.match_content_to_concepts(content)
168+
def match_content_to_concepts(content)
165169
existing_concepts = InferredConcept.all.pluck(:name)
166170
return [] if existing_concepts.empty?
167171

168-
Applier.match_concepts_to_content(content, existing_concepts)
172+
DiscourseAi::InferredConcepts::Applier.new.match_concepts_to_content(content, existing_concepts)
169173
end
170174

171175
# Find candidate topics that are good for concept generation
@@ -179,15 +183,15 @@ def self.match_content_to_concepts(content)
179183
# @option opts [Array<Integer>] :category_ids (nil) Only include topics from these categories
180184
# @option opts [DateTime] :created_after (30.days.ago) Only include topics created after this time
181185
# @return [Array<Topic>] Array of Topic objects that are good candidates
182-
def self.find_candidate_topics(opts = {})
183-
Finder.find_candidate_topics(**opts)
186+
def find_candidate_topics(opts = {})
187+
DiscourseAi::InferredConcepts::Finder.new.find_candidate_topics(**opts)
184188
end
185189

186190
# Find candidate posts that are good for concept generation
187191
# @param opts [Hash] Options to pass to the finder
188192
# @return [Array<Post>] Array of Post objects that are good candidates
189-
def self.find_candidate_posts(opts = {})
190-
Finder.find_candidate_posts(**opts)
193+
def find_candidate_posts(opts = {})
194+
DiscourseAi::InferredConcepts::Finder.new.find_candidate_posts(**opts)
191195
end
192196
end
193197
end

lib/personas/concept_finder.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ def self.default_enabled
88
end
99

1010
def system_prompt
11-
existing_concepts = DiscourseAi::InferredConcepts::Manager.list_concepts(limit: 100)
11+
existing_concepts = DiscourseAi::InferredConcepts::Manager.new.list_concepts(limit: 100)
1212
existing_concepts_text = ""
1313

1414
existing_concepts_text = <<~CONCEPTS if existing_concepts.present?

0 commit comments

Comments
 (0)