v3.17.0
Changed
- Dialogue reviews now gate raw weakest-link claim support at a
matched-FPR operating point instead of squeezing bidirectional NLI
through the 0.80 baseline (WCS-2a). The WCS-1 end-to-end proof
(BENCHMARK_REPORT §16, tracked artefact
e2e_nli_only_200_wcs1_wired.json) showed the squeeze absorbs
evidence improvements — decisions were identical at catch 4.5 % —
while the raw operating point measures catch 27 % at the same 4.5 %
false-positive rate on HaluEval-dialogue. Each response claim is
scored independently against the conversation context and the
weakest claim decides; coherence for the dialogue route is that raw
support. New config:nli_dialogue_scoring
("raw_support"default,"baseline_squeeze"restores the previous
behaviour) andnli_dialogue_support_threshold(seeded 0.0091 from
the FactCG sweep; recalibrate per deployment — see the calibration
tool below). - Cache hits now resolve the review gate exactly like fresh scoring
(task detection + adaptive per-task threshold + meta-classifier +
raw-support operating point). Previously a cache hit re-gated the
cached score on the global threshold, so a decision could differ
between a cache miss and a cache hit of the same input. - Summarisation claim-coverage layers (MiniCheck and FactCG) now score
against the whole source document instead of a 3 000-character
prefix; each backend applies its own long-input chunking. The
2026-07-15 evidence sweep (BENCHMARK_REPORT §16) showed the prefix
truncation is dominated on both HaluEval and RAGTruth — catch at the
matched false-positive rate rose from 0.049 to 0.289 on RAGTruth
Summary with whole-document scoring. The previous behaviour is
restorable viaDirectorConfig.nli_summarization_premise_chars = 3000
(DIRECTOR_NLI_SUMMARIZATION_PREMISE_CHARS).
Security
- Defence-in-depth hardening from the validated 2026-07-15 external
review intake: SQL DDL identifiers and column clauses are strictly
validated before f-string composition (audit log chain migration,
feedback-store column migration — internal constants today, now
guarded against future callers); gRPC server reflection is an
explicit opt-in (grpc_reflection_enabled, default off — the full
service schema is a reconnaissance aid); the regulated-domain
profiles (medical,finance,legal) enableprivacy_modeso
PII is redacted from logs and review queues by default. - Streaming trend statistics (
core/runtime/streaming.py) now state
their contract honestly: they are MANDATORY Rust accelerators
(enforced 2026-05-22) — the unreachable pure-Python fallback bodies,
the misleading_RUST_TRENDdispatch flag, and the stale
"falls back to Python" tests (inverted by a conftest hook) are
gone; kernel-absent installs raise the actionable[rust]-extra
error, and the tests assert that propagation directly. - Operator-supplied regexes (policy YAML patterns, sanitiser
extra_patterns/allowlist) are validated before compilation:
the classic catastrophic-backtracking shape — an unbounded repeat
nested inside another unbounded repeat, e.g.(a+)+— is rejected
with an actionable error, as are patterns over 4 096 chars
(core/safety/_regex_guard.py). Bounded repetitions pass. - WebSocket streaming sessions cap each prompt at 100 000 chars
(mirrors the SSE cap; the per-connection char budget still applies). - Prompt/query hashes in logs are now keyed (HMAC-SHA256 with a
per-process ephemeral key) instead of bare SHA-256, so exfiltrated
log lines cannot be brute-forced offline against short prompts;
in-run correlation is unchanged (the meta-guard decision log is
in-memory, retrieval query hashes are log-line correlators).
Fixed
- PII redaction was quadratic in the number of findings: the
non-overlapping span selection rescanned every accepted span per
candidate, so a 2 MB input of dense PII took 13 minutes to redact —
a denial-of-service vector whereverprivacy_modeis enabled. The
selection is now linear (2.4 s on the same input, 324× faster),
with the old implementation kept as a behavioural oracle in tests.
Added
-
Weakest-link summarisation aggregation
(nli_summarization_aggregation="weakest_link"+
nli_summarization_support_threshold): scores every summary claim
against the whole source and gates the least-supported claim at a
matched-FPR support operating point — the only WCS-1 configuration
that improved on both HaluEval (catch 0.120 → 0.125) and RAGTruth
(0.049 → 0.289). Default stays the coverage/Layer-A blend. -
Matched-FPR operating-point calibration
(director_ai.core.calibration.operating_pointsand
director-ai operating-points <labeled.jsonl>): collects raw
weakest-link supports through the production scorer
(CoherenceScorer.raw_task_support), picks the largest support
threshold whose false-positive rate on labelled good responses stays
within a per-task target, and emits a ready-to-use config/env
overlay. Shipped thresholds are sweep seeds — deployments should
calibrate on their own traffic. -
Rubric-scored, optionally ensembled LLM judge
(DirectorConfig.llm_judge_rubric/llm_judge_ensemble): the
escalation judge can score grounding / fabrication-risk /
contradiction-risk dimensions (G-Eval-style, composite decides the
verdict) instead of a bare YES/NO, and can aggregate 1–5
independent calls — majority vote with confidence damped by the
agreement fraction, so a split panel weakens the judge's influence
on the blended score. Defaults unchanged (single-shot verdict);
invalid or empty panels fall back to the NLI score exactly as
before. -
Self-consistency / semantic-entropy uncertainty signal
(director_ai.core.scoring.self_consistency):SelfConsistencyScorer
clusters caller-supplied alternative generations by bidirectional
entailment (shipped NLI backend, lexical fallback labelled in the
result) and scores normalised semantic entropy plus the primary
response's consensus agreement. Opt-in fusion on the review
pipeline:enable_self_consistency(weight=…)+
review_with_samples(prompt, action, samples)— the fused score can
revoke an approval but never approves a rejected review;
CoherenceScoregainsself_consistency_score,semantic_entropy
andself_consistency_backendfields. -
Hardened per-tenant isolation for shared vector indexes:
TenantScopedBackend
(director_ai.core.retrieval.vector_store.tenant_guard) binds any
VectorBackendto one tenant — adds stamp the bound tenant into
metadata (conflicting labels raiseTenantIsolationError), an
empty caller tenant can no longer widen a query to the whole
index, and every returned row is verified against the bound tenant
(foreign or unlabelled rows are dropped and counted in the
tenant_isolation_violationsmetric, or raise with
strict=True).VectorGroundTruthStore.grounded()gains
enforce_tenant_isolation=Trueto wrap the whole retrieval stack;
FAISSBackendtenant-filtered queries now expand the over-fetch
window geometrically until enough tenant documents are found or
the whole index is searched, instead of silently starving tenants
whose documents sit beyond the previous fixed 3× window. -
Hybrid retrieval fusion strategies beyond RRF
(director_ai.core.retrieval.vector_store.fusion):convex
(min-max normalised convex combination, CombSUM family),combmnz
(CombSUM × cross-run agreement) andzscore(standardised-score
sum) join the default weightedrrf. Selectable via
HybridBackend(fusion_method=...),
VectorGroundTruthStore.grounded(fusion_method=...)and the new
DirectorConfig.hybrid_fusion_method/
hybrid_sparse_weight/hybrid_dense_weightfields;
HybridBackend.with_fusion()derives query views that share one
BM25 + dense index so strategies can be compared without
re-indexing. The BM25 run now carries native scores into the
fusion layer, and the RRF docstring cites the actual source
(Cormack, Clarke & Büttcher, SIGIR 2009). -
NeMo Guardrails rails-as-config loader
(director_ai.integrations.rails_config.load_rails_config): maps the
honest subset of a NeMo config directory (config.yml+ Colang.co
files) onto Director's nativePolicy— Colang v1 topical refusal
rails become forbidden phrases, recognised self-check/content-safety
rails enable the dependency-free moderation detectors — and reports
every unmapped construct inRailsLoadResult.unsupportedinstead of
silently dropping it. RAIL XML deliberately raises with a pointer to
the native Guardrails AI validator integration. New docs page
docs-site/integrations/nemo-rails.md. -
Computed AI-governance controls (
director_ai.compliance. governance_controls, BUSL tier):compute_governance_controls()
derives NIST AI RMF 1.0 / ISO/IEC 42001:2023 / EU AI Act crosswalk
controls from observable deployment state — DirectorConfig guard and
data-governance knobs, a liveverify_chain()pass over the
tamper-evident audit log, and documentation evidence artefacts under
an operator-supplied evidence root. Six controls cover Articles 9
(risk management), 10 (data governance), 11 (technical
documentation), 12 (record-keeping), plus the Article 15 accuracy
bridge and Article 14 human-oversight readiness; every status is
derived from namedControlSignalobservations, so missing inputs
degrade honestly instead of aborting. New endpoint
GET /v1/compliance/governance-controls(never 503s — an
unconfigured audit log is itself the reported finding) and CLI
subcommanddirector-ai compliance governance. Previously only
Article 15 reporting was computed; SOC 2/ISO 27001/HIPAA remain the
static readiness catalogue. -
POST /v1/stream/sse— REST Server-Sent Events streaming endpoint
(routers/streaming_sse.py). Serves the same two session shapes as
the/v1/streamWebSocket — whole-answerresult, or token-level
pre-egress oversight withtoken/halt/completeframes — as
text/event-stream, for server-to-server callers and clients behind
WebSocket-hostile proxies. Auth/tenant binding ride the standard REST
middleware (no ticket exchange); validation failures are plain HTTP
errors before the stream starts; a per-process concurrent-stream cap
mirrors the WebSocket connection budget. -
OpenAI-compatible proxy:
/v1/moderations(local mode analyses inputs
with the shipped dependency-free toxicity/PII detectors and answers in
the OpenAI moderations shape;moderations="upstream"forwards the
request verbatim),/v1/completions(legacy text completions scored
through the same review flow as chat, streaming included — halt chunks
mirror the legacytextdelta shape), and/v1/embeddings
(passthrough). New CLI flagdirector-ai proxy --moderations local|upstream; audit entries recordtask_type="completion"for the
legacy route. New guide pagedocs-site/guide/openai-proxy.md. -
benchmarks/retrieval_model_refresh_ab.py— embedder/reranker refresh
A/B evidence for thegrounded()default pair (WCA-8). Eight arms
through the public recipe on the internal retrieval evaluation set
(30 queries, CPU): any cross-encoder lifts hit@1 from 0.733–0.767 to
0.933–1.000;bge-m3 + bge-reranker-v2-m3reaches hit@1 1.000 at
~2.2 s/query,bge-m3 + ms-marco-MiniLM-L-6-v2reaches 0.967 at
~0.56 s/query. The internal set saturates near the ceiling, so the
refresh decision defers to the public-benchmark run below. Artefact:
benchmarks/results/retrieval_model_refresh_ab.json(committed) with
exact model revisions and host environment. -
benchmarks/beir_competitive_bench.py— BEIR NFCorpus + SciFact test
splits through the shippedgrounded()recipe (hybrid BM25+dense RRF,
optional cross-encoder rerank of the top 30), scored with
pytrec_eval(nDCG@10) and cross-checked against a built-in
linear-gain implementation. The artefact embeds published baselines
verified at source (BEIR paper Table 2; bge-large-en-v1.5 model-card
MTEB metrics) so the numbers are comparable across systems.
Measured (L4 GPU, artefact committed): the shipped hybrid pipeline
without a reranker reaches nDCG@10 0.3703 (NFCorpus) / 0.7331
(SciFact), above the BEIR paper's BM25 (0.325/0.665) and BM25+CE
(0.350/0.688) rows; the candidate bge-m3 embedder is below the
shipped bge-large default on both datasets, so theRECOMMENDED_*
defaults stay unchanged — an evidence-based no-change. Full table,
readings, and claim boundary inbenchmarks/BENCHMARK_REPORT.md
§11; the table is registered in
benchmarks/public_accuracy_manifest.toml. Quality numbers
cross-checked CPU vs GPU: the committed
beir_competitive_bench_cpu_i5_11600K.jsonre-runs all five NFCorpus
arms end-to-end on CPU and reproduces the GPU nDCG@10 values exactly,
while measuring CPU rerank latency under load (ms-marco ~3 s/query,
bge-reranker-v2-m3 ~70 s/query on 30 candidates). -
DirectorConfig.finetune_models_dir(env:DIRECTOR_FINETUNE_MODELS_DIR)
— the server now passes a configurable models/jobs directory to the
fine-tuning router instead of always mounting the hard-coded
./director-modelsdefault.
Changed
-
Add native unit tests to the
backfire-fficrate (18 tests: Bulletproof
range-proof round-trip/tamper/error paths, geometry containment,
two-link inverse kinematics, Merkle root/auth-path/walk parity,
HMAC challenge derivation, reality-anchor MAC verification, and the
statistical helper contracts). The crate'sextension-moduleflag is
now a default crate feature socargo test -p backfire-ffi --no-default-featurescan link libpython for the test binary; maturin
wheel builds are unchanged. -
Stop tracking the
benchmarks/models/factcg-cbtokeniser blobs
(tokenizer.json8.4 MB,spm.model2.4 MB) in git; they are inherited
unchanged from the fine-tune's base model and the directory README now
documents the one-line restore. Configs and training results stay
tracked; nothing insrc/ortests/reads the directory. -
Consolidate the four typos configurations into the single canonical
.typos.toml(union of all allow-lists and file excludes) and delete
typos.toml,_typos.toml, andbackfire-kernel/_typos.toml. The
pre-commit hook, the CI Pre-commit workflow, and a loosetyposrun
from any repo directory now read the same configuration — previously a
loose run silently picked the staletypos.toml, which wins CLI
discovery over the hook-pinned.typos.toml. -
Decompose
finetune_api.pyby responsibility: request/response
contracts move to_finetune_schemas, the local training worker to
_finetune_worker, and the/managed/*endpoints to
_finetune_managed; thefinetune_apifacade keeps the local lane,
the router factory, and the historical import surface unchanged. -
VectorGroundTruthStore.grounded()now builds the full retrieval stack by
default: FAISS-indexed dense search (use_ann), BM25 + RRF hybrid fusion,
and cross-encoder reranking (use_reranker), each degrading gracefully to
the previous behaviour when its optional dependency is missing. On the
committed benchmark (benchmarks/results/grounded_ann_bench.json, run via
python -m benchmarks.grounded_ann_bench) the reranked default lifts hit@1
from 0.767 to 0.967 and hit@3 to 1.000 on the retrieval evaluation set at
~57 ms per query on CPU, and flat FAISS answers 20 000-document queries in
8.6 ms (p50) against 25.3 ms for the previous linear scan at identical
recall.
Fixed
- Align the Rust
rust_standard_normal_quantiledomain guard with the
Python reference:p = 0.0now raises instead of returning negative
infinity (the documented domain is the open interval, and the Python
dispatcher already rejected it). SentenceTransformerBackendnow selects a compatible torch device via
select_torch_device(), so a visible-but-unsupported CUDA GPU (for
example an sm_61 card next to a modern PyTorch wheel) no longer crashes
dense retrieval at the first encode. The backend also accepts a preloaded
modelobject for offline and embedded deployments, mirroring the
rerankerinjection onRerankedBackend.