ragfabric-core 0.3.1__tar.gz

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 (196) hide show
  1. ragfabric_core-0.3.1/.gitignore +49 -0
  2. ragfabric_core-0.3.1/PKG-INFO +50 -0
  3. ragfabric_core-0.3.1/README.md +3 -0
  4. ragfabric_core-0.3.1/pyproject.toml +64 -0
  5. ragfabric_core-0.3.1/src/ragfabric_core/__init__.py +3 -0
  6. ragfabric_core-0.3.1/src/ragfabric_core/agent/__init__.py +1 -0
  7. ragfabric_core-0.3.1/src/ragfabric_core/agent/contracts.py +70 -0
  8. ragfabric_core-0.3.1/src/ragfabric_core/agent/cost.py +91 -0
  9. ragfabric_core-0.3.1/src/ragfabric_core/agent/loop.py +574 -0
  10. ragfabric_core-0.3.1/src/ragfabric_core/agent/nodes.py +545 -0
  11. ragfabric_core-0.3.1/src/ragfabric_core/agent/policy.py +330 -0
  12. ragfabric_core-0.3.1/src/ragfabric_core/agent/state.py +176 -0
  13. ragfabric_core-0.3.1/src/ragfabric_core/agent/tools.py +216 -0
  14. ragfabric_core-0.3.1/src/ragfabric_core/auth/__init__.py +1 -0
  15. ragfabric_core-0.3.1/src/ragfabric_core/auth/api_keys.py +126 -0
  16. ragfabric_core-0.3.1/src/ragfabric_core/auth/base.py +14 -0
  17. ragfabric_core-0.3.1/src/ragfabric_core/auth/policy.py +124 -0
  18. ragfabric_core-0.3.1/src/ragfabric_core/auth/principal.py +64 -0
  19. ragfabric_core-0.3.1/src/ragfabric_core/auth/ratelimit.py +16 -0
  20. ragfabric_core-0.3.1/src/ragfabric_core/auth/service.py +103 -0
  21. ragfabric_core-0.3.1/src/ragfabric_core/config.py +130 -0
  22. ragfabric_core-0.3.1/src/ragfabric_core/config_file.py +305 -0
  23. ragfabric_core-0.3.1/src/ragfabric_core/connectors/__init__.py +1 -0
  24. ragfabric_core-0.3.1/src/ragfabric_core/connectors/base.py +26 -0
  25. ragfabric_core-0.3.1/src/ragfabric_core/db/__init__.py +0 -0
  26. ragfabric_core-0.3.1/src/ragfabric_core/db/migrate.py +31 -0
  27. ragfabric_core-0.3.1/src/ragfabric_core/db/session.py +103 -0
  28. ragfabric_core-0.3.1/src/ragfabric_core/embeddings/__init__.py +0 -0
  29. ragfabric_core-0.3.1/src/ragfabric_core/embeddings/normalise.py +23 -0
  30. ragfabric_core-0.3.1/src/ragfabric_core/generate/__init__.py +0 -0
  31. ragfabric_core-0.3.1/src/ragfabric_core/generate/answer.py +316 -0
  32. ragfabric_core-0.3.1/src/ragfabric_core/generate/cited.py +660 -0
  33. ragfabric_core-0.3.1/src/ragfabric_core/generate/contract.py +119 -0
  34. ragfabric_core-0.3.1/src/ragfabric_core/generate/llm.py +239 -0
  35. ragfabric_core-0.3.1/src/ragfabric_core/graph/__init__.py +1 -0
  36. ragfabric_core-0.3.1/src/ragfabric_core/graph/citations.py +246 -0
  37. ragfabric_core-0.3.1/src/ragfabric_core/graph/contracts.py +317 -0
  38. ragfabric_core-0.3.1/src/ragfabric_core/graph/extract.py +793 -0
  39. ragfabric_core-0.3.1/src/ragfabric_core/graph/resolve.py +1195 -0
  40. ragfabric_core-0.3.1/src/ragfabric_core/graph/traverse.py +592 -0
  41. ragfabric_core-0.3.1/src/ragfabric_core/ingest/__init__.py +0 -0
  42. ragfabric_core-0.3.1/src/ragfabric_core/ingest/chunk.py +101 -0
  43. ragfabric_core-0.3.1/src/ragfabric_core/ingest/clean.py +103 -0
  44. ragfabric_core-0.3.1/src/ragfabric_core/ingest/embed.py +112 -0
  45. ragfabric_core-0.3.1/src/ragfabric_core/ingest/indexing.py +85 -0
  46. ragfabric_core-0.3.1/src/ragfabric_core/ingest/parser.py +126 -0
  47. ragfabric_core-0.3.1/src/ragfabric_core/ingest/pipeline.py +272 -0
  48. ragfabric_core-0.3.1/src/ragfabric_core/ingest/reindex.py +116 -0
  49. ragfabric_core-0.3.1/src/ragfabric_core/ingest/storage.py +65 -0
  50. ragfabric_core-0.3.1/src/ragfabric_core/json_contract.py +77 -0
  51. ragfabric_core-0.3.1/src/ragfabric_core/migrations/alembic.ini +44 -0
  52. ragfabric_core-0.3.1/src/ragfabric_core/migrations/env.py +122 -0
  53. ragfabric_core-0.3.1/src/ragfabric_core/migrations/script.py.mako +25 -0
  54. ragfabric_core-0.3.1/src/ragfabric_core/migrations/versions/0001_initial_schema.py +147 -0
  55. ragfabric_core-0.3.1/src/ragfabric_core/migrations/versions/0002_platform_tables.py +457 -0
  56. ragfabric_core-0.3.1/src/ragfabric_core/migrations/versions/0003_ingestion_and_indexes.py +93 -0
  57. ragfabric_core-0.3.1/src/ragfabric_core/migrations/versions/0004_pin_vector_dim_and_hnsw.py +58 -0
  58. ragfabric_core-0.3.1/src/ragfabric_core/migrations/versions/0005_ingestion_runs.py +59 -0
  59. ragfabric_core-0.3.1/src/ragfabric_core/migrations/versions/0006_timezone_aware_timestamps.py +82 -0
  60. ragfabric_core-0.3.1/src/ragfabric_core/migrations/versions/0007_bm25_term_stats.py +53 -0
  61. ragfabric_core-0.3.1/src/ragfabric_core/migrations/versions/0008_graph_confidence_and_merges.py +158 -0
  62. ragfabric_core-0.3.1/src/ragfabric_core/models/__init__.py +14 -0
  63. ragfabric_core-0.3.1/src/ragfabric_core/models/access.py +118 -0
  64. ragfabric_core-0.3.1/src/ragfabric_core/models/base.py +42 -0
  65. ragfabric_core-0.3.1/src/ragfabric_core/models/document.py +160 -0
  66. ragfabric_core-0.3.1/src/ragfabric_core/models/evaluation.py +55 -0
  67. ragfabric_core-0.3.1/src/ragfabric_core/models/graph.py +179 -0
  68. ragfabric_core-0.3.1/src/ragfabric_core/models/index.py +82 -0
  69. ragfabric_core-0.3.1/src/ragfabric_core/models/runs.py +97 -0
  70. ragfabric_core-0.3.1/src/ragfabric_core/models/user.py +34 -0
  71. ragfabric_core-0.3.1/src/ragfabric_core/pricing.py +99 -0
  72. ragfabric_core-0.3.1/src/ragfabric_core/pricing.yaml +53 -0
  73. ragfabric_core-0.3.1/src/ragfabric_core/providers/__init__.py +1 -0
  74. ragfabric_core-0.3.1/src/ragfabric_core/providers/anthropic_provider.py +128 -0
  75. ragfabric_core-0.3.1/src/ragfabric_core/providers/base.py +82 -0
  76. ragfabric_core-0.3.1/src/ragfabric_core/providers/offline.py +182 -0
  77. ragfabric_core-0.3.1/src/ragfabric_core/providers/openai_compat.py +225 -0
  78. ragfabric_core-0.3.1/src/ragfabric_core/providers/registry.py +84 -0
  79. ragfabric_core-0.3.1/src/ragfabric_core/queue/__init__.py +1 -0
  80. ragfabric_core-0.3.1/src/ragfabric_core/queue/base.py +19 -0
  81. ragfabric_core-0.3.1/src/ragfabric_core/queue/memory_queue.py +27 -0
  82. ragfabric_core-0.3.1/src/ragfabric_core/queue/redis_queue.py +42 -0
  83. ragfabric_core-0.3.1/src/ragfabric_core/queue/registry.py +13 -0
  84. ragfabric_core-0.3.1/src/ragfabric_core/rerank/__init__.py +0 -0
  85. ragfabric_core-0.3.1/src/ragfabric_core/rerank/base.py +61 -0
  86. ragfabric_core-0.3.1/src/ragfabric_core/rerank/cross_encoder.py +68 -0
  87. ragfabric_core-0.3.1/src/ragfabric_core/rerank/llm_reranker.py +99 -0
  88. ragfabric_core-0.3.1/src/ragfabric_core/rerank/noop.py +12 -0
  89. ragfabric_core-0.3.1/src/ragfabric_core/rerank/registry.py +20 -0
  90. ragfabric_core-0.3.1/src/ragfabric_core/runtime.py +32 -0
  91. ragfabric_core-0.3.1/src/ragfabric_core/security.py +66 -0
  92. ragfabric_core-0.3.1/src/ragfabric_core/stores/__init__.py +1 -0
  93. ragfabric_core-0.3.1/src/ragfabric_core/stores/access_sql.py +57 -0
  94. ragfabric_core-0.3.1/src/ragfabric_core/stores/base.py +48 -0
  95. ragfabric_core-0.3.1/src/ragfabric_core/stores/bm25_memory.py +187 -0
  96. ragfabric_core-0.3.1/src/ragfabric_core/stores/bm25_sql.py +339 -0
  97. ragfabric_core-0.3.1/src/ragfabric_core/stores/boosting.py +123 -0
  98. ragfabric_core-0.3.1/src/ragfabric_core/stores/chroma_store.py +266 -0
  99. ragfabric_core-0.3.1/src/ragfabric_core/stores/document_chunks.py +103 -0
  100. ragfabric_core-0.3.1/src/ragfabric_core/stores/fusion.py +64 -0
  101. ragfabric_core-0.3.1/src/ragfabric_core/stores/memory_cache.py +39 -0
  102. ragfabric_core-0.3.1/src/ragfabric_core/stores/pgvector_store.py +167 -0
  103. ragfabric_core-0.3.1/src/ragfabric_core/stores/postgres_fts.py +254 -0
  104. ragfabric_core-0.3.1/src/ragfabric_core/stores/redis_cache.py +29 -0
  105. ragfabric_core-0.3.1/src/ragfabric_core/stores/registry.py +83 -0
  106. ragfabric_core-0.3.1/src/ragfabric_core/stores/term_stats.py +189 -0
  107. ragfabric_core-0.3.1/src/ragfabric_core/strategies/__init__.py +1 -0
  108. ragfabric_core-0.3.1/src/ragfabric_core/strategies/agentic.py +179 -0
  109. ragfabric_core-0.3.1/src/ragfabric_core/strategies/base.py +174 -0
  110. ragfabric_core-0.3.1/src/ragfabric_core/strategies/contract.py +30 -0
  111. ragfabric_core-0.3.1/src/ragfabric_core/strategies/graph.py +395 -0
  112. ragfabric_core-0.3.1/src/ragfabric_core/strategies/registry_defaults.py +154 -0
  113. ragfabric_core-0.3.1/src/ragfabric_core/strategies/traditional.py +180 -0
  114. ragfabric_core-0.3.1/src/ragfabric_core/strategies/vectorless.py +185 -0
  115. ragfabric_core-0.3.1/src/ragfabric_core/telemetry/__init__.py +1 -0
  116. ragfabric_core-0.3.1/src/ragfabric_core/telemetry/tracing.py +88 -0
  117. ragfabric_core-0.3.1/src/ragfabric_core/testing/__init__.py +0 -0
  118. ragfabric_core-0.3.1/src/ragfabric_core/testing/fixtures.py +138 -0
  119. ragfabric_core-0.3.1/src/ragfabric_core/tokens.py +99 -0
  120. ragfabric_core-0.3.1/src/ragfabric_core/workers/__init__.py +1 -0
  121. ragfabric_core-0.3.1/src/ragfabric_core/workers/handlers.py +322 -0
  122. ragfabric_core-0.3.1/src/ragfabric_core/workers/runner.py +147 -0
  123. ragfabric_core-0.3.1/tests/agent_doubles.py +186 -0
  124. ragfabric_core-0.3.1/tests/test_access_policy.py +290 -0
  125. ragfabric_core-0.3.1/tests/test_access_sql.py +87 -0
  126. ragfabric_core-0.3.1/tests/test_agent_assess.py +215 -0
  127. ragfabric_core-0.3.1/tests/test_agent_branches.py +668 -0
  128. ragfabric_core-0.3.1/tests/test_agent_budgets.py +346 -0
  129. ragfabric_core-0.3.1/tests/test_agent_contracts.py +109 -0
  130. ragfabric_core-0.3.1/tests/test_agent_loop.py +270 -0
  131. ragfabric_core-0.3.1/tests/test_agent_plan.py +130 -0
  132. ragfabric_core-0.3.1/tests/test_agent_policy.py +198 -0
  133. ragfabric_core-0.3.1/tests/test_agent_progress.py +147 -0
  134. ragfabric_core-0.3.1/tests/test_agent_state.py +112 -0
  135. ragfabric_core-0.3.1/tests/test_agent_tools.py +390 -0
  136. ragfabric_core-0.3.1/tests/test_agentic_integration.py +288 -0
  137. ragfabric_core-0.3.1/tests/test_api_keys.py +106 -0
  138. ragfabric_core-0.3.1/tests/test_bm25_filters.py +144 -0
  139. ragfabric_core-0.3.1/tests/test_bm25_memory.py +162 -0
  140. ragfabric_core-0.3.1/tests/test_bm25_sql.py +244 -0
  141. ragfabric_core-0.3.1/tests/test_boosting.py +90 -0
  142. ragfabric_core-0.3.1/tests/test_caches.py +24 -0
  143. ragfabric_core-0.3.1/tests/test_chroma_store_unit.py +240 -0
  144. ragfabric_core-0.3.1/tests/test_citation_contract.py +115 -0
  145. ragfabric_core-0.3.1/tests/test_cited_generation.py +99 -0
  146. ragfabric_core-0.3.1/tests/test_clean.py +56 -0
  147. ragfabric_core-0.3.1/tests/test_config_agentic.py +155 -0
  148. ragfabric_core-0.3.1/tests/test_config_file.py +161 -0
  149. ragfabric_core-0.3.1/tests/test_fusion.py +57 -0
  150. ragfabric_core-0.3.1/tests/test_graph_config_wiring.py +658 -0
  151. ragfabric_core-0.3.1/tests/test_graph_contracts.py +534 -0
  152. ragfabric_core-0.3.1/tests/test_graph_empty.py +186 -0
  153. ragfabric_core-0.3.1/tests/test_graph_extract.py +446 -0
  154. ragfabric_core-0.3.1/tests/test_graph_extract_incremental.py +532 -0
  155. ragfabric_core-0.3.1/tests/test_graph_guided.py +240 -0
  156. ragfabric_core-0.3.1/tests/test_graph_integration.py +557 -0
  157. ragfabric_core-0.3.1/tests/test_graph_reingest.py +437 -0
  158. ragfabric_core-0.3.1/tests/test_graph_resolve.py +1385 -0
  159. ragfabric_core-0.3.1/tests/test_graph_surface_names.py +232 -0
  160. ragfabric_core-0.3.1/tests/test_graph_traverse.py +860 -0
  161. ragfabric_core-0.3.1/tests/test_indexing.py +431 -0
  162. ragfabric_core-0.3.1/tests/test_interfaces_shape.py +122 -0
  163. ragfabric_core-0.3.1/tests/test_migrations.py +306 -0
  164. ragfabric_core-0.3.1/tests/test_migrations_postgres.py +143 -0
  165. ragfabric_core-0.3.1/tests/test_migrations_postgres_0008.py +130 -0
  166. ragfabric_core-0.3.1/tests/test_models_graph_confidence.py +87 -0
  167. ragfabric_core-0.3.1/tests/test_models_term_stats.py +32 -0
  168. ragfabric_core-0.3.1/tests/test_normalise.py +20 -0
  169. ragfabric_core-0.3.1/tests/test_pipeline.py +465 -0
  170. ragfabric_core-0.3.1/tests/test_pricing.py +79 -0
  171. ragfabric_core-0.3.1/tests/test_production_safety.py +97 -0
  172. ragfabric_core-0.3.1/tests/test_provider_registry.py +76 -0
  173. ragfabric_core-0.3.1/tests/test_provider_streaming.py +18 -0
  174. ragfabric_core-0.3.1/tests/test_providers_anthropic.py +114 -0
  175. ragfabric_core-0.3.1/tests/test_providers_offline.py +47 -0
  176. ragfabric_core-0.3.1/tests/test_providers_openai_compat.py +180 -0
  177. ragfabric_core-0.3.1/tests/test_queue.py +52 -0
  178. ragfabric_core-0.3.1/tests/test_registry_agentic_wiring.py +138 -0
  179. ragfabric_core-0.3.1/tests/test_reindex.py +267 -0
  180. ragfabric_core-0.3.1/tests/test_rerank.py +241 -0
  181. ragfabric_core-0.3.1/tests/test_runtime.py +27 -0
  182. ragfabric_core-0.3.1/tests/test_storage.py +49 -0
  183. ragfabric_core-0.3.1/tests/test_stores_chroma.py +146 -0
  184. ragfabric_core-0.3.1/tests/test_stores_postgres.py +199 -0
  185. ragfabric_core-0.3.1/tests/test_stores_registry.py +70 -0
  186. ragfabric_core-0.3.1/tests/test_stores_sqlite.py +245 -0
  187. ragfabric_core-0.3.1/tests/test_strategy_agentic.py +193 -0
  188. ragfabric_core-0.3.1/tests/test_strategy_graph.py +406 -0
  189. ragfabric_core-0.3.1/tests/test_strategy_interface.py +141 -0
  190. ragfabric_core-0.3.1/tests/test_strategy_vectorless.py +226 -0
  191. ragfabric_core-0.3.1/tests/test_sub_question_reports.py +196 -0
  192. ragfabric_core-0.3.1/tests/test_term_stats.py +121 -0
  193. ragfabric_core-0.3.1/tests/test_tokens.py +116 -0
  194. ragfabric_core-0.3.1/tests/test_tracing.py +45 -0
  195. ragfabric_core-0.3.1/tests/test_traditional_strategy.py +246 -0
  196. ragfabric_core-0.3.1/tests/test_version.py +48 -0
