spicyapiDokumentacja
Tekst i czat
Treść główna

Migracja z OpenAI lub OpenRouter

Skieruj istniejący kod bibliotek OpenAI do SpicyAPI. Zmiany adresu bazowego, klucza i modelu, różnice pól żądania oraz pola usuwane przy migracji z OpenRouter.

Jeśli twój kod już wywołuje model czatu przez oficjalne biblioteki OpenAI lub dowolne narzędzie z ustawieniem „OpenAI-compatible”, migracja do SpicyAPI wymaga trzech zmian: adresu bazowego, klucza API i identyfikatora modelu. Ta strona wymienia te zmiany, a następnie wszystkie różnice działania, aby migracja nie zakończyła się niespodziewanym 400. Pełne programy przykładowe znajdziesz w Przykładach czatu.

Trzy zmiany

UstawienieWartość w SpicyAPI
Adres bazowyhttps://api.spicyapi.ai/v1
Klucz APIKlucz SpicyAPI (zaczyna się od sk-spicy-), przesyłany jako Authorization: Bearer
ModelDokładny identyfikator modelu tekstowego z katalogu na żywo, na przykład w SPICY_MODEL (zobacz Identyfikatory modeli)
import os
from openai import OpenAI

# Before: client = OpenAI()   (reads OPENAI_API_KEY, calls api.openai.com)
client = OpenAI(api_key=os.environ["SPICY_API_KEY"], base_url="https://api.spicyapi.ai/v1")

reply = client.chat.completions.create(
    model=os.environ["SPICY_MODEL"],
    messages=[{"role": "user", "content": "Say hello in one sentence."}],
)
print(reply.choices[0].message.content)

Biblioteki OpenAI odczytują też OPENAI_BASE_URL i OPENAI_API_KEY ze środowiska. Ustawienie tych dwóch wartości pozwala uruchomić istniejący kod bez zmian, ale przekierowuje również każde inne wywołanie OpenAI w tym samym procesie, więc lepiej przekazywać wartości jawnie jak powyżej.

Identyfikatory modeli

Identyfikatory modeli tekstowych mają postać publisher/model/chat. Kopiuj je z katalogu na żywo zamiast przekształcać znaną z innego miejsca nazwę:

  • GET /api/v1/models?modality=text wymienia wyłącznie modele tekstowe z cenami i dodaje inputSchema każdego modelu przy includeSchema=1. Zobacz Katalog modeli i schematy.
  • GET /v1/models to lista w formacie OpenAI odczytywana przez narzędzia. Bez filtra obejmuje modele obrazów, wideo i audio, których nie można wywołać przez /v1/chat/completions; dodaj ?modality=text, aby otrzymać tylko tekstowe. Poza czterema polami OpenAI każda pozycja ma modality i task, a modele czatu również context_length, max_output_tokens i supported_parameters (pola żądania akceptowane przez model). Biblioteki OpenAI ignorują dodatkowe pola.

Nieistniejący identyfikator modelu zwraca 404 z code: "model_not_found" i param: "model"; komunikat pokazuje format identyfikatora i, jeśli taki istnieje, najbliższy model czatu dostępny do wywołania. Wywołanie modelu obrazów lub wideo przez endpoint czatu zwraca 400 z informacją, że nie jest to model czatu. Identyfikatory innych usług często wyglądają podobnie, ale nie są wymienne: nazwy wydawców mają inny zapis, przyrostki takie jak :free tu nie istnieją, a modele tekstowe kończą się na /chat.

Pola żądania

Każdy model publikuje akceptowane pola w inputSchema (oraz ich nazwy w supported_parameters na /v1/models).

  • Pola próbkowania niewymienione przez model są ignorowane. temperature, top_p, top_k, min_p, top_a, frequency_penalty, presence_penalty i repetition_penalty zmieniają tylko losowość lub powtarzalność odpowiedzi. Wiele klientów wysyła je domyślnie, więc model, który ich nie obsługuje, po prostu generuje bez nich. To samo dotyczy n: 1, pustego stop, stop w modelu bez sekwencji zatrzymania i reasoning_effort w modelu bez poziomów rozumowania. Odpowiedź wskazuje zignorowane pola w nagłówku X-Spicy-Ignored-Params, na przykład frequency_penalty, presence_penalty.
  • Wartości poza obsługiwanym zakresem są zmieniane na najbliższe akceptowane. Wartość próbkowania poza zakresem modelu jest ograniczana do jego granicy, lista stop dłuższa od dopuszczalnej zachowuje pierwsze pozycje, a nieoferowany poziom reasoning_effort staje się najbliższym dostępnym (niższym przy remisie). Odpowiedź wymienia każdą zmianę jako name=value w nagłówku X-Spicy-Adjusted-Params, na przykład reasoning_effort=low, temperature=1.
  • Każde inne pole niewymienione przez model jest odrzucane z 400, code: "unsupported_parameter", param ustawionym na to pole i komunikatem z jego nazwą, na przykład logprobs: is not a supported field. Te pola zmieniają to, co otrzymujesz, więc nigdy nie płacisz za odpowiedź wygenerowaną bez funkcji, którą w twoim przekonaniu przesłano.
