<?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: Rust</title>
    <description>The latest articles on DEV Community by Rust (@fortunto2).</description>
    <link>https://dev.to/fortunto2</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3842127%2Fac119db4-1474-49ee-b70f-11d989978599.jpeg</url>
      <title>DEV Community: Rust</title>
      <link>https://dev.to/fortunto2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/fortunto2"/>
    <language>en</language>
    <item>
      <title>I ported the OpenAI Python SDK to Rust in 5 days with Claude Code. Here's what I learned.</title>
      <dc:creator>Rust</dc:creator>
      <pubDate>Tue, 24 Mar 2026 19:43:42 +0000</pubDate>
      <link>https://dev.to/fortunto2/squeezing-every-millisecond-from-the-openai-api-in-rust-4b11</link>
      <guid>https://dev.to/fortunto2/squeezing-every-millisecond-from-the-openai-api-in-rust-4b11</guid>
      <description>&lt;p&gt;I needed a fast OpenAI client for a realtime voice agent project. The official Python SDK is great, but I needed Rust for WebSocket audio streaming, edge deployment, and sub-second latency in agentic loops.&lt;/p&gt;

&lt;p&gt;So I ported it. 500+ commits, 5 days for the initial version, 100+ API methods. Day one (120 commits) was mostly Claude Code translating types from Python to Rust while I set up pre-commit hooks, WASM checks, and benchmarks. The rest was architecture decisions, performance tuning, Node/Python bindings, and a standalone types crate with 1100+ auto-synced types.&lt;/p&gt;

&lt;p&gt;The result: &lt;a href="https://github.com/fortunto2/openai-oxide" rel="noopener noreferrer"&gt;openai-oxide&lt;/a&gt;, a Rust client that matches the official Python SDK's API surface, with persistent WebSockets, structured outputs, and WASM deployment that aren't available in other Rust clients.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Just Use What Exists?
&lt;/h2&gt;

&lt;p&gt;My goal was a Rust client with complete 1:1 parity with the official Python SDK. All endpoints, plus WASM deployment, persistent WebSockets for the Responses API, and structured outputs with auto-generated schemas.&lt;/p&gt;

&lt;p&gt;The types and HTTP layer were ported from the Python SDK. But OpenAI also has a &lt;a href="https://platform.openai.com/docs/guides/websocket-mode" rel="noopener noreferrer"&gt;WebSocket mode&lt;/a&gt; for the Responses API, a server-side feature at &lt;code&gt;wss://api.openai.com/v1/responses&lt;/code&gt; where you keep one persistent connection open for multi-turn agent loops. The endpoint exists and is documented, but the official Python and Node SDKs haven't added a convenience wrapper for it yet (their WebSocket support covers only the Realtime API for audio/multimodal). We implemented the client for this endpoint directly from the OpenAI docs.&lt;/p&gt;

&lt;p&gt;In the Rust ecosystem, &lt;a href="https://crates.io/crates/async-openai" rel="noopener noreferrer"&gt;async-openai&lt;/a&gt; is the closest. Good type coverage and active maintenance. I actually found it after I'd mostly finished the initial version. But at the time of building, no single Rust crate offered WebSocket sessions for the Responses API, &lt;code&gt;parse::&amp;lt;T&amp;gt;()&lt;/code&gt; with auto-generated JSON schema, and WASM compilation together. That's the gap we filled.&lt;/p&gt;

&lt;h2&gt;
  
  
  1100+ Types, Auto-Synced from Python SDK
&lt;/h2&gt;

