DeepInfra raises $107M Series B to scale the inference cloud — read the announcement

Model Deprecation: Build LLM Apps That Last
Published on 2026.09.24 by Stefan Fidanov
Model Deprecation: Build LLM Apps That Last

Your model ID is the shortest-lived dependency in your stack and odds are it doesn’t have a maintenance schedule. On June 15, 2026, claude-sonnet-4-20250514 and claude-opus-4-20250514 stopped answering requests. Anthropic had posted the notice 62 days earlier. Teams with either string in a call site learned about it from an error rate, not an email.

Model deprecation is a production dependency that you have no control over. 40 model IDs across OpenAI, Anthropic, and Google carry a 2026 retirement date. The standard advice is to add an abstraction layer. That is correct and incomplete: an abstraction layer helps only if something behind it survives, and pointing every candidate in your chain at a different closed vendor buys a second copy of the same calendar.

What follows is the architecture that holds. Model IDs as runtime configuration. A deprecation feed wired into continuous integration. A fallback chain that fails over on the right errors and refuses on the wrong ones. And open weights as the one candidate nobody can recall.

What Model Deprecation Actually Does To A Running Service

A retired model does not get slower or dumber. It returns a 404 with a model_not_found error, to every request and most retry logic makes it worse. Exponential backoff assumes the resource comes back, but this one never does. A well-behaved client uses up its retry budget on a dead endpoint, then gives the user a timeout instead of a real error.

Here is how many codebases start, and what turns a retirement date into an incident:

from openai import OpenAI

client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])

def summarize(ticket_body: str) -> str:
    resp = client.chat.completions.create(
        model="gpt-5-2025-08-07",          # pinned in the call site
        messages=[
            {"role": "system", "content": "Summarize the support ticket in two sentences."},
            {"role": "user", "content": ticket_body},
        ],
    )
    return resp.choices[0].message.content
copy

That string is a hardcoded constant in a file that doesn’t get checked before a release. gpt-5-2025-08-07 shuts down on December 11, 2026, about sixteen months after it shipped.

That is one string out of forty with a published 2026 end date.

The clustering matters for planning. Anthropic’s retirements trickle through the first half of the year. OpenAI’s arrive in three waves. The notice period before each one varies and so does what the endpoint does afterward.

ProviderStated noticeBehavior at cutoff
OpenAIAt least 6 months for generally available models, at least 3 months for specialized variants, as little as 2 weeks for previewsEndpoint removed, requests fail
AnthropicAt least 60 days for publicly released modelsRequests to retired models fail
GooglePublished dates are the earliest possible shutdown, exact date communicated laterModel turned off, endpoint unavailable
DeepInfraAt least 1 weekRequests forwarded to a recommended replacement

Two things to notice in that table. DeepInfra gives the shortest notice of the four, and it is the only one where the date passing does not produce a failed request. Which matters more depends on whether a silent substitution beats a loud 404 for your workload.

The Two Failure Modes Hiding Inside Model Deprecation

Model deprecation is two problems that fail in opposite directions, and the mitigation for each causes the other.

The first is the dated snapshot. You pin gpt-4o-2024-05-13 or claude-opus-4-1-20250805, the weights behind that string never move, and your prompts behave identically for as long as the endpoint exists. Then the date arrives and every call returns 404. The failure is loud, total, and scheduled.

The second is the alias. You point at gpt-5 or a -latest suffix, the provider keeps the string alive across versions, and you never see a 404. What happens instead is structured output starts failing schema validation at four percent instead of 0.2, your few-shot formatting drifts, and your evals change without a deploy. Nothing errored. The weights under the name changed. Aliases are not immune either: DeepInfra’s catalog flagged anthropic/claude-3-7-sonnet-latest deprecated on May 20, 2026, naming anthropic/claude-sonnet-4-6 as its replacement. The alias meant to save you from a dated ID got a date of its own.

They need different instrumentation, which is why they need separated. Mode one is a status code, so an alert on model_not_found grouped by model ID fires the moment the cutoff lands. Mode two never changes your error rate. Only a scheduled run of a fixed prompt set against fixed expected output will catch it. The answers should not move, so any movement means something changed upstream without telling you.

Version pinning buys reproducibility and guarantees a hard cutoff. Aliasing buys continuity and guarantees silent drift. No provider gives you both, which is why the answer is a boundary in your code that knows which string it is using and can change its mind at runtime.

