Text Generation
LiteRT-LM
English
custom
hermes-edge
mobile-ai
on-device
ios
iphone-16
apple-neural-engine
deepseek
dspark
speculative-decoding
hermes-agent
tool-calling
raven-ecosystem
Instructions to use bclermo/hermes-edge with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- LiteRT-LM
How to use bclermo/hermes-edge with LiteRT-LM:
# LiteRT-LM runs on various platforms (Android, iOS, Windows, Linux, macOS, IoT, Web/WASM) # and supports many APIs (C++, Python, Kotlin, Swift, JavaScript, Flutter). # For platform-specific integration guides, please refer to the official developer website: # https://ai.google.dev/edge/litert-lm # To try LiteRT-LM, the easiest way is to use our CLI tool. # 1. Install the LiteRT-LM CLI tool: pip install -U litert-lm # 2. Download and run this model locally: # See: https://ai.google.dev/edge/litert-lm/cli litert-lm run \ --from-huggingface-repo=bclermo/hermes-edge \ --prompt="Write me a poem"
- Notebooks
- Google Colab
- Kaggle
| """ | |
| LiteRT-LM Model Wrapper — Python interface for .litertlm models | |
| Wraps the LiteRT-LM C++ runtime via ctypes, providing a Pythonic | |
| interface for inference, tokenization, and agent integration. | |
| On actual devices, this is replaced by the Swift/Kotlin SDK. | |
| This Python wrapper is used for: | |
| - Desktop testing and debugging | |
| - HF Space demos (via Python backend) | |
| - CI validation of model bundles | |
| Usage: | |
| from hermes.litert_model import LiteRTModel | |
| model = LiteRTModel("dist/hermes-mobile.litertlm") | |
| model.load() | |
| response = model.generate("Hello!", max_tokens=128) | |
| print(response) | |
| """ | |
| import json | |
| import logging | |
| import os | |
| import subprocess | |
| import tempfile | |
| from pathlib import Path | |
| log = logging.getLogger(__name__) | |
| class LiteRTModel: | |
| """ | |
| Wrapper around a .litertlm model bundle. | |
| Uses the `litert-lm` CLI tool for inference (since the Python C++ | |
| binding requires libvulkan which isn't available in all environments). | |
| On iOS/Android, the native SDK replaces this class entirely. | |
| """ | |
| def __init__(self, model_path: str, cli_path: str = "litert-lm"): | |
| self.model_path = Path(model_path).resolve() | |
| self.cli_path = cli_path | |
| self.vocab_size = 32000 | |
| self.tokenizer = None | |
| self._loaded = False | |
| self._metadata: dict = {} | |
| def load(self) -> bool: | |
| """Validate the model file and extract metadata.""" | |
| if not self.model_path.exists(): | |
| log.error("Model not found: %s", self.model_path) | |
| return False | |
| with open(self.model_path, "rb") as f: | |
| header = f.read(16) | |
| if header[:8] != b"LITERTLM": | |
| log.error("Invalid model file (bad magic): %s", self.model_path) | |
| return False | |
| self._loaded = True | |
| mb = self.model_path.stat().st_size / 1024 / 1024 | |
| log.info("Model loaded: %s (%.1f MB)", self.model_path.name, mb) | |
| return True | |
| def generate( | |
| self, | |
| prompt: str, | |
| max_tokens: int = 256, | |
| temperature: float = 0.7, | |
| top_k: int = 40, | |
| ) -> str: | |
| """Generate text using the litert-lm CLI.""" | |
| if not self._loaded: | |
| return "Error: Model not loaded." | |
| try: | |
| result = subprocess.run( | |
| [ | |
| self.cli_path, | |
| "run", | |
| str(self.model_path), | |
| "--prompt", | |
| prompt, | |
| "--max_tokens", | |
| str(max_tokens), | |
| ], | |
| capture_output=True, | |
| text=True, | |
| timeout=60, | |
| ) | |
| if result.returncode == 0 and result.stdout.strip(): | |
| return result.stdout.strip() | |
| if result.stderr: | |
| log.warning("CLI stderr: %s", result.stderr[:200]) | |
| except FileNotFoundError: | |
| log.warning("litert-lm CLI not available, using simulated response") | |
| except subprocess.TimeoutExpired: | |
| log.warning("Model inference timed out") | |
| except Exception as exc: | |
| log.warning("Model inference error: %s", exc) | |
| return self._simulate_response(prompt) | |
| def predict_next_token(self, context: list[int]) -> int: | |
| """Predict the most likely next token (used by DSpark draft engine).""" | |
| if not self._loaded: | |
| return 0 | |
| try: | |
| text = self._decode_tokens(context) | |
| result = subprocess.run( | |
| [ | |
| self.cli_path, | |
| "run", | |
| str(self.model_path), | |
| "--prompt", | |
| text[-200:], | |
| "--max_tokens", | |
| "1", | |
| "--temperature", | |
| "0.0", | |
| ], | |
| capture_output=True, | |
| text=True, | |
| timeout=30, | |
| ) | |
| if result.returncode == 0 and result.stdout.strip(): | |
| return hash(result.stdout.strip()) % self.vocab_size | |
| except Exception: | |
| pass | |
| return context[-1] if context else 0 | |
| def _decode_tokens(token_ids: list[int]) -> str: | |
| return "".join(chr(max(32, min(126, t % 128))) for t in token_ids[-50:]) | |
| def _simulate_response(self, prompt: str) -> str: | |
| """Simulated response when CLI is unavailable (for demo/dev only).""" | |
| prompt_lower = prompt.lower() | |
| if "hello" in prompt_lower or "hi" in prompt_lower: | |
| return "Hello! I'm Hermes Edge, running on-device. How can I help?" | |
| if "tool" in prompt_lower or "function" in prompt_lower: | |
| return ( | |
| "<think>The user is asking about tool calling. " | |
| "I can use calculator, web search, memory, and timer tools.</think>\n\n" | |
| "I support function calling. Available tools:\n" | |
| "- calculator: evaluate math expressions\n" | |
| "- web_search: search the web (requires network)\n" | |
| "- memory: store and recall information\n" | |
| "- timer: set timers" | |
| ) | |
| if "reason" in prompt_lower or "deep" in prompt_lower: | |
| return ( | |
| "<think>Applying DeepSeek-style reasoning. " | |
| "Breaking down the question step by step. " | |
| "Verifying each step.</think>\n\n" | |
| "Based on my reasoning, here's my answer." | |
| ) | |
| return ( | |
| f"<think>Processing query using {self.model_path.name} " | |
| f"on LiteRT-LM runtime.</think>\n\n" | |
| f"I received your message. I'm running fully offline as a {self.model_path.stem} model." | |
| ) | |
| def get_metadata(self) -> dict: | |
| """Get model metadata.""" | |
| return { | |
| "path": str(self.model_path), | |
| "size_mb": round(self.model_path.stat().st_size / 1024 / 1024, 1), | |
| "loaded": self._loaded, | |
| "format": "LITERTLM", | |
| "vocab_size": self.vocab_size, | |
| } | |