ruvector/examples/refrag-pipeline
rUv eafba64fa5
fix(security): RUSTSEC advisories + clippy hardening in RuVector (#504)
* fix(security): RUSTSEC advisories + clippy hardening in RuVector

- Replace all bare `partial_cmp().unwrap()` calls on f32/f64 with
  `.unwrap_or(Ordering::Equal)` to prevent panics on NaN values in
  sorting/max-by operations across ruvllm, ruvector-dag, prime-radiant,
  and rvagent-wasm (12 sites in production code).
- Add input validation guards to the HTTP search endpoint: reject k=0,
  k > 10_000, empty vectors, and vectors exceeding 65_536 dimensions,
  preventing memory exhaustion via unbounded allocations.
- Harden LocalFsBackend::execute in rvagent-cli with env_clear() +
  safe-env allowlist (SEC-005), deadline-based timeout enforcement, and
  1 MB output truncation, matching the security posture of LocalShellBackend.
- Remove 129 occurrences of the deprecated `unused_unit = "allow"` lint
  and 3 occurrences of the removed `clippy::match_on_vec_items` lint from
  Cargo.toml files workspace-wide; both are no-ops in current Rust/Clippy.
- All 653+ tests across ruvector-core, ruvector-server, ruvector-dag,
  rvagent-cli, and prime-radiant pass with zero failures.

Note: `bytes` is already at 1.11.1 (>= 1.10.0); `paste` 1.0.15 is a
transitive dependency with no semver fix available upstream; `cargo audit`
returns clean.

Co-Authored-By: claude-flow <ruv@ruv.net>

* fix(ci): cargo fmt + restore workspace unused_unit lint allow

- Run cargo fmt --all across all 9 files that drifted from rustfmt style
  (prime-radiant/energy.rs, ruvector-dag/bottleneck.rs+reasoning_bank.rs,
   ruvector-server/points.rs, ruvllm/pretrain_pipeline.rs+report.rs+registry.rs,
   rvagent-cli/app.rs, rvagent-wasm/gallery.rs)
- Add [workspace.lints.clippy] unused_unit = "allow" to root Cargo.toml;
  the per-crate entries removed in the security commit were still needed —
  moving to workspace-level is cleaner and restores -D warnings CI pass

Co-Authored-By: claude-flow <ruv@ruv.net>

* fix(ci): remove unneeded unit return type in ruvix bench

Removes `-> ()` from the Fn bound in run_benchmark_with_kernel
(crates/ruvix/benches/src/ruvix.rs:50) — triggers clippy::unused_unit
under -D warnings. Clippy prefers `Fn(&mut Kernel)` without explicit
unit return.

Co-Authored-By: claude-flow <ruv@ruv.net>

* fix(ci): resolve rustfmt and clippy unused_unit failures

- Run cargo fmt --all to fix long closure formatting in 9 files
  (energy.rs, bottleneck.rs, reasoning_bank.rs, points.rs,
  pretrain_pipeline.rs, report.rs, registry.rs, app.rs, gallery.rs)
- Add unused_unit = "allow" to [lints.clippy] in ruvix-bench and
  ruvector-mincut Cargo.toml files to suppress the unused_unit lint
  that was previously suppressed globally and now fires on two
  Fn(&mut T) -> () and FnMut() -> () function bounds

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-23 05:40:24 -04:00
..
benches fix(ci): Fix formatting and workflow permission issues 2025-12-26 22:11:57 +00:00
src fix(ci): Fix formatting and workflow permission issues 2025-12-26 22:11:57 +00:00
Cargo.toml fix(security): RUSTSEC advisories + clippy hardening in RuVector (#504) 2026-05-23 05:40:24 -04:00
README.md feat: Add REFRAG pipeline example demonstrating 30x RAG latency reduction 2025-11-27 20:59:23 +00:00

REFRAG Pipeline Example

Compress-Sense-Expand Architecture for ~30x RAG Latency Reduction

This example demonstrates the REFRAG (Rethinking RAG) framework from arXiv:2509.01092 using ruvector as the underlying vector store.

Overview

Traditional RAG systems return text chunks that must be tokenized and processed by the LLM. REFRAG instead stores pre-computed "representation tensors" and uses a lightweight policy network to decide whether to return:

  • COMPRESS: The tensor representation (directly injectable into LLM context)
  • EXPAND: The original text (for cases where full context is needed)

Architecture

┌─────────────────────────────────────────────────────────────────┐
│                      REFRAG Pipeline                             │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐       │
│  │   COMPRESS   │    │    SENSE     │    │    EXPAND    │       │
│  │    Layer     │───▶│    Layer     │───▶│    Layer     │       │
│  └──────────────┘    └──────────────┘    └──────────────┘       │
│                                                                  │
│  Binary tensor       Policy network     Dimension projection    │
│  storage with        decides COMPRESS   (768 → 4096 dims)       │
│  zero-copy access    vs EXPAND                                  │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Compress Layer (compress.rs)

Stores representation tensors in binary format with multiple compression strategies:

Strategy Compression Use Case
None 1x Maximum precision
Float16 2x Good balance
Int8 4x Memory constrained
Binary 32x Extreme compression

Sense Layer (sense.rs)

Policy network that decides the response type for each retrieved chunk:

Policy Latency Description
ThresholdPolicy ~2μs Cosine similarity threshold
LinearPolicy ~5μs Single layer classifier
MLPPolicy ~15μs Two-layer neural network

Expand Layer (expand.rs)

Projects tensors to target LLM dimensions when needed:

Source Target LLM
768 4096 LLaMA-3 8B
768 8192 LLaMA-3 70B
1536 8192 GPT-4

Quick Start

# Run the demo
cargo run --bin refrag-demo

# Run benchmarks (use release for accurate measurements)
cargo run --bin refrag-benchmark --release

Usage

Basic Usage

use refrag_pipeline_example::{RefragStore, RefragEntry};

// Create REFRAG-enabled store
let store = RefragStore::new(384, 768)?;

// Insert with representation tensor
let entry = RefragEntry::new("doc_1", search_vector, "The quick brown fox...")
    .with_tensor(tensor_bytes, "llama3-8b");
store.insert(entry)?;

// Standard search (text only)
let results = store.search(&query, 10)?;

// Hybrid search (policy-based COMPRESS/EXPAND)
let results = store.search_hybrid(&query, 10, Some(0.85))?;

for result in results {
    match result.response_type {
        RefragResponseType::Compress => {
            println!("Tensor: {} dims", result.tensor_dims.unwrap());
        }
        RefragResponseType::Expand => {
            println!("Text: {}", result.content.unwrap());
        }
    }
}

Custom Configuration

use refrag_pipeline_example::{
    RefragStoreBuilder,
    PolicyNetwork,
    ExpandLayer,
};

let store = RefragStoreBuilder::new()
    .search_dimensions(384)
    .tensor_dimensions(768)
    .target_dimensions(4096)
    .compress_threshold(0.85)  // Higher = more COMPRESS
    .auto_project(true)
    .policy(PolicyNetwork::mlp(768, 32, 0.85))
    .expand_layer(ExpandLayer::for_roberta())
    .build()?;

Response Format

REFRAG search returns a hybrid response format:

{
  "results": [
    {
      "id": "doc_1",
      "score": 0.95,
      "response_type": "EXPAND",
      "content": "The quick brown fox...",
      "policy_confidence": 0.92
    },
    {
      "id": "doc_2",
      "score": 0.88,
      "response_type": "COMPRESS",
      "tensor_b64": "base64_encoded_float32_array...",
      "tensor_dims": 4096,
      "alignment_model_id": "llama3-8b",
      "policy_confidence": 0.97
    }
  ]
}

Performance

Latency Breakdown

Component Latency
Vector search (HNSW) 100-500μs
Policy decision 1-50μs
Tensor decompression 1-10μs
Projection (optional) 10-100μs
Total ~150-700μs

Comparison to Traditional RAG

Operation Traditional REFRAG
Text tokenization 1-5ms N/A
LLM context prep 5-20ms ~100μs
Network transfer 10-50ms ~1-5ms
Speedup - 10-30x

Why REFRAG Works for RuVector

  1. Rust/WASM: Python implementations suffer from loop overhead. RuVector runs the policy in SIMD-optimized Rust (<50μs decisions).

  2. Edge Deployment: The WASM build can serve as a "Smart Context Compressor" in the browser, sending only necessary tokens/tensors to the server LLM.

  3. Zero-Copy: Using rkyv serialization enables direct memory access to tensors without deserialization.

Future Integration

This example demonstrates REFRAG concepts without modifying ruvector-core. For production use, consider:

  1. Phase 1: Add RefragEntry as new struct in ruvector-core
  2. Phase 2: Integrate policy network into ruvector-router
  3. Phase 3: Update REST API with hybrid response format

See Issue #10 for the full integration proposal.

References