fact_db 0.0.2 → 0.0.3
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/.envrc +2 -0
- data/.yardopts +5 -0
- data/CHANGELOG.md +64 -0
- data/README.md +107 -6
- data/Rakefile +243 -10
- data/db/migrate/001_enable_extensions.rb +1 -0
- data/db/migrate/002_create_sources.rb +49 -0
- data/db/migrate/003_create_entities.rb +27 -15
- data/db/migrate/004_create_entity_aliases.rb +20 -7
- data/db/migrate/005_create_facts.rb +37 -21
- data/db/migrate/006_create_entity_mentions.rb +14 -6
- data/db/migrate/007_create_fact_sources.rb +16 -8
- data/docs/api/extractors/index.md +5 -5
- data/docs/api/extractors/llm.md +17 -17
- data/docs/api/extractors/rule-based.md +14 -14
- data/docs/api/facts.md +20 -20
- data/docs/api/index.md +4 -4
- data/docs/api/models/entity.md +21 -21
- data/docs/api/models/fact.md +15 -15
- data/docs/api/models/index.md +7 -7
- data/docs/api/models/{content.md → source.md} +29 -29
- data/docs/api/pipeline/extraction.md +25 -25
- data/docs/api/pipeline/index.md +1 -1
- data/docs/api/pipeline/resolution.md +4 -4
- data/docs/api/services/entity-service.md +20 -20
- data/docs/api/services/fact-service.md +12 -12
- data/docs/api/services/index.md +5 -5
- data/docs/api/services/{content-service.md → source-service.md} +27 -27
- data/docs/architecture/database-schema.md +46 -46
- data/docs/architecture/entity-resolution.md +6 -6
- data/docs/architecture/index.md +10 -10
- data/docs/architecture/temporal-facts.md +5 -5
- data/docs/architecture/three-layer-model.md +17 -17
- data/docs/concepts.md +6 -6
- data/docs/examples/basic-usage.md +20 -20
- data/docs/examples/hr-onboarding.md +17 -17
- data/docs/examples/index.md +4 -4
- data/docs/examples/news-analysis.md +23 -23
- data/docs/getting-started/database-setup.md +28 -20
- data/docs/getting-started/index.md +3 -3
- data/docs/getting-started/quick-start.md +33 -30
- data/docs/guides/batch-processing.md +26 -26
- data/docs/guides/configuration.md +158 -77
- data/docs/guides/entity-management.md +14 -14
- data/docs/guides/extracting-facts.md +28 -28
- data/docs/guides/ingesting-content.md +14 -14
- data/docs/guides/llm-integration.md +40 -32
- data/docs/guides/temporal-queries.md +11 -11
- data/docs/index.md +6 -2
- data/examples/.envrc +4 -0
- data/examples/.gitignore +1 -0
- data/examples/001_configuration.rb +312 -0
- data/examples/{basic_usage.rb → 010_basic_usage.rb} +47 -56
- data/examples/{entity_management.rb → 020_entity_management.rb} +57 -72
- data/examples/{temporal_queries.rb → 030_temporal_queries.rb} +39 -59
- data/examples/040_output_formats.rb +177 -0
- data/examples/{rule_based_extraction.rb → 050_rule_based_extraction.rb} +39 -45
- data/examples/060_fluent_temporal_api.rb +217 -0
- data/examples/070_introspection.rb +252 -0
- data/examples/{hr_system.rb → 080_hr_system.rb} +56 -75
- data/examples/090_ingest_demo.rb +515 -0
- data/examples/100_query_context.rb +668 -0
- data/examples/110_prove_it.rb +204 -0
- data/examples/120_dump_database.rb +358 -0
- data/examples/130_rag_feedback_loop.rb +858 -0
- data/examples/README.md +229 -15
- data/examples/data/lincoln_associates.md +201 -0
- data/examples/data/lincoln_biography.md +66 -0
- data/examples/data/lincoln_cabinet.md +243 -0
- data/examples/data/lincoln_family.md +163 -0
- data/examples/data/lincoln_military.md +241 -0
- data/examples/data/lincoln_todd_family.md +136 -0
- data/examples/ingest_reporter.rb +335 -0
- data/examples/utilities.rb +182 -0
- data/lib/fact_db/config/defaults.yml +254 -0
- data/lib/fact_db/config.rb +94 -35
- data/lib/fact_db/database.rb +98 -8
- data/lib/fact_db/extractors/base.rb +106 -21
- data/lib/fact_db/extractors/llm_extractor.rb +35 -63
- data/lib/fact_db/extractors/manual_extractor.rb +46 -6
- data/lib/fact_db/extractors/rule_based_extractor.rb +136 -25
- data/lib/fact_db/llm/adapter.rb +3 -3
- data/lib/fact_db/models/entity.rb +94 -22
- data/lib/fact_db/models/entity_alias.rb +41 -7
- data/lib/fact_db/models/entity_mention.rb +34 -1
- data/lib/fact_db/models/fact.rb +259 -28
- data/lib/fact_db/models/fact_source.rb +43 -9
- data/lib/fact_db/models/source.rb +113 -0
- data/lib/fact_db/pipeline/extraction_pipeline.rb +35 -35
- data/lib/fact_db/pipeline/resolution_pipeline.rb +5 -5
- data/lib/fact_db/query_result.rb +202 -0
- data/lib/fact_db/resolution/entity_resolver.rb +139 -39
- data/lib/fact_db/resolution/fact_resolver.rb +86 -14
- data/lib/fact_db/services/entity_service.rb +246 -37
- data/lib/fact_db/services/fact_service.rb +254 -17
- data/lib/fact_db/services/source_service.rb +164 -0
- data/lib/fact_db/temporal/query.rb +71 -7
- data/lib/fact_db/temporal/query_builder.rb +69 -0
- data/lib/fact_db/temporal/timeline.rb +102 -11
- data/lib/fact_db/transformers/base.rb +77 -0
- data/lib/fact_db/transformers/cypher_transformer.rb +185 -0
- data/lib/fact_db/transformers/json_transformer.rb +17 -0
- data/lib/fact_db/transformers/raw_transformer.rb +35 -0
- data/lib/fact_db/transformers/text_transformer.rb +114 -0
- data/lib/fact_db/transformers/triple_transformer.rb +138 -0
- data/lib/fact_db/validation/alias_filter.rb +185 -0
- data/lib/fact_db/version.rb +1 -1
- data/lib/fact_db.rb +281 -30
- data/mkdocs.yml +2 -2
- metadata +60 -16
- data/db/migrate/002_create_contents.rb +0 -44
- data/lib/fact_db/models/content.rb +0 -62
- data/lib/fact_db/services/content_service.rb +0 -93
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Introspection Example for FactDb
|
|
5
|
+
#
|
|
6
|
+
# This example demonstrates:
|
|
7
|
+
# - Schema introspection - discovering what the system knows
|
|
8
|
+
# - Topic introspection - examining specific entities
|
|
9
|
+
# - Query suggestions based on available data
|
|
10
|
+
# - Retrieval strategy recommendations
|
|
11
|
+
# - New service methods for entity analysis
|
|
12
|
+
|
|
13
|
+
require_relative "utilities"
|
|
14
|
+
|
|
15
|
+
demo_setup!("FactDb Introspection Demo")
|
|
16
|
+
demo_configure_logging(__FILE__)
|
|
17
|
+
|
|
18
|
+
facts = FactDb.new
|
|
19
|
+
entity_service = facts.entity_service
|
|
20
|
+
fact_service = facts.fact_service
|
|
21
|
+
|
|
22
|
+
demo_section("Setup: Creating Sample Data")
|
|
23
|
+
|
|
24
|
+
# Create entities
|
|
25
|
+
maria = entity_service.resolve_or_create(
|
|
26
|
+
"Maria Santos",
|
|
27
|
+
kind: :person,
|
|
28
|
+
aliases: ["M. Santos"],
|
|
29
|
+
description: "Engineering Manager"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
raj = entity_service.resolve_or_create(
|
|
33
|
+
"Raj Patel",
|
|
34
|
+
kind: :person,
|
|
35
|
+
description: "Senior Engineer"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
sarah = entity_service.resolve_or_create(
|
|
39
|
+
"Sarah Kim",
|
|
40
|
+
kind: :person,
|
|
41
|
+
description: "Software Engineer"
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
techcorp = entity_service.resolve_or_create(
|
|
45
|
+
"TechCorp",
|
|
46
|
+
kind: :organization,
|
|
47
|
+
description: "Software company"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
austin = entity_service.resolve_or_create(
|
|
51
|
+
"Austin",
|
|
52
|
+
kind: :place,
|
|
53
|
+
description: "City in Texas"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
puts "Created 5 entities"
|
|
57
|
+
|
|
58
|
+
# Create facts with various relationships
|
|
59
|
+
fact_service.create(
|
|
60
|
+
"Maria Santos is Engineering Manager at TechCorp",
|
|
61
|
+
valid_at: Date.new(2023, 1, 1),
|
|
62
|
+
extraction_method: :manual,
|
|
63
|
+
confidence: 1.0,
|
|
64
|
+
mentions: [
|
|
65
|
+
{ entity_id: maria.id, role: :subject, text: "Maria Santos" },
|
|
66
|
+
{ entity_id: techcorp.id, role: :object, text: "TechCorp" }
|
|
67
|
+
]
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
fact_service.create(
|
|
71
|
+
"Raj Patel reports to Maria Santos",
|
|
72
|
+
valid_at: Date.new(2023, 6, 1),
|
|
73
|
+
extraction_method: :manual,
|
|
74
|
+
mentions: [
|
|
75
|
+
{ entity_id: raj.id, role: :subject, text: "Raj Patel" },
|
|
76
|
+
{ entity_id: maria.id, role: :object, text: "Maria Santos" }
|
|
77
|
+
]
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
fact_service.create(
|
|
81
|
+
"Sarah Kim reports to Maria Santos",
|
|
82
|
+
valid_at: Date.new(2024, 1, 1),
|
|
83
|
+
extraction_method: :manual,
|
|
84
|
+
mentions: [
|
|
85
|
+
{ entity_id: sarah.id, role: :subject, text: "Sarah Kim" },
|
|
86
|
+
{ entity_id: maria.id, role: :object, text: "Maria Santos" }
|
|
87
|
+
]
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Historical fact (superseded)
|
|
91
|
+
old_role = fact_service.create(
|
|
92
|
+
"Maria Santos was Senior Engineer at TechCorp",
|
|
93
|
+
valid_at: Date.new(2020, 1, 1),
|
|
94
|
+
invalid_at: Date.new(2022, 12, 31),
|
|
95
|
+
extraction_method: :manual,
|
|
96
|
+
status: :superseded,
|
|
97
|
+
mentions: [
|
|
98
|
+
{ entity_id: maria.id, role: :subject, text: "Maria Santos" },
|
|
99
|
+
{ entity_id: techcorp.id, role: :object, text: "TechCorp" }
|
|
100
|
+
]
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
fact_service.create(
|
|
104
|
+
"Maria Santos works at Austin office",
|
|
105
|
+
valid_at: Date.new(2023, 1, 1),
|
|
106
|
+
extraction_method: :rule_based,
|
|
107
|
+
mentions: [
|
|
108
|
+
{ entity_id: maria.id, role: :subject, text: "Maria Santos" },
|
|
109
|
+
{ entity_id: austin.id, role: :location, text: "Austin" }
|
|
110
|
+
]
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
puts "Created facts with various relationships and history"
|
|
114
|
+
|
|
115
|
+
demo_section("Section 1: Schema Introspection - facts.introspect()")
|
|
116
|
+
|
|
117
|
+
schema = facts.introspect
|
|
118
|
+
puts "\nSystem Capabilities:"
|
|
119
|
+
schema[:capabilities].each { |c| puts " - #{c}" }
|
|
120
|
+
|
|
121
|
+
puts "\nEntity Types in Database:"
|
|
122
|
+
schema[:entity_kinds].each { |t| puts " - #{t}" }
|
|
123
|
+
|
|
124
|
+
puts "\nAvailable Fact Statuses:"
|
|
125
|
+
schema[:fact_statuses].each { |s| puts " - #{s}" }
|
|
126
|
+
|
|
127
|
+
puts "\nExtraction Methods:"
|
|
128
|
+
schema[:extraction_methods].each { |m| puts " - #{m}" }
|
|
129
|
+
|
|
130
|
+
puts "\nSupported Output Formats:"
|
|
131
|
+
schema[:output_formats].each { |f| puts " - #{f}" }
|
|
132
|
+
|
|
133
|
+
puts "\nRetrieval Strategies:"
|
|
134
|
+
schema[:retrieval_strategies].each { |s| puts " - #{s}" }
|
|
135
|
+
|
|
136
|
+
puts "\nStatistics:"
|
|
137
|
+
puts JSON.pretty_generate(schema[:statistics])
|
|
138
|
+
|
|
139
|
+
demo_section("Section 2: Topic Introspection - facts.introspect('Maria Santos')")
|
|
140
|
+
|
|
141
|
+
maria_info = facts.introspect("Maria Santos")
|
|
142
|
+
if maria_info
|
|
143
|
+
puts "\nEntity Information:"
|
|
144
|
+
puts " Name: #{maria_info[:entity][:name]}"
|
|
145
|
+
puts " Type: #{maria_info[:entity][:kind]}"
|
|
146
|
+
puts " Status: #{maria_info[:entity][:resolution_status]}"
|
|
147
|
+
|
|
148
|
+
puts "\nFact Coverage:"
|
|
149
|
+
puts " Canonical: #{maria_info[:coverage][:facts][:canonical]}"
|
|
150
|
+
puts " Superseded: #{maria_info[:coverage][:facts][:superseded]}"
|
|
151
|
+
puts " Corroborated: #{maria_info[:coverage][:facts][:corroborated]}"
|
|
152
|
+
puts " Synthesized: #{maria_info[:coverage][:facts][:synthesized]}"
|
|
153
|
+
|
|
154
|
+
puts "\nTimespan:"
|
|
155
|
+
puts " From: #{maria_info[:coverage][:timespan][:from]}"
|
|
156
|
+
puts " To: #{maria_info[:coverage][:timespan][:to]}"
|
|
157
|
+
|
|
158
|
+
puts "\nRelationship Types:"
|
|
159
|
+
maria_info[:relationships].each { |r| puts " - #{r}" }
|
|
160
|
+
|
|
161
|
+
puts "\nSuggested Queries:"
|
|
162
|
+
maria_info[:suggested_queries].each { |q| puts " - #{q}" }
|
|
163
|
+
else
|
|
164
|
+
puts "Entity not found"
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
demo_section("Section 3: Query Suggestions - facts.suggest_queries()")
|
|
168
|
+
|
|
169
|
+
%w[Maria\ Santos Raj\ Patel TechCorp].each do |topic|
|
|
170
|
+
suggestions = facts.suggest_queries(topic)
|
|
171
|
+
puts "\nSuggested queries for '#{topic}':"
|
|
172
|
+
if suggestions.empty?
|
|
173
|
+
puts " (no suggestions available)"
|
|
174
|
+
else
|
|
175
|
+
suggestions.each { |s| puts " - #{s}" }
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
demo_section("Section 4: Strategy Suggestions")
|
|
180
|
+
|
|
181
|
+
test_queries = [
|
|
182
|
+
"What happened last week?",
|
|
183
|
+
"Who works at TechCorp?",
|
|
184
|
+
"Find similar projects",
|
|
185
|
+
"Current team members",
|
|
186
|
+
"Changes since January"
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
test_queries.each do |query|
|
|
190
|
+
strategies = facts.suggest_strategies(query)
|
|
191
|
+
puts "\nQuery: \"#{query}\""
|
|
192
|
+
puts " Recommended strategies:"
|
|
193
|
+
strategies.each do |s|
|
|
194
|
+
puts " - #{s[:strategy]}: #{s[:description]}"
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
demo_section("Section 5: New Entity Service Methods")
|
|
199
|
+
|
|
200
|
+
puts "\n--- relationship_types ---"
|
|
201
|
+
puts "All relationship types in database:"
|
|
202
|
+
all_relationships = entity_service.relationship_types
|
|
203
|
+
all_relationships.each { |r| puts " - #{r}" }
|
|
204
|
+
|
|
205
|
+
puts "\n--- relationship_types_for(entity_id) ---"
|
|
206
|
+
puts "Relationship types for Maria Santos:"
|
|
207
|
+
maria_relationships = entity_service.relationship_types_for(maria.id)
|
|
208
|
+
maria_relationships.each { |r| puts " - #{r}" }
|
|
209
|
+
|
|
210
|
+
puts "\n--- timespan_for(entity_id) ---"
|
|
211
|
+
puts "Timespan of facts for Maria Santos:"
|
|
212
|
+
timespan = entity_service.timespan_for(maria.id)
|
|
213
|
+
puts " From: #{timespan[:from]}"
|
|
214
|
+
puts " To: #{timespan[:to]}"
|
|
215
|
+
|
|
216
|
+
demo_section("Section 6: New Fact Service Methods")
|
|
217
|
+
|
|
218
|
+
puts "\n--- fact_stats(entity_id) ---"
|
|
219
|
+
puts "Fact statistics for Maria Santos:"
|
|
220
|
+
maria_stats = fact_service.fact_stats(maria.id)
|
|
221
|
+
maria_stats.each { |status, count| puts " #{status}: #{count}" }
|
|
222
|
+
|
|
223
|
+
puts "\nFact statistics for all facts:"
|
|
224
|
+
all_stats = fact_service.fact_stats
|
|
225
|
+
all_stats.each { |status, count| puts " #{status}: #{count}" }
|
|
226
|
+
|
|
227
|
+
demo_section("Section 7: Practical Use Case - LLM Context Building")
|
|
228
|
+
|
|
229
|
+
puts "\nBuilding context for an LLM query about Maria Santos:\n"
|
|
230
|
+
|
|
231
|
+
# Step 1: Introspect the topic
|
|
232
|
+
topic_info = facts.introspect("Maria Santos")
|
|
233
|
+
|
|
234
|
+
puts "1. Entity identified: #{topic_info[:entity][:name]} (#{topic_info[:entity][:kind]})"
|
|
235
|
+
puts " Coverage: #{topic_info[:coverage][:facts][:canonical]} current facts, #{topic_info[:coverage][:facts][:superseded]} historical"
|
|
236
|
+
|
|
237
|
+
# Step 2: Get facts in LLM-friendly format
|
|
238
|
+
puts "\n2. Facts in text format for LLM consumption:"
|
|
239
|
+
facts_text = facts.current_facts_for(maria.id, format: :text)
|
|
240
|
+
puts facts_text
|
|
241
|
+
|
|
242
|
+
# Step 3: Get facts in structured format for reasoning
|
|
243
|
+
puts "\n3. Facts as triples for structured reasoning:"
|
|
244
|
+
facts_triples = facts.current_facts_for(maria.id, format: :triples)
|
|
245
|
+
facts_triples.take(5).each { |t| puts " #{t.inspect}" }
|
|
246
|
+
puts " ..." if facts_triples.size > 5
|
|
247
|
+
|
|
248
|
+
# Step 4: Suggest follow-up queries
|
|
249
|
+
puts "\n4. Suggested follow-up queries:"
|
|
250
|
+
topic_info[:suggested_queries].each { |q| puts " - #{q}" }
|
|
251
|
+
|
|
252
|
+
demo_footer
|
|
@@ -10,33 +10,26 @@
|
|
|
10
10
|
# - Auditing changes with temporal queries
|
|
11
11
|
# - Detecting conflicts in employee data
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
require_relative "utilities"
|
|
14
|
+
|
|
15
|
+
demo_setup!("HR Knowledge Management System Demo")
|
|
16
|
+
demo_configure_logging(__FILE__)
|
|
15
17
|
|
|
16
18
|
FactDb.configure do |config|
|
|
17
|
-
config.database_url = ENV.fetch("DATABASE_URL", "postgres://#{ENV['USER']}@localhost/fact_db_demo")
|
|
18
19
|
config.default_extractor = :manual
|
|
19
20
|
end
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
entity_service = clock.entity_service
|
|
26
|
-
fact_service = clock.fact_service
|
|
27
|
-
content_service = clock.content_service
|
|
28
|
-
|
|
29
|
-
puts "=" * 60
|
|
30
|
-
puts "HR Knowledge Management System Demo"
|
|
31
|
-
puts "=" * 60
|
|
22
|
+
facts = FactDb.new
|
|
23
|
+
entity_service = facts.entity_service
|
|
24
|
+
fact_service = facts.fact_service
|
|
25
|
+
source_service = facts.source_service
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
puts "\n--- Section 1: Setting Up Organization ---\n"
|
|
27
|
+
demo_section("Section 1: Setting Up Organization")
|
|
35
28
|
|
|
36
29
|
# Create the company
|
|
37
30
|
company = entity_service.create(
|
|
38
31
|
"Innovate Corp",
|
|
39
|
-
|
|
32
|
+
kind: :organization,
|
|
40
33
|
description: "Technology company specializing in AI solutions",
|
|
41
34
|
attributes: { industry: "Technology", founded: "2010" }
|
|
42
35
|
)
|
|
@@ -44,54 +37,53 @@ company = entity_service.create(
|
|
|
44
37
|
# Create departments
|
|
45
38
|
engineering = entity_service.create(
|
|
46
39
|
"Engineering Department",
|
|
47
|
-
|
|
40
|
+
kind: :organization,
|
|
48
41
|
description: "Software engineering team",
|
|
49
42
|
attributes: { parent: company.id }
|
|
50
43
|
)
|
|
51
44
|
|
|
52
45
|
product = entity_service.create(
|
|
53
46
|
"Product Department",
|
|
54
|
-
|
|
47
|
+
kind: :organization,
|
|
55
48
|
description: "Product management team",
|
|
56
49
|
attributes: { parent: company.id }
|
|
57
50
|
)
|
|
58
51
|
|
|
59
52
|
hr_dept = entity_service.create(
|
|
60
53
|
"Human Resources",
|
|
61
|
-
|
|
54
|
+
kind: :organization,
|
|
62
55
|
description: "HR team",
|
|
63
56
|
attributes: { parent: company.id }
|
|
64
57
|
)
|
|
65
58
|
|
|
66
|
-
puts "Created company: #{company.
|
|
67
|
-
puts "Created departments: #{engineering.
|
|
59
|
+
puts "Created company: #{company.name}"
|
|
60
|
+
puts "Created departments: #{engineering.name}, #{product.name}, #{hr_dept.name}"
|
|
68
61
|
|
|
69
62
|
# Create locations
|
|
70
63
|
hq = entity_service.create(
|
|
71
64
|
"San Francisco HQ",
|
|
72
|
-
|
|
65
|
+
kind: :place,
|
|
73
66
|
aliases: ["SF Office", "Headquarters"],
|
|
74
67
|
attributes: { city: "San Francisco", state: "CA" }
|
|
75
68
|
)
|
|
76
69
|
|
|
77
70
|
remote_office = entity_service.create(
|
|
78
71
|
"Austin Office",
|
|
79
|
-
|
|
72
|
+
kind: :place,
|
|
80
73
|
aliases: ["Austin TX Office"],
|
|
81
74
|
attributes: { city: "Austin", state: "TX" }
|
|
82
75
|
)
|
|
83
76
|
|
|
84
|
-
puts "Created locations: #{hq.
|
|
77
|
+
puts "Created locations: #{hq.name}, #{remote_office.name}"
|
|
85
78
|
|
|
86
|
-
|
|
87
|
-
puts "\n--- Section 2: Creating Employee Profiles ---\n"
|
|
79
|
+
demo_section("Section 2: Create Employee Profiles")
|
|
88
80
|
|
|
89
81
|
employees = {}
|
|
90
82
|
|
|
91
83
|
# CEO
|
|
92
84
|
employees[:ceo] = entity_service.create(
|
|
93
85
|
"Katherine Rodriguez",
|
|
94
|
-
|
|
86
|
+
kind: :person,
|
|
95
87
|
aliases: ["Kate Rodriguez", "K. Rodriguez"],
|
|
96
88
|
attributes: { employee_id: "EMP001", email: "krodriguez@innovatecorp.com" },
|
|
97
89
|
description: "Chief Executive Officer"
|
|
@@ -100,7 +92,7 @@ employees[:ceo] = entity_service.create(
|
|
|
100
92
|
# VP Engineering
|
|
101
93
|
employees[:vp_eng] = entity_service.create(
|
|
102
94
|
"Marcus Chen",
|
|
103
|
-
|
|
95
|
+
kind: :person,
|
|
104
96
|
aliases: ["Marc Chen"],
|
|
105
97
|
attributes: { employee_id: "EMP002", email: "mchen@innovatecorp.com" },
|
|
106
98
|
description: "VP of Engineering"
|
|
@@ -109,7 +101,7 @@ employees[:vp_eng] = entity_service.create(
|
|
|
109
101
|
# Senior Engineer
|
|
110
102
|
employees[:senior_eng] = entity_service.create(
|
|
111
103
|
"Priya Sharma",
|
|
112
|
-
|
|
104
|
+
kind: :person,
|
|
113
105
|
attributes: { employee_id: "EMP003", email: "psharma@innovatecorp.com" },
|
|
114
106
|
description: "Senior Software Engineer"
|
|
115
107
|
)
|
|
@@ -117,7 +109,7 @@ employees[:senior_eng] = entity_service.create(
|
|
|
117
109
|
# Junior Engineer (will be promoted)
|
|
118
110
|
employees[:junior_eng] = entity_service.create(
|
|
119
111
|
"Alex Kim",
|
|
120
|
-
|
|
112
|
+
kind: :person,
|
|
121
113
|
attributes: { employee_id: "EMP004", email: "akim@innovatecorp.com" },
|
|
122
114
|
description: "Software Engineer"
|
|
123
115
|
)
|
|
@@ -125,7 +117,7 @@ employees[:junior_eng] = entity_service.create(
|
|
|
125
117
|
# Product Manager
|
|
126
118
|
employees[:pm] = entity_service.create(
|
|
127
119
|
"Jordan Taylor",
|
|
128
|
-
|
|
120
|
+
kind: :person,
|
|
129
121
|
attributes: { employee_id: "EMP005", email: "jtaylor@innovatecorp.com" },
|
|
130
122
|
description: "Product Manager"
|
|
131
123
|
)
|
|
@@ -133,18 +125,17 @@ employees[:pm] = entity_service.create(
|
|
|
133
125
|
# HR Manager
|
|
134
126
|
employees[:hr_mgr] = entity_service.create(
|
|
135
127
|
"Michelle Brown",
|
|
136
|
-
|
|
128
|
+
kind: :person,
|
|
137
129
|
attributes: { employee_id: "EMP006", email: "mbrown@innovatecorp.com" },
|
|
138
130
|
description: "HR Manager"
|
|
139
131
|
)
|
|
140
132
|
|
|
141
133
|
puts "Created #{employees.length} employee profiles"
|
|
142
134
|
|
|
143
|
-
|
|
144
|
-
puts "\n--- Section 3: Recording Employment History ---\n"
|
|
135
|
+
demo_section("Section 3: Record Initial Employment Facts")
|
|
145
136
|
|
|
146
137
|
# Ingest an onboarding document
|
|
147
|
-
onboarding_doc =
|
|
138
|
+
onboarding_doc = source_service.create(
|
|
148
139
|
<<~DOC,
|
|
149
140
|
EMPLOYEE ONBOARDING RECORDS - 2020-2024
|
|
150
141
|
|
|
@@ -155,7 +146,7 @@ onboarding_doc = content_service.create(
|
|
|
155
146
|
Jordan Taylor - Hired as Associate PM on February 1, 2022
|
|
156
147
|
Michelle Brown - Hired as HR Coordinator on April 1, 2021
|
|
157
148
|
DOC
|
|
158
|
-
|
|
149
|
+
kind: :document,
|
|
159
150
|
title: "Historical Onboarding Records"
|
|
160
151
|
)
|
|
161
152
|
|
|
@@ -168,7 +159,7 @@ ceo_employment = fact_service.create(
|
|
|
168
159
|
{ entity_id: company.id, role: :object, text: "Innovate Corp" }
|
|
169
160
|
]
|
|
170
161
|
)
|
|
171
|
-
ceo_employment.add_source(
|
|
162
|
+
ceo_employment.add_source(source: onboarding_doc, kind: :primary)
|
|
172
163
|
|
|
173
164
|
ceo_location = fact_service.create(
|
|
174
165
|
"Katherine Rodriguez works at San Francisco HQ",
|
|
@@ -259,11 +250,10 @@ hr_current = fact_service.create(
|
|
|
259
250
|
|
|
260
251
|
puts "Recorded employment history facts"
|
|
261
252
|
|
|
262
|
-
|
|
263
|
-
puts "\n--- Section 4: Processing a Promotion ---\n"
|
|
253
|
+
demo_section("Section 4: Process a Promotion")
|
|
264
254
|
|
|
265
255
|
# Ingest the promotion memo
|
|
266
|
-
promotion_memo =
|
|
256
|
+
promotion_memo = source_service.create(
|
|
267
257
|
<<~MEMO,
|
|
268
258
|
INTERNAL MEMO
|
|
269
259
|
Date: January 8, 2026
|
|
@@ -277,7 +267,7 @@ promotion_memo = content_service.create(
|
|
|
277
267
|
|
|
278
268
|
Congratulations Alex!
|
|
279
269
|
MEMO
|
|
280
|
-
|
|
270
|
+
kind: :document,
|
|
281
271
|
title: "Promotion Memo - Alex Kim"
|
|
282
272
|
)
|
|
283
273
|
|
|
@@ -291,17 +281,16 @@ promoted_fact = fact_service.supersede(
|
|
|
291
281
|
{ entity_id: engineering.id, role: :object, text: "Engineering Department" }
|
|
292
282
|
]
|
|
293
283
|
)
|
|
294
|
-
promoted_fact.add_source(
|
|
284
|
+
promoted_fact.add_source(source: promotion_memo, kind: :primary)
|
|
295
285
|
|
|
296
286
|
puts "Promoted Alex Kim from Junior Developer to Software Engineer"
|
|
297
287
|
puts "Previous fact (#{junior_original.id}) now superseded"
|
|
298
288
|
puts "New fact ID: #{promoted_fact.id}"
|
|
299
289
|
|
|
300
|
-
|
|
301
|
-
puts "\n--- Section 5: Recording a Transfer ---\n"
|
|
290
|
+
demo_section("Section 5: Record a Transfer")
|
|
302
291
|
|
|
303
292
|
# Jordan is transferring to Austin
|
|
304
|
-
transfer_memo =
|
|
293
|
+
transfer_memo = source_service.create(
|
|
305
294
|
<<~MEMO,
|
|
306
295
|
INTERNAL MEMO
|
|
307
296
|
Date: January 10, 2026
|
|
@@ -311,7 +300,7 @@ transfer_memo = content_service.create(
|
|
|
311
300
|
effective February 1, 2026. Jordan will continue in the
|
|
312
301
|
Product Manager role but will lead our Texas expansion efforts.
|
|
313
302
|
MEMO
|
|
314
|
-
|
|
303
|
+
kind: :document,
|
|
315
304
|
title: "Transfer Notice - Jordan Taylor"
|
|
316
305
|
)
|
|
317
306
|
|
|
@@ -336,52 +325,48 @@ jordan_austin_location = fact_service.create(
|
|
|
336
325
|
{ entity_id: remote_office.id, role: :location, text: "Austin Office" }
|
|
337
326
|
]
|
|
338
327
|
)
|
|
339
|
-
jordan_austin_location.add_source(
|
|
328
|
+
jordan_austin_location.add_source(source: transfer_memo, kind: :primary)
|
|
340
329
|
|
|
341
330
|
puts "Recorded Jordan Taylor's transfer to Austin Office"
|
|
342
331
|
|
|
343
|
-
|
|
344
|
-
puts "\n--- Section 6: HR Queries ---\n"
|
|
332
|
+
demo_section("Section 6: Query Employee Information")
|
|
345
333
|
|
|
346
334
|
# Current state of all employees
|
|
347
335
|
puts "\nCurrent Employee Status:"
|
|
348
336
|
puts "-" * 50
|
|
349
337
|
|
|
350
338
|
employees.each do |key, employee|
|
|
351
|
-
puts "\n#{employee.
|
|
339
|
+
puts "\n#{employee.name}:"
|
|
352
340
|
current_facts = fact_service.current_facts(entity: employee.id)
|
|
353
341
|
current_facts.each do |fact|
|
|
354
|
-
puts " - #{fact.
|
|
342
|
+
puts " - #{fact.text}"
|
|
355
343
|
end
|
|
356
344
|
end
|
|
357
345
|
|
|
358
|
-
|
|
359
|
-
puts "\n--- Section 7: Historical Employee Query ---\n"
|
|
346
|
+
demo_section("Section 7: Historical Query")
|
|
360
347
|
|
|
361
348
|
# What was Alex Kim's role in December 2024?
|
|
362
349
|
puts "\nAlex Kim's facts as of December 2024:"
|
|
363
350
|
past_facts = fact_service.facts_at(Date.new(2024, 12, 1), entity: employees[:junior_eng].id)
|
|
364
|
-
past_facts.each { |f| puts " - #{f.
|
|
351
|
+
past_facts.each { |f| puts " - #{f.text}" }
|
|
365
352
|
|
|
366
353
|
# What is Alex Kim's role now?
|
|
367
354
|
puts "\nAlex Kim's facts as of today:"
|
|
368
355
|
current_facts = fact_service.facts_at(Date.today, entity: employees[:junior_eng].id)
|
|
369
|
-
current_facts.each { |f| puts " - #{f.
|
|
356
|
+
current_facts.each { |f| puts " - #{f.text}" }
|
|
370
357
|
|
|
371
|
-
|
|
372
|
-
puts "\n--- Section 8: Organization Chart ---\n"
|
|
358
|
+
demo_section("Section 8: Organization Chart Query")
|
|
373
359
|
|
|
374
360
|
puts "\nReporting relationships:"
|
|
375
361
|
# Find all "reports to" facts
|
|
376
362
|
reporting_facts = fact_service.search("reports to")
|
|
377
|
-
reporting_facts.each { |f| puts " #{f.
|
|
363
|
+
reporting_facts.each { |f| puts " #{f.text}" }
|
|
378
364
|
|
|
379
365
|
puts "\nEngineering Department members:"
|
|
380
366
|
engineering_facts = fact_service.current_facts(entity: engineering.id)
|
|
381
|
-
engineering_facts.each { |f| puts " #{f.
|
|
367
|
+
engineering_facts.each { |f| puts " #{f.text}" }
|
|
382
368
|
|
|
383
|
-
|
|
384
|
-
puts "\n--- Section 9: Marcus Chen Career Timeline ---\n"
|
|
369
|
+
demo_section("Section 9: Employee Timeline")
|
|
385
370
|
|
|
386
371
|
timeline = fact_service.timeline(
|
|
387
372
|
entity_id: employees[:vp_eng].id,
|
|
@@ -392,11 +377,10 @@ timeline = fact_service.timeline(
|
|
|
392
377
|
timeline.each do |entry|
|
|
393
378
|
end_date = entry[:invalid_at]&.strftime("%Y-%m-%d") || "present"
|
|
394
379
|
status_marker = entry[:status] != "canonical" ? " [#{entry[:status]}]" : ""
|
|
395
|
-
puts " #{entry[:valid_at].strftime('%Y-%m-%d')} - #{end_date}: #{entry[:
|
|
380
|
+
puts " #{entry[:valid_at].strftime('%Y-%m-%d')} - #{end_date}: #{entry[:text]}#{status_marker}"
|
|
396
381
|
end
|
|
397
382
|
|
|
398
|
-
|
|
399
|
-
puts "\n--- Section 10: Audit Trail for Alex Kim ---\n"
|
|
383
|
+
demo_section("Section 10: Audit Trail")
|
|
400
384
|
|
|
401
385
|
alex_facts = FactDb::Models::Fact.joins(:entity_mentions)
|
|
402
386
|
.where(entity_mentions: { entity_id: employees[:junior_eng].id })
|
|
@@ -406,23 +390,20 @@ puts "Complete fact history:"
|
|
|
406
390
|
alex_facts.each do |fact|
|
|
407
391
|
status_info = fact.status != "canonical" ? " [#{fact.status}]" : ""
|
|
408
392
|
validity = fact.invalid_at ? "#{fact.valid_at} - #{fact.invalid_at}" : "#{fact.valid_at} - present"
|
|
409
|
-
puts " [#{validity}] #{fact.
|
|
393
|
+
puts " [#{validity}] #{fact.text}#{status_info}"
|
|
410
394
|
|
|
411
395
|
fact.fact_sources.each do |source|
|
|
412
|
-
puts " Source: #{source.
|
|
396
|
+
puts " Source: #{source.source.title} (#{source.kind})"
|
|
413
397
|
end
|
|
414
398
|
end
|
|
415
399
|
|
|
416
|
-
|
|
417
|
-
puts "\n--- Section 11: HR System Statistics ---\n"
|
|
400
|
+
demo_section("Section 11: Statistics")
|
|
418
401
|
|
|
419
|
-
puts "Total employees tracked: #{entity_service.
|
|
420
|
-
puts "Total departments: #{entity_service.
|
|
402
|
+
puts "Total employees tracked: #{entity_service.by_kind("person").count}"
|
|
403
|
+
puts "Total departments: #{entity_service.by_kind("organization").where("description LIKE ?", "%team%").count}"
|
|
421
404
|
puts "Total employment facts: #{fact_service.stats[:total]}"
|
|
422
405
|
puts "Current facts: #{FactDb::Models::Fact.currently_valid.count}"
|
|
423
406
|
puts "Historical facts: #{FactDb::Models::Fact.historical.count}"
|
|
424
|
-
puts "Documents processed: #{
|
|
407
|
+
puts "Documents processed: #{source_service.stats[:total]}"
|
|
425
408
|
|
|
426
|
-
|
|
427
|
-
puts "HR System Demo Complete!"
|
|
428
|
-
puts "=" * 60
|
|
409
|
+
demo_footer
|