Source code for models.jan_code_local_model

# SPDX-FileCopyrightText: 2026 Julen Gamboa <j.a.r.gamboa@gmail.com>
# SPDX-License-Identifier: AGPL-3.0-or-later

"""
Script
------
jan_code_local_model.py

Path
----
python/hillstar/models/jan_code_local_model.py

Purpose
-------
 LOCAL JAN-CODE 4B MODEL - OPTIONAL ADVANCED SETUP

Integrates Jan-Code 4B via local llama.cpp HTTP server.
This is an OPTIONAL setup for power users with appropriate hardware.

Connects to llama.cpp server running on localhost:8081.
Uses OpenAI-compatible /v1/chat/completions endpoint (not Ollama API).
Free, local execution on GPU. Default temperature 0 for deterministic output.

 HARDWARE REQUIREMENTS (MANDATORY)
-----------------------------------
 Minimum: 16GB VRAM GPU (RTX 5070 Ti, RTX 4080, RTX 4090, A100, etc.)
 Model: Q8_0 GGUF format (~4.4GB) from Jan AI / HuggingFace
 Setup: Requires jan_code_server.sh running on port 8081
 Context: 32K tokens (tuned for 16GB VRAM with all layers on GPU)
 NOT suitable for CPU-only systems (Q8_0 quant is GPU-optimized)

Setup Instructions
------------------
1. GPU required (16GB+ VRAM)
2. Download Q8_0 GGUF model from Jan AI or HuggingFace
3. Symlink or place model at ~/models/jan-code/Jan-Code-4B-Q8_0.gguf
4. Start server: ~/bin/jan_code_server.sh
5. Verify: ~/bin/jan_code_ping.sh
6. Then use this model in workflows

Inputs
------
model_name (str): Model identifier (any value accepted by llama.cpp)
endpoint (str): llama.cpp server URL (default: http://127.0.0.1:8081)

Outputs
-------
Dictionary: {output, model, tokens_used, provider, error}

Assumptions
-----------
- llama.cpp server running on localhost:8081 (started via jan_code_server.sh)
- Server exposes OpenAI-compatible /v1/chat/completions endpoint
- Local GPU with 16GB+ VRAM available
- Q8_0 GGUF model loaded in llama.cpp

Parameters
----------
temperature: Default 0 (deterministic)
max_tokens: Configurable per call (default 4096)
system: Optional system prompt

Failure Modes
-------------
- Server not running error "llama.cpp server not responding on port 8081"
- Insufficient VRAM server crashes or OOM errors
- Model not loaded server connection fails
- Timeout requests.exceptions.Timeout
- Model file missing server startup failure

When NOT to Use This
--------------------
 No GPU or GPU < 16GB VRAM Use Ollama cloud models instead
 Need large context > 32K Use cloud API providers
 Complex multi-document review Use devstral-2:123b-cloud or Anthropic

Alternative: Use ollama cloud model minimax-m2.5:cloud or devstral-2:123b-cloud

Compliance
----------
 Local execution (no external API calls)
 Free (no licensing costs)
 Apache 2.0 licensed model
 Optional - users must explicitly set up
 Not included in standard hillstar installation

Author: Julen Gamboa <julen.gamboa.ds@gmail.com>

Created
-------
2026-03-07

Last Edited
-----------
2026-03-07
"""

from __future__ import annotations

from typing import Any

import requests


[docs] class JanCodeLocalModel: """LOCAL Jan-Code 4B via llama.cpp (OpenAI-compatible API). OPTIONAL - Requires 16GB+ VRAM GPU and Q8_0 GGUF model """ TEMPERATURE_DEFAULT = 0 # Deterministic by default
[docs] def __init__( self, model_name: str = "jan-code", endpoint: str = "http://127.0.0.1:8081", ): """ Args: model_name: Model identifier (llama.cpp accepts any value) endpoint: llama.cpp server endpoint (OpenAI-compatible) Warning: Requires 16GB+ VRAM GPU and running jan_code_server.sh """ self.model_name = model_name self.endpoint = endpoint self.api_url = f"{endpoint}/v1/chat/completions"
def _check_server(self) -> bool: """Check if llama.cpp server is running via /health endpoint.""" try: response = requests.get(f"{self.endpoint}/health", timeout=2) return response.status_code == 200 except requests.exceptions.RequestException: return False
[docs] def call( self, prompt: str, max_tokens: int = 4096, temperature: float | None = None, system: str | None = None, ) -> dict[str, Any]: """ Call Jan-Code via llama.cpp OpenAI-compatible chat completions endpoint. Args: prompt: User message content max_tokens: Maximum tokens to generate temperature: Sampling temperature (default: 0) system: System prompt Returns: Dictionary with response and metadata Note: Requires jan_code_server.sh running on localhost:8081 """ if temperature is None: temperature = self.TEMPERATURE_DEFAULT if not self._check_server(): return { "output": None, "error": ( f"llama.cpp server not responding at {self.endpoint}. " "Start with: ~/bin/jan_code_server.sh " "(requires 16GB+ VRAM GPU and Q8_0 GGUF model)" ), "provider": "jan_code_local", } messages: list[dict[str, str]] = [] if system: messages.append({"role": "system", "content": system}) messages.append({"role": "user", "content": prompt}) try: payload = { "model": self.model_name, "messages": messages, "temperature": temperature, "max_tokens": max_tokens, } response = requests.post(self.api_url, json=payload, timeout=300) response.raise_for_status() data = response.json() content = ( data.get("choices", [{}])[0] .get("message", {}) .get("content", "") .strip() ) usage = data.get("usage", {}) return { "output": content, "model": self.model_name, "tokens_used": usage.get("total_tokens", 0), "provider": "jan_code_local", } except Exception as e: return { "output": None, "error": str(e), "provider": "jan_code_local", }