&lt;p&gt;Type coverage was the hardest part. OpenAI's API surface spans 24 domains with hundreds of nested types that change regularly. We solved this by building &lt;a href="https://crates.io/crates/openai-types" rel="noopener noreferrer"&gt;openai-types&lt;/a&gt;, a standalone crate auto-generated from the Python SDK via a custom &lt;code&gt;py2rust.py&lt;/code&gt; tool.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make sync-types  &lt;span class="c"&gt;# re-generates from ~/openai-python/src/openai/types/&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mechanism: &lt;code&gt;_gen.rs&lt;/code&gt; files are machine-owned (overwritten on every sync), while manual &lt;code&gt;.rs&lt;/code&gt; files contain hand-crafted overrides (enums, builders, Option fields) that are never touched. This gives us Python SDK parity on types without manual maintenance. When OpenAI adds a new field, &lt;code&gt;py2rust&lt;/code&gt; picks it up automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;openai_types&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ChatCompletion&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;openai_types&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;responses&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ResponseCreateRequest&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;openai_types&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ReasoningEffort&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The types crate has zero runtime dependencies beyond &lt;code&gt;serde&lt;/code&gt; and can be used on its own if you're building your own HTTP layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Persistent WebSockets
&lt;/h2&gt;

&lt;p&gt;Keep one &lt;code&gt;wss://&lt;/code&gt; connection open for the entire agent cycle. Both HTTP and WebSocket reuse TCP+TLS connections (reqwest pools them), but the server caches context locally for WebSocket connections, keeping the previous response state in memory for faster continuations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.ws_session&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// All calls route through the same wss:// connection&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// execute tool, feed result back&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="nf"&gt;.close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our preliminary measurements (gpt-5.4, warm connections, n=5):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Plain text:&lt;/strong&gt; 710ms WS vs 1011ms HTTP (29% faster)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rapid-fire (5 calls):&lt;/strong&gt; 3227ms vs 5807ms (44% faster)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This aligns with &lt;a href="https://platform.openai.com/docs/guides/websocket-mode" rel="noopener noreferrer"&gt;OpenAI's own documentation&lt;/a&gt;: &lt;em&gt;"For rollouts with 20+ tool calls, we have seen up to roughly 40% faster end-to-end execution."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Our numbers are preliminary at n=5, but the direction matches OpenAI's published benchmarks.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Structured Outputs Without Boilerplate
&lt;/h2&gt;

&lt;p&gt;Every Rust OpenAI client supports &lt;code&gt;response_format: json_schema&lt;/code&gt;. But you have to build the schema by hand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Other clients: manual schema construction&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;schema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;json!&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"object"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;"answer"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="s"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="s"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"answer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"confidence"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="s"&gt;"additionalProperties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With openai-oxide, derive the schema from your types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Deserialize,&lt;/span&gt; &lt;span class="nd"&gt;JsonSchema)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Answer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.chat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.completions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="py"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Answer&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="py"&gt;.parsed&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.answer&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One derive, both directions. The same &lt;code&gt;#[derive(JsonSchema)]&lt;/code&gt; generates response schemas and tool parameter definitions. No manual JSON, no drift between types and schemas.&lt;/p&gt;

&lt;h2&gt;
  
  
  SSE Streaming
&lt;/h2&gt;

&lt;p&gt;Time-to-first-token matters for UX. Our SSE parser uses incremental buffered line extraction and sets standard anti-buffering headers that prevent reverse proxies from holding back chunks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Accept: text/event-stream
Cache-Control: no-cache
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without these, Cloudflare and nginx buffer streaming responses, adding 50-200ms to TTFT.&lt;/p&gt;

&lt;p&gt;On mock benchmarks (localhost, no network), SSE processing via our Node napi-rs bindings is 2.6x faster than the official JS SDK: 283µs vs 742µs for 114 real agent chunks (p&amp;lt;0.001). On live API calls, the difference is masked by 200ms+ network latency, but it compounds in agent loops with many streaming rounds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stream Helpers
&lt;/h2&gt;

&lt;p&gt;Raw SSE chunks require manual stitching: tracking content deltas, assembling tool call arguments by index, detecting completion. We provide typed events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.chat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.completions&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.create_stream_helper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stream&lt;/span&gt;&lt;span class="nf"&gt;.next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;ChatStreamEvent&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ContentDelta&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;delta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;snapshot&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;print!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{delta}"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// snapshot has full text so far&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nn"&gt;ChatStreamEvent&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ToolCallDone&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;execute_tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or just get the final result: &lt;code&gt;stream.get_final_completion().await?&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  WASM Support
&lt;/h2&gt;

