NL ▾
API-sleutel aanvragen

LangChain + LLM: Verbind een ongecensureerd API

LangChain communiceert met elke OpenAI-compatibele endpoint via de ChatOpenAI-klasse. Stel de base URL, de sleutel en de model-ID in, en je chains en agents draaien op deze API.

Bijgewerkt

Verbonden blokken die een chain vormen die naar een centraal gloeiend knooppunt loopt, wat LangChain-pijplijnen voorstelt die één API aanroepen

Belangrijkste punten

  1. Gebruik ChatOpenAI met base_url ingesteld op de API en model ingesteld op uncensored.
  2. Chains, streaming, tool calling en JSON-uitvoer gaan allemaal via de standaard chat-completions-route.
  3. Beperk de batch-concurrentie tot 8 op een volledige sleutel, of 2 op een proefsleutel, om 429-fouten te vermijden.
  4. Dit is één gehost model, geen router, dus er is geen modelwisseling per aanbieder.

De korte setup

De gebruikelijke langchain + llm-vraag is of je een speciale integratie nodig hebt voor een endpoint van een derde partij. Dat is niet zo. De OpenAI-chatwrapper van LangChain accepteert een aangepaste base URL, en deze API volgt het OpenAI chat-completions-formaat, dus de standaardklasse werkt. Installeer het pakket, geef drie waarden door, en je hebt een chat-modelobject dat je in elke chain kunt gebruiken.

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)

Haal de sleutel op van de API-sleutelpagina. Nieuwe gebruikers kunnen daar met één klik na het aanmelden $0,50 aan proeftegoed claimen, wat ruim voldoende is om alles in dit artikel vele malen uit te voeren. De model-ID is altijd uncensored, en verzoeken gaan naar POST /v1/chat/completions.

Een opmerking over de site-naam: ondanks het woord "router" in het domein, is dit een enkel open-weight model dat wordt gerund door een onafhankelijke aanbieder, geen router tussen aanbieders en geen officiële API van een aanbieder. Je kiest hier niet tussen modellen. Je code noemt één model, en dat is het model dat antwoordt.

Houd de sleutel buiten de broncode

Een sleutel hardcoderen werkt voor een eerste test, maar wordt een probleem zodra je het bestand commit. Lees het uit de omgeving in plaats daarvan. De OpenAI-clientbibliotheken die LangChain omhult, zoeken standaard naar OPENAI_API_KEY en OPENAI_BASE_URL, dus je kunt ze in je shell instellen en het model met minder argumenten construeren.

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"],
)

Het expliciet doorgeven van de base URL, zoals hierboven, heeft één voordeel. Als een ander project op dezelfde machine de echte OpenAI-service gebruikt en vertrouwt op dezelfde omgevingsvariabele, voorkomt je expliciete argument dat de twee met elkaar botsen.

Een eerste chain met een prompt-template

De meeste echte taken gebruiken een prompt-template, het model en een output-parser, verbonden met de pipe-operator. Dit is de LangChain Expression Language-stijl, en dit heeft niets speciaals nodig voor een aangepaste endpoint.

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"}))

Het model kent geen inhoudelijke weigeringen voor wettelijk volwassen gebruik, wat van belang is wanneer je chain fictie, roleplay-scènes of companion-dialogen genereert die een voorzichtiger model zou weigeren. De enige vaste regel is dat seksuele inhoud met minderjarigen nooit wordt gegenereerd. Bouw je eigen inputvalidatie hierbovenop als je product strengere regels heeft.

Streaming en gebruik

De API ondersteunt streaming via server-sent events met stream: true, en het gebruik wordt gemeld aan het einde van de stream. In LangChain roep je .stream() aan en itereren over chunks.

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)

Een detail om in gedachten te houden: een gestreamd verzoek bezet één van je concurrency-slots tot de stream eindigt. Als je UI een stream opent en de gebruiker verdwijnt zonder deze te sluiten, blijft dat slot bezet. Op een volledige sleutel heb je 8 gelijktijdige verzoeken, dus een handvol verlaten streams kunnen nieuwe blokkeren. Annuleer de iterator wanneer de client disconnecteert.

Function calling en agents

Tool calling wordt ondersteund: je geeft tools door en optioneel tool_choice, het model antwoordt met tool_calls (die ook gestreamd kunnen worden), en je stuurt resultaten terug als berichten met role: "tool". In LangChain zorgt bind_tools voor het wire formaat.

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"]))

Voor een volledige agent-loop voeren de agent-hulpprogramma's van LangChain of LangGraph deze cyclus voor je uit: roep het model aan, voer het opgeroepen hulpprogramma uit, voeg het hulpprogrammabericht toe en roep opnieuw aan. Toolgebruik volgt de standaard OpenAI-indeling, dus dezelfde code die je voor elke compatibele endpoint zou schrijven, werkt. Test, net als bij elk open-weight model, je specifieke hulpprogrammaschema's. Houd argumentnamen eenvoudig, beschrijvingen kort en de lijst met hulpprogramma's klein, omdat lange lijsten modellen met minder vermogen meer ruimte geven om het verkeerde te kiezen.

Gestructureerde uitvoer met JSON-modus

JSON-modus is beschikbaar via response_format: {"type": "json_object"}. In LangChain kun je dit doorgeven als een modelargument en het resultaat zelf parseren, wat je controle geeft over validatie en opnieuw proberen.

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"])

