spicyapiDocumentation
Texte et chat
Contenu principal

Migrer depuis OpenAI ou OpenRouter

Adaptez du code OpenAI à SpicyAPI : URL de base, clé et ID de modèle, différences de paramètres et champs OpenRouter à retirer.

Si votre code appelle déjà un modèle de chat par les bibliothèques OpenAI officielles ou un outil proposant un réglage « compatible OpenAI », le passage à SpicyAPI demande trois modifications : l’URL de base, la clé API et l’ID du modèle. Cette page les détaille, puis explique les différences de comportement pour éviter un 400 inattendu à la migration. Pour des programmes d’exemple complets, voir Recettes de chat.

Les trois modifications

RéglageValeur sur SpicyAPI
URL de basehttps://api.spicyapi.ai/v1
Clé APIClé SpicyAPI commençant par sk-spicy-, envoyée comme Authorization: Bearer
ModèleID exact d’un modèle de texte du catalogue actuel, par exemple dans SPICY_MODEL ; voir IDs des modèles
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)

Les bibliothèques OpenAI lisent aussi OPENAI_BASE_URL et OPENAI_API_KEY dans l’environnement. Les définir permet d’exécuter le code sans modification, mais redirige aussi tous les autres appels OpenAI du même processus : préférez donc passer explicitement les valeurs comme ci-dessus.

IDs des modèles

Les IDs de modèles de texte ont la forme publisher/model/chat. Copiez-les depuis le catalogue actuel plutôt que d’adapter un nom connu d’un autre service :

  • GET /api/v1/models?modality=text liste uniquement les modèles de texte avec leurs tarifs et ajoute leur inputSchema avec includeSchema=1. Voir Catalogue des modèles et schémas.
  • GET /v1/models est la liste au format OpenAI lue par les outils. Sans filtre, elle inclut des modèles d’image, de vidéo et d’audio non appelables sur /v1/chat/completions ; ajoutez ?modality=text pour le texte seul. Outre les quatre champs OpenAI, chaque entrée a modality et task. Les modèles de chat ont aussi context_length, max_output_tokens et supported_parameters, les champs de requête acceptés. Les bibliothèques OpenAI ignorent ces champs supplémentaires.

Un ID inexistant renvoie 404 avec code: "model_not_found" et param: "model" ; le message indique la forme de l’ID et, s’il en existe un, le modèle de chat appelable le plus proche. Appeler un modèle d’image ou de vidéo sur un point de terminaison de chat renvoie 400, indiquant que ce n’est pas un modèle de chat. Les IDs d’autres services peuvent ressembler aux nôtres sans être interchangeables : les noms d’éditeurs diffèrent, les suffixes comme :free n’existent pas ici, et les modèles de texte finissent par /chat.

Champs de requête

Chaque modèle publie les champs acceptés dans son inputSchema, et leurs noms dans supported_parameters sur /v1/models.

  • Les champs d’échantillonnage non listés par le modèle sont ignorés. temperature, top_p, top_k, min_p, top_a, frequency_penalty, presence_penalty et repetition_penalty changent seulement le caractère aléatoire ou répétitif de la réponse. Beaucoup de clients les envoient par défaut : un modèle qui ne les prend pas en charge génère simplement sans eux. Cela vaut aussi pour n: 1, un stop vide, stop sur un modèle sans séquences d’arrêt et reasoning_effort sur un modèle sans niveaux de raisonnement. La réponse indique les champs ignorés dans X-Spicy-Ignored-Params, par exemple frequency_penalty, presence_penalty.
  • Les valeurs hors des possibilités du modèle sont ramenées à la valeur acceptée la plus proche. Une valeur d’échantillonnage hors plage est bornée, une liste stop trop longue garde ses premières entrées, et un niveau reasoning_effort absent est remplacé par le plus proche, le plus bas en cas d’égalité. La réponse liste chaque changement en name=value dans X-Spicy-Adjusted-Params, par exemple reasoning_effort=low, temperature=1.
  • Tout autre champ non listé est refusé avec 400, code: "unsupported_parameter", un param désignant le champ et un message le nommant, par exemple logprobs: is not a supported field. Ces champs changent ce que vous obtenez : vous ne payez donc jamais une réponse produite sans une option que vous pensiez avoir envoyée.