&lt;p&gt;The entire client compiles to &lt;code&gt;wasm32-unknown-unknown&lt;/code&gt; and runs in any WASM environment (browsers, Cloudflare Workers, Deno, Dioxus, Leptos):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;openai-oxide&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.11"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;default-features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"chat"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"responses"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;worker&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.7"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Streaming, structured outputs, and JSON request retries work in WASM. Limitations: no multipart uploads, no streaming retries (yet). &lt;a href="https://cloudflare-worker-dioxus.nameless-sunset-8f24.workers.dev" rel="noopener noreferrer"&gt;Live demo&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  HTTP Tuning Defaults
&lt;/h2&gt;

&lt;p&gt;These are standard reqwest builder options, enabled by default in openai-oxide:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Optimization&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;gzip compression&lt;/td&gt;
&lt;td&gt;~30% smaller responses&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TCP_NODELAY&lt;/td&gt;
&lt;td&gt;Disables Nagle's algorithm&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP/2 keep-alive (20s ping)&lt;/td&gt;
&lt;td&gt;Prevents idle connection drops&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP/2 adaptive window&lt;/td&gt;
&lt;td&gt;Auto-tunes flow control&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connection pool (4/host)&lt;/td&gt;
&lt;td&gt;Better parallel throughput&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Will these make your API calls faster? Probably not. Server-side latency dominates. But they prevent edge cases (stale connections, buffering delays) that bite you in production. &lt;a href="https://github.com/fortunto2/openai-oxide/blob/main/src/client.rs#L85" rel="noopener noreferrer"&gt;Source&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Benchmarks — What's Real and What's Noise
&lt;/h2&gt;

&lt;p&gt;After many rounds of benchmarking: &lt;strong&gt;on today's API latencies, SDK choice doesn't matter for single calls.&lt;/strong&gt; Network latency (200ms-2s) dwarfs SDK overhead (0.1-5ms). At n=5, differences under 15% are API jitter.&lt;/p&gt;

&lt;h3&gt;
  
  
  Live API (gpt-5.4) — honest results
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Rust ecosystem&lt;/strong&gt; (n=5, median of 3 runs):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;openai-oxide&lt;/th&gt;
&lt;th&gt;async-openai&lt;/th&gt;
&lt;th&gt;genai&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Plain text&lt;/td&gt;
&lt;td&gt;1011ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;960ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;835ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;oxide slower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Function calling&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1192ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1748ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1030ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;genai fastest&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streaming TTFT&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;645ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;685ms&lt;/td&gt;
&lt;td&gt;670ms&lt;/td&gt;
&lt;td&gt;within noise&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;No single SDK consistently wins at n=5. oxide takes function calling and streaming, genai wins plain text (it skips full deserialization).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node.js&lt;/strong&gt; (n=5, median of 3 runs):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;openai-oxide&lt;/th&gt;
&lt;th&gt;official openai&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Plain text&lt;/td&gt;
&lt;td&gt;1075ms&lt;/td&gt;
&lt;td&gt;1311ms&lt;/td&gt;
&lt;td&gt;-18%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured output&lt;/td&gt;
&lt;td&gt;1370ms&lt;/td&gt;
&lt;td&gt;1765ms&lt;/td&gt;
&lt;td&gt;-22%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-turn (2 reqs)&lt;/td&gt;
&lt;td&gt;2283ms&lt;/td&gt;
&lt;td&gt;2859ms&lt;/td&gt;
&lt;td&gt;-20%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Streaming TTFT&lt;/td&gt;
&lt;td&gt;534ms&lt;/td&gt;
&lt;td&gt;580ms&lt;/td&gt;
&lt;td&gt;within noise&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Python&lt;/strong&gt; (n=5, median of 3 runs):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;openai-oxide&lt;/th&gt;
&lt;th&gt;official openai&lt;/th&gt;
&lt;th&gt;Note&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Multi-turn (2 reqs)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2260ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3089ms&lt;/td&gt;
&lt;td&gt;+27%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prompt-cached&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;4425ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;5564ms&lt;/td&gt;
&lt;td&gt;+20%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Plain text&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;845ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;997ms&lt;/td&gt;
&lt;td&gt;+15%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured output&lt;/td&gt;
&lt;td&gt;1367ms&lt;/td&gt;
&lt;td&gt;1379ms&lt;/td&gt;
&lt;td&gt;within noise&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  SDK overhead — where oxide actually shines
&lt;/h3&gt;

