From first principles. From zero. From Rust.
Companion to
ARCHITECTURE.md,ARCHITECTURE_V2.md, andARCHITECTURE_V3.md. This document covers only what v4.0 adds on top of the completed v3.0.0 architecture. Sections continue numbering from v3's Section 52. Everything in the three prior documents is unchanged and continues to work exactly as documented. v4.0 is the final planned version of aarambh-studio as an application β see Β§69.
- What's New in v4.0
- Updated Workspace β 20 Library Crates
- Multi-Head Latent Attention (MLA)
- Audio Modality
- Sparse/Grouped MoE Dispatch
- Multi-Node Distributed Training
- Test-Time Compute Scaling
- RLAIF
- Tool Execution With Sandboxing
- Multi-Agent Orchestration
- Retrieval-Augmented Generation (RAG)
- Model Merging / Weight Averaging
- Public Inference Server + Prefix Caching
- System Role, Chat-Template Versioning, and Context Management
- Red-Team / Adversarial Safety Evaluation
- Model Card
- Updated Dependency Layers
- Updated Memory & Compute Estimates
- Updated Hardware Strategy
- Final Release Contract β Why v4.0 Is the Last Version
v4.0 closes out aarambh-studio with twelve additions across four themes:
Attention completion: Multi-Head Latent Attention (Β§55) joins Gated DeltaNet and DSA (both v3) as the third attention kind, completing the hybrid-attention family at the same maturity level current open-weight frontier labs ship.
Modality and scale completion: audio (Β§56) closes the last major input modality gap; sparse MoE dispatch (Β§57) resolves an optimisation deferred since v2; multi-node training (Β§58) extends single-node data-parallelism (v2) to genuinely larger scale.
Reasoning-quality completion: test-time compute scaling (Β§59) and RLAIF (Β§60) round out the alignment/reasoning toolkit β GRPO (verifier-based, v1), DPO (preference-based, v2), and now RLAIF (AI-feedback-based, v4) as three complementary training-time signals, plus a genuinely new inference-time axis in test-time scaling.
Agency and deployment completion: sandboxed tool execution (Β§61) and multi-agent orchestration (Β§62) close the arc opened by v2's emit-only tool calling; RAG (Β§63) and model merging (Β§64) are independent utility completions; the public inference server (Β§65) is the final, highest-risk deployment capability.
Contract and safety completion: Β§66β68 are a retrofit pass, not new capability β formalizing the system role and chat-template version tag that were reserved but under-specified since v1, a systematic red-team evaluation of the complete v4.0 surface, and a canonical, assembled- not-hand-written model card. These close gaps identified by auditing the project's own documentation against what it actually shipped.
This is a completion release, not a foundation release the way v1 was, or a growth release the way v2 and v3 were. Β§72 explains why v4.0 is the final planned version.
Two new crates. Everything else extends in place β no crate is removed or renamed, matching the discipline every version has held since v1.
aarambh-studio/
βββ Cargo.toml
βββ ARCHITECTURE.md / ARCHITECTURE_V2.md / ARCHITECTURE_V3.md / ARCHITECTURE_V4.md
βββ ROADMAP.md / ROADMAP_V2.md / ROADMAP_V3.md / ROADMAP_V4.md
βββ SELF_LEARNING.md / SELF_LEARNING_V2.md / SELF_LEARNING_V3.md / SELF_LEARNING_V4.md
β
βββ crates/
β β ...Layers 0β6 from v1.0.0/v2.0.0/v3.0.0, extended (see Β§55β65)...
β β
β βββ aarambh-studio-audio/ β NEW, LAYER 3: Audio modality
β β βββ src/
β β βββ lib.rs
β β βββ encoder.rs β frozen pretrained audio encoder
β β βββ preprocess.rs β mel-spectrogram extraction
β β βββ projector.rs β trainable audio->llm projector
β β βββ fusion.rs β audio-token interleaving
β β βββ instruct_data.rs β AudioQaExample schema
β β
β βββ aarambh-studio-retrieve/ β NEW, LAYER 4: RAG
β βββ src/
β βββ lib.rs
β βββ embedding.rs β contrastive text-embedding head
β βββ index.rs β from-scratch ANN index
β βββ chunking.rs β document chunking policy
β βββ retrieval.rs β RetrievalPipeline
β
βββ aarambh-studio/ β LAYER 6: CLI binary
βββ src/cmd/
βββ ...train.rs / infer.rs / finetune.rs / quantise.rs /
β convert.rs / eval.rs / serve.rs / agent.rs...
βββ retrieve.rs β NEW: `aarambh-studio retrieve`
βββ merge.rs β NEW: `aarambh-studio merge`
| Crate | v4.0 additions |
|---|---|
aarambh-studio-nn |
mla.rs (Β§55), dispatch.rs extended with DispatchKind::Sparse (Β§57) |
aarambh-studio-model |
attention_schedule accepts LatentMLA entries, MoeConfig.dispatch: DispatchKind |
aarambh-studio-train |
MLA retrofit recipe, multi-node distributed.rs extended (Β§58) |
aarambh-studio-weights |
Partial-checkpoint loading extended for MLA layers, merge.rs (Β§64) |
aarambh-studio-tokenizer |
<audio>/<audio_end> reserved tokens |
aarambh-studio-finetune |
vlm_dora.rs extended for audio, rlaif.rs (Β§60) |
aarambh-studio-inference |
best_of_n.rs, self_consistency.rs, process_reward.rs (Β§59), KV-cache report tooling (Β§55) |
aarambh-studio-eval |
audio_qa_subset.rs, --best-of-n comparison flag |
aarambh-studio-agent |
sandbox.rs, authorization.rs (Β§61), orchestrator.rs (Β§62) β extends the crate v3 Β§37 scaffolded |
aarambh-studio-serve |
auth.rs, prefix_cache.rs, tenant_isolation.rs (Β§65) |
v1.0.0: 14 crates (13 library + 1 binary)
v2.0.0: 17 crates (16 library + 1 binary)
v3.0.0: 19 crates (18 library + 1 binary)
v4.0.0: 21 crates (20 library + 1 binary)
Six library crates added across the project's entire history. Zero renamed. Zero removed. This is the architectural claim v2's blog post made explicit and v3/v4 continue to hold: growth in one direction has never required touching the transformer core in a way that breaks another.
Crate: aarambh-studio-nn (mla.rs) | Depends on: v1 Β§6.3 (GQA/RoPE), v2 Β§21 (YaRN/NTK), v3 Β§29 (HybridAttentionSchedule)
Status: Implemented in v4.0.0-alpha.1 (Phase 41).
MlaAttentionandMlaCacheship incrates/aarambh-studio-nn/src/mla.rs;AttentionKind::LatentMLAandMlaConfigextend the schedule; the partial-checkpoint retrofit and--kv-cache-reportare wired through. Seedocs/phase41_mla.mdfor usage.
v3 gave the model two ways to reduce the cost of a growing KV cache: Gated DeltaNet (Β§29, linear attention β constant-size recurrent state) and DSA (Β§30, sparse attention β attend to a learned subset of positions). Neither directly attacks the storage cost of the remaining full-attention layers, which still cache a full key and value vector per head, per token.
MLA compresses what gets cached, not what gets computed. Instead of caching per-head K and V directly, a token's hidden state is down-projected once into a single shared latent vector; per-head keys and values are then reconstructed from that one latent via small per-head up-projection matrices β which are ordinary trainable weights, not part of the cache.
hidden_state (d_model)
β
βΌ
down_proj: d_model -> latent_dim (e.g. 4096 -> 512)
β
βΌ
c_kv (this is the ONLY thing cached per token, for MLA layers)
β
ββββΆ per-head up_proj_K^(h): latent_dim -> head_dim (weights, not cached)
β β
β βΌ
β K^(h) (reconstructed per head, at attention time)
β
ββββΆ per-head up_proj_V^(h): latent_dim -> head_dim (weights, not cached)
β
βΌ
V^(h) (reconstructed per head, at attention time)
Decoupled RoPE. A naively-compressed latent cannot carry an already-rotated (position-encoded) key β rotation is head-dimension- specific and applying it before compression would defeat the point of sharing one latent across heads. MLA splits each head's query and key into two parts: a larger "nope" (no positional encoding) part derived straight from the compressed latent, and a small separate "rope" part that is rotary-encoded and cached on the side, at a much smaller per-head width than a full key would need:
query/key (head_dim)
β
ββββΆ nope part (larger slice) β derived from c_kv, no RoPE applied
β
ββββΆ rope part (smaller slice) β separately cached, RoPE applied
normally (v2 Β§21's YaRN/NTK scaling still applies here
unchanged)
The cache for an MLA layer, per token, is therefore `{c_kv (latent_dim)
- rope_half (small, per-head or shared, per config)}` β substantially smaller than a full per-head K and V cache at typical configurations, while per-head expressiveness is preserved through the up-projection weights at attention time.
HybridAttentionSchedule (v3 Β§29) already supported mixing Full and
GatedDeltaNet layers by index. v4 extends the schedule to a three-way
choice:
enum AttentionKind {
Full, // v1 GQA + RoPE/YaRN, unchanged
GatedDeltaNet, // v3 Β§29, unchanged
LatentMLA, // v4, new
}A schedule with zero LatentMLA entries reproduces v3.0.0 exactly β
the same backward-compatibility discipline every attention change since
v1 has held. A schedule can now, for example, use DSA-style sparse full
attention for one layer, Gated DeltaNet for the next three, and
LatentMLA for a fourth β the schedule is per-layer and fully
configurable, not a global architecture choice.
Following the exact pattern v3 Β§29 established: MLA layers are added to an existing v3.0.0 checkpoint via continued pretraining, not a from-scratch rebuild. Scheduled layers are reinitialised with fresh MLA parameters; every other layer's weights load unchanged from the v3 checkpoint. Training proceeds at a reduced learning rate so the untouched layers do not drift meaningfully while the new layers learn.
Same discipline as v2 Β§26 (MoE) and every attention change since:
retrofit success is judged by the eval harness (v2 Β§17) reporting
scores within a documented tolerance band of the pre-retrofit baseline,
and by a KV-cache memory report (--kv-cache-report) showing the
measured bytes/token reduction at long context β not by assuming
compression helps because the mechanism is theoretically sound.
Crate: aarambh-studio-audio (new) | Depends on: v2 Β§24β25 (vision fusion pattern), v1 Β§7 (thinking engine)
v2 Β§24 established a pattern for adding a modality without touching the
decoder: a frozen, pretrained encoder converts raw input into a grid of
embeddings; a small trainable projector maps those into the decoder's
d_model space; the result is spliced into the token sequence as
ordinary-looking tokens. v3 Β§35β36 reused this pattern for video and
documents. v4 reuses it a third time for audio, changing only the
domain-specific preprocessing step.
Raw audio waveform
β
βΌ
Mel-spectrogram extraction (local, pure-Rust/system-library decode β
no network calls, no Python audio ML tooling)
β
βΌ
FrozenAudioEncoder (pretrained, ~40-90M params, loaded as SafeTensors
via candle-core β same loading path as every other encoder in the
project)
β
βΌ
Projector MLP (trainable): audio_d_model -> hidden -> llm_d_model
β
βΌ
N "audio tokens" in llm_d_model space
β
βΌ
Spliced into the input sequence at the <audio> special token position
β
βΌ
...the rest of the decoder, completely unmodified...
Identical structure to v2 Β§25's vision recipe:
- Projector-only stage. Everything else frozen; the projector trains alone on audio-captioning-style data, learning to map audio embeddings into a space the (frozen) decoder can already interpret reasonably.
- Instruction-tuning stage. The projector continues training alongside a DoRA-adapted (v2 Β§23) LLM on open-ended audio-QA data β full fine-tuning at this stage remains out of reach of the free Kaggle compute budget, exactly the same constraint that made DoRA the right choice for vision in v2.
Because fusion happens before the decoder sees the sequence, nothing
about the thinking engine (v1 Β§7), grammar-constrained tool calling
(v2 Β§30), or long-horizon tool chains (v3 Β§46) needs to change β a
<think> block or a tool call generated after audio tokens behaves
identically to one generated after text-only or image-only context.
This is the same composability guarantee every prior modality addition
has held, and it continues to hold because the fusion mechanism itself
never changes, only what feeds it.
Following the same explicit-scope discipline as every other modality phase: this covers audio understanding (the model can be asked about audio it's given), not audio generation β aarambh-studio does not produce audio output. That capability, along with the broader question of a dedicated audio-generation stack, belongs to a separate project entirely and is intentionally out of scope here.
Delivered in v4.0.0-alpha.2 (Phase 42). The
aarambh-studio-audiocrate,<audio>/<audio_end>tokenizer tokens,convert --upgrade-audio-vocab,[vision.audio]config,finetune audio-dora,infer --audio, theaudio-qaeval task, and the full test suite are implemented. See docs/phase42_audio.md. WAV PCM decode and mel-spectrogram extraction are pure-Rust from first principles (norustfftor audio-ML dependency); MP3/FLAC/Ogg decode is future work.
Crate: aarambh-studio-nn (dispatch.rs, extended) + aarambh-studio-core (DispatchKind) | Depends on: v2 Β§26 (MoE), v3 Β§40 (fine-grained MoE) | Status: shipped in v4.0.0-alpha.3 (Phase 43)
v2 Β§26 shipped MoE with dense masked-matmul dispatch β every expert computes on every token, then gets masked and weighted by the router β explicitly documented as "simplest-correct first, sparse dispatch is a future optimisation." v3 Β§40 extended MoE to fine-grained routing with a shared expert but kept the same dense dispatch, carrying the same deferred note forward a second time. v4 finally resolves it.
enum DispatchKind {
DenseMasked, // v2/v3 behaviour β kept as CPU fallback and
// correctness reference, default for exact backward
// compatibility
Sparse, // v4, new β GPU-only real benefit
}Sparse dispatch, conceptually:
Router produces top-k expert assignment per token (unchanged from v2)
β
βΌ
Gather: sort/group tokens by assigned expert into contiguous
per-expert batches
β
βΌ
Each expert's FFN computes ONLY on its assigned token group
(grouped GEMM on CUDA) β not the full sequence, unlike DenseMasked
β
βΌ
Scatter: results written back into original token order
The load-balancing auxiliary loss (v2 Β§26) is completely unchanged β
DispatchKind only changes the compute path, never the objective the
router is trained against, so a checkpoint's routing behavior is
identical regardless of which dispatch kind executes it.
Sparse dispatch's benefit β skipping compute for unassigned experts β
only pays off when the grouped-GEMM kernels have enough tokens per
expert per batch to be worth dispatching separately, which is a
GPU-batch-scale assumption. On CPU, DispatchKind::Sparse is
documented to fall back to DenseMasked behaviour rather than attempt
a sparse path that would be slower, not faster, at CPU batch sizes.
This is stated plainly rather than silently downgraded.
Correctness is proven first β sparse output must numerically match the dense-masked reference within tolerance on identical inputs β before throughput is even discussed. The throughput claim itself is a wall-clock measurement at Kaggle GPU scale, reported honestly, the same discipline v2 Β§29 (speculative decoding) and every MoE-related claim since have followed.
The shipped sparse_grouped_dispatch (aarambh-studio-nn/src/dispatch.rs)
is fully candle-native and differentiable, using no custom kernel:
flatten tokens β [N, H]; flatten assignments β [N*top_k]
arg_sort by expert id (no-grad permutation) β grouped order
gather token-ids + weights into grouped order (gather is differentiable)
per-expert boundaries scanned on host (O(N*top_k))
for each expert e with count_e > 0:
index_select e's token group β [count_e, H] (differentiable)
expert.forward(group) (matmul on group ONLY)
mul group_weights (differentiable)
index_add scatter back into [N, H] (differentiable)
reshape β [batch, seq, H]
The grouping permutation is a discrete index (no gradient), computed with
arg_sort_last_dim (a no-grad op); every value that flows into the loss
remains differentiable through candle's gather/index_select/index_add.
On CUDA, candle routes these ops plus the per-expert matmul to cuBLAS β
a genuine grouped-GEMM path that skips non-routed experts. A fused
single-kernel grouped-GEMM .cu file remains a documented future
optimisation; the current path already realises the throughput win without
it, and is verifiable on CPU for correctness.
effective_dispatch_kind(configured, device) selects Sparse only on a
CUDA device; CPU falls back to DenseMasked regardless of configuration
(the "GPU only pays off" policy). MoeFfn::dispatch_kind() exposes the
configured kind. QAT calibration (forward_with_capture) always uses the
dense reference to observe full per-expert activation distributions. See
docs/phase43_sparse_moe.md for the full test matrix and configs.
Crate: aarambh-studio-train (distributed.rs, extended) | Depends on: v2 Β§27 (single-node NCCL data parallel)
v2 Β§27 proved single-node, multi-GPU data-parallel training via NCCL. v4 extends the same data-parallel approach β not model or pipeline parallelism β across multiple nodes.
World: N nodes Γ M GPUs per node = world_size total ranks
Node rank (which machine) Γ local rank (which GPU on that machine)
β
βΌ
NCCL rendezvous over TCP across nodes
β
βΌ
Sharded data loader: each of the world_size ranks sees a disjoint
slice of the global batch β same principle as v2's single-node
sharding, extended to the larger world_size
β
βΌ
Gradient all-reduce across ALL ranks, all nodes β same math as v2 Β§27,
different (larger) topology
β
βΌ
Rank-zero of node-zero specifically logs and checkpoints β prevents
duplicate checkpoints from every node's own local rank zero
Kaggle notebooks do not provide genuine multi-node access β this is stated plainly rather than glossed over. Validation of this phase realistically happens one of two ways: externally-provisioned machines (a free or low-cost cloud tier) tunnelled together for a real NCCL multi-node run, or a documented single-machine simulation using multiple processes over loopback networking, which exercises the code path's correctness without genuinely separate hardware. Any throughput numbers reported for this phase are explicitly labelled with which validation path produced them β a simulation-derived number is never presented as a real-hardware benchmark.
This phase implements exactly one fault-tolerance behavior: a single retry on a transient NCCL rendezvous timeout, after which the run fails loudly. Full elastic training (nodes joining/leaving mid-run, checkpoint-and-resume on node failure) is explicitly out of scope β a genuinely large feature in its own right that this project does not attempt to half-implement.
Crate: aarambh-studio-inference (best_of_n.rs, self_consistency.rs, process_reward.rs) | Depends on: v1 Β§7 (thinking engine), v2 Β§29 (speculative decoding), v1 Β§11/v2 Β§22 (verifiers)
The thinking engine (v1 Β§7) controls how many tokens one generation spends reasoning before answering (None/Low/Medium/High/Max β v3 Β§48 added Max). Test-time compute scaling is a different axis entirely: generate multiple independent candidate completions, then select among them. The two compose freely β each of the N candidates can itself use any thinking mode.
Prompt
β
βΌ
Generate N candidates in parallel (each independently uses the
existing sampler; speculative decoding, v2 Β§29, accelerates each
candidate independently where enabled)
β
βΌ
SelectionStrategy:
β
ββ Verifier β for checkable tasks (math/code): run the existing
β MathVerifier/CodeVerifier (v1 Β§11, v2 Β§22)
β against each candidate, select a verified-correct
β one if any exists
β
ββ SelfConsistency β extract each candidate's final answer, majority
β vote across all N β no verifier required, works
β for tasks with a well-defined final answer even
β without a hard checker
β
ββ ProcessReward β a small classifier head, trained on GRPO/DPO-style
contrastive step data, scores intermediate
reasoning steps (not just final answers) and
selects the highest-scoring trace β used for
open-ended tasks where neither a hard verifier
nor a clean final-answer extraction exists
N=1 reproduces single-sample generation exactly β this is the
backward-compatibility floor, same as every optional feature in the
project. Larger N scales compute roughly linearly (N independent
generations), which is why the roadmap gates larger N to Kaggle and
keeps small N (2β4) available on i3, following the exact budget
precedent v1 Β§12's self-learning N-completion sampling already
established for CPU-safe operation.
Whether Best-of-N with a given selection strategy actually improves
accuracy on a given task is an eval-harness question (v2 Β§17), answered
per task via eval --compare --best-of-n, not assumed from the
technique's general reputation. Different tasks and selection
strategies are expected to show different β sometimes negligible β
deltas; the scorecard is the source of truth, not the roadmap's prose.
Crate: aarambh-studio-finetune (rlaif.rs) | Depends on: v1 Β§11 (GRPO), v2 Β§28 (DPO), v1 Β§12 (self-learning N-completion sampling)
GRPO (v1 Β§11) needs a hard verifier β it works when correctness is checkable (math, code, format compliance). DPO (v2 Β§28) needs a preference dataset β static pairs of (chosen, rejected) completions, whether from a public dataset or hand-labelled. Neither covers "generate fresh preference signal automatically, for qualities that are neither checkable nor already labelled" β open-ended chat quality being the clearest example. RLAIF fills exactly that gap.
Self-sample N candidate completions for a prompt (reuses v1 Β§12's
N-completion sampling infrastructure directly, unchanged)
β
βΌ
Judge model (a frozen checkpoint β either an earlier stage of the same
model, or the Large scale judging Small/Tiny outputs) scores pairs of
candidates: "which is better, and roughly by how much"
β
βΌ
Position-swap bias correction: EVERY pair is judged twice, in both
A/B orderings. Judges have a documented first-position bias; when the
two orderings disagree, the pair is down-weighted or discarded rather
than trusted at face value
β
βΌ
Output: (chosen, rejected) pairs β the EXACT SAME SCHEMA v2 Β§28's DPO
pipeline already consumes
β
βΌ
Feed directly into the existing, UNMODIFIED `finetune dpo`/`finetune
qdpo` training path
RLAIF is deliberately architected as a data-generation front end,
not a new training objective β dpo_loss (v2 Β§28) does not change at
all. This keeps the numerically-stable two-class log-softmax
formulation v2 Β§28 already got right, rather than re-deriving a new
loss function with its own numerical edge cases.
| Method | Signal source | Best for |
|---|---|---|
| GRPO (v1 Β§11) | Hard verifier | Math, code, format-checkable tasks |
| DPO (v2 Β§28) | Static human preference data | Chat quality, where labelled pairs exist |
| RLAIF (v4 Β§60) | AI judge, self-sampled | Chat quality, where no labelled pairs exist yet |
All three remain available and complementary β v4 does not deprecate or replace GRPO or DPO, it adds a third signal source for the cases neither of the first two covers well.
An RLAIF-tuned checkpoint's win rate is reported against the pre-RLAIF
baseline using v2 Β§28's existing preference eval task β an honest
delta, not a claimed win, the same discipline every alignment claim in
this project has held since v1.
Crate: aarambh-studio-agent (sandbox.rs, authorization.rs) | Depends on: v2 Β§30 (grammar-constrained emit-only tool calling), v3 Β§46 (multi-step chains, still emit-only)
The boundary has moved twice already: v2 Β§30 gave the model grammar-constrained JSON tool calls it could emit but never execute. v3 Β§46 let it emit sequences of tool calls, using real intermediate results β but the execution of each call still happened outside aarambh-studio entirely, by the developer integrating it. v4 Β§61 is the first phase where aarambh-studio itself is permitted to execute a tool call β and it is scoped as narrowly as the risk demands.
trait ToolExecutor {
fn name(&self) -> &'static str; // exact match required, no
// pattern matching, no fuzzy
// resolution
fn execute(&self, args: ValidatedArgs) -> Result<ToolResult, ExecError>;
}There is no generic "run a shell command" or "eval this code"
ToolExecutor anywhere in the crate, by design β every executor
implements one specific, named capability (e.g. a whitelisted read-only
file lookup within a fixed working directory, or an HTTP GET restricted
to an explicitly whitelisted domain list). An unrecognised tool name is
a hard refusal, never a best-effort fallback attempt at
interpreting what the model might have meant.
Model emits grammar-constrained JSON tool call (v2 Β§30)
β
βΌ
JSON validated against declared schema β malformed or partially-
streamed calls are NEVER executed, matching v2 Β§30's existing
atomicity guarantee for tool-call output
β
βΌ
Tool name checked against the closed allowlist:
not in allowlist β hard refusal, chain records the refusal, no
execution attempt of any kind
in allowlist β continue
β
βΌ
Tool name checked against operator authorization (see below)
not authorized β hard refusal
authorized β continue
β
βΌ
Execute inside a bounded envelope:
- explicit timeout (execution killed if exceeded)
- explicit memory/CPU ceiling (execution killed if exceeded)
- no filesystem access beyond what the specific ToolExecutor exposes
- no network access beyond what the specific ToolExecutor exposes
β
βΌ
ToolResult re-enters the chain via v3 Β§46's existing
result_ingestion.rs β no new ingestion mechanism, execution is purely
additive to what v3 already built
A model can declare any tool in its schema and request execution of
anything it declares. Whether that request is ever actually carried out
depends entirely on what the operator explicitly enabled at server or
CLI startup (authorization.rs) β the model's own confidence, phrasing,
or reasoning about why a tool call is justified has no bearing on
whether it gets executed. This separation is deliberate: it keeps the
attack surface for "convince the model to execute something dangerous"
bounded by what a human operator pre-approved, not by what the model
can be talked into requesting.
Crate: aarambh-studio-agent (orchestrator.rs) | Depends on: v4 Β§61 (sandboxed execution) β hard dependency, must ship after
Orchestrator's own reasoning produces a DelegationPlan: a set of
independent sub-tasks
β
βΌ
Each sub-task becomes its own sub-chain β a full GenerationSession
(reusing v2 Β§31's server session abstraction), with:
- its own sandboxed tool scope (v4 Β§61)
- its own execution timeout budget
β
βΌ
Sub-chains run (conceptually parallel; actual concurrency bounded by
configured limits below)
β
βΌ
Result aggregation: each sub-chain's output re-enters the
orchestrator's own context via the same ToolResult ingestion pattern
(v3 Β§46, v4 Β§61) β applied recursively, not a new mechanism
Three ceilings are enforced as operator-set configuration, never as something the orchestrator's own output can influence:
- Maximum sub-agent count β an orchestrator cannot request unbounded fan-out regardless of how it reasons about the task.
- Maximum total execution time budget β the sum across all sub-chains, not per sub-chain, so many small sub-agents cannot collectively exceed the same ceiling one large one would hit.
- Sandbox scope containment β a sub-agent's authorized tool scope
can only be a subset of what the orchestrator itself was
authorized for (v4 Β§61's
authorization.rs). Orchestration can never be used as an escalation path to reach tools the operator did not explicitly enable at the top level.
One sub-agent's failure or execution error is contained to that sub-chain's own result β it does not corrupt or silently swallow sibling sub-agents' results, and the orchestrator's aggregation step receives an explicit failure marker for that sub-chain rather than a missing or malformed entry.
Crate: aarambh-studio-retrieve (new) | Depends on: none within the model itself β deliberately a prompt-level augmentation, not a model-internals change
RAG is implemented as prompt augmentation, not a new fusion mechanism inside the decoder. This is a deliberate simplicity choice, the same instinct that chose prefix fusion over cross-attention for vision back in v2 Β§24: retrieved text chunks are spliced into the existing prompt-construction pipeline (the same code path that already assembles system prompt + chat history + user turn) as additional context ahead of the user's question. The decoder never knows the difference between retrieved context and any other text in its prompt.
Document corpus
β
βΌ
chunking.rs β fixed-size chunks with configurable overlap
β
βΌ
embedding.rs β a small, dedicated, CONTRASTIVELY-TRAINED text-embedding
head (separate from the main decoder, CPU-capable) turns each chunk
into a fixed-size vector
β
βΌ
index.rs β a FROM-SCRATCH approximate-nearest-neighbour index
(graph-based, pure Rust, no FFI to an external vector-search library)
β
βΌ
[ index persisted to disk, reloadable ]
β
βΌ
Query time: embed the query -> search the index -> top-k chunks
returned
β
βΌ
Chunks spliced into the prompt ahead of the user's question, using the
EXISTING prompt-assembly path β no decoder-level change whatsoever
Consistent with the project's standing policy (no PyTorch, no ONNX
Runtime, no Python FFI, everything through candle), the default and
tested retrieval index is implemented in pure Rust rather than binding
to an external vector-database service. An external vector-DB adapter
may exist as an optional plug-in, but it is not the default path and is
not what the eval-harness retrieval tests validate against.
Retrieval quality (recall on a small labelled holdout) and end-to-end usefulness (a factual eval-harness task's score with vs without RAG enabled) are both reported as measured deltas, following the same discipline every capability claim in this project has held since v2 Β§17's eval harness first shipped.
Crate: aarambh-studio-weights (merge.rs) | Depends on: v2 Β§23 (DoRA), v2 Β§28 (DPO), v4 Β§60 (RLAIF), v3 Β§40 (fine-grained MoE), v3 Β§42 (distillation) β needs their checkpoint variants to exist
Model merging is only useful once genuinely different, independently- trained checkpoint variants exist to merge. Earlier in the project's history there was nothing meaningful to merge; by v4, DoRA-tuned, DPO-tuned, RLAIF-tuned, MoE, and distilled variants all exist side-by-side, making this the first point where merging has real utility.
SLERP (spherical linear interpolation) between two or more compatible checkpoints:
merged = slerp(checkpoint_a, checkpoint_b, weight)
At weight = 1.0 or 0.0, this reproduces one input checkpoint
exactly β the backward-compatibility floor for the merge tool itself.
Task-vector arithmetic, for combining independently-tuned deltas onto one shared base:
delta_i = tuned_checkpoint_i - base_checkpoint
merged = base_checkpoint + Ξ£ (scale_i Γ delta_i)
This lets you, for example, combine a math-focused DoRA delta and a chat-focused DPO delta onto the same base checkpoint, each scaled independently.
Both methods validate tensor shapes and architecture compatibility before producing any output β merging checkpoints from architecturally incompatible configs (different hidden sizes, different attention schedules, mismatched vocabularies) fails loudly at the validation step. This project has never silently produced a corrupted or nonsensical checkpoint from a mismatched operation, and merging is no exception.
A merged checkpoint's eval-harness scorecard is reported honestly against both of its input checkpoints' individual scores β merging is not assumed to be strictly additive or strictly beneficial, following the identical "measure, don't assume" framing v2 Β§26 established for MoE and every subsequent capability claim has held since.
Crate: aarambh-studio-serve (auth.rs, prefix_cache.rs, tenant_isolation.rs) | Depends on: v2 Β§31 (local OpenAI-compatible server)
v2 Β§31 and v3 both kept the inference server local-only, deliberately. v4 Β§65 is the first phase that opens it to genuinely multi-tenant, authenticated traffic β which is why it is sequenced as the very last feature phase in the entire v1βv4 roadmap, only after the model underneath it (attention, MoE, agentic tool use) is fully settled.
Incoming request
β
βΌ
auth.rs: API-key validation (replaces v2 Β§31's simple loopback-
exemption/bearer-token model with real per-key identity)
invalid/missing key β rejected BEFORE admission into the continuous
batcher (v2 Β§31) β never queued, never
partially processed
β
βΌ
Per-key rate limiting (requests/minute, tokens/minute) β enforced at
the same admission point
β
βΌ
prefix_cache.rs: hash the prompt's prefix; check for a cached KV state
hit β reuse cached KV state, skip recomputing that portion of the
forward pass
miss β compute normally, cache the resulting prefix KV state (LRU
eviction under a configurable memory ceiling)
β
βΌ
tenant_isolation.rs: per-tenant resource ceilings within the EXISTING
bounded-admission continuous batcher (v2 Β§31, unchanged) β one
tenant's request burst cannot starve another tenant's already-admitted
requests
β
βΌ
...rest of the existing v2 Β§31 serving pipeline, unchanged...
Prefix caching is the single highest-leverage serving optimisation for exactly the traffic pattern v4's own agentic features (Β§61β62) generate β repeated system prompts, shared conversation prefixes across many tool-execution sub-chains from one orchestrator. Placing it in the same phase as multi-tenant auth is deliberate: the two are the pieces that make multi-tenant and agentic traffic economically and operationally viable on the same server.
This phase makes public, multi-tenant self-hosting possible; it does not make it the recommended default, and it is not a hosted product. There is no billing system. There is no horizontal auto-scaling. The loopback-only, unauthenticated local mode from v2 Β§31 remains the documented default for single-user, local use β Β§65 adds a capability, it does not change the recommended starting point.
Crate: aarambh-studio-tokenizer, aarambh-studio-safety, aarambh-studio-serve, aarambh-studio-inference/aarambh-studio-agent | Depends on: <|system|> token reservation (v1, ID 7), v2 Β§31 (server), v3 Β§46/v4 Β§61β62 (agentic chains), v4 Β§63 (RAG)
The <|system|> special token has been reserved at ID 7 since v1.0.0,
but no prior version documented a role, a precedence rule, or a chat-
template interaction for it. v4.0 closes that gap without changing a
single token ID:
<|system|>\n{operator-set instructions}\n<|user|>\n{user turn}\n<|assistant|>\n...
- Optional, single-use, leading position. A session may include at
most one
<|system|>turn, placed before any<|user|>turn. Omitting it entirely reproduces every prior version's<|user|>...<|assistant|>format exactly β this is purely additive. - Loss masking.
SftTrainer's existing rule (mask everything before the<|assistant|>position) already covers a leading system turn correctly by construction β no training-code change was needed, only the documentation of why. - Precedence over user input. System-turn content is always
operator- or application-supplied.
GenerationSession(v2 Β§31) never derives system-turn content from a user's own message β a user's message can only ever occupy the<|user|>position, which the existing prompt-injection guardrails (aarambh-studio-safety) already treat as untrusted. This is the system-side half of a defense whose user-side half (detecting"new system prompt:"-style injection attempts inside user input) has existed since v1.
The chat template's shape has changed with every version β v2 added
image tokens, v3 added video/document/tool tokens, v4 adds audio
tokens β with no prior version recording which shape a given checkpoint
expects. v4.0 adds a chat_template_version field to tokenizer config
and checkpoint metadata:
v1.0.0 template shape β chat_template_version = 1
v2.0.0 (+ image tokens) β chat_template_version = 2
v3.0.0 (+ video/document/tool tokens) β chat_template_version = 3
v4.0.0 (+ system role formalized, + audio tokens) β chat_template_version = 4
A server refuses to load a checkpoint whose declared version it does not recognize, with a clear startup error β never a silent misinterpretation of an older or newer prompt structure. This is the same fail-loud-not-silent discipline every hardware and dispatch gate in this project has held since v2.
Never previously documented, and made a real question (not a theoretical one) by v4's own long agentic chains (Β§61β62) and RAG-augmented prompts (Β§63):
enum ContextTruncationPolicy {
SlidingWindow, // drop oldest non-system turns first; the system
// turn, if present, is NEVER evicted
Summarize, // replace evicted turns with a generated summary
// turn, reusing the project's existing self-
// critique-style summarization capability
Reject, // refuse to proceed rather than silently drop
// context β the mandatory default for anything
// safety- or execution-sensitive, e.g. sandboxed
// tool-execution sessions (Β§61) and orchestration
// (Β§62)
}One policy, referenced consistently by every long-context feature in the project, rather than each feature inventing its own ad hoc truncation behavior.
docs/SAMPLING_DEFAULTS.md consolidates temperature/top-p/top-k
guidance β previously scattered informally across three prior
architecture documents β into one canonical table, organized by use
case (deterministic tool-call generation, open-ended chat, creative
writing, math/code verification).
Crate: aarambh-studio-safety (redteam/) | Depends on: ARCHITECTURE.md Β§13 (safety layer), v4 Β§61 (sandboxed execution), v4 Β§65 (public server), v4 Β§66 (system-role precedence)
Every phase in this project ships its own unit-level safety tests (a malformed tool call is rejected, an unauthorized execution is refused, a PII pattern is redacted). Red-team evaluation is different in kind: one systematic, end-to-end adversarial pass run against the complete v4.0 surface near the end of the roadmap, specifically because Phase 65 (public server) and Phase 61 (execution) are the two highest-risk capabilities in the project's history and deserve a dedicated adversarial pass beyond what any single phase's own tests would think to cover in isolation.
struct AdversarialCase {
input: AdversarialInput, // text, or a full request shape
// targeting the server/execution
// surface
expected_outcome: ExpectedOutcome, // Refused | Sanitized | ExecutedSafely
category: String, // e.g. "system_turn_injection",
// "unauthorized_tool_execution",
// "orchestrator_bound_bypass",
// "auth_bypass_attempt"
}Corpus categories, each targeting a specific v4.0 boundary:
- Prompt-injection variants specifically targeting the system-turn precedence rule (Β§66) β attempts to make user input masquerade as system-level instruction.
- Attempts to get an unauthorized tool executed despite the closed- world allowlist (Β§61).
- Attempts to get an orchestrator (Β§62) to exceed its configured sub-agent count or time-budget ceilings.
- Attempts to bypass authentication or exhaust rate limits on the public server (Β§65).
Every case carries a labelled expected outcome; a failing case is surfaced plainly in the generated report, never silently excluded β the same "measure, don't assume" discipline that has governed every capability claim in this project since v2 Β§17's eval harness. Corpus content is hand-authored or drawn from free/public sources only, the same dataset-licensing policy every training/eval dataset in the project has followed since v1.
Crate: aarambh-studio-eval (model_card.rs) | Depends on: v2 Β§17 (eval harness), v4 Β§67 (red-team report)
Prior to v4.0, a released checkpoint's capabilities, limitations, and
provenance were described piecemeal across ARCHITECTURE*.md,
ROADMAP*.md, and README.md. MODEL_CARD.md consolidates this into
one document per released checkpoint configuration, generated from
real data rather than authored by hand each time:
ModelCard {
intended_use: String, // static metadata
training_data: Vec<DatasetEntry>, // static metadata, license-tagged
capabilities: EvalHarnessScorecard, // PULLED from an actual eval run
// (v2 Β§17), never hand-entered
known_limitations: Vec<String>, // static + eval-derived
redteam_summary: RedTeamReport, // PULLED from v4 Β§67's actual
// report, never hand-entered
hardware_requirements: String, // static metadata
chat_template_version: u32, // PULLED from v4 Β§66's version tag
}
Because the capabilities and red-team sections are pulled directly from real eval-harness and red-team runs rather than typed by hand, a model card cannot silently drift out of sync with a checkpoint's actual measured behavior β generation fails loudly if no red-team report is present for the checkpoint being documented, rather than shipping a model card with an empty or stale safety section.
Layer 0: aarambh-studio-core
Layer 1: aarambh-studio-tokenizer, aarambh-studio-data
Layer 2: aarambh-studio-nn, aarambh-studio-kernel
Layer 3: aarambh-studio-model, aarambh-studio-vision, aarambh-studio-audio (NEW)
Layer 4: aarambh-studio-weights, aarambh-studio-quant, aarambh-studio-retrieve (NEW)
Layer 5: aarambh-studio-train, aarambh-studio-finetune, aarambh-studio-inference,
aarambh-studio-safety, aarambh-studio-selflearn, aarambh-studio-eval,
aarambh-studio-distill, aarambh-studio-agent
Layer 6: aarambh-studio-serve, aarambh-studio (CLI binary)
aarambh-studio-audio sits at Layer 3 alongside aarambh-studio-vision β
same role, a modality-specific encoder/fusion crate consumed by the
model layer above it. aarambh-studio-retrieve sits at Layer 4 β it
depends on tokenization and its own small embedding model, but produces
prompt-level context rather than model-internal state, placing it
alongside the weights/quant utilities rather than inside the core
model stack. Neither new crate required any Layer 0β2 change β the
same pattern every prior modality and utility addition has held since
v2.
New allowed dependencies, scoped narrowly:
| Dependency | Allowed crates | Reason |
|---|---|---|
| Permissively-licensed audio decode (pure-Rust or system-library-bound) | aarambh-studio-audio |
Local mel-spectrogram extraction only β no network calls, no Python audio tooling |
| Small contrastive text-embedding architecture | aarambh-studio-retrieve |
Loaded as SafeTensors via candle-core, same policy as every other encoder in the project |
Still forbidden everywhere, unchanged since v1: PyTorch bindings,
ONNX Runtime, Python FFI, llama.cpp as a backend. All computation
goes through candle. An external vector-database adapter for RAG may
exist as an optional plug-in, but the from-scratch pure-Rust index
remains the default and the tested path.
| Addition | Approx. extra params (Small scale) | Approx. extra memory | Notes |
|---|---|---|---|
| MLA (per retrofitted layer) | Small β down/up-projections are narrow | KV cache reduction, not increase, at long context | Net memory win at the cache level despite added projection weights |
| Frozen audio encoder | ~40β90M (frozen, not trained) | Encoder weights + activation memory during forward pass only | Same class of cost as the frozen CLIP encoder (v2 Β§24) |
| Audio projector | Small (MLP) | Small | Same class as vision's projector |
| Sparse MoE dispatch | No parameter change | GPU: reduced active compute per token vs dense; CPU: unchanged (falls back to dense) | Compute-path change, not a parameter-count change |
| Test-time compute scaling (Best-of-N) | No parameter change | NΓ generation compute at inference time, scales with N | Inference-time cost only, no training-time or storage cost |
| Process reward model | Small (classifier head) | Small | Trained once, small additional checkpoint |
| RLAIF | No architecture change | Judge-model inference cost during data generation only | No cost at the trained checkpoint itself |
| RAG embedding head | Small, separate model | Small | CPU-capable by design |
| RAG index | N/A (not a model) | Scales with corpus size, disk-persisted | Not part of the model's own memory footprint |
| Model merging | No new architecture | Same footprint as any single input checkpoint | Merging does not increase parameter count |
Same headline as v3 Β§50: additions are additive and scoped, not architecture-wide multipliers β MLA's KV-cache section is the one place in v4 where the net effect is a reduction, not an addition.
| Workload | Hardware | Reasoning |
|---|---|---|
| MLA retrofit training | Kaggle | Continued-pretraining recipe, same class of cost as v3 Β§29's Gated DeltaNet retrofit |
| Audio projector/instruction training | Kaggle | Frozen encoder forward pass + DoRA training, same class as vision (v2 Β§25) |
| Sparse MoE dispatch (training/inference) | Kaggle (GPU) for the real benefit; i3 falls back to dense automatically | Sparse dispatch's payoff is GPU-batch-scale specific |
| Multi-node training | External multi-VM or documented simulation | Kaggle does not provide genuine multi-node access β stated plainly, not glossed over |
| Test-time compute scaling, small N (2β4) | i3 | Follows v1 Β§12's existing CPU-safe N-completion budget precedent |
| Test-time compute scaling, larger N | Kaggle | Cost scales with N; larger N gated to free GPU quota |
| RLAIF data generation | Kaggle | Judge-model inference at self-sampling scale |
| Sandboxed tool execution (text/tool-result only) | i3 | Lightweight orchestration overhead |
| Sandboxed tool execution (multimodal results) | Kaggle | Inherits the vision/video/document gate the moment any tool result is non-text |
| Multi-agent orchestration | i3 (orchestration) + Kaggle (any multimodal sub-chain) | Same inheritance rule as execution |
| RAG index build/query | i3 | CPU-capable embedding head and index by design |
| Model merging | i3 | Tensor arithmetic on disk-loaded checkpoints, no training involved |
| Public inference server load testing | Kaggle | Simulated concurrent-tenant load needs real GPU-backed serving to be meaningful |
Same discipline as every prior hardware table: a workload refuses to start on hardware it isn't gated for, with a clear error message, rather than silently degrading or producing misleading numbers.
v4.0.0 is confirmed as the final planned version of aarambh-studio as an application. This section states the reasoning plainly, the same way every other design decision in this project has been stated rather than left implicit.
What "final" means here. No ROADMAP_V5.md, ARCHITECTURE_V5.md,
or SELF_LEARNING_V5.md exists or is planned as of this release. The
project's roadmap arc β from v1's foundational pipeline, through v2's
first growth phase (vision, scale, tool calling, serving), v3's second
growth phase (hybrid attention, more modalities, forgetting
diagnostics), to v4's completion phase (attention family finished,
modality coverage finished, alignment toolkit finished, agentic
capability finished, deployment capability finished) β reaches a
natural, deliberately-declared end state here.
Release policy, corrected and finalised. v4.0.0 ships as a GitHub
source release. Every workspace crate remains publish = false. This
explicitly corrects the direction implied by v3 Β§40 (which described a
crates.io publish) β aarambh-studio is an application, not a library,
and the project's actual, confirmed policy going forward is: source
only, no crates.io publish, ever, consistent with v1.0.0's and
v2.0.0's original release policy. No pretrained checkpoint, adapter,
tokenizer, optimizer state, SafeTensors, or GGUF file is released at
any version, including this one.
What "done" looks like for a from-scratch project. Not every software project needs an indefinitely-growing roadmap. v4.0.0 represents the point where the core engineering questions this project set out to answer β can a complete LLM pipeline, including alignment, multimodality, efficient serving, and safe tool use, be built from scratch in Rust without Python β have been answered, demonstrated, and documented in full, phase by phase, exactly as they were tackled. The "prove it, document it, measure it before it ships" discipline that shaped every phase from v1 Β§0 through v4 Β§65 applies to this decision too: v4.0.0 is declared final because the roadmap's own stated goals are met, not because momentum ran out.