Points clés
- Utilisez ChatOpenAI avec base_url défini sur l'API et model défini sur uncensored.
- Les chaînes, le streaming, l'appel d'outils et la sortie JSON passent tous par la route standard des complétions de chat.
- Limitez la concurrence par lot à 8 sur une clé complète, ou 2 sur une clé d'essai, pour éviter les erreurs 429.
- Il s'agit d'un modèle hébergé unique, pas d'un routeur, donc il n'y a pas de changement de modèle par fournisseur.
Configuration rapide
La question classique langchain + llm est de savoir si vous avez besoin d'une intégration spéciale pour un endpoint tiers. Non. Le wrapper de chat OpenAI de LangChain accepte une URL de base personnalisée, et cette API suit le format des complétions de chat OpenAI, donc la classe standard fonctionne. Installez le package, passez trois valeurs, et vous avez un objet modèle de chat que vous pouvez intégrer dans n'importe quelle chaîne.
pip install -U langchain-openai langchain-core
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
temperature=0.7,
)
print(llm.invoke("Give me one sentence about LangChain.").content)
Obtenez la clé depuis la page clé API. Les nouveaux utilisateurs peuvent réclamer 0,50 $ de crédit d'essai gratuit en un clic après s'être connectés, ce qui est largement suffisant pour exécuter tout ce qui est dans cet article à de nombreuses reprises. L'ID de modèle est toujours uncensored, et les requêtes vont à POST /v1/chat/completions.
Une note sur le nom du site : malgré le mot "router" dans le domaine, il s'agit d'un seul modèle à poids ouverts exécuté par un fournisseur indépendant, pas d'un routeur entre fournisseurs et pas de l'API officielle d'aucun fournisseur. Vous ne choisissez pas entre des modèles ici. Votre code nomme un seul modèle, et c'est celui qui répond.
Gardez la clé hors du code source
Coder en dur une clé fonctionne pour un premier test et devient un problème dès la première fois que vous validez le fichier. Lisez-la depuis l'environnement à la place. Les bibliothèques client OpenAI que LangChain enveloppe recherchent OPENAI_API_KEY et OPENAI_BASE_URL par défaut, vous pouvez donc les définir dans votre shell et construire le modèle avec moins d'arguments.
export OPENAI_API_KEY="YOUR_API_KEY"
export OPENAI_BASE_URL="https://api.nsfwllmrouter.com/v1"
import os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url=os.environ.get("OPENAI_BASE_URL", "https://api.nsfwllmrouter.com/v1"),
api_key=os.environ["OPENAI_API_KEY"],
)
Passer l'URL de base explicitement, comme ci-dessus, présente un avantage. Si un autre projet sur la même machine utilise le vrai service OpenAI et s'appuie sur la même variable d'environnement, votre argument explicite empêche les deux d'entrer en collision.
Une première chaîne avec un template de prompt
La plupart des travaux réels utilisent un template de prompt, le modèle et un analyseur de sortie joints avec l'opérateur pipe. C'est le style LangChain Expression Language, et il ne nécessite rien de spécial pour un endpoint personnalisé.
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
)
prompt = ChatPromptTemplate.from_messages([
("system", "You write short story openings in the style the user asks for."),
("human", "Genre: {genre}\nSetting: {setting}\nWrite three sentences."),
])
chain = prompt | llm | StrOutputParser()
print(chain.invoke({"genre": "noir mystery", "setting": "a rainy harbor town"}))
Le modèle n'a pas de refus de contenu pour une utilisation adulte légale, ce qui est important lorsque votre chaîne génère de la fiction, des scènes de jeu de rôle ou des dialogues de compagnon qu'un modèle plus prudent refuserait. La seule règle fixe est que le contenu sexuel impliquant des mineurs n'est jamais produit. Construisez votre propre validation des entrées par-dessus cela si votre produit a des règles plus strictes.
Streaming et utilisation
L'API prend en charge le streaming sur les événements envoyés par le serveur avec stream: true, et l'utilisation est signalée à la fin du flux. Dans LangChain, vous appelez .stream() et itérez sur les blocs.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
)
for chunk in llm.stream("Write a four-line poem about debugging at night."):
print(chunk.content, end="", flush=True)
Un détail à garder à l'esprit : une requête en streaming occupe l'un de vos slots de parallélisme jusqu'à la fin du flux. Si votre interface utilisateur ouvre un flux et que l'utilisateur s'éloigne sans le fermer, ce slot reste occupé. Sur une clé complète, vous avez 8 requêtes simultanées, donc quelques flux abandonnés peuvent en bloquer de nouveaux. Annulez l'itérateur lorsque le client se déconnecte.
Appel de fonctions et agents
L'appel de fonctions est pris en charge : vous passez tools et éventuellement tool_choice, le modèle répond avec tool_calls (qui peut aussi être en streaming), et vous renvoyez les résultats sous forme de messages avec role: "tool". Dans LangChain, bind_tools gère le format filaire pour vous.
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
@tool
def word_count(text: str) -> int:
'Count the words in a piece of text.'
return len(text.split())
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
)
llm_with_tools = llm.bind_tools([word_count])
msg = llm_with_tools.invoke("How many words are in: the quick brown fox?")
for call in msg.tool_calls:
print(call["name"], call["args"], word_count.invoke(call["args"]))
Pour une boucle d'agent complète, les assistants d'agent de LangChain ou LangGraph exécutent ce cycle pour vous : appelez le modèle, exécutez l'outil demandé, ajoutez le message d'outil, appelez à nouveau. L'utilisation des outils suit la forme standard OpenAI, donc le même code que vous écririez pour n'importe quel endpoint compatible fonctionne. Comme pour tout modèle à poids ouverts, testez vos schémas d'outils spécifiques. Gardez les noms d'arguments simples, les descriptions courtes et la liste des outils petite, car les longues listes donnent aux modèles plus faibles plus de chances de choisir le mauvais.
Sortie structurée avec le mode JSON
Le mode JSON est disponible via response_format: {"type": "json_object"}. Dans LangChain, vous pouvez le passer en tant qu'argument de modèle et analyser le résultat vous-même, ce qui vous donne le contrôle sur la validation et les tentatives.
import json
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
model_kwargs={"response_format": {"type": "json_object"}},
)
msg = llm.invoke(
"Return a JSON object with keys title and tags (a list of three strings) "
"for an article about hiking."
)
data = json.loads(msg.content)
print(data["title"], data["tags"])
Écrivez le mot « JSON » dans le prompt et décrivez les clés que vous souhaitez. Le mode JSON rend la sortie en JSON valide, mais il ne permet pas de garantir le schéma exact, alors vérifiez les clés après l'analyse et réessayez en cas d'échec. Les requêtes échouées n'étant pas facturées, une boucle de réessai pour une sortie malformée coûte peu.
Requêtes simultanées, tentatives et limites de débit
LangChain facilite la répartition du travail avec .batch(), et c'est là que la plupart des gens rencontrent leur première limite de débit. Une clé complète permet 8 requêtes simultanées et 300 requêtes par minute. Une clé d'essai permet 2 requêtes simultanées et 60 par minute. Si vous dépassez, l'API répond avec HTTP 429. Le code d'erreur est rate_limited lorsque vous dépassez les requêtes par minute, et concurrency lorsque trop de requêtes s'exécutent en même temps. En cas de surcharge temporaire, vous obtenez également une 429 avec un en-tête Retry-After: 5.
Définissez vous-même la concurrence par lot et laissez le client réessayer avec une progression :
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
max_retries=4,
timeout=120,
)
inputs = [f"Summarize idea number {i} in one line." for i in range(40)]
results = llm.batch(inputs, config={"max_concurrency": 6})
print(len(results))
Rester à 6 sur une clé complète laisse de la marge pour d'autres requêtes du même compte. Sur une clé d'essai, utilisez 2. Si vous voyez encore des erreurs concurrency, recherchez les flux qui ne se sont jamais terminés, car chacun occupe un slot. Chaque réponse porte X-Request-Id, qui est la valeur à citer si vous contactez le support, et X-Balance-USD, qui affiche votre crédit restant.
Appels asynchrones pour les applications web
Dans un serveur web, vous voulez rarement des appels bloquants. Chaque modèle de chat LangChain a des versions asynchrones de ses méthodes, nommées ainvoke, astream et abatch, et elles fonctionnent contre cet endpoint de la même manière que les versions synchrones.
import asyncio
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
)
async def main():
async for chunk in llm.astream("Describe a quiet harbor at dawn in two sentences."):
print(chunk.content, end="", flush=True)
asyncio.run(main())
Dans un service asynchrone, limitez le nombre d'appels en cours avec un sémaphore de taille adaptée à votre clé, par exemple 8 pour une clé complète. Cela évite qu'un endpoint occupé de votre propre application ne remplisse tous les slots et n'affame les autres. Capturez les exceptions autour de l'appel, journalisez l'en-tête X-Request-Id lorsque vous l'avez, et affichez un court message de réessai à l'utilisateur au lieu d'une trace de pile.
Astuce de débogage : si une chaîne se comporte bizarrement, imprimez les messages exacts que le modèle reçoit. Les templates de prompt insèrent parfois des espaces blancs supplémentaires ou perdent une variable, et le modèle ne peut répondre qu'à ce qui lui a été envoyé. Les commutateurs verbose et debug de LangChain, ou le traçage LangSmith si vous l'utilisez déjà, montrent la liste des messages finaux.
Ce que la fenêtre de contexte signifie pour les chaînes
La fenêtre de contexte est de 100 000 tokens pour le prompt et la complétion ensemble, et max_tokens est optionnel. C'est assez grand pour les chaînes de récupération qui bourrent beaucoup de documents dans un seul prompt, ou pour une mémoire de conversation qui conserve un long historique. C'est aussi assez grand pour devenir cher si vous renvoyez tout l'historique à chaque tour sans le réduire.
Un calcul rapide. Une étape de récupération qui envoie 12 000 tokens d'entrée et en reçoit 800 coûte 0,012 x $0,15 + 0,0008 x $0,50 = $0,0018 + $0,0004 = $0,0022. Exécutez cela 1 000 fois et vous payez $2,20. La tarification est de $0,15 par 1M de tokens d'entrée et de $0,50 par 1M de tokens de sortie, sans abonnement, donc vous ne payez que ce que la chaîne utilise réellement. La page de tarification a les options de rechargement, et la documentation de démarrage rapide liste tous les paramètres pris en charge. Les prompts ne sont pas utilisés pour l'entraînement.