&lt;p&gt;The interesting part is pure SDK overhead, isolated with a localhost mock server. No network, no model inference. Just request building, JSON serialization, response parsing, SSE chunk processing. Fixtures from a real coding agent session (320 messages, 42 tools, 718KB request body).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test&lt;/th&gt;
&lt;th&gt;openai-oxide&lt;/th&gt;
&lt;th&gt;official JS SDK&lt;/th&gt;
&lt;th&gt;oxide faster&lt;/th&gt;
&lt;th&gt;sig&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tiny req → Tiny resp&lt;/td&gt;
&lt;td&gt;172µs&lt;/td&gt;
&lt;td&gt;443µs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+61%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;***&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Heavy 657KB → Real resp&lt;/td&gt;
&lt;td&gt;4.9ms&lt;/td&gt;
&lt;td&gt;6.2ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+21%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;***&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SSE stream (114 chunks)&lt;/td&gt;
&lt;td&gt;283µs&lt;/td&gt;
&lt;td&gt;742µs&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+62%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;***&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent 20x sequential&lt;/td&gt;
&lt;td&gt;2.1ms&lt;/td&gt;
&lt;td&gt;5.4ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+61%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;***&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;50 iterations, 20 warmup, Welch's t-test — all p&amp;lt;0.001.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What this means today:&lt;/strong&gt; on OpenAI's API (200ms-2s), SDK overhead is &amp;lt;1% of wall time. But the picture changes with fast inference providers (Cerebras, Groq, local models returning in 10-50ms) and agent farms running hundreds of parallel sessions. At those speeds, SDK overhead becomes 5-30% of wall time, and the 2-3x gap compounds.&lt;/p&gt;

&lt;p&gt;The value right now is &lt;strong&gt;API completeness&lt;/strong&gt; (WebSocket with connection pool, structured outputs, WASM, stream helpers), &lt;strong&gt;type safety&lt;/strong&gt; (1100+ auto-synced types), and the trajectory: as APIs get faster, the Rust overhead advantage grows.&lt;/p&gt;

&lt;p&gt;Full reproducible benchmarks: &lt;code&gt;node --expose-gc benchmarks/bench_science.js&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Drop-in Replacement
&lt;/h2&gt;

&lt;p&gt;For existing codebases, change one import:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Python:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# from openai import AsyncOpenAI
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;openai_oxide.compat&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;AsyncOpenAI&lt;/span&gt;

&lt;span class="c1"&gt;# rest of code unchanged
&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AsyncOpenAI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;completions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gpt-5.4-mini&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[...])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Node.js:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// const OpenAI = require('openai');&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;OpenAI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai-oxide/compat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// rest of code unchanged&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OpenAI&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How This Was Built
&lt;/h2&gt;

&lt;p&gt;This started as a need for a fast OpenAI client for a realtime TTS voice agent project. The Python SDK worked, but I needed Rust for WebSocket audio streaming and edge deployment.&lt;/p&gt;

