fred-core 3.4.3__tar.gz → 3.4.4__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 (176) hide show
  1. {fred_core-3.4.3 → fred_core-3.4.4}/PKG-INFO +1 -1
  2. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/common/resilient_sink.py +3 -5
  3. fred_core-3.4.4/fred_core/store/vector_search.py +128 -0
  4. fred_core-3.4.4/fred_core/tests/store/test_vector_search.py +84 -0
  5. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core.egg-info/PKG-INFO +1 -1
  6. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core.egg-info/SOURCES.txt +1 -0
  7. {fred_core-3.4.3 → fred_core-3.4.4}/pyproject.toml +1 -1
  8. fred_core-3.4.3/fred_core/store/vector_search.py +0 -72
  9. {fred_core-3.4.3 → fred_core-3.4.4}/README.md +0 -0
  10. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/__init__.py +0 -0
  11. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/cli/__init__.py +0 -0
  12. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/cli/auth.py +0 -0
  13. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/cli/ui.py +0 -0
  14. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/common/__init__.py +0 -0
  15. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/common/config_files.py +0 -0
  16. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/common/config_loader.py +0 -0
  17. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/common/env.py +0 -0
  18. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/common/fastapi_handlers.py +0 -0
  19. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/common/lru_cache.py +0 -0
  20. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/common/structures.py +0 -0
  21. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/common/team_id.py +0 -0
  22. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/common/utils.py +0 -0
  23. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/documents/__init__.py +0 -0
  24. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/documents/document_models.py +0 -0
  25. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/documents/document_store.py +0 -0
  26. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/documents/document_structures.py +0 -0
  27. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/documents/postgres_document_store.py +0 -0
  28. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/documents/tag_models.py +0 -0
  29. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/filesystem/gcs_filesystem.py +0 -0
  30. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/filesystem/local_filesystem.py +0 -0
  31. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/filesystem/minio_filesystem.py +0 -0
  32. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/filesystem/structures.py +0 -0
  33. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/history/__init__.py +0 -0
  34. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/history/base_history_store.py +0 -0
  35. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/history/history_models.py +0 -0
  36. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/history/history_schema.py +0 -0
  37. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/history/postgres_history_store.py +0 -0
  38. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/__init__.py +0 -0
  39. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/base_kpi_store.py +0 -0
  40. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/base_kpi_writer.py +0 -0
  41. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/http_middleware.py +0 -0
  42. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/kpi_factory.py +0 -0
  43. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/kpi_phase_metric.py +0 -0
  44. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/kpi_process.py +0 -0
  45. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/kpi_reader_structures.py +0 -0
  46. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/kpi_writer.py +0 -0
  47. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/kpi_writer_structures.py +0 -0
  48. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/log_kpi_store.py +0 -0
  49. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/noop_kpi_writer.py +0 -0
  50. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/opensearch_kpi_store.py +0 -0
  51. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/kpi/prometheus_kpi_store.py +0 -0
  52. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/logs/__init__.py +0 -0
  53. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/logs/audit_log.py +0 -0
  54. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/logs/base_log_store.py +0 -0
  55. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/logs/log_setup.py +0 -0
  56. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/logs/log_store_factory.py +0 -0
  57. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/logs/log_structures.py +0 -0
  58. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/logs/memory_log_store.py +0 -0
  59. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/logs/null_log_store.py +0 -0
  60. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/logs/opensearch_log_store.py +0 -0
  61. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/model/factory.py +0 -0
  62. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/model/http_clients.py +0 -0
  63. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/model/models.py +0 -0
  64. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/models/__init__.py +0 -0
  65. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/models/base.py +0 -0
  66. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/portable/__init__.py +0 -0
  67. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/portable/observability.py +0 -0
  68. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/py.typed +0 -0
  69. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/scheduler/__init__.py +0 -0
  70. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/scheduler/backend.py +0 -0
  71. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/scheduler/scheduler_structures.py +0 -0
  72. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/scheduler/temporal_client_provider.py +0 -0
  73. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/__init__.py +0 -0
  74. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/authorization.py +0 -0
  75. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/backend_to_backend_auth.py +0 -0
  76. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/keycloak/__init__.py +0 -0
  77. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/keycloak/keycloack_admin_client.py +0 -0
  78. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/models.py +0 -0
  79. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/oidc.py +0 -0
  80. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/outbound.py +0 -0
  81. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/rebac/noop_engine.py +0 -0
  82. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/rebac/openfga_engine.py +0 -0
  83. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/rebac/openfga_schema.py +0 -0
  84. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/rebac/rebac_engine.py +0 -0
  85. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/rebac/rebac_factory.py +0 -0
  86. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/rebac/schema.fga +0 -0
  87. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/rebac/schema.fga.json +0 -0
  88. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/structure.py +0 -0
  89. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/security/whitelist_access_control/access_control.py +0 -0
  90. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/session/__init__.py +0 -0
  91. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/session/session_schema.py +0 -0
  92. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/session/stores/__init__.py +0 -0
  93. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/session/stores/base_session_store.py +0 -0
  94. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/session/stores/postgres_session_store.py +0 -0
  95. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/session/stores/session_models.py +0 -0
  96. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/sql/__init__.py +0 -0
  97. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/sql/alembic_env.py +0 -0
  98. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/sql/async_session.py +0 -0
  99. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/sql/base_sql.py +0 -0
  100. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/sql/mixin.py +0 -0
  101. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/store/__init__.py +0 -0
  102. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/store/base_content_store.py +0 -0
  103. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/store/local_content_store.py +0 -0
  104. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/store/minio_content_store.py +0 -0
  105. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/store/opensearch_mapping_validator.py +0 -0
  106. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tasks/__init__.py +0 -0
  107. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tasks/authz.py +0 -0
  108. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tasks/bus.py +0 -0
  109. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tasks/models.py +0 -0
  110. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tasks/orm_models.py +0 -0
  111. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tasks/service.py +0 -0
  112. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tasks/sse.py +0 -0
  113. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tasks/store.py +0 -0
  114. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tasks/workflow_control.py +0 -0
  115. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/teams/__init__.py +0 -0
  116. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/teams/metadata_store.py +0 -0
  117. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/teams/team_metatada_models.py +0 -0
  118. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/__init__.py +0 -0
  119. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/cli/test_auth.py +0 -0
  120. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/cli/test_ui.py +0 -0
  121. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/common/test_config_loader.py +0 -0
  122. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/common/test_env.py +0 -0
  123. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/common/test_fastapi_handlers.py +0 -0
  124. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/common/test_log_setup.py +0 -0
  125. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/common/test_lru_cache.py +0 -0
  126. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/common/test_resilient_sink.py +0 -0
  127. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/common/test_structures.py +0 -0
  128. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/filesystem/test_gcs_filesystem.py +0 -0
  129. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/filesystem/test_local_filesystem.py +0 -0
  130. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/integration/__init__.py +0 -0
  131. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/integration/test_rebac.py +0 -0
  132. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/kpi/test_http_middleware.py +0 -0
  133. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/kpi/test_noop_kpi_writer.py +0 -0
  134. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/kpi/test_opensearch_kpi_store_anonymise.py +0 -0
  135. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/kpi/test_opensearch_kpi_store_ensure_ready.py +0 -0
  136. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/logs/test_audit_log.py +0 -0
  137. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/logs/test_log_store_factory.py +0 -0
  138. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/logs/test_memory_log_store.py +0 -0
  139. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/logs/test_opensearch_log_store_ensure_ready.py +0 -0
  140. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/model/test_anthropic_factory.py +0 -0
  141. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/model/test_embedding_factory.py +0 -0
  142. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/model/test_http_clients.py +0 -0
  143. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/model/test_vertex_model_garden_auth.py +0 -0
  144. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/portable/test_observability.py +0 -0
  145. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/scheduler/test_backend.py +0 -0
  146. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/scheduler/test_temporal_client_provider.py +0 -0
  147. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/security/__init__.py +0 -0
  148. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/security/test_capability_scoping_1980.py +0 -0
  149. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/security/test_oidc_strict.py +0 -0
  150. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/security/test_rebac_config_defaults.py +0 -0
  151. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/security/test_rebac_engine_team_helpers.py +0 -0
  152. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/security/test_rebac_schema_authz05.py +0 -0
  153. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/security/test_security_profile.py +0 -0
  154. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/security/test_service_agent.py +0 -0
  155. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/security/test_whitelist_access_control.py +0 -0
  156. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/session/test_postgres_json_session_store_sqlite.py +0 -0
  157. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/store/test_local_content_store.py +0 -0
  158. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/tasks/__init__.py +0 -0
  159. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/tasks/test_authz.py +0 -0
  160. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/tasks/test_bus.py +0 -0
  161. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/tasks/test_models.py +0 -0
  162. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/tasks/test_reconcile.py +0 -0
  163. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/tasks/test_scheduling.py +0 -0
  164. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/tasks/test_sse.py +0 -0
  165. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/test_kpi_writer_fail_open.py +0 -0
  166. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/test_log_kpi_store.py +0 -0
  167. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/tests/test_prometheus_kpi_store.py +0 -0
  168. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/users/__init__.py +0 -0
  169. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/users/store/__init__.py +0 -0
  170. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/users/store/base_user_store.py +0 -0
  171. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/users/store/postgres_user_store.py +0 -0
  172. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core/users/user_models.py +0 -0
  173. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core.egg-info/dependency_links.txt +0 -0
  174. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core.egg-info/requires.txt +0 -0
  175. {fred_core-3.4.3 → fred_core-3.4.4}/fred_core.egg-info/top_level.txt +0 -0
  176. {fred_core-3.4.3 → fred_core-3.4.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fred-core
3
- Version: 3.4.3
3
+ Version: 3.4.4
4
4
  Summary: Core shared utilities for Fred backends (config, storage, security, and runtime helpers).
5
5
  Author-email: Thales <noreply@thalesgroup.com>
6
6
  License: Apache-2.0
@@ -50,17 +50,15 @@ from typing import Any, Protocol
50
50
  logger = logging.getLogger(__name__)
51
51
 
52
52
 
53
- # fmt: off
54
53
  class _WritableStore(Protocol):
55
54
  def ensure_ready(self) -> None:
56
- ...
55
+ pass
57
56
 
58
57
  def index_event(self, event: Any) -> None:
59
- ...
58
+ pass
60
59
 
61
60
  def bulk_index(self, events: list[Any]) -> None:
62
- ...
63
- # fmt: on
61
+ pass
64
62
 
65
63
 
66
64
  class _CircuitBreaker:
@@ -0,0 +1,128 @@
1
+ # Copyright Thales 2025
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # fred_core/vector_search.py
16
+
17
+ from typing import List, Optional, Sequence
18
+
19
+ from pydantic import BaseModel, Field
20
+
21
+ # A pointer chunk (RAG-DATASET-DISCOVERY-RFC.md) describes a structured dataset
22
+ # for discovery — it carries no real content, so it must never be presented as
23
+ # a citable "source" the way a real content chunk is. `chunk_kind` distinguishes
24
+ # the two; this constant is the single place both writers (TabularProcessor)
25
+ # and readers (document_access, knowledge.search) agree on the pointer value.
26
+ DATASET_POINTER_CHUNK_KIND = "dataset_pointer"
27
+
28
+ # Default for `select_citable_sources`'s `min_score_ratio` — a hit scoring
29
+ # below half the best hit's score in the same search call is treated as noise
30
+ # relative to the strongest match, not a citable basis for an answer. Callers
31
+ # that expose this as a tunable field (e.g. DocumentAccessConfig) should use
32
+ # this as their own field default, so the two stay in sync.
33
+ DEFAULT_MIN_SOURCE_SCORE_RATIO = 0.5
34
+
35
+
36
+ class VectorSearchHit(BaseModel):
37
+ # Content (chunk)
38
+ content: str
39
+ page: Optional[int] = None
40
+ section: Optional[str] = None
41
+ viewer_fragment: Optional[str] = None # e.g., "p=12&sel=340-520"
42
+ slide_id: Optional[int] = None
43
+ has_visual_evidence: Optional[bool] = None
44
+ slide_image_uri: Optional[str] = None
45
+ chunk_kind: Optional[str] = Field(
46
+ default=None,
47
+ description=(
48
+ "content (default, real ingested prose/data) or "
49
+ f"'{DATASET_POINTER_CHUNK_KIND}' (a discovery pointer to a structured "
50
+ "dataset, never citable as a source)."
51
+ ),
52
+ )
53
+
54
+ # Identity
55
+ uid: str = Field(..., description="Document UID")
56
+ title: str
57
+ author: Optional[str] = None
58
+ created: Optional[str] = None
59
+ modified: Optional[str] = None
60
+
61
+ # File/source
62
+ file_name: Optional[str] = None
63
+ file_path: Optional[str] = None
64
+ repository: Optional[str] = None
65
+ pull_location: Optional[str] = None
66
+ language: Optional[str] = None
67
+ mime_type: Optional[str] = None
68
+ type: Optional[str] = Field(default=None, description="File type/category")
69
+
70
+ # Tags (UI wants *names*; keep ids too for filters)
71
+ tag_ids: List[str] = []
72
+ tag_names: List[str] = []
73
+ tag_full_paths: List[str] = []
74
+
75
+ # Link fields (internal viewers / external)
76
+ preview_url: Optional[str] = None # e.g., "/documents/{uid}"
77
+ preview_at_url: Optional[str] = None # e.g., "/documents/{uid}#{viewer_fragment}"
78
+ repo_url: Optional[str] = None # e.g., "https://git/.../blob/ref/path#Lx-Ly"
79
+ citation_url: Optional[str] = None # e.g., "/documents/{uid}#chunk={chunk_id}"
80
+
81
+ # Access (optional, forward-looking)
82
+ license: Optional[str] = None
83
+ confidential: Optional[bool] = None
84
+
85
+ # Metrics
86
+ score: float = Field(..., description="Similarity score from vector search")
87
+ rank: Optional[int] = None
88
+ embedding_model: Optional[str] = None
89
+ vector_index: Optional[str] = None
90
+ token_count: Optional[int] = None
91
+
92
+ # Provenance
93
+ retrieved_at: Optional[str] = None
94
+ retrieval_session_id: Optional[str] = None
95
+
96
+
97
+ def select_citable_sources(
98
+ hits: Sequence[VectorSearchHit],
99
+ *,
100
+ min_score_ratio: float = DEFAULT_MIN_SOURCE_SCORE_RATIO,
101
+ ) -> tuple[VectorSearchHit, ...]:
102
+ """
103
+ Narrow a raw search-tool hit set down to what a chat "Sources" panel may
104
+ cite — the full, unfiltered hit set must still reach the model's tool
105
+ content (it needs every hit, including a dataset pointer, to decide how
106
+ to answer); this filter is for the human-facing citation list only.
107
+
108
+ Excludes two kinds of hit:
109
+ - dataset pointer chunks (RAG-DATASET-DISCOVERY-RFC.md) — descriptive
110
+ metadata about a structured dataset, never real content a human should
111
+ be pointed to as "the source" of a factual claim.
112
+ - hits scoring below `min_score_ratio` of the best score in this same
113
+ hit set — noise relative to the strongest match in the batch, found
114
+ live citing near-zero-relevance paragraphs from an unrelated document
115
+ alongside a SQL-derived answer (RAG-DATASET-DISCOVERY-RFC.md §7).
116
+
117
+ `min_score_ratio` is relative, not absolute, so it stays meaningful
118
+ across embedding models with different score scales/distributions.
119
+ """
120
+ if not hits:
121
+ return ()
122
+ top_score = max(hit.score for hit in hits)
123
+ threshold = top_score * min_score_ratio
124
+ return tuple(
125
+ hit
126
+ for hit in hits
127
+ if hit.chunk_kind != DATASET_POINTER_CHUNK_KIND and hit.score >= threshold
128
+ )
@@ -0,0 +1,84 @@
1
+ # Copyright Thales 2026
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ from fred_core.store.vector_search import (
16
+ DATASET_POINTER_CHUNK_KIND,
17
+ DEFAULT_MIN_SOURCE_SCORE_RATIO,
18
+ VectorSearchHit,
19
+ select_citable_sources,
20
+ )
21
+
22
+
23
+ def _hit(uid: str, score: float, chunk_kind: str | None = None) -> VectorSearchHit:
24
+ return VectorSearchHit(
25
+ uid=uid, title=f"Doc {uid}", content="body", score=score, chunk_kind=chunk_kind
26
+ )
27
+
28
+
29
+ def test_select_citable_sources_excludes_dataset_pointer_regardless_of_score():
30
+ pointer = _hit("p1", score=0.9, chunk_kind=DATASET_POINTER_CHUNK_KIND)
31
+ real = _hit("d1", score=0.5)
32
+
33
+ result = select_citable_sources([pointer, real], min_score_ratio=0.1)
34
+
35
+ assert [hit.uid for hit in result] == ["d1"]
36
+
37
+
38
+ def test_select_citable_sources_excludes_hits_below_relative_score_threshold():
39
+ strong = _hit("d1", score=0.5)
40
+ noise = _hit("d2", score=0.05) # 10% of the top score
41
+
42
+ result = select_citable_sources([strong, noise], min_score_ratio=0.5)
43
+
44
+ assert [hit.uid for hit in result] == ["d1"]
45
+
46
+
47
+ def test_select_citable_sources_keeps_hit_exactly_at_the_ratio_threshold():
48
+ strong = _hit("d1", score=1.0)
49
+ boundary = _hit("d2", score=0.5) # exactly 50% of the top score
50
+
51
+ result = select_citable_sources([strong, boundary], min_score_ratio=0.5)
52
+
53
+ assert {hit.uid for hit in result} == {"d1", "d2"}
54
+
55
+
56
+ def test_select_citable_sources_default_ratio_matches_the_shared_constant():
57
+ strong = _hit("d1", score=1.0)
58
+ just_below_default = _hit("d2", score=DEFAULT_MIN_SOURCE_SCORE_RATIO - 0.01)
59
+
60
+ result = select_citable_sources([strong, just_below_default])
61
+
62
+ assert [hit.uid for hit in result] == ["d1"]
63
+
64
+
65
+ def test_select_citable_sources_keeps_every_real_hit_when_scores_are_close():
66
+ a = _hit("d1", score=0.6)
67
+ b = _hit("d2", score=0.35) # ~58% of the top score
68
+
69
+ result = select_citable_sources([a, b], min_score_ratio=0.5)
70
+
71
+ assert {hit.uid for hit in result} == {"d1", "d2"}
72
+
73
+
74
+ def test_select_citable_sources_empty_input_returns_empty_tuple():
75
+ assert select_citable_sources([]) == ()
76
+
77
+
78
+ def test_select_citable_sources_all_pointers_returns_empty_tuple():
79
+ pointers = [
80
+ _hit("p1", score=0.9, chunk_kind=DATASET_POINTER_CHUNK_KIND),
81
+ _hit("p2", score=0.8, chunk_kind=DATASET_POINTER_CHUNK_KIND),
82
+ ]
83
+
84
+ assert select_citable_sources(pointers) == ()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fred-core
3
- Version: 3.4.3
3
+ Version: 3.4.4
4
4
  Summary: Core shared utilities for Fred backends (config, storage, security, and runtime helpers).
5
5
  Author-email: Thales <noreply@thalesgroup.com>
6
6
  License: Apache-2.0
@@ -158,6 +158,7 @@ fred_core/tests/security/test_service_agent.py
158
158
  fred_core/tests/security/test_whitelist_access_control.py
159
159
  fred_core/tests/session/test_postgres_json_session_store_sqlite.py
160
160
  fred_core/tests/store/test_local_content_store.py
161
+ fred_core/tests/store/test_vector_search.py
161
162
  fred_core/tests/tasks/__init__.py
162
163
  fred_core/tests/tasks/test_authz.py
163
164
  fred_core/tests/tasks/test_bus.py
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fred-core"
3
- version = "3.4.3"
3
+ version = "3.4.4"
4
4
  description = "Core shared utilities for Fred backends (config, storage, security, and runtime helpers)."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -1,72 +0,0 @@
1
- # Copyright Thales 2025
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
- # fred_core/vector_search.py
16
-
17
- from typing import List, Optional
18
-
19
- from pydantic import BaseModel, Field
20
-
21
-
22
- class VectorSearchHit(BaseModel):
23
- # Content (chunk)
24
- content: str
25
- page: Optional[int] = None
26
- section: Optional[str] = None
27
- viewer_fragment: Optional[str] = None # e.g., "p=12&sel=340-520"
28
- slide_id: Optional[int] = None
29
- has_visual_evidence: Optional[bool] = None
30
- slide_image_uri: Optional[str] = None
31
-
32
- # Identity
33
- uid: str = Field(..., description="Document UID")
34
- title: str
35
- author: Optional[str] = None
36
- created: Optional[str] = None
37
- modified: Optional[str] = None
38
-
39
- # File/source
40
- file_name: Optional[str] = None
41
- file_path: Optional[str] = None
42
- repository: Optional[str] = None
43
- pull_location: Optional[str] = None
44
- language: Optional[str] = None
45
- mime_type: Optional[str] = None
46
- type: Optional[str] = Field(None, description="File type/category")
47
-
48
- # Tags (UI wants *names*; keep ids too for filters)
49
- tag_ids: List[str] = []
50
- tag_names: List[str] = []
51
- tag_full_paths: List[str] = []
52
-
53
- # Link fields (internal viewers / external)
54
- preview_url: Optional[str] = None # e.g., "/documents/{uid}"
55
- preview_at_url: Optional[str] = None # e.g., "/documents/{uid}#{viewer_fragment}"
56
- repo_url: Optional[str] = None # e.g., "https://git/.../blob/ref/path#Lx-Ly"
57
- citation_url: Optional[str] = None # e.g., "/documents/{uid}#chunk={chunk_id}"
58
-
59
- # Access (optional, forward-looking)
60
- license: Optional[str] = None
61
- confidential: Optional[bool] = None
62
-
63
- # Metrics
64
- score: float = Field(..., description="Similarity score from vector search")
65
- rank: Optional[int] = None
66
- embedding_model: Optional[str] = None
67
- vector_index: Optional[str] = None
68
- token_count: Optional[int] = None
69
-
70
- # Provenance
71
- retrieved_at: Optional[str] = None
72
- retrieval_session_id: Optional[str] = None
File without changes
File without changes
File without changes
File without changes