@@ -0,0 +1,49 @@
1
+ # Environment
2
+ .env
3
+ .env.*
4
+ !.env.example
5
+
6
+ # local configuration (copy of ragfabric.example.yaml)
7
+ ragfabric.yaml
8
+
9
+ # OS
10
+ .DS_Store
11
+
12
+ # Python
13
+ __pycache__/
14
+ *.pyc
15
+ *.pyo
16
+ .venv/
17
+ venv/
18
+ .pytest_cache/
19
+ *.egg-info/
20
+
21
+ # Local data / databases
22
+ *.db
23
+ backend/data/uploads/
24
+
25
+ # Node / Angular
26
+ node_modules/
27
+ dist/
28
+ .angular/
29
+ npm-debug.log*
30
+
31
+ # Editors
32
+ .idea/
33
+ .vscode/
34
+
35
+ # Frontend test artifacts
36
+ coverage/
37
+ out-tsc/
38
+
39
+ # Local planning notes and scratch (never committed)
40
+ MEMORY*.md
41
+ *.local.md
42
+ .notes/
43
+ .scratch/
44
+
45
+ # Secrets and keys (belt and braces; .env already excluded above)
46
+ *.pem
47
+ *.key
48
+ *.p12
49
+ secrets/
@@ -0,0 +1,50 @@
1
+ Metadata-Version: 2.5
2
+ Name: ragfabric-core
3
+ Version: 0.3.1
4
+ Summary: RagFabric engine: retrieval strategies, providers, stores, ingestion, evaluation, access policy.
5
+ Project-URL: Homepage, https://github.com/ranjan-del/ragfabric
6
+ Project-URL: Repository, https://github.com/ranjan-del/ragfabric
7
+ Project-URL: Documentation, https://github.com/ranjan-del/ragfabric/tree/main/docs
8
+ Project-URL: Changelog, https://github.com/ranjan-del/ragfabric/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/ranjan-del/ragfabric/issues
10
+ License-Expression: Apache-2.0
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
17
+ Requires-Python: <3.14,>=3.13
18
+ Requires-Dist: alembic>=1.20
19
+ Requires-Dist: bcrypt>=5.0
20
+ Requires-Dist: email-validator>=2.3
21
+ Requires-Dist: numpy>=2.3
22
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.44
23
+ Requires-Dist: opentelemetry-sdk>=1.44
24
+ Requires-Dist: pgvector>=0.5
25
+ Requires-Dist: psycopg[binary]>=3.3
26
+ Requires-Dist: pydantic-settings>=2.15
27
+ Requires-Dist: pydantic>=2.13
28
+ Requires-Dist: pypdf>=6.18
29
+ Requires-Dist: python-docx>=1.2
30
+ Requires-Dist: python-jose[cryptography]>=3.5
31
+ Requires-Dist: python-pptx>=1.0
32
+ Requires-Dist: pyyaml>=6.0
33
+ Requires-Dist: redis>=8.1
34
+ Requires-Dist: sqlalchemy>=2.0.52
35
+ Requires-Dist: tiktoken>=0.12
36
+ Provides-Extra: anthropic
37
+ Requires-Dist: anthropic>=1.5; extra == 'anthropic'
38
+ Provides-Extra: bm25
39
+ Requires-Dist: rank-bm25>=0.2.2; extra == 'bm25'
40
+ Provides-Extra: chroma
41
+ Requires-Dist: chromadb>=1.5; extra == 'chroma'
42
+ Provides-Extra: openai
43
+ Requires-Dist: openai>=3.13; extra == 'openai'
44
+ Provides-Extra: rerank
45
+ Requires-Dist: sentence-transformers>=5.1; extra == 'rerank'
46
+ Description-Content-Type: text/markdown
47
+
48
+ # ragfabric-core
49
+
50
+ The RagFabric engine. See the repository README.
@@ -0,0 +1,3 @@
1
+ # ragfabric-core
2
+
3
+ The RagFabric engine. See the repository README.
@@ -0,0 +1,64 @@
1
+ [project]
2
+ name = "ragfabric-core"
3
+ version = "0.3.1"
4
+ description = "RagFabric engine: retrieval strategies, providers, stores, ingestion, evaluation, access policy."
5
+ readme = "README.md"
6
+ license = "Apache-2.0"
7
+ requires-python = ">=3.13,<3.14"
8
+ classifiers = [
9
+ "Development Status :: 3 - Alpha",
10
+ "Intended Audience :: Developers",
11
+ "Operating System :: OS Independent",
12
+ "Programming Language :: Python :: 3",
13
+ "Programming Language :: Python :: 3.13",
14
+ "Topic :: Scientific/Engineering :: Artificial Intelligence",
15
+ ]
16
+ dependencies = [
17
+ "pydantic>=2.13",
18
+ "pydantic-settings>=2.15",
19
+ "sqlalchemy>=2.0.52",
20
+ "alembic>=1.20",
21
+ "psycopg[binary]>=3.3",
22
+ "numpy>=2.3",
23
+ "pypdf>=6.18",
24
+ "python-docx>=1.2",
25
+ "python-pptx>=1.0",
26
+ "python-jose[cryptography]>=3.5",
27
+ "bcrypt>=5.0",
28
+ "email-validator>=2.3",
29
+ "pyyaml>=6.0",
30
+ "pgvector>=0.5",
31
+ "redis>=8.1",
32
+ "opentelemetry-sdk>=1.44",
33
+ "opentelemetry-exporter-otlp-proto-http>=1.44",
34
+ "tiktoken>=0.12",
35
+ ]
36
+
37
+ [project.optional-dependencies]
38
+ chroma = ["chromadb>=1.5"]
39
+ # In-process BM25 (stores/bm25_memory.py). Optional because it is not the
40
+ # production path: the SQL store needs no extra at all, and a deployment
41
+ # that never uses the in-process store should not carry the dependency.
42
+ bm25 = ["rank_bm25>=0.2.2"]
43
+ rerank = ["sentence-transformers>=5.1"]
44
+ # Ollama also goes through OpenAICompatibleLLM/OpenAICompatibleEmbeddings
45
+ # (providers/openai_compat.py), since Ollama serves the OpenAI chat and
46
+ # embeddings API at /v1, so an Ollama-only deployment still needs this
47
+ # extra installed; only a deployment that uses neither OpenAI, Azure, vLLM,
48
+ # LM Studio nor Ollama can skip it.
49
+ openai = ["openai>=3.13"]
50
+ anthropic = ["anthropic>=1.5"]
51
+
52
+ [project.urls]
53
+ Homepage = "https://github.com/ranjan-del/ragfabric"
54
+ Repository = "https://github.com/ranjan-del/ragfabric"
55
+ Documentation = "https://github.com/ranjan-del/ragfabric/tree/main/docs"
56
+ Changelog = "https://github.com/ranjan-del/ragfabric/blob/main/CHANGELOG.md"
57
+ Issues = "https://github.com/ranjan-del/ragfabric/issues"
58
+
59
+ [build-system]
60
+ requires = ["hatchling>=1.27"]
61
+ build-backend = "hatchling.build"
62
+
63
+ [tool.hatch.build.targets.wheel]
64
+ packages = ["src/ragfabric_core"]
@@ -0,0 +1,3 @@
1
+ """RagFabric engine."""
2
+
3
+ __version__ = "0.3.1"
@@ -0,0 +1 @@
1
+ """The bounded retrieval agent: state, contracts, tools, nodes and the loop."""
@@ -0,0 +1,70 @@
1
+ """What the agent asks a model for, and what it will accept back.
2
+
3
+ Every decision the agent delegates to a model comes back as JSON validated
4
+ against a schema here. There is no native tool calling involved, deliberately:
5
+ ``LLMProvider`` exposes only ``complete`` and ``stream``, and building a
6
+ tool-calling surface would be a second subsystem that could not be exercised
7
+ without paid API credit. JSON contracts work on every provider, including a
8
+ 3B model running locally, and they let the scripted test double drive every
9
+ branch of the loop offline.
10
+
11
+ The parsers are deliberately forgiving about *packaging* and strict about
12
+ *content*. Small models wrap JSON in a fenced block, preface it with a sentence
13
+ of agreement, or explain themselves afterwards. None of that is a real failure,
14
+ so it is tolerated. A missing field, an empty plan or an invented repair move
15
+ is a real failure, and it returns a ``ContractViolation`` rather than raising,
16
+ so the caller decides whether to retry with a corrective instruction or fall
17
+ back to a safe default. A malformed response must never crash a request and
18
+ must never be silently counted as success.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from pydantic import BaseModel, Field
24
+
25
+ from ragfabric_core.agent.state import RepairMove
26
+ from ragfabric_core.json_contract import ContractViolation, parse_contract
27
+
28
+
29
+ class PlannedSubQuestion(BaseModel):
30
+ text: str = Field(min_length=1)
31
+ tool: str = Field(min_length=1)
32
+ why: str = ""
33
+
34
+
35
+ class PlanResponse(BaseModel):
36
+ sub_questions: list[PlannedSubQuestion] = Field(min_length=1)
37
+
38
+
39
+ class Verdict(BaseModel):
40
+ sub_question: str = Field(min_length=1)
41
+ answered: bool
42
+ missing: str | None = None
43
+
44
+
45
+ class AssessResponse(BaseModel):
46
+ verdicts: list[Verdict]
47
+
48
+
49
+ class RepairResponse(BaseModel):
50
+ move: RepairMove
51
+ rewritten_query: str | None = None
52
+ why: str = ""
53
+
54
+
55
+ def parse_plan(text: str) -> PlanResponse | ContractViolation:
56
+ """A plan with no sub-questions is refused.
57
+
58
+ An empty plan leaves the loop nothing to retrieve for, and it would then
59
+ report no progress on the next pass, which hides the real problem.
60
+ """
61
+ return parse_contract(text, "plan", PlanResponse)
62
+
63
+
64
+ def parse_assess(text: str) -> AssessResponse | ContractViolation:
65
+ return parse_contract(text, "assess", AssessResponse)
66
+
67
+
68
+ def parse_repair(text: str) -> RepairResponse | ContractViolation:
69
+ """An invented move is refused by the schema before it reaches the policy."""
70
+ return parse_contract(text, "repair", RepairResponse)
@@ -0,0 +1,91 @@
1
+ """What a run has spent, and the honest answer when nobody knows (ADR 0004).
2
+
3
+ The agent is the one strategy whose spend is not knowable before it runs, so a
4
+ cost cap has to be checked against tokens that have actually been returned.
5
+ This ledger holds those tokens, per model, and prices them through the same
6
+ ``pricing.yaml`` table the rest of the product reads.
7
+
8
+ **The unpriced model is the whole design.** ``estimate_cost`` reports
9
+ ``usd=None, known=False`` for a model the table has no price for, and there are
10
+ only two ways to carry that into a cap, one of which is a lie:
11
+
12
+ - treat unknown as zero, and the cap silently never binds while the trace still
13
+ implies it was watching;
14
+ - treat unknown as infinite, and every run on a self hosted model stops on its
15
+ first call.
16
+
17
+ So unknown means the cap cannot bind, and the ledger says which models it could
18
+ not price. The caller records that alongside the run rather than reporting a
19
+ number nobody measured. A price of zero is a different thing entirely: local
20
+ inference is priced at zero in the table, with a source saying why, and that is
21
+ a known cost of nothing rather than an unknown cost.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from functools import lru_cache
27
+
28
+ from ragfabric_core.pricing import PricingTable, estimate_cost
29
+
30
+
31
+ @lru_cache(maxsize=1)
32
+ def _shared_table() -> PricingTable:
33
+ """The packaged pricing table, read once per process.
34
+
35
+ Cached because the loop asks for a price several times per run and the file
36
+ cannot change under a running process.
37
+ """
38
+ return PricingTable.load()
39
+
40
+
41
+ class CostLedger:
42
+ """Real token counts per model, priced only where a price exists."""
43
+
44
+ def __init__(self, table: PricingTable | None = None) -> None:
45
+ self._table = table if table is not None else _shared_table()
46
+ self._tokens: dict[tuple[str, str], list[int]] = {}
47
+
48
+ def record(self, provider: str, model: str, input_tokens: int, output_tokens: int) -> None:
49
+ """Add one completion's reported tokens.
50
+
51
+ A node that made no model call passes an empty provider and is ignored,
52
+ because an entry for a call that never happened would put a model in
53
+ the unpriced list on the strength of nothing.
54
+ """
55
+ if not provider:
56
+ return
57
+ spent = self._tokens.setdefault((provider, model), [0, 0])
58
+ spent[0] += input_tokens
59
+ spent[1] += output_tokens
60
+
61
+ def unpriced_models(self) -> list[str]:
62
+ """The ``provider/model`` keys this run used that the table cannot price."""
63
+ return sorted(
64
+ f"{provider}/{model}"
65
+ for (provider, model), (input_tokens, output_tokens) in self._tokens.items()
66
+ if not estimate_cost(self._table, provider, model, input_tokens, output_tokens).known
67
+ )
68
+
69
+ @property
70
+ def known(self) -> bool:
71
+ """True when every model used so far has a price.
72
+
73
+ A run that has made no call at all is known: nothing has been spent,
74
+ and that is a measurement rather than a guess.
75
+ """
76
+ return not self.unpriced_models()
77
+
78
+ def usd(self) -> float | None:
79
+ """The estimated spend, or ``None`` when any model used has no price.
80
+
81
+ Deliberately all or nothing. Summing the priced part of a run and
82
+ presenting it as the total would understate the bill by exactly the
83
+ amount nobody can compute.
84
+ """
85
+ if not self.known:
86
+ return None
87
+ total = 0.0
88
+ for (provider, model), (input_tokens, output_tokens) in self._tokens.items():
89
+ estimate = estimate_cost(self._table, provider, model, input_tokens, output_tokens)
90
+ total += estimate.usd or 0.0
91
+ return round(total, 8)