PoleW SpicyAPI
messagesTa sama struktura. Role system, developer, user, assistant i tool. Tekst działa wszędzie; części image_url tylko w modelach, których schemat je wymienia
max_tokens, max_completion_tokensOba akceptowane, z tym samym znaczeniem. Tokeny rozumowania wliczają się do limitu. Każdy model ma własną wartość domyślną i maksimum
stream, stream_optionsObsługiwane zgodnie z zasadami użycia OpenAI: strumień zawiera użycie tylko wtedy, gdy include_usage ma wartość true (zobacz Odpowiedzi i strumienie)
tools, tool_choiceObsługiwane w każdym modelu tekstowym. Niektóre przyjmują tylko auto i none dla tool_choice, więc wymuszenie nazwanego narzędzia zwraca w nich 400
parallel_tool_callsTylko w modelach, które wymieniają to pole
reasoning_effortW większości modeli, ale dozwolone wartości różnią się między modelami: niektóre dodają none, minimal, xhigh lub max, a niektóre nie mają medium. Nieoferowana wartość jest zmieniana na najbliższą dostępną, więc odczytaj enum modelu, jeśli dokładny poziom ma znaczenie
temperature, top_p, top_k, frequency_penalty, presence_penaltyUżywane w modelach, które je wymieniają, ograniczane do zakresu modelu, a w pozostałych ignorowane (wskazane w X-Spicy-Ignored-Params). Kilka modeli nie przyjmuje żadnych pól próbkowania
seedTylko w modelach, które wymieniają to pole
stopUżywane w modelach, które je wymieniają, przycinane do maksymalnej liczby pozycji; ignorowane w modelach bez sekwencji zatrzymania oraz gdy jest puste
response_formatjson_object w modelach, które wymieniają pole; niedostępne w pozostałych
user, metadata, storeAkceptowane i ignorowane
n1 jest ignorowane; inne wartości są odrzucane z 400 w modelach, które nie wymieniają pola
logprobs, top_logprobs, logit_bias i każde inne pole niewymienione przez modelOdrzucane z 400

Aby zobaczyć pola modelu i ich dozwolone wartości przed wysłaniem żądania, uruchom polecenie z Przykładów czatu: zanim zaczniesz.

Odpowiedzi i strumienie

Odpowiedź bez strumieniowania jest zwykłym obiektem chat.completion, więc reply.choices[0].message.content i reply.usage działają jak wcześniej. Różnice są niewielkie:

  • reasoning_content. Modele rozumujące przed odpowiedzią zwracają ten tekst w message.reasoning_content (lub delta.reasoning_content podczas strumieniowania), obok content. Jest już wliczony w usage.completion_tokens.
  • Identyfikator jest identyfikatorem żądania. id ma postać chatcmpl-req_..., a nagłówek odpowiedzi x-request-id zawiera tę samą wartość req_.... Podawaj ją przy kontakcie ze wsparciem. Drugi nagłówek, X-Spicy-Task-Id, wskazuje zadanie zawierające opłatę (zobacz Koszt i użycie).
  • Brak system_fingerprint i brak logprobs.

Strumienie używają tych samych zdarzeń chat.completion.chunk, kończą się data: [DONE] i przestrzegają zasad użycia OpenAI:

  • Użycie tylko na żądanie. Bez stream_options.include_usage żaden fragment nie zawiera usage, a każdy ma niepustą listę choices. Z "include_usage": true dodatkowy fragment z pustą listą choices i ostatecznym usage dociera tuż przed [DONE], a wszystkie pozostałe mają usage: null. Kod napisany według przykładów strumieniowania OpenAI działa bez zmian; sprawdzanie choices przed odczytaniem choices[0] chroni go przy ostatnim fragmencie.
  • Jedno finish_reason na wybór. Każdy wybór kończy się dokładnie jednym fragmentem zawierającym jego finish_reason, niezależnie od wywołanego modelu.
  • Wiersze podtrzymujące połączenie. Gdy model nadal rozumuje, strumień zawiera komentarze : keep-alive, aby połączenie pozostało otwarte. Biblioteki OpenAI je pomijają; własny parser też musi to robić.

Dla długich odpowiedzi lub wysokiego reasoning_effort używaj strumieniowania. Żądanie bez strumieniowania trwające ponad około 30 sekund jest podtrzymywane białymi znakami przed treścią JSON i nagłówkiem X-Spicy-Keepalive: 1, więc biblioteki OpenAI nadal je parsują; jednak przy niepowodzeniu po tym momencie obiekt błędu dociera w treści odpowiedzi 200, której biblioteki nie zgłaszają jako błędu. Zobacz Czas oczekiwania i strumieniowanie.

Błędy

