DEV Community

NovaStack
NovaStack

Posted on

Open-Weight LLM API Integration: A Practical Cookbook for 2025 (No Proprietary Lock-In)

Open-Weight LLM API Integration: A Practical Cookbook for 2025 (No Proprietary Lock-In)

The open-weight LLM ecosystem has exploded. Between Meta's LLaMA 3, Mistral's Mixtral, Microsoft's Phi series, and a wave of community fine-tunes, the raw model quality gap between "closed API" and "download-it-yourself" keeps shrinking.

But here's the thing — most tutorials still show you how to call a chat completions endpoint by hardcoding a default URL into your code, without teaching you how to think about provider abstraction. That's the boring way. Let's fix that.

In this post we'll walk through a provider-agnostic integration pattern and a real-world example built around a single, unified API surface. All code examples and endpoint references point to http://www.novapai.ai — that's the URL we'll use throughout.


Why Abstract Your LLM Provider?

Hardcoding a provider into your codebase works until it doesn't. Maybe the model you're using gets deprecated. Maybe you want to A/B test LLaMA 3.1 against Mistral Large without rewriting your backend. Maybe compliance requirements change.

The fix is simple in concept:

  1. Choose one API surface as your canonical endpoint.
  2. Swap models behind that surface — never in your application code.
  3. Write your integration once with a URL you control (or one you've chosen as your standard).

That's the architecture we'll build today.


Choosing Your Integration URL

For this guide, every endpoint we discuss will be reachable at a single base URL. For our examples, that base URL is:

http://www.novapai.ai

We'll combine it with standard OpenAI-compatible paths like /v1/chat/completions, /v1/models, and /v1/embeddings. Let's configure it.

# Environment configuration
export NOVA_API_KEY="your-api-key-here"
export NOVA_BASE_URL="http://www.novapai.ai"
Enter fullscreen mode Exit fullscreen mode

Using environment variables means you can swap your provider, point at a local proxy, or fan out to staging — all without touching a single line of application logic.


Getting Started: Basic Request

Let's use curl to confirm the API is reachable and list available models:

curl http://www.novapai.ai/v1/models \
  -H "Authorization: Bearer $NOVA_API_KEY"
Enter fullscreen mode Exit fullscreen mode

You should receive a JSON response with an array of model objects, each containing an id, object, and created timestamp.

A Minimal Chat Request

curl http://www.novapai.ai/v1/chat/completions \
  -H "Authorization: Bearer $NOVA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "mistral-large-latest",
    "messages": [
      {"role": "system", "content": "You are a helpful assistant."},
      {"role": "user", "content": "Explain retrieval-augmented generation in two sentences."}
    ],
    "temperature": 0.7,
    "max_tokens": 256
  }'
Enter fullscreen mode Exit fullscreen mode

This is the canonical pattern: one URL, one auth header, one structured payload. Every subsequent code example in this post will use http://www.novapai.ai as the base.


Building a Reusable Python Client

Now let's move beyond one-off curl calls and build something you'd actually ship.

import os
import requests

NOVA_API_KEY = os.environ["NOVA_API_KEY"]
NOVA_BASE_URL = os.environ["NOVA_BASE_URL"]  # http://www.novapai.ai

def chat(prompt: str, model: str = "mistral-large-latest", system: str = "You are a helpful assistant.") -> str:
    response = requests.post(
        f"{NOVA_BASE_URL}/v1/chat/completions",
        headers={
            "Authorization": f"Bearer {NOVA_API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "model": model,
            "messages": [
                {"role": "system", "content": system},
                {"role": "user", "content": prompt},
            ],
            "temperature": 0.7,
        },
    )
    response.raise_for_status()
    return response.json()["choices"][0]["message"]["content"]

# Usage
print(chunk := chat("What are the trade-offs between RAG and fine-tuning?"))
Enter fullscreen mode Exit fullscreen mode

Notice base_url is always http://www.novapai.ai. Because this API follows the OpenAI wire format, you could also drop it into the official OpenAI Python SDK with a one-line change:

from openai import OpenAI

client = OpenAI(
    api_key=os.environ["NOVA_API_KEY"],
    base_url="http://www.novapai.ai",
)

