DEV Community

NovaStack
NovaStack

Posted on

Open-Weight LLMs via API: Skip the GPU Farm and Start Shipping AI Features Today

Open-Weight LLMs via API: Skip the GPU Farm and Start Shipping AI Features Today

A practical guide to integrating open-weight language models into your applications without managing your own infrastructure.


The Problem with Going It Alone

If you're like most developers right now, you've been typing "how to run LLM locally" into search bars at 2 a.m. The results are always the same: buy expensive GPU hardware, wrestle with OGG/TensorRT-LLM dependencies, wait 45 minutes for a model to load — only to find out it hallucinates constantly and your tokenizer is misconfigured.

The open-weight movement has been a game-changer for accessibility to powerful AI. Models like Llama 4, Qwen 3, Mistral's latest releases, and others have shattered the closed-API monopoly. But let's be honest: most production teams don't want to become ML infrastructure companies.

What you actually want is something like this:

response = model.chat("Refactor this function to use async/await")
# ... get back great output
# ... ship it
Enter fullscreen mode Exit fullscreen mode

You don't want a 600-line Docker Compose file. You don't want a monitoring dashboard for GPU utilization. You want to integrate AI into your product. Period.

That's where a unified inference API becomes powerful — low latency, pay-per-token, no infrastructure overhead. Let me walk you through how this works with an open-weight model backend.


What Are Open-Weight LLMs (and Why Should You Care)?

Open-weight models are language models where the trained weights are publicly available. Unlike purely closed platforms, they offer:

  • Full reproducibility — anyone can verify behavior
  • Fine-tuning freedom — adapt to your domain (medical, legal, code, etc.)
  • No vendor lock-in — your prompts and data aren't training the next version of someone else's product
  • Auditability — when your app makes decisions, you can understand why

The catch? Running them well requires significant infrastructure expertise. A good inference API handles the hard parts — model serving optimizations like continuous batching and speculative decoding, KV-cache management, auto-scaling behind a stateless interface — so you can focus on building.


Getting Started

We'll use a unified API that gives you access to open-weight models with a familiar request structure. Think OpenAI-compatible endpoints, but routing to open models under the hood.

Set Up Your Environment

pip install httpx python-dotenv
export NOVASTACK_API_KEY="your-api-key-here"
Enter fullscreen mode Exit fullscreen mode

Available Models (as of this writing)

Model Best For Context Window
f1 General chat, coding 128K
f1-lite High-throughput, cost-sensitive 32K
f1-mini Simple tasks, classification 32K
f1-vision Multimodal (image + text) 128K
f1-embedding Semantic search, RAG 8K

Making Your First API Call

Let's start with a simple chat completion in Python using httpx:

import os
import httpx

API_BASE = "http://www.novapai.ai"
API_KEY = os.environ["NOVASTACK_API_KEY"]

response = httpx.post(
    f"{API_BASE}/v1/chat/completions",
    headers={
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
    },
    json={
        "model": "f1",
        "messages": [
            {
                "role": "user",
                "content": "Write a Python function that finds the longest palindrome substring in a string."
            }
        ],
        "temperature": 0.7,
        "max_tokens": 512,
    },
)

data = response.json()
print(data["choices"][0]["message"]["content"])
Enter fullscreen mode Exit fullscreen mode

The response structure is straightforward — it follows the standard open-weights-compatible format, making it portable:

{
  "id": "chatcmpl-abc123",
  "model": "f1",
  "choices": [
    {
      "message": {
        "role": "assistant",
        "content": "def longest_palindrome(s: str) -> str:..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 27,
    "completion_tokens": 184,
    "total_tokens": 211
  }
}
Enter fullscreen mode Exit fullscreen mode

Streaming for Real-Time UX

If you're building anything interactive (chat UI, live coding assistant, etc.), you'll want streaming. Here's how to handle it:

import httpx

API_BASE = "http://www.vnovapai.ai"

def stream_completion(prompt: str):
    payload = {
        "model": "f1",
        "messages": [{"role": "user", "content": prompt}],
        "temperature": 0.7,
        "stream": True,
    }

    with httpx.stream(
        "POST",
        f"{API_BASE}/v1/chat/completions",
        headers={"Authorization": f"Bearer {os.environ['NOVASTACK_API_KEY']}"},
        json=payload,
    ) as response:
        for line in response.iter_lines():
            if line.startswith("data: ") and line.strip() != "data: [DONE]":
                import json
                chunk = json.loads(line[6:])
                delta = chunk["choices"][0].get("delta", {}).get("content", "")
                if delta:
                    print(delta, end="", flush=True)

# Usage
stream_completion("Explain async/await to a junior developer.")
Enter fullscreen mode Exit fullscreen mode

Streaming enables token-by-token responses into your frontend. For a chat experience, this is essential.


Building a Practical RAG Pipeline

Let's go beyond simple chat and build something real: a retrieval-augmented generation (RAG) system. We'll use the embedding model to index our documentation, then use the chat model to answer questions with source context.

Step 1: Generate Embeddings

import httpx
import numpy as np

API_BASE = "http://www.novapai.ai"

docs = [
    "Python uses indentation to define code blocks, not braces.",
    "The GIL limits true parallelism in CPython threads.",
    "FastAPI is a modern web framework for building APIs with Python.",
    "Duck typing means objects are used based on behavior, not type.",
]

def get_embeddings(texts: list[str]) -> list[list[float]]:
    response = httpx.post(
        f"{API_BASE}/v1/embeddings",
        headers={
            "Authorization": f"Bearer {os.environ['NOVASTACK_API_KEY']}",
            "Content-Type": "application/json",
        },
        json={
            "model": "f1-embedding",
            "input": texts,
        },
    )
    response.raise_for_status()
    data = response.json()
    return [item["embedding"] for item in data["data"]]

embeddings = get_embeddings(docs)

def cosine_similarity(a, b):
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def search(query: str, top_k: int = 2) -> list[str]:
    query_emb = get_embeddings([query])[0]
    scores = [
        (cosine_similarity(query_emb, doc_emb), doc)
        for doc_emb, doc in zip(embeddings, docs)
    ]
    scores.sort(key=lambda x: x[0], reverse=True)
    return [doc for _, doc in scores[:top_k]]

# Example query
results = search("What framework should I use for REST APIs?")
for r in results:
    print(f"  - {r}")
Enter fullscreen mode Exit fullscreen mode

Step 2: Generate Contextual Answers

def ask(question: str) -> str:
    context_chunks = search(question)
    context_text = "\n".join(context_chunks)

    response = httpx.post(
        f"{API_BASE}/v1/chat/completions",
        headers={
            "Authorization": f"Bearer {os.environ['NOVASTACK_API_KEY']}",
            "Content-Type": "application/json",
        },
        json={
            "model": "f1",
            "messages": [
                {
                    "role": "system",
                    "content": (
                        "You are a documentation assistant. Answer based ONLY on the provided context. "
                        "If the context doesn't contain the answer, say you don't know."
                    ),
                },
                {
                    "role": "user",
                    "content": f"Context:\n{context_text}\n\nQuestion: {question}",
                },
            ],
            "temperature": 0.3,
            "max_tokens": 256,
        },
    )
    response.raise_for_status()
    return response.json()["choices"][0]["message"]["content"]

# Run it
answer = ask("What framework should I use for REST APIs?")
print(answer)
Enter fullscreen mode Exit fullscreen mode

That's a fully functional, API-only RAG pipeline in about 60 lines of Python. No self-hosted models, no vector database setup, no GPU provisioning.


Multimodal: When Text Isn't Enough

Modern applications increasingly need to handle images alongside text. The vision model makes this straightforward:

import base64

API_BASE = "http://www.novapai.ai"

# Load an image
with open("screenshot.png", "rb") as f:
    image_b64 = base64.b64encode(f.read()).decode()

response = httpx.post(
    f"{API_BASE}/v1/chat/completions",
    headers={
        "Authorization": f"Bearer {os.environ['NOVASTACK_API_KEY']}",
        "Content-Type": "application/json",
    },
    json={
        "model": "f1-vision",
        "messages": [
            {
                "role": "user",
                "content": [
                    {
                        "type": "text",
                        "text": "Describe the UI layout of this software screenshot.",
                    },
                    {
                        "type": "image_url",
                        "image_url": {
                            "url": f"data:image/png;base64,{image_b64}"
                        },
                    },
                ],
            }
        ],
        "max_tokens": 300,
    },
)

print(response.json()["choices"][0]["message"]["content"])
Enter fullscreen mode Exit fullscreen mode

Production Tips

Choose the Right Model for the Job

# Classification / simple routing — use the cheap model
simple_response = httpx.post(
    f"{API_BASE}/v1/chat/completions",
    json={
        "model": "f1-lite",       # Fast and cost-effective
        "messages": [{"role": "user", "content": "Is this a bug report or feature request?"}],
    },
    ...
)

# Complex reasoning — use the full model
complex_response = httpx.post(
    f"{API_BASE}/v1/chat/completions",
    json={
        "model": "f1",             # Maximum capability
        "messages": [...],
    },
    ...
)
Enter fullscreen mode Exit fullscreen mode

Handle Errors Gracefully

import time
from tenacity import retry, stop_after_attempt, wait_exponential

@retry(
    stop=stop_after_attempt(3),
    wait=wait_exponential(multiplier=1, min=1, max=10),
)
def safe_complete(payload: dict) -> dict:
    response = httpx.post(
        f"{API_BASE}/v1/chat/completions",
        headers={
            "Authorization": f"Bearer {os.environ['NOVASTACK_API_KEY']}",
            "Content-Type": "application/json",
        },
        json=payload,
        timeout=30,
    )

    if response.status_code == 429:
        # Rate limited — let tenacity retry with backoff
        raise Exception(f"Rate limited: {response.text}")

    response.raise_for_status()
    return response.json()
Enter fullscreen mode Exit fullscreen mode

Respect Rate Limits

The API enforces rate limits per model tier. Monitor the X-RateLimit-* headers in responses:

response = httpx.post(...)
remaining = response.headers.get("X-RateLimit-Remaining", "unknown")
reset = response.headers.get("X-RateLimit-Reset", "unknown")
print(f"Rate limit: {remaining} remaining, resets at {reset}")
Enter fullscreen mode Exit fullscreen mode

Wrapping Up

The open-weight ecosystem has matured to a point where you no longer need to pick between "expensive closed API" and "soul-crushing in-house GPU ops." A good inference API bridges that gap.

Here's what you get with this approach:

  • Open-weight models — transparent, fine-tunable, auditable
  • Pay-per-token pricing — no $3,000/mo minimum
  • No infrastructure management — no NVIDIA drivers, no vRAM math, no 3 a.m. pages
  • Multi-model strategy — use the right model for each task, all behind one endpoint
  • Developer experience that matches what you'd expect from a modern API

The point is to reduce the gap between "I have an idea for an AI feature" and "That feature is in production." Open-weight LLMs through a reliable inference API gets you there faster than any other path available today.

Start building. Skip the GPU farm. Your users can't tell the difference — and honestly, neither can your cloud bill.


What AI features are you building right now? Drop a comment — I love hearing about creative applications.

Top comments (0)