Błędy używają struktury OpenAI {"error": {"message", "type", "param", "code"}} ze zwykłymi statusami HTTP, więc biblioteki OpenAI zgłaszają standardowe wyjątki. param nazywa pole, którego dotyczy błąd, jako ścieżkę z kropkami, na przykład temperature lub messages.1.content.0, a code rozróżnia błędy z tym samym statusem: unsupported_parameter (pole lub część wiadomości nieobsługiwana przez model), invalid_request (brak pola lub nieakceptowana wartość), content_rejected (odmowa według zasad treści, również 400) i model_not_found (404). Jedna różnica względem OpenAI: 402 jest używane, gdy saldo lub limit wydatków nie pokrywa żądania. Pełna tabela wraz z działaniem dla każdego kodu znajduje się w Przykładach czatu: gdy coś się nie powiedzie.

Biblioteki OpenAI samodzielnie ponawiają odpowiedzi 429 i 5xx. Tutaj jest to bezpieczne: nieudane żądanie nie jest płatne. Jeśli wysyłasz Idempotency-Key, przekazuj go dla każdego wywołania osobno i najpierw przeczytaj Klucze idempotencji w endpointach tekstowych.

Koszt i użycie

Odpowiedzi raportują liczbę tokenów w usage, a nie cenę. Ceny za milion tokenów wejściowych, wejściowych z pamięci podręcznej i wyjściowych są podane na stronie każdego modelu i w katalogu. Aby ustalić rzeczywisty koszt wywołania, odczytaj nagłówek odpowiedzi X-Spicy-Task-Id (job_…), obecny w każdym zaakceptowanym żądaniu, strumieniowym i zwykłym, i odczytaj zadanie przez GET /api/v1/jobs/recordInfo?taskId=<job_…>: cost jest ostateczne, gdy settled ma wartość true. Pozycje w GET /api/v1/jobs zawierają też requestId wywołania, które je utworzyło (nagłówek X-Request-Id), a Konsola → Logi pokazuje te same zadania. Odczyt nagłówka przez biblioteki OpenAI pokazują Przykłady czatu: sprawdź koszt wywołania; zobacz też Wyceny i rozliczenia.

Przed wykonaniem żądania z salda blokowana jest kwota pokrywająca twoje wiadomości oraz pełne max_tokens wyniku (wartość domyślną modelu, jeśli pominiesz pole). Płacisz tylko za zużycie, ale najpierw trzeba pokryć blokadę, więc małe saldo połączone z dużym max_tokens zwraca 402. Ustaw realistyczne max_tokens lub limity wydatków klucza; zobacz Rozliczenia.

Endpointy, których tu nie znajdziesz

Generowanie tekstu jest dostępne jako OpenAI Chat Completions, OpenAI Responses, Anthropic Messages i Google Gemini; szczegóły opisuje Tekst i strumieniowanie. Następujące endpointy OpenAI nie istnieją i zwracają 404: /v1/embeddings, starsze /v1/completions, /v1/images/*, /v1/audio/*, /v1/moderations, /v1/files, /v1/batches, /v1/assistants oraz dostrajanie fine-tuning. Modele obrazów, wideo i audio wywołuje się zamiast tego przez API zadań, a wideo także przez /v1/videos.

Migracja z OpenRouter

OpenRouter też używa formatu OpenAI, więc obowiązują trzy powyższe zmiany: zastąp adres bazowy https://openrouter.ai/api/v1 adresem https://api.spicyapi.ai/v1 oraz zmień klucz i identyfikatory modeli. Następnie usuń pola istniejące tylko w OpenRouter; tutaj zwracają 400:

Pole OpenRouterCo zrobić
models (lista alternatyw) i routeUsuń. Wyślij jeden identyfikator modelu; SpicyAPI wybiera sposób jego obsługi
providerUsuń. Nie ma wyboru dostawcy w pojedynczym żądaniu
reasoning (obiekt taki jak {"effort": "high"})Jest przekształcany w główne reasoning_effort (enabled: false staje się none, jeśli model je obsługuje); pozostałe klucze wewnątrz są ignorowane. Jawne wysłanie reasoning_effort jest czytelniejsze
usage ({"include": true})Usuń. Odpowiedzi bez strumieniowania zawsze zawierają usage; dla strumieni wyślij stream_options: {"include_usage": true}
transforms, pluginsUsuń

Nagłówki HTTP-Referer i X-Title są nieszkodliwe i ignorowane.

Lista kontrolna

  1. Adres bazowy https://api.spicyapi.ai/v1 i klucz SpicyAPI.
  2. Identyfikator modelu skopiowany z katalogu na żywo, kończący się na /chat.
  3. Pola żądania sprawdzone względem inputSchema modelu; nieobsługiwane pola inne niż próbkowanie usunięte, a nagłówki odpowiedzi X-Spicy-Ignored-Params i X-Spicy-Adjusted-Params sprawdzone raz, aby ustalić zmiany.
  4. Jeśli wysyłasz stream_options.include_usage, pętle strumienia pomijają końcowy fragment z pustą listą choices.
  5. max_tokens ustawione realistycznie lub pozostawione z wartością domyślną modelu, z limitami wydatków klucza.
  6. Długie żądania lub te z dużym poziomem rozumowania używają stream: true.

Czytaj dalej

Przykłady czatu · Tekst i strumieniowanie · Katalog modeli i schematy · Uwierzytelnianie · Rozliczenia

Na tej stronie