ChampSur SpicyAPI
messagesMême forme. Rôles system, developer, user, assistant et tool. Le texte fonctionne partout ; les parties image_url uniquement sur les modèles dont le schéma les liste
max_tokens, max_completion_tokensTous deux acceptés, même sens. Les tokens de raisonnement y sont inclus. Chaque modèle a sa valeur par défaut et son plafond
stream, stream_optionsPris en charge, selon les règles d’utilisation d’OpenAI : un flux contient l’utilisation seulement si include_usage est true ; voir Réponses et flux
tools, tool_choicePris en charge sur tous les modèles de texte. Certains n’acceptent que auto et none pour tool_choice : imposer un outil nommé y renvoie 400
parallel_tool_callsUniquement sur les modèles qui le listent
reasoning_effortSur la plupart des modèles, mais les valeurs autorisées diffèrent : certains ajoutent none, minimal, xhigh ou max, d’autres n’ont pas medium. Une valeur absente est remplacée par la plus proche : lisez l’enum du modèle si le niveau exact compte
temperature, top_p, top_k, frequency_penalty, presence_penaltyUtilisés sur les modèles qui les listent, bornés à leur plage et ignorés ailleurs, avec mention dans X-Spicy-Ignored-Params. Plusieurs modèles n’acceptent aucun champ d’échantillonnage
seedUniquement sur les modèles qui le listent
stopUtilisé sur les modèles qui le listent, tronqué au nombre maximal accepté ; ignoré sans séquences d’arrêt ou lorsqu’il est vide
response_formatjson_object sur les modèles qui listent le champ ; indisponible ailleurs
user, metadata, storeAcceptés et ignorés
n1 est ignoré ; toute autre valeur est refusée avec 400 sur les modèles qui ne le listent pas
logprobs, top_logprobs, logit_bias et tout autre champ non listéRefusés avec 400

Pour voir les champs et valeurs autorisés avant tout envoi, exécutez la commande dans Recettes de chat : avant de commencer.

Réponses et flux

Une réponse sans streaming est un objet chat.completion normal : reply.choices[0].message.content et reply.usage fonctionnent comme avant. Les différences sont limitées :

  • reasoning_content. Les modèles qui raisonnent avant de répondre renvoient ce texte dans message.reasoning_content, ou delta.reasoning_content en streaming, à côté de content. Il est déjà compté dans usage.completion_tokens.
  • L’ID est celui de la requête. id a la forme chatcmpl-req_..., et l’en-tête de réponse x-request-id porte la même valeur req_.... Indiquez-la au support. Un second en-tête, X-Spicy-Task-Id, désigne la tâche portant la facture ; voir Coût et utilisation.
  • Aucun system_fingerprint ni logprobs.

Les flux utilisent les mêmes événements chat.completion.chunk, se terminent par data: [DONE] et suivent les règles d’OpenAI pour l’utilisation :

  • Utilisation uniquement sur demande. Sans stream_options.include_usage, aucun fragment ne porte usage et tous ont une liste choices non vide. Avec "include_usage": true, un fragment supplémentaire avec choices vide et l’usage final arrive juste avant [DONE], et tous les autres ont usage: null. Les exemples de streaming OpenAI fonctionnent sans changement ; vérifier choices avant de lire choices[0] sécurise ce dernier fragment.
  • Un finish_reason par choix. Chaque choix se termine par exactement un fragment portant son finish_reason, quel que soit le modèle.
  • Lignes de maintien de connexion. Pendant le raisonnement, le flux comporte des commentaires : keep-alive pour garder la connexion ouverte. Les bibliothèques OpenAI les ignorent ; un parseur maison doit aussi le faire.

Pour les longues réponses ou un reasoning_effort élevé, utilisez le streaming. Une requête sans streaming dépassant environ 30 secondes reste ouverte avec des espaces avant le corps JSON et l’en-tête X-Spicy-Keepalive: 1, que les bibliothèques OpenAI savent analyser. Mais si elle échoue ensuite, l’objet d’erreur arrive dans le corps d’une réponse 200, sans que ces bibliothèques lèvent une erreur. Voir Timeouts et streaming.

Erreurs

Les erreurs utilisent la forme OpenAI {"error": {"message", "type", "param", "code"}} et les statuts HTTP habituels : les bibliothèques OpenAI lèvent leurs exceptions normales. param désigne le champ concerné sous forme de chemin à points, par exemple temperature ou messages.1.content.0. code distingue les erreurs partageant un statut : unsupported_parameter pour un champ ou une partie de message non accepté, invalid_request pour un champ manquant ou une valeur refusée, content_rejected pour un refus de politique de contenu, également 400, et model_not_found avec 404. Une différence avec OpenAI : 402 indique que le solde ou un plafond de dépenses ne couvre pas la requête. Le tableau complet et les actions figurent dans Recettes de chat : en cas de problème.

