Text Generation
Transformers
Safetensors
English
fabric
efficient
0.7b
causal-lm
chunked-memory
conversational
custom_code
Instructions to use FabricAI/Fabric1.5-0.7B-Instruct with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use FabricAI/Fabric1.5-0.7B-Instruct with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="FabricAI/Fabric1.5-0.7B-Instruct", trust_remote_code=True) messages = [ {"role": "user", "content": "Who are you?"}, ] pipe(messages)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("FabricAI/Fabric1.5-0.7B-Instruct", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use FabricAI/Fabric1.5-0.7B-Instruct with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "FabricAI/Fabric1.5-0.7B-Instruct" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FabricAI/Fabric1.5-0.7B-Instruct", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker
docker model run hf.co/FabricAI/Fabric1.5-0.7B-Instruct
- SGLang
How to use FabricAI/Fabric1.5-0.7B-Instruct with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "FabricAI/Fabric1.5-0.7B-Instruct" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FabricAI/Fabric1.5-0.7B-Instruct", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "FabricAI/Fabric1.5-0.7B-Instruct" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/chat/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "FabricAI/Fabric1.5-0.7B-Instruct", "messages": [ { "role": "user", "content": "What is the capital of France?" } ] }' - Docker Model Runner
How to use FabricAI/Fabric1.5-0.7B-Instruct with Docker Model Runner:
docker model run hf.co/FabricAI/Fabric1.5-0.7B-Instruct
| from __future__ import annotations | |
| import base64 | |
| import json | |
| import math | |
| from dataclasses import dataclass | |
| from pathlib import Path | |
| from typing import Any | |
| import numpy as np | |
| import torch | |
| from safetensors import safe_open | |
| from torch import nn | |
| from torch.nn import functional as F | |
| class ModelConfig: | |
| model_name: str = "Fabric 1.5" | |
| architecture: str = "fabric" | |
| vocab_size: int = 65536 | |
| hidden_size: int = 1536 | |
| intermediate_size: int = 4096 | |
| num_layers: int = 24 | |
| num_query_heads: int = 24 | |
| num_kv_heads: int = 6 | |
| head_dim: int = 64 | |
| sequence_length: int = 32768 | |
| local_attention_window: int = 2048 | |
| memory_chunk_size: int = 512 | |
| summaries_per_chunk: int = 4 | |
| rope_theta: float = 1000000.0 | |
| rms_norm_eps: float = 1e-6 | |
| tie_word_embeddings: bool = True | |
| attention_backend: str = "auto" | |
| attention_chunk_size: int = 1024 | |
| activation_checkpointing: bool = False | |
| chunked_cross_entropy: bool = True | |
| loss_chunk_size: int = 1024 | |
| def _decode_structure(value: Any, tensors: dict[str, torch.Tensor]) -> Any: | |
| if not isinstance(value, dict) or "__kind__" not in value: | |
| return value | |
| kind = value["__kind__"] | |
| if kind == "tensor": | |
| return tensors[value["key"]] | |
| if kind == "dict": | |
| return { | |
| _decode_structure(key, tensors): _decode_structure(item, tensors) | |
| for key, item in value["items"] | |
| } | |
| if kind == "tuple": | |
| return tuple(_decode_structure(item, tensors) for item in value["items"]) | |
| if kind == "list": | |
| return [_decode_structure(item, tensors) for item in value["items"]] | |
| if kind == "ndarray": | |
| return np.asarray(value["items"], dtype=np.dtype(value["dtype"])).reshape(value["shape"]) | |
| if kind == "path": | |
| return Path(value["value"]) | |
| if kind == "bytes": | |
| return base64.b64decode(value["value"]) | |
| raise ValueError(f"unknown checkpoint structure kind: {kind}") | |
| def load_checkpoint(path: str | Path, map_location: str | torch.device = "cpu") -> dict[str, Any]: | |
| with safe_open(path, framework="pt", device=str(map_location)) as handle: | |
| metadata = handle.metadata() | |
| if metadata.get("format") != "fabric_complete_checkpoint": | |
| raise ValueError("file is not a Fabric complete checkpoint") | |
| tensors = {key: handle.get_tensor(key) for key in handle.keys()} | |
| structure = json.loads(metadata["structure"]) | |
| state = _decode_structure(structure, tensors) | |
| if not isinstance(state, dict): | |
| raise ValueError("checkpoint root must be a dictionary") | |
| return state | |
| class RMSNorm(nn.Module): | |
| def __init__(self, hidden_size: int, eps: float = 1e-6) -> None: | |
| super().__init__() | |
| self.weight = nn.Parameter(torch.ones(hidden_size)) | |
| self.eps = eps | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| dtype = x.dtype | |
| variance = x.float().pow(2).mean(dim=-1, keepdim=True) | |
| return (x.float() * torch.rsqrt(variance + self.eps)).to(dtype) * self.weight | |
| class SwiGLU(nn.Module): | |
| def __init__(self, hidden_size: int, intermediate_size: int) -> None: | |
| super().__init__() | |
| self.gate_proj = nn.Linear(hidden_size, intermediate_size, bias=False) | |
| self.up_proj = nn.Linear(hidden_size, intermediate_size, bias=False) | |
| self.down_proj = nn.Linear(intermediate_size, hidden_size, bias=False) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x)) | |
| def rotate_half(x: torch.Tensor) -> torch.Tensor: | |
| x1, x2 = x.chunk(2, dim=-1) | |
| return torch.cat((-x2, x1), dim=-1) | |
| class RotaryEmbedding(nn.Module): | |
| def __init__(self, head_dim: int, theta: float = 10000.0) -> None: | |
| super().__init__() | |
| inv_freq = 1.0 / (theta ** (torch.arange(0, head_dim, 2).float() / head_dim)) | |
| self.register_buffer("inv_freq", inv_freq, persistent=False) | |
| def forward(self, q: torch.Tensor, k: torch.Tensor, position_ids: torch.Tensor): | |
| angles = position_ids.float().unsqueeze(-1) * self.inv_freq.float() | |
| emb = torch.cat((angles, angles), dim=-1) | |
| cos = emb.cos().to(q.dtype).unsqueeze(1) | |
| sin = emb.sin().to(q.dtype).unsqueeze(1) | |
| return q * cos + rotate_half(q) * sin, k * cos + rotate_half(k) * sin | |
| def build_local_causal_mask(query_length: int, key_length: int, window: int, device, query_offset: int = 0): | |
| query_positions = torch.arange(query_offset, query_offset + query_length, device=device) | |
| key_positions = torch.arange(key_length, device=device) | |
| return (key_positions[None, :] <= query_positions[:, None]) & ( | |
| key_positions[None, :] > query_positions[:, None] - window | |
| ) | |
| def repeat_kv(x: torch.Tensor, groups: int) -> torch.Tensor: | |
| if groups == 1: | |
| return x | |
| batch, kv_heads, length, dim = x.shape | |
| return x[:, :, None, :, :].expand(batch, kv_heads, groups, length, dim).reshape( | |
| batch, kv_heads * groups, length, dim | |
| ) | |
| def reference_attention(q: torch.Tensor, k: torch.Tensor, v: torch.Tensor, allowed_mask: torch.Tensor): | |
| scores = torch.matmul(q.float(), k.float().transpose(-1, -2)) / math.sqrt(q.shape[-1]) | |
| scores = scores.masked_fill(~allowed_mask, torch.finfo(scores.dtype).min) | |
| probabilities = torch.softmax(scores, dim=-1) | |
| probabilities = torch.where(allowed_mask.any(dim=-1, keepdim=True), probabilities, 0.0) | |
| return torch.matmul(probabilities.to(v.dtype), v) | |
| class GQAAttention(nn.Module): | |
| def __init__(self, config: ModelConfig, window: int | None = None) -> None: | |
| super().__init__() | |
| self.num_query_heads = config.num_query_heads | |
| self.num_kv_heads = config.num_kv_heads | |
| self.head_dim = config.head_dim | |
| self.groups = config.num_query_heads // config.num_kv_heads | |
| self.window = window or config.sequence_length | |
| self.backend = "sdpa" if config.attention_backend == "flash_attn" else config.attention_backend | |
| self.attention_chunk_size = config.attention_chunk_size | |
| self.q_proj = nn.Linear(config.hidden_size, config.num_query_heads * config.head_dim, bias=False) | |
| self.k_proj = nn.Linear(config.hidden_size, config.num_kv_heads * config.head_dim, bias=False) | |
| self.v_proj = nn.Linear(config.hidden_size, config.num_kv_heads * config.head_dim, bias=False) | |
| self.o_proj = nn.Linear(config.num_query_heads * config.head_dim, config.hidden_size, bias=False) | |
| self.rope = RotaryEmbedding(config.head_dim, config.rope_theta) | |
| def forward(self, x: torch.Tensor, position_ids: torch.Tensor | None = None) -> torch.Tensor: | |
| batch, length, _ = x.shape | |
| if position_ids is None: | |
| position_ids = torch.arange(length, device=x.device).expand(batch, -1) | |
| q = self.q_proj(x).view(batch, length, self.num_query_heads, self.head_dim).transpose(1, 2) | |
| k = self.k_proj(x).view(batch, length, self.num_kv_heads, self.head_dim).transpose(1, 2) | |
| v = self.v_proj(x).view(batch, length, self.num_kv_heads, self.head_dim).transpose(1, 2) | |
| q, k = self.rope(q, k, position_ids) | |
| k = repeat_kv(k, self.groups) | |
| v = repeat_kv(v, self.groups) | |
| use_sdpa = self.backend in {"auto", "sdpa"} and hasattr(F, "scaled_dot_product_attention") | |
| if use_sdpa: | |
| outputs = [] | |
| for start in range(0, length, self.attention_chunk_size): | |
| end = min(start + self.attention_chunk_size, length) | |
| key_start = max(0, start - self.window + 1) | |
| key_end = end | |
| allowed = build_local_causal_mask( | |
| end - start, | |
| key_end - key_start, | |
| self.window, | |
| x.device, | |
| query_offset=start - key_start, | |
| )[None, None] | |
| outputs.append( | |
| F.scaled_dot_product_attention( | |
| q[:, :, start:end], | |
| k[:, :, key_start:key_end], | |
| v[:, :, key_start:key_end], | |
| attn_mask=allowed, | |
| dropout_p=0.0, | |
| ) | |
| ) | |
| output = torch.cat(outputs, dim=2) | |
| else: | |
| allowed = build_local_causal_mask(length, length, self.window, x.device)[None, None] | |
| output = reference_attention(q, k, v, allowed) | |
| output = output.transpose(1, 2).contiguous().view(batch, length, -1) | |
| return self.o_proj(output) | |
| def build_completed_chunk_mask(sequence_length: int, num_chunks: int, summaries_per_chunk: int, chunk_size: int, device): | |
| query_chunk = torch.arange(sequence_length, device=device) // chunk_size | |
| summary_chunk = torch.arange(num_chunks, device=device).repeat_interleave(summaries_per_chunk) | |
| return summary_chunk[None, :] < query_chunk[:, None] | |
| class ChunkSummarizer(nn.Module): | |
| def __init__(self, config: ModelConfig) -> None: | |
| super().__init__() | |
| self.chunk_size = config.memory_chunk_size | |
| self.num_summaries = config.summaries_per_chunk | |
| self.hidden_size = config.hidden_size | |
| self.queries = nn.Parameter(torch.empty(self.num_summaries, self.hidden_size)) | |
| nn.init.normal_(self.queries, std=0.02) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| batch, length, hidden = x.shape | |
| num_chunks = (length + self.chunk_size - 1) // self.chunk_size | |
| padded_length = num_chunks * self.chunk_size | |
| if padded_length != length: | |
| x = torch.cat((x, x.new_zeros(batch, padded_length - length, hidden)), dim=1) | |
| chunks = x.view(batch, num_chunks, self.chunk_size, hidden) | |
| scores = torch.einsum("mh,bnch->bnmc", self.queries.float(), chunks.float()) / math.sqrt(hidden) | |
| if padded_length != length: | |
| valid = torch.arange(padded_length, device=x.device).view(num_chunks, self.chunk_size) < length | |
| scores = scores.masked_fill(~valid[None, :, None, :], torch.finfo(scores.dtype).min) | |
| weights = torch.softmax(scores, dim=-1).to(chunks.dtype) | |
| return torch.einsum("bnmc,bnch->bnmh", weights, chunks) | |
| class MemoryAttention(nn.Module): | |
| def __init__(self, config: ModelConfig) -> None: | |
| super().__init__() | |
| self.num_query_heads = config.num_query_heads | |
| self.num_kv_heads = config.num_kv_heads | |
| self.head_dim = config.head_dim | |
| self.groups = config.num_query_heads // config.num_kv_heads | |
| self.chunk_size = config.memory_chunk_size | |
| self.num_summaries = config.summaries_per_chunk | |
| self.q_proj = nn.Linear(config.hidden_size, config.num_query_heads * config.head_dim, bias=False) | |
| self.k_proj = nn.Linear(config.hidden_size, config.num_kv_heads * config.head_dim, bias=False) | |
| self.v_proj = nn.Linear(config.hidden_size, config.num_kv_heads * config.head_dim, bias=False) | |
| self.o_proj = nn.Linear(config.num_query_heads * config.head_dim, config.hidden_size, bias=False) | |
| def forward(self, x: torch.Tensor, summaries: torch.Tensor) -> torch.Tensor: | |
| batch, length, _ = x.shape | |
| num_chunks = summaries.shape[1] | |
| flat = summaries.reshape(batch, num_chunks * self.num_summaries, -1) | |
| q = self.q_proj(x).view(batch, length, self.num_query_heads, self.head_dim).transpose(1, 2) | |
| k = self.k_proj(flat).view(batch, -1, self.num_kv_heads, self.head_dim).transpose(1, 2) | |
| v = self.v_proj(flat).view(batch, -1, self.num_kv_heads, self.head_dim).transpose(1, 2) | |
| k, v = repeat_kv(k, self.groups), repeat_kv(v, self.groups) | |
| scores = torch.matmul(q.float(), k.float().transpose(-1, -2)) / math.sqrt(self.head_dim) | |
| allowed = build_completed_chunk_mask(length, num_chunks, self.num_summaries, self.chunk_size, x.device)[None, None] | |
| scores = scores.masked_fill(~allowed, torch.finfo(scores.dtype).min) | |
| probabilities = torch.softmax(scores, dim=-1) | |
| probabilities = torch.where(allowed.any(dim=-1, keepdim=True), probabilities, 0.0) | |
| output = torch.matmul(probabilities.to(v.dtype), v) | |
| output = output.transpose(1, 2).contiguous().view(batch, length, -1) | |
| return self.o_proj(output) | |
| class LocalBlock(nn.Module): | |
| def __init__(self, config: ModelConfig, window: int | None = None) -> None: | |
| super().__init__() | |
| self.attention_norm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.attention = GQAAttention(config, window or config.local_attention_window) | |
| self.mlp_norm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.mlp = SwiGLU(config.hidden_size, config.intermediate_size) | |
| def forward(self, x: torch.Tensor, position_ids: torch.Tensor | None = None) -> torch.Tensor: | |
| x = x + self.attention(self.attention_norm(x), position_ids) | |
| return x + self.mlp(self.mlp_norm(x)) | |
| class FabricMemoryBlock(nn.Module): | |
| def __init__(self, config: ModelConfig) -> None: | |
| super().__init__() | |
| self.attention_norm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.local_attention = GQAAttention(config, config.local_attention_window) | |
| self.summarizer = ChunkSummarizer(config) | |
| self.memory_attention = MemoryAttention(config) | |
| self.gate = nn.Linear(config.hidden_size, 1, bias=True) | |
| self.mlp_norm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.mlp = SwiGLU(config.hidden_size, config.intermediate_size) | |
| def forward(self, x: torch.Tensor, position_ids: torch.Tensor | None = None) -> torch.Tensor: | |
| normalized = self.attention_norm(x) | |
| local = self.local_attention(normalized, position_ids) | |
| summaries = self.summarizer(normalized) | |
| memory = self.memory_attention(normalized, summaries) | |
| gate = torch.sigmoid(self.gate(normalized)) | |
| x = x + gate * local + (1.0 - gate) * memory | |
| return x + self.mlp(self.mlp_norm(x)) | |
| class CausalLMOutput: | |
| logits: torch.Tensor | None | |
| loss: torch.Tensor | None = None | |
| class FabricCoreForCausalLM(nn.Module): | |
| def __init__(self, config: ModelConfig) -> None: | |
| super().__init__() | |
| self.config = config | |
| self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size) | |
| if config.architecture == "fabric": | |
| layers = [FabricMemoryBlock(config) if i % 3 == 2 else LocalBlock(config) for i in range(config.num_layers)] | |
| else: | |
| window = config.sequence_length if config.architecture == "full" else config.local_attention_window | |
| layers = [LocalBlock(config, window) for _ in range(config.num_layers)] | |
| self.layers = nn.ModuleList(layers) | |
| self.norm = RMSNorm(config.hidden_size, config.rms_norm_eps) | |
| self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False) | |
| if config.tie_word_embeddings: | |
| self.lm_head.weight = self.embed_tokens.weight | |
| def forward(self, input_ids: torch.Tensor, labels: torch.Tensor | None = None) -> CausalLMOutput: | |
| if input_ids.ndim != 2: | |
| raise ValueError("input_ids must have shape [batch, sequence]") | |
| if input_ids.shape[1] > self.config.sequence_length: | |
| raise ValueError("input sequence exceeds configured sequence_length") | |
| position_ids = torch.arange(input_ids.shape[1], device=input_ids.device).expand(input_ids.shape[0], -1) | |
| hidden = self.embed_tokens(input_ids) | |
| for layer in self.layers: | |
| hidden = layer(hidden, position_ids) | |
| normalized = self.norm(hidden) | |
| logits = self.lm_head(normalized).float() | |
| loss = None | |
| if labels is not None: | |
| loss = F.cross_entropy( | |
| logits[:, :-1].reshape(-1, logits.shape[-1]), | |
| labels[:, 1:].reshape(-1), | |
| ignore_index=-100, | |
| ) | |
| return CausalLMOutput(logits=logits, loss=loss) | |