Text Generation
Transformers
Safetensors
textgeneratorc2k
feature-extraction
storygeneratorc2k
userneeds2k
gemma-4
latent-variable-models
text-classification
multilingual
bfloat16
custom_code
Instructions to use bbin2022/textgeneratorc2k with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use bbin2022/textgeneratorc2k with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="bbin2022/textgeneratorc2k", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("bbin2022/textgeneratorc2k", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use bbin2022/textgeneratorc2k with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "bbin2022/textgeneratorc2k" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "bbin2022/textgeneratorc2k", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/bbin2022/textgeneratorc2k
- SGLang
How to use bbin2022/textgeneratorc2k 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 "bbin2022/textgeneratorc2k" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "bbin2022/textgeneratorc2k", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'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 "bbin2022/textgeneratorc2k" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "bbin2022/textgeneratorc2k", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use bbin2022/textgeneratorc2k with Docker Model Runner:
docker model run hf.co/bbin2022/textgeneratorc2k
Download modeling_textgeneratorc2k.py from bbin2022/textgeneratorc2k: direct link, hf CLI and curl.
- Browser
- Download file 15.4 kB
-
https://huggingface.co/bbin2022/textgeneratorc2k/resolve/main/modeling_textgeneratorc2k.py
- Command line
-
hf download hf://bbin2022/textgeneratorc2k/modeling_textgeneratorc2k.py
-
curl -L -o modeling_textgeneratorc2k.py https://huggingface.co/bbin2022/textgeneratorc2k/resolve/main/modeling_textgeneratorc2k.py
15.4 kB
| """Hugging Face `transformers` model and pipeline for TextGeneratorC2K (`storygeneratorc2k` + `Gemma-4-31B` + `EmbeddingGemma-300M` + `userneeds2k` BF16).""" | |
| from __future__ import annotations | |
| import json | |
| import math | |
| import re | |
| import zlib | |
| from pathlib import Path | |
| from typing import Any | |
| import torch | |
| import torch.nn.functional as F | |
| from torch import nn | |
| from transformers import ( | |
| Gemma3TextConfig, | |
| Gemma3TextModel, | |
| Gemma4TextConfig, | |
| Gemma4TextModel, | |
| Pipeline, | |
| PretrainedConfig, | |
| PreTrainedModel, | |
| ) | |
| class TextGeneratorC2KConfig(PretrainedConfig): | |
| model_type = "textgeneratorc2k" | |
| def __init__( | |
| self, | |
| text_config: dict[str, Any] | None = None, | |
| embedding_config: dict[str, Any] | None = None, | |
| num_categories: int = 2000, | |
| num_topics: int = 5, | |
| num_levels: int = 3, | |
| subspace_dim: int = 256, | |
| classifier_input_dim: int = 768, | |
| classifier_hidden_dim: int = 2048, | |
| embedding_dense_dim: int = 3072, | |
| valid_vocab_len: int = 50780, | |
| runtime_meta_zlib_len: int = 1, | |
| **kwargs: Any, | |
| ) -> None: | |
| super().__init__(**kwargs) | |
| self.text_config = text_config or {} | |
| self.embedding_config = embedding_config or {} | |
| self.num_categories = num_categories | |
| self.num_topics = num_topics | |
| self.num_levels = num_levels | |
| self.subspace_dim = subspace_dim | |
| self.classifier_input_dim = classifier_input_dim | |
| self.classifier_hidden_dim = classifier_hidden_dim | |
| self.embedding_dense_dim = embedding_dense_dim | |
| self.valid_vocab_len = valid_vocab_len | |
| self.runtime_meta_zlib_len = runtime_meta_zlib_len | |
| class MLPClassifier(nn.Module): | |
| def __init__(self, input_dim: int = 768, hidden_dim: int = 2048, num_labels: int = 2000, dropout: float = 0.15): | |
| super().__init__() | |
| self.network = nn.Sequential( | |
| nn.LayerNorm(input_dim), | |
| nn.Linear(input_dim, hidden_dim), | |
| nn.GELU(), | |
| nn.Dropout(dropout), | |
| nn.Linear(hidden_dim, num_labels), | |
| ) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return self.network(x) | |
| class TextGeneratorC2KForCausalLM(PreTrainedModel): | |
| config_class = TextGeneratorC2KConfig | |
| _tied_weights_keys = [] | |
| _no_split_modules = ["Gemma4DecoderLayer", "Gemma3DecoderLayer", "MLPClassifier"] | |
| def __init__(self, config: TextGeneratorC2KConfig) -> None: | |
| super().__init__(config) | |
| gemma_cfg = Gemma4TextConfig.from_dict(config.text_config) | |
| eg_cfg = Gemma3TextConfig.from_dict(config.embedding_config) | |
| self.lm = Gemma4TextModel(gemma_cfg) | |
| self.embeddinggemma = Gemma3TextModel(eg_cfg) | |
| self.embedding_dense1 = nn.Linear(config.classifier_input_dim, config.embedding_dense_dim, bias=False) | |
| self.embedding_dense2 = nn.Linear(config.embedding_dense_dim, config.classifier_input_dim, bias=False) | |
| self.classifier = MLPClassifier( | |
| input_dim=config.classifier_input_dim, | |
| hidden_dim=config.classifier_hidden_dim, | |
| num_labels=config.num_categories, | |
| ) | |
| d = gemma_cfg.hidden_size | |
| c = config.num_categories | |
| t = config.num_topics | |
| l = config.num_levels | |
| s = config.subspace_dim | |
| self.register_buffer("level_centroids", torch.empty((c, l, d), dtype=torch.bfloat16)) | |
| self.register_buffer("topic_subspace_coeffs", torch.empty((c, t, l, s), dtype=torch.bfloat16)) | |
| self.register_buffer("subspace_basis", torch.empty((s, d), dtype=torch.bfloat16)) | |
| self.register_buffer("sigma_il", torch.empty((c, l), dtype=torch.bfloat16)) | |
| self.register_buffer("embed_mean", torch.empty((d,), dtype=torch.bfloat16)) | |
| self.register_buffer("bridge_proj", torch.empty((d, config.classifier_input_dim), dtype=torch.bfloat16)) | |
| self.register_buffer("valid_vocab_ids", torch.empty((config.valid_vocab_len,), dtype=torch.int32)) | |
| self.register_buffer("runtime_meta_zlib", torch.empty((config.runtime_meta_zlib_len,), dtype=torch.uint8)) | |
| self._runtime_initialized = False | |
| self.post_init() | |
| def _ensure_runtime(self) -> None: | |
| if getattr(self, "_runtime_initialized", False): | |
| return | |
| dev = self.lm.embed_tokens.weight.device | |
| for buf_name in ( | |
| "level_centroids", | |
| "topic_subspace_coeffs", | |
| "subspace_basis", | |
| "sigma_il", | |
| "embed_mean", | |
| "bridge_proj", | |
| "valid_vocab_ids", | |
| ): | |
| buf = getattr(self, buf_name) | |
| if buf.device != dev: | |
| setattr(self, buf_name, buf.to(dev)) | |
| for mod in (self.embeddinggemma, self.embedding_dense1, self.embedding_dense2, self.classifier): | |
| if next(mod.parameters()).device != dev: | |
| mod.to(dev) | |
| meta_bytes = bytes(self.runtime_meta_zlib.detach().cpu().numpy().tobytes()) | |
| meta = json.loads(zlib.decompress(meta_bytes).decode("utf-8")) | |
| self.label_ids: list[str] = meta["label_ids"] | |
| self.label_to_idx = {lid: i for i, lid in enumerate(self.label_ids)} | |
| self.id_to_token: list[str] = meta["id_to_token"] | |
| self.token_to_id = {tok: i for i, tok in enumerate(self.id_to_token)} | |
| self.eos_token_ids = set(meta["eos_token_ids"]) | |
| self.pad_token_id = int(meta["pad_token_id"]) | |
| self.comma_token_id = int(meta["comma_token_id"]) | |
| self.level_lang_prompts = meta["level_lang_prompts"] | |
| self.cat_leaf_token_ids = meta["cat_leaf_token_ids"] | |
| cat_file = Path(getattr(self.config, "_name_or_path", ".")) / "catid_define.json" | |
| if not cat_file.exists(): | |
| cat_file = Path(__file__).resolve().parent / "catid_define.json" | |
| self.catid_define = json.loads(cat_file.read_text(encoding="utf-8")) if cat_file.exists() else {} | |
| with torch.inference_mode(): | |
| v_ids = self.valid_vocab_ids.long() | |
| w_raw = F.normalize(self.lm.embed_tokens.weight[v_ids].float(), dim=-1) | |
| self.w_centered = F.normalize(w_raw - self.embed_mean.float().unsqueeze(0), dim=-1) | |
| v_list = v_ids.cpu().tolist() | |
| self.valid_vocab_set = set(v_list) | |
| self.valid_words = [self.id_to_token[int(i)].lstrip("▁").lower() for i in v_list] | |
| self._runtime_initialized = True | |
| def category_name(self, category_id: str, language: str = "en") -> str: | |
| self._ensure_runtime() | |
| return " / ".join( | |
| self.catid_define.get(c, {}).get(language) or self.catid_define.get(c, {}).get("en", c) | |
| for c in category_id.split("/") | |
| ) | |
| def _encode_6lang(self, text: str) -> list[int]: | |
| s = "▁" + text.strip().replace(" ", "▁") | |
| ids = [2] # <bos> | |
| i, n = 0, len(s) | |
| while i < n: | |
| matched = False | |
| for end in range(min(n, i + 24), i, -1): | |
| tid = self.token_to_id.get(s[i:end]) | |
| if tid is not None: | |
| ids.append(tid) | |
| i = end | |
| matched = True | |
| break | |
| if not matched: | |
| i += 1 | |
| return ids[:512] | |
| def generate_paragraph( | |
| self, | |
| category_id: str = "10008/20081/31194", | |
| language: str = "en", | |
| level: int = 1, | |
| topic_idx: int = 1, | |
| gamma: float = 0.45, | |
| seed: int = 42, | |
| max_new_tokens: int = 260, | |
| ) -> dict[str, Any]: | |
| self._ensure_runtime() | |
| dev = self.lm.embed_tokens.weight.device | |
| cat_idx = self.label_to_idx[category_id] | |
| lvl_idx = max(0, min(2, level - 1)) | |
| t_idx = max(0, min(4, topic_idx - 1)) | |
| ebar = self.level_centroids[cat_idx, lvl_idx].float() | |
| coeff = self.topic_subspace_coeffs[cat_idx, t_idx, lvl_idx].float() | |
| basis = self.subspace_basis.float() | |
| e_anchor = F.normalize(ebar + torch.matmul(coeff, basis), dim=-1) | |
| g = torch.Generator(device=dev).manual_seed(seed + cat_idx * 31 + level * 7 + topic_idx) | |
| eps = torch.randn((basis.shape[0],), generator=g, device=dev, dtype=torch.float32) / math.sqrt(basis.shape[0]) | |
| delta = self.sigma_il[cat_idx, lvl_idx].float() * torch.matmul(eps, basis) | |
| h_tilde = F.normalize(e_anchor + gamma * delta, dim=-1) | |
| sims = torch.matmul(self.w_centered, h_tilde) | |
| _, top_pos = torch.topk(sims, k=64) | |
| c_ids: list[int] = [] | |
| c_words: list[str] = [] | |
| seen: set[str] = set() | |
| for pos in top_pos.cpu().tolist(): | |
| w = self.valid_words[pos] | |
| if len(w) < 4 or any(w[:4] == s[:4] for s in seen): | |
| continue | |
| seen.add(w) | |
| c_ids.append(int(self.valid_vocab_ids[pos].item())) | |
| c_words.append(w) | |
| if len(c_ids) >= 7: | |
| break | |
| tmpl = self.level_lang_prompts[language][str(level)] | |
| cat_toks = self.cat_leaf_token_ids[language][cat_idx] | |
| seq = list(tmpl["prefix_ids"]) + list(cat_toks) + list(tmpl["mid_ids"]) | |
| for i, cid in enumerate(c_ids): | |
| if i > 0: | |
| seq.append(self.comma_token_id) | |
| seq.append(cid) | |
| seq.extend(tmpl["suffix_ids"]) | |
| inp = torch.tensor([seq], device=dev, dtype=torch.long) | |
| mask = torch.ones_like(inp) | |
| out = self.lm(inputs_embeds=self.lm.embed_tokens(inp), attention_mask=mask, use_cache=True) | |
| pkv, last_h = out.past_key_values, out.last_hidden_state[:, -1, :] | |
| gen: list[int] = [] | |
| for _ in range(max_new_tokens): | |
| logits = F.linear(last_h.to(dev), self.lm.embed_tokens.weight).float() | |
| cap = getattr(self.lm.config, "final_logit_softcapping", None) | |
| if cap: | |
| logits = torch.tanh(logits / cap) * cap | |
| nid = int(torch.argmax(logits, dim=-1).item()) | |
| if nid in self.eos_token_ids: | |
| break | |
| gen.append(nid) | |
| nxt = torch.tensor([[nid]], device=dev, dtype=torch.long) | |
| mask = torch.cat([mask, torch.ones((1, 1), device=dev, dtype=torch.long)], dim=1) | |
| out = self.lm(inputs_embeds=self.lm.embed_tokens(nxt), attention_mask=mask, past_key_values=pkv, use_cache=True) | |
| pkv, last_h = out.past_key_values, out.last_hidden_state[:, -1, :] | |
| text = re.sub(r"\s+", " ", "".join(self.id_to_token[t] for t in gen).replace("▁", " ")).strip() | |
| if language == "cn": | |
| text = re.sub(r"\s*,\s*", ",", text) | |
| text = re.sub(r"\s*;\s*", ";", text) | |
| text = re.sub(r"\s*:\s*", ":", text) | |
| text = re.sub(r"(?<=[\u4e00-\u9fff])\s+(?=[\u4e00-\u9fff])", "", text) | |
| elif language == "jp": | |
| text = re.sub(r"\s*,\s*", "、", text) | |
| text = re.sub(r"(?<=[\u3040-\u30ff\u4e00-\u9fff])\s+(?=[\u3040-\u30ff\u4e00-\u9fff])", "", text) | |
| leaf_name = self.catid_define.get(category_id.split("/")[-1], {}).get(language) or self.category_name(category_id, "en") | |
| return { | |
| "category_id": category_id, | |
| "category_name": self.category_name(category_id, language), | |
| "language": language, | |
| "level": level, | |
| "concepts": c_words, | |
| "paragraph": text, | |
| "categorical_scores": self.classify_text(text, query=leaf_name, language=language, top_k=5)["categorical_scores"], | |
| } | |
| def classify_text(self, text: str, query: str = "", language: str = "en", top_k: int = 5) -> dict[str, Any]: | |
| self._ensure_runtime() | |
| dev = self.lm.embed_tokens.weight.device | |
| doc_str = f"Query: {query}\nSnippet: {text}" if query else f"Query: {text[:80]}\nSnippet: {text}" | |
| inp = torch.tensor([self._encode_6lang(doc_str)], device=dev, dtype=torch.long) | |
| eg_out = self.embeddinggemma(input_ids=inp) | |
| pooled = eg_out.last_hidden_state.mean(dim=1) | |
| dense = self.embedding_dense2(self.embedding_dense1(pooled)) | |
| emb_768 = F.normalize(dense.float(), dim=-1).to(self.embedding_dense1.weight.dtype) | |
| eg_probs = self.classifier(emb_768).detach().float().sigmoid()[0] | |
| words = re.findall(r"[A-Za-z]{4,}", text.lower()) | |
| c_ids: list[int] = [] | |
| seen_ids: set[int] = set() | |
| for w in words: | |
| tid = self.token_to_id.get(f"▁{w}") | |
| if tid is not None and tid in self.valid_vocab_set and tid not in seen_ids: | |
| seen_ids.add(tid) | |
| c_ids.append(tid) | |
| if not c_ids: | |
| for tid in inp[0].cpu().tolist()[1:]: | |
| if tid in self.valid_vocab_set and tid not in seen_ids: | |
| seen_ids.add(tid) | |
| c_ids.append(tid) | |
| if c_ids: | |
| t_ids = torch.tensor(c_ids[:256], device=dev, dtype=torch.long) | |
| w_tok = F.normalize(self.lm.embed_tokens.weight[t_ids].float(), dim=-1) | |
| w_c = F.normalize(w_tok - self.embed_mean.float().unsqueeze(0), dim=-1) | |
| tok_cat_sims = torch.einsum("cld,md->clm", self.level_centroids.float(), w_c) | |
| k_top = min(6, w_c.shape[0]) | |
| bank_sims = torch.topk(tok_cat_sims, k=k_top, dim=-1).values.mean(dim=-1).max(dim=1).values.clamp_min(0.0) | |
| scores = 0.65 * eg_probs + 0.35 * bank_sims | |
| else: | |
| scores = eg_probs | |
| scores[0] = 0.0 | |
| vals, idxs = torch.topk(scores, k=min(top_k, len(scores))) | |
| preds = [ | |
| { | |
| "category_id": self.label_ids[idx], | |
| "category_name": self.category_name(self.label_ids[idx], language), | |
| "score": round(float(val), 4), | |
| "userneeds2k_prob": round(float(eg_probs[idx].item()), 4), | |
| } | |
| for val, idx in zip(vals.cpu().tolist(), idxs.cpu().tolist()) | |
| ] | |
| return { | |
| "text": text, | |
| "categorical_scores": preds, | |
| "predictions": preds, | |
| } | |
| class TextGeneratorC2KPipeline(Pipeline): | |
| _load_tokenizer = False | |
| _load_image_processor = False | |
| _load_video_processor = False | |
| _load_feature_extractor = False | |
| _load_processor = False | |
| def _sanitize_parameters(self, **kwargs: Any) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]: | |
| return {}, kwargs, {} | |
| def preprocess(self, inputs: Any) -> Any: | |
| return inputs | |
| def _forward(self, model_inputs: Any, **forward_params: Any) -> dict[str, Any]: | |
| if isinstance(model_inputs, dict) and "category_id" in model_inputs: | |
| merged = {**forward_params, **model_inputs} | |
| return self.model.generate_paragraph(**merged) | |
| if isinstance(model_inputs, str): | |
| self.model._ensure_runtime() | |
| if model_inputs in self.model.label_to_idx: | |
| return self.model.generate_paragraph(category_id=model_inputs, **forward_params) | |
| return self.model.classify_text(model_inputs, **forward_params) | |
| raise ValueError(f"Unsupported pipeline input: {model_inputs}") | |
| def postprocess(self, model_outputs: dict[str, Any]) -> dict[str, Any]: | |
| return model_outputs | |
| # Aliases for StoryGeneratorC2K | |
| StoryGeneratorC2KConfig = TextGeneratorC2KConfig | |
| StoryGeneratorC2KForCausalLM = TextGeneratorC2KForCausalLM | |
| StoryGeneratorC2KPipeline = TextGeneratorC2KPipeline | |