Les bibliothèques OpenAI réessaient automatiquement les réponses 429 et 5xx. C’est sûr ici : une requête échouée n’est pas facturée. Si vous envoyez un Idempotency-Key, fournissez-le par appel et lisez d’abord Clés d’idempotence sur les API de texte.

Coût et utilisation

Les réponses donnent des nombres de tokens dans usage, pas un tarif. Les tarifs par million de tokens d’entrée, d’entrée en cache et de sortie figurent sur chaque page de modèle et dans le catalogue. Pour connaître le coût réel, lisez l’en-tête X-Spicy-Task-Id (job_…), présent pour toute requête acceptée, avec ou sans streaming, puis retrouvez la tâche par GET /api/v1/jobs/recordInfo?taskId=<job_…> : cost est définitif lorsque settled est true. Les entrées de GET /api/v1/jobs portent aussi le requestId de l’appel de création, correspondant à l’en-tête X-Request-Id, et Console → Journaux affiche les mêmes tâches. La lecture des en-têtes avec les bibliothèques OpenAI est montrée dans Recettes de chat : trouver le coût d’un appel ; voir aussi Devis et facturation.

Avant l’exécution, un montant est réservé sur votre solde pour couvrir les messages et l’intégralité des max_tokens de sortie, ou la valeur par défaut du modèle si vous l’omettez. Vous ne payez que l’utilisation réelle, mais la réservation doit d’abord tenir dans le solde : un faible solde et un grand max_tokens renvoient 402. Fixez un max_tokens réaliste ou des plafonds de dépenses sur la clé ; voir Facturation.

Les points de terminaison absents

La génération de texte est proposée au format OpenAI Chat Completions, OpenAI Responses, Anthropic Messages et Google Gemini ; voir Texte et streaming. Ces API OpenAI n’existent pas et renvoient 404 : /v1/embeddings, l’ancien /v1/completions, /v1/images/*, /v1/audio/*, /v1/moderations, /v1/files, /v1/batches, /v1/assistants et le fine-tuning. Les modèles d’image, de vidéo et d’audio s’appellent par l’API de tâches, et les vidéos aussi par /v1/videos.

Depuis OpenRouter

OpenRouter utilise aussi le format OpenAI ; les trois modifications précédentes s’appliquent : remplacez l’URL de base https://openrouter.ai/api/v1 par https://api.spicyapi.ai/v1, puis la clé et les IDs de modèles. Retirez ensuite les champs propres à OpenRouter, qui renvoient ici 400 :

Champ OpenRouterAction
models (liste de repli) et routeRetirer. Envoyez un seul ID ; SpicyAPI choisit comment l’exécuter
providerRetirer. Il n’y a pas de choix de prestataire par requête
reasoning, objet comme {"effort": "high"}Converti en reasoning_effort à la racine ; enabled: false devient none si le modèle le permet. Les autres clés internes sont ignorées. Envoyer directement reasoning_effort est plus clair
usage ({"include": true})Retirer. Les réponses sans streaming incluent toujours usage ; pour les flux, envoyez stream_options: {"include_usage": true}
transforms, pluginsRetirer

Les en-têtes HTTP-Referer et X-Title sont sans conséquence et ignorés.

Liste de vérification

  1. URL de base https://api.spicyapi.ai/v1 et clé SpicyAPI.
  2. ID de modèle copié du catalogue actuel, finissant par /chat.
  3. Champs vérifiés contre l’inputSchema du modèle ; champs non liés à l’échantillonnage et non pris en charge retirés ; en-têtes X-Spicy-Ignored-Params et X-Spicy-Adjusted-Params examinés une fois pour voir les changements.
  4. Si vous envoyez stream_options.include_usage, les boucles ignorent le dernier fragment dont la liste choices est vide.
  5. max_tokens réaliste ou valeur par défaut du modèle, avec plafonds de dépenses sur la clé.
  6. Les requêtes longues ou à raisonnement élevé utilisent stream: true.

Pour continuer

Recettes de chat · Texte et streaming · Catalogue des modèles et schémas · Authentification · Facturation

Sur cette page