DEV Community

shashank ms
shashank ms

Posted on

Mastering LLM Model Versioning: A Step-by-Step Guide

Teams running LLMs in production need a safe way to test new model releases before cutting over traffic. In this tutorial we will build a versioned inference router that calls multiple Oxlo.ai models, compares their outputs, and recommends a winner. The final script gives you canary deployments and A/B tests without external infrastructure.

What you'll need

Step 1: Scaffold the version manifest and generator

First we define a manifest that maps semantic tags to Oxlo.ai model IDs. This keeps application code decoupled from provider-specific model strings so swaps only require a one-line change.

from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

MANIFEST = {
    "stable": "llama-3.3-70b",
    "candidate": "qwen-3-32b",
    "fallback": "deepseek-v3.2"
}

def generate(tag: str, system_prompt: str, user_message: str, temperature: float = 0.7):
    model = MANIFEST.get(tag, MANIFEST["fallback"])
    response = client.chat.completions.create(
        model=model,
        messages=[
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_message},
        ],
        temperature=temperature,
    )
    return {
        "tag": tag,
        "model": model,
        "content": response.choices[0].message.content,
    }

Step 2: Add automatic failover

If a model is temporarily unavailable, we catch the error and fall back to the fallback tag. Oxlo.ai hosts over 45 models with no cold starts on popular ones, so switching is fast.

from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

MANIFEST = {
    "stable": "llama-3.3-70b",
    "candidate": "qwen-3-32b",
    "fallback": "deepseek-v3.2"
}

def generate_with_failover(tag: str, system_prompt: str, user_message: str, temperature: float = 0.7):
    model = MANIFEST.get(tag, MANIFEST["fallback"])
    try:
        response = client.chat.completions.create(
            model=model,
            messages=[
                {"role": "system", "content": system_prompt},
                {"role": "user", "content": user_message},
            ],
            temperature=temperature,
        )
        return {
            "tag": tag,
            "model": model,
            "content": response.choices[0].message.content,
        }
    except Exception:
        if tag == "fallback":
            raise
        return generate_with_failover("fallback", system_prompt, user_message, temperature)

Step 3: Build the side-by-side comparison harness

Before promoting a model from candidate to stable, we run the same prompt against both versions and collect the results in a structured dictionary.

def compare_versions(system_prompt: str, user_message: str):
    return {
        "stable": generate_with_failover("stable", system_prompt, user_message),
        "candidate": generate_with_failover("candidate", system_prompt, user_message),
    }

Step 4: Define the evaluator agent system prompt

I use a separate judge to score outputs. Because Oxlo.ai uses flat per-request pricing (https://oxlo.ai/pricing), adding this evaluation step stays cheap even when we pass long contexts to the judge.

EVALUATOR_PROMPT = """You are a model-version evaluator. Two responses are provided for the same user prompt.

Task:
1. Score each response from 1 to 10 on accuracy, clarity, and safety.
2. State which response is better and why.
3. Recommend whether the candidate model should replace the stable model.

Respond in JSON with keys: stable_score, candidate_score, winner, reasoning, recommend_promotion."""

def evaluate(stable_output: str, candidate_output: str, user_message: str):
    from openai import OpenAI

    client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

    payload = (
        f"User prompt: {user_message}\n\n"
        f"Stable response:\n{stable_output}\n\n"
        f"Candidate response:\n{candidate_output}"
    )

    response = client.chat.completions.create(
        model="kimi-k2.6",
        messages=[
            {"role": "system", "content": EVALUATOR_PROMPT},
            {"role": "user", "content": payload},
        ],
        response_format={"type": "json_object"},
    )
    return response.choices[0].message.content

Step 5: Wire the canary pipeline

This final helper orchestrates the comparison and the judge into a single verdict. I run the stable and candidate versions through Oxlo.ai, then pass both outputs to the evaluator.

import json
from openai import OpenAI

client = OpenAI(base_url="https://api.oxlo.ai/v1", api_key="YOUR_OXLO_API_KEY")

def run_canary(system_prompt: str, user_message: str):
    outputs = compare_versions(system_prompt, user_message)

    judge_text = evaluate(
        outputs["stable"]["content"],
        outputs["candidate"]["content"],
        user_message,
    )

    return {
        "stable": outputs["stable"],
        "candidate": outputs["candidate"],
        "verdict": json.loads(judge_text),
    }

Run it

Test with a support scenario that mixes policy logic and tone. The script calls Oxlo.ai three times: once for stable, once for candidate, and once for the judge.

SYSTEM_PROMPT = "You are a helpful support agent. Refund requests under $50 are approved automatically. Explain your decision in one sentence."

if __name__ == "__main__":
    result = run_canary(
        SYSTEM_PROMPT,
        "I want a refund for my $29 charging cable. It arrived frayed."
    )
    print("Stable:", result["stable"]["content"])
    print("Candidate:", result["candidate"]["content"])
    print("Verdict:", json.dumps(result["verdict"], indent=2))

Example output:

Stable: Your refund for the $29 charging cable has been approved because it arrived damaged and falls under our automatic under-$50 refund policy.
Candidate: I've approved your $29 refund since damaged items under $50 qualify for automatic approval.
Verdict: {
  "stable_score": 8,
  "candidate_score": 9,
  "winner": "candidate",
  "reasoning": "Both answers are accurate, but the candidate is more concise while preserving all required details.",
  "recommend_promotion": true
}

Next steps

Hook this script into your CI pipeline so every manifest change triggers a canary test before merge. You can also store results in SQLite and track win rates across a prompt regression suite to automate model promotions.

Top comments (0)