Een modelaanroep doen¶
Stuur POST /responses om een beurt met een taalmodel uit te voeren. Een beurt omvat de
modelaanroep en eventuele toolaanroepen die nodig zijn voor het antwoord. Deze pagina beschrijft
het verzoek, het antwoord, streamen en de fouten die je tegenkomt; elke andere pagina onder
Core aanroepen bouwt hierop voort.
Je hebt een API-sleutel nodig en een model-id dat de policy van die sleutel toestaat. Heb je nog geen verzoek gestuurd, begin dan bij Je eerste aanroep.
Verzoekvelden¶
{
"model": "<een model-id>",
"instructions": "Je antwoordt in één zin.",
"input": [{ "role": "user", "content": "Wat is een bearer-token?" }],
"temperature": 0.2,
"max_output_tokens": 400
}
| Veld | Beschrijving |
|---|---|
model |
Model-id. GET /models toont de modellen die je sleutel mag gebruiken. |
input |
Een string of een lijst met items. Een string wordt behandeld als één user-bericht. |
instructions |
Instructies voor deze beurt, met developer-autoriteit. |
temperature, max_output_tokens |
Optionele instellingen die worden doorgegeven aan het model. Een model dat een sampling-parameter weigert, laat de beurt mislukken in plaats van hem te negeren: de huidige Claude-modellen weigeren temperature, dus laat die daar weg. Gemini-modellen accepteren hem, behalve gemini-3.5-flash-lite en gemini-3.6-flash: die hebben vaste sampling en negeren hem. |
text |
Uitvoerformaat, bijvoorbeeld {"format": {"type": "json_schema", "name": "...", "schema": {...}}}. |
reasoning |
Optioneel. Wat deze beurt van het denkwerk van het model vraagt: {"effort": "low" \| "medium" \| "high" \| "xhigh" \| "max", "summary": "auto" \| "concise" \| "detailed" \| "none"}. Beide onderdelen zijn optioneel. Een effort die je weglaat wordt niet verstuurd, dus het model houdt zijn eigen standaard aan. summary vraagt het denkwerk in leesbare vorm terug; laat je het weg, dan beslist de instelling van je sleutel of organisatie (Antwoordvelden). "summary": "none" vraagt bij deze beurt om geen samenvatting, wat die instellingen ook zeggen, en elk model accepteert het: het model denkt nog steeds na op zijn effort en de redeneertokens tellen nog steeds mee in usage. Een model dat wordt bereikt via een OpenAI-compatibele chat-completions-API heeft geen manier om een samenvatting te vragen, en wat er terugkomt hangt af van de server van het model (Antwoordvelden); een model waarvan de client er nooit een kan teruggeven, een Azure-deployment op chat completions, weigert een verzoek dat erom vraagt met een 400 die reasoning.summary noemt. Een model dat een effort weigert, laat de beurt falen, net als bij een sampling-parameter die het weigert. |
agent |
Optioneel. Noemt een opgeslagen agent waarop deze beurt is gebouwd: zijn model, instructie, vastgezette prompt, skills en tools. Zijn skills en tools komen bij die van het verzoek; elk ander veld dat hij vastlegt en dat het verzoek óók meegeeft, wordt geweigerd met 400, met dat veld in error.param. Legt de agent een model vast, dan mag model weg. |
timezone |
Optioneel, een uitbreiding van Core. Een IANA-zonenaam zoals Europe/Amsterdam: de tijdzone waarin de ingebouwde datum- en tijdwaarden van een prompt voor deze beurt worden gelezen. Laat je hem weg, dan geldt de tijdzone van de planning die de beurt afvuurt, anders die van je organisatie, anders UTC. Een zone die Core niet kent, geeft 400 met timezone in error.param. |
tool_search |
Optioneel, een uitbreiding van Core. {"mode": "router"} biedt het model twee tools aan, builtin__tool_search en builtin__tool_run, in plaats van de tools die Core voor deze beurt uitvoert en je eigen functietools, zodat een beurt veel meer tools kan bereiken dan een model in één aanroep accepteert; {"mode": "direct"} biedt elke tool zoals gewoonlijk aan. top_k (1 tot 20) en budget_tokens bepalen hoeveel tools één zoekopdracht teruggeeft en hoe groot het antwoord mag zijn. Laat je het weg, dan vraagt defer_loading: true op een tool of het item {"type": "tool_search"} van OpenAI om router (coding agents), en anders geldt de keuze van de agent, daarna die van je sleutel. tooling.tool_search.enabled van je sleutel moet aan staan en je organisatie moet tool search toestaan, anders geeft router 400 met tool_search.mode in error.param. Zie De toolrouter. |
helper |
Optioneel, een uitbreiding van Core. Noemt een van je eigen helpers, zoals acme.summary, waarvan deze beurt het werk is: elke verbruiksrij die de beurt schrijft, draagt de naam, en zonder model (en zonder agent die er een vastlegt) draait de beurt op het hulpmodel van je organisatie. Twee tot vier delen in kleine letters met punten ertussen, hoogstens 64 tekens; namen onder assist. zijn van Core zelf. Een naam buiten die regels geeft 400 met helper in error.param. Het antwoord herhaalt hem niet. Zie Je eigen helper bouwen. |
metadata |
Maximaal 16 sleutel/waarde-paren van strings, die ongewijzigd worden opgeslagen en teruggegeven. |
tool_variables |
Optioneel, een uitbreiding van Core. Waarden voor de aanroepvariabelen die de tools van de beurt declareren, als een plat object van namen naar strings, getallen of booleans, zoals het account-id van je klant of zijn eigen token voor jouw API. Het model ziet ze nooit en het antwoord herhaalt ze niet; elke tool krijgt alleen de waarden die hij declareert, en een naam die geen tool declareert wordt genegeerd. Een verplichte die ontbreekt, of een waarde die de declaratie niet toelaat, geeft 400 met tool_variables.<naam> in error.param. Zie Een waarde van je klant aan een tool meegeven. |
hosting |
Optioneel. Eist dat het model dat deze beurt beantwoordt op een genoemde plek draait, bijvoorbeeld {"region": "eu"}. region is een jurisdictie (eu, us, in-cluster, global) of een landcode van twee letters. Alleen een exacte match met de regio of het land van het model voldoet: nl voldoet niet aan eu, en eu voldoet niet aan nl. De eis geldt ook voor elk ander model dat voor deze beurt wordt aangeroepen: het model dat de oudere berichten van de thread samenvat, het embedding-model waarmee een kenniszoekopdracht zoekt, het model van een afbeeldings- of transcriptietool en de modelstappen van een kennistool. Draait zo'n model elders, dan faalt de beurt niet: de berichten van de thread gaan onsamengevat mee, een kenniszoekopdracht die het embedding-model nodig heeft is niet beschikbaar, en een tool antwoordt Denied by policy. Het audiomodel dat een input_audio-deel transcribeert, is de uitzondering: draait er geen audiomodel in de regio, dan wordt het verzoek geweigerd met 403. |
Invoeritems ondersteunen de rollen user, developer, system, delegate en assistant. Zet
applicatie-instructies in instructions of een developer-bericht. Zet inhoud van eindgebruikers
in een user-bericht, ook als die als opdracht is geformuleerd. Core labelt deze inhoud als
onvertrouwd en stuurt haar nooit als systeeminstructie; een sleutel met
trust_envelope vertelt het model ook dat het gegevens zijn.
Een user-bericht kan ook bestanden, afbeeldingen en opnames meedragen als inhoudsdelen:
input_file en input_image (Bestanden),
en input_audio, een opname die Core transcribeert voordat het model haar leest, met het
audiomodel dat het deel in model noemt of anders het eerste dat je sleutel toelaat (Audio
transcriberen).
Een model kiezen¶
GET /models toont de chatmodellen die je sleutel mag aanroepen. Het antwoord bevat dezelfde
modellen twee keer, voor twee soorten lezers:
| Veld | Wat erin staat |
|---|---|
items, next_cursor |
de eigen lijst van Core, met cursor gepagineerd, met het volledige item dat hieronder staat |
object, data, has_more, first_id, last_id |
de lijst die de SDK's van OpenAI en Anthropic lezen, zodat client.models.list() met allebei werkt. data bevat elk chatmodel dat je sleutel mag aanroepen in één pagina, dus has_more is altijd false |
Een item van data is kort: id, object en type (allebei model), owned_by,
display_name (nogmaals het id), en created en created_at, die altijd 0 en het Unix-epoch
zijn omdat Core voor een model geen aanmaaktijd bijhoudt. Lees items voor al het andere. Met
?kind=image, ?kind=embedding of ?kind=audio (hieronder) is data leeg en staan die modellen alleen in
items.
Elk item van items bevat waar het model draait, welk lab het heeft getraind, de keten die je
beurten erop volgen bij uitwijken (uitwijken), en wat het je organisatie op dit
moment kost:
{
"id": "gpt-5.6-sol",
"object": "model",
"owned_by": "azure-openai-eu",
"region": "eu",
"country": "nl",
"lab": "openai",
"fallbacks": ["claude-sonnet-5"],
"price": {
"input_eur_per_million_tokens": "2.50",
"output_eur_per_million_tokens": "10.00",
"cached_input_eur_per_million_tokens": "0.25",
"cache_write_eur_per_million_tokens": null,
"eur_per_unit": null,
"effective_from": "2026-09-01T00:00:00Z",
"is_tenant_override": false,
"listed": {
"currency": "EUR",
"input_per_million_tokens": "2.50",
"output_per_million_tokens": "10.00",
"cached_input_per_million_tokens": "0.25",
"cache_write_per_million_tokens": null,
"per_unit": null
},
"exchange_rate": null
},
"deprecation": null
}
lab is het lab dat het model heeft getraind — anthropic, openai, google, meta,
mistral, perplexity, black_forest of artific — zoals je operator het vastlegt, en null
als er geen is vastgelegd. Het zegt niet waar het model draait: dat is region, en owned_by is
de aanbieder die het serveert.
price is null als niets het model beprijst; verbruik op zo'n model wordt nog steeds vastgelegd en
kost simpelweg niets tot er een prijs is (verbruik). Bedragen zijn
in euro als exacte decimale strings. cached_input_eur_per_million_tokens is het tarief voor
invoer die de provider uit zijn promptcache las en cache_write_eur_per_million_tokens het tarief
voor invoer die hij erin schreef (promptcaching); null betekent geen apart
tarief, dus die tokens kosten het gewone invoertarief. eur_per_unit staat in plaats van de tokenbedragen bij een
model dat per eenheid wordt afgerekend: per afbeelding bij een afbeeldingsmodel (?kind=image,
hieronder), per minuut audio bij een audiomodel (?kind=audio). is_tenant_override is true als je organisatie een eigen prijs betaalt in plaats van die
van de omgeving, en effective_from is het moment waarop de geldende prijs inging — een prijs die
voor een toekomstige datum is aangekondigd, staat hier pas als die geldt.
listed is dezelfde prijs in de valuta waarin hij is vastgelegd, de valuta waarin de leverancier
van het model factureert, met dezelfde bedragen zonder eur_ in de naam. exchange_rate is null
bij een prijs in euro. Bij een prijs in een andere valuta noemt het de koers waarmee Core omrekende:
de nieuwste referentiekoers van de Europese Centrale Bank en de dag waarop die werd gepubliceerd.
"listed": {
"currency": "USD",
"input_per_million_tokens": "3.00",
"output_per_million_tokens": "15.00",
"cached_input_per_million_tokens": "0.30",
"cache_write_per_million_tokens": "3.75",
"per_unit": null
},
"exchange_rate": { "units_per_euro": "1.1650", "rate_date": "2026-10-08" }
units_per_euro is hoeveel eenheden van de valuta één euro koopt. Elk bedrag in euro is het bedrag
in listed gedeeld door die koers, zonder afronding: hier 3.00 / 1.1650 voor de invoer. Het bedrag
in euro verandert dus wanneer de ECB een nieuwe koers publiceert. Hoe dat doorwerkt in wat een maand
kost, staat bij Een prijs in een andere valuta.
deprecation is null, tenzij je operator het model uitfaseert; zie
hieronder.
Een uitgefaseerd model¶
Een model dat wordt uitgefaseerd, draagt in items de datum en de vervanger:
"deprecation": {
"effective_date": "2026-12-01",
"replacement": "claude-sonnet-5",
"in_effect": false
}
effective_date is de dag (in UTC) vanaf wiens begin het model niet meer als zichzelf antwoordt.
replacement is het model dat dan zijn plaats inneemt, en in_effect zegt of die dag al is begonnen
op het moment dat je de lijst las.
Elk antwoord op een verzoek dat zo'n model noemt, vóór de datum en erna, draagt de header
X-Core-Model-Deprecation, ook een weigering:
X-Core-Model-Deprecation: "gpt-5.6-sol"; effective="2026-12-01"; replacement="claude-sonnet-5"; rerouted
rerouted staat erbij als de beurt al op de vervanger draaide. Noemde het verzoek meer dan één
uitgefaseerd model, dan staan ze na elkaar, gescheiden door komma's. Een gestreamd antwoord draagt de
header vanaf het begin van de stream, en een achtergrondrun op het antwoord dat
hem aanmaakt. Een beurt zonder eigen HTTP-antwoord, zoals die van een schema dat afgaat, draagt geen
header; daar zie je de omleiding aan model van het opgeslagen antwoord. Een webpagina kan deze header niet lezen;
lees deprecation in GET /models.
Vanaf de datum hangt het af van de policy van je organisatie en je sleutel (Uitgefaseerde modellen):
- Omleiden (de standaard): de beurt draait op de vervanger, alsof je die had genoemd.
modelin het antwoord noemt de vervanger, en het verbruik wordt tegen diens prijs berekend. Weigert je sleutel de vervanger, dan krijg je dezelfde status als wanneer je hem zelf had genoemd, met een melding die beide modellen noemt:The model 'gpt-5.6-sol' was retired on 2026-12-01 and its replacement 'claude-sonnet-5' cannot take this request: .... - Blokkeren: op
POST /responses400metmodelinerror.paramen de meldingThe model 'gpt-5.6-sol' was retired on 2026-12-01. Use 'claude-sonnet-5' instead.Noem de vervanger in je verzoek.
Dat geldt voor elke manier waarop een beurt start: POST /responses, POST /messages, een schema
dat afgaat, een agent die een andere agent aanroept en een taak. Een achtergrondrun wordt
omgeleid op het moment dat hij wordt aangenomen, niet wanneer hij wordt opgepakt: een achtergrondrun
die vóór de datum is aangenomen en pas daarna wordt opgepakt, roept nog het uitgefaseerde model aan.
Zet vlak voor de datum geen achtergrondruns meer in de wachtrij op dat model; noem de vervanger.
Modellen die je sleutel bereikt zonder ze te noemen¶
Een beurt kan meer modellen aanroepen dan het model in model: een afbeeldingsmodel via zijn tool,
een audiomodel dat een opname transcribeert, het embeddingmodel dat in je kennis, geheugen en skills zoekt, en het model dat een lange thread
samenvat. Elk daarvan wordt aan de regio's van je sleutel getoetst, dus elk staat ook met zijn
region en country in een lijst.
GET /models?kind=image toont de afbeeldingsmodellen, elk met de tool_name waaronder het wordt
aangeboden (builtin__generate_image__...). GET /models?kind=audio toont de audiomodellen, elk
met de tool_name van zijn transcriptietool (builtin__transcribe_audio__...); dezelfde modellen
bedienen POST /audio/transcriptions en input_audio (Audio transcriberen).
GET /models?kind=embedding toont de embeddingmodellen.
Anders dan de standaard kind=chat tonen deze elk model dat je organisatie heeft, en allowed zegt
of het modelbeleid van je sleutel het nu toelaat; false betekent dat een aanroep erop wordt
geweigerd. Voor een afbeeldingsmodel is dat alleen de helft van het model: de tool moet ook aanstaan
en toegestaan zijn (afbeeldingen genereren).
Elk chatitem heeft kind: "chat" en allowed: true, omdat een chatmodel dat je sleutel niet mag
aanroepen niet in de lijst staat.
GET /model-roles noemt de modellen die Core voor je kiest:
{
"items": [
{"object": "model_role", "role": "embedding", "model": "text-embedding-eu",
"region": "eu", "country": "nl", "allowed": true},
{"object": "model_role", "role": "summariser", "model": null,
"region": null, "country": null, "allowed": null}
],
"next_cursor": null
}
summariser staat erbij als je sleutel compaction heeft en samenvat (elke modus behalve
window). Zonder eigen compaction.model noemt het het samenvattingsmodel dat in jouw omgeving is
ingesteld, als dat er is; een model van null betekent dat het model van de beurt zelf samenvat,
en dat wordt met de beurt beslist. Zodra de
maandlimiet van je sleutel of van je organisatie is
bereikt, worden er geen nieuwe samenvattingen gemaakt en gaan de oudere beurten van de thread
ongewijzigd mee. embedding staat erbij als de omgeving een embeddingmodel voor je organisatie
heeft. Geen van deze lijsten houdt rekening met hosting.region van een verzoek: dat beperkt één
beurt.
Antwoordvelden¶
{
"id": "resp_0KQ2mR...",
"object": "response",
"status": "completed",
"model": "<een model-id>",
"output": [
{
"type": "message",
"role": "assistant",
"content": [{ "type": "output_text", "text": "..." }]
}
],
"usage": {
"input_tokens": 31,
"input_tokens_details": { "cached_tokens": 0, "cache_write_tokens": 0 },
"output_tokens": 22,
"output_tokens_details": { "reasoning_tokens": 0 },
"total_tokens": 53
},
"conversation": { "id": "thr_0KQ2mR..." }
}
Er is geen veld output_text: de OpenAI-SDK berekent dat uit output, en dat kun jij ook.
conversation is de thread waarop de items van een opgeslagen beurt zijn geschreven, en null
bij een beurt zonder opslag. Het id van het verzoek dat het antwoord maakte staat in de
response-header X-Request-Id.
Verwerk output als een lijst. Deze kan assistentberichten bevatten, function_call-items die je
applicatie moet uitvoeren, mcp_call-items voor een tool die Core voor je uitvoerde, en een
redeneeritem als het denkwerk van het model met het antwoord is meegekomen. Een achtergrondrun die
op goedkeuring wacht toont elke wachtende aanroep
als een mcp_approval_request-item, en een aanroep waarover na het wachten is beslist, noemt zijn
verzoek in approval_request_id. Dat item bevat tekst
als er om een samenvatting is gevraagd en het model daar een leesbare vorm voor heeft. Core vraagt
er bij elke beurt om, tenzij je verzoek, je sleutel of je organisatie iets anders zegt. Zonder
tekst is summary gelijk aan [], wat zegt dat het model heeft nagedacht en dat voor zich heeft
gehouden:
{ "id": "item_0KQ2mR...", "type": "reasoning", "summary": [] }
Modellen die je installatie via een OpenAI-compatibele chat-completions-API bereikt, gedragen zich anders, omdat die API geen manier heeft om een samenvatting te vragen:
- Een server die het denkwerk van zijn model teruggeeft, stuurt het mee of iemand erom vroeg of
niet. Core zet het op de plek van een samenvatting: één
summary_textvan het redeneeritem, dezelfde streamgebeurtenissen, en eenthinking-blok opPOST /messages. Het is het eigen denkwerk van het model, woordelijk en zo lang als het duurde, geen samenvatting die voor een lezer is geschreven. Core geeft het alleen terug bij een beurt die om een samenvatting vraagt; bij elke andere beurt wordt het weggelaten, al tellen de tokens ervan nog wel mee inusage. - Een server die geen denkwerk teruggeeft, geeft Core niets om het item van te maken.
outputbevat dan helemaal geen redeneeritem, ook geen item met een lege samenvatting.
Een Azure-deployment die op chat completions is ingesteld (de Azure-provider van een deployment
die de Responses API niet aanzet) is geen van beide: het stuurt geen samenvatting en geen
denkwerk, dus een verzoek dat daar om een samenvatting vraagt wordt geweigerd met een 400 die
reasoning.summary noemt (thinking op POST /messages), in plaats van zonder beantwoord te
worden. De samenvatting die Core uit zichzelf vraagt, wanneer je sleutel of organisatie dat zegt,
wordt nooit geweigerd, en "summary": "none" evenmin.
Om een samenvatting vragen garandeert er geen. OpenAI schrijft redeneersamenvattingen alleen voor
een account dat het heeft geverifieerd. Op een installatie waarvan het OpenAI-account dat niet is,
krijgt een beurt die bij een OpenAI-model om een samenvatting vraagt nog steeds een 200, zonder
de tekst van de samenvatting.
Wil je bij één beurt geen samenvatting, stuur dan "reasoning": {"summary": "none"} op
POST /responses, of "thinking": {"type": "adaptive", "display": "omitted"} op POST /messages.
Beide gaan boven de instelling van je sleutel en je organisatie. Bij een
agent die reasoning vastlegt, geldt de waarde van de agent en noemt
het antwoord reasoning als overschreven.
Waar de bronnen van een antwoord staan¶
Heeft een beurt een kennistool of builtin__web_fetch gebruikt, dan komen de
bronnen op twee plekken terug.
Het resultaat van de tool noemt alles wat die heeft gelezen, of het model het nu aanhaalt of niet:
{
"type": "function_call_output",
"call_id": "call_0KQ...",
"name": "knowledge__handboek",
"output": "[1] …",
"citations": [{ "kind": "file", "ref": "chk_0KQ…", "title": "Handboek" }]
}
kind is url voor een pagina die het model heeft opgehaald -- ref is dan een adres dat je kunt
openen -- en file voor een passage uit je eigen geïndexeerde inhoud, waar ref het id is van het
tekstblok waar die uit komt. Een tool die geen bronnen leest, antwoordt [].
Dat item staat in het gesprek, niet in het antwoord: lees het terug met GET
/threads/{thread_id}/items (Een gesprek voortzetten). Het antwoord zelf draagt
de ronde als één mcp_call — het item waarmee de Responses-API een tool aanduidt die de server
voor je uitvoerde — en een mcp_call heeft geen plek voor een bronnenlijst. Daarom zijn de
annotaties hieronder het eigen verslag van het antwoord van wat het aanhaalde.
De tekst van de assistent zegt waar die ze aanhaalt, als annotations bij het tekstdeel:
{
"type": "output_text",
"text": "Verlenging is jaarlijks [1].",
"annotations": [
{ "type": "file_citation", "file_id": "chk_0KQ…", "filename": "Handboek", "index": 24 }
]
}
De posities zijn posities in diezelfde text: index bij een file_citation, start_index en
end_index bij een url_citation, die ook url en title draagt. Ze beslaan de markering [1]
zelf, zodat je er een link van kunt maken zonder de tekst te doorzoeken.
De posities tellen Unicode-codepunten, geen UTF-8-bytes en niet de UTF-16-eenheden waarmee een
JavaScript-string telt: knip in JavaScript in Array.from(text) in plaats van in text zelf
wanneer het antwoord een emoji of een ander teken buiten het Basic Multilingual Plane kan bevatten.
Zoekt een beurt meer dan eens, dan loopt de nummering door: een tweede zoekopdracht na drie
passages geeft het model [4] en [5], zodat een antwoord dat beide zoekopdrachten aanhaalt bij
elke markering zijn eigen bron krijgt. Een beurt die op de achtergrond loopt (background: true)
draagt geen annotaties; de bronnen staan bij elk resultaat.
Core gokt nooit. Een markering die een passage noemt die de beurt nooit heeft opgehaald, krijgt geen annotatie en blijft in de tekst staan zoals het model die schreef. Een pagina die het model wel las maar niet aanhaalde, staat bij het resultaat en in geen enkele annotatie.
Promptcaching¶
Een modelprovider kan het begin van een verzoek dat hij net verwerkte bewaren. Begint het volgende verzoek met precies dezelfde inhoud, dan leest de provider dat deel uit zijn cache in plaats van het opnieuw te verwerken; dat is sneller en goedkoper. Het opslaan in de cache kan extra kosten.
Hoe dat gaat, hangt af van het model:
- Claude-modellen. Core vraagt de provider voor jou om het begin van het verzoek te cachen, wanneer een latere aanroep het waarschijnlijk terugleest: een beurt die tools aanbiedt (elke toolronde verstuurt het hele verzoek opnieuw), elke modelaanroep na de eerste binnen één beurt, en elke beurt van een opgeslagen gesprek. Eén aanroep zonder tools in een beurt zonder opslag wordt niet gecachet. Een cache-item blijft ongeveer vijf minuten na de laatste keer dat het gelezen werd.
- Andere modellen (OpenAI, ook via Azure OpenAI, Gemini en Scaleway). De provider cachet zelf, waar hij caching ondersteunt; jij en Core sturen daar niets voor mee.
Een verzoek dat korter is dan het minimum van de provider wordt helemaal niet gecachet, en kost ook niets extra.
Wat het antwoord je vertelt¶
usage.input_tokens_details zegt wat caching voor deze beurt deed. Beide getallen zitten al in
input_tokens:
| Veld | Betekenis |
|---|---|
cached_tokens |
invoer die de provider uit zijn cache las, gerekend tegen het cachetarief van het model |
cache_write_tokens |
invoer die de provider in zijn cache schreef, gerekend boven het gewone invoertarief; alleen Claude-modellen melden dit, andere modellen geven 0 |
Dezelfde getallen worden vastgelegd als de verbruikssoorten cached_tokens en cache_write_tokens,
en geprijsd met cached_input_eur_per_million_tokens en cache_write_eur_per_million_tokens van
het model (wat je verbruik kostte). Bij Claude kost het
schrijven van een cache-item ongeveer een kwart meer dan dezelfde invoer één keer versturen, en
kost het teruglezen een fractie daarvan. Caching loont dus zodra een item minstens één keer wordt
teruggelezen.
Meer uit de cache halen¶
De cache herkent alleen inhoud die vanaf het allereerste begin van het verzoek gelijk is, in de volgorde tools, instructies, berichten. Verandert er vroeg in het verzoek iets, dan begint de cache opnieuw voor alles wat erna komt. Houd tussen de beurten van één gesprek deze dingen gelijk:
instructions;- de agent en zijn versie, als je er een noemt;
- de tools en skills die de beurt aanbiedt, die ook een beleidsregel kan veranderen;
reasoning.
Sommige dingen laten de cache opnieuw beginnen, wat je ook stuurt:
- terugvallen op een ander model, want elk model heeft zijn eigen cache;
- gesprekken samenvatten, wat de oudste berichten door een samenvatting vervangt;
- geheugenrecords die Core aan een beurt toevoegt en die afwijken van die van de vorige beurt;
- een pauze van meer dan ongeveer vijf minuten tussen twee beurten.
Antwoordstatus¶
Controleer de status van het antwoord, ook als het HTTP-verzoek slaagt:
status |
Betekenis |
|---|---|
completed |
De beurt is afgerond. Is het laatste uitvoeritem een function_call, dan voert je applicatie die tool uit en stuurt het resultaat in de volgende beurt. |
failed |
De modelaanroep is mislukt of heeft de time-out bereikt. error.message bevat een vaste melding van Core, niet de fouttekst van de leverancier. |
incomplete |
De beurt stopte voordat ze af was: het model bereikte zijn uitvoerlimiet, de beurt bereikte de limiet voor toolrondes, een live check hield het antwoord achter, of een achtergrondrun bereikte zijn kostenlimiet. incomplete_details.reason zegt welke (hieronder). |
requires_action |
Een achtergrondrun pauzeert tot je de toolresultaten instuurt, of tot je beslist over elke aanroep die hij laat wachten op goedkeuring. |
queued, in_progress |
Een achtergrondrun wacht of wordt uitgevoerd. |
cancelled |
Een achtergrondrun of een gestreamde beurt is geannuleerd. |
Een modelfout staat in het antwoordobject omdat Core het verzoek heeft geaccepteerd en de beurt heeft uitgevoerd. Validatiefouten en ontbrekende rechten geven HTTP-foutcodes.
Een afgekapt antwoord¶
Een antwoord met incomplete bevat incomplete_details.reason:
reason |
Wat er gebeurde | Wat je kunt doen |
|---|---|---|
max_output_tokens |
Het model stopte bij zijn uitvoerlimiet: jouw max_output_tokens, of het eigen maximum van het model. De tekst die er al stond, staat in output, in een berichtitem met status: "incomplete". Een toolaanroep die halverwege is afgekapt, komt niet terug. |
Vraag om een korter antwoord, verhoog max_output_tokens, of ga verder vanaf de tekst die je hebt. |
max_tool_rounds |
De beurt heeft al zijn toolrondes gebruikt. | Verhoog max_tool_rounds, of stuur een vervolgbeurt. |
usage_cap_exceeded |
De maandlimiet van je sleutel of van je organisatie werd bereikt terwijl de beurt liep. Wat hij tot dan toe opleverde staat in output en wordt in rekening gebracht. |
Wacht tot de maand omslaat of laat de limiet verhogen; tot die tijd antwoordt het volgende verzoek met 429. |
cost_limit |
Een achtergrondrun, de agenttaken die hij startte en hun runs bereikten samen de kostenlimiet van de run. Wat tot dan toe is opgeleverd staat in output en wordt in rekening gebracht. |
Stuur een nieuwe run met een hogere cost_limit_eur, of ga verder met wat je hebt. |
content_filter |
Een live check van de agent hield het antwoord achter. Zijn alle blokkerende checks op de uitvoer van de agent patronen, dan stopt een gestreamd antwoord waar de verboden tekst begint; anders staat er, gestreamd of niet, geen antwoord in output. De header X-Core-Check, of bij een gestreamde beurt GET /responses/{id}/checks, noemt de check. |
Laat de persoon zien dat er geen antwoord gegeven kan worden. Vraag de beheerder van je organisatie naar de check als hij antwoorden tegenhoudt die hij niet zou moeten tegenhouden. |
Een gestreamde beurt eindigt met response.incomplete met hetzelfde object. Op POST /messages
is een afgekapt antwoord stop_reason: "max_tokens", een achtergehouden antwoord
stop_reason: "refusal", en is een beurt zonder toolrondes over of gestopt op een limiet
stop_reason: "pause_turn": de leverancier heeft geen woord voor een limiet. Een kostenlimiet hoort
bij een achtergrondrun, en die start POST /messages niet.
Waar de catalogus van je installatie de maximale uitvoer van een model vermeldt, wordt een hogere
max_output_tokens bij de modelaanroep naar dat maximum verlaagd. Het antwoord toont nog steeds de
waarde die je stuurde.
Heeft je beheerder een routeringsconfiguratie ingesteld, dan beëindigt een model dat niet
beschikbaar is de beurt niet: Core voert hem uit op het volgende model en vertelt in
model welk model antwoordde. Zie Terugvallen op een ander model.
Opslag en HTTP-status¶
Bij een verzoek zonder streaming betekent 201 met een Location-header dat het antwoord is
opgeslagen. Haal het op met GET /responses/{id}. Een aanroep zonder opslag geeft 200 en bewaart
geen gespreksinhoud. Opslag hangt af van de capability threads en de instelling store; zie Een
gesprek voortzetten.
Het antwoord streamen¶
Stel "stream": true in om uitvoer te ontvangen als Server-Sent Events:
curl -N -X POST "$CORE_URL/responses" \
-H "Authorization: Bearer $CORE_KEY" -H "Content-Type: application/json" \
-d '{"model": "<een model-id>", "input": "Leg bearer-tokens uit.", "stream": true}'
Elke gebeurtenis bevat een event:-regel, een data:-regel met JSON en een lege regel. De JSON
bevat een sequence_number dat begint bij 0. Daarmee kun je ontbrekende gebeurtenissen herkennen.
| Gebeurtenis | Afhandeling |
|---|---|
response.created |
Bewaar het antwoord-id. |
response.in_progress |
Toon dat de beurt wordt uitgevoerd. |
response.output_item.added |
Begin een item op output_index. |
response.content_part.added |
Begin het tekstdeel van het item. |
response.output_text.delta |
Voeg delta toe aan de ontvangen tekst. |
response.output_text.annotation.added |
Eén bron die de afgeronde tekst aanhaalt, met de posities die haar aanhalen (hierboven). |
response.output_text.done |
Lees de volledige tekst van het item. |
response.content_part.done |
Rond het tekstdeel af. |
response.output_item.done |
Verwerk het volledige item, bijvoorbeeld een function_call. |
response.completed, response.incomplete, response.failed |
Lees het uiteindelijke antwoordobject. De stream eindigt hier. Na een annulering is het response.completed met status: cancelled. |
error |
De beurt is uitgevoerd maar kon niet worden opgeslagen. Lees code; er is geen resultaat bewaard. |
Sla commentaarregels zoals : keepalive over. Die houden de verbinding open.
Modeltekst en een samenvatting van de redenering worden in delen verstuurd. Het redeneeritem van
een denkend model komt eerst: response.output_item.added, dan
response.reasoning_summary_part.added, één response.reasoning_summary_text.delta per stuk terwijl
het model nadenkt, dan response.reasoning_summary_text.done, response.reasoning_summary_part.done
en response.output_item.done, voordat het bericht begint. Op POST /messages komt dezelfde
redenering binnen als een thinking-blok dat met thinking_delta-gebeurtenissen wordt gevuld.
Een model dat opnieuw nadenkt nadat het aan zijn antwoord is begonnen, krijgt een tweede
redeneeritem: het bericht wordt eerst afgerond, het nieuwe redeneeritem wordt op de volgende
output_index toegevoegd en op dezelfde manier gestreamd, en tekst die daarna komt is een tweede
bericht. output bevat de items in die volgorde; lees dus elk message-item en voeg hun tekst
samen, zoals output_text van de OpenAI-SDK doet, en niet alleen het eerste. Op POST /messages is dat een tweede
thinking-blok en een tweede text-blok.
Weigeringen en toolaanroepen komen als volledige items binnen, met response.output_item.added
gevolgd door response.output_item.done.
Als het model tijdens de uitvoer faalt, rondt Core het huidige item af met de ontvangen tekst, met
status: incomplete op dat item, en eindigt de stream met response.failed.
Een gestarte stream gebruikt HTTP 200 en Content-Type: text/event-stream, ook als het antwoord
wordt opgeslagen. De headers worden verstuurd voordat de beurt begint. Gebruik het id uit de eerste
gebeurtenis om een opgeslagen resultaat op te halen.
Fouten die Core vóór de stream ontdekt, zoals een ongeldig verzoek of een niet-toegestaan model,
geven nog steeds een gewone HTTP-status, met het foutobject van
OpenAI als body. Modelfouten na de start verschijnen als
response.failed.
Als de verbinding wegvalt, loopt de beurt door en wordt het verbruik geregistreerd. Is opslag
ingeschakeld, haal het resultaat dan later op met GET /responses/{id}. Wil je dat de beurt stopt,
annuleer hem dan (hieronder). Streamen combineren met een
achtergrondrun geeft 400.
Gebruik een client die Server-Sent Events ondersteunt. De gegenereerde Python-client ondersteunt alleen antwoorden zonder streaming, en de Swagger UI-verkenner toont een body pas als het antwoord klaar is. Ontvangt een SSE-client alsnog alle gebeurtenissen tegelijk, vraag je beheerder dan te controleren of een proxy ze buffert, of dat de leverancier van het model gestreamde uitvoer filtert voordat hij die vrijgeeft (Azure OpenAI doet dat, tenzij het contentfilter van de installatie in asynchrone modus draait).
Een gestreamde beurt stoppen¶
Een stopknop in je applicatie annuleert de beurt met het id uit response.created. Daarvoor is geen
extra capability nodig: de sleutel die de beurt startte, mag hem ook stoppen. Een beurt van een
andere sleutel of een achtergrondrun annuleren vraagt runs; zonder runs krijg
je 404.
curl -s -X POST "$CORE_URL/responses/resp_0KQ2mR.../cancel" -H "Authorization: Bearer $CORE_KEY"
Core stopt de beurt waar hij op dat moment op wacht: het model dat nog tekst stuurt, of een tool
die nog loopt. Het antwoord is het opgeslagen antwoordobject met status: cancelled. Het bevat de
items die de beurt al had gemaakt. Een bericht dat het model nog aan het schrijven was, staat erin
met de tekst die al was aangekomen en status: incomplete. Wie de stream nog leest, krijgt als
laatste gebeurtenis response.completed met datzelfde antwoord.
Wat in rekening wordt gebracht:
- elke ronde die al klaar was, zoals altijd;
- voor de modelaanroep die werd afgebroken: wat de leverancier al over het verbruik had gemeld. Sommige leveranciers melden de invoertokens aan het begin, andere pas aan het eind. Meldde de leverancier nog niets, dan wordt voor die aanroep niets geregistreerd.
Nog een keer annuleren geeft hetzelfde antwoord, zonder fout. Was de beurt al klaar voordat de
annulering hem bereikte, dan krijg je 409. Is de beurt niet binnen enkele seconden gestopt, dan
krijg je 503; annuleer dan gewoon nog een keer. Een beurt zonder opslag heeft daarna geen
uitvoer in het antwoord op de annulering, want Core bewaart die niet. De stream zelf toont hem wel.
POST /messages kent geen annulering.
Veelvoorkomende fouten¶
| HTTP-status | Oorzaak |
|---|---|
400 |
Het verzoek is geweigerd: een veld dat Core niet kan inwilligen, een veld dat ontbreekt, of een veld dat een agent vastlegt. error.param noemt het. Op model kan het ook een uitgefaseerd model zijn dat je policy blokkeert. |
401 |
De secret van de API-sleutel ontbreekt of is ongeldig. |
403 |
Het sleutelbeleid weigert het model, de organisatie is opgeschort, of een hostingeis van het verzoek of de sleutel is niet voldaan (zie hieronder). |
403 op agent |
Het sleutelbeleid vereist een agent en het verzoek noemt er geen. Noem een van de agents van de sleutel. |
404 |
Een genoemd model, prompt, skill of bestand is niet beschikbaar, of de capability is uitgeschakeld. |
409 |
De idempotentiesleutel is eerder gebruikt voor een aanroep zonder opslag. |
503 |
Een benodigde dienst is niet beschikbaar. |
Elk van deze antwoorden draagt het foutobject van OpenAI
in plaats van problem+json, net als GET /responses/{id}, POST /responses/{id}/tool_outputs en
POST /responses/{id}/cancel. Zie Fouten, paginering en herhalen voor de velden en
het afhandelen van herhalingen.
Hostingweigeringen¶
Een hosting-eis, of het regions_allow-beleid van de sleutel, geeft 403 met precies een van
deze vier meldingen in detail:
detail |
Wat je kunt aanpassen |
|---|---|
The requested hosting region is not allowed for this key. |
Vraag een regio aan die in het regions_allow-beleid van je sleutel staat (Sleutels), of laat hosting weg. |
Where this model runs is not known. |
Dit hoort niet voor te komen bij een model dat GET /models toont; meld het bij je beheerder. |
The model is not hosted in a region this key allows. |
Kies in GET /models een model waarvan de regio of het land in de lijst van je sleutel staat, of vraag je operator die lijst te verruimen. |
The model is not hosted in the requested region. |
Laat hosting.region weg, of kies een model dat wel in die regio draait (Modellen toont de hosting per model). |
Time-outs¶
Een synchrone beurt kan maximaal vijf minuten duren, inclusief toolrondes. Stel de time-out van je client daarop in. Gebruik een achtergrondrun als je de verbinding niet zo lang kunt openhouden.