<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: stelkey</title>
    <description>The latest articles on DEV Community by stelkey (@stelkey).</description>
    <link>https://dev.to/stelkey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4076994%2F463c1557-9c51-4057-a254-e455db81bf8c.png</url>
      <title>DEV Community: stelkey</title>
      <link>https://dev.to/stelkey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stelkey"/>
    <language>en</language>
    <item>
      <title>What 79 Production Probes Taught Us About OpenAI-Compatible APIs</title>
      <dc:creator>stelkey</dc:creator>
      <pubDate>Wed, 26 Aug 2026 12:51:06 +0000</pubDate>
      <link>https://dev.to/stelkey/what-79-production-probes-taught-us-about-openai-compatible-apis-30d7</link>
      <guid>https://dev.to/stelkey/what-79-production-probes-taught-us-about-openai-compatible-apis-30d7</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Disclosure: AI assistance was used to organize and edit this article. I manually&lt;br&gt;
checked the technical claims against the underlying test evidence and take&lt;br&gt;
responsibility for the final text.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;An endpoint returning HTTP 200 is not enough to call it OpenAI-compatible.&lt;/p&gt;

&lt;p&gt;Real clients depend on request validation, response shapes, public model identity,&lt;br&gt;
event-stream framing, termination, usage accounting, authentication errors, and&lt;br&gt;
repeatable behavior across every route a gateway advertises. A one-line curl command&lt;br&gt;
can succeed while an SDK still hangs, a wallet is charged twice, or an upstream model&lt;br&gt;
name leaks through the public boundary.&lt;/p&gt;

&lt;p&gt;This article describes a production canary run performed on August 15, 2026. The&lt;br&gt;
frozen catalog contained 40 public routes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;32 Chat routes, each tested in JSON and SSE modes;&lt;/li&gt;
&lt;li&gt;7 Responses routes, each tested in JSON and SSE modes;&lt;/li&gt;
&lt;li&gt;1 Embeddings route, tested in JSON mode.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That produced 79 protocol probes in total. All 79 completed successfully after the&lt;br&gt;
release gate caught and corrected two Embeddings compatibility defects. The method is&lt;br&gt;
vendor-neutral and can be applied to any OpenAI-compatible gateway.&lt;/p&gt;
&lt;h2&gt;
  
  
  1. Freeze the public contract before sending traffic
&lt;/h2&gt;

&lt;p&gt;Fetch the public model catalog once, store the exact set, and calculate an immutable&lt;br&gt;
digest. Do not rediscover the catalog halfway through a test run.&lt;/p&gt;

&lt;p&gt;For each public ID, record:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the model ID that clients are expected to send;&lt;/li&gt;
&lt;li&gt;the exact supported protocol;&lt;/li&gt;
&lt;li&gt;the endpoint and response modes covered by the release;&lt;/li&gt;
&lt;li&gt;any public limits or prices that have a verified source;&lt;/li&gt;
&lt;li&gt;the catalog digest used by the test runner.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;“Configured upstream,” “present in a database,” “listed publicly,” and “proven in the&lt;br&gt;
customer path” are four different states. The test matrix should be generated from the&lt;br&gt;
public state, not from an internal provider inventory.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. Generate probes from the protocol declaration
&lt;/h2&gt;

&lt;p&gt;A useful test count is derived, not hand-written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Chat:       32 routes × (JSON + SSE) = 64 probes
Responses:   7 routes × (JSON + SSE) = 14 probes
Embeddings:  1 route  × JSON         =  1 probe
                                      ---------
                                            79
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This prevents two common mistakes: sending an SSE test to an endpoint that does not&lt;br&gt;
stream, or counting one successful route as evidence for a different protocol.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. Use a disposable identity and a hard cost ceiling
&lt;/h2&gt;

&lt;p&gt;The canary should not reuse a customer account or key. Create a short-lived synthetic&lt;br&gt;
identity with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a model-scoped or release-scoped API key;&lt;/li&gt;
&lt;li&gt;a small explicit wallet ceiling;&lt;/li&gt;
&lt;li&gt;an expiry measured in minutes;&lt;/li&gt;
&lt;li&gt;a unique traffic label excluded from customer analytics;&lt;/li&gt;
&lt;li&gt;cleanup logic prepared before the first request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run probes serially when upstream limits, ledger order, or settlement workers matter.&lt;br&gt;
If a request fails, fail the release gate closed instead of silently removing that&lt;br&gt;
model from the result.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. Assert the JSON contract, not only the body text
&lt;/h2&gt;

&lt;p&gt;For Chat and Responses, assert at least:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP 200 and the expected content type;&lt;/li&gt;
&lt;li&gt;a stable response object and non-empty request ID;&lt;/li&gt;
&lt;li&gt;the requested public model identity or a documented public alias;&lt;/li&gt;
&lt;li&gt;usable output content;&lt;/li&gt;
&lt;li&gt;a meaningful completion status;&lt;/li&gt;
&lt;li&gt;finite, non-negative usage values;&lt;/li&gt;
&lt;li&gt;a stable sanitized error shape for deliberately invalid requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Embeddings, also assert:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a non-empty numeric vector;&lt;/li&gt;
&lt;li&gt;a stable vector length for the tested route;&lt;/li&gt;
&lt;li&gt;the correct public model ID in the response;&lt;/li&gt;
&lt;li&gt;usable input and total token accounting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The public-model assertion matters. In the first Embeddings canary, the request worked,&lt;br&gt;
but the response exposed the private upstream model name. That is a boundary failure&lt;br&gt;
even when the vector itself is correct.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Treat SSE as a protocol
&lt;/h2&gt;

&lt;p&gt;For every route that declares streaming, parse frames incrementally and require:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;a valid event-stream content type;&lt;/li&gt;
&lt;li&gt;valid &lt;code&gt;data:&lt;/code&gt; records;&lt;/li&gt;
&lt;li&gt;independently parseable JSON frames;&lt;/li&gt;
&lt;li&gt;ordered output deltas;&lt;/li&gt;
&lt;li&gt;an error frame never being counted as a success;&lt;/li&gt;
&lt;li&gt;exactly one unambiguous terminal condition;&lt;/li&gt;
&lt;li&gt;bounded connection close;&lt;/li&gt;
&lt;li&gt;attributable usage when the gateway promises it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;“Some text arrived” is not enough. Duplicate terminal markers, truncated UTF-8, a&lt;br&gt;
missing final event, or a socket that never closes can all break otherwise standard&lt;br&gt;
clients.&lt;/p&gt;
&lt;h2&gt;
  
  
  6. Verify status-code semantics
&lt;/h2&gt;

&lt;p&gt;Compatibility includes the HTTP layer. The first successful Embeddings response in&lt;br&gt;
this release returned 201 because of a framework default. The payload was valid, but&lt;br&gt;
the public contract required 200.&lt;/p&gt;

&lt;p&gt;That defect was only visible because the canary asserted the exact status code rather&lt;br&gt;
than accepting every 2xx response. After the route declared 200 explicitly, the test&lt;br&gt;
was rerun from the customer boundary.&lt;/p&gt;
&lt;h2&gt;
  
  
  7. Reconcile usage, billing, and wallet movement
&lt;/h2&gt;

&lt;p&gt;After each request, the authoritative records should satisfy one invariant:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;request usage
  -&amp;gt; one settled charge
  -&amp;gt; one wallet movement
  -&amp;gt; no pending liability
  -&amp;gt; no second settlement during retry or readback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use the same decimal scale and rounding contract as the production ledger. Missing,&lt;br&gt;
negative, infinite, or contradictory usage must fail closed before a customer is&lt;br&gt;
charged.&lt;/p&gt;

&lt;p&gt;Also test idempotency. Re-reading a request, receiving a duplicate callback, or&lt;br&gt;
retrying a settlement worker must not debit the wallet twice.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Make revocation part of the release gate
&lt;/h2&gt;

&lt;p&gt;After all probes finish:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;revoke the disposable key;&lt;/li&gt;
&lt;li&gt;repeat one minimal request with that exact key;&lt;/li&gt;
&lt;li&gt;require HTTP 401 with a sanitized error body;&lt;/li&gt;
&lt;li&gt;confirm the request did not reach upstream routing or billing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A dashboard saying “revoked” is not proof until the API boundary enforces it.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Cleanup is part of the result
&lt;/h2&gt;

&lt;p&gt;The run is not complete when the final model returns output. It is complete when the&lt;br&gt;
temporary state is gone.&lt;/p&gt;

&lt;p&gt;Check for zero release-specific residue across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;active synthetic users and keys;&lt;/li&gt;
&lt;li&gt;in-flight requests;&lt;/li&gt;
&lt;li&gt;unsettled usage and pending liabilities;&lt;/li&gt;
&lt;li&gt;spend-limit leases and retry markers;&lt;/li&gt;
&lt;li&gt;temporary wallet and ledger rows;&lt;/li&gt;
&lt;li&gt;test-only sessions and cached credentials.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Keep immutable evidence for the catalog, runner, runtime image, accounting summary,&lt;br&gt;
and final readback. Never store API keys, cookies, provider secrets, payment&lt;br&gt;
credentials, or private prompts in the report.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Publish the limitations next to the pass count
&lt;/h2&gt;

&lt;p&gt;The 79/79 result proves only the matrix that was actually executed: Chat JSON/SSE,&lt;br&gt;
Responses JSON/SSE, and Embeddings JSON for the exact frozen routes.&lt;/p&gt;

&lt;p&gt;It does not automatically prove:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tool calling;&lt;/li&gt;
&lt;li&gt;vision or audio;&lt;/li&gt;
&lt;li&gt;computer control;&lt;/li&gt;
&lt;li&gt;Anthropic Messages compatibility;&lt;/li&gt;
&lt;li&gt;every optional parameter;&lt;/li&gt;
&lt;li&gt;model quality or long-term availability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each extension needs its own capability matrix. A model name containing &lt;code&gt;vl&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;reasoner&lt;/code&gt;, or &lt;code&gt;computer-use&lt;/code&gt; is not a substitute for a protocol-level canary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reusable release checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Freeze and hash the public catalog.&lt;/li&gt;
&lt;li&gt;[ ] Generate probes from each model's declared protocol.&lt;/li&gt;
&lt;li&gt;[ ] Use a disposable identity, limited key, expiry, and hard cost ceiling.&lt;/li&gt;
&lt;li&gt;[ ] Assert exact JSON fields, public model identity, and HTTP status.&lt;/li&gt;
&lt;li&gt;[ ] Assert SSE framing, ordered deltas, termination, and bounded close.&lt;/li&gt;
&lt;li&gt;[ ] Validate Embeddings vector shape and public model normalization.&lt;/li&gt;
&lt;li&gt;[ ] Reconcile usage, one settled charge, wallet movement, and pending liability.&lt;/li&gt;
&lt;li&gt;[ ] Retry readback to detect duplicate settlement.&lt;/li&gt;
&lt;li&gt;[ ] Revoke the key and require 401 without upstream work.&lt;/li&gt;
&lt;li&gt;[ ] Remove release-specific synthetic state and prove cleanup.&lt;/li&gt;
&lt;li&gt;[ ] State explicitly what the matrix did not test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Compatibility is a system property. The useful question is not “Did one request return&lt;br&gt;
200?” It is “Can every advertised route complete its declared contract, settle once,&lt;br&gt;
fail safely, and leave no release-specific test state behind?”&lt;/p&gt;

</description>
      <category>api</category>
      <category>testing</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