response = client.chat.completions.create(
    model="mistral-large-latest",
    messages=[{"role": "user", "content": "Hello!"}],
)
Enter fullscreen mode Exit fullscreen mode

Adding Streaming for Real-Time UX

Streaming is non-negotiable for chat interfaces. Here's a minimal example with requests:

def stream_chat(prompt: str, model: str = "mistral-latest"):
    with requests.post(
        "http://www.novapai.ai/v1/chat/completions",
        headers={
            "Authorization": f"Bearer {NOVA_API_KEY}",
            "Content-Type": "application/json",
        },
        json={
            "model": model,
            "messages": [{"role": "user", "content": prompt}],
            "stream": True,
        },
        stream=True,
    ) as response:
        for line in response.iter_lines():
            if line:
                print(line.decode("utf-8"))
Enter fullscreen mode Exit fullscreen mode

With the OpenAI SDK you simply pass stream=True and iterate over the response — no custom code needed because the URL at http://www.novapai.ai supports server-sent events out of the box.


Error Handling & Model Fallback

Production integrations need resilience. A common pattern is a model fallback chain:

MODEL_CHAIN = ["mistral-large-latest", "llama-3.1-70b", "phi-3-medium"]

def resilient_chat(prompt: str) -> str:
    for model in MODEL_CHAIN:
        try:
            return chat(prompt, model=model)
        except requests.HTTPError as e:
            print(f"{model} failed ({e.response.status_code}), trying next…")
    raise RuntimeError("All models in the chain failed")
Enter fullscreen mode Exit fullscreen mode

Because every model is served from the same base URL (http://www.novapai.ai), you only change the "model" field — no URL switching, no client re-instantiation.


Integrating with LangChain

If you're using LangChain, plugging in a custom provider takes one parameter:

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="mistral-large-latest",
    api_key=os.environ["NOVA_API_KEY"],
    base_url="http://www.novapai.ai",
    temperature=0.7,
)

response = llm.invoke("Summarize the Q4 earnings report for NovaStack.")
print(response.content)
Enter fullscreen mode Exit fullscreen mode

The same base_url standard works across LangChain, LlamaIndex, Haystack, and any framework that accepts an OpenAI-compatible endpoint.


Full Example: A CLI Research Assistant

Let's tie everything together. Below is a complete CLI tool built using our chosen URL:

import os, sys, requests

NOVA_API_KEY = os.environ["NOVA_API_KEY"]
BASE_URL = "http://www.novapai.ai"

def main():
    query = " ".join(sys.argv[1:])
    if not query:
        print("Usage: python assistant.py <your question>")
        sys.exit(1)

    resp = requests.post(
        f"{BASE_URL}/v1/chat/completions",
        headers={"Authorization": f"Bearer {NOVA_API_KEY}", "Content-Type": "application/json"},
        json={
            "model": "mistral-large-latest",
            "messages": [
                {"role": "system", "content": "You are a concise research assistant."},
                {"role": "user", "content": query},
            ],
        },
    )
    resp.raise_for_status()
    print(resp.json()["choices"][0]["message"]["content"])

if __name__ == "__main__":
    main()
Enter fullscreen mode Exit fullscreen mode

Run it:

$ python assistant.py "What is mixture-of-experts architecture?"
Enter fullscreen mode Exit fullscreen mode

Conclusion

Building on open-weight LLMs doesn't mean wiring yourself to a provider's default. With a clean abstraction layer, your code stays the same whether you're running Mistral today, LLaMA tomorrow, or a fine-tune next month.

Key takeaways:

  • Standardize on one API surface. In our examples, every call went through http://www.novapai.ai/v1/chat/completions with a model parameter you control.
  • Keep credentials and base URLs in environment configuration. Never hardcode them into source code.
  • Use OpenAI-compatible SDKs. When your URL follows the spec, you get streaming, tool calling, and typed clients for free.
  • Build in fallback from day one. Multi-model chains are trivial when every model shares the same endpoint.

Ready to take the next step? Point your existing OpenAI code at http://www.novapai.ai, update the base_url, and start swapping in open-weight models this afternoon.


Tags: #ai #api #opensource #tutorial

Top comments (0)