&lt;p&gt;The whole thing (100+ API methods, typed streaming, structured outputs, WASM, Node/Python bindings) was built in a few days using &lt;a href="https://claude.ai/claude-code" rel="noopener noreferrer"&gt;Claude Code&lt;/a&gt; and my own toolkit:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Setup&lt;/strong&gt;: configured pre-commit hooks (tests, clippy, WASM check, secret scan), OpenAPI spec as ground truth, Python SDK source as reference&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Planning&lt;/strong&gt;: &lt;a href="https://github.com/fortunto2/solo-factory" rel="noopener noreferrer"&gt;solo-factory&lt;/a&gt; skills (&lt;code&gt;/plan&lt;/code&gt;, &lt;code&gt;/build&lt;/code&gt;) with &lt;a href="https://github.com/fortunto2/solograph" rel="noopener noreferrer"&gt;solograph&lt;/a&gt; for code intelligence (MCP server that indexes the codebase and provides semantic search)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Building&lt;/strong&gt;: initial scaffold via Ralph Loop (autonomous agent loop), then manual refinement. Architecture decisions, API design, performance tuning.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type sync&lt;/strong&gt;: built &lt;code&gt;py2rust.py&lt;/code&gt; to auto-convert Python Pydantic models to Rust serde structs. 1100+ types across 24 domains, two-pass resolver for cross-file references.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quality gates&lt;/strong&gt;: every commit runs tests + clippy + WASM compilation check + doc coverage. Pre-commit catches regressions before they land&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The key insight: treat the Python SDK as a spec, not as code to port line-by-line. The agent handles mechanical translation (types, error mapping, serialization); you focus on Rust-specific wins (tagged enums, feature gates, WASM cfg).&lt;/p&gt;

&lt;p&gt;A harder lesson: &lt;strong&gt;benchmarks are treacherous.&lt;/strong&gt; We went through multiple rounds removing claims that weren't statistically significant at n=5. The real story is not about milliseconds on single requests. It's about what happens at scale: structured outputs with schema generation on every call, hundreds of parallel agent sessions, function calling chains with 20+ tool invocations. That's where Rust's lack of GC pauses and lower per-call overhead start to compound.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Crate, Every Platform
&lt;/h2&gt;

&lt;p&gt;The biggest payoff from writing the core in Rust: it runs everywhere.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Platform&lt;/th&gt;
&lt;th&gt;Binding&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rust&lt;/td&gt;
&lt;td&gt;native&lt;/td&gt;
&lt;td&gt;stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js / TypeScript&lt;/td&gt;
&lt;td&gt;napi-rs&lt;/td&gt;
&lt;td&gt;stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python&lt;/td&gt;
&lt;td&gt;PyO3 + maturin&lt;/td&gt;
&lt;td&gt;stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser / Edge / Dioxus / Leptos&lt;/td&gt;
&lt;td&gt;WASM&lt;/td&gt;
&lt;td&gt;stable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iOS / macOS&lt;/td&gt;
&lt;td&gt;UniFFI (Swift)&lt;/td&gt;
&lt;td&gt;planned&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android&lt;/td&gt;
&lt;td&gt;UniFFI (Kotlin)&lt;/td&gt;
&lt;td&gt;planned&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same HTTP tuning, WebSocket pool, streaming parser, and retry logic on every platform. No reimplementation, no behavior drift.&lt;/p&gt;

&lt;p&gt;This also means the crate works as &lt;strong&gt;agent infrastructure&lt;/strong&gt;. &lt;a href="https://github.com/fortunto2/rust-code/tree/master/crates/sgr-agent" rel="noopener noreferrer"&gt;sgr-agent&lt;/a&gt; is an LLM agent framework built on openai-oxide that runs as a &lt;a href="https://github.com/fortunto2/rust-code" rel="noopener noreferrer"&gt;TUI coding agent&lt;/a&gt; today and can compile to WASM for browser-based agents tomorrow. The same agent code, the same OpenAI layer, different targets.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo add openai-oxide tokio &lt;span class="nt"&gt;--features&lt;/span&gt; tokio/full
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/fortunto2/openai-oxide" rel="noopener noreferrer"&gt;fortunto2/openai-oxide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;crates.io: &lt;a href="https://crates.io/crates/openai-oxide" rel="noopener noreferrer"&gt;openai-oxide&lt;/a&gt; + &lt;a href="https://crates.io/crates/openai-types" rel="noopener noreferrer"&gt;openai-types&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/openai-oxide" rel="noopener noreferrer"&gt;openai-oxide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PyPI: &lt;a href="https://pypi.org/project/openai-oxide/" rel="noopener noreferrer"&gt;openai-oxide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docs: &lt;a href="https://fortunto2.github.io/openai-oxide/" rel="noopener noreferrer"&gt;fortunto2.github.io/openai-oxide&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>openai</category>
      <category>ai</category>
      <category>webassembly</category>
    </item>
  </channel>
</rss>