Why An Abstraction Layer Is Only Half The Fix

Every guide lands on the same recommendation: put a provider interface in front of your model calls so the model ID becomes swappable. That is right, but what do you swap to?

A fallback chain of gpt-5.6-sol, then claude-opus-5, then gemini-3.8-flash is three vendors running one policy. Each string is on a schedule set by the company that owns the weights. When the date arrives, the fallback and the primary are in the same position. One year of that, restricted to published dates:

ProviderRetirement dateModel IDsNamed replacement
Anthropic2026-02-19claude-3-7-sonnet-20250219, claude-3-5-haiku-20241022claude-sonnet-4-6, claude-haiku-4-5-20251001
Anthropic2026-06-15claude-sonnet-4-20250514, claude-opus-4-20250514claude-sonnet-4-6, claude-opus-4-8
Anthropic2026-08-05claude-opus-4-1-20250805claude-opus-4-8
Google2026-06-01gemini-2.0-flash, gemini-2.0-flash-001, gemini-2.0-flash-lite, gemini-2.0-flash-lite-001gemini-2.5-flash line
OpenAI2026-09-28gpt-3.5-turbo-instruct, babbage-002, davinci-002, gpt-3.5-turbo-1106gpt-5.6-terra
OpenAI2026-10-2311 IDs including gpt-4-turbo, gpt-4o-2024-05-13, o1-2024-12-17, o3-mini-2025-01-31gpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna
OpenAI2026-12-11gpt-5-2025-08-07, gpt-5-mini, gpt-5-nano, gpt-5-pro, o3-2025-04-16, o3-progpt-5.6-sol, gpt-5.6-terra, gpt-5.6-luna

Sources: OpenAI, Anthropic, and Google deprecation pages, September 4, 2026.

This is the half of vendor lock-in that survives an abstraction layer. Open weights change its shape without eliminating it, since hosts retire open models too. What changes is the meaning of retirement. When a closed model retires, the weights go with it. When a host stops serving meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo, the weights stay published and another provider serves them, so retirement is a hosting decision rather than a terminal event. The usual open-versus-closed argument weighs intelligence, price, and speed. Availability is what decides whether your chain still works next year.

The asymmetry shows up in how long a string keeps answering.

The closed bars are not uniformly short. gpt-4o-2024-05-13 gets about 29 months, longer than some open models have been hosted anywhere. But every closed bar has an end drawn on it, and the open bars do not have one yet.

Make The Model Id Configuration, Not Code

Stop naming models at call sites. Start naming roles. A role is the job you need done: summarize a ticket, review a diff, extract fields from an invoice. Roles stay stable across model generations and model IDs do not, so the mapping between them belongs in a file you can change without a code review.

# config/models.yaml
defaults:
  base_url: https://api.deepinfra.com/v1/openai

roles:
  ticket_summary:
    requires: [json]
    candidates:
      - meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo   # $0.02 / $0.04 per 1M
      - openai/gpt-oss-120b                           # $0.037 / $0.17 per 1M

  code_review:
    requires: [tools, structured-output]
    candidates:
      - Qwen/Qwen3.5-35B-A3B                          # $0.14 / $1.00 per 1M
      - deepseek-ai/DeepSeek-V4-Flash                 # $0.09 / $0.18 per 1M
copy

Two details do real work. The candidate list is ordered, so position zero is what you want and everything after is what you will accept. The requires list names the capabilities the role depends on, turning “is this a valid substitution” into a machine check rather than a judgment made under deadline pressure.

Be deliberate about what stays out. Prompts stay in code, because a prompt change is a behavior change that belongs in review and git history. Sampling parameters too. The config holds the mapping and the capability contract, which keeps it small enough to read in full at 2am.

The resolver behind it stays small. One client, one base URL, one lookup:

# ai/registry.py
import functools
import os

import yaml
from openai import OpenAI


@functools.lru_cache(maxsize=1)
def config() -> dict:
    path = os.environ.get("MODEL_CONFIG", "config/models.yaml")
    with open(path) as fh:
        return yaml.safe_load(fh)


@functools.lru_cache(maxsize=1)
def client() -> OpenAI:
    return OpenAI(
        api_key=os.environ["DEEPINFRA_API_TOKEN"],
        base_url=config()["defaults"]["base_url"],
    )


def candidates(role: str) -> list[str]:
    try:
        return config()["roles"][role]["candidates"]
    except KeyError as exc:
        raise KeyError(f"no candidates configured for role: {role}") from exc
copy

One OpenAI client covers every candidate because DeepInfra speaks the OpenAI Chat Completions protocol, so pointing an OpenAI client at DeepInfra’s endpoint is the whole integration. That matters more here than in a greenfield tutorial. When the candidates in your chain live behind four SDKs, each with its own auth, message shape, and streaming format, your abstraction layer becomes four adapters to maintain. Behind one endpoint, swapping candidates[0] for candidates[1] is a string change. MODEL_CONFIG is the escape hatch on top: when a model goes away at an inconvenient hour, point the process at a corrected config and restart. No build required.

Reading Deepinfra’s Model Deprecation Feed From CI

Every provider publishes deprecations, nearly all to a documentation page written for humans. Checking programmatically then means scraping HTML that was never designed to be parsed and will be redesigned without warning.

DeepInfra publishes the same information as JSON, unauthenticated, on the catalog endpoint. Each entry carries a deprecated field (a Unix timestamp, or null) and a replaced_by field naming its successor:

curl -s https://api.deepinfra.com/models/list \
  | jq -r '.[] | select(.deprecated != null)
           | [(.deprecated | todate | .[0:10]), .model_name, (.replaced_by // "none")]
           | @tsv' \
  | sort | tail -5
copy

Run that today and you get the tail of the retirement schedule, including entries whose date has not arrived yet:

2026-08-24	Qwen/Qwen3-235B-A22B-Thinking-2507	Qwen/Qwen3.6-35B-A3B
2026-09-07	moonshotai/Kimi-K2.5	moonshotai/Kimi-K2.6
2026-09-10	MiniMaxAI/MiniMax-M2.7	MiniMaxAI/MiniMax-M3
2026-09-10	zai-org/GLM-4.7-Flash	zai-org/GLM-5.3-Flash
2026-09-10	zai-org/GLM-5	zai-org/GLM-5.2
copy

As of September 4, 2026 the catalog listed 371 models, 142 carrying a deprecation timestamp, 140 of those naming a replacement, and four dates still in the future. A forward-looking schedule with successor IDs attached, readable by anyone with curl, is a different class of object from a changelog you skim on a Friday.

Pick the warning window against the notice you actually get. DeepInfra commits to at least one week, so a 30-day window catches most entries well before the date while a 7-day window lets some arrive as failures. Set it wide. An early warning costs a line of build output.

The missing-from-catalog branch matters as much as the deprecated one. A model already pulled carries no flag at all. It is simply absent, and absence is what becomes a 404 in production. Catching both stops a stale config surviving a merge.

That makes the check cheap enough to run on every build:

# tools/check_models.py
import sys
import time

import requests
import yaml

CATALOG = "https://api.deepinfra.com/models/list"
WARN_WINDOW_DAYS = 30


def main() -> int:
    catalog = {m["model_name"]: m for m in requests.get(CATALOG, timeout=15).json()}
    with open("config/models.yaml") as fh:
        roles = yaml.safe_load(fh)["roles"]

    now, failed = time.time(), False
    for role, spec in roles.items():
        for rank, model in enumerate(spec["candidates"]):
            entry = catalog.get(model)
            if entry is None:
                print(f"FAIL {role}[{rank}] {model}: not in catalog")
                failed = True
                continue

            when = entry.get("deprecated")
            if not when:
                continue

            days = (when - now) / 86400
            successor = entry.get("replaced_by") or "no replacement named"
            if days <= 0:
                print(f"FAIL {role}[{rank}] {model}: deprecated, use {successor}")
                failed = True
            elif days <= WARN_WINDOW_DAYS:
                print(f"WARN {role}[{rank}] {model}: {days:.0f}d left, then {successor}")

    return 1 if failed else 0


if __name__ == "__main__":
    sys.exit(main())
copy

Wire that into your pipeline and a retirement stops surprising you. The build tells you which role is affected, which position in the chain it sits at, and what the catalog names as successor. The same check runs against whatever another provider exposes. With the closed vendors that means scraping a docs page or paying someone to scrape it, and either way you inherit a parser you did not want to own.

Preflight The Catalog Instead Of Failing A User Request

A build-time check covers the config that shipped. It says nothing about the four weeks your container runs afterward, and the catalog does not hold still for your release cadence. Close that gap at process start.

Assert against the OpenAI-compatible list endpoint, because it returns what is serving right now rather than everything that ever existed. On September 4, 2026 it returned 189 model IDs against 371 in the full catalog, and no deprecated entry appeared in it.

# ai/preflight.py
from ai.registry import client, config


class ModelUnavailable(RuntimeError):
    pass


def preflight() -> None:
    serving = {m.id for m in client().models.list().data}
    problems = []

    for role, spec in config()["roles"].items():
        available = [m for m in spec["candidates"] if m in serving]
        if not available:
            problems.append(f"{role}: no candidate is serving")
        elif available[0] != spec["candidates"][0]:
            print(f"degraded {role}: primary unavailable, running on {available[0]}")

    if problems:
        raise ModelUnavailable("; ".join(problems))
copy

One failure mode is worth naming first. A preflight that calls a third party on every boot makes it a hard dependency of your deploys. Give it a short timeout and decide in advance what a timeout means. Treating an unreachable catalog as a pass is usually right: a network blip should not block a rollback, and CI already checked the config you are shipping.

Call preflight() from your startup path, before the process reports healthy. Two outcomes matter, and they are deliberately different. A role with no serving candidate raises. The container never passes its health check, so your deployment system holds the previous version instead of rolling a broken one into the load balancer. A role whose primary is gone but whose backup is fine logs a degraded line and keeps going. That one is a page for the morning, not an outage.

A Fallback Chain That Fails Over On Availability, Not On Your Own Bad Request

Fallback logic is where this usually goes wrong, and the mistake is always the same: catching Exception and moving to the next candidate. That turns a bug in your own payload into a loop that sends the same malformed request to every model you configured, pays for each rejection, and reports the last error rather than the first useful one.

Sort every failure into “the model is unavailable” or “the request is wrong”, and let only the first kind advance the chain.

FailureWhat it meansAction
404 model_not_foundThe ID is retired or was never validAdvance to the next candidate immediately
429 rate limitedCapacity, not correctnessBack off and retry, then advance
500, 502, 503Provider-side faultBack off and retry, then advance
Connection error, timeoutNetwork, ambiguousBack off and retry, then advance
400 invalid requestYour payload is malformedRaise. Every candidate will reject it
401, 403Auth or permissionsRaise. Retrying makes it worse
422 unsupported parameterThis model lacks a feature you asked forRaise, and fix the capability contract
# ai/complete.py
import logging
import time

from openai import (
    APIConnectionError,
    APIStatusError,
    BadRequestError,
    NotFoundError,
    RateLimitError,
)

from ai.registry import candidates, client

log = logging.getLogger(__name__)
RETRYABLE = (RateLimitError, APIConnectionError)


def complete(role: str, **kwargs):
    chain = candidates(role)
    last_error = None

    for model in chain:
        for attempt in range(3):
            try:
                response = client().chat.completions.create(model=model, **kwargs)
                if model != chain[0]:
                    log.warning("role=%s served by fallback %s", role, model)
                return response
            except BadRequestError:
                raise                       # our payload, not their availability
            except NotFoundError as exc:
                last_error = exc
                break                       # model is gone, next candidate
            except RETRYABLE as exc:
                last_error = exc
                time.sleep(2**attempt)
            except APIStatusError as exc:
                last_error = exc
                if exc.status_code < 500:
                    raise
                time.sleep(2**attempt)

    raise RuntimeError(f"all candidates exhausted for role: {role}") from last_error
copy

The diagram is the classification table drawn as a boundary: every response takes exactly one arrow, and only the availability arrows move down the candidate list.

The log.warning on a non-primary response is what prevents the chain from degrading silently. The primary retired in March, everything has run on the backup since, quality moved slightly, and nobody knows the config is fiction. Alert on it and a retirement announces itself in your own telemetry, in the words of your own roles, whether or not the email reached anybody.

The Parts Of A Swap An Abstraction Layer Cannot Cover

Changing the string is the easy half. What breaks is everything downstream that is tuned to the old model’s behavior. No amount of interface design saves you from a candidate model that cannot follow the same instructions correctly.

This is what the requires list in the config was for. DeepInfra’s catalog tags each model with the capabilities it actually supports, so the contract is checkable rather than aspirational:

# tools/check_capabilities.py
import requests
import yaml

catalog = {m["model_name"]: set(m.get("tags") or []) for m in
           requests.get("https://api.deepinfra.com/models/list", timeout=15).json()}

roles = yaml.safe_load(open("config/models.yaml"))["roles"]

for role, spec in roles.items():
    required = set(spec.get("requires") or [])
    for model in spec["candidates"]:
        missing = required - catalog.get(model, set())
        if missing:
            print(f"FAIL {role}: {model} lacks {sorted(missing)}")
copy

Point that at a code_review role requiring tools and structured-output and it tells you before merge that a candidate cannot do function calling. Better than finding out during a failover.

Streaming is the subtlety that survives the tag check. Every candidate emits deltas, but reasoning models emit a stretch of thinking first, and a client written against a non-reasoning model treats that as the answer. meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo carries a non-reasoning tag while deepseek-ai/DeepSeek-V4-Flash carries reasoning, so a chain that lists both needs a consumer that handles the difference:

def stream(role: str, messages: list[dict]) -> str:
    answer = []
    for chunk in complete(role, messages=messages, stream=True):
        delta = chunk.choices[0].delta
        if getattr(delta, "reasoning_content", None):
            continue                       # thinking tokens, not the answer
        if delta.content:
            answer.append(delta.content)
            yield delta.content
    return "".join(answer)
copy

Context windows are the other silent mismatch. Qwen/Qwen2.5-72B-Instruct serves 32K while deepseek-ai/DeepSeek-V4-Flash serves 1M, so a chain ordered by price can hand a request that fits the primary to a backup that rejects it. The shortest context in the chain is your real limit. Truncating to it deliberately beats discovering it through a 400.

Structured output and tool calling deserve one probe you run against any candidate before it joins a chain. “Supports JSON mode” and “returns the schema you asked for under your prompts” are different claims:

SCHEMA = {
    "type": "object",
    "properties": {"severity": {"enum": ["low", "medium", "high"]},
                   "summary": {"type": "string"}},
    "required": ["severity", "summary"],
    "additionalProperties": False,
}

def probe(model: str, sample: str) -> dict:
    resp = client().chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": sample}],
        response_format={"type": "json_schema",
                         "json_schema": {"name": "triage", "schema": SCHEMA,
                                         "strict": True}},
        tools=[{"type": "function",
                "function": {"name": "escalate",
                             "parameters": {"type": "object", "properties": {}}}}],
    )
    return {"model": model, "content": resp.choices[0].message.content,
            "tool_calls": resp.choices[0].message.tool_calls}
copy

Run the probe across the whole chain, not just the primary. A backup that has never served a request is one you are guessing about, and the guess resolves on the worst day. Store the output per candidate and diff it when the catalog changes, since a model can keep its ID and its tags while its behavior under your schema shifts. That is the same discipline behind model routing across agentic workloads, where one task swaps models between steps and every one has to honor the same tool schema.

Gate The Swap With A Golden Set Before The Deadline

The line item that hurts in a forced migration is almost never the API bill. Prices between comparable open models sit close enough that a swap barely registers on the invoice. What costs you is proving the new model still does the job. Re-running evals. Finding the prompts that leaned on the old model’s formatting habits. That takes the same engineer-days whether you scheduled it or a retirement date did.

So schedule it. A golden set is a fixed list of production-shaped inputs with known-acceptable outputs. Running the backups against it on a cron turns a migration into a diff:

# tools/golden.py
import json

from ai.registry import candidates, client


def score(role: str, cases: list[dict]) -> dict[str, float]:
    results = {}
    for model in candidates(role):
        passed = 0
        for case in cases:
            out = client().chat.completions.create(
                model=model, messages=case["messages"], seed=7,
            ).choices[0].message.content
            passed += case["check"](out)
        results[model] = passed / len(cases)
    return results


if __name__ == "__main__":
    cases = json.load(open("evals/ticket_summary.json"))
    print(score("ticket_summary", cases))
copy

Run it against every candidate, not only the one serving. When a retirement lands, the decision is already made and the evidence collected. The candidate pool those roles draw from:

ModelContextInput / 1MOutput / 1MQuantization
Meta-Llama-3.1-8B-Instruct-Turbo131K$0.02$0.04fp8
gpt-oss-120b131K$0.04$0.17bfloat16
DeepSeek-V4-Flash1M$0.09$0.18fp8
Qwen3.5-35B-A3B262K$0.14$1.00fp8
DeepSeek-V3163K$0.32$0.89fp4
Qwen2.5-72B-Instruct32K$0.36$0.40fp8

Prices from the live DeepInfra catalog, September 4, 2026. The Qwen entries move often enough that a current breakdown of Qwen API pricing is worth a look before you commit a candidate order.

Weekly runs are close to free. Roughly: 200 cases at about 1,500 input and 300 output tokens, across four candidates, is around 1.2M input and 240K output tokens a week. On meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo at $0.02 and $0.04 per million, that estimate lands near four cents a week. On the priciest model in the table it stays under sixty. Set against the cost of running inference at production scale, continuous proof that your fallbacks work is a rounding error.

Where This Leaves You

None of this is complicated. A YAML file, a curl in CI, a boot-time assertion, an error-classification table, and a cron job. Do it because the alternative is a dated outage you were warned about and did not write down.

The real choice is what goes in the candidate list. Every closed model carries a retirement date the vendor sets and can move, and when it arrives the weights are gone. An open-weight candidate can lose its host, but the weights stay published and the failure degrades to a base URL change. That is the argument for keeping open models in the chain rather than treating them as the cheap option.

DeepInfra serves 189 of them behind one OpenAI-compatible endpoint, with the retirement schedule published as JSON a build script can read. Browse the model catalog, check pricing, or read the docs. If you build on this pattern and hit an edge we missed, tell us at feedback@deepinfra.com, join us on Discord, or find us on X at @DeepInfra.

Frequently Asked Questions

How much notice do LLM providers give before a model is deprecated?

It depends on the model tier more than the provider. OpenAI commits to at least six months for generally available models but as little as two weeks for previews. Anthropic commits to at least 60 days for publicly released models. Google publishes the earliest possible shutdown date and confirms the exact one later. Previews are the short-notice trap in every catalog.

Does pinning a dated model snapshot protect me from model deprecation?

No, pinning solves a different problem. A dated snapshot like gpt-4o-2024-05-13 guarantees the weights behind the string never change, which protects your prompts from silent drift, but it also guarantees a hard cutoff on a published date. Aliases avoid the cutoff and accept the drift. Pick per role and write down which you picked.

What happens to my request when a DeepInfra model is deprecated?

DeepInfra’s documentation states that requests are automatically forwarded to a recommended replacement rather than failing, after at least one week of notice. That is gentler than a 404 and still a behavior change you did not deploy. Treat the forward as a grace period for finishing a migration, not a substitute for one.

Can I keep using an open-weight model after a provider stops hosting it?

Yes, and that is the practical difference from a closed model. The weights are published, so another provider can serve the same checkpoint, or you can run it yourself. You lose that host’s serving stack, quantization, and price, not access to the model. Expect small output differences between hosts running different quantizations.

How often will I actually have to migrate?

Plan on at least once a year per model in production. In 2026 alone, 40 model IDs across OpenAI, Anthropic, and Google carry a published retirement date, and flagships are not exempt: claude-opus-4-1-20250805 shipped in August 2025 and retired in August 2026. Budget eval time annually and migrations stop being surprises.

Related articles
DeepSeek V4 Pro Pricing Guide 2026: Pricing, Providers & Cost ComparisonDeepSeek V4 Pro Pricing Guide 2026: Pricing, Providers & Cost Comparison<p>DeepSeek V4 Pro matters because it pushes two levers developers actually care about at the same time: open-weight availability and a very competitive provider market. As of the research here, DeepSeek V4 Pro Max is tracked across six API providers, and five of them cluster at the same blended price of $2.17 per 1M tokens [&hellip;]</p>
Langchain improvements: async and streamingLangchain improvements: async and streamingStarting from langchain v0.0.322 you can make efficient async generation and streaming tokens with deepinfra. Async generation The deepinfra wrapper now supports native async calls, so you can expect more performance (no more t...
Qwen API Pricing Guide 2026: Max Performance on a BudgetQwen API Pricing Guide 2026: Max Performance on a Budget<p>If you have been following the AI leaderboards lately, you have likely noticed a name that keeps trading blows with the field&#8217;s proprietary leaders: Qwen. Developed by Alibaba Cloud, the Qwen model family has kept expanding at a rapid pace — the current lineup spans Qwen3, Qwen3.5, Qwen3.6, and the flagship Qwen3.8-Max, alongside dedicated coding [&hellip;]</p>