Noem "JSON" in de prompt en beschrijf de sleutels die je wilt. JSON-modus zorgt ervoor dat de uitvoer geldige JSON is, maar garandeert niet dat deze exact aan jouw schema voldoet, dus controleer de sleutels na het parseren en probeer het opnieuw bij een fout. Omdat mislukte verzoeken niet worden aangerekend, kost een herhalingsslus voor een ongeldige uitvoer weinig.

Concurrentie, retries en rate limits

LangChain maakt het eenvoudig om werk uit te voeren met .batch(), en daar lopen de meeste mensen tegen hun eerste rate limit aan. Een volledige sleutel staat 8 gelijktijdige verzoeken en 300 verzoeken per minuut toe. Een proefsleutel staat 2 gelijktijdige verzoeken en 60 per minuut toe. Ga daarboven en de API antwoordt met HTTP 429. De foutcode is rate_limited wanneer je de verzoeken per minuut overschrijdt, en concurrency wanneer er te veel verzoeken tegelijk lopen. Bij tijdelijke overbelasting krijg je ook een 429 met een Retry-After: 5-header.

Stel de batchconcurrentie zelf in en laat de client opnieuw proberen met backoff:

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))

Blijf op 6 op een volledige sleutel voor ruimte voor andere verzoeken van hetzelfde account. Gebruik op een proefsleutel 2. Als je nog steeds concurrency-fouten ziet, zoek dan naar streams die nooit zijn afgelopen, omdat elk een slot vasthoudt. Elk antwoord bevat X-Request-Id, dat de waarde is om te noemen als je support contacteert, en X-Balance-USD, dat je resterende tegoed toont.

Asynchrone verzoeken voor webapplicaties

Binnen een webserver wil je zelden blokkerende verzoeken. Elk LangChain chatmodel heeft asynchrone varianten van zijn methoden, met de namen ainvoke, astream en abatch, en ze werken op deze endpoint op dezelfde manier als de synchrone versies.

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())

In een asynchrone service, bescherm het aantal lopende verzoeken met een semaphore die is afgestemd op je sleutel, bijvoorbeeld 8 op een volledige sleutel. Zo voorkom je dat één drukke endpoint van je eigen app alle beschikbare slots vult en de andere verzoeken honger laat lijden. Vang uitzonderingen af rond het verzoek, log de X-Request-Id-header als je die hebt, en toon de gebruiker een kort bericht om het opnieuw te proberen in plaats van een stacktracering.

Debugtip: als een ketting zich vreemd gedraagt, print dan de exacte berichten die het model ontvangt. Prompt-sjablonen voegen soms extra spaties toe of laten een variabele weg, en het model kan alleen antwoorden op wat het heeft ontvangen. De uitgebreide en debug-modi van LangChain, of LangSmith-tracing als je die al gebruikt, tonen de definitieve lijst met berichten.

Wat het contextvenster betekent voor chains

Het contextvenster is 100.000 tokens voor prompt en voltooiing samen, en max_tokens is optioneel. Dat is groot genoeg voor retrieval chains die veel documenten in één prompt proppen, of voor een conversation memory die een lange geschiedenis bijhoudt. Het is ook groot genoeg om duur te worden als je elke beurt een volledige geschiedenis opnieuw verstuurt zonder deze te trimmen.

Een snelle berekening. Een retrievalstap die 12.000 invoertokens verstuurt en er 800 terugkrijgt kost 0,012 x $0,15 + 0,0008 x $0,50 = $0,0018 + $0,0004 = $0,0022. Voer dat 1.000 keer uit en je betaalt $2,20. De prijzen zijn $0,15 per 1M invoertokens en $0,50 per 1M uitvoertokens, zonder abonnement, dus je betaalt alleen voor wat de ketting daadwerkelijk gebruikt. De prijslijst heeft de opties om op te waarderen, en de quickstart-documentatie somt alle ondersteunde parameters op. Prompts worden niet gebruikt voor training.

Vragen en antwoorden

Heb je een speciaal LangChain-pakket nodig voor deze endpoint?

Nee. Het langchain-openai-pakket is genoeg. Geef de base URL, je sleutel en de model-ID uncensored door aan ChatOpenAI.

Werkt het met LangGraph en agents?

Ja, zolang ze het standaard OpenAI-chat- en tool-calling-formaat gebruiken. Tool calls, gestreamde tool_calls en role tool-resultaten worden ondersteund.

Kan ik schakelen tussen modellen binnen een chain?

Nee. Er is één model, uncensored, en de service is geen router tussen aanbieders. Gebruik een apart LangChain-modelobject als je voor een andere stap een andere aanbieder nodig hebt.

Wat moet ik doen met 429-fouten?

Verlaag je batch-concurrentie, respecteer de Retry-After-header wanneer deze aanwezig is, en zorg dat gestreamde verbindingen worden gesloten. Een volledige sleutel staat 8 gelijktijdige verzoeken en 300 per minuut toe.

Zijn embeddings beschikbaar voor mijn vector store?

Nee. De API is alleen tekst en heeft geen embeddings-endpoint, dus je hebt een aparte embedding-bron nodig voor retrieval.

Je sleutel is nog maar één formulier verwijderd

Maak een account aan, kopieer de sleutel, pas de base URL aan. Dat is de hele setup.

API-sleutel aanvragen