Kronk

repository·main·Indexed 20 days ago

https://github.com/ardanlabs/kronk

A Go-based framework for hardware-accelerated local inference using llama.cpp and whisper.cpp. It provides a high-level API for text, vision, and audio tasks, including the Bucky SDK for speech-to-text. Kronk includes a model server compatible with OpenWebUI and Claude Code, supporting hardware acceleration via CUDA, Metal, Vulkan, HIP, ROCm, SYCL, and OpenCL across Linux, macOS, and Windows.

Tokens
37.2K
Snippets
105
Records
167
Agent score
71%

What's inside kronk

  1. Use Kronk for self-hosted Go inference

    main

    Kronk is a FOSS Go SDK designed for native, self-hosted model inference. It allows you to run models (including vision, audio, embeddings, and rerankers) directly within your Go applications without requiring Python, CGO, or a network hop to external services like Ollama.

    Key benefits include:

    • No Per-Token Costs: Since you run models on your own hardware.
    • Privacy: Data never leaves your environment.
    • Native Performance: Supports GPU acceleration when available and is optimized for CPU performance.
    • Small Model Support: Capable of running quantized small models (e.g., Qwen3.5-0.8B-Q8_0) on standard consumer hardware like laptops.
  2. Overview of Kronk Architecture and Capabilities

    main

    Kronk is designed for self-hosted, local AI inference using a Go SDK. It allows developers to run open-source models directly inside Go applications without CGO, Python, or external network hops (like Ollama).

    Core Components

    • Kronk SDK: An SDK-first design that enables running models locally. It uses yzma to avoid CGO requirements.
    • Kronk Model Server (KMS): An optional server component that provides a catalog system for verified models and supports batch processing.

    Key Features and Concepts

    • Local Inference: Provides privacy, low latency, and no per-token costs by running on your own hardware.
    • Batch Engine: Supports concurrent requests using n_seq_max slots.
    • Caching: Includes intelligent caching systems (KV caching, prompt caching).
    • Tool Calling: Enables local models to decide when to call specific functions (e.g., get_weather).
    • Vision Support: Uses 'projectors' to enable vision model capabilities.
    • Compatibility: Compatible with OpenAI clients, Claude Code, OpenWebUI, and MCP services (e.g., Brave Search).
  3. Implement Collision Detection and Rules

    main

    Use circle-based collision detection. A collision occurs when the wrap-aware distance between two object centers is less than the sum of their collision radii.

    Collision Logic:

    • Bullet vs. Asteroid: Remove bullet and asteroid; add 100 points to score.
    • Ship vs. Asteroid:
      • If ship is not invulnerable: Remove one life, clear all bullets.
      • If lives remain: Reset ship to center, zero velocity, point upward, and grant 2 seconds of invulnerability (ship should blink).
      • If no lives remain: Set lives to 0 and enter game-over state.
    • Invulnerability: During the 2-second spawn window, ignore ship-versus-asteroid collisions.
  4. Constrain model output using JSON Schema

    main
    When integrating a local model into a logic flow (such as a game engine), you can use JSON Schema to constrain the model's output. This ensures the model's response adheres to a specific structure, such as returning only valid, legal moves, rather than free-form text.
  5. Configure Game State and Constants

    main

    All game state must be stored in JavaScript memory within game.js. Use clear constants at the top of the file for gameplay values to avoid magic numbers in the game loop.

    State to track:

    • Ship: Position, velocity, facing angle, collision radius, and temporary invulnerability state.
    • Asteroids: Position, velocity, rotation, rotation speed, collision radius, and a stable set of polygon vertices.
    • Bullets: Position, velocity, remaining lifetime, and collision radius.
    • Global State: Current score, remaining lives, current input state, game running/over status, previous animation-frame timestamp, and bullet firing cooldown.
  6. Implement Ship Movement and Physics

    main

    The ship uses arcade-style inertia. Movement must be frame-rate independent by multiplying changes by the elapsed time in seconds.

    Physics Parameters:

    • Rotation speed: ~4 radians per second.
    • Forward acceleration: ~220 pixels per second squared.
    • Maximum ship speed: ~350 pixels per second.
    • Drag coefficient: ~0.25 per second.

    Behavior:

    • Thrust accelerates in the direction the nose is pointing.
    • Releasing thrust results in gradual slowing due to drag.
    • No reverse thrust or downward gravity.
    • The ship rotates independently of its travel direction.
  7. How Jinja chat templates work in Kronk

    main

    By default, Kronk does not ship any chat templates. Instead, it relies on the tokenizer.chat_template embedded directly within the model's GGUF file.

    Kronk preserves reasoning/thinking content and template-specific request fields without modification. Because each GGUF template follows its own native policy, users wanting specific behaviors must use the fields supported by that specific template.

  8. How the Kronk SDK and Bucky SDK work

    main

    Kronk provides two primary SDK surfaces for interacting with local GGUF models (via llama.cpp):

    1. Kronk SDK: Used for direct interaction with local open-source GGUF models for text and media inference (vision and audio).
    2. Bucky SDK: A specialized surface for speech-to-text via whisper.cpp.

    Batch Processing Architecture:

    • Generation: Uses Kronk's generation batch engine.
    • Embedding and Reranking: Uses a separate sequence-batch engine that combines complete inputs from concurrent requests onto a single model context.
    • Unverified Architectures: Fall back to a context-pool mechanism.
  9. Implement Asteroid Behavior

    main

    Asteroids are irregular polygons that wrap around the world.

    Asteroid Specifications:

    • Shape: Irregular outlined polygon with 8 to 12 vertices (vertices must remain stable across frames).
    • Size: Collision radius between 28 and 48 pixels.
    • Movement: Random speed (35 to 90 pixels/sec) and random direction/rotation.
    • Spawning: Exactly 6 asteroids per set. When all are destroyed, spawn a new set of 6 after a short delay. New asteroids must not spawn within 180 pixels of the ship.
  10. Override a model's chat template

    main

    You can override the default GGUF template for a specific model by placing a custom Jinja template file in the ~/.kronk/jinja/ directory.

    To target a model, name the file using one of these patterns:

    1. <model-id>.jinja
    2. <model-id-without-quant-suffix>.jinja

    The template loader automatically discovers these files at runtime.

    mkdir -p ~/.kronk/jinja/
    echo "{% for message in messages %}{{ message.content }}{% endfor %}" > ~/.kronk/jinja/my-model-id.jinja
  11. Audio Transcription with Bucky

    main

    Kronk includes whisper.cpp and ffmpeg for audio transcription. You must pull a Whisper model before use. Models are not baked into the images.

    # 1. Pull a model (e.g., ggml-tiny.bin)
    docker exec -it <container> kronk bucky model pull ggml-tiny.bin
    
    # 2. Transcribe an audio file
    curl -X POST http://localhost:11435/v1/audio/transcriptions \
      -F file=@samples/jfk.wav \
      -F model=ggml-tiny.bin \
      -F response_format=json
  12. Initialize and load a model in Kronk

    main

    To use Kronk, you must first ensure the underlying libraries (like llama.cpp) and the model files are available on your system.

    1. Install Libraries: Use libs.New with libs.WithDetect and call libs.Download to fetch the necessary runtime libraries.
    2. Initialize Kronk: Call kronk.Init providing the path to the downloaded libraries via kronk.WithLibPath(libs.LibsPath()).
    3. Download Model: Use models.New() and mdls.Download with a model source string (e.g., "unsloth/Qwen3-0.6B-Q8_0") to fetch the model files.
    4. Create Kronk Instance: Use kronk.New passing model.WithModelFiles(mp.ModelFiles) and optional tuning options like model.WithAutoTune(true).
    5. Cleanup: Always call krn.Unload(ctx) to properly release the model from memory when finished.
    // 1. Install libraries
    libs, _ := libs.New(libs.WithDetect(ctx, kronk.FmtLogger))
    libs.Download(ctx, kronk.FmtLogger)
    
    // 2. Init Kronk with library path
    kronk.Init(kronk.WithLibPath(libs.LibsPath()))
    
    // 3. Download model
    mdls, _ := models.New()
    mp, _ := mdls.Download(ctx, kronk.FmtLogger, "unsloth/Qwen3-0.6B-Q8_0")
    
    // 4. Create instance
    krn, _ := kronk.New(
    	model.WithModelFiles(mp.ModelFiles),
    	model.WithAutoTune(true),
    )
    
    // 5. Unload
    defer krn.Unload(context.Background())