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.
- {fde_framework-0.1.2 → fde_framework-0.1.3}/CHANGELOG.md +16 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/PKG-INFO +53 -16
- {fde_framework-0.1.2 → fde_framework-0.1.3}/README.md +51 -15
- {fde_framework-0.1.2 → fde_framework-0.1.3}/examples/invoice-extraction/README.md +1 -1
- fde_framework-0.1.3/framework/approaches/hybrid-search.md +24 -0
- fde_framework-0.1.3/framework/approaches/reranked-retrieval.md +24 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/corpus_size.md +1 -1
- fde_framework-0.1.3/framework/patterns/hybrid-search.md +9 -0
- fde_framework-0.1.3/framework/patterns/reranked-retrieval.md +9 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/vector-search.md +1 -0
- fde_framework-0.1.3/framework/stacks/llamaindex.md +20 -0
- fde_framework-0.1.3/framework/templates/retrieval/hybrid-search.plain.py.j2 +43 -0
- fde_framework-0.1.3/framework/templates/retrieval/reranked-retrieval.plain.py.j2 +41 -0
- fde_framework-0.1.3/framework/templates/retrieval/vector-search.llamaindex.py.j2 +44 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/pyproject.toml +2 -1
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/cli.py +20 -5
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/costing.py +1 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_dead_zones.py +30 -0
- fde_framework-0.1.3/tests/test_example_transcript.py +62 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_sanitisation.py +1 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/.gitignore +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/LICENSE +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/ansible-playbook.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/assisted-deterministic.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/audit-only.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/boundary-and-audit.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/cascade.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/classical-ml.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/compose.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/decision-log.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/deterministic-masking.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/deterministic.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/direct-call.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/episodic-store.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/explainability-record.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/field-match.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/finetune.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/fixed-sequence.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/gitops.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/governed-tools.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/graph-retrieval.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/judged.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/keyword-search.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/kubernetes-manifests.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/labelled-metrics.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/llm-extraction.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/llm-scrubbing.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/llm.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/local-embedding.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/managed-api.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/managed-embedding.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/manual-runbook.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/model-planner.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/ocr-pipeline.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/optimisation-reasoning.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/optimisation.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/passthrough.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/role-scoped-authority.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/segmentation.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/self-hosted.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/serverless-gpu.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/speech-transcription.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/structured-logs.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/systemd-unit.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/terraform-module.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/text-extraction.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/traced.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/vector-search.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/video-ingestion.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/windowed-ingestion.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/approaches/working-state.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/cases/churn-scoring.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/cases/route-planning.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/cases/structured-extraction.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/cases/studio-style.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/accountability.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/deployment.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/embedding.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/evaluation.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/governance.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/integration.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/memory.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/observability.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/perception.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/planning.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/provisioning.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/reasoning.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/redaction.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/representation.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/retrieval.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/components/serving.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/accelerator.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/access_model.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/arrival_rate.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/availability_target.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/cheap_path_coverage.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/confidence_calibrated.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/container_competence.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/data_residency.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/environment_lifetime.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/existing_cluster.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/existing_iac_tool.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/external_systems.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/hosting.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/human_waiting.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/input_format.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/interpretability_required.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/labelled_count.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/latency_budget_ms.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/licence_posture.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/operates_after_handover.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/output_shape.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/provisioning_api.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/query_pattern.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/recall_span.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/dimensions/sensitivity_present.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Generator.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Guard.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Mapper.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/ModelServer.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Parser.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Planner.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Retriever.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Scorer.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Store.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/ToolBoundary.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/interfaces/Tracer.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/locales/eu-gdpr.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/locales/in-dpdp.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/ansible-playbook.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/assisted-deterministic.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/audit-only.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/boundary-and-audit.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/cascade-reasoning.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/cascade-representation.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/cascade-retrieval.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/classical-ml-reasoning.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/classical-ml.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/compose.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/decision-log.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/deterministic-masking.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/deterministic.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/direct-call.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/episodic-store.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/explainability-record.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/field-match.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/finetune-representation.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/finetune.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/fixed-sequence.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/gitops.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/governed-tools.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/graph-retrieval.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/judged.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/keyword-search.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/kubernetes-manifests.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/labelled-metrics.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/llm-representation.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/llm-scrubbing.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/llm.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/local-embedding.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/managed-api.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/managed-embedding.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/manual-runbook.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/model-planner.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/ocr-pipeline.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/optimisation-reasoning.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/optimisation.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/passthrough.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/role-scoped-authority.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/segmentation.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/self-hosted.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/serverless-gpu.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/speech-transcription.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/structured-logs.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/systemd-unit.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/terraform-module.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/text-extraction.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/traced.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/video-ingestion.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/windowed-ingestion.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/patterns/working-state.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/langgraph.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/local-judge.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/mcp.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/ollama.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/openai-judge.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/opentelemetry.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/ortools.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/pgvector.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/plain-python.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/qdrant.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/tesseract.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/vllm.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/whisper.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/stacks/xgboost.md +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/accountability/decision-log.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/accountability/explainability-record.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/deployment/compose.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/deployment/kubernetes-manifests.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/deployment/systemd-unit.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/embedding/local-embedding.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/embedding/managed-embedding.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/field-match.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/judged.local-judge.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/judged.openai-judge.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/judged.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/labelled-metrics.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/evaluation/labelled-metrics.xgboost.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/governance/audit-only.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/governance/boundary-and-audit.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/governance/role-scoped-authority.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/integration/direct-call.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/integration/governed-tools.mcp.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/integration/governed-tools.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/memory/episodic-store.pgvector.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/memory/episodic-store.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/memory/working-state.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/observability/structured-logs.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/observability/traced.opentelemetry.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/observability/traced.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/ocr-pipeline.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/ocr-pipeline.tesseract.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/passthrough.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/speech-transcription.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/speech-transcription.whisper.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/text-extraction.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/video-ingestion.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/perception/windowed-ingestion.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/planning/fixed-sequence.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/planning/model-planner.langgraph.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/planning/model-planner.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/planning/optimisation.ortools.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/planning/optimisation.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/provisioning/ansible-playbook.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/provisioning/gitops.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/provisioning/manual-runbook.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/provisioning/terraform-module.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/cascade.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/classical-ml.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/classical-ml.xgboost.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/finetune.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/llm.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/optimisation.ortools.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/reasoning/optimisation.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/redaction/deterministic-masking.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/redaction/llm-scrubbing.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/assisted.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/cascade.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/classical-ml.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/classical-ml.xgboost.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/deterministic.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/finetune.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/llm.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/representation/segmentation.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/cascade.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/graph-retrieval.pgvector.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/graph-retrieval.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/graph-retrieval.qdrant.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/keyword-search.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/vector-search.pgvector.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/vector-search.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/retrieval/vector-search.qdrant.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/serving/managed-api.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/serving/self-hosted.ollama.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/serving/self-hosted.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/serving/self-hosted.vllm.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/framework/templates/serving/serverless-gpu.plain.py.j2 +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/__init__.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/architect.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/decide.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/decompose.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/deploy.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/emit.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/evolution.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/factlog.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/gates.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/graph.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/implement.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/__init__.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/answers.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/documents.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/interview.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/llm_reader.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/prose.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/intake/samples.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/__init__.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/base.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/fact.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/profile.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/respondent.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/models/schema.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/moves.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/ops.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/predicate.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/realization.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/registry.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/scan.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/space.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/src/fde/workflow.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/__init__.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/conftest.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/models/__init__.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/models/test_profile.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_access_and_sensitivity.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_answers.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cascade.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_engagement.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_evolution.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_gates.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_intake.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_kb.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_robustness.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_cli_samples.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_costing.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_coverage.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_decide.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_decompose.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_deploy.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_documents.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_embedding.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_emit.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_evolution.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_factlog.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_gates.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_implement.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_interview.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_llm_reader.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_locales.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_memory_retrieval.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_more_templates.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_moves.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_multimodal.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_ops.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_package.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_prose.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_realization.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_registry.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_remaining.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_reversibility.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_samples.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_scan.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_schema.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_serving.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_shipped_registry.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_space.py +0 -0
- {fde_framework-0.1.2 → fde_framework-0.1.3}/tests/test_stack_swap.py +0 -0
- {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.
|
|
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
|

|
|
48
49
|
[](https://pypistats.org/packages/fde-framework)
|
|
49
50
|

|
|
50
|
-
[](LICENSE)
|
|
51
|
+
[](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
|
|
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
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
519
|
-
|
|
520
|
-
|
|
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
|

|
|
11
11
|
[](https://pypistats.org/packages/fde-framework)
|
|
12
12
|

|
|
13
|
-
[](LICENSE)
|
|
13
|
+
[](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
|
|
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
|
|
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
|
|
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
|
-
##
|
|
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
|
-
|
|
482
|
-
|
|
483
|
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
1601
|
-
|
|
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")
|