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
| Ustawienie | Wartość w SpicyAPI |
|---|---|
| Adres bazowy | https://api.spicyapi.ai/v1 |
| Klucz API | Klucz SpicyAPI (zaczyna się od sk-spicy-), przesyłany jako Authorization: Bearer |
| Model | Dokł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=textwymienia wyłącznie modele tekstowe z cenami i dodajeinputSchemakażdego modelu przyincludeSchema=1. Zobacz Katalog modeli i schematy.GET /v1/modelsto 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 mamodalityitask, a modele czatu równieżcontext_length,max_output_tokensisupported_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_penaltyirepetition_penaltyzmieniają 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 dotyczyn: 1, pustegostop,stopw modelu bez sekwencji zatrzymania ireasoning_effortw modelu bez poziomów rozumowania. Odpowiedź wskazuje zignorowane pola w nagłówkuX-Spicy-Ignored-Params, na przykładfrequency_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
stopdłuższa od dopuszczalnej zachowuje pierwsze pozycje, a nieoferowany poziomreasoning_effortstaje się najbliższym dostępnym (niższym przy remisie). Odpowiedź wymienia każdą zmianę jakoname=valuew nagłówkuX-Spicy-Adjusted-Params, na przykładreasoning_effort=low, temperature=1. - Każde inne pole niewymienione przez model jest odrzucane z
400,code: "unsupported_parameter",paramustawionym na to pole i komunikatem z jego nazwą, na przykładlogprobs: 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.
| Pole | W SpicyAPI |
|---|---|
messages | Ta 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_tokens | Oba akceptowane, z tym samym znaczeniem. Tokeny rozumowania wliczają się do limitu. Każdy model ma własną wartość domyślną i maksimum |
stream, stream_options | Obsł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_choice | Obsł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_calls | Tylko w modelach, które wymieniają to pole |
reasoning_effort | W 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_penalty | Uż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 |
seed | Tylko w modelach, które wymieniają to pole |
stop | Używane w modelach, które je wymieniają, przycinane do maksymalnej liczby pozycji; ignorowane w modelach bez sekwencji zatrzymania oraz gdy jest puste |
response_format | json_object w modelach, które wymieniają pole; niedostępne w pozostałych |
user, metadata, store | Akceptowane i ignorowane |
n | 1 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 model | Odrzucane 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 wmessage.reasoning_content(lubdelta.reasoning_contentpodczas strumieniowania), obokcontent. Jest już wliczony wusage.completion_tokens.- Identyfikator jest identyfikatorem żądania.
idma postaćchatcmpl-req_..., a nagłówek odpowiedzix-request-idzawiera 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_fingerprinti braklogprobs.
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 zawierausage, a każdy ma niepustą listęchoices. Z"include_usage": truedodatkowy fragment z pustą listąchoicesi ostatecznymusagedociera tuż przed[DONE], a wszystkie pozostałe mająusage: null. Kod napisany według przykładów strumieniowania OpenAI działa bez zmian; sprawdzaniechoicesprzed odczytaniemchoices[0]chroni go przy ostatnim fragmencie. - Jedno
finish_reasonna wybór. Każdy wybór kończy się dokładnie jednym fragmentem zawierającym jegofinish_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 OpenRouter | Co zrobić |
|---|---|
models (lista alternatyw) i route | Usuń. Wyślij jeden identyfikator modelu; SpicyAPI wybiera sposób jego obsługi |
provider | Usuń. 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, plugins | Usuń |
Nagłówki HTTP-Referer i X-Title są nieszkodliwe i ignorowane.
Lista kontrolna
- Adres bazowy
https://api.spicyapi.ai/v1i klucz SpicyAPI. - Identyfikator modelu skopiowany z katalogu na żywo, kończący się na
/chat. - Pola żądania sprawdzone względem
inputSchemamodelu; nieobsługiwane pola inne niż próbkowanie usunięte, a nagłówki odpowiedziX-Spicy-Ignored-ParamsiX-Spicy-Adjusted-Paramssprawdzone raz, aby ustalić zmiany. - Jeśli wysyłasz
stream_options.include_usage, pętle strumienia pomijają końcowy fragment z pustą listąchoices. max_tokensustawione realistycznie lub pozostawione z wartością domyślną modelu, z limitami wydatków klucza.- 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
Przykłady czatu
Kompletne działające programy do rozmów wieloturowych, roleplay z postacią, strumieniowania do własnego interfejsu i wywoływania narzędzi w Python, Node.js i curl.
Samouczek: z obrazu do wideo
Zacznij od jednego obrazu na swoim komputerze, a skończ na pobranym pliku wideo. Każdy krok pokazuje CLI, SDK dla TypeScriptu i zwykłe HTTP.

