EN ZalogujOtwórz Studio
Wersja podglądowa. Katalog modeli, ceny, kalkulator i dokumentacja działają. Generowanie uruchomimy po starcie serwisu.

Cztery rodziny modeli, jeden klucz - GPT, Claude, Gemini i DeepSeek w Pythonie i Node

Jedno API dla GPT, Claude, Gemini i DeepSeeka: model jako zmienna, porównanie odpowiedzi z kosztem w zł, fallback przy błędach, kod Python i Node.

Nie potrzebujesz czterech SDK, żeby rozmawiać z GPT, Claude, Gemini i DeepSeekiem. Wystarczy oficjalne SDK OpenAI ustawione na https://kadraris.com/v1 - poradzi sobie z każdym czatowym modelem z oferty, a zmiana modelu to podmiana napisu, np. gpt-6-sol na claude-sonnet-5 czy deepseek-v4-flash. Płacisz z jednego konta w złotych i dostajesz polską fakturę.

W tekście: gotowe skrypty Python i Node, wybór modelu przez konfigurację, równoległy test kilku modeli ze stoperem i wyceną, zapasowy model na wypadek awarii oraz różnice między modelami, które wychodzą dopiero w praktyce.

Po co upraszczać

OpenAI, Anthropic, Google i DeepSeek mają własne biblioteki, własną strukturę wiadomości, własne błędy i własne rozliczenia w dolarach. Integracja z każdym osobno oznacza mnożenie kodu, sekretów i dokumentów dla księgowej.

Kadraris API udostępnia /v1/chat/completions zgodne z OpenAI, które przyjmie dowolny model czatu, oraz /v1/messages zgodne z Anthropic, przeznaczone dla Claude. W kodzie model staje się zwykłą wartością w konfiguracji, w księgowości - jednym saldem w złotych i krajową fakturą.

Przykładowy modelIdentyfikatorZa 1 mln tokenów (wejście / wyjście)
GPT-6 Solgpt-6-sol11,73 zł / 58,65 zł za 1 mln tokenów
Claude Sonnet 5claude-sonnet-517,60 zł / 87,98 zł za 1 mln tokenów
Gemini 3.6 Flashgemini-3.6-flash4,29 zł / 21,45 zł za 1 mln tokenów
DeepSeek V4 Flashdeepseek-v4-flash0,733 zł / 1,47 zł za 1 mln tokenów
GPT-6 Lunagpt-6-luna0,587 zł / 2,93 zł za 1 mln tokenów

Komplet, łącznie z GLM, Kimi i MiniMax, znajdziesz w katalogu modeli i pod GET /v1/models?category=chat.

Przygotowanie

  1. Zarejestruj się (rejestracja) i zasil konto, choćby minimalnymi 40 zł przez BLIK.
  2. Wygeneruj klucz w ustawieniach konta i od razu nadaj mu miesięczny pułap wydatków. Klucz sk-kd-... zobaczysz tylko raz.
  3. Trzymaj go w zmiennej środowiskowej, nie w repozytorium.
Tworzenie klucza API z polem miesięcznego limitu w złotych
Jeden klucz na wszystkie modele, a limit w złotych zatrzyma zapętlony skrypt
bash
pip install openai requests
export KADRARIS_API_KEY=sk-kd-...
export LLM_MODEL=gpt-6-sol

W PowerShellu: $env:KADRARIS_API_KEY = "sk-kd-...".

Model jako zmienna

Najprostszy wariant: model czytasz ze zmiennej środowiskowej i przełączasz go bez dotykania kodu.

python
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://kadraris.com/v1",
    api_key=os.environ["KADRARIS_API_KEY"],
)
MODEL = os.environ.get("LLM_MODEL", "gpt-6-sol")


def ask(prompt, model=MODEL):
    resp = client.chat.completions.create(
        model=model,
        messages=[
            {"role": "system",
             "content": "Piszesz po polsku, bez lania wody."},
            {"role": "user", "content": prompt},
        ],
    )
    return resp.choices[0].message.content


q = "Kiedy w SQL użyć LEFT JOIN zamiast INNER JOIN?"
print(ask(q))
print(ask(q, model="claude-sonnet-5"))

Wystarczy LLM_MODEL=deepseek-v4-flash python app.py, żeby identyczny skrypt odpowiadał DeepSeekiem. Strumieniowanie (stream=True) i wywołania funkcji (tools) wyglądają tak samo bez względu na model - szczegóły w dokumentacji czatu.

Test kilku modeli z kosztem w złotych

Zanim wybiorę model do projektu, wysyłam te same pytania do kilku modeli jednocześnie i oceniam jakość, czas i koszt. Cennik ściągam z otwartego GET /v1/models (kwoty brutto w złotych), liczbę tokenów odczytuję z usage.

