@tryhamster/gerbil 1.12.0 → 1.13.0
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.
- package/README.md +25 -0
- package/dist/{architectures-BH_z6k9d.mjs → architectures-DHwj9AQD.mjs} +37 -27
- package/dist/architectures-DHwj9AQD.mjs.map +1 -0
- package/dist/cli.mjs +8 -8
- package/dist/cli.mjs.map +1 -1
- package/dist/{defaults-DfGx4d1m.mjs → defaults-B0aQZJTM.mjs} +3 -2
- package/dist/defaults-B0aQZJTM.mjs.map +1 -0
- package/dist/frameworks/express.mjs +1 -1
- package/dist/frameworks/fastify.mjs +1 -1
- package/dist/frameworks/hono.mjs +1 -1
- package/dist/frameworks/next.d.mts +2 -2
- package/dist/frameworks/next.mjs +1 -1
- package/dist/frameworks/trpc.mjs +1 -1
- package/dist/gerbil-Bw6do78d.mjs +4 -0
- package/dist/{gerbil-DF009Waa.d.mts → gerbil-CD_skiL3.d.mts} +2 -2
- package/dist/{gerbil-DF009Waa.d.mts.map → gerbil-CD_skiL3.d.mts.map} +1 -1
- package/dist/{gerbil-CpS3P240.mjs → gerbil-DAz_a4Jh.mjs} +22 -13
- package/dist/{gerbil-CpS3P240.mjs.map → gerbil-DAz_a4Jh.mjs.map} +1 -1
- package/dist/gpu/hooks.d.mts +1 -1
- package/dist/gpu/hooks.mjs +1 -1
- package/dist/gpu/index.d.mts +2 -2
- package/dist/gpu/index.mjs +5 -5
- package/dist/{gpu-DRFhiv4R.mjs → gpu-CU_Mldk0.mjs} +1275 -195
- package/dist/gpu-CU_Mldk0.mjs.map +1 -0
- package/dist/index-B3tjyDJI.d.mts.map +1 -1
- package/dist/{index-CNYoTRgr.d.mts → index-Fj2XkP-o.d.mts} +374 -3
- package/dist/index-Fj2XkP-o.d.mts.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +6 -6
- package/dist/integrations/ai-sdk.mjs +1 -1
- package/dist/integrations/langchain.mjs +1 -1
- package/dist/integrations/llamaindex.mjs +1 -1
- package/dist/integrations/mcp.d.mts +2 -2
- package/dist/integrations/mcp.mjs +4 -4
- package/dist/{mcp-DAbWO8VS.mjs → mcp-CdMvLQ_9.mjs} +3 -3
- package/dist/{mcp-DAbWO8VS.mjs.map → mcp-CdMvLQ_9.mjs.map} +1 -1
- package/dist/{moonshine-stt-B1kV5n1c.mjs → moonshine-stt-COJeK2Zb.mjs} +778 -3
- package/dist/moonshine-stt-COJeK2Zb.mjs.map +1 -0
- package/dist/moonshine-stt-Dp4j5JEB.mjs +4 -0
- package/dist/{one-liner-CmP9ktUn.mjs → one-liner-CMiGNWJ7.mjs} +2 -2
- package/dist/{one-liner-CmP9ktUn.mjs.map → one-liner-CMiGNWJ7.mjs.map} +1 -1
- package/dist/repl-BhaLCJFb.mjs +9 -0
- package/dist/skills/index.d.mts +2 -2
- package/dist/skills/index.mjs +3 -3
- package/dist/{skills-BDOEbHSx.mjs → skills-BXybFWlG.mjs} +2 -2
- package/dist/{skills-BDOEbHSx.mjs.map → skills-BXybFWlG.mjs.map} +1 -1
- package/dist/tune/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/architectures-BH_z6k9d.mjs.map +0 -1
- package/dist/defaults-DfGx4d1m.mjs.map +0 -1
- package/dist/gerbil-CZvoFo0T.mjs +0 -4
- package/dist/gpu-DRFhiv4R.mjs.map +0 -1
- package/dist/index-CNYoTRgr.d.mts.map +0 -1
- package/dist/moonshine-stt-B1kV5n1c.mjs.map +0 -1
- package/dist/moonshine-stt-DA1WuiZb.mjs +0 -4
- package/dist/repl-17NVeIaJ.mjs +0 -9
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { a as resolveDefaultRepo, i as isTTSRepo, n as OUTETTS_ASSETS, r as OUTETTS_PRESET_VOICES, t as DEFAULT_MODELS } from "./defaults-
|
|
2
|
-
import { A as kaniSinTensor, C as computeKaniPositions, D as kaniAttentionLayerIndices, E as generateNanoCodecDecoderGraph, F as CANONICAL_KEYS, I as DTYPE_BYTES, L as GEMMA4_VIS_KEYS, M as parseKaniConfig, N as DEFAULT_GROUP_SIZE, O as kaniCosTensor, S as buildKaniLayerCosSin, T as generateKaniTtsGraph, a as PARLER_DAC_LATENT_DIM, b as KANI_START_OF_HUMAN, c as PARLER_SAMPLE_RATE, d as generateParlerEncoderGraph, f as parseParlerConfig, i as PARLER_DAC_DECODER_DIM, k as kaniLayerAlpha, l as buildT5RelativeBias, o as PARLER_DECODER_RATES, p as revertDelayPattern, r as PARLER_BOS_TOKEN_ID, s as PARLER_EOS_TOKEN_ID, u as generateParlerDecoderGraph, x as audioTokensToCodes, y as KANI_END_OF_HUMAN } from "./architectures-
|
|
3
|
-
import { C as
|
|
1
|
+
import { a as resolveDefaultRepo, i as isTTSRepo, n as OUTETTS_ASSETS, r as OUTETTS_PRESET_VOICES, t as DEFAULT_MODELS } from "./defaults-B0aQZJTM.mjs";
|
|
2
|
+
import { A as kaniSinTensor, C as computeKaniPositions, D as kaniAttentionLayerIndices, E as generateNanoCodecDecoderGraph, F as CANONICAL_KEYS, I as DTYPE_BYTES, L as GEMMA4_VIS_KEYS, M as parseKaniConfig, N as DEFAULT_GROUP_SIZE, O as kaniCosTensor, S as buildKaniLayerCosSin, T as generateKaniTtsGraph, a as PARLER_DAC_LATENT_DIM, b as KANI_START_OF_HUMAN, c as PARLER_SAMPLE_RATE, d as generateParlerEncoderGraph, f as parseParlerConfig, i as PARLER_DAC_DECODER_DIM, k as kaniLayerAlpha, l as buildT5RelativeBias, o as PARLER_DECODER_RATES, p as revertDelayPattern, r as PARLER_BOS_TOKEN_ID, s as PARLER_EOS_TOKEN_ID, u as generateParlerDecoderGraph, x as audioTokensToCodes, y as KANI_END_OF_HUMAN } from "./architectures-DHwj9AQD.mjs";
|
|
3
|
+
import { C as createStorageBuffer, D as initGPU, E as getOrCreatePipeline, O as verifyGPU, S as createBindGroup, T as destroyBuffers, _ as fetchAdapter, a as loadKaniTTS, b as MATMUL_BIAS_F16C_SPEC, c as loadOuteTTS, d as quantizeKaniBackbone, f as remapPrunedToken, g as buildLoRADeltas, i as loadGepardTTS, l as loadParlerTTS, m as Executor, o as loadModel, r as createKeyMapperForArch, u as quantizeBackboneInt4, w as createUniformBuffer, x as clearPipelineCache, y as KERNEL_REGISTRY } from "./moonshine-stt-COJeK2Zb.mjs";
|
|
4
4
|
|
|
5
5
|
//#region src/gpu/architectures/gemma4_vision.ts
|
|
6
6
|
/**
|
|
@@ -767,7 +767,7 @@ const KANI_CODEC_LOOKBACK_FRAMES = 32;
|
|
|
767
767
|
/** Hard cap so a stuck decode cannot loop forever (reference uses 3000). */
|
|
768
768
|
const DEFAULT_MAX_NEW_TOKENS$1 = 3e3;
|
|
769
769
|
/** Build a fresh map of just the constant weights a graph references (see moonshine-stt). */
|
|
770
|
-
function selectGraphWeights$
|
|
770
|
+
function selectGraphWeights$3(graph, weights) {
|
|
771
771
|
const out = /* @__PURE__ */ new Map();
|
|
772
772
|
for (const [name, desc] of Object.entries(graph.tensors)) {
|
|
773
773
|
if (desc.storage !== "constant") continue;
|
|
@@ -891,7 +891,7 @@ var KaniTTS = class KaniTTS {
|
|
|
891
891
|
maxSeqLen,
|
|
892
892
|
kvMode: "f32"
|
|
893
893
|
});
|
|
894
|
-
exec.uploadWeightsMap(selectGraphWeights$
|
|
894
|
+
exec.uploadWeightsMap(selectGraphWeights$3(graph, loaded.backboneWeights));
|
|
895
895
|
exec.initBindGroups();
|
|
896
896
|
return exec;
|
|
897
897
|
}
|
|
@@ -1105,7 +1105,7 @@ var KaniTTS = class KaniTTS {
|
|
|
1105
1105
|
kvMode: "f32"
|
|
1106
1106
|
});
|
|
1107
1107
|
try {
|
|
1108
|
-
exec.uploadWeightsMap(selectGraphWeights$
|
|
1108
|
+
exec.uploadWeightsMap(selectGraphWeights$3(graph, this.loaded.codecWeights));
|
|
1109
1109
|
exec.initBindGroups();
|
|
1110
1110
|
exec.reset();
|
|
1111
1111
|
exec.writeInput("audio_codes", winCodes);
|
|
@@ -1580,213 +1580,1264 @@ var RequestScheduler = class {
|
|
|
1580
1580
|
anyAdapter = true;
|
|
1581
1581
|
}
|
|
1582
1582
|
}
|
|
1583
|
-
const last = this.lastLaneAdapterIds;
|
|
1584
|
-
if (last === null) {
|
|
1585
|
-
if (!anyAdapter) return;
|
|
1586
|
-
} else if (ids.every((id, b) => id === last[b])) return;
|
|
1587
|
-
this.host.executor.setBatchLaneAdapters(ids);
|
|
1588
|
-
this.lastLaneAdapterIds = ids;
|
|
1583
|
+
const last = this.lastLaneAdapterIds;
|
|
1584
|
+
if (last === null) {
|
|
1585
|
+
if (!anyAdapter) return;
|
|
1586
|
+
} else if (ids.every((id, b) => id === last[b])) return;
|
|
1587
|
+
this.host.executor.setBatchLaneAdapters(ids);
|
|
1588
|
+
this.lastLaneAdapterIds = ids;
|
|
1589
|
+
}
|
|
1590
|
+
/**
|
|
1591
|
+
* Admission: move the next queued request into a free slot when (a) a slot
|
|
1592
|
+
* is free, (b) no other prefill is staged (the single-sequence prefill path
|
|
1593
|
+
* stages SSM state in singleton buffers — one at a time), and (c) its
|
|
1594
|
+
* worst-case block need fits the unreserved KV pool with headroom.
|
|
1595
|
+
*/
|
|
1596
|
+
admit() {
|
|
1597
|
+
if (this.prefilling || this.queue.length === 0) return;
|
|
1598
|
+
const slot = this.slots.indexOf(null);
|
|
1599
|
+
if (slot === -1) return;
|
|
1600
|
+
const req = this.queue[0];
|
|
1601
|
+
const worstTokens = req.promptIds.length + req.maxTokens + PIPELINE_DEPTH;
|
|
1602
|
+
const blocksNeeded = Math.ceil(worstTokens / this.blockSize);
|
|
1603
|
+
const totalBlocks = this.host.executor.kvTotalBlocks;
|
|
1604
|
+
if (this.reservedBlocks + blocksNeeded + this.blockHeadroom > totalBlocks) return;
|
|
1605
|
+
this.queue.shift();
|
|
1606
|
+
req.state = "prefilling";
|
|
1607
|
+
req.slot = slot;
|
|
1608
|
+
req.blocksReserved = blocksNeeded;
|
|
1609
|
+
req.admittedAt = performance.now();
|
|
1610
|
+
this.reservedBlocks += blocksNeeded;
|
|
1611
|
+
this.slots[slot] = req;
|
|
1612
|
+
this.prefilling = req;
|
|
1613
|
+
if (req.adapterDeltas) {
|
|
1614
|
+
const { applied } = this.host.executor.applyRuntimeLoRA(req.adapterDeltas);
|
|
1615
|
+
if (applied === 0) throw new Error(`RequestScheduler: adapter "${req.adapterName}" resolved to 0 applicable prefill targets`);
|
|
1616
|
+
}
|
|
1617
|
+
this.host.executor.beginBatchSlot(slot);
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* Run one bounded prefill chunk for the staged request on the
|
|
1621
|
+
* single-sequence path (into its slot's block-table row via tableBase).
|
|
1622
|
+
* Intermediate chunks are submit-only; the final chunk reads logits, adopts
|
|
1623
|
+
* the singleton SSM state into the slot's pool row, seeds the lane's first
|
|
1624
|
+
* token, and flips the lane to decoding.
|
|
1625
|
+
*/
|
|
1626
|
+
async prefillChunkStep() {
|
|
1627
|
+
const req = this.prefilling;
|
|
1628
|
+
if (!req) return;
|
|
1629
|
+
const ex = this.host.executor;
|
|
1630
|
+
const end = Math.min(req.prefillPos + this.prefillChunk, req.promptIds.length);
|
|
1631
|
+
const isLast = end === req.promptIds.length;
|
|
1632
|
+
const chunk = req.promptIds.subarray(req.prefillPos, end);
|
|
1633
|
+
const { logits } = await ex.forward(chunk, { readLogits: isLast });
|
|
1634
|
+
req.prefillPos = end;
|
|
1635
|
+
this.stats.prefillChunks++;
|
|
1636
|
+
if (!isLast) return;
|
|
1637
|
+
ex.finishBatchSlot(req.slot);
|
|
1638
|
+
if (req.adapterDeltas) ex.clearRuntimeLoRA();
|
|
1639
|
+
this.prefilling = null;
|
|
1640
|
+
const first = this.host.remapToken(sampleToken(logits, { temperature: 0 }));
|
|
1641
|
+
ex.injectBatchToken(req.slot, first);
|
|
1642
|
+
req.state = "decoding";
|
|
1643
|
+
req.firstTokenAt = performance.now();
|
|
1644
|
+
this.consumeToken(req, first);
|
|
1645
|
+
}
|
|
1646
|
+
/** Read the oldest in-flight step and deliver its tokens. */
|
|
1647
|
+
async readStep() {
|
|
1648
|
+
const step = this.inFlight.shift();
|
|
1649
|
+
if (!step) return;
|
|
1650
|
+
const tokens = await this.host.executor.readBatchTokens(step.readbackSlot);
|
|
1651
|
+
for (const { slot, req } of step.rows) {
|
|
1652
|
+
if (this.slots[slot] !== req || req.state !== "decoding") continue;
|
|
1653
|
+
this.consumeToken(req, tokens[slot]);
|
|
1654
|
+
}
|
|
1655
|
+
}
|
|
1656
|
+
/** Append one generated token; finish + retire the lane when terminal. */
|
|
1657
|
+
consumeToken(req, tokenId) {
|
|
1658
|
+
req.generatedIds.push(tokenId);
|
|
1659
|
+
this.stats.tokensGenerated++;
|
|
1660
|
+
if (req.firstTokenAt === 0) req.firstTokenAt = performance.now();
|
|
1661
|
+
const { eosTokenId, eotTokenId } = this.host;
|
|
1662
|
+
if (eosTokenId !== null && tokenId === eosTokenId || eotTokenId !== null && tokenId === eotTokenId) {
|
|
1663
|
+
req.finishReason = "eos";
|
|
1664
|
+
this.finish(req);
|
|
1665
|
+
return;
|
|
1666
|
+
}
|
|
1667
|
+
const piece = this.host.decodeToken(tokenId);
|
|
1668
|
+
req.text += piece;
|
|
1669
|
+
if (req.stopSequences.some((s) => req.text.includes(s))) {
|
|
1670
|
+
for (const s of req.stopSequences) {
|
|
1671
|
+
const idx = req.text.indexOf(s);
|
|
1672
|
+
if (idx !== -1) req.text = req.text.slice(0, idx);
|
|
1673
|
+
}
|
|
1674
|
+
req.finishReason = "stop_sequence";
|
|
1675
|
+
this.finish(req);
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
req.onToken?.(piece, {
|
|
1679
|
+
requestId: req.id,
|
|
1680
|
+
tokenId,
|
|
1681
|
+
generated: req.generatedIds.length
|
|
1682
|
+
});
|
|
1683
|
+
if (req.generatedIds.length >= req.maxTokens) {
|
|
1684
|
+
req.finishReason = "max_tokens";
|
|
1685
|
+
this.finish(req);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
/** Retire the lane immediately: free KV blocks, open the slot, resolve. */
|
|
1689
|
+
finish(req) {
|
|
1690
|
+
req.state = "done";
|
|
1691
|
+
if (req.slot >= 0 && this.slots[req.slot] === req) {
|
|
1692
|
+
this.host.executor.retireBatchSlot(req.slot);
|
|
1693
|
+
this.slots[req.slot] = null;
|
|
1694
|
+
}
|
|
1695
|
+
this.reservedBlocks -= req.blocksReserved;
|
|
1696
|
+
this.stats.completed++;
|
|
1697
|
+
const finishedAt = performance.now();
|
|
1698
|
+
req.resolve({
|
|
1699
|
+
requestId: req.id,
|
|
1700
|
+
text: req.text,
|
|
1701
|
+
tokenIds: [...req.generatedIds],
|
|
1702
|
+
tokensGenerated: req.generatedIds.length,
|
|
1703
|
+
finishReason: req.finishReason,
|
|
1704
|
+
adapter: req.adapterName ?? void 0,
|
|
1705
|
+
submittedAt: req.submittedAt,
|
|
1706
|
+
admittedAt: req.admittedAt,
|
|
1707
|
+
firstTokenAt: req.firstTokenAt,
|
|
1708
|
+
finishedAt,
|
|
1709
|
+
ttftMs: req.firstTokenAt - req.submittedAt,
|
|
1710
|
+
e2eMs: finishedAt - req.submittedAt
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
};
|
|
1714
|
+
|
|
1715
|
+
//#endregion
|
|
1716
|
+
//#region src/gpu/batch/swarm.ts
|
|
1717
|
+
/** Default reduce: member-keyed map, disambiguating duplicated members. */
|
|
1718
|
+
function reduceToKeyedMap(results) {
|
|
1719
|
+
const out = {};
|
|
1720
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1721
|
+
for (const { member, result } of results) {
|
|
1722
|
+
const n = (seen.get(member) ?? 0) + 1;
|
|
1723
|
+
seen.set(member, n);
|
|
1724
|
+
out[n === 1 ? member : `${member}#${n}`] = result;
|
|
1725
|
+
}
|
|
1726
|
+
return out;
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* A swarm handle: registered members + plan/reduce over the engine's
|
|
1730
|
+
* scheduler. Create via {@link WebGPUEngine.createSwarm}.
|
|
1731
|
+
*/
|
|
1732
|
+
var Swarm = class {
|
|
1733
|
+
scheduler;
|
|
1734
|
+
memberSources;
|
|
1735
|
+
plan;
|
|
1736
|
+
reduce;
|
|
1737
|
+
constructor(scheduler, options) {
|
|
1738
|
+
if (Object.keys(options.members).length === 0) throw new Error("Swarm: members must contain at least one member");
|
|
1739
|
+
this.scheduler = scheduler;
|
|
1740
|
+
this.memberSources = { ...options.members };
|
|
1741
|
+
this.plan = options.plan;
|
|
1742
|
+
this.reduce = options.reduce;
|
|
1743
|
+
}
|
|
1744
|
+
/** Member names, declaration order. */
|
|
1745
|
+
get members() {
|
|
1746
|
+
return Object.keys(this.memberSources);
|
|
1747
|
+
}
|
|
1748
|
+
/**
|
|
1749
|
+
* Run one input through the swarm: plan → concurrent fan-out (one batched
|
|
1750
|
+
* pass across the scheduler's lanes) → reduce.
|
|
1751
|
+
*
|
|
1752
|
+
* @param input The task input handed to the plan (default plan: broadcast
|
|
1753
|
+
* it to every member as the prompt).
|
|
1754
|
+
* @param options Per-request options applied to every sub-task (a task's
|
|
1755
|
+
* own `options` win field-by-field).
|
|
1756
|
+
*/
|
|
1757
|
+
async run(input, options = {}) {
|
|
1758
|
+
const tasks = this.plan ? await this.plan(input, this.members) : this.members.map((member) => ({
|
|
1759
|
+
member,
|
|
1760
|
+
prompt: input
|
|
1761
|
+
}));
|
|
1762
|
+
if (tasks.length === 0) throw new Error("Swarm: plan produced no sub-tasks");
|
|
1763
|
+
const results = await Promise.all(tasks.map(async (task) => {
|
|
1764
|
+
const result = await this.submitFor(task.member, task.prompt, {
|
|
1765
|
+
...options,
|
|
1766
|
+
...task.options
|
|
1767
|
+
});
|
|
1768
|
+
return {
|
|
1769
|
+
member: task.member,
|
|
1770
|
+
result
|
|
1771
|
+
};
|
|
1772
|
+
}));
|
|
1773
|
+
if (this.reduce) return this.reduce(results);
|
|
1774
|
+
return reduceToKeyedMap(results);
|
|
1775
|
+
}
|
|
1776
|
+
/**
|
|
1777
|
+
* Mode-A access: decode one prompt with a single member (no plan/reduce).
|
|
1778
|
+
* Still rides the shared scheduler, so it batches with any concurrent work.
|
|
1779
|
+
*/
|
|
1780
|
+
generate(member, prompt, options = {}) {
|
|
1781
|
+
return this.submitFor(member, prompt, options);
|
|
1782
|
+
}
|
|
1783
|
+
submitFor(member, prompt, options) {
|
|
1784
|
+
if (!Object.hasOwn(this.memberSources, member)) throw new Error(`Swarm: unknown member "${member}" (members: ${this.members.join(", ")})`);
|
|
1785
|
+
const adapter = this.memberSources[member] == null ? void 0 : member;
|
|
1786
|
+
return this.scheduler.submit(prompt, {
|
|
1787
|
+
...options,
|
|
1788
|
+
adapter
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
};
|
|
1792
|
+
|
|
1793
|
+
//#endregion
|
|
1794
|
+
//#region src/gpu/architectures/gepard_tts.ts
|
|
1795
|
+
const GEPARD_START_OF_TEXT = 248073;
|
|
1796
|
+
const GEPARD_END_OF_TEXT = 248074;
|
|
1797
|
+
const GEPARD_START_OF_SPEECH = 248070;
|
|
1798
|
+
/** FSQ levels per dimension within each codec group (repeats per group). */
|
|
1799
|
+
const GEPARD_FSQ_LEVEL_PATTERN = [
|
|
1800
|
+
8,
|
|
1801
|
+
7,
|
|
1802
|
+
6,
|
|
1803
|
+
6
|
|
1804
|
+
];
|
|
1805
|
+
/** Number of codec groups (codebooks). */
|
|
1806
|
+
const GEPARD_NUM_GROUPS = 8;
|
|
1807
|
+
/** Total per-dimension channels per frame = groups × dims/group. */
|
|
1808
|
+
const GEPARD_NUM_CHANNELS = 32;
|
|
1809
|
+
/** NanoCodec 21.5 fps decoder geometry (codec_config.yaml, mlx-community mirror). */
|
|
1810
|
+
const GEPARD_CODEC_UP_SAMPLE_RATES = [
|
|
1811
|
+
8,
|
|
1812
|
+
8,
|
|
1813
|
+
4,
|
|
1814
|
+
2,
|
|
1815
|
+
2
|
|
1816
|
+
];
|
|
1817
|
+
const GEPARD_CODEC_INPUT_DIM = 32;
|
|
1818
|
+
/** PCM samples per audio frame (product of the up-sample rates). */
|
|
1819
|
+
const GEPARD_HOP = 1024;
|
|
1820
|
+
const GEPARD_SAMPLE_RATE = 22050;
|
|
1821
|
+
/** Per-channel FSQ cardinalities in head order (channel c = group⌊c/4⌋, dim c%4). */
|
|
1822
|
+
function gepardChannelLevels() {
|
|
1823
|
+
const out = [];
|
|
1824
|
+
for (let g = 0; g < GEPARD_NUM_GROUPS; g++) out.push(...GEPARD_FSQ_LEVEL_PATTERN);
|
|
1825
|
+
return out;
|
|
1826
|
+
}
|
|
1827
|
+
/** Matches `level_audio_{i}` head names (channel index = numeric suffix). */
|
|
1828
|
+
const LEVEL_AUDIO_RE = /^level_audio_(\d+)$/;
|
|
1829
|
+
/**
|
|
1830
|
+
* Parse gepard_config.json. Head order is recovered from the numeric suffix of
|
|
1831
|
+
* each `level_audio_{i}` key (NOT the serialized key order), so the wiring is
|
|
1832
|
+
* immune to any JSON round-trip that re-sorts keys.
|
|
1833
|
+
*/
|
|
1834
|
+
function parseGepardConfig(raw) {
|
|
1835
|
+
const backboneConfig = raw.backbone_config;
|
|
1836
|
+
if (!backboneConfig) throw new Error("gepard_config.json has no backbone_config — not a Gepard checkpoint.");
|
|
1837
|
+
const audioHeads = raw.audio_heads ?? {};
|
|
1838
|
+
const byIndex = [];
|
|
1839
|
+
for (const [name, size] of Object.entries(audioHeads)) {
|
|
1840
|
+
const m = LEVEL_AUDIO_RE.exec(name);
|
|
1841
|
+
if (!m) throw new Error(`Unexpected audio head name "${name}" in gepard_config.json.`);
|
|
1842
|
+
byIndex[Number(m[1])] = size;
|
|
1843
|
+
}
|
|
1844
|
+
if (byIndex.length === 0) throw new Error("gepard_config.json has no audio_heads.");
|
|
1845
|
+
for (let i = 0; i < byIndex.length; i++) if (byIndex[i] == null) throw new Error(`gepard_config.json audio_heads is missing level_audio_${i}.`);
|
|
1846
|
+
const special = raw.special_tokens ?? {};
|
|
1847
|
+
const rep = raw.text_repetition ?? {};
|
|
1848
|
+
const codec = raw.codec ?? {};
|
|
1849
|
+
return {
|
|
1850
|
+
backboneConfig,
|
|
1851
|
+
vocabSizes: byIndex,
|
|
1852
|
+
audioEmbedDim: raw.audio_embed_dim ?? 32,
|
|
1853
|
+
startOfText: special.start_of_text ?? GEPARD_START_OF_TEXT,
|
|
1854
|
+
endOfText: special.end_of_text ?? GEPARD_END_OF_TEXT,
|
|
1855
|
+
startOfSpeech: special.start_of_speech ?? GEPARD_START_OF_SPEECH,
|
|
1856
|
+
textRepetition: {
|
|
1857
|
+
enabled: rep.enabled ?? false,
|
|
1858
|
+
targetTextTokens: rep.target_text_tokens ?? 16,
|
|
1859
|
+
applyBelow: rep.apply_below ?? 13,
|
|
1860
|
+
maxRepeats: rep.max_repeats ?? 8
|
|
1861
|
+
},
|
|
1862
|
+
codecId: codec.codec_id ?? "nvidia/nemo-nano-codec-22khz-1.89kbps-21.5fps",
|
|
1863
|
+
fsqLevels: codec.fsq_levels ?? [...GEPARD_FSQ_LEVEL_PATTERN],
|
|
1864
|
+
sampleRate: codec.sample_rate ?? GEPARD_SAMPLE_RATE
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
/**
|
|
1868
|
+
* Build the (possibly repeated) text-id layout — mirrors the reference
|
|
1869
|
+
* TextRepeater byte-for-byte (text_repetition.py):
|
|
1870
|
+
* R == 1 → [ SOT, *text, EOT, SOS ]
|
|
1871
|
+
* R > 1 → [ SOT, *text, EOT ] × (R−1) + [ SOT, *text, EOT, SOS ]
|
|
1872
|
+
* Only the final (canonical) copy carries SOS. R is deterministic from the
|
|
1873
|
+
* text-token count: ceil(target / n) capped at maxRepeats, applied only when
|
|
1874
|
+
* n < applyBelow (short prompts derail without the extra text mass).
|
|
1875
|
+
*/
|
|
1876
|
+
function buildGepardTextLayout(textTokenIds, cfg) {
|
|
1877
|
+
const rep = cfg.textRepetition;
|
|
1878
|
+
const n = textTokenIds.length;
|
|
1879
|
+
let repeats = 1;
|
|
1880
|
+
if (rep.enabled && n > 0 && n < rep.applyBelow && n < rep.targetTextTokens) repeats = Math.max(1, Math.min(Math.ceil(rep.targetTextTokens / n), rep.maxRepeats));
|
|
1881
|
+
const block = [
|
|
1882
|
+
cfg.startOfText,
|
|
1883
|
+
...textTokenIds,
|
|
1884
|
+
cfg.endOfText
|
|
1885
|
+
];
|
|
1886
|
+
const out = [];
|
|
1887
|
+
for (let r = 0; r < repeats - 1; r++) out.push(...block);
|
|
1888
|
+
out.push(...block, cfg.startOfSpeech);
|
|
1889
|
+
return out;
|
|
1890
|
+
}
|
|
1891
|
+
/**
|
|
1892
|
+
* Dequantize per-channel FSQ codes [32, T] (channel-major) into the decoder
|
|
1893
|
+
* latent [32, T] f32: value = (code − L//2) / (L//2), matching
|
|
1894
|
+
* UnfoldedCodecModel.decode_from_codes / codec_ops.dequantize_codes.
|
|
1895
|
+
*/
|
|
1896
|
+
function gepardCodesToLatent(codes, numFrames) {
|
|
1897
|
+
const levels = gepardChannelLevels();
|
|
1898
|
+
const latent = new Float32Array(GEPARD_NUM_CHANNELS * numFrames);
|
|
1899
|
+
for (let c = 0; c < GEPARD_NUM_CHANNELS; c++) {
|
|
1900
|
+
const scale = Math.max(1, Math.floor(levels[c] / 2));
|
|
1901
|
+
const base = c * numFrames;
|
|
1902
|
+
for (let t = 0; t < numFrames; t++) latent[base + t] = (codes[base + t] - scale) / scale;
|
|
1903
|
+
}
|
|
1904
|
+
return latent;
|
|
1905
|
+
}
|
|
1906
|
+
function generateGepardBackboneGraph(backboneConfig, dtype, groupSizeOverride, kvDtype) {
|
|
1907
|
+
const hidden_size = backboneConfig.hidden_size;
|
|
1908
|
+
const num_layers = backboneConfig.num_hidden_layers;
|
|
1909
|
+
const num_heads = backboneConfig.num_attention_heads;
|
|
1910
|
+
const num_kv_heads = backboneConfig.num_key_value_heads ?? num_heads;
|
|
1911
|
+
const intermediate_size = backboneConfig.intermediate_size;
|
|
1912
|
+
const context_length = backboneConfig.max_position_embeddings ?? 262144;
|
|
1913
|
+
const rms_norm_eps = backboneConfig.rms_norm_eps ?? 1e-6;
|
|
1914
|
+
const head_dim = backboneConfig.head_dim ?? Math.floor(hidden_size / num_heads);
|
|
1915
|
+
const q_dim = num_heads * head_dim;
|
|
1916
|
+
const kv_dim = num_kv_heads * head_dim;
|
|
1917
|
+
const ropeParams = backboneConfig.rope_parameters;
|
|
1918
|
+
const rope_base = ropeParams?.rope_theta ?? backboneConfig.rope_theta ?? 1e7;
|
|
1919
|
+
const partialRotaryFactor = ropeParams?.partial_rotary_factor ?? backboneConfig.partial_rotary_factor ?? 1;
|
|
1920
|
+
const rope_dim = Math.floor(head_dim * partialRotaryFactor);
|
|
1921
|
+
const layerTypes = backboneConfig.layer_types ?? [];
|
|
1922
|
+
for (let i = 0; i < num_layers; i++) if (layerTypes.length > i && layerTypes[i] !== "full_attention") throw new Error(`Gepard backbone expects all-full-attention layers; layer ${i} is "${layerTypes[i]}".`);
|
|
1923
|
+
const config = {
|
|
1924
|
+
hidden_size,
|
|
1925
|
+
num_layers,
|
|
1926
|
+
num_heads,
|
|
1927
|
+
num_kv_heads,
|
|
1928
|
+
head_dim,
|
|
1929
|
+
intermediate_size,
|
|
1930
|
+
vocab_size: hidden_size,
|
|
1931
|
+
context_length,
|
|
1932
|
+
rms_norm_eps,
|
|
1933
|
+
norm_type: "rmsnorm",
|
|
1934
|
+
rope_base,
|
|
1935
|
+
rope_dim,
|
|
1936
|
+
kv_layout: "LHSd",
|
|
1937
|
+
is_moe: false,
|
|
1938
|
+
has_vision_tower: false
|
|
1939
|
+
};
|
|
1940
|
+
const capabilities = {
|
|
1941
|
+
text: true,
|
|
1942
|
+
vision: false,
|
|
1943
|
+
moe: false
|
|
1944
|
+
};
|
|
1945
|
+
const tensors = {};
|
|
1946
|
+
const nodes = [];
|
|
1947
|
+
const executionOrder = [];
|
|
1948
|
+
const addTensor = (desc) => {
|
|
1949
|
+
tensors[desc.name] = desc;
|
|
1950
|
+
};
|
|
1951
|
+
const addNode = (node) => {
|
|
1952
|
+
nodes.push(node);
|
|
1953
|
+
executionOrder.push(node.id);
|
|
1954
|
+
};
|
|
1955
|
+
const useQ4 = dtype === "q4";
|
|
1956
|
+
const groupSize = groupSizeOverride ?? DEFAULT_GROUP_SIZE;
|
|
1957
|
+
const addLinearOp = (id, activationInput, weightName, weightShape, outputName, K, N) => {
|
|
1958
|
+
if (useQ4) {
|
|
1959
|
+
const wQ = `${weightName}.q`;
|
|
1960
|
+
const wScales = `${weightName}.scales`;
|
|
1961
|
+
const wZeros = `${weightName}.zeros`;
|
|
1962
|
+
const totalElements = K * N;
|
|
1963
|
+
addTensor({
|
|
1964
|
+
name: wQ,
|
|
1965
|
+
shape: [Math.ceil(totalElements / 8)],
|
|
1966
|
+
dtype: "u32",
|
|
1967
|
+
storage: "constant"
|
|
1968
|
+
});
|
|
1969
|
+
addTensor({
|
|
1970
|
+
name: wScales,
|
|
1971
|
+
shape: [Math.ceil(totalElements / groupSize)],
|
|
1972
|
+
dtype: "f32",
|
|
1973
|
+
storage: "constant"
|
|
1974
|
+
});
|
|
1975
|
+
addTensor({
|
|
1976
|
+
name: wZeros,
|
|
1977
|
+
shape: [Math.ceil(totalElements / groupSize)],
|
|
1978
|
+
dtype: "f32",
|
|
1979
|
+
storage: "constant"
|
|
1980
|
+
});
|
|
1981
|
+
addNode({
|
|
1982
|
+
id,
|
|
1983
|
+
opType: "MatMulInt4",
|
|
1984
|
+
inputs: [
|
|
1985
|
+
activationInput,
|
|
1986
|
+
wQ,
|
|
1987
|
+
wScales,
|
|
1988
|
+
wZeros
|
|
1989
|
+
],
|
|
1990
|
+
outputs: [outputName],
|
|
1991
|
+
attributes: {
|
|
1992
|
+
M_tensor: activationInput,
|
|
1993
|
+
K,
|
|
1994
|
+
N,
|
|
1995
|
+
group_size: groupSize
|
|
1996
|
+
}
|
|
1997
|
+
});
|
|
1998
|
+
return;
|
|
1999
|
+
}
|
|
2000
|
+
addTensor({
|
|
2001
|
+
name: weightName,
|
|
2002
|
+
shape: weightShape,
|
|
2003
|
+
dtype: "f32",
|
|
2004
|
+
storage: "constant",
|
|
2005
|
+
safetensorsKey: weightName
|
|
2006
|
+
});
|
|
2007
|
+
addNode({
|
|
2008
|
+
id,
|
|
2009
|
+
opType: "MatMul",
|
|
2010
|
+
inputs: [activationInput, weightName],
|
|
2011
|
+
outputs: [outputName],
|
|
2012
|
+
attributes: {
|
|
2013
|
+
M_tensor: activationInput,
|
|
2014
|
+
K,
|
|
2015
|
+
N
|
|
2016
|
+
}
|
|
2017
|
+
});
|
|
2018
|
+
};
|
|
2019
|
+
addTensor({
|
|
2020
|
+
name: "inputs_embeds",
|
|
2021
|
+
shape: ["T", hidden_size],
|
|
2022
|
+
dtype: "f32",
|
|
2023
|
+
storage: "activation"
|
|
2024
|
+
});
|
|
2025
|
+
const buildAttentionBlock = (i, prefix, norm1Out) => {
|
|
2026
|
+
const qOut = `${prefix}_q`;
|
|
2027
|
+
const gateOut = `${prefix}_attn_gate`;
|
|
2028
|
+
const kOut = `${prefix}_k`;
|
|
2029
|
+
const vOut = `${prefix}_v`;
|
|
2030
|
+
addTensor({
|
|
2031
|
+
name: qOut,
|
|
2032
|
+
shape: ["T", q_dim],
|
|
2033
|
+
dtype: "f32",
|
|
2034
|
+
storage: "activation"
|
|
2035
|
+
});
|
|
2036
|
+
addTensor({
|
|
2037
|
+
name: gateOut,
|
|
2038
|
+
shape: ["T", q_dim],
|
|
2039
|
+
dtype: "f32",
|
|
2040
|
+
storage: "activation"
|
|
2041
|
+
});
|
|
2042
|
+
addTensor({
|
|
2043
|
+
name: kOut,
|
|
2044
|
+
shape: ["T", kv_dim],
|
|
2045
|
+
dtype: "f32",
|
|
2046
|
+
storage: "activation"
|
|
2047
|
+
});
|
|
2048
|
+
addTensor({
|
|
2049
|
+
name: vOut,
|
|
2050
|
+
shape: ["T", kv_dim],
|
|
2051
|
+
dtype: "f32",
|
|
2052
|
+
storage: "activation"
|
|
2053
|
+
});
|
|
2054
|
+
addLinearOp(`${prefix}_q_proj`, norm1Out, CANONICAL_KEYS.qProj(i), [q_dim, hidden_size], qOut, hidden_size, q_dim);
|
|
2055
|
+
addLinearOp(`${prefix}_gate_proj`, norm1Out, CANONICAL_KEYS.attnGate(i), [q_dim, hidden_size], gateOut, hidden_size, q_dim);
|
|
2056
|
+
addLinearOp(`${prefix}_k_proj`, norm1Out, CANONICAL_KEYS.kProj(i), [kv_dim, hidden_size], kOut, hidden_size, kv_dim);
|
|
2057
|
+
addLinearOp(`${prefix}_v_proj`, norm1Out, CANONICAL_KEYS.vProj(i), [kv_dim, hidden_size], vOut, hidden_size, kv_dim);
|
|
2058
|
+
const qNormW = CANONICAL_KEYS.qNorm(i);
|
|
2059
|
+
const kNormW = CANONICAL_KEYS.kNorm(i);
|
|
2060
|
+
addTensor({
|
|
2061
|
+
name: qNormW,
|
|
2062
|
+
shape: [head_dim],
|
|
2063
|
+
dtype: "f32",
|
|
2064
|
+
storage: "constant",
|
|
2065
|
+
safetensorsKey: qNormW
|
|
2066
|
+
});
|
|
2067
|
+
addTensor({
|
|
2068
|
+
name: kNormW,
|
|
2069
|
+
shape: [head_dim],
|
|
2070
|
+
dtype: "f32",
|
|
2071
|
+
storage: "constant",
|
|
2072
|
+
safetensorsKey: kNormW
|
|
2073
|
+
});
|
|
2074
|
+
const qNormed = `${prefix}_q_normed`;
|
|
2075
|
+
const kNormed = `${prefix}_k_normed`;
|
|
2076
|
+
addTensor({
|
|
2077
|
+
name: qNormed,
|
|
2078
|
+
shape: ["T", q_dim],
|
|
2079
|
+
dtype: "f32",
|
|
2080
|
+
storage: "activation"
|
|
2081
|
+
});
|
|
2082
|
+
addTensor({
|
|
2083
|
+
name: kNormed,
|
|
2084
|
+
shape: ["T", kv_dim],
|
|
2085
|
+
dtype: "f32",
|
|
2086
|
+
storage: "activation"
|
|
2087
|
+
});
|
|
2088
|
+
addNode({
|
|
2089
|
+
id: `${prefix}_q_norm`,
|
|
2090
|
+
opType: "RMSNorm",
|
|
2091
|
+
inputs: [qOut, qNormW],
|
|
2092
|
+
outputs: [qNormed],
|
|
2093
|
+
attributes: {
|
|
2094
|
+
hidden_size: head_dim,
|
|
2095
|
+
eps: rms_norm_eps,
|
|
2096
|
+
seq_len_tensor: qOut
|
|
2097
|
+
}
|
|
2098
|
+
});
|
|
2099
|
+
addNode({
|
|
2100
|
+
id: `${prefix}_k_norm`,
|
|
2101
|
+
opType: "RMSNorm",
|
|
2102
|
+
inputs: [kOut, kNormW],
|
|
2103
|
+
outputs: [kNormed],
|
|
2104
|
+
attributes: {
|
|
2105
|
+
hidden_size: head_dim,
|
|
2106
|
+
eps: rms_norm_eps,
|
|
2107
|
+
seq_len_tensor: kOut
|
|
2108
|
+
}
|
|
2109
|
+
});
|
|
2110
|
+
addNode({
|
|
2111
|
+
id: `${prefix}_rope`,
|
|
2112
|
+
opType: "RoPE",
|
|
2113
|
+
inputs: [qNormed, kNormed],
|
|
2114
|
+
outputs: [qNormed, kNormed],
|
|
2115
|
+
attributes: {
|
|
2116
|
+
head_dim,
|
|
2117
|
+
num_q_heads: num_heads,
|
|
2118
|
+
num_kv_heads,
|
|
2119
|
+
rope_base,
|
|
2120
|
+
rope_dim,
|
|
2121
|
+
partial_rotary: true,
|
|
2122
|
+
seq_len_tensor: qNormed
|
|
2123
|
+
}
|
|
2124
|
+
});
|
|
2125
|
+
const kvCacheDtype = kvDtype ?? "f32";
|
|
2126
|
+
const kCache = `${prefix}_k_cache`;
|
|
2127
|
+
const vCache = `${prefix}_v_cache`;
|
|
2128
|
+
addTensor({
|
|
2129
|
+
name: kCache,
|
|
2130
|
+
shape: ["L_max", kv_dim],
|
|
2131
|
+
dtype: kvCacheDtype,
|
|
2132
|
+
storage: "kv_cache"
|
|
2133
|
+
});
|
|
2134
|
+
addTensor({
|
|
2135
|
+
name: vCache,
|
|
2136
|
+
shape: ["L_max", kv_dim],
|
|
2137
|
+
dtype: kvCacheDtype,
|
|
2138
|
+
storage: "kv_cache"
|
|
2139
|
+
});
|
|
2140
|
+
addNode({
|
|
2141
|
+
id: `${prefix}_kv_cache_k`,
|
|
2142
|
+
opType: "KVCacheAppend",
|
|
2143
|
+
inputs: [kNormed],
|
|
2144
|
+
outputs: [kCache],
|
|
2145
|
+
attributes: {
|
|
2146
|
+
width: kv_dim,
|
|
2147
|
+
T_tensor: kNormed
|
|
2148
|
+
}
|
|
2149
|
+
});
|
|
2150
|
+
addNode({
|
|
2151
|
+
id: `${prefix}_kv_cache_v`,
|
|
2152
|
+
opType: "KVCacheAppend",
|
|
2153
|
+
inputs: [vOut],
|
|
2154
|
+
outputs: [vCache],
|
|
2155
|
+
attributes: {
|
|
2156
|
+
width: kv_dim,
|
|
2157
|
+
T_tensor: vOut
|
|
2158
|
+
}
|
|
2159
|
+
});
|
|
2160
|
+
const attnOut = `${prefix}_attn_out`;
|
|
2161
|
+
addTensor({
|
|
2162
|
+
name: attnOut,
|
|
2163
|
+
shape: ["T", q_dim],
|
|
2164
|
+
dtype: "f32",
|
|
2165
|
+
storage: "activation"
|
|
2166
|
+
});
|
|
2167
|
+
addNode({
|
|
2168
|
+
id: `${prefix}_attn`,
|
|
2169
|
+
opType: "Attention",
|
|
2170
|
+
inputs: [
|
|
2171
|
+
qNormed,
|
|
2172
|
+
kCache,
|
|
2173
|
+
vCache
|
|
2174
|
+
],
|
|
2175
|
+
outputs: [attnOut],
|
|
2176
|
+
attributes: {
|
|
2177
|
+
hidden_size: q_dim,
|
|
2178
|
+
num_q_heads: num_heads,
|
|
2179
|
+
num_kv_heads,
|
|
2180
|
+
head_dim,
|
|
2181
|
+
causal: true,
|
|
2182
|
+
layer_index: i
|
|
2183
|
+
}
|
|
2184
|
+
});
|
|
2185
|
+
const gatedOut = `${prefix}_gated`;
|
|
2186
|
+
addTensor({
|
|
2187
|
+
name: gatedOut,
|
|
2188
|
+
shape: ["T", q_dim],
|
|
2189
|
+
dtype: "f32",
|
|
2190
|
+
storage: "activation"
|
|
2191
|
+
});
|
|
2192
|
+
addNode({
|
|
2193
|
+
id: `${prefix}_sigmoid_gate`,
|
|
2194
|
+
opType: "SigmoidGate",
|
|
2195
|
+
inputs: [attnOut, gateOut],
|
|
2196
|
+
outputs: [gatedOut],
|
|
2197
|
+
attributes: { count_tensor: attnOut }
|
|
2198
|
+
});
|
|
2199
|
+
const oProjOut = `${prefix}_o_proj_out`;
|
|
2200
|
+
addTensor({
|
|
2201
|
+
name: oProjOut,
|
|
2202
|
+
shape: ["T", hidden_size],
|
|
2203
|
+
dtype: "f32",
|
|
2204
|
+
storage: "activation"
|
|
2205
|
+
});
|
|
2206
|
+
addLinearOp(`${prefix}_o_proj`, gatedOut, CANONICAL_KEYS.oProj(i), [hidden_size, q_dim], oProjOut, q_dim, hidden_size);
|
|
2207
|
+
return oProjOut;
|
|
2208
|
+
};
|
|
2209
|
+
let prevOutput = "inputs_embeds";
|
|
2210
|
+
let pendingNorm1Out = null;
|
|
2211
|
+
for (let i = 0; i < num_layers; i++) {
|
|
2212
|
+
const prefix = `layer${i}`;
|
|
2213
|
+
let norm1Out;
|
|
2214
|
+
if (pendingNorm1Out) norm1Out = pendingNorm1Out;
|
|
2215
|
+
else {
|
|
2216
|
+
const normWeightName = CANONICAL_KEYS.layerInputNorm(i);
|
|
2217
|
+
addTensor({
|
|
2218
|
+
name: normWeightName,
|
|
2219
|
+
shape: [hidden_size],
|
|
2220
|
+
dtype: "f32",
|
|
2221
|
+
storage: "constant",
|
|
2222
|
+
safetensorsKey: normWeightName
|
|
2223
|
+
});
|
|
2224
|
+
norm1Out = `${prefix}_norm1_out`;
|
|
2225
|
+
addTensor({
|
|
2226
|
+
name: norm1Out,
|
|
2227
|
+
shape: ["T", hidden_size],
|
|
2228
|
+
dtype: "f32",
|
|
2229
|
+
storage: "activation"
|
|
2230
|
+
});
|
|
2231
|
+
addNode({
|
|
2232
|
+
id: `${prefix}_norm1`,
|
|
2233
|
+
opType: "RMSNorm",
|
|
2234
|
+
inputs: [prevOutput, normWeightName],
|
|
2235
|
+
outputs: [norm1Out],
|
|
2236
|
+
attributes: {
|
|
2237
|
+
hidden_size,
|
|
2238
|
+
eps: rms_norm_eps,
|
|
2239
|
+
seq_len_tensor: prevOutput
|
|
2240
|
+
}
|
|
2241
|
+
});
|
|
2242
|
+
}
|
|
2243
|
+
const attnOutput = buildAttentionBlock(i, prefix, norm1Out);
|
|
2244
|
+
const resid1Out = `${prefix}_resid1`;
|
|
2245
|
+
const postNormW = CANONICAL_KEYS.layerPostAttnNorm(i);
|
|
2246
|
+
const norm2Out = `${prefix}_norm2_out`;
|
|
2247
|
+
addTensor({
|
|
2248
|
+
name: resid1Out,
|
|
2249
|
+
shape: ["T", hidden_size],
|
|
2250
|
+
dtype: "f32",
|
|
2251
|
+
storage: "activation"
|
|
2252
|
+
});
|
|
2253
|
+
addTensor({
|
|
2254
|
+
name: postNormW,
|
|
2255
|
+
shape: [hidden_size],
|
|
2256
|
+
dtype: "f32",
|
|
2257
|
+
storage: "constant",
|
|
2258
|
+
safetensorsKey: postNormW
|
|
2259
|
+
});
|
|
2260
|
+
addTensor({
|
|
2261
|
+
name: norm2Out,
|
|
2262
|
+
shape: ["T", hidden_size],
|
|
2263
|
+
dtype: "f32",
|
|
2264
|
+
storage: "activation"
|
|
2265
|
+
});
|
|
2266
|
+
addNode({
|
|
2267
|
+
id: `${prefix}_resid1_norm2`,
|
|
2268
|
+
opType: "ResidualRMSNorm",
|
|
2269
|
+
inputs: [
|
|
2270
|
+
prevOutput,
|
|
2271
|
+
attnOutput,
|
|
2272
|
+
postNormW
|
|
2273
|
+
],
|
|
2274
|
+
outputs: [resid1Out, norm2Out],
|
|
2275
|
+
attributes: {
|
|
2276
|
+
hidden_size,
|
|
2277
|
+
eps: rms_norm_eps,
|
|
2278
|
+
seq_len_tensor: prevOutput
|
|
2279
|
+
}
|
|
2280
|
+
});
|
|
2281
|
+
const gateW = CANONICAL_KEYS.gateProj(i);
|
|
2282
|
+
const upW = CANONICAL_KEYS.upProj(i);
|
|
2283
|
+
const downW = CANONICAL_KEYS.downProj(i);
|
|
2284
|
+
const mlpGateOut = `${prefix}_gate_out`;
|
|
2285
|
+
const upOut = `${prefix}_up_out`;
|
|
2286
|
+
const swigluOut = `${prefix}_swiglu_out`;
|
|
2287
|
+
const mlpOut = `${prefix}_mlp_out`;
|
|
2288
|
+
addTensor({
|
|
2289
|
+
name: mlpGateOut,
|
|
2290
|
+
shape: ["T", intermediate_size],
|
|
2291
|
+
dtype: "f32",
|
|
2292
|
+
storage: "activation"
|
|
2293
|
+
});
|
|
2294
|
+
addTensor({
|
|
2295
|
+
name: upOut,
|
|
2296
|
+
shape: ["T", intermediate_size],
|
|
2297
|
+
dtype: "f32",
|
|
2298
|
+
storage: "activation"
|
|
2299
|
+
});
|
|
2300
|
+
addTensor({
|
|
2301
|
+
name: swigluOut,
|
|
2302
|
+
shape: ["T", intermediate_size],
|
|
2303
|
+
dtype: "f32",
|
|
2304
|
+
storage: "activation"
|
|
2305
|
+
});
|
|
2306
|
+
addTensor({
|
|
2307
|
+
name: mlpOut,
|
|
2308
|
+
shape: ["T", hidden_size],
|
|
2309
|
+
dtype: "f32",
|
|
2310
|
+
storage: "activation"
|
|
2311
|
+
});
|
|
2312
|
+
addLinearOp(`${prefix}_gate`, norm2Out, gateW, [intermediate_size, hidden_size], mlpGateOut, hidden_size, intermediate_size);
|
|
2313
|
+
addLinearOp(`${prefix}_up`, norm2Out, upW, [intermediate_size, hidden_size], upOut, hidden_size, intermediate_size);
|
|
2314
|
+
addNode({
|
|
2315
|
+
id: `${prefix}_swiglu`,
|
|
2316
|
+
opType: "SwiGLU",
|
|
2317
|
+
inputs: [mlpGateOut, upOut],
|
|
2318
|
+
outputs: [swigluOut],
|
|
2319
|
+
attributes: { count_tensor: mlpGateOut }
|
|
2320
|
+
});
|
|
2321
|
+
addLinearOp(`${prefix}_down`, swigluOut, downW, [hidden_size, intermediate_size], mlpOut, intermediate_size, hidden_size);
|
|
2322
|
+
const resid2Out = `${prefix}_resid2`;
|
|
2323
|
+
addTensor({
|
|
2324
|
+
name: resid2Out,
|
|
2325
|
+
shape: ["T", hidden_size],
|
|
2326
|
+
dtype: "f32",
|
|
2327
|
+
storage: "activation"
|
|
2328
|
+
});
|
|
2329
|
+
if (i < num_layers - 1) {
|
|
2330
|
+
const nextNormW = CANONICAL_KEYS.layerInputNorm(i + 1);
|
|
2331
|
+
addTensor({
|
|
2332
|
+
name: nextNormW,
|
|
2333
|
+
shape: [hidden_size],
|
|
2334
|
+
dtype: "f32",
|
|
2335
|
+
storage: "constant",
|
|
2336
|
+
safetensorsKey: nextNormW
|
|
2337
|
+
});
|
|
2338
|
+
const nextNorm1Out = `layer${i + 1}_norm1_out`;
|
|
2339
|
+
addTensor({
|
|
2340
|
+
name: nextNorm1Out,
|
|
2341
|
+
shape: ["T", hidden_size],
|
|
2342
|
+
dtype: "f32",
|
|
2343
|
+
storage: "activation"
|
|
2344
|
+
});
|
|
2345
|
+
addNode({
|
|
2346
|
+
id: `${prefix}_resid2_norm1`,
|
|
2347
|
+
opType: "ResidualRMSNorm",
|
|
2348
|
+
inputs: [
|
|
2349
|
+
resid1Out,
|
|
2350
|
+
mlpOut,
|
|
2351
|
+
nextNormW
|
|
2352
|
+
],
|
|
2353
|
+
outputs: [resid2Out, nextNorm1Out],
|
|
2354
|
+
attributes: {
|
|
2355
|
+
hidden_size,
|
|
2356
|
+
eps: rms_norm_eps,
|
|
2357
|
+
seq_len_tensor: resid1Out
|
|
2358
|
+
}
|
|
2359
|
+
});
|
|
2360
|
+
pendingNorm1Out = nextNorm1Out;
|
|
2361
|
+
} else {
|
|
2362
|
+
addNode({
|
|
2363
|
+
id: `${prefix}_resid2`,
|
|
2364
|
+
opType: "Add",
|
|
2365
|
+
inputs: [resid1Out, mlpOut],
|
|
2366
|
+
outputs: [resid2Out],
|
|
2367
|
+
attributes: {
|
|
2368
|
+
count_tensor: resid1Out,
|
|
2369
|
+
hidden_size
|
|
2370
|
+
}
|
|
2371
|
+
});
|
|
2372
|
+
pendingNorm1Out = null;
|
|
2373
|
+
}
|
|
2374
|
+
prevOutput = resid2Out;
|
|
2375
|
+
}
|
|
2376
|
+
addTensor({
|
|
2377
|
+
name: CANONICAL_KEYS.FINAL_NORM,
|
|
2378
|
+
shape: [hidden_size],
|
|
2379
|
+
dtype: "f32",
|
|
2380
|
+
storage: "constant",
|
|
2381
|
+
safetensorsKey: CANONICAL_KEYS.FINAL_NORM
|
|
2382
|
+
});
|
|
2383
|
+
addTensor({
|
|
2384
|
+
name: "final_norm_out",
|
|
2385
|
+
shape: ["T", hidden_size],
|
|
2386
|
+
dtype: "f32",
|
|
2387
|
+
storage: "activation"
|
|
2388
|
+
});
|
|
2389
|
+
addNode({
|
|
2390
|
+
id: "final_norm",
|
|
2391
|
+
opType: "RMSNorm",
|
|
2392
|
+
inputs: [prevOutput, CANONICAL_KEYS.FINAL_NORM],
|
|
2393
|
+
outputs: ["final_norm_out"],
|
|
2394
|
+
attributes: {
|
|
2395
|
+
hidden_size,
|
|
2396
|
+
eps: rms_norm_eps,
|
|
2397
|
+
seq_len_tensor: prevOutput
|
|
2398
|
+
}
|
|
2399
|
+
});
|
|
2400
|
+
addTensor({
|
|
2401
|
+
name: "logits",
|
|
2402
|
+
shape: [1, hidden_size],
|
|
2403
|
+
dtype: "f32",
|
|
2404
|
+
storage: "activation"
|
|
2405
|
+
});
|
|
2406
|
+
addNode({
|
|
2407
|
+
id: "slice_last_row",
|
|
2408
|
+
opType: "SliceLastRow",
|
|
2409
|
+
inputs: ["final_norm_out"],
|
|
2410
|
+
outputs: ["logits"],
|
|
2411
|
+
attributes: { width: hidden_size }
|
|
2412
|
+
});
|
|
2413
|
+
return {
|
|
2414
|
+
architecture: "GepardForTTS",
|
|
2415
|
+
config,
|
|
2416
|
+
capabilities,
|
|
2417
|
+
tensors,
|
|
2418
|
+
nodes,
|
|
2419
|
+
executionOrder,
|
|
2420
|
+
inputs: ["inputs_embeds"],
|
|
2421
|
+
outputs: ["logits"]
|
|
2422
|
+
};
|
|
2423
|
+
}
|
|
2424
|
+
|
|
2425
|
+
//#endregion
|
|
2426
|
+
//#region src/gpu/gepard-tts.ts
|
|
2427
|
+
/**
|
|
2428
|
+
* Gepard-1.0 — native streaming text-to-speech engine for Gerbil's WebGPU backend.
|
|
2429
|
+
*
|
|
2430
|
+
* Gepard (nineninesix/gepard-1.0, Apache-2.0) is a frame-synchronous multihead
|
|
2431
|
+
* codec-LM TTS. Unlike the token-serial Kani/Oute engines (which emit one codec
|
|
2432
|
+
* token per forward), Gepard emits ONE WHOLE AUDIO FRAME per backbone forward:
|
|
2433
|
+
*
|
|
2434
|
+
* 1. BACKBONE (GPU): stock Qwen3.5 full-attention body (14 layers, hidden 1024)
|
|
2435
|
+
* run through generateGepardBackboneGraph — input is a host-written
|
|
2436
|
+
* `inputs_embeds` row, output is the last position's final-norm hidden state.
|
|
2437
|
+
* 2. OVERLAY (host): 32 parallel classifier heads (cardinalities [8,7,6,6]×8,
|
|
2438
|
+
* 216 logits total) + a binary stop head read the hidden state; the sampled
|
|
2439
|
+
* 32 FSQ codes feed back through per-channel embeddings → concat → GELU MLP
|
|
2440
|
+
* → affine-free LayerNorm → × audio_embed_scale as the next step's input.
|
|
2441
|
+
* 3. CODEC (GPU): NanoCodec 21.5 fps decoder (same validated CausalHiFiGAN
|
|
2442
|
+
* graph family as Kani's codec, rates [8,8,4,2,2], hop 1024) turns the
|
|
2443
|
+
* per-channel codes into 22.05 kHz PCM — decoded incrementally in chunks
|
|
2444
|
+
* with left-context lookback, so audio streams out while the model is
|
|
2445
|
+
* still generating (≈1.5 s of audio per 32-frame chunk).
|
|
2446
|
+
*
|
|
2447
|
+
* Mirrors gepard-inference (runner.py) semantics: adaptive text repetition,
|
|
2448
|
+
* SOS-seeded first frame, stop-head termination, independent per-head sampling
|
|
2449
|
+
* (temperature / top-k / windowed repetition penalty) in fp32. Voice cloning
|
|
2450
|
+
* (the ref_compressor speaker prefix) is NOT wired yet — the default voice ships
|
|
2451
|
+
* first; cloning is a documented follow-up.
|
|
2452
|
+
*/
|
|
2453
|
+
/** Codec decode chunking: 32 frames ≈ 1.49 s of audio per streamed chunk. */
|
|
2454
|
+
const GEPARD_CODEC_CHUNK_FRAMES = 32;
|
|
2455
|
+
/**
|
|
2456
|
+
* Left-context warm-up frames re-decoded (and discarded) per chunk. The decoder
|
|
2457
|
+
* is fully causal with a bounded receptive field; 64 frames (≈65k samples) of
|
|
2458
|
+
* lookback keeps chunked output numerically identical to a monolithic decode
|
|
2459
|
+
* while staying under the WebGPU per-dimension dispatch cap.
|
|
2460
|
+
*/
|
|
2461
|
+
const GEPARD_CODEC_LOOKBACK_FRAMES = 64;
|
|
2462
|
+
/** Hard ceiling on generated frames (the reference runner's max_frames). */
|
|
2463
|
+
const DEFAULT_MAX_FRAMES$1 = 2e3;
|
|
2464
|
+
/** PyTorch nn.LayerNorm default epsilon (audio_embed_proj's affine-free LN). */
|
|
2465
|
+
const LAYERNORM_EPS = 1e-5;
|
|
2466
|
+
/** Build a fresh map of just the constant weights a graph references. */
|
|
2467
|
+
function selectGraphWeights$2(graph, weights) {
|
|
2468
|
+
const out = /* @__PURE__ */ new Map();
|
|
2469
|
+
for (const [name, desc] of Object.entries(graph.tensors)) {
|
|
2470
|
+
if (desc.storage !== "constant") continue;
|
|
2471
|
+
const w = weights.get(name) ?? (desc.safetensorsKey ? weights.get(desc.safetensorsKey) : void 0);
|
|
2472
|
+
if (w) out.set(name, w);
|
|
2473
|
+
}
|
|
2474
|
+
return out;
|
|
2475
|
+
}
|
|
2476
|
+
/** Abramowitz–Stegun 7.1.26 erf approximation (max |err| ≈ 1.5e-7). */
|
|
2477
|
+
function erf(x) {
|
|
2478
|
+
const sign = x < 0 ? -1 : 1;
|
|
2479
|
+
const ax = Math.abs(x);
|
|
2480
|
+
const t = 1 / (1 + .3275911 * ax);
|
|
2481
|
+
return sign * (1 - ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - .284496736) * t + .254829592) * t * Math.exp(-ax * ax));
|
|
2482
|
+
}
|
|
2483
|
+
/** Exact (erf-based) GELU, matching PyTorch nn.GELU()'s default. */
|
|
2484
|
+
function gelu(x) {
|
|
2485
|
+
return .5 * x * (1 + erf(x / Math.SQRT2));
|
|
2486
|
+
}
|
|
2487
|
+
/** y = W·x + b for a row-major [out, in] weight. */
|
|
2488
|
+
function linear(w, b, x, outDim, inDim, out) {
|
|
2489
|
+
for (let o = 0; o < outDim; o++) {
|
|
2490
|
+
let acc = b ? b[o] : 0;
|
|
2491
|
+
const base = o * inDim;
|
|
2492
|
+
for (let k = 0; k < inDim; k++) acc += w[base + k] * x[k];
|
|
2493
|
+
out[o] = acc;
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
var GepardTTS = class GepardTTS {
|
|
2497
|
+
ctx;
|
|
2498
|
+
loaded;
|
|
2499
|
+
tokenizer;
|
|
2500
|
+
cfg;
|
|
2501
|
+
maxSeqLen;
|
|
2502
|
+
hidden;
|
|
2503
|
+
/** Backbone executor (built once; reused across speak() calls). */
|
|
2504
|
+
backboneExec;
|
|
2505
|
+
/** Cached codec executor for the steady-state window size (chunk + lookback). */
|
|
2506
|
+
codecExecCache = /* @__PURE__ */ new Map();
|
|
2507
|
+
_destroyed = false;
|
|
2508
|
+
/** The selected backbone precision ("q4" or "f32"). */
|
|
2509
|
+
dtype;
|
|
2510
|
+
audioEmbTables;
|
|
2511
|
+
projW0;
|
|
2512
|
+
projB0;
|
|
2513
|
+
projW2;
|
|
2514
|
+
projB2;
|
|
2515
|
+
audioEmbedScale;
|
|
2516
|
+
headW;
|
|
2517
|
+
headB;
|
|
2518
|
+
stopW;
|
|
2519
|
+
stopB;
|
|
2520
|
+
constructor(ctx, loaded, maxSeqLen, dtype) {
|
|
2521
|
+
this.ctx = ctx;
|
|
2522
|
+
this.loaded = loaded;
|
|
2523
|
+
this.tokenizer = loaded.tokenizer;
|
|
2524
|
+
this.cfg = parseGepardConfig(loaded.gepardConfig);
|
|
2525
|
+
this.maxSeqLen = maxSeqLen;
|
|
2526
|
+
this.hidden = this.cfg.backboneConfig.hidden_size;
|
|
2527
|
+
this.dtype = dtype;
|
|
2528
|
+
const overlay = loaded.overlayWeights;
|
|
2529
|
+
const need = (key) => {
|
|
2530
|
+
const w = overlay.get(key);
|
|
2531
|
+
if (!w) throw new Error(`Gepard overlay weight missing: ${key}`);
|
|
2532
|
+
return w.data;
|
|
2533
|
+
};
|
|
2534
|
+
const n = this.cfg.vocabSizes.length;
|
|
2535
|
+
if (n !== GEPARD_NUM_CHANNELS) throw new Error(`Gepard expects ${GEPARD_NUM_CHANNELS} audio heads, config has ${n}.`);
|
|
2536
|
+
this.audioEmbTables = [];
|
|
2537
|
+
this.headW = [];
|
|
2538
|
+
this.headB = [];
|
|
2539
|
+
for (let i = 0; i < n; i++) {
|
|
2540
|
+
this.audioEmbTables.push(need(`audio_embeddings.${i}.weight`));
|
|
2541
|
+
this.headW.push(need(`codebook_heads.${i}.weight`));
|
|
2542
|
+
this.headB.push(need(`codebook_heads.${i}.bias`));
|
|
2543
|
+
}
|
|
2544
|
+
this.projW0 = need("audio_embed_proj.0.weight");
|
|
2545
|
+
this.projB0 = need("audio_embed_proj.0.bias");
|
|
2546
|
+
this.projW2 = need("audio_embed_proj.2.weight");
|
|
2547
|
+
this.projB2 = need("audio_embed_proj.2.bias");
|
|
2548
|
+
this.audioEmbedScale = need("audio_embed_scale")[0];
|
|
2549
|
+
this.stopW = need("stop_head.weight");
|
|
2550
|
+
this.stopB = need("stop_head.bias")[0];
|
|
2551
|
+
const graph = generateGepardBackboneGraph(this.cfg.backboneConfig, this.dtype === "q4" ? "q4" : void 0);
|
|
2552
|
+
if (this.dtype === "q4") quantizeBackboneInt4(graph, loaded.backboneWeights);
|
|
2553
|
+
this.backboneExec = new Executor(ctx, graph, {
|
|
2554
|
+
maxSeqLen,
|
|
2555
|
+
kvMode: "f32"
|
|
2556
|
+
});
|
|
2557
|
+
this.backboneExec.uploadWeightsMap(selectGraphWeights$2(graph, loaded.backboneWeights));
|
|
2558
|
+
this.backboneExec.initBindGroups();
|
|
2559
|
+
}
|
|
2560
|
+
static async create(options = {}) {
|
|
2561
|
+
const ctx = await initGPU();
|
|
2562
|
+
const loaded = await loadGepardTTS({
|
|
2563
|
+
repo: options.repo,
|
|
2564
|
+
codecRepo: options.codecRepo,
|
|
2565
|
+
revision: options.revision,
|
|
2566
|
+
hfToken: options.hfToken,
|
|
2567
|
+
cacheDir: options.cacheDir,
|
|
2568
|
+
onProgress: options.onProgress
|
|
2569
|
+
});
|
|
2570
|
+
const ctxLen = loaded.rawConfig.max_position_embeddings ?? 262144;
|
|
2571
|
+
return new GepardTTS(ctx, loaded, Math.min(options.maxSeqLen ?? 2048, ctxLen), options.dtype ?? "q4");
|
|
2572
|
+
}
|
|
2573
|
+
/** NanoCodec's fixed output sample rate (Hz). */
|
|
2574
|
+
get sampleRate() {
|
|
2575
|
+
return this.cfg.sampleRate;
|
|
2576
|
+
}
|
|
2577
|
+
/** Preset voices. Gepard ships one default voice (cloning is a follow-up). */
|
|
2578
|
+
availableVoices() {
|
|
2579
|
+
return [];
|
|
2580
|
+
}
|
|
2581
|
+
/** Gather text-token rows from the raw BF16 embed table → f32 [T, hidden]. */
|
|
2582
|
+
embedTextTokens(ids) {
|
|
2583
|
+
const h = this.hidden;
|
|
2584
|
+
const table = this.loaded.embedTokensBF16;
|
|
2585
|
+
const out = new Float32Array(ids.length * h);
|
|
2586
|
+
const view = new DataView(out.buffer);
|
|
2587
|
+
for (let t = 0; t < ids.length; t++) {
|
|
2588
|
+
const rowBase = ids[t] * h;
|
|
2589
|
+
const dstBase = t * h;
|
|
2590
|
+
for (let k = 0; k < h; k++) view.setUint32((dstBase + k) * 4, table[rowBase + k] << 16, true);
|
|
2591
|
+
}
|
|
2592
|
+
return out;
|
|
1589
2593
|
}
|
|
1590
2594
|
/**
|
|
1591
|
-
*
|
|
1592
|
-
*
|
|
1593
|
-
*
|
|
1594
|
-
* worst-case block need fits the unreserved KV pool with headroom.
|
|
2595
|
+
* Embed one audio frame (32 FSQ codes) through the overlay stack:
|
|
2596
|
+
* per-channel lookup → concat → Linear→GELU→Linear → affine-free LayerNorm →
|
|
2597
|
+
* × audio_embed_scale. Mirrors GepardModel._embed_audio.
|
|
1595
2598
|
*/
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
const
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
if (this.reservedBlocks + blocksNeeded + this.blockHeadroom > totalBlocks) return;
|
|
1605
|
-
this.queue.shift();
|
|
1606
|
-
req.state = "prefilling";
|
|
1607
|
-
req.slot = slot;
|
|
1608
|
-
req.blocksReserved = blocksNeeded;
|
|
1609
|
-
req.admittedAt = performance.now();
|
|
1610
|
-
this.reservedBlocks += blocksNeeded;
|
|
1611
|
-
this.slots[slot] = req;
|
|
1612
|
-
this.prefilling = req;
|
|
1613
|
-
if (req.adapterDeltas) {
|
|
1614
|
-
const { applied } = this.host.executor.applyRuntimeLoRA(req.adapterDeltas);
|
|
1615
|
-
if (applied === 0) throw new Error(`RequestScheduler: adapter "${req.adapterName}" resolved to 0 applicable prefill targets`);
|
|
2599
|
+
embedAudioFrame(codes) {
|
|
2600
|
+
const h = this.hidden;
|
|
2601
|
+
const dim = this.cfg.audioEmbedDim;
|
|
2602
|
+
const x = new Float32Array(GEPARD_NUM_CHANNELS * dim);
|
|
2603
|
+
for (let c = 0; c < GEPARD_NUM_CHANNELS; c++) {
|
|
2604
|
+
const table = this.audioEmbTables[c];
|
|
2605
|
+
const base = codes[c] * dim;
|
|
2606
|
+
x.set(table.subarray(base, base + dim), c * dim);
|
|
1616
2607
|
}
|
|
1617
|
-
|
|
2608
|
+
const h1 = new Float32Array(h);
|
|
2609
|
+
linear(this.projW0, this.projB0, x, h, x.length, h1);
|
|
2610
|
+
for (let i = 0; i < h; i++) h1[i] = gelu(h1[i]);
|
|
2611
|
+
const h2 = new Float32Array(h);
|
|
2612
|
+
linear(this.projW2, this.projB2, h1, h, h, h2);
|
|
2613
|
+
let mean = 0;
|
|
2614
|
+
for (let i = 0; i < h; i++) mean += h2[i];
|
|
2615
|
+
mean /= h;
|
|
2616
|
+
let variance = 0;
|
|
2617
|
+
for (let i = 0; i < h; i++) {
|
|
2618
|
+
const d = h2[i] - mean;
|
|
2619
|
+
variance += d * d;
|
|
2620
|
+
}
|
|
2621
|
+
variance /= h;
|
|
2622
|
+
const inv = this.audioEmbedScale / Math.sqrt(variance + LAYERNORM_EPS);
|
|
2623
|
+
for (let i = 0; i < h; i++) h2[i] = (h2[i] - mean) * inv;
|
|
2624
|
+
return h2;
|
|
2625
|
+
}
|
|
2626
|
+
/** Stop-head probability for a hidden state. */
|
|
2627
|
+
stopProbability(hidden) {
|
|
2628
|
+
let acc = this.stopB;
|
|
2629
|
+
for (let k = 0; k < this.hidden; k++) acc += this.stopW[k] * hidden[k];
|
|
2630
|
+
return 1 / (1 + Math.exp(-acc));
|
|
1618
2631
|
}
|
|
1619
2632
|
/**
|
|
1620
|
-
*
|
|
1621
|
-
*
|
|
1622
|
-
*
|
|
1623
|
-
* the singleton SSM state into the slot's pool row, seeds the lane's first
|
|
1624
|
-
* token, and flips the lane to decoding.
|
|
2633
|
+
* Sample one audio frame: all 32 heads independently in fp32 —
|
|
2634
|
+
* windowed repetition penalty → temperature → top-k → softmax → multinomial.
|
|
2635
|
+
* Mirrors GepardRunner._sample_frame / _sample_head_logits.
|
|
1625
2636
|
*/
|
|
1626
|
-
|
|
1627
|
-
const
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
2637
|
+
sampleFrame(hidden, p, recentFrames) {
|
|
2638
|
+
const frame = new Uint32Array(GEPARD_NUM_CHANNELS);
|
|
2639
|
+
for (let c = 0; c < GEPARD_NUM_CHANNELS; c++) {
|
|
2640
|
+
const L = this.cfg.vocabSizes[c];
|
|
2641
|
+
const w = this.headW[c];
|
|
2642
|
+
const b = this.headB[c];
|
|
2643
|
+
const logits = new Float32Array(L);
|
|
2644
|
+
for (let j = 0; j < L; j++) {
|
|
2645
|
+
let acc = b[j];
|
|
2646
|
+
const base = j * this.hidden;
|
|
2647
|
+
for (let k = 0; k < this.hidden; k++) acc += w[base + k] * hidden[k];
|
|
2648
|
+
logits[j] = acc;
|
|
2649
|
+
}
|
|
2650
|
+
if (p.repetitionPenalty !== 1 && recentFrames) for (const prev of recentFrames) {
|
|
2651
|
+
const tok = prev[c];
|
|
2652
|
+
logits[tok] = logits[tok] > 0 ? logits[tok] / p.repetitionPenalty : logits[tok] * p.repetitionPenalty;
|
|
2653
|
+
}
|
|
2654
|
+
if (p.temperature !== 1) for (let j = 0; j < L; j++) logits[j] /= p.temperature;
|
|
2655
|
+
if (p.topK > 0 && p.topK < L) {
|
|
2656
|
+
const threshold = [...logits].sort((a, b2) => b2 - a)[p.topK - 1];
|
|
2657
|
+
for (let j = 0; j < L; j++) if (logits[j] < threshold) logits[j] = Number.NEGATIVE_INFINITY;
|
|
2658
|
+
}
|
|
2659
|
+
let maxV = Number.NEGATIVE_INFINITY;
|
|
2660
|
+
for (let j = 0; j < L; j++) if (logits[j] > maxV) maxV = logits[j];
|
|
2661
|
+
let sum = 0;
|
|
2662
|
+
for (let j = 0; j < L; j++) {
|
|
2663
|
+
const e = Math.exp(logits[j] - maxV);
|
|
2664
|
+
logits[j] = e;
|
|
2665
|
+
sum += e;
|
|
2666
|
+
}
|
|
2667
|
+
let pick$1 = Math.random() * sum;
|
|
2668
|
+
let chosen = L - 1;
|
|
2669
|
+
for (let j = 0; j < L; j++) {
|
|
2670
|
+
pick$1 -= logits[j];
|
|
2671
|
+
if (pick$1 <= 0) {
|
|
2672
|
+
chosen = j;
|
|
2673
|
+
break;
|
|
2674
|
+
}
|
|
2675
|
+
}
|
|
2676
|
+
frame[c] = chosen;
|
|
2677
|
+
}
|
|
2678
|
+
return frame;
|
|
1645
2679
|
}
|
|
1646
|
-
/**
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
2680
|
+
/**
|
|
2681
|
+
* Synthesize speech for `text`. Returns 22.05 kHz mono PCM; when
|
|
2682
|
+
* `opts.onChunk` is set, audio streams out chunk-by-chunk during generation.
|
|
2683
|
+
*
|
|
2684
|
+
* Pipeline: adaptive-repetition text layout → prefill (host-gathered text
|
|
2685
|
+
* embeddings) → frame-synchronous AR loop (one frame = 32 codes per forward,
|
|
2686
|
+
* stop head decides termination) → incremental NanoCodec decode → PCM.
|
|
2687
|
+
*/
|
|
2688
|
+
async speak(text, opts = {}) {
|
|
2689
|
+
if (this._destroyed) throw new Error("GepardTTS has been destroyed.");
|
|
2690
|
+
const temperature = opts.temperature ?? .4;
|
|
2691
|
+
const topK = opts.topK ?? 0;
|
|
2692
|
+
const stopThreshold = opts.stopThreshold ?? .5;
|
|
2693
|
+
const repetitionPenalty = opts.repetitionPenalty ?? 1;
|
|
2694
|
+
const repetitionWindow = opts.repetitionWindow ?? 32;
|
|
2695
|
+
const layout = buildGepardTextLayout(this.tokenizer.encode(text), this.cfg);
|
|
2696
|
+
if (layout.length + 2 >= this.maxSeqLen) throw new Error(`Gepard prompt is ${layout.length} tokens >= maxSeqLen ${this.maxSeqLen}.`);
|
|
2697
|
+
const maxFrames = Math.min(opts.maxFrames ?? DEFAULT_MAX_FRAMES$1, this.maxSeqLen - layout.length - 1);
|
|
2698
|
+
const startTime = performance.now();
|
|
2699
|
+
this.backboneExec.reset();
|
|
2700
|
+
this.backboneExec.writeInput("inputs_embeds", this.embedTextTokens(layout));
|
|
2701
|
+
let { logits: hidden } = await this.backboneExec.forward(new Uint32Array(layout.length));
|
|
2702
|
+
const frames = [this.sampleFrame(hidden, {
|
|
2703
|
+
temperature,
|
|
2704
|
+
topK,
|
|
2705
|
+
repetitionPenalty
|
|
2706
|
+
}, null)];
|
|
2707
|
+
const streamer = new GepardChunkStreamer(this, opts.onChunk);
|
|
2708
|
+
const sampleOpts = {
|
|
2709
|
+
temperature,
|
|
2710
|
+
topK,
|
|
2711
|
+
repetitionPenalty
|
|
2712
|
+
};
|
|
2713
|
+
for (let step = 1; step < maxFrames; step++) {
|
|
2714
|
+
const frameEmbed = this.embedAudioFrame(frames[frames.length - 1]);
|
|
2715
|
+
this.backboneExec.writeInput("inputs_embeds", frameEmbed);
|
|
2716
|
+
hidden = (await this.backboneExec.forward(new Uint32Array(1))).logits;
|
|
2717
|
+
if (this.stopProbability(hidden) > stopThreshold) break;
|
|
2718
|
+
const window = repetitionPenalty !== 1 ? frames.slice(repetitionWindow > 0 ? -repetitionWindow : 0) : null;
|
|
2719
|
+
frames.push(this.sampleFrame(hidden, sampleOpts, window));
|
|
2720
|
+
await streamer.maybeEmit(frames, false);
|
|
1654
2721
|
}
|
|
2722
|
+
await streamer.maybeEmit(frames, true);
|
|
2723
|
+
const pcm = streamer.assembled(frames.length);
|
|
2724
|
+
const totalTime = (performance.now() - startTime) / 1e3;
|
|
2725
|
+
const audioSeconds = frames.length * GEPARD_HOP / this.cfg.sampleRate;
|
|
2726
|
+
console.log(`[gepard] frames=${frames.length} audio=${audioSeconds.toFixed(2)}s wall=${totalTime.toFixed(2)}s RTF=${(audioSeconds / totalTime).toFixed(2)}x`);
|
|
2727
|
+
return {
|
|
2728
|
+
pcm,
|
|
2729
|
+
sampleRate: this.cfg.sampleRate,
|
|
2730
|
+
frames: frames.length,
|
|
2731
|
+
audioSeconds
|
|
2732
|
+
};
|
|
1655
2733
|
}
|
|
1656
|
-
/**
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
2734
|
+
/**
|
|
2735
|
+
* Decode frames [from, to) of the utterance to PCM, re-decoding up to
|
|
2736
|
+
* `GEPARD_CODEC_LOOKBACK_FRAMES` of left context as warm-up (discarded).
|
|
2737
|
+
* The decoder is causal with a bounded receptive field, so chunked output
|
|
2738
|
+
* matches a monolithic decode.
|
|
2739
|
+
*/
|
|
2740
|
+
async decodeFrameRange(frames, from, to) {
|
|
2741
|
+
const ctxStart = Math.max(0, from - GEPARD_CODEC_LOOKBACK_FRAMES);
|
|
2742
|
+
const winFrames = to - ctxStart;
|
|
2743
|
+
const codes = new Uint32Array(GEPARD_NUM_CHANNELS * winFrames);
|
|
2744
|
+
for (let t = 0; t < winFrames; t++) {
|
|
2745
|
+
const frame = frames[ctxStart + t];
|
|
2746
|
+
for (let c = 0; c < GEPARD_NUM_CHANNELS; c++) codes[c * winFrames + t] = frame[c];
|
|
1666
2747
|
}
|
|
1667
|
-
const
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
2748
|
+
const latent = gepardCodesToLatent(codes, winFrames);
|
|
2749
|
+
const win = await this.decodeLatentWindow(latent, winFrames);
|
|
2750
|
+
const drop = (from - ctxStart) * GEPARD_HOP;
|
|
2751
|
+
const keep = (to - from) * GEPARD_HOP;
|
|
2752
|
+
const out = win.slice(drop, drop + keep);
|
|
2753
|
+
for (let i = 0; i < out.length; i++) out[i] = Math.min(1, Math.max(-1, out[i]));
|
|
2754
|
+
return out;
|
|
2755
|
+
}
|
|
2756
|
+
/** Run the codec graph for one latent window → raw PCM (pre-clamp). */
|
|
2757
|
+
async decodeLatentWindow(latent, winFrames) {
|
|
2758
|
+
const steady = winFrames === GEPARD_CODEC_CHUNK_FRAMES + GEPARD_CODEC_LOOKBACK_FRAMES;
|
|
2759
|
+
let exec = steady ? this.codecExecCache.get(winFrames) : void 0;
|
|
2760
|
+
let graphOutputs;
|
|
2761
|
+
if (exec) graphOutputs = exec.graphOutputs;
|
|
2762
|
+
else {
|
|
2763
|
+
const graph = generateNanoCodecDecoderGraph({
|
|
2764
|
+
numFrames: winFrames,
|
|
2765
|
+
geometry: {
|
|
2766
|
+
upSampleRates: GEPARD_CODEC_UP_SAMPLE_RATES,
|
|
2767
|
+
inputDim: GEPARD_CODEC_INPUT_DIM
|
|
2768
|
+
},
|
|
2769
|
+
latentInput: true
|
|
2770
|
+
});
|
|
2771
|
+
exec = new Executor(this.ctx, graph, {
|
|
2772
|
+
maxSeqLen: winFrames,
|
|
2773
|
+
kvMode: "f32"
|
|
2774
|
+
});
|
|
2775
|
+
exec.uploadWeightsMap(selectGraphWeights$2(graph, this.loaded.codecWeights));
|
|
2776
|
+
exec.initBindGroups();
|
|
2777
|
+
graphOutputs = graph.outputs;
|
|
2778
|
+
if (steady) this.codecExecCache.set(winFrames, exec);
|
|
1677
2779
|
}
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
}
|
|
1683
|
-
|
|
1684
|
-
req.finishReason = "max_tokens";
|
|
1685
|
-
this.finish(req);
|
|
2780
|
+
try {
|
|
2781
|
+
exec.reset();
|
|
2782
|
+
exec.writeInput("nc_latent", latent);
|
|
2783
|
+
return await exec.runGraphOutput(graphOutputs[0], winFrames * GEPARD_HOP);
|
|
2784
|
+
} finally {
|
|
2785
|
+
if (!steady) exec.destroy();
|
|
1686
2786
|
}
|
|
1687
2787
|
}
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
this.
|
|
1696
|
-
this.
|
|
1697
|
-
const finishedAt = performance.now();
|
|
1698
|
-
req.resolve({
|
|
1699
|
-
requestId: req.id,
|
|
1700
|
-
text: req.text,
|
|
1701
|
-
tokenIds: [...req.generatedIds],
|
|
1702
|
-
tokensGenerated: req.generatedIds.length,
|
|
1703
|
-
finishReason: req.finishReason,
|
|
1704
|
-
adapter: req.adapterName ?? void 0,
|
|
1705
|
-
submittedAt: req.submittedAt,
|
|
1706
|
-
admittedAt: req.admittedAt,
|
|
1707
|
-
firstTokenAt: req.firstTokenAt,
|
|
1708
|
-
finishedAt,
|
|
1709
|
-
ttftMs: req.firstTokenAt - req.submittedAt,
|
|
1710
|
-
e2eMs: finishedAt - req.submittedAt
|
|
1711
|
-
});
|
|
2788
|
+
destroy() {
|
|
2789
|
+
if (this._destroyed) return;
|
|
2790
|
+
this._destroyed = true;
|
|
2791
|
+
this.backboneExec.destroy();
|
|
2792
|
+
for (const exec of this.codecExecCache.values()) exec.destroy();
|
|
2793
|
+
this.codecExecCache.clear();
|
|
2794
|
+
this.loaded.backboneWeights.clear();
|
|
2795
|
+
this.loaded.overlayWeights.clear();
|
|
2796
|
+
this.loaded.codecWeights.clear();
|
|
1712
2797
|
}
|
|
1713
2798
|
};
|
|
1714
|
-
|
|
1715
|
-
//#endregion
|
|
1716
|
-
//#region src/gpu/batch/swarm.ts
|
|
1717
|
-
/** Default reduce: member-keyed map, disambiguating duplicated members. */
|
|
1718
|
-
function reduceToKeyedMap(results) {
|
|
1719
|
-
const out = {};
|
|
1720
|
-
const seen = /* @__PURE__ */ new Map();
|
|
1721
|
-
for (const { member, result } of results) {
|
|
1722
|
-
const n = (seen.get(member) ?? 0) + 1;
|
|
1723
|
-
seen.set(member, n);
|
|
1724
|
-
out[n === 1 ? member : `${member}#${n}`] = result;
|
|
1725
|
-
}
|
|
1726
|
-
return out;
|
|
1727
|
-
}
|
|
1728
2799
|
/**
|
|
1729
|
-
*
|
|
1730
|
-
*
|
|
2800
|
+
* Incremental chunk decoder: every GEPARD_CODEC_CHUNK_FRAMES completed frames
|
|
2801
|
+
* it decodes the new span (with lookback) and emits it via `onChunk`, keeping
|
|
2802
|
+
* all emitted PCM so the final result is assembled without a second decode.
|
|
1731
2803
|
*/
|
|
1732
|
-
var
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
constructor(
|
|
1738
|
-
|
|
1739
|
-
this.
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
*/
|
|
1757
|
-
async run(input, options = {}) {
|
|
1758
|
-
const tasks = this.plan ? await this.plan(input, this.members) : this.members.map((member) => ({
|
|
1759
|
-
member,
|
|
1760
|
-
prompt: input
|
|
1761
|
-
}));
|
|
1762
|
-
if (tasks.length === 0) throw new Error("Swarm: plan produced no sub-tasks");
|
|
1763
|
-
const results = await Promise.all(tasks.map(async (task) => {
|
|
1764
|
-
const result = await this.submitFor(task.member, task.prompt, {
|
|
1765
|
-
...options,
|
|
1766
|
-
...task.options
|
|
2804
|
+
var GepardChunkStreamer = class {
|
|
2805
|
+
tts;
|
|
2806
|
+
onChunk;
|
|
2807
|
+
decodedUpTo = 0;
|
|
2808
|
+
chunks = [];
|
|
2809
|
+
constructor(tts, onChunk) {
|
|
2810
|
+
this.tts = tts;
|
|
2811
|
+
this.onChunk = onChunk;
|
|
2812
|
+
}
|
|
2813
|
+
/** Decode + emit any complete pending chunk (or everything when `final`). */
|
|
2814
|
+
async maybeEmit(frames, final) {
|
|
2815
|
+
if (!(final || this.onChunk)) return;
|
|
2816
|
+
while (frames.length - this.decodedUpTo >= GEPARD_CODEC_CHUNK_FRAMES || final) {
|
|
2817
|
+
const from = this.decodedUpTo;
|
|
2818
|
+
const to = final ? frames.length : Math.min(frames.length, from + GEPARD_CODEC_CHUNK_FRAMES);
|
|
2819
|
+
if (to <= from) break;
|
|
2820
|
+
const pcm = await this.tts.decodeFrameRange(frames, from, to);
|
|
2821
|
+
this.chunks.push(pcm);
|
|
2822
|
+
this.decodedUpTo = to;
|
|
2823
|
+
this.onChunk?.({
|
|
2824
|
+
pcm,
|
|
2825
|
+
sampleRate: this.tts.sampleRate,
|
|
2826
|
+
frameIndex: from,
|
|
2827
|
+
isFinal: final && this.decodedUpTo >= frames.length
|
|
1767
2828
|
});
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
result
|
|
1771
|
-
};
|
|
1772
|
-
}));
|
|
1773
|
-
if (this.reduce) return this.reduce(results);
|
|
1774
|
-
return reduceToKeyedMap(results);
|
|
1775
|
-
}
|
|
1776
|
-
/**
|
|
1777
|
-
* Mode-A access: decode one prompt with a single member (no plan/reduce).
|
|
1778
|
-
* Still rides the shared scheduler, so it batches with any concurrent work.
|
|
1779
|
-
*/
|
|
1780
|
-
generate(member, prompt, options = {}) {
|
|
1781
|
-
return this.submitFor(member, prompt, options);
|
|
2829
|
+
if (final && this.decodedUpTo >= frames.length) break;
|
|
2830
|
+
}
|
|
1782
2831
|
}
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
const
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
2832
|
+
/** Concatenate all emitted chunks into the full utterance PCM. */
|
|
2833
|
+
assembled(totalFrames) {
|
|
2834
|
+
const pcm = new Float32Array(totalFrames * GEPARD_HOP);
|
|
2835
|
+
let offset = 0;
|
|
2836
|
+
for (const chunk of this.chunks) {
|
|
2837
|
+
pcm.set(chunk, offset);
|
|
2838
|
+
offset += chunk.length;
|
|
2839
|
+
}
|
|
2840
|
+
return pcm;
|
|
1790
2841
|
}
|
|
1791
2842
|
};
|
|
1792
2843
|
|
|
@@ -5596,6 +6647,8 @@ var WebGPUEngine = class WebGPUEngine {
|
|
|
5596
6647
|
_batchAdapters = /* @__PURE__ */ new Map();
|
|
5597
6648
|
/** Lazily-created Parler-TTS engine (Flan-T5 encoder + decoder LM + dac_44khz). */
|
|
5598
6649
|
_parlerTTS = null;
|
|
6650
|
+
/** Lazily-created Gepard-1.0 engine (Qwen3.5 multihead codec-LM + NanoCodec 21.5 fps). */
|
|
6651
|
+
_gepardTTS = null;
|
|
5599
6652
|
/** Lazily-created continuous-batching scheduler (GERBIL_BATCH, Phase 5). */
|
|
5600
6653
|
_scheduler = null;
|
|
5601
6654
|
/**
|
|
@@ -6601,6 +7654,7 @@ var WebGPUEngine = class WebGPUEngine {
|
|
|
6601
7654
|
const repo = this._createOptions.repo ?? "";
|
|
6602
7655
|
if (/parler/i.test(repo) || /parler/i.test(this._architecture)) return this.speakParler(text, options);
|
|
6603
7656
|
if (/outetts/i.test(repo) || /oute/i.test(this._architecture)) return this.speakOute(text, options);
|
|
7657
|
+
if (/gepard/i.test(repo) || /gepard/i.test(this._architecture)) return this.speakGepard(text, options);
|
|
6604
7658
|
if (!(this._architecture === "KaniTTS2ForCausalLM" || /kani-tts/i.test(repo) || /\btts\b/i.test(repo))) throw new Error(`speak() requires a Kani-TTS or OuteTTS checkpoint (e.g. "${DEFAULT_MODELS.tts}" or "${DEFAULT_MODELS.ttsOute}"), loaded engine is "${this._architecture}" (repo "${repo}").`);
|
|
6605
7659
|
if (!this._kaniTTS) this._kaniTTS = await KaniTTS.create({
|
|
6606
7660
|
repo: this._createOptions.repo,
|
|
@@ -6645,6 +7699,31 @@ var WebGPUEngine = class WebGPUEngine {
|
|
|
6645
7699
|
return ParlerTTS.randomVoiceDescription();
|
|
6646
7700
|
}
|
|
6647
7701
|
/**
|
|
7702
|
+
* Gepard-1.0 speak path: lazily build the Gepard engine (stock Qwen3.5
|
|
7703
|
+
* full-attention codec-LM + 32 host-side FSQ classifier heads + NanoCodec
|
|
7704
|
+
* 21.5 fps decoder) and synthesize `text`. Frame-synchronous: when `onChunk`
|
|
7705
|
+
* is set, audio streams out during generation. Single default voice for now
|
|
7706
|
+
* (voice cloning via the ref-compressor is a documented follow-up).
|
|
7707
|
+
*/
|
|
7708
|
+
async speakGepard(text, options) {
|
|
7709
|
+
if (!this._gepardTTS) this._gepardTTS = await GepardTTS.create({
|
|
7710
|
+
repo: this._createOptions.repo,
|
|
7711
|
+
revision: this._createOptions.revision,
|
|
7712
|
+
hfToken: this._createOptions.hfToken,
|
|
7713
|
+
cacheDir: this._createOptions.cacheDir,
|
|
7714
|
+
maxSeqLen: this.maxSeqLen,
|
|
7715
|
+
dtype: options.dtype
|
|
7716
|
+
});
|
|
7717
|
+
return this._gepardTTS.speak(text, {
|
|
7718
|
+
temperature: options.temperature,
|
|
7719
|
+
topK: options.topK,
|
|
7720
|
+
repetitionPenalty: options.repetitionPenalty,
|
|
7721
|
+
maxFrames: options.maxFrames,
|
|
7722
|
+
stopThreshold: options.stopThreshold,
|
|
7723
|
+
onChunk: options.onChunk
|
|
7724
|
+
});
|
|
7725
|
+
}
|
|
7726
|
+
/**
|
|
6648
7727
|
* OuteTTS speak path: lazily build the OuteTTS engine (loading the chosen preset
|
|
6649
7728
|
* voice's speaker JSON + the folded DAC codec weights from the mirror), rebuilding
|
|
6650
7729
|
* if the requested voice changed. Mirrors the Kani public shape (temperature/topP/topK).
|
|
@@ -7427,6 +8506,7 @@ var WebGPUEngine = class WebGPUEngine {
|
|
|
7427
8506
|
this._kaniTTS?.destroy();
|
|
7428
8507
|
this._outeTTS?.destroy();
|
|
7429
8508
|
this._parlerTTS?.destroy();
|
|
8509
|
+
this._gepardTTS?.destroy();
|
|
7430
8510
|
this.visionExecutor?.destroy();
|
|
7431
8511
|
if (this._ctx) {
|
|
7432
8512
|
clearPipelineCache(this._ctx.device);
|
|
@@ -7461,5 +8541,5 @@ var WebGPUEngine = class WebGPUEngine {
|
|
|
7461
8541
|
};
|
|
7462
8542
|
|
|
7463
8543
|
//#endregion
|
|
7464
|
-
export {
|
|
7465
|
-
//# sourceMappingURL=gpu-
|
|
8544
|
+
export { generateGepardBackboneGraph as A, generateGemma4VisionGraph as B, audioTokensToDacCodes as C, parseOuteTtsConfig as D, generateOuteTtsBackboneGraph as E, RequestScheduler as F, resolveGemma4VisionInfo as H, KaniTTS as I, generateQwen3_5VisionGraph as L, gepardCodesToLatent as M, parseGepardConfig as N, GepardTTS as O, Swarm as P, dequantizeGemma4VisionProjection as R, loadOuteSpeaker as S, generateDacSpeechDecoderGraph as T, patchGemma4VisionClips as V, smartResize as _, buildGemma4PosEmbeds as a, OuteTTS as b, buildMRoPECosSin as c, buildPositionIds as d, buildRotaryCosSin as f, preprocessImageGemma4 as g, preprocessImage as h, buildGemma4PoolMatrix as i, gepardChannelLevels as j, buildGepardTextLayout as k, buildMRoPEPositionIds as l, mropeFreqDims as m, GEMMA4_IMAGE_PROCESSOR as n, buildGemma4RotaryCosSin as o, buildVisionPositionTensors as p, QWEN3_5_IMAGE_PROCESSOR as r, buildGemma4VisionPositionTensors as s, WebGPUEngine as t, buildPosEmbeds as u, VisionExecutor as v, dacOutputLength as w, buildOutePromptString as x, ParlerTTS as y, dequantizeMLXProjection as z };
|
|
8545
|
+
//# sourceMappingURL=gpu-CU_Mldk0.mjs.map
|