<?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: Triumph</title>
    <description>The latest articles on DEV Community by Triumph (@triumph1701).</description>
    <link>https://dev.to/triumph1701</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%2F4059908%2Fbcad935f-da0d-4aaf-b1b8-c654432454a1.png</url>
      <title>DEV Community: Triumph</title>
      <link>https://dev.to/triumph1701</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/triumph1701"/>
    <language>en</language>
    <item>
      <title>OpenAI-Compatible Is Not Agent-Compatible: A Practical Smoke Test</title>
      <dc:creator>Triumph</dc:creator>
      <pubDate>Mon, 03 Aug 2026 07:14:08 +0000</pubDate>
      <link>https://dev.to/triumph1701/openai-compatible-is-not-agent-compatible-a-practical-smoke-test-3h4h</link>
      <guid>https://dev.to/triumph1701/openai-compatible-is-not-agent-compatible-a-practical-smoke-test-3h4h</guid>
      <description>&lt;p&gt;Most providers can pass a basic &lt;code&gt;POST /chat/completions&lt;/code&gt; request.&lt;br&gt;
That is useful, but it is not enough if your real workload is a coding agent.&lt;/p&gt;

&lt;p&gt;Agent loops stress more than transport:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;tool-call formatting&lt;/li&gt;
&lt;li&gt;streaming reliability&lt;/li&gt;
&lt;li&gt;retries after partial failure&lt;/li&gt;
&lt;li&gt;session handoff&lt;/li&gt;
&lt;li&gt;usage reporting&lt;/li&gt;
&lt;li&gt;timeout behavior&lt;/li&gt;
&lt;li&gt;billing traceability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This post is a practical smoke test you can run against any OpenAI-compatible API before you trust it for a coding agent.&lt;/p&gt;
&lt;h2&gt;
  
  
  1) Why &lt;code&gt;/chat/completions&lt;/code&gt; alone is not enough
&lt;/h2&gt;

&lt;p&gt;A request can succeed and still fail the job you actually care about.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the SDK call works, but tool calls are malformed&lt;/li&gt;
&lt;li&gt;streaming starts, but the stream breaks mid-response&lt;/li&gt;
&lt;li&gt;the provider returns content, but usage is missing&lt;/li&gt;
&lt;li&gt;retries duplicate a tool call or change the answer&lt;/li&gt;
&lt;li&gt;the API accepts the model name, but the agent loop cannot continue cleanly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your app uses Codex, OpenCode, Cursor, Claude Code, or a similar coding agent, you need to test the whole loop, not just one happy-path request.&lt;/p&gt;
&lt;h2&gt;
  
  
  2) Basic authentication and model list check
&lt;/h2&gt;

&lt;p&gt;Start by verifying the base URL and API key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;BASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/v1"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"sk-your-key"&lt;/span&gt;

curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/models"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | jq &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What to confirm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the request returns JSON&lt;/li&gt;
&lt;li&gt;the model list is readable&lt;/li&gt;
&lt;li&gt;the model IDs are the ones your SDK expects&lt;/li&gt;
&lt;li&gt;the response does not expose private metadata you did not ask for&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If &lt;code&gt;/models&lt;/code&gt; fails, stop there.&lt;/p&gt;

&lt;h2&gt;
  
  
  3) Ordinary chat completions test
&lt;/h2&gt;

&lt;p&gt;Use one small prompt and one known model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/chat/completions"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "your-model-id",
    "messages": [
      { "role": "user", "content": "Say hello in one short sentence." }
    ],
    "temperature": 0
  }'&lt;/span&gt; | jq &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP status is 200&lt;/li&gt;
&lt;li&gt;content is non-empty&lt;/li&gt;
&lt;li&gt;the response format matches your client&lt;/li&gt;
&lt;li&gt;usage is present when expected&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4) Streaming test
&lt;/h2&gt;

&lt;p&gt;Streaming is where many providers diverge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-N&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/chat/completions"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "your-model-id",
    "messages": [
      { "role": "user", "content": "Count from 1 to 5 slowly." }
    ],
    "stream": true
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the first chunk arrives promptly&lt;/li&gt;
&lt;li&gt;chunks keep flowing&lt;/li&gt;
&lt;li&gt;the stream ends cleanly&lt;/li&gt;
&lt;li&gt;the client does not hang waiting for the final marker&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5) Tool call test
&lt;/h2&gt;

&lt;p&gt;Agent compatibility usually depends on tool calls.&lt;/p&gt;

&lt;p&gt;Example request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/chat/completions"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "your-model-id",
    "messages": [
      {
        "role": "user",
        "content": "If you need a calculator, call the tool and return the result."
      }
    ],
    "tools": [
      {
        "type": "function",
        "function": {
          "name": "calculator",
          "description": "Add two numbers",
          "parameters": {
            "type": "object",
            "properties": {
              "a": { "type": "number" },
              "b": { "type": "number" }
            },
            "required": ["a", "b"]
          }
        }
      }
    ],
    "tool_choice": "auto"
  }'&lt;/span&gt; | jq &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the tool call is syntactically valid&lt;/li&gt;
&lt;li&gt;arguments are complete&lt;/li&gt;
&lt;li&gt;the tool call can be parsed by your client&lt;/li&gt;
&lt;li&gt;a retry does not duplicate the tool call unexpectedly&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6) Responses API compatibility check
&lt;/h2&gt;

&lt;p&gt;If your client uses the Responses API, test that path too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/responses"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "your-model-id",
    "input": "Give me a one-line summary of why agent testing is different from chat testing."
  }'&lt;/span&gt; | jq &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the route exists&lt;/li&gt;
&lt;li&gt;the response shape is valid for your client&lt;/li&gt;
&lt;li&gt;usage is present or documented&lt;/li&gt;
&lt;li&gt;streaming works if you enable it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7) Error codes and timeout behavior
&lt;/h2&gt;

&lt;p&gt;Good APIs are not only correct when happy.&lt;/p&gt;

&lt;p&gt;Test at least one failure path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /tmp/devto-smoke-error.json &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$BASE_URL&lt;/span&gt;&lt;span class="s2"&gt;/chat/completions"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="nv"&gt;$API_KEY&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
    "model": "missing-model",
    "messages": [
      { "role": "user", "content": "Hello" }
    ]
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;invalid model returns a clear error&lt;/li&gt;
&lt;li&gt;timeout behavior is documented or observable&lt;/li&gt;
&lt;li&gt;the client can distinguish retryable and non-retryable failures&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8) Usage, cache, and billing logs
&lt;/h2&gt;

&lt;p&gt;After a successful request, verify the accounting trail.&lt;/p&gt;

&lt;p&gt;Look for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;input tokens&lt;/li&gt;
&lt;li&gt;output tokens&lt;/li&gt;
&lt;li&gt;cached input or cache read tokens&lt;/li&gt;
&lt;li&gt;cache write tokens if supported&lt;/li&gt;
&lt;li&gt;request status&lt;/li&gt;
&lt;li&gt;model name&lt;/li&gt;
&lt;li&gt;request duration&lt;/li&gt;
&lt;li&gt;final cost&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main question is simple:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Can you explain the bill from the logs?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If not, the provider may still be usable for chat, but it is harder to trust for production agent work.&lt;/p&gt;

&lt;h2&gt;
  
  
  9) Copy-paste smoke test checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;[ ] &lt;code&gt;/models&lt;/code&gt; returns valid JSON&lt;/li&gt;
&lt;li&gt;[ ] basic chat request succeeds&lt;/li&gt;
&lt;li&gt;[ ] streaming request completes cleanly&lt;/li&gt;
&lt;li&gt;[ ] tool call is emitted correctly&lt;/li&gt;
&lt;li&gt;[ ] Responses API works if your client uses it&lt;/li&gt;
&lt;li&gt;[ ] invalid model returns a readable error&lt;/li&gt;
&lt;li&gt;[ ] timeout and retry behavior are understandable&lt;/li&gt;
&lt;li&gt;[ ] usage fields are present&lt;/li&gt;
&lt;li&gt;[ ] billing logs match the request&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  10) Final judgment table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Pass?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Basic compatible&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streaming compatible&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tool compatible&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent compatible&lt;/td&gt;
&lt;td&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If a provider only passes the first row, it may still be fine for chat.&lt;br&gt;
If it passes all four, it is much more plausible for a coding agent workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing note
&lt;/h2&gt;

&lt;p&gt;This article is about how to test the route, not about any one vendor.&lt;br&gt;
That matters because the gap between “OpenAI-compatible” and “agent-compatible” is often where the real integration risk lives.&lt;/p&gt;

&lt;p&gt;Disclosure: I’m building Your Model, an OpenAI-compatible multi-model API. The checklist above is based on the compatibility issues I look for when testing API routes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://y-models.com" rel="noopener noreferrer"&gt;https://y-models.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This article was prepared with AI-assisted research and editing, then reviewed before publication.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7e1ktdwisxp6k0plfjcr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7e1ktdwisxp6k0plfjcr.png" alt=" " width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>programming</category>
      <category>api</category>
    </item>
  </channel>
</rss>