python
import os
import time
from concurrent.futures import ThreadPoolExecutor

import requests
from openai import OpenAI

BASE = "https://kadraris.com/v1"
client = OpenAI(base_url=BASE,
                api_key=os.environ["KADRARIS_API_KEY"])

catalog = requests.get(
    f"{BASE}/models",
    params={"category": "chat", "currency": "PLN"},
    timeout=30).json()["data"]
PRICES = {m["id"]: m["pricing"] for m in catalog}

MODELS = ["gpt-6-sol", "claude-sonnet-5",
          "gemini-3.6-flash", "deepseek-v4-flash"]
PROMPT = ("Klientka pyta, czy może zwrócić buty noszone "
          "raz. Odpisz grzecznie w 3 zdaniach.")


def run(model):
    start = time.perf_counter()
    r = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": PROMPT}])
    u, p = r.usage, PRICES[model]
    cost = (u.prompt_tokens * float(p["input_per_mtok"])
            + u.completion_tokens
            * float(p["output_per_mtok"])) / 1_000_000
    took = time.perf_counter() - start
    return (model, took, u.prompt_tokens,
            u.completion_tokens, cost,
            r.choices[0].message.content)


with ThreadPoolExecutor(max_workers=len(MODELS)) as pool:
    results = list(pool.map(run, MODELS))

# od najtańszej odpowiedzi
for model, sec, tin, tout, cost, text in sorted(
        results, key=lambda x: x[4]):
    print(f"\n--- {model} | {sec:.1f} s | "
          f"{tin}+{tout} tok. | {cost:.4f} zł")
    print(text)

Co warto wiedzieć, czytając wynik:

  • pojedyncze zapytanie to anegdota; wiążącą decyzję podejmuję po 20-50 realnych przypadkach,
  • u modeli rozumujących tokeny wyjścia obejmują też myślenie, więc krótka odpowiedź może mieć ich sporo,
  • tokenizery się różnią, więc identyczne zdanie kosztuje każdy model inną liczbę tokenów - liczy się cena odpowiedzi, nie stawka z tabeli.

Ranking stawek na przykładzie 1000 odpowiedzi czatu pokazuję w poradniku najtańsze API LLM.

Plan B, gdy model milczy

Awarie u producentów się zdarzają. Niektóre modele w Kadrarisie same przechodzą na innego dostawcę infrastruktury, ale własny mechanizm w aplikacji to dodatkowe zabezpieczenie: lista modeli zapasowych. Uważaj tylko, by nie skakać po modelach, gdy problem leży po twojej stronie.

python
import time
from openai import APIStatusError, APIConnectionError

CHAIN = ["claude-sonnet-5", "gpt-6-sol",
         "gemini-3.6-flash"]
# klucz, saldo, limit klucza, złe parametry
NO_RETRY = {401, 402, 403, 422}


def ask_with_fallback(messages, **kw):
    last = None
    for model in CHAIN:
        for attempt in range(2):
            try:
                r = client.chat.completions.create(
                    model=model, messages=messages, **kw)
                return model, r.choices[0].message.content
            except APIStatusError as e:
                if e.status_code in NO_RETRY:
                    raise
                last = e
                if e.status_code == 404:
                    break  # brak modelu: od razu kolejny
                # 429 i 5xx: krótka pauza, potem ponowienie
                time.sleep(2 * (attempt + 1))
            except APIConnectionError as e:
                last = e
                time.sleep(2 * (attempt + 1))
    raise last


model, answer = ask_with_fallback(
    [{"role": "user",
      "content": "Wyjaśnij w 5 punktach, czym jest KSeF."}])
print(f"[{model}] {answer}")

Jak traktować poszczególne kody:

KodCo się stałoZmienić model?
401zły lub usunięty klucznie
402za mało środkównie, doładuj
403klucz wyczerpał limit miesięcznynie, podnieś limit
404nie ma takiego modelutak, i popraw identyfikator
422coś nie tak z polami zapytanianie, napraw treść
429za dużo zapytań naraztak, odczekaj
502awaria u dostawcy modelutak

Za nieudane wywołanie nic nie płacisz, więc kolejna próba nie dubluje kosztów. Wszystkie kody opisuje dokumentacja błędów.

Wersja dla Node.js

bash
npm install openai
javascript
import OpenAI from "openai";

const BASE = "https://kadraris.com/v1";
const client = new OpenAI({
  baseURL: BASE,
  apiKey: process.env.KADRARIS_API_KEY,
});

const res = await fetch(
  `${BASE}/models?category=chat&currency=PLN`);
const prices = Object.fromEntries(
  (await res.json()).data.map((m) => [m.id, m.pricing]));

const models = ["gpt-6-luna", "claude-haiku-4.5",
                "deepseek-v4-flash"];
const prompt = "Wymyśl 3 nazwy dla warsztatu rowerowego.";

const results = await Promise.allSettled(
  models.map(async (model) => {
    const r = await client.chat.completions.create({
      model,
      messages: [{ role: "user", content: prompt }],
    });
    const p = prices[model];
    const cost = (r.usage.prompt_tokens
        * Number(p.input_per_mtok)
      + r.usage.completion_tokens
        * Number(p.output_per_mtok)) / 1e6;
    return { model, cost: cost.toFixed(4) + " zł",
             text: r.choices[0].message.content };
  }),
);

for (const r of results) {
  console.log(r.status === "fulfilled"
    ? r.value : r.reason.message);
}

Dzięki Promise.allSettled awaria jednego modelu nie przerywa testu pozostałych. Zwróć uwagę na pisownię: biblioteka JavaScript oczekuje baseURL, pythonowa - base_url.

Kod pod SDK Anthropic zostaw w spokoju

Jeśli twój projekt korzysta już z biblioteki Anthropic (np. bloki tool_use, cache promptów), zostaw go tak, jak jest - /v1/messages przyjmuje natywny format Claude:

python
import os
from anthropic import Anthropic

claude = Anthropic(base_url="https://kadraris.com",
                   api_key=os.environ["KADRARIS_API_KEY"])
msg = claude.messages.create(
    model="claude-opus-5.5",
    max_tokens=1024,
    messages=[{"role": "user",
               "content": "Zaprojektuj tabele dla "
                          "wypożyczalni hulajnóg."}],
)
print(msg.content[-1].text)

W tym kliencie bazowy adres jest bez /v1, a obsługiwane są wyłącznie modele Claude. Jeśli łączysz w programie modele kilku producentów, wygodniej trzymać się formatu OpenAI. Koszty i cache Claude rozpisałem w poradniku Claude Opus 5.5 przez API - cena.

Na czym potykam się przy zmianie modelu

Wspólny format nie oznacza identycznego zachowania:

  • temperature i top_p. Własne wartości tych pól kończą się w Claude Sonnet 5 kodem 400, a GPT-6 Astra (według dokumentacji OpenAI) też ich nie akceptuje. Kod uniwersalny po prostu ich nie wysyła.
  • max_tokens w modelach, które myślą. Rozumowanie wlicza się do tego limitu, więc zbyt mała wartość daje odpowiedź obciętą lub pustą. Lepiej zostawić pole puste albo dać hojny zapas.
  • Rozmiar okna i odpowiedzi. Gemini 3.6 Flash odpowie najwyżej 8192 tokenami, a Claude Haiku 4.5 zmieści 200 tys. tokenów kontekstu, nie milion. Wartości dla każdego modelu podaje GET /v1/models w context_window i max_output_tokens.
  • Styl. Identyczne instrukcje systemowe dają u różnych modeli inne tempo, długość i ton. Nowy model najpierw testuję na kilkunastu rozmowach, dopiero potem wpuszczam do użytkowników.
Szybki start w dokumentacji z adresem bazowym i pierwszym zapytaniem
Adres bazowy, klucz i pierwsze zapytanie do modelu czatu

Nie programujesz? Klucz podepniesz też w n8n, Make czy Zapierze z adresem https://kadraris.com/v1; kroki opisuje dokumentacja no-code. Stawki rodziny GPT-6 rozpisałem w poradniku GPT-6 API - cena.

Pytania

Czy przez SDK OpenAI zadziała też Claude?

Tak. Ustaw base_url="https://kadraris.com/v1", klucz sk-kd-... i model claude-sonnet-5. Wszystko idzie w formacie Chat Completions, więc strumieniowanie i funkcje działają jak przy GPT.

Kiedy fallback powinien od razu przerwać, zamiast próbować dalej?

Przy 401, 402, 403 i 422 - wtedy zawodzi klucz, brakuje środków, wyczerpał się pułap albo zapytanie jest źle zbudowane, a zmiana modelu nic nie da. Przełączaj się przy 429, 5xx i błędach połączenia.

Skąd skrypt bierze ceny do wyliczenia kosztu?

Z publicznego GET /v1/models?category=chat&currency=PLN, który zwraca stawki w złotych z VAT. Zużycie tokenów pochodzi z pola usage każdej odpowiedzi.

Czy jeden klucz obsłuży też modele wideo i obrazu?

Tak. Klucz i saldo działają we wszystkich modelach z katalogu - czacie, obrazach, wideo i muzyce. Fakturę VAT zaznaczasz przy doładowaniu.