Wire Parakeet ASR into ZeroGPU Space
Browse files- README.md +4 -3
- app.py +30 -0
- docs/local_parakeet_asr_evidence.md +12 -1
- docs/model_parameter_evidence_ledger.md +5 -4
- docs/submission_checklist.md +2 -2
- figment/audio_intake.py +1 -2
- figment/config.py +9 -1
- figment/parakeet_asr.py +191 -0
README.md
CHANGED
|
@@ -10,6 +10,7 @@ pinned: false
|
|
| 10 |
python_version: 3.12.12
|
| 11 |
preload_from_hub:
|
| 12 |
- build-small-hackathon/figment-finetuned-model-archive figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/chat_template.jinja,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/config.json,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/configuration_nemotron_h.py,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/generation_config.json,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/model-00001-of-00002.safetensors,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/model-00002-of-00002.safetensors,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/model.safetensors.index.json,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/modeling_nemotron_h.py,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/special_tokens_map.json,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/tokenizer.json,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/tokenizer_config.json
|
|
|
|
| 13 |
---
|
| 14 |
|
| 15 |
# Figment
|
|
@@ -24,7 +25,7 @@ Figment turns messy field intake into a card-cited protocol workflow: confirm th
|
|
| 24 |
|
| 25 |
| Surface | Current evidence | What it means | Boundary |
|
| 26 |
| --- | --- | --- | --- |
|
| 27 |
-
| Public Space | [build-small-hackathon/figment](https://huggingface.co/spaces/build-small-hackathon/figment) was `RUNNING` on `zero-a10g` at Space commit `
|
| 28 |
| Hosted Omni eval | `31/50` whole-output competence, `8/50` full fallback, `480/650` model-retained fields, `170/650` deterministic patches, and `50/50` final validation. | Hosted Omni can carry bounded fields, and the app can keep outputs inside the safety contract. | `50/50` final validation is app safety after validation, repair, and fallback. It is not pure model performance. |
|
| 29 |
| 4B LoRA system eval | v14p repair-union on the corrected 150-case field-workflow holdout: `150/150` competence, `150/150` expected labels, `150/150` final validation, `0` deterministic patches, `0` fallback. Raw first-pass success is `146/150`; `4/150` cases close through focused model repair. | The strongest documented small-model result is model-owned output plus model repair on a synthetic/de-identified holdout. | This is not clinical validation, target-user validation, local ASR proof, or proof that raw first-pass output solved every case. |
|
| 30 |
| Public artifacts | [model archive](https://huggingface.co/build-small-hackathon/figment-finetuned-model-archive) and [eval/training dataset](https://huggingface.co/datasets/build-small-hackathon/figment-eval-traces). | Versioned BF16/GGUF model artifacts, synthetic corpora, eval traces, and summaries are inspectable outside this checkout. | Generated `traces/`, `data/finetune/`, weights, and checkpoint folders are intentionally not part of a clean clone. |
|
|
@@ -95,12 +96,12 @@ Figment supports four runtime routes:
|
|
| 95 |
| --- | --- | --- |
|
| 96 |
| Canned fallback | `MODEL_BACKEND=canned` | No-secret app smoke, UI development, honest fallback traces. |
|
| 97 |
| Hosted Omni | `MODEL_BACKEND=hosted_omni` with `NVIDIA_API_KEY` | Live hosted demo and hosted eval path using `nvidia/nemotron-3-nano-omni-30b-a3b-reasoning`. |
|
| 98 |
-
| HF ZeroGPU v14p | `MODEL_BACKEND=hf_zerogpu`
|
| 99 |
| Local OpenAI-compatible | `MODEL_BACKEND=llama_cpp` with `LLAMA_BASE_URL` | Local text-navigation route for the 4B BF16/GGUF artifacts and local evidence bundles. |
|
| 100 |
|
| 101 |
The Build Small constraint is <=32B total parameters. The hosted Omni path is tracked with a parameter-count caveat: the NVIDIA model-card body reports 31B total parameters, while sidebar counts have differed. The 4B BF16 base model, `nvidia/NVIDIA-Nemotron-3-Nano-4B-BF16`, is the local text-navigation training target.
|
| 102 |
|
| 103 |
-
Parakeet ASR remains
|
| 104 |
|
| 105 |
## Evaluation
|
| 106 |
|
|
|
|
| 10 |
python_version: 3.12.12
|
| 11 |
preload_from_hub:
|
| 12 |
- build-small-hackathon/figment-finetuned-model-archive figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/chat_template.jinja,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/config.json,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/configuration_nemotron_h.py,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/generation_config.json,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/model-00001-of-00002.safetensors,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/model-00002-of-00002.safetensors,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/model.safetensors.index.json,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/modeling_nemotron_h.py,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/special_tokens_map.json,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/tokenizer.json,figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/tokenizer_config.json
|
| 13 |
+
- nvidia/parakeet-ctc-1.1b config.json,model.safetensors,preprocessor_config.json,special_tokens_map.json,tokenizer.json,tokenizer_config.json,vocab.json
|
| 14 |
---
|
| 15 |
|
| 16 |
# Figment
|
|
|
|
| 25 |
|
| 26 |
| Surface | Current evidence | What it means | Boundary |
|
| 27 |
| --- | --- | --- | --- |
|
| 28 |
+
| Public Space | [build-small-hackathon/figment](https://huggingface.co/spaces/build-small-hackathon/figment) was `RUNNING` on `zero-a10g` at Space commit `45df7643e9e592f8496214c436532a3ade3cfdfc` on 2026-06-15. A synthetic `/run_navigator` call returned `raw_route=hf_zerogpu`, `fallback_tier=configured`, `field_level_fallback_used=true`, `final_route=model_with_deterministic_patches`, and `validation_status=passed` in 41.87 seconds. | The public Space reaches the published v14p BF16 model archive through HF ZeroGPU and the validator passed on the returned response. | This is one live synthetic route check. Deterministic patches still contributed, so it is live serving proof, not pure model-only competence proof. |
|
| 29 |
| Hosted Omni eval | `31/50` whole-output competence, `8/50` full fallback, `480/650` model-retained fields, `170/650` deterministic patches, and `50/50` final validation. | Hosted Omni can carry bounded fields, and the app can keep outputs inside the safety contract. | `50/50` final validation is app safety after validation, repair, and fallback. It is not pure model performance. |
|
| 30 |
| 4B LoRA system eval | v14p repair-union on the corrected 150-case field-workflow holdout: `150/150` competence, `150/150` expected labels, `150/150` final validation, `0` deterministic patches, `0` fallback. Raw first-pass success is `146/150`; `4/150` cases close through focused model repair. | The strongest documented small-model result is model-owned output plus model repair on a synthetic/de-identified holdout. | This is not clinical validation, target-user validation, local ASR proof, or proof that raw first-pass output solved every case. |
|
| 31 |
| Public artifacts | [model archive](https://huggingface.co/build-small-hackathon/figment-finetuned-model-archive) and [eval/training dataset](https://huggingface.co/datasets/build-small-hackathon/figment-eval-traces). | Versioned BF16/GGUF model artifacts, synthetic corpora, eval traces, and summaries are inspectable outside this checkout. | Generated `traces/`, `data/finetune/`, weights, and checkpoint folders are intentionally not part of a clean clone. |
|
|
|
|
| 96 |
| --- | --- | --- |
|
| 97 |
| Canned fallback | `MODEL_BACKEND=canned` | No-secret app smoke, UI development, honest fallback traces. |
|
| 98 |
| Hosted Omni | `MODEL_BACKEND=hosted_omni` with `NVIDIA_API_KEY` | Live hosted demo and hosted eval path using `nvidia/nemotron-3-nano-omni-30b-a3b-reasoning`. |
|
| 99 |
+
| HF ZeroGPU v14p + Parakeet ASR | `MODEL_BACKEND=hf_zerogpu`, `AUDIO_BACKEND=parakeet_nemo`, and `ALLOW_LOCAL_ASR=true` | Public Space route using the published v14p BF16 merged model plus gated Parakeet ASR draft intake on Hugging Face ZeroGPU. |
|
| 100 |
| Local OpenAI-compatible | `MODEL_BACKEND=llama_cpp` with `LLAMA_BASE_URL` | Local text-navigation route for the 4B BF16/GGUF artifacts and local evidence bundles. |
|
| 101 |
|
| 102 |
The Build Small constraint is <=32B total parameters. The hosted Omni path is tracked with a parameter-count caveat: the NVIDIA model-card body reports 31B total parameters, while sidebar counts have differed. The 4B BF16 base model, `nvidia/NVIDIA-Nemotron-3-Nano-4B-BF16`, is the local text-navigation training target.
|
| 103 |
|
| 104 |
+
Parakeet ASR remains evidence-gated. A Space route can use the Transformers-native `nvidia/parakeet-ctc-1.1b` Parakeet model for live audio draft intake; the original local/offline proof path still requires a real provider-output evidence bundle before any no-cloud/local-ASR claim is upgraded.
|
| 105 |
|
| 106 |
## Evaluation
|
| 107 |
|
app.py
CHANGED
|
@@ -14,6 +14,12 @@ from figment.audio_intake import draft_audio_intake as _draft_audio_intake
|
|
| 14 |
from figment.config import FigmentConfig, load_config
|
| 15 |
from figment.model_client import ModelClient, ModelClientError, hosted_audio_limits_text, validate_hosted_audio_file
|
| 16 |
from figment.navigator import run_navigation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
from figment.retrieval import load_protocol_cards, query_from_intake, retrieval_source_summary, search_protocol_cards
|
| 18 |
from figment.rules import evaluate_rules, run_red_flag_checks
|
| 19 |
from figment.sbar import render_sbar
|
|
@@ -158,6 +164,16 @@ def draft_audio_intake(
|
|
| 158 |
provider_payload = ModelClient(config).generate_audio_draft(audio_file)
|
| 159 |
except ModelClientError as exc:
|
| 160 |
provider_error = f"Hosted Omni audio draft failed; typed transcript or canned fallback required. {exc}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
draft = _draft_audio_intake(
|
| 162 |
transcript=transcript,
|
| 163 |
config=config,
|
|
@@ -176,6 +192,11 @@ def draft_audio_intake(
|
|
| 176 |
hosted_disclosure = _hosted_audio_disclosure_text()
|
| 177 |
draft["hosted_audio_disclosure"] = hosted_disclosure
|
| 178 |
retention_note = f"{retention_note} {hosted_disclosure}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 179 |
draft["audio_retention_note"] = retention_note
|
| 180 |
if provider_error and draft.get("audio_intake_path") == "audio_received_needs_transcript_or_model":
|
| 181 |
draft["processing_status"] = provider_error
|
|
@@ -1635,6 +1656,15 @@ def _should_use_hosted_omni_audio(config: FigmentConfig) -> bool:
|
|
| 1635 |
)
|
| 1636 |
|
| 1637 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1638 |
def _hosted_audio_disclosure_text() -> str:
|
| 1639 |
return (
|
| 1640 |
"Hosted audio is sent to the configured hosted endpoint for drafting; use only synthetic or "
|
|
|
|
| 14 |
from figment.config import FigmentConfig, load_config
|
| 15 |
from figment.model_client import ModelClient, ModelClientError, hosted_audio_limits_text, validate_hosted_audio_file
|
| 16 |
from figment.navigator import run_navigation
|
| 17 |
+
from figment.parakeet_asr import (
|
| 18 |
+
ParakeetAsrError,
|
| 19 |
+
parakeet_audio_limits_text,
|
| 20 |
+
transcribe_audio_with_parakeet,
|
| 21 |
+
validate_parakeet_audio_file,
|
| 22 |
+
)
|
| 23 |
from figment.retrieval import load_protocol_cards, query_from_intake, retrieval_source_summary, search_protocol_cards
|
| 24 |
from figment.rules import evaluate_rules, run_red_flag_checks
|
| 25 |
from figment.sbar import render_sbar
|
|
|
|
| 164 |
provider_payload = ModelClient(config).generate_audio_draft(audio_file)
|
| 165 |
except ModelClientError as exc:
|
| 166 |
provider_error = f"Hosted Omni audio draft failed; typed transcript or canned fallback required. {exc}"
|
| 167 |
+
elif audio_file and not transcript.strip() and provider_payload is None and _should_use_parakeet_audio(config):
|
| 168 |
+
try:
|
| 169 |
+
validate_parakeet_audio_file(audio_file)
|
| 170 |
+
except ParakeetAsrError as exc:
|
| 171 |
+
provider_error = f"Parakeet ASR draft skipped; typed transcript or canned fallback required. {exc}"
|
| 172 |
+
else:
|
| 173 |
+
try:
|
| 174 |
+
provider_payload = transcribe_audio_with_parakeet(audio_file, config=config)
|
| 175 |
+
except ParakeetAsrError as exc:
|
| 176 |
+
provider_error = f"Parakeet ASR draft failed; typed transcript or canned fallback required. {exc}"
|
| 177 |
draft = _draft_audio_intake(
|
| 178 |
transcript=transcript,
|
| 179 |
config=config,
|
|
|
|
| 192 |
hosted_disclosure = _hosted_audio_disclosure_text()
|
| 193 |
draft["hosted_audio_disclosure"] = hosted_disclosure
|
| 194 |
retention_note = f"{retention_note} {hosted_disclosure}"
|
| 195 |
+
elif _should_use_parakeet_audio(config):
|
| 196 |
+
retention_note = (
|
| 197 |
+
f"{retention_note} Parakeet ASR runs on the configured local/ZeroGPU runtime; "
|
| 198 |
+
f"limit: {parakeet_audio_limits_text()}."
|
| 199 |
+
)
|
| 200 |
draft["audio_retention_note"] = retention_note
|
| 201 |
if provider_error and draft.get("audio_intake_path") == "audio_received_needs_transcript_or_model":
|
| 202 |
draft["processing_status"] = provider_error
|
|
|
|
| 1656 |
)
|
| 1657 |
|
| 1658 |
|
| 1659 |
+
def _should_use_parakeet_audio(config: FigmentConfig) -> bool:
|
| 1660 |
+
return (
|
| 1661 |
+
config.enable_audio_intake
|
| 1662 |
+
and config.audio_backend == "parakeet_nemo"
|
| 1663 |
+
and config.allow_local_asr
|
| 1664 |
+
and config.model_stack == "local_4b_parakeet"
|
| 1665 |
+
)
|
| 1666 |
+
|
| 1667 |
+
|
| 1668 |
def _hosted_audio_disclosure_text() -> str:
|
| 1669 |
return (
|
| 1670 |
"Hosted audio is sent to the configured hosted endpoint for drafting; use only synthetic or "
|
docs/local_parakeet_asr_evidence.md
CHANGED
|
@@ -17,6 +17,16 @@ This note separates local Parakeet ASR artifact availability from a real local A
|
|
| 17 |
|
| 18 |
Use the helper to capture local ASR evidence:
|
| 19 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
```bash
|
| 21 |
PYTHON_DOTENV_DISABLED=true \
|
| 22 |
python3 scripts/run_local_asr_evidence.py \
|
|
@@ -29,6 +39,7 @@ The helper writes a timestamped evidence directory under `traces/local_asr_parak
|
|
| 29 |
|
| 30 |
- `artifact_metadata.json`: Parakeet `.nemo` presence, size, and hash
|
| 31 |
- `audio_metadata.json`: optional source-audio metadata and hash only; raw audio is not copied into the evidence bundle
|
|
|
|
| 32 |
- `provider_payload_metadata.json`: provider output file hash
|
| 33 |
- `audio_draft.json`: Figment draft generated from the provider payload under `AUDIO_BACKEND=parakeet_nemo` and `ALLOW_LOCAL_ASR=true`
|
| 34 |
- `draft_checks.json`: evidence-gated checks for Parakeet provenance, confirmation status, and raw-audio handling
|
|
@@ -53,4 +64,4 @@ raw_audio_stored=false
|
|
| 53 |
asr_evidence_manifest_path=/tmp/figment-local-asr-artifact-only-manifest-check/asr_evidence_manifest.json
|
| 54 |
```
|
| 55 |
|
| 56 |
-
This means
|
|
|
|
| 17 |
|
| 18 |
Use the helper to capture local ASR evidence:
|
| 19 |
|
| 20 |
+
```bash
|
| 21 |
+
PYTHON_DOTENV_DISABLED=true \
|
| 22 |
+
python3 scripts/run_local_asr_evidence.py \
|
| 23 |
+
--audio <source-audio.wav> \
|
| 24 |
+
--transcribe-audio \
|
| 25 |
+
--provider-note "<device/runtime command>"
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
If a provider payload was captured by a separate local runtime, pass it directly:
|
| 29 |
+
|
| 30 |
```bash
|
| 31 |
PYTHON_DOTENV_DISABLED=true \
|
| 32 |
python3 scripts/run_local_asr_evidence.py \
|
|
|
|
| 39 |
|
| 40 |
- `artifact_metadata.json`: Parakeet `.nemo` presence, size, and hash
|
| 41 |
- `audio_metadata.json`: optional source-audio metadata and hash only; raw audio is not copied into the evidence bundle
|
| 42 |
+
- `provider_payload.json`: provider output generated by `--transcribe-audio`, when that path is used
|
| 43 |
- `provider_payload_metadata.json`: provider output file hash
|
| 44 |
- `audio_draft.json`: Figment draft generated from the provider payload under `AUDIO_BACKEND=parakeet_nemo` and `ALLOW_LOCAL_ASR=true`
|
| 45 |
- `draft_checks.json`: evidence-gated checks for Parakeet provenance, confirmation status, and raw-audio handling
|
|
|
|
| 64 |
asr_evidence_manifest_path=/tmp/figment-local-asr-artifact-only-manifest-check/asr_evidence_manifest.json
|
| 65 |
```
|
| 66 |
|
| 67 |
+
This means artifact presence alone remains insufficient. The app now has a Parakeet ASR adapter path; the proof upgrades only after a real audio run produces a provider payload and the helper records `counts_as_local_asr_proof=true`.
|
docs/model_parameter_evidence_ledger.md
CHANGED
|
@@ -6,11 +6,11 @@ Purpose: keep parameter, route, locality, adapter, ASR, and organizer-confirmati
|
|
| 6 |
|
| 7 |
## Current Claim Boundary
|
| 8 |
|
| 9 |
-
- Hosted Omni has measured eval evidence through the eval harness. The public Space is now
|
| 10 |
-
- The public Space target exists and
|
| 11 |
- The public model archive now records published measured Figment 4B LoRA merged artifacts through `figment_sft_v14p`. The strongest measured result is v14p repair-union on the corrected 150-case field-workflow holdout: 150/150 competence, 150/150 expected labels, 150/150 final validation, 0 deterministic patches, and 0 fallback. This is tuned model-system evidence, not no-cloud or clinical validation evidence.
|
| 12 |
- The local 4B + Parakeet route is still the preferred no-cloud/off-grid proof path. The full BF16 4B artifact and Parakeet artifact are present locally, and the tuned v14p GGUF is published, but no-cloud route proof and local ASR provider proof are still separate gates.
|
| 13 |
-
- Well-Tuned wording may cite the published measured v14p tuned artifacts if the submission/judging interpretation accepts the merged-model archive and local route support.
|
| 14 |
- Organizer confirmation is still needed for the Omni 31B body-count versus 33B sidebar ambiguity and for any additive local stack or adapter-count interpretation.
|
| 15 |
|
| 16 |
## Parameter Ledger
|
|
@@ -20,6 +20,7 @@ Purpose: keep parameter, route, locality, adapter, ASR, and organizer-confirmati
|
|
| 20 |
| Hosted Omni primary | `nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16`; API route `nvidia/nemotron-3-nano-omni-30b-a3b-reasoning` | NVIDIA model-card body: 31B total parameters; HF sidebar has been observed as 33B in planning docs | Roughly 3B active parameters per token is a runtime/MoE note, not the compliance number | None used in current evals | Native Omni speech encoder is part of the Omni model-card count; no separate ASR model is claimed for hosted Omni | Hosted NVIDIA API route in current evals; self-hosted no-cloud route not recorded | Pending: ask organizers whether model-card body count is acceptable if sidebar count differs | Baseline eval: 28/50 whole-output competence, 22/50 full fallback, 50/50 final validation. Follow-up eval: 31/50 competence, 8/50 full fallback, 480/650 model-retained fields, 170/650 deterministic patches, 50/50 final validation |
|
| 21 |
| Self-hosted Omni no-cloud target | `nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16`, FP8, or NVFP4 variant if served locally | Same Omni 31B body-count claim, with same 33B sidebar ambiguity | Active parameters do not decide compliance | None recorded | Native Omni audio if used locally; included in Omni count if organizers accept the model-card count | Would be local/self-hosted only if served with no runtime cloud APIs | Pending for count ambiguity and hardware/runtime proof | No recorded no-cloud eval or public demo trace yet. Do not claim Off the Grid achieved |
|
| 22 |
| Local 4B + Parakeet proof path | Text base: `nvidia/NVIDIA-Nemotron-3-Nano-4B-BF16`; tuned archive: `build-small-hackathon/figment-finetuned-model-archive`; ASR: `nvidia/parakeet-rnnt-1.1b`; local route `MODEL_BACKEND=llama_cpp`, `MODEL_STACK=local_4b_parakeet` | Workback model-card notes: 3.97B text model plus about 1.1B Parakeet, roughly 5.1B nominal before adapters | No active-parameter substitution; use additive total-count story if organizers require stack accounting | Published merged LoRA artifacts exist through `figment_sft_v14p`; exact adapter-only parameter count is not used as the primary public claim | About 1.1B for Parakeet RNNT ASR, only if real local ASR is enabled and proven | Intended local OpenAI-compatible endpoint serving the tuned GGUF plus local ASR; no-cloud only after recorded proof | Pending: confirm additive multi-model counting and adapter-count treatment | Full BF16 4B snapshot downloaded at repo revision `dfaf35de3e30f1867dd8dbc38a7fc9fb52d3914f`; `model.safetensors` is `7947142640` bytes with SHA-256 `55d4e2519456c4a9bddf596b0748d630e3b2ce6ff6f4c2b7ed3e07e2b00dad42`. Parakeet artifact downloaded at repo revision `a07b19e98a26c1873a3f2622c446a4a1ca6316cb`; `parakeet-rnnt-1.1b.nemo` is `4283105280` bytes with SHA-256 `535896f014953d945b287ac533560e20da8103c6781b152de4645528e2b60738`. The v14p repair-union measured result is 150/150 competence, 150/150 expected labels, 150/150 final validation, 0 deterministic patches, and 0 fallback on the corrected 150-case field-workflow holdout. No no-cloud local route proof, local ASR provider proof, or target-user trace is recorded yet |
|
|
|
|
| 23 |
| 4B Figment tuned archive | `build-small-hackathon/figment-finetuned-model-archive`, especially `figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/` and `figment_sft_v14p/figment-sft-v14p-lora-merged-bf16.bf16.gguf` | Base model is the 3.97B Nemotron 4B BF16 target; merged LoRA artifacts are derived from that base | Not applicable | Published as merged BF16 and GGUF artifacts with merge manifests; adapter-only count remains a documentation detail if needed by organizers | None for text-only eval | Public HF model repo plus local OpenAI-compatible app route support | Pending only for any organizer-specific adapter-count interpretation | Public model repo is `private=False`, current commit `7da772ec7c0de20011d42780ea8afa65af4aef70`, with v1 pilot plus v5-v14p artifacts. v14p model card records the repair-union result and GGUF SHA. |
|
| 24 |
| Canned fallback | No live model | Not a model-compliance artifact | Not applicable | Not applicable | Not applicable | Local deterministic fallback; public Space no-secret fallback verified | Not applicable | Useful for safety, deployment health, and cold-start fallback only. Cannot count as model competence, Off the Grid proof, Llama Champion proof, or Well-Tuned proof |
|
| 25 |
|
|
@@ -27,7 +28,7 @@ Purpose: keep parameter, route, locality, adapter, ASR, and organizer-confirmati
|
|
| 27 |
|
| 28 |
| Claim | Required upgrade evidence |
|
| 29 |
| --- | --- |
|
| 30 |
-
| Public Space runnable | Satisfied for
|
| 31 |
| Hosted model load-bearing | Cite hosted eval metrics separately from final validation: 31/50 whole-output competence and 480/650 model-retained fields in the follow-up run |
|
| 32 |
| <=32B hosted Omni compliance | Organizer accepts the 31B model-card body count or the submission falls back to a clearly eligible smaller route |
|
| 33 |
| Off the Grid | Recorded no-cloud run with trace evidence, either self-hosted Omni or local 4B + Parakeet/typed intake |
|
|
|
|
| 6 |
|
| 7 |
## Current Claim Boundary
|
| 8 |
|
| 9 |
+
- Hosted Omni has measured eval evidence through the eval harness. The public Space proof is now live HF ZeroGPU v14p serving evidence, but it is not live hosted Omni generation evidence.
|
| 10 |
+
- The public Space target exists and runs on HF ZeroGPU. Current public API evidence on 2026-06-15: `runtime.stage=RUNNING`, `hardware=zero-a10g`, `MODEL_BACKEND=hf_zerogpu`, `MODEL_STACK=local_4b_parakeet`, and a synthetic `/run_navigator` route with `raw_route=hf_zerogpu`, `final_route=model_with_deterministic_patches`, and `validation_status=passed`.
|
| 11 |
- The public model archive now records published measured Figment 4B LoRA merged artifacts through `figment_sft_v14p`. The strongest measured result is v14p repair-union on the corrected 150-case field-workflow holdout: 150/150 competence, 150/150 expected labels, 150/150 final validation, 0 deterministic patches, and 0 fallback. This is tuned model-system evidence, not no-cloud or clinical validation evidence.
|
| 12 |
- The local 4B + Parakeet route is still the preferred no-cloud/off-grid proof path. The full BF16 4B artifact and Parakeet artifact are present locally, and the tuned v14p GGUF is published, but no-cloud route proof and local ASR provider proof are still separate gates.
|
| 13 |
+
- Well-Tuned wording may cite the published measured v14p tuned artifacts if the submission/judging interpretation accepts the merged-model archive and HF ZeroGPU/local route support. Keep deterministic patch use visible when citing public Space route checks.
|
| 14 |
- Organizer confirmation is still needed for the Omni 31B body-count versus 33B sidebar ambiguity and for any additive local stack or adapter-count interpretation.
|
| 15 |
|
| 16 |
## Parameter Ledger
|
|
|
|
| 20 |
| Hosted Omni primary | `nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16`; API route `nvidia/nemotron-3-nano-omni-30b-a3b-reasoning` | NVIDIA model-card body: 31B total parameters; HF sidebar has been observed as 33B in planning docs | Roughly 3B active parameters per token is a runtime/MoE note, not the compliance number | None used in current evals | Native Omni speech encoder is part of the Omni model-card count; no separate ASR model is claimed for hosted Omni | Hosted NVIDIA API route in current evals; self-hosted no-cloud route not recorded | Pending: ask organizers whether model-card body count is acceptable if sidebar count differs | Baseline eval: 28/50 whole-output competence, 22/50 full fallback, 50/50 final validation. Follow-up eval: 31/50 competence, 8/50 full fallback, 480/650 model-retained fields, 170/650 deterministic patches, 50/50 final validation |
|
| 21 |
| Self-hosted Omni no-cloud target | `nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16`, FP8, or NVFP4 variant if served locally | Same Omni 31B body-count claim, with same 33B sidebar ambiguity | Active parameters do not decide compliance | None recorded | Native Omni audio if used locally; included in Omni count if organizers accept the model-card count | Would be local/self-hosted only if served with no runtime cloud APIs | Pending for count ambiguity and hardware/runtime proof | No recorded no-cloud eval or public demo trace yet. Do not claim Off the Grid achieved |
|
| 22 |
| Local 4B + Parakeet proof path | Text base: `nvidia/NVIDIA-Nemotron-3-Nano-4B-BF16`; tuned archive: `build-small-hackathon/figment-finetuned-model-archive`; ASR: `nvidia/parakeet-rnnt-1.1b`; local route `MODEL_BACKEND=llama_cpp`, `MODEL_STACK=local_4b_parakeet` | Workback model-card notes: 3.97B text model plus about 1.1B Parakeet, roughly 5.1B nominal before adapters | No active-parameter substitution; use additive total-count story if organizers require stack accounting | Published merged LoRA artifacts exist through `figment_sft_v14p`; exact adapter-only parameter count is not used as the primary public claim | About 1.1B for Parakeet RNNT ASR, only if real local ASR is enabled and proven | Intended local OpenAI-compatible endpoint serving the tuned GGUF plus local ASR; no-cloud only after recorded proof | Pending: confirm additive multi-model counting and adapter-count treatment | Full BF16 4B snapshot downloaded at repo revision `dfaf35de3e30f1867dd8dbc38a7fc9fb52d3914f`; `model.safetensors` is `7947142640` bytes with SHA-256 `55d4e2519456c4a9bddf596b0748d630e3b2ce6ff6f4c2b7ed3e07e2b00dad42`. Parakeet artifact downloaded at repo revision `a07b19e98a26c1873a3f2622c446a4a1ca6316cb`; `parakeet-rnnt-1.1b.nemo` is `4283105280` bytes with SHA-256 `535896f014953d945b287ac533560e20da8103c6781b152de4645528e2b60738`. The v14p repair-union measured result is 150/150 competence, 150/150 expected labels, 150/150 final validation, 0 deterministic patches, and 0 fallback on the corrected 150-case field-workflow holdout. No no-cloud local route proof, local ASR provider proof, or target-user trace is recorded yet |
|
| 23 |
+
| HF ZeroGPU v14p + Parakeet ASR Space route | Text model: `build-small-hackathon/figment-finetuned-model-archive` subfolder `figment_sft_v14p/figment-sft-v14p-lora-merged-bf16`; ASR model: `nvidia/parakeet-ctc-1.1b` when `PARAKEET_ASR_RUNTIME=transformers` | 4B tuned text route plus about 1.1B Parakeet CTC ASR | No active-parameter substitution | Published merged LoRA artifacts exist through `figment_sft_v14p` | About 1.1B for Parakeet CTC ASR on the Space route | Hosted HF ZeroGPU, not no-cloud/off-grid | Pending only for any organizer-specific additive stack interpretation | The text route is live on `zero-a10g`; Parakeet ASR is implemented as gated draft intake and should be cited only after a live audio-draft route check or evidence bundle is recorded |
|
| 24 |
| 4B Figment tuned archive | `build-small-hackathon/figment-finetuned-model-archive`, especially `figment_sft_v14p/figment-sft-v14p-lora-merged-bf16/` and `figment_sft_v14p/figment-sft-v14p-lora-merged-bf16.bf16.gguf` | Base model is the 3.97B Nemotron 4B BF16 target; merged LoRA artifacts are derived from that base | Not applicable | Published as merged BF16 and GGUF artifacts with merge manifests; adapter-only count remains a documentation detail if needed by organizers | None for text-only eval | Public HF model repo plus local OpenAI-compatible app route support | Pending only for any organizer-specific adapter-count interpretation | Public model repo is `private=False`, current commit `7da772ec7c0de20011d42780ea8afa65af4aef70`, with v1 pilot plus v5-v14p artifacts. v14p model card records the repair-union result and GGUF SHA. |
|
| 25 |
| Canned fallback | No live model | Not a model-compliance artifact | Not applicable | Not applicable | Not applicable | Local deterministic fallback; public Space no-secret fallback verified | Not applicable | Useful for safety, deployment health, and cold-start fallback only. Cannot count as model competence, Off the Grid proof, Llama Champion proof, or Well-Tuned proof |
|
| 26 |
|
|
|
|
| 28 |
|
| 29 |
| Claim | Required upgrade evidence |
|
| 30 |
| --- | --- |
|
| 31 |
+
| Public Space runnable | Satisfied for HF ZeroGPU v14p: app files present, Space is `RUNNING` on `zero-a10g`, `/runtime` reports `MODEL_BACKEND=hf_zerogpu`, and a synthetic route check preserves fallback/patch labeling. |
|
| 32 |
| Hosted model load-bearing | Cite hosted eval metrics separately from final validation: 31/50 whole-output competence and 480/650 model-retained fields in the follow-up run |
|
| 33 |
| <=32B hosted Omni compliance | Organizer accepts the 31B model-card body count or the submission falls back to a clearly eligible smaller route |
|
| 34 |
| Off the Grid | Recorded no-cloud run with trace evidence, either self-hosted Omni or local 4B + Parakeet/typed intake |
|
docs/submission_checklist.md
CHANGED
|
@@ -22,7 +22,7 @@ Primary tracker: [adversarial review action items](adversarial-review-action-ite
|
|
| 22 |
| No-cloud/off-grid trace | Proof needed before claiming Off the Grid achieved | Pending |
|
| 23 |
| Hosted Omni eval results | Measured | [hosted_omni_eval_results.md](hosted_omni_eval_results.md): 31/50 whole-output competence, 8/50 full fallback, 480/650 model-retained fields, 170/650 deterministic patches, 50/50 final validation in the follow-up run |
|
| 24 |
| 4B LoRA system eval results | Published and measured | v14p repair-union on the corrected 150-case field-workflow holdout: 150/150 competence, 150/150 expected labels, 150/150 final validation, 0 deterministic patches, 0 fallback; raw first-pass success is 146/150 and 4/150 close through focused model repair. This is model-system evidence, not no-cloud proof or clinical validation |
|
| 25 |
-
| Local 4B + Parakeet no-cloud evidence | Helpers ready; proof needed | Full BF16 4B snapshot and Parakeet `.nemo` artifact are present locally; `scripts/run_local_4b_evidence.py` captures endpoint metadata, route smoke, eval records, eval summary, and `eval_evidence_manifest.json` once the local endpoint is live; `scripts/run_local_asr_evidence.py` captures Parakeet ASR provider evidence and `asr_evidence_manifest.json`. Pending no-cloud 50-case eval and real local ASR proof |
|
| 26 |
| Parameter/evidence ledger | Present; organizer confirmation pending | [model_parameter_evidence_ledger.md](model_parameter_evidence_ledger.md) |
|
| 27 |
| Submission claim audit | Present | `make audit-claims` scans submission-facing copy for premature Off the Grid, Llama Champion, Well-Tuned, Backyard user-use, local 4B, local ASR, demo-video, and social-post claims |
|
| 28 |
| Evidence gate status report | Present; incomplete by design until external proofs exist | `make evidence-gates` reports each evidence gate, paths found, and next actions. Current missing gates are badge/demo/user-proof gates, not missing Hub repos: no-cloud route, Llama Champion route, local ASR provider proof, trained-responder user test, demo video, and social post |
|
|
@@ -52,7 +52,7 @@ The README may say the project is built for a real trained responder. It should
|
|
| 52 |
|
| 53 |
## Eval Evidence Boundary
|
| 54 |
|
| 55 |
-
The hosted Omni eval proves model and fallback behavior through the eval harness. The public Space proof currently proves
|
| 56 |
|
| 57 |
The 4B LoRA model archive proves a published, measured tuned local-model artifact. Use the v14p repair-union result as the current small-model score: 150/150 competence, 150/150 expected labels, 150/150 final validation, 0 deterministic patches, and 0 fallback on the corrected 150-case field-workflow holdout. Do not convert that into Off the Grid, local ASR, target-user, or clinical-validation evidence.
|
| 58 |
|
|
|
|
| 22 |
| No-cloud/off-grid trace | Proof needed before claiming Off the Grid achieved | Pending |
|
| 23 |
| Hosted Omni eval results | Measured | [hosted_omni_eval_results.md](hosted_omni_eval_results.md): 31/50 whole-output competence, 8/50 full fallback, 480/650 model-retained fields, 170/650 deterministic patches, 50/50 final validation in the follow-up run |
|
| 24 |
| 4B LoRA system eval results | Published and measured | v14p repair-union on the corrected 150-case field-workflow holdout: 150/150 competence, 150/150 expected labels, 150/150 final validation, 0 deterministic patches, 0 fallback; raw first-pass success is 146/150 and 4/150 close through focused model repair. This is model-system evidence, not no-cloud proof or clinical validation |
|
| 25 |
+
| Local 4B + Parakeet no-cloud evidence | Helpers ready; proof needed | Full BF16 4B snapshot and Parakeet `.nemo` artifact are present locally; `scripts/run_local_4b_evidence.py` captures endpoint metadata, route smoke, eval records, eval summary, and `eval_evidence_manifest.json` once the local endpoint is live; `scripts/run_local_asr_evidence.py --audio <source.wav> --transcribe-audio` captures Parakeet ASR provider evidence and `asr_evidence_manifest.json`. Pending no-cloud 50-case eval and real local ASR proof |
|
| 26 |
| Parameter/evidence ledger | Present; organizer confirmation pending | [model_parameter_evidence_ledger.md](model_parameter_evidence_ledger.md) |
|
| 27 |
| Submission claim audit | Present | `make audit-claims` scans submission-facing copy for premature Off the Grid, Llama Champion, Well-Tuned, Backyard user-use, local 4B, local ASR, demo-video, and social-post claims |
|
| 28 |
| Evidence gate status report | Present; incomplete by design until external proofs exist | `make evidence-gates` reports each evidence gate, paths found, and next actions. Current missing gates are badge/demo/user-proof gates, not missing Hub repos: no-cloud route, Llama Champion route, local ASR provider proof, trained-responder user test, demo video, and social post |
|
|
|
|
| 52 |
|
| 53 |
## Eval Evidence Boundary
|
| 54 |
|
| 55 |
+
The hosted Omni eval proves model and fallback behavior through the eval harness. The public Space proof currently proves live HF ZeroGPU v14p serving and deployment health, not hosted Omni generation. Use the follow-up hosted eval run as the current hosted eval score: 31/50 whole-output hosted competence, 8/50 full fallback, 480/650 model-retained fields, 170/650 deterministic patches, and 50/50 final validation.
|
| 56 |
|
| 57 |
The 4B LoRA model archive proves a published, measured tuned local-model artifact. Use the v14p repair-union result as the current small-model score: 150/150 competence, 150/150 expected labels, 150/150 final validation, 0 deterministic patches, and 0 fallback on the corrected 150-case field-workflow holdout. Do not convert that into Off the Grid, local ASR, target-user, or clinical-validation evidence.
|
| 58 |
|
figment/audio_intake.py
CHANGED
|
@@ -16,7 +16,6 @@ from .config import (
|
|
| 16 |
FigmentConfig,
|
| 17 |
NVIDIA_NEMOTRON_3_NANO_4B_BF16_MODEL_ID,
|
| 18 |
OMNI_MODEL_ID,
|
| 19 |
-
PARAKEET_ASR_MODEL_ID,
|
| 20 |
load_config,
|
| 21 |
)
|
| 22 |
from .schemas import AudioDraft, AudioFieldSuggestion
|
|
@@ -211,7 +210,7 @@ def draft_audio_intake(
|
|
| 211 |
audio_model_id = OMNI_MODEL_ID
|
| 212 |
draft_source = "omni_audio_provider"
|
| 213 |
elif provider_payload_used and audio_enabled and _is_local_parakeet_backend(config):
|
| 214 |
-
audio_model_id =
|
| 215 |
field_fill_model_id = _local_field_fill_model_id(config)
|
| 216 |
runtime = LOCAL_PARAKEET_RUNTIME
|
| 217 |
path = LOCAL_PARAKEET_PATH
|
|
|
|
| 16 |
FigmentConfig,
|
| 17 |
NVIDIA_NEMOTRON_3_NANO_4B_BF16_MODEL_ID,
|
| 18 |
OMNI_MODEL_ID,
|
|
|
|
| 19 |
load_config,
|
| 20 |
)
|
| 21 |
from .schemas import AudioDraft, AudioFieldSuggestion
|
|
|
|
| 210 |
audio_model_id = OMNI_MODEL_ID
|
| 211 |
draft_source = "omni_audio_provider"
|
| 212 |
elif provider_payload_used and audio_enabled and _is_local_parakeet_backend(config):
|
| 213 |
+
audio_model_id = config.audio_model_id
|
| 214 |
field_fill_model_id = _local_field_fill_model_id(config)
|
| 215 |
runtime = LOCAL_PARAKEET_RUNTIME
|
| 216 |
path = LOCAL_PARAKEET_PATH
|
figment/config.py
CHANGED
|
@@ -12,6 +12,7 @@ OMNI_FP8_MODEL_ID = "nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-FP8"
|
|
| 12 |
OMNI_NVFP4_MODEL_ID = "nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-NVFP4"
|
| 13 |
NVIDIA_NEMOTRON_3_NANO_4B_BF16_MODEL_ID = "nvidia/NVIDIA-Nemotron-3-Nano-4B-BF16"
|
| 14 |
PARAKEET_ASR_MODEL_ID = "nvidia/parakeet-rnnt-1.1b"
|
|
|
|
| 15 |
NVIDIA_OMNI_API_MODEL_ID = "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning"
|
| 16 |
NVIDIA_API_BASE_URL = "https://integrate.api.nvidia.com/v1"
|
| 17 |
FIGMENT_CANNED_MODEL_ID = "figment-canned-deterministic"
|
|
@@ -91,6 +92,8 @@ class FigmentConfig:
|
|
| 91 |
llama_base_url: str = "http://127.0.0.1:8001/v1"
|
| 92 |
zerogpu_model_repo: str = FIGMENT_V14P_MODEL_REPO
|
| 93 |
zerogpu_model_subfolder: str = FIGMENT_V14P_MODEL_SUBFOLDER
|
|
|
|
|
|
|
| 94 |
trace_dir: Path = Path("traces")
|
| 95 |
|
| 96 |
@classmethod
|
|
@@ -156,6 +159,9 @@ class FigmentConfig:
|
|
| 156 |
or FIGMENT_V14P_MODEL_REPO,
|
| 157 |
zerogpu_model_subfolder=os.getenv("ZEROGPU_MODEL_SUBFOLDER", FIGMENT_V14P_MODEL_SUBFOLDER).strip()
|
| 158 |
or FIGMENT_V14P_MODEL_SUBFOLDER,
|
|
|
|
|
|
|
|
|
|
| 159 |
trace_dir=Path(os.getenv("FIGMENT_TRACE_DIR", "traces").strip() or "traces"),
|
| 160 |
).validated()
|
| 161 |
|
|
@@ -179,6 +185,8 @@ class FigmentConfig:
|
|
| 179 |
errors.append("AUDIO_BACKEND=parakeet_nemo requires ALLOW_LOCAL_ASR=true")
|
| 180 |
if self.audio_backend == "parakeet_nemo" and self.model_stack != "local_4b_parakeet":
|
| 181 |
errors.append("AUDIO_BACKEND=parakeet_nemo requires MODEL_STACK=local_4b_parakeet")
|
|
|
|
|
|
|
| 182 |
errors.extend(
|
| 183 |
_mode_consistency_errors(
|
| 184 |
self.figment_mode,
|
|
@@ -211,7 +219,7 @@ class FigmentConfig:
|
|
| 211 |
if self.audio_backend == "omni_native":
|
| 212 |
return OMNI_MODEL_ID
|
| 213 |
if self.audio_backend == "parakeet_nemo":
|
| 214 |
-
return
|
| 215 |
return None
|
| 216 |
|
| 217 |
@property
|
|
|
|
| 12 |
OMNI_NVFP4_MODEL_ID = "nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-NVFP4"
|
| 13 |
NVIDIA_NEMOTRON_3_NANO_4B_BF16_MODEL_ID = "nvidia/NVIDIA-Nemotron-3-Nano-4B-BF16"
|
| 14 |
PARAKEET_ASR_MODEL_ID = "nvidia/parakeet-rnnt-1.1b"
|
| 15 |
+
PARAKEET_TRANSFORMERS_CTC_MODEL_ID = "nvidia/parakeet-ctc-1.1b"
|
| 16 |
NVIDIA_OMNI_API_MODEL_ID = "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning"
|
| 17 |
NVIDIA_API_BASE_URL = "https://integrate.api.nvidia.com/v1"
|
| 18 |
FIGMENT_CANNED_MODEL_ID = "figment-canned-deterministic"
|
|
|
|
| 92 |
llama_base_url: str = "http://127.0.0.1:8001/v1"
|
| 93 |
zerogpu_model_repo: str = FIGMENT_V14P_MODEL_REPO
|
| 94 |
zerogpu_model_subfolder: str = FIGMENT_V14P_MODEL_SUBFOLDER
|
| 95 |
+
parakeet_asr_model_id: str = PARAKEET_ASR_MODEL_ID
|
| 96 |
+
parakeet_asr_runtime: str = "auto"
|
| 97 |
trace_dir: Path = Path("traces")
|
| 98 |
|
| 99 |
@classmethod
|
|
|
|
| 159 |
or FIGMENT_V14P_MODEL_REPO,
|
| 160 |
zerogpu_model_subfolder=os.getenv("ZEROGPU_MODEL_SUBFOLDER", FIGMENT_V14P_MODEL_SUBFOLDER).strip()
|
| 161 |
or FIGMENT_V14P_MODEL_SUBFOLDER,
|
| 162 |
+
parakeet_asr_model_id=os.getenv("PARAKEET_ASR_MODEL_ID", PARAKEET_ASR_MODEL_ID).strip()
|
| 163 |
+
or PARAKEET_ASR_MODEL_ID,
|
| 164 |
+
parakeet_asr_runtime=os.getenv("PARAKEET_ASR_RUNTIME", "auto").strip().lower() or "auto",
|
| 165 |
trace_dir=Path(os.getenv("FIGMENT_TRACE_DIR", "traces").strip() or "traces"),
|
| 166 |
).validated()
|
| 167 |
|
|
|
|
| 185 |
errors.append("AUDIO_BACKEND=parakeet_nemo requires ALLOW_LOCAL_ASR=true")
|
| 186 |
if self.audio_backend == "parakeet_nemo" and self.model_stack != "local_4b_parakeet":
|
| 187 |
errors.append("AUDIO_BACKEND=parakeet_nemo requires MODEL_STACK=local_4b_parakeet")
|
| 188 |
+
if self.parakeet_asr_runtime not in {"auto", "nemo", "transformers"}:
|
| 189 |
+
errors.append("PARAKEET_ASR_RUNTIME must be one of ['auto', 'nemo', 'transformers']")
|
| 190 |
errors.extend(
|
| 191 |
_mode_consistency_errors(
|
| 192 |
self.figment_mode,
|
|
|
|
| 219 |
if self.audio_backend == "omni_native":
|
| 220 |
return OMNI_MODEL_ID
|
| 221 |
if self.audio_backend == "parakeet_nemo":
|
| 222 |
+
return self.parakeet_asr_model_id
|
| 223 |
return None
|
| 224 |
|
| 225 |
@property
|
figment/parakeet_asr.py
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Parakeet ASR adapters for Figment audio draft intake."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from functools import lru_cache
|
| 6 |
+
import os
|
| 7 |
+
from pathlib import Path
|
| 8 |
+
import threading
|
| 9 |
+
from typing import Any
|
| 10 |
+
from contextlib import suppress
|
| 11 |
+
import wave
|
| 12 |
+
|
| 13 |
+
from .config import FigmentConfig, PARAKEET_ASR_MODEL_ID
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
PARAKEET_TRANSFORMERS_CTC_MODEL_ID = "nvidia/parakeet-ctc-1.1b"
|
| 17 |
+
LOCAL_ASR_MAX_BYTES = int(os.getenv("PARAKEET_ASR_MAX_BYTES", str(25 * 1024 * 1024)))
|
| 18 |
+
LOCAL_ASR_MAX_SECONDS = float(os.getenv("PARAKEET_ASR_MAX_SECONDS", "120"))
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
try: # pragma: no cover - exercised on Hugging Face Spaces.
|
| 22 |
+
import spaces
|
| 23 |
+
except ImportError: # pragma: no cover - local tests run without spaces.
|
| 24 |
+
|
| 25 |
+
class _SpacesCompat:
|
| 26 |
+
@staticmethod
|
| 27 |
+
def GPU(**_kwargs: Any):
|
| 28 |
+
def decorator(func):
|
| 29 |
+
return func
|
| 30 |
+
|
| 31 |
+
return decorator
|
| 32 |
+
|
| 33 |
+
spaces = _SpacesCompat()
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
class ParakeetAsrError(RuntimeError):
|
| 37 |
+
"""Raised when Parakeet cannot produce a usable transcript."""
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
@spaces.GPU(
|
| 41 |
+
duration=int(os.getenv("PARAKEET_ASR_DURATION_SECONDS", os.getenv("ZEROGPU_DURATION_SECONDS", "60"))),
|
| 42 |
+
size=os.getenv("PARAKEET_ASR_SIZE", os.getenv("ZEROGPU_SIZE", "large")),
|
| 43 |
+
)
|
| 44 |
+
def transcribe_audio_with_parakeet(audio_file: str, *, config: FigmentConfig) -> dict[str, Any]:
|
| 45 |
+
"""Return a Figment provider payload from a local or ZeroGPU Parakeet ASR run."""
|
| 46 |
+
|
| 47 |
+
validate_parakeet_audio_file(audio_file)
|
| 48 |
+
model_id = getattr(config, "parakeet_asr_model_id", PARAKEET_ASR_MODEL_ID) or PARAKEET_ASR_MODEL_ID
|
| 49 |
+
runtime_name = getattr(config, "parakeet_asr_runtime", "auto") or "auto"
|
| 50 |
+
runtime = _runtime_for(runtime_name=runtime_name, model_id=model_id)
|
| 51 |
+
transcript = _clean_transcript(runtime.transcribe(audio_file))
|
| 52 |
+
if not transcript:
|
| 53 |
+
raise ParakeetAsrError("Parakeet ASR returned an empty transcript")
|
| 54 |
+
return {
|
| 55 |
+
"transcript": transcript,
|
| 56 |
+
"suggested_fields": [],
|
| 57 |
+
"missing_or_unclear_fields": [],
|
| 58 |
+
"provisional_red_flag_mentions": [],
|
| 59 |
+
"provider_metadata": {
|
| 60 |
+
"asr_model_id": model_id,
|
| 61 |
+
"asr_runtime": runtime.runtime_name,
|
| 62 |
+
"raw_audio_stored": False,
|
| 63 |
+
},
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def validate_parakeet_audio_file(audio_file: str) -> None:
|
| 68 |
+
path = Path(audio_file)
|
| 69 |
+
if not path.exists():
|
| 70 |
+
raise ParakeetAsrError(f"audio file does not exist: {audio_file}")
|
| 71 |
+
size = path.stat().st_size
|
| 72 |
+
if size > LOCAL_ASR_MAX_BYTES:
|
| 73 |
+
raise ParakeetAsrError(
|
| 74 |
+
f"audio file exceeds Parakeet ASR size limit: {size} bytes > {LOCAL_ASR_MAX_BYTES} bytes"
|
| 75 |
+
)
|
| 76 |
+
duration = _wav_duration_seconds(path)
|
| 77 |
+
if duration is not None and duration > LOCAL_ASR_MAX_SECONDS:
|
| 78 |
+
raise ParakeetAsrError(
|
| 79 |
+
f"audio file exceeds Parakeet ASR duration limit: {duration:.1f}s > {LOCAL_ASR_MAX_SECONDS:.1f}s"
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def parakeet_audio_limits_text() -> str:
|
| 84 |
+
mb = LOCAL_ASR_MAX_BYTES / (1024 * 1024)
|
| 85 |
+
return f"{mb:.0f} MB, {LOCAL_ASR_MAX_SECONDS:.0f} seconds"
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def _wav_duration_seconds(path: Path) -> float | None:
|
| 89 |
+
with suppress(wave.Error, OSError, EOFError):
|
| 90 |
+
with wave.open(str(path), "rb") as wav:
|
| 91 |
+
rate = wav.getframerate()
|
| 92 |
+
if not rate:
|
| 93 |
+
return None
|
| 94 |
+
return wav.getnframes() / rate
|
| 95 |
+
return None
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def _clean_transcript(value: Any) -> str:
|
| 99 |
+
if isinstance(value, str):
|
| 100 |
+
return value.strip()
|
| 101 |
+
return str(value or "").strip()
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
@lru_cache(maxsize=8)
|
| 105 |
+
def _runtime_for(*, runtime_name: str, model_id: str):
|
| 106 |
+
normalized = runtime_name.strip().lower()
|
| 107 |
+
if normalized not in {"auto", "nemo", "transformers"}:
|
| 108 |
+
raise ParakeetAsrError("PARAKEET_ASR_RUNTIME must be one of auto, nemo, transformers")
|
| 109 |
+
if normalized == "nemo":
|
| 110 |
+
return _NemoParakeetRuntime(model_id)
|
| 111 |
+
if normalized == "transformers":
|
| 112 |
+
return _TransformersParakeetRuntime(model_id)
|
| 113 |
+
if model_id == PARAKEET_ASR_MODEL_ID:
|
| 114 |
+
try:
|
| 115 |
+
return _NemoParakeetRuntime(model_id)
|
| 116 |
+
except ParakeetAsrError as exc:
|
| 117 |
+
raise ParakeetAsrError(
|
| 118 |
+
f"{model_id} is a NeMo .nemo checkpoint; install NeMo or set "
|
| 119 |
+
f"PARAKEET_ASR_MODEL_ID={PARAKEET_TRANSFORMERS_CTC_MODEL_ID} with "
|
| 120 |
+
"PARAKEET_ASR_RUNTIME=transformers"
|
| 121 |
+
) from exc
|
| 122 |
+
return _TransformersParakeetRuntime(model_id)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
class _NemoParakeetRuntime:
|
| 126 |
+
runtime_name = "nemo"
|
| 127 |
+
|
| 128 |
+
def __init__(self, model_id: str) -> None:
|
| 129 |
+
self.model_id = model_id
|
| 130 |
+
self.lock = threading.Lock()
|
| 131 |
+
try:
|
| 132 |
+
import nemo.collections.asr as nemo_asr
|
| 133 |
+
except Exception as exc: # pragma: no cover - NeMo is optional in local CI.
|
| 134 |
+
raise ParakeetAsrError(f"NeMo ASR is unavailable: {exc}") from exc
|
| 135 |
+
try:
|
| 136 |
+
if Path(model_id).exists():
|
| 137 |
+
self.model = nemo_asr.models.ASRModel.restore_from(restore_path=model_id)
|
| 138 |
+
else:
|
| 139 |
+
self.model = nemo_asr.models.ASRModel.from_pretrained(model_name=model_id)
|
| 140 |
+
with suppress(Exception):
|
| 141 |
+
self.model.eval()
|
| 142 |
+
except Exception as exc: # pragma: no cover - heavyweight model path.
|
| 143 |
+
raise ParakeetAsrError(f"failed to load NeMo Parakeet model {model_id}: {exc}") from exc
|
| 144 |
+
|
| 145 |
+
def transcribe(self, audio_file: str) -> str:
|
| 146 |
+
with self.lock:
|
| 147 |
+
try:
|
| 148 |
+
result = self.model.transcribe([audio_file])
|
| 149 |
+
except Exception as exc: # pragma: no cover - heavyweight model path.
|
| 150 |
+
raise ParakeetAsrError(f"NeMo Parakeet transcription failed: {exc}") from exc
|
| 151 |
+
return _transcription_text(result)
|
| 152 |
+
|
| 153 |
+
|
| 154 |
+
class _TransformersParakeetRuntime:
|
| 155 |
+
runtime_name = "transformers"
|
| 156 |
+
|
| 157 |
+
def __init__(self, model_id: str) -> None:
|
| 158 |
+
self.model_id = model_id
|
| 159 |
+
self.lock = threading.Lock()
|
| 160 |
+
try:
|
| 161 |
+
import torch
|
| 162 |
+
from transformers import pipeline
|
| 163 |
+
except Exception as exc:
|
| 164 |
+
raise ParakeetAsrError(f"Transformers ASR dependencies are unavailable: {exc}") from exc
|
| 165 |
+
device = 0 if torch.cuda.is_available() else -1
|
| 166 |
+
kwargs: dict[str, Any] = {"model": model_id, "device": device}
|
| 167 |
+
try:
|
| 168 |
+
self.pipeline = pipeline("automatic-speech-recognition", **kwargs)
|
| 169 |
+
except Exception as exc:
|
| 170 |
+
raise ParakeetAsrError(f"failed to load Transformers Parakeet model {model_id}: {exc}") from exc
|
| 171 |
+
|
| 172 |
+
def transcribe(self, audio_file: str) -> str:
|
| 173 |
+
with self.lock:
|
| 174 |
+
try:
|
| 175 |
+
result = self.pipeline(audio_file)
|
| 176 |
+
except Exception as exc:
|
| 177 |
+
raise ParakeetAsrError(f"Transformers Parakeet transcription failed: {exc}") from exc
|
| 178 |
+
return _transcription_text(result)
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def _transcription_text(result: Any) -> str:
|
| 182 |
+
if isinstance(result, list):
|
| 183 |
+
if not result:
|
| 184 |
+
return ""
|
| 185 |
+
return _transcription_text(result[0])
|
| 186 |
+
if isinstance(result, dict):
|
| 187 |
+
return _clean_transcript(result.get("text") or result.get("transcript"))
|
| 188 |
+
text = getattr(result, "text", None)
|
| 189 |
+
if text is not None:
|
| 190 |
+
return _clean_transcript(text)
|
| 191 |
+
return _clean_transcript(result)
|