fde-framework 0.1.2__tar.gz → 0.1.3__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 (347) hide show
  1. {fde_framework-0.1.2 → fde_framework-0.1.3}/CHANGELOG.md +16 -0
  2. {fde_framework-0.1.2 → fde_framework-0.1.3}/PKG-INFO +53 -16
  3. {fde_framework-0.1.2 → fde_framework-0.1.3}/README.md +51 -15
  4. {fde_framework-0.1.2 → fde_framework-0.1.3}/examples/invoice-extraction/README.md +1 -1
  5. fde_framework-0.1.3/framework/approaches/hybrid-search.md +24 -0
  6. fde_framework-0.1.3/framework/approaches/reranked-retrieval.md +24 -0
  7. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/corpus_size.md +1 -1
  8. fde_framework-0.1.3/framework/patterns/hybrid-search.md +9 -0
  9. fde_framework-0.1.3/framework/patterns/reranked-retrieval.md +9 -0
  10. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/vector-search.md +1 -0
  11. fde_framework-0.1.3/framework/stacks/llamaindex.md +20 -0
  12. fde_framework-0.1.3/framework/templates/retrieval/hybrid-search.plain.py.j2 +43 -0
  13. fde_framework-0.1.3/framework/templates/retrieval/reranked-retrieval.plain.py.j2 +41 -0
  14. fde_framework-0.1.3/framework/templates/retrieval/vector-search.llamaindex.py.j2 +44 -0
  15. {fde_framework-0.1.2 → fde_framework-0.1.3}/pyproject.toml +2 -1
  16. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/cli.py +20 -5
  17. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/costing.py +1 -0
  18. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_dead_zones.py +30 -0
  19. fde_framework-0.1.3/tests/test_example_transcript.py +62 -0
  20. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_sanitisation.py +1 -0
  21. {fde_framework-0.1.2 → fde_framework-0.1.3}/.gitignore +0 -0
  22. {fde_framework-0.1.2 → fde_framework-0.1.3}/LICENSE +0 -0
  23. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/ansible-playbook.md +0 -0
  24. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/assisted-deterministic.md +0 -0
  25. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/audit-only.md +0 -0
  26. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/boundary-and-audit.md +0 -0
  27. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/cascade.md +0 -0
  28. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/classical-ml.md +0 -0
  29. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/compose.md +0 -0
  30. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/decision-log.md +0 -0
  31. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/deterministic-masking.md +0 -0
  32. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/deterministic.md +0 -0
  33. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/direct-call.md +0 -0
  34. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/episodic-store.md +0 -0
  35. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/explainability-record.md +0 -0
  36. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/field-match.md +0 -0
  37. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/finetune.md +0 -0
  38. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/fixed-sequence.md +0 -0
  39. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/gitops.md +0 -0
  40. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/governed-tools.md +0 -0
  41. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/graph-retrieval.md +0 -0
  42. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/judged.md +0 -0
  43. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/keyword-search.md +0 -0
  44. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/kubernetes-manifests.md +0 -0
  45. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/labelled-metrics.md +0 -0
  46. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/llm-extraction.md +0 -0
  47. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/llm-scrubbing.md +0 -0
  48. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/llm.md +0 -0
  49. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/local-embedding.md +0 -0
  50. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/managed-api.md +0 -0
  51. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/managed-embedding.md +0 -0
  52. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/manual-runbook.md +0 -0
  53. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/model-planner.md +0 -0
  54. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/ocr-pipeline.md +0 -0
  55. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/optimisation-reasoning.md +0 -0
  56. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/optimisation.md +0 -0
  57. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/passthrough.md +0 -0
  58. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/role-scoped-authority.md +0 -0
  59. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/segmentation.md +0 -0
  60. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/self-hosted.md +0 -0
  61. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/serverless-gpu.md +0 -0
  62. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/speech-transcription.md +0 -0
  63. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/structured-logs.md +0 -0
  64. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/systemd-unit.md +0 -0
  65. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/terraform-module.md +0 -0
  66. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/text-extraction.md +0 -0
  67. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/traced.md +0 -0
  68. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/vector-search.md +0 -0
  69. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/video-ingestion.md +0 -0
  70. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/windowed-ingestion.md +0 -0
  71. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/working-state.md +0 -0
  72. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/cases/churn-scoring.md +0 -0
  73. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/cases/route-planning.md +0 -0
  74. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/cases/structured-extraction.md +0 -0
  75. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/cases/studio-style.md +0 -0
  76. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/accountability.md +0 -0
  77. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/deployment.md +0 -0
  78. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/embedding.md +0 -0
  79. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/evaluation.md +0 -0
  80. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/governance.md +0 -0
  81. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/integration.md +0 -0
  82. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/memory.md +0 -0
  83. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/observability.md +0 -0
  84. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/perception.md +0 -0
  85. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/planning.md +0 -0
  86. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/provisioning.md +0 -0
  87. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/reasoning.md +0 -0
  88. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/redaction.md +0 -0
  89. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/representation.md +0 -0
  90. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/retrieval.md +0 -0
  91. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/serving.md +0 -0
  92. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/accelerator.md +0 -0
  93. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/access_model.md +0 -0
  94. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/arrival_rate.md +0 -0
  95. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/availability_target.md +0 -0
  96. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/cheap_path_coverage.md +0 -0
  97. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/confidence_calibrated.md +0 -0
  98. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/container_competence.md +0 -0
  99. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/data_residency.md +0 -0
  100. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/environment_lifetime.md +0 -0
  101. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/existing_cluster.md +0 -0
  102. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/existing_iac_tool.md +0 -0
  103. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/external_systems.md +0 -0
  104. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/hosting.md +0 -0
  105. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/human_waiting.md +0 -0
  106. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/input_format.md +0 -0
  107. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/interpretability_required.md +0 -0
  108. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/labelled_count.md +0 -0
  109. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/latency_budget_ms.md +0 -0
  110. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/licence_posture.md +0 -0
  111. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/operates_after_handover.md +0 -0
  112. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/output_shape.md +0 -0
  113. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/provisioning_api.md +0 -0
  114. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/query_pattern.md +0 -0
  115. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/recall_span.md +0 -0
  116. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/sensitivity_present.md +0 -0
  117. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Generator.md +0 -0
  118. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Guard.md +0 -0
  119. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Mapper.md +0 -0
  120. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/ModelServer.md +0 -0
  121. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Parser.md +0 -0
  122. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Planner.md +0 -0
  123. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Retriever.md +0 -0
  124. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Scorer.md +0 -0
  125. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Store.md +0 -0
  126. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/ToolBoundary.md +0 -0
  127. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Tracer.md +0 -0
  128. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/locales/eu-gdpr.md +0 -0
  129. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/locales/in-dpdp.md +0 -0
  130. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/ansible-playbook.md +0 -0
  131. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/assisted-deterministic.md +0 -0
  132. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/audit-only.md +0 -0
  133. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/boundary-and-audit.md +0 -0
  134. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/cascade-reasoning.md +0 -0
  135. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/cascade-representation.md +0 -0
  136. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/cascade-retrieval.md +0 -0
  137. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/classical-ml-reasoning.md +0 -0
  138. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/classical-ml.md +0 -0
  139. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/compose.md +0 -0
  140. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/decision-log.md +0 -0
  141. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/deterministic-masking.md +0 -0
  142. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/deterministic.md +0 -0
  143. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/direct-call.md +0 -0
  144. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/episodic-store.md +0 -0
  145. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/explainability-record.md +0 -0
  146. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/field-match.md +0 -0
  147. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/finetune-representation.md +0 -0
  148. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/finetune.md +0 -0
  149. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/fixed-sequence.md +0 -0
  150. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/gitops.md +0 -0
  151. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/governed-tools.md +0 -0
  152. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/graph-retrieval.md +0 -0
  153. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/judged.md +0 -0
  154. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/keyword-search.md +0 -0
  155. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/kubernetes-manifests.md +0 -0
  156. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/labelled-metrics.md +0 -0
  157. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/llm-representation.md +0 -0
  158. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/llm-scrubbing.md +0 -0
  159. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/llm.md +0 -0
  160. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/local-embedding.md +0 -0
  161. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/managed-api.md +0 -0
  162. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/managed-embedding.md +0 -0
  163. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/manual-runbook.md +0 -0
  164. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/model-planner.md +0 -0
  165. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/ocr-pipeline.md +0 -0
  166. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/optimisation-reasoning.md +0 -0
  167. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/optimisation.md +0 -0
  168. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/passthrough.md +0 -0
  169. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/role-scoped-authority.md +0 -0
  170. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/segmentation.md +0 -0
  171. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/self-hosted.md +0 -0
  172. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/serverless-gpu.md +0 -0
  173. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/speech-transcription.md +0 -0
  174. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/structured-logs.md +0 -0
  175. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/systemd-unit.md +0 -0
  176. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/terraform-module.md +0 -0
  177. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/text-extraction.md +0 -0
  178. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/traced.md +0 -0
  179. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/video-ingestion.md +0 -0
  180. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/windowed-ingestion.md +0 -0
  181. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/working-state.md +0 -0
  182. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/langgraph.md +0 -0
  183. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/local-judge.md +0 -0
  184. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/mcp.md +0 -0
  185. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/ollama.md +0 -0
  186. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/openai-judge.md +0 -0
  187. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/opentelemetry.md +0 -0
  188. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/ortools.md +0 -0
  189. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/pgvector.md +0 -0
  190. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/plain-python.md +0 -0
  191. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/qdrant.md +0 -0
  192. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/tesseract.md +0 -0
  193. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/vllm.md +0 -0
  194. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/whisper.md +0 -0
  195. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/xgboost.md +0 -0
  196. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/accountability/decision-log.plain.py.j2 +0 -0
  197. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/accountability/explainability-record.plain.py.j2 +0 -0
  198. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/deployment/compose.plain.py.j2 +0 -0
  199. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/deployment/kubernetes-manifests.plain.py.j2 +0 -0
  200. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/deployment/systemd-unit.plain.py.j2 +0 -0
  201. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/embedding/local-embedding.plain.py.j2 +0 -0
  202. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/embedding/managed-embedding.plain.py.j2 +0 -0
  203. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/field-match.plain.py.j2 +0 -0
  204. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/judged.local-judge.py.j2 +0 -0
  205. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/judged.openai-judge.py.j2 +0 -0
  206. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/judged.plain.py.j2 +0 -0
  207. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/labelled-metrics.plain.py.j2 +0 -0
  208. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/labelled-metrics.xgboost.py.j2 +0 -0
  209. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/governance/audit-only.plain.py.j2 +0 -0
  210. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/governance/boundary-and-audit.plain.py.j2 +0 -0
  211. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/governance/role-scoped-authority.plain.py.j2 +0 -0
  212. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/integration/direct-call.plain.py.j2 +0 -0
  213. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/integration/governed-tools.mcp.py.j2 +0 -0
  214. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/integration/governed-tools.plain.py.j2 +0 -0
  215. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/memory/episodic-store.pgvector.py.j2 +0 -0
  216. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/memory/episodic-store.plain.py.j2 +0 -0
  217. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/memory/working-state.plain.py.j2 +0 -0
  218. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/observability/structured-logs.plain.py.j2 +0 -0
  219. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/observability/traced.opentelemetry.py.j2 +0 -0
  220. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/observability/traced.plain.py.j2 +0 -0
  221. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/ocr-pipeline.plain.py.j2 +0 -0
  222. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/ocr-pipeline.tesseract.py.j2 +0 -0
  223. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/passthrough.plain.py.j2 +0 -0
  224. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/speech-transcription.plain.py.j2 +0 -0
  225. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/speech-transcription.whisper.py.j2 +0 -0
  226. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/text-extraction.plain.py.j2 +0 -0
  227. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/video-ingestion.plain.py.j2 +0 -0
  228. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/windowed-ingestion.plain.py.j2 +0 -0
  229. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/planning/fixed-sequence.plain.py.j2 +0 -0
  230. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/planning/model-planner.langgraph.py.j2 +0 -0
  231. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/planning/model-planner.plain.py.j2 +0 -0
  232. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/planning/optimisation.ortools.py.j2 +0 -0
  233. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/planning/optimisation.plain.py.j2 +0 -0
  234. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/provisioning/ansible-playbook.plain.py.j2 +0 -0
  235. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/provisioning/gitops.plain.py.j2 +0 -0
  236. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/provisioning/manual-runbook.plain.py.j2 +0 -0
  237. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/provisioning/terraform-module.plain.py.j2 +0 -0
  238. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/cascade.plain.py.j2 +0 -0
  239. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/classical-ml.plain.py.j2 +0 -0
  240. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/classical-ml.xgboost.py.j2 +0 -0
  241. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/finetune.plain.py.j2 +0 -0
  242. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/llm.plain.py.j2 +0 -0
  243. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/optimisation.ortools.py.j2 +0 -0
  244. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/optimisation.plain.py.j2 +0 -0
  245. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/redaction/deterministic-masking.plain.py.j2 +0 -0
  246. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/redaction/llm-scrubbing.plain.py.j2 +0 -0
  247. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/assisted.plain.py.j2 +0 -0
  248. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/cascade.plain.py.j2 +0 -0
  249. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/classical-ml.plain.py.j2 +0 -0
  250. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/classical-ml.xgboost.py.j2 +0 -0
  251. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/deterministic.plain.py.j2 +0 -0
  252. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/finetune.plain.py.j2 +0 -0
  253. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/llm.plain.py.j2 +0 -0
  254. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/segmentation.plain.py.j2 +0 -0
  255. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/cascade.plain.py.j2 +0 -0
  256. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/graph-retrieval.pgvector.py.j2 +0 -0
  257. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/graph-retrieval.plain.py.j2 +0 -0
  258. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/graph-retrieval.qdrant.py.j2 +0 -0
  259. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/keyword-search.plain.py.j2 +0 -0
  260. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/vector-search.pgvector.py.j2 +0 -0
  261. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/vector-search.plain.py.j2 +0 -0
  262. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/vector-search.qdrant.py.j2 +0 -0
  263. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/serving/managed-api.plain.py.j2 +0 -0
  264. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/serving/self-hosted.ollama.py.j2 +0 -0
  265. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/serving/self-hosted.plain.py.j2 +0 -0
  266. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/serving/self-hosted.vllm.py.j2 +0 -0
  267. {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/serving/serverless-gpu.plain.py.j2 +0 -0
  268. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/__init__.py +0 -0
  269. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/architect.py +0 -0
  270. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/decide.py +0 -0
  271. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/decompose.py +0 -0
  272. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/deploy.py +0 -0
  273. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/emit.py +0 -0
  274. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/evolution.py +0 -0
  275. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/factlog.py +0 -0
  276. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/gates.py +0 -0
  277. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/graph.py +0 -0
  278. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/implement.py +0 -0
  279. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/__init__.py +0 -0
  280. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/answers.py +0 -0
  281. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/documents.py +0 -0
  282. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/interview.py +0 -0
  283. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/llm_reader.py +0 -0
  284. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/prose.py +0 -0
  285. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/samples.py +0 -0
  286. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/__init__.py +0 -0
  287. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/base.py +0 -0
  288. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/fact.py +0 -0
  289. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/profile.py +0 -0
  290. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/respondent.py +0 -0
  291. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/schema.py +0 -0
  292. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/moves.py +0 -0
  293. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/ops.py +0 -0
  294. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/predicate.py +0 -0
  295. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/realization.py +0 -0
  296. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/registry.py +0 -0
  297. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/scan.py +0 -0
  298. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/space.py +0 -0
  299. {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/workflow.py +0 -0
  300. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/__init__.py +0 -0
  301. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/conftest.py +0 -0
  302. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/models/__init__.py +0 -0
  303. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/models/test_profile.py +0 -0
  304. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_access_and_sensitivity.py +0 -0
  305. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_answers.py +0 -0
  306. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cascade.py +0 -0
  307. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_engagement.py +0 -0
  308. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_evolution.py +0 -0
  309. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_gates.py +0 -0
  310. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_intake.py +0 -0
  311. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_kb.py +0 -0
  312. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_robustness.py +0 -0
  313. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_samples.py +0 -0
  314. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_costing.py +0 -0
  315. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_coverage.py +0 -0
  316. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_decide.py +0 -0
  317. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_decompose.py +0 -0
  318. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_deploy.py +0 -0
  319. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_documents.py +0 -0
  320. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_embedding.py +0 -0
  321. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_emit.py +0 -0
  322. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_evolution.py +0 -0
  323. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_factlog.py +0 -0
  324. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_gates.py +0 -0
  325. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_implement.py +0 -0
  326. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_interview.py +0 -0
  327. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_llm_reader.py +0 -0
  328. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_locales.py +0 -0
  329. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_memory_retrieval.py +0 -0
  330. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_more_templates.py +0 -0
  331. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_moves.py +0 -0
  332. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_multimodal.py +0 -0
  333. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_ops.py +0 -0
  334. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_package.py +0 -0
  335. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_prose.py +0 -0
  336. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_realization.py +0 -0
  337. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_registry.py +0 -0
  338. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_remaining.py +0 -0
  339. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_reversibility.py +0 -0
  340. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_samples.py +0 -0
  341. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_scan.py +0 -0
  342. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_schema.py +0 -0
  343. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_serving.py +0 -0
  344. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_shipped_registry.py +0 -0
  345. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_space.py +0 -0
  346. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_stack_swap.py +0 -0
  347. {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_templates.py +0 -0
@@ -5,6 +5,22 @@ the project is pre-release, so everything sits under 0.1.0 until the first tag.
5
5
 
6
6
  ## [Unreleased]
7
7
 
8
+ ## [0.1.3] — 2026-09-08
9
+
10
+ A post-launch funnel audit replayed every public transcript against the
11
+ shipped package; everything it caught, in one release:
12
+
13
+ - `fde start` with a statement now also offers the three questions worth
14
+ asking next (the follow-ups hint names the actual engagement path).
15
+ - Seat economics dates its figures; `fde scan` on unified-memory machines
16
+ no longer prints a contradictory "0GB total".
17
+ - Retrieval corpus: hybrid-search and reranked-retrieval (adopted by
18
+ measurement, like finetune); LlamaIndex stack with a realization;
19
+ invoices/complaints/claims join the corpus-size vocabulary.
20
+ - The worked example's transcript is now a replay test: an output change
21
+ that would strand it fails in the same commit.
22
+ - CI tests 3.11/3.12/3.13; doc links absolute so PyPI renders them.
23
+
8
24
  ## [0.1.2] — 2026-09-07
9
25
 
10
26
  A fresh-eyes usability round, every finding verified in a clean venv:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fde-framework
3
- Version: 0.1.2
3
+ Version: 0.1.3
4
4
  Summary: A framework for Forward Deployed Engineers: from problem statement to a deployable AI project, every decision traced to a fact.
5
5
  Project-URL: Homepage, https://github.com/atulkapoor/fde-framework
6
6
  Project-URL: Repository, https://github.com/atulkapoor/fde-framework
@@ -14,6 +14,7 @@ Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: Apache Software License
15
15
  Classifier: Programming Language :: Python :: 3.11
16
16
  Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
17
18
  Classifier: Topic :: Software Development :: Code Generators
18
19
  Requires-Python: >=3.11
19
20
  Requires-Dist: jinja2>=3.1
@@ -47,15 +48,16 @@ Description-Content-Type: text/markdown
47
48
  ![Python](https://img.shields.io/pypi/pyversions/fde-framework?logo=python&logoColor=white)
48
49
  [![Downloads](https://img.shields.io/pypi/dm/fde-framework?color=blueviolet)](https://pypistats.org/packages/fde-framework)
49
50
  ![Status](https://img.shields.io/badge/status-alpha-orange)
50
- [![License](https://img.shields.io/badge/license-Apache--2.0-green)](LICENSE)
51
+ [![License](https://img.shields.io/badge/license-Apache--2.0-green)](https://github.com/atulkapoor/fde-framework/blob/main/LICENSE)
51
52
 
52
53
  <p>
53
54
  <a href="#install">Install</a> ·
54
- <a href="#try-it-in-two-minutes">Quickstart</a> ·
55
+ <a href="#try-it">Quickstart</a> ·
55
56
  <a href="ARCHITECTURE.md">Architecture</a> ·
56
57
  <a href="examples/invoice-extraction/">Worked example</a> ·
57
58
  <a href="CONTRIBUTING.md">Contributing</a> ·
58
- <a href="https://pypi.org/project/fde-framework/">PyPI</a>
59
+ <a href="https://pypi.org/project/fde-framework/">PyPI</a> ·
60
+ <a href="https://atulkapoor.github.io/fde-framework/">Website</a>
59
61
  </p>
60
62
 
61
63
  **fde** is an open-source framework for Forward Deployed Engineers: it takes a
@@ -94,6 +96,16 @@ access — no build. The remedies ship with every gate.*
94
96
  | **Jurisdiction as data** | Locale packs preset answers at the weakest provenance and attach dated compliance obligations to the build; they can never change how decisions are made |
95
97
  | **Self-evolution, honestly** | Overrides, trigger calibration and anonymised cases are captured per engagement; the corpus grows only through human-reviewed ingestion |
96
98
 
99
+
100
+ ## How it fits together
101
+
102
+ <img src="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/how-it-fits.png" alt="statement to typed facts to answer space to seven gates, then decide, architect, emit, implement — registry as data, deterministic builds" width="800">
103
+
104
+ Discovery narrows an answer space; gates decide whether building is honest
105
+ yet; the decision engine picks the simplest applicable approach per component
106
+ and cites why; emit writes a project whose exam fails until it is truly
107
+ implemented. The full design is in [ARCHITECTURE.md](https://github.com/atulkapoor/fde-framework/blob/main/ARCHITECTURE.md).
108
+
97
109
  ---
98
110
 
99
111
  ## Who this is for
@@ -261,7 +273,7 @@ check. `--lenient` exists for the hour when you are mid-way through authoring
261
273
  content and the links do not resolve yet.
262
274
 
263
275
  A complete worked engagement — real transcript, synthetic client — lives in
264
- [examples/invoice-extraction](examples/invoice-extraction/).
276
+ [examples/invoice-extraction](https://github.com/atulkapoor/fde-framework/tree/main/examples/invoice-extraction).
265
277
 
266
278
  ## What a build emits
267
279
 
@@ -270,10 +282,10 @@ project/
270
282
  ├── app/ # components, pipeline, controls, boundary check
271
283
  │ ├── components/ # implementations or honest scaffolds — never silent gaps
272
284
  │ ├── pipeline.py # topological order; approval gates before anything mutative
273
- │ ├── controls.py # fail-closed: an unwired gate refuses, loudly
285
+ │ ├── controls.py # fail-closed gates & critics — when anything mutative was decided
274
286
  │ ├── boundary.py # imported at startup when data may not leave
275
287
  │ ├── contract.py # RefusedInput: forbidden input is refused, never guessed at
276
- │ └── llm.py # the one model touchpoint, when a decision needs one (boundary-gated)
288
+ │ └── llm.py # the one model touchpoint — when a decision needs a model (boundary-gated)
277
289
  ├── evals/ # golden / edge / adversarial sets from the client's own pairs
278
290
  │ ├── harness.py # fails CI until implemented; judge-based when the evaluation decided judged
279
291
  │ ├── acceptance.md # blind UAT protocol for the client's own judges
@@ -498,7 +510,7 @@ The registry is the shared asset; engagements are private working state.
498
510
  deliberately waits for a corpus of measured retrospectives rather than
499
511
  pretending a handful is evidence.
500
512
  - **More locale packs and stacks** — both are data; contributions enter
501
- against [CONTRIBUTING.md](CONTRIBUTING.md)'s contract (and the [code of conduct](CODE_OF_CONDUCT.md)).
513
+ against [CONTRIBUTING.md](https://github.com/atulkapoor/fde-framework/blob/main/CONTRIBUTING.md)'s contract (and the [code of conduct](https://github.com/atulkapoor/fde-framework/blob/main/CODE_OF_CONDUCT.md)).
502
514
  - **Language, channel, and device axes** — six of twenty industry test
503
515
  statements named regional languages, low bandwidth, or basic devices;
504
516
  the honest wiring (per-language evaluation, SMS/IVR serving approaches,
@@ -513,22 +525,47 @@ The registry is the shared asset; engagements are private working state.
513
525
  `fde kb sweep` report what the corpus is missing and which profile shapes
514
526
  no approach can serve yet.
515
527
 
516
- ## Learn more
528
+ ## Documentation
529
+
530
+ | I want to… | Read |
531
+ |---|---|
532
+ | Run the whole lifecycle once | [The full lifecycle, copy-paste](#the-full-lifecycle-copy-paste) |
533
+ | See a real transcript with expected output | [Worked example](https://github.com/atulkapoor/fde-framework/tree/main/examples/invoice-extraction) |
534
+ | Understand the moving parts | [ARCHITECTURE.md](https://github.com/atulkapoor/fde-framework/blob/main/ARCHITECTURE.md) |
535
+ | Understand a gate that just refused me | `fde status <eng>` — every gate names its remedy and its clearing command |
536
+ | Add a dimension / approach / template | [CONTRIBUTING.md](https://github.com/atulkapoor/fde-framework/blob/main/CONTRIBUTING.md) — incl. the template context table |
537
+ | Use it as a library | [Python API](#python-api) |
538
+ | Report a vulnerability | [SECURITY.md](https://github.com/atulkapoor/fde-framework/blob/main/SECURITY.md) |
539
+ | See what changed | [CHANGELOG.md](https://github.com/atulkapoor/fde-framework/blob/main/CHANGELOG.md) · [Releases](https://github.com/atulkapoor/fde-framework/releases) |
540
+
541
+
542
+ ## Development
543
+
544
+ ```bash
545
+ git clone https://github.com/atulkapoor/fde-framework.git && cd fde-framework
546
+ python3 -m venv .venv && .venv/bin/pip install -e ".[dev,documents]"
547
+ .venv/bin/pytest -q # ~850 tests, < 30s
548
+ .venv/bin/ruff check src tests
549
+ ```
550
+
551
+ The registry is data: most contributions are a markdown file in `framework/`
552
+ plus a test that pins the behaviour. CI additionally runs a sanitisation sweep
553
+ over the tree and history.
554
+
555
+ ## Community
517
556
 
518
- - [ARCHITECTURE.md](ARCHITECTURE.md) — module map, the data/mechanism boundary, how to extend the registry
519
- - [CHANGELOG.md](CHANGELOG.md) — what exists and how it was hardened
520
- - [examples/invoice-extraction](examples/invoice-extraction/) — a full engagement transcript
521
- - [SECURITY.md](SECURITY.md) — reporting, and what counts as security-grade here
522
- - [llms.txt](llms.txt) — the project summarised for AI assistants
557
+ Questions and engagement war stories → [Discussions](https://github.com/atulkapoor/fde-framework/discussions).
558
+ Bugs and corpus gaps → [issues](https://github.com/atulkapoor/fde-framework/issues/new/choose) (the forms ask for evidence, the way the framework does).
559
+ Conduct → [CODE_OF_CONDUCT.md](https://github.com/atulkapoor/fde-framework/blob/main/CODE_OF_CONDUCT.md).
523
560
 
524
561
  ## License
525
562
 
526
- [Apache 2.0](LICENSE) — chosen for the explicit patent grant, because
563
+ [Apache 2.0](https://github.com/atulkapoor/fde-framework/blob/main/LICENSE) — chosen for the explicit patent grant, because
527
564
  enterprise legal review is a real gate for the audience this is for.
528
565
 
529
566
  ## Contributing
530
567
 
531
- See [CONTRIBUTING.md](CONTRIBUTING.md). The short version: contributions enter
568
+ See [CONTRIBUTING.md](https://github.com/atulkapoor/fde-framework/blob/main/CONTRIBUTING.md). The short version: contributions enter
532
569
  against a contract, and **client material never enters this repository** — only
533
570
  patterns re-expressed in the framework's own words. Sanitisation is enforced in
534
571
  CI: allowed paths only, history checked, credential and personal-data patterns,
@@ -10,15 +10,16 @@
10
10
  ![Python](https://img.shields.io/pypi/pyversions/fde-framework?logo=python&logoColor=white)
11
11
  [![Downloads](https://img.shields.io/pypi/dm/fde-framework?color=blueviolet)](https://pypistats.org/packages/fde-framework)
12
12
  ![Status](https://img.shields.io/badge/status-alpha-orange)
13
- [![License](https://img.shields.io/badge/license-Apache--2.0-green)](LICENSE)
13
+ [![License](https://img.shields.io/badge/license-Apache--2.0-green)](https://github.com/atulkapoor/fde-framework/blob/main/LICENSE)
14
14
 
15
15
  <p>
16
16
  <a href="#install">Install</a> ·
17
- <a href="#try-it-in-two-minutes">Quickstart</a> ·
17
+ <a href="#try-it">Quickstart</a> ·
18
18
  <a href="ARCHITECTURE.md">Architecture</a> ·
19
19
  <a href="examples/invoice-extraction/">Worked example</a> ·
20
20
  <a href="CONTRIBUTING.md">Contributing</a> ·
21
- <a href="https://pypi.org/project/fde-framework/">PyPI</a>
21
+ <a href="https://pypi.org/project/fde-framework/">PyPI</a> ·
22
+ <a href="https://atulkapoor.github.io/fde-framework/">Website</a>
22
23
  </p>
23
24
 
24
25
  **fde** is an open-source framework for Forward Deployed Engineers: it takes a
@@ -57,6 +58,16 @@ access — no build. The remedies ship with every gate.*
57
58
  | **Jurisdiction as data** | Locale packs preset answers at the weakest provenance and attach dated compliance obligations to the build; they can never change how decisions are made |
58
59
  | **Self-evolution, honestly** | Overrides, trigger calibration and anonymised cases are captured per engagement; the corpus grows only through human-reviewed ingestion |
59
60
 
61
+
62
+ ## How it fits together
63
+
64
+ <img src="https://raw.githubusercontent.com/atulkapoor/fde-framework/main/assets/how-it-fits.png" alt="statement to typed facts to answer space to seven gates, then decide, architect, emit, implement — registry as data, deterministic builds" width="800">
65
+
66
+ Discovery narrows an answer space; gates decide whether building is honest
67
+ yet; the decision engine picks the simplest applicable approach per component
68
+ and cites why; emit writes a project whose exam fails until it is truly
69
+ implemented. The full design is in [ARCHITECTURE.md](https://github.com/atulkapoor/fde-framework/blob/main/ARCHITECTURE.md).
70
+
60
71
  ---
61
72
 
62
73
  ## Who this is for
@@ -224,7 +235,7 @@ check. `--lenient` exists for the hour when you are mid-way through authoring
224
235
  content and the links do not resolve yet.
225
236
 
226
237
  A complete worked engagement — real transcript, synthetic client — lives in
227
- [examples/invoice-extraction](examples/invoice-extraction/).
238
+ [examples/invoice-extraction](https://github.com/atulkapoor/fde-framework/tree/main/examples/invoice-extraction).
228
239
 
229
240
  ## What a build emits
230
241
 
@@ -233,10 +244,10 @@ project/
233
244
  ├── app/ # components, pipeline, controls, boundary check
234
245
  │ ├── components/ # implementations or honest scaffolds — never silent gaps
235
246
  │ ├── pipeline.py # topological order; approval gates before anything mutative
236
- │ ├── controls.py # fail-closed: an unwired gate refuses, loudly
247
+ │ ├── controls.py # fail-closed gates & critics — when anything mutative was decided
237
248
  │ ├── boundary.py # imported at startup when data may not leave
238
249
  │ ├── contract.py # RefusedInput: forbidden input is refused, never guessed at
239
- │ └── llm.py # the one model touchpoint, when a decision needs one (boundary-gated)
250
+ │ └── llm.py # the one model touchpoint — when a decision needs a model (boundary-gated)
240
251
  ├── evals/ # golden / edge / adversarial sets from the client's own pairs
241
252
  │ ├── harness.py # fails CI until implemented; judge-based when the evaluation decided judged
242
253
  │ ├── acceptance.md # blind UAT protocol for the client's own judges
@@ -461,7 +472,7 @@ The registry is the shared asset; engagements are private working state.
461
472
  deliberately waits for a corpus of measured retrospectives rather than
462
473
  pretending a handful is evidence.
463
474
  - **More locale packs and stacks** — both are data; contributions enter
464
- against [CONTRIBUTING.md](CONTRIBUTING.md)'s contract (and the [code of conduct](CODE_OF_CONDUCT.md)).
475
+ against [CONTRIBUTING.md](https://github.com/atulkapoor/fde-framework/blob/main/CONTRIBUTING.md)'s contract (and the [code of conduct](https://github.com/atulkapoor/fde-framework/blob/main/CODE_OF_CONDUCT.md)).
465
476
  - **Language, channel, and device axes** — six of twenty industry test
466
477
  statements named regional languages, low bandwidth, or basic devices;
467
478
  the honest wiring (per-language evaluation, SMS/IVR serving approaches,
@@ -476,22 +487,47 @@ The registry is the shared asset; engagements are private working state.
476
487
  `fde kb sweep` report what the corpus is missing and which profile shapes
477
488
  no approach can serve yet.
478
489
 
479
- ## Learn more
490
+ ## Documentation
491
+
492
+ | I want to… | Read |
493
+ |---|---|
494
+ | Run the whole lifecycle once | [The full lifecycle, copy-paste](#the-full-lifecycle-copy-paste) |
495
+ | See a real transcript with expected output | [Worked example](https://github.com/atulkapoor/fde-framework/tree/main/examples/invoice-extraction) |
496
+ | Understand the moving parts | [ARCHITECTURE.md](https://github.com/atulkapoor/fde-framework/blob/main/ARCHITECTURE.md) |
497
+ | Understand a gate that just refused me | `fde status <eng>` — every gate names its remedy and its clearing command |
498
+ | Add a dimension / approach / template | [CONTRIBUTING.md](https://github.com/atulkapoor/fde-framework/blob/main/CONTRIBUTING.md) — incl. the template context table |
499
+ | Use it as a library | [Python API](#python-api) |
500
+ | Report a vulnerability | [SECURITY.md](https://github.com/atulkapoor/fde-framework/blob/main/SECURITY.md) |
501
+ | See what changed | [CHANGELOG.md](https://github.com/atulkapoor/fde-framework/blob/main/CHANGELOG.md) · [Releases](https://github.com/atulkapoor/fde-framework/releases) |
502
+
503
+
504
+ ## Development
505
+
506
+ ```bash
507
+ git clone https://github.com/atulkapoor/fde-framework.git && cd fde-framework
508
+ python3 -m venv .venv && .venv/bin/pip install -e ".[dev,documents]"
509
+ .venv/bin/pytest -q # ~850 tests, < 30s
510
+ .venv/bin/ruff check src tests
511
+ ```
512
+
513
+ The registry is data: most contributions are a markdown file in `framework/`
514
+ plus a test that pins the behaviour. CI additionally runs a sanitisation sweep
515
+ over the tree and history.
516
+
517
+ ## Community
480
518
 
481
- - [ARCHITECTURE.md](ARCHITECTURE.md) — module map, the data/mechanism boundary, how to extend the registry
482
- - [CHANGELOG.md](CHANGELOG.md) — what exists and how it was hardened
483
- - [examples/invoice-extraction](examples/invoice-extraction/) — a full engagement transcript
484
- - [SECURITY.md](SECURITY.md) — reporting, and what counts as security-grade here
485
- - [llms.txt](llms.txt) — the project summarised for AI assistants
519
+ Questions and engagement war stories → [Discussions](https://github.com/atulkapoor/fde-framework/discussions).
520
+ Bugs and corpus gaps → [issues](https://github.com/atulkapoor/fde-framework/issues/new/choose) (the forms ask for evidence, the way the framework does).
521
+ Conduct → [CODE_OF_CONDUCT.md](https://github.com/atulkapoor/fde-framework/blob/main/CODE_OF_CONDUCT.md).
486
522
 
487
523
  ## License
488
524
 
489
- [Apache 2.0](LICENSE) — chosen for the explicit patent grant, because
525
+ [Apache 2.0](https://github.com/atulkapoor/fde-framework/blob/main/LICENSE) — chosen for the explicit patent grant, because
490
526
  enterprise legal review is a real gate for the audience this is for.
491
527
 
492
528
  ## Contributing
493
529
 
494
- See [CONTRIBUTING.md](CONTRIBUTING.md). The short version: contributions enter
530
+ See [CONTRIBUTING.md](https://github.com/atulkapoor/fde-framework/blob/main/CONTRIBUTING.md). The short version: contributions enter
495
531
  against a contract, and **client material never enters this repository** — only
496
532
  patterns re-expressed in the framework's own words. Sanitisation is enforced in
497
533
  CI: allowed paths only, history checked, credential and personal-data patterns,
@@ -25,7 +25,7 @@ fde frame engagements/acme --file examples/invoice-extraction/brief.md
25
25
  #
26
26
  # Correct anything wrong before we go further.
27
27
  #
28
- # worth asking next (fde ask <eng> --role <who>):
28
+ # worth asking next (fde ask engagements/acme --role <who>):
29
29
  # - What accelerator does the machine this runs on have? [admin]
30
30
  # - Who may use this -- one operating team, distinct roles with different
31
31
  # permissions, or anyone internal? [admin/sponsor]
@@ -0,0 +1,24 @@
1
+ ---
2
+ id: hybrid-search
3
+ name: Hybrid search
4
+ complexity: 2
5
+ components: [retrieval]
6
+ applies_when: [query_pattern == comparative and corpus_size > 100000]
7
+ avoid_when: [query_pattern == multi_hop, corpus_size < 10000]
8
+ evidence: {case_ids: [structured-extraction], confidence: medium, last_verified: 2026-09-07}
9
+ ---
10
+ Keyword and semantic retrieval fused, because each fails where the other
11
+ holds: exact identifiers, part numbers and names that embeddings blur past
12
+ are what BM25 was built for, and paraphrase is what it cannot see. Fusion
13
+ (reciprocal rank, not score mixing -- scores from different retrievers do
14
+ not share a scale) takes both.
15
+
16
+ Earns its second index where the corpus is large enough that either
17
+ retriever alone leaves recall on the table, and comparative queries mix
18
+ named things with described things. Below ten thousand documents, one good
19
+ retriever plus a rerank is less to operate than two indexes that must stay
20
+ in sync -- the sync is the real cost, and it fails silently.
21
+
22
+ Reached by override wherever the sample queries show identifier-plus-prose
23
+ mixtures the chosen retriever measurably misses; the golden set is the
24
+ evidence, never the hunch.
@@ -0,0 +1,24 @@
1
+ ---
2
+ id: reranked-retrieval
3
+ name: Reranked retrieval
4
+ complexity: 2
5
+ components: [retrieval]
6
+ applies_when: [query_pattern == lookup and corpus_size > 200000 and human_waiting == "no"]
7
+ avoid_when: [latency_budget_ms < 500, query_pattern == multi_hop]
8
+ evidence: {case_ids: [structured-extraction], confidence: medium, last_verified: 2026-09-07}
9
+ ---
10
+ A cheap retriever casts wide, a cross-encoder reads the top candidates
11
+ properly, and precision comes from the second pass. The first-stage
12
+ retriever's job quietly changes from "rank correctly" to "do not miss" --
13
+ recall at fifty, not precision at five -- which is a much easier contract to
14
+ keep on a big corpus.
15
+
16
+ The cost is a model call per query on the reranking pass, which is why the
17
+ latency budget avoids it where somebody is waiting on a tight budget: a
18
+ cross-encoder over fifty candidates is real milliseconds, and no fusion
19
+ trick refunds them.
20
+
21
+ Like the finetune rule, this is mostly reached by measurement rather than by
22
+ default: run the golden queries, look at where the right answer ranked, and
23
+ adopt the reranker when the gap between position one and position twenty is
24
+ where your answers actually live.
@@ -6,7 +6,7 @@ kind: requirement
6
6
  weight: 1.5
7
7
  asks: "How many items in total?"
8
8
  recognises_near:
9
- [documents, docs, document, files, records, pages, rows, accounts, items, tickets, reports, corpus]
9
+ [documents, docs, document, files, records, pages, rows, accounts, items, tickets, reports, corpus, invoices, complaints, claims]
10
10
  ask_role: ['admin', 'eval_owner']
11
11
  ---
12
12
  Total volume. Distinct from how many are labelled, which is usually far smaller
@@ -0,0 +1,9 @@
1
+ ---
2
+ id: hybrid-search
3
+ component: retrieval
4
+ approach: hybrid-search
5
+ realizations:
6
+ - {stack: plain-python, template: retrieval/hybrid-search.plain.py.j2, provides: Retriever}
7
+ evidence: {case_ids: [structured-extraction], confidence: medium, last_verified: 2026-09-07}
8
+ ---
9
+ Implements hybrid-search for retrieval, satisfying Retriever.
@@ -0,0 +1,9 @@
1
+ ---
2
+ id: reranked-retrieval
3
+ component: retrieval
4
+ approach: reranked-retrieval
5
+ realizations:
6
+ - {stack: plain-python, template: retrieval/reranked-retrieval.plain.py.j2, provides: Retriever}
7
+ evidence: {case_ids: [structured-extraction], confidence: medium, last_verified: 2026-09-07}
8
+ ---
9
+ Implements reranked-retrieval for retrieval, satisfying Retriever.
@@ -6,6 +6,7 @@ realizations:
6
6
  - {stack: plain-python, template: retrieval/vector-search.plain.py.j2, provides: Retriever}
7
7
  - {stack: pgvector, template: retrieval/vector-search.pgvector.py.j2, provides: Retriever}
8
8
  - {stack: qdrant, template: retrieval/vector-search.qdrant.py.j2, provides: Retriever}
9
+ - {stack: llamaindex, template: retrieval/vector-search.llamaindex.py.j2, provides: Retriever}
9
10
  evidence: {case_ids: [structured-extraction], confidence: medium, last_verified: 2026-08-21}
10
11
  ---
11
12
  Implements vector-search for retrieval, satisfying Retriever.
@@ -0,0 +1,20 @@
1
+ ---
2
+ id: llamaindex
3
+ name: LlamaIndex
4
+ licence: MIT
5
+ topologies: [public-saas, managed-api, customer-vpc, hybrid, on-prem]
6
+ last_verified: 2026-09-07
7
+ provides: {Retriever: stable}
8
+ reversibility: moderate
9
+ ---
10
+ The data layer of the LLM framework stack: ingestion, indexing, query
11
+ engines, RAG plumbing. Earns its place where the client already runs it or
12
+ where the retrieval surface is complex enough (many sources, many readers)
13
+ that its connectors beat hand-rolled ingestion -- the published clinical-QA
14
+ deployments are built exactly this way.
15
+
16
+ Moderate reversibility on purpose: its index formats and query-engine
17
+ abstractions weave into the retrieval path, and unwinding them is a
18
+ re-embedding project, not a refactor. Where nothing justifies carrying the
19
+ framework, the plain-python and pgvector realizations answer the same
20
+ contract with less to own.
@@ -0,0 +1,43 @@
1
+ """{{ component }}: {{ approach }}, via {{ stack }}.
2
+
3
+ {{ rationale }}
4
+
5
+ Keyword and semantic results fused by reciprocal rank -- never by score,
6
+ because BM25 scores and cosine similarities do not share a scale, and
7
+ mixing them rewards whichever index is having a loud day.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from collections.abc import Callable
13
+
14
+ from app.contract import RefusedInput
15
+
16
+ interface = "{{ interface }}"
17
+ approach = "{{ approach }}"
18
+ stack = "{{ stack }}"
19
+
20
+ TOP_K = 8
21
+ CANDIDATES_PER_SIDE = 25
22
+ RRF_K = 60 # the standard damping constant; rank 1 vs 2 matters, 40 vs 41 does not
23
+
24
+ # Injected at wiring time: each callable(query, k) -> ordered list of
25
+ # {"id": ..., ...} results. Two indexes, one contract.
26
+ keyword_search: Callable | None = None
27
+ semantic_search: Callable | None = None
28
+
29
+
30
+ class {{ class_name }}:
31
+ def run(self, query: str, top_k: int = TOP_K) -> list[dict]:
32
+ if keyword_search is None or semantic_search is None:
33
+ raise RefusedInput(
34
+ "both retrievers must be wired -- hybrid over one index is "
35
+ "the single index wearing a costume"
36
+ )
37
+ fused: dict[str, dict] = {}
38
+ for results in (keyword_search(query, CANDIDATES_PER_SIDE),
39
+ semantic_search(query, CANDIDATES_PER_SIDE)):
40
+ for rank, item in enumerate(results):
41
+ entry = fused.setdefault(item["id"], {**item, "rrf": 0.0})
42
+ entry["rrf"] += 1.0 / (RRF_K + rank + 1)
43
+ return sorted(fused.values(), key=lambda e: -e["rrf"])[:top_k]
@@ -0,0 +1,41 @@
1
+ """{{ component }}: {{ approach }}, via {{ stack }}.
2
+
3
+ {{ rationale }}
4
+
5
+ Wide first pass, careful second pass. The first-stage contract is recall --
6
+ do not miss -- and the cross-encoder buys precision on the shortlist.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from collections.abc import Callable
12
+
13
+ from app.contract import RefusedInput
14
+
15
+ interface = "{{ interface }}"
16
+ approach = "{{ approach }}"
17
+ stack = "{{ stack }}"
18
+
19
+ TOP_K = 8
20
+ CANDIDATES = 50 # recall-at-fifty is the first stage's whole job
21
+
22
+ # Injected at wiring time: retrieve(query, k) -> candidates;
23
+ # rerank(query, text) -> relevance score, higher is better.
24
+ retrieve: Callable | None = None
25
+ rerank: Callable | None = None
26
+
27
+
28
+ class {{ class_name }}:
29
+ def run(self, query: str, top_k: int = TOP_K) -> list[dict]:
30
+ if retrieve is None or rerank is None:
31
+ raise RefusedInput(
32
+ "wire retrieve(query, k) and rerank(query, text) -- an "
33
+ "unreranked shortlist returned from here would wear a "
34
+ "precision it does not have"
35
+ )
36
+ candidates = retrieve(query, CANDIDATES)
37
+ scored = [
38
+ {**c, "rerank_score": rerank(query, c.get("text", ""))}
39
+ for c in candidates
40
+ ]
41
+ return sorted(scored, key=lambda c: -c["rerank_score"])[:top_k]
@@ -0,0 +1,44 @@
1
+ """{{ component }}: {{ approach }}, via {{ stack }}.
2
+
3
+ {{ rationale }}
4
+
5
+ LlamaIndex as the retrieval surface -- the right realization where the
6
+ client already operates it, or where many sources feed one index and its
7
+ connectors beat hand-rolled ingestion.
8
+
9
+ **The same contract as the in-process implementation.** Swapping between
10
+ realizations changes this file and nothing about the architecture -- which
11
+ is the seam the registry exists to preserve, and the reason the index is
12
+ injected rather than constructed here.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Any
18
+
19
+ from app.contract import RefusedInput
20
+
21
+ interface = "{{ interface }}"
22
+ approach = "{{ approach }}"
23
+ stack = "{{ stack }}"
24
+
25
+ TOP_K = 8
26
+
27
+ # Injected at wiring time: a llama_index VectorStoreIndex (or anything with
28
+ # .as_retriever). Kept injectable so tests run without an index and the
29
+ # boundary decides where the store lives.
30
+ index: Any = None
31
+
32
+
33
+ class {{ class_name }}:
34
+ def run(self, query: str, top_k: int = TOP_K) -> list[dict]:
35
+ if index is None:
36
+ raise RefusedInput(
37
+ "no index is wired -- an empty result from an unwired store "
38
+ "would read as 'no evidence'"
39
+ )
40
+ nodes = index.as_retriever(similarity_top_k=top_k).retrieve(query)
41
+ return [
42
+ {"id": n.node_id, "score": n.score, "text": n.get_content()}
43
+ for n in nodes
44
+ ]
@@ -2,7 +2,7 @@
2
2
  # Distribution name matches the repository and stays unique on any index;
3
3
  # the import package and the CLI command remain the short `fde`.
4
4
  name = "fde-framework"
5
- version = "0.1.2"
5
+ version = "0.1.3"
6
6
  description = "A framework for Forward Deployed Engineers: from problem statement to a deployable AI project, every decision traced to a fact."
7
7
  readme = "README.md"
8
8
  license = {text = "Apache-2.0"}
@@ -19,6 +19,7 @@ classifiers = [
19
19
  "License :: OSI Approved :: Apache Software License",
20
20
  "Programming Language :: Python :: 3.11",
21
21
  "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
22
23
  "Topic :: Software Development :: Code Generators",
23
24
  ]
24
25
 
@@ -159,6 +159,15 @@ def start(
159
159
  if facts:
160
160
  typer.echo("")
161
161
  typer.echo(restate(facts, registry))
162
+ space_now = Space.from_registry(registry).apply(_with_all(Profile(), facts))
163
+ follow_ups = remaining_questions(
164
+ space_now, _with_all(Profile(), facts), registry
165
+ )[:3]
166
+ if follow_ups:
167
+ typer.echo(f"\nworth asking next (fde ask {name} --role <who>):")
168
+ for question in follow_ups:
169
+ roles = "/".join(question.roles) if question.roles else "anyone"
170
+ typer.echo(f" - {question.asks} [{roles}]")
162
171
  session_id = _next_session_id(engagement, "frame")
163
172
  briefs = engagement.artifacts_dir / "briefs"
164
173
  briefs.mkdir(parents=True, exist_ok=True)
@@ -367,7 +376,7 @@ def frame(
367
376
  space_now = Space.from_registry(registry).apply(with_new)
368
377
  follow_ups = remaining_questions(space_now, with_new, registry)[:3]
369
378
  if follow_ups:
370
- typer.echo("\nworth asking next (fde ask <eng> --role <who>):")
379
+ typer.echo(f"\nworth asking next (fde ask {root} --role <who>):")
371
380
  for question in follow_ups:
372
381
  roles = "/".join(question.roles) if question.roles else "anyone"
373
382
  typer.echo(f" - {question.asks} [{roles}]")
@@ -1597,8 +1606,13 @@ def scan_cmd(
1597
1606
  typer.echo(f" {gpu.model} {gpu.vram_gb:.0f}GB sm {gpu.sm}")
1598
1607
  elif measured:
1599
1608
  typer.echo(" no accelerator")
1600
- typer.echo(f" {hardware.total_vram_gb:.0f}GB total"
1601
- f"{'' if vram is None else ' (stated, not measured)'}")
1609
+ if hardware.gpus or hardware.total_vram_gb:
1610
+ typer.echo(f" {hardware.total_vram_gb:.0f}GB total"
1611
+ f"{'' if vram is None else ' (stated, not measured)'}")
1612
+ elif hardware.ram_gb:
1613
+ # Unified memory or CPU-only: "0GB total" beside "17GB of host
1614
+ # memory" read as the tool contradicting itself.
1615
+ typer.echo(f" {hardware.ram_gb:.0f}GB host memory (shared)")
1602
1616
 
1603
1617
  fit = fits(hardware, params_b, precision=precision)
1604
1618
  if not hardware.gpus:
@@ -1897,7 +1911,7 @@ def cost_cmd(
1897
1911
  )
1898
1912
 
1899
1913
  if price_per_seat is not None:
1900
- from fde.costing import unit_economics
1914
+ from fde.costing import COSTING_AS_OF, unit_economics
1901
1915
 
1902
1916
  coverage = None
1903
1917
  if root is not None:
@@ -1908,7 +1922,8 @@ def cost_cmd(
1908
1922
  )
1909
1923
  typer.echo(
1910
1924
  f"\nunit economics at ${price_per_seat:.2f}/seat, "
1911
- f"{workflows_per_day:g} workflows/day, {steps} step(s):"
1925
+ f"{workflows_per_day:g} workflows/day, {steps} step(s) "
1926
+ f"-- token pricing as of {COSTING_AS_OF}:"
1912
1927
  )
1913
1928
  typer.echo(f" ${economics['cost_per_workflow']:.4f}/workflow -> "
1914
1929
  f"${economics['cost_per_seat_month']:.2f}/seat-month in model spend")
@@ -30,6 +30,7 @@ STALE_AFTER_DAYS = 270
30
30
 
31
31
  # Indicative. Re-derive against current pricing rather than quoting these.
32
32
  GPU_COST_PER_HOUR = 4.50
33
+ COSTING_AS_OF = "2026-08"
33
34
  MANAGED_COST_PER_MILLION_TOKENS = 3.00
34
35
  TOKENS_PER_REQUEST = 1_500
35
36