Build in Public · LF-09

Calling Gemini with the OpenAI SDK: From Minimal Text Requests to Rollback-Ready Migration

Isolate the compatibility boundaries of the SDK, base URL, and models. Validate non-streaming text requests, errors, and billing details before restoring parameters task by task, avoiding the assumption that client compatibility equals full service compatibility.

Product status update (2026-09-13 CST verification): The EveryInfra general text API was retired on 2026-09-12. This document preserves the original SDK, baseURL, and non-streaming examples to help understand historical clients and rollback boundaries, rather than serving as a new integration guide. Historical calls retain 410 ai_chat_retired compatibility; supported data processing should instead use data cleaning bound to personal EveryData sources, independent key scopes, and fixed recipes.

When migrating an application that already uses the OpenAI SDK, the easiest work to underestimate is not changing the address, but re-verifying which behaviors still hold true. A request successfully returning text only indicates that this input produced a result on this service; it does not simultaneously prove that previous prompt performance remains unchanged, streaming works, tool calls function correctly, or the accounting state is consistent after an exception.

Using the EveryInfra Gemini non-streaming text endpoint as an example, this document provides a migration sequence that can be verified item by item: lock configuration first, verify minimal requests next, check responses and errors, and finally restore business parameters and decide whether to expand usage. It is not a general compatibility promise to migrate all OpenAI features to another service.

Understanding the Compatibility Layer

First, distinguish between three objects: the OpenAI SDK is a client library; the service address determines the system actually receiving requests; and the model ID must belong to the catalog currently provided by that service. Using the same npm package does not imply using the same key, model name set, or service agreement.

Google's OpenAI compatibility layer documentation provides examples of accessing Google Gemini services using the OpenAI SDK. This document uses the EveryInfra address and credentials. Models and features appearing in Google documentation cannot automatically become an available checklist for EveryInfra; authentication and billing for both must be verified separately.

The OpenAI TypeScript SDK documentation explains Chat Completions call methods. The SDK also contains other APIs, but having a method on the client does not equal the target address implementing the corresponding route. In particular, do not assume migration is complete for legacy projects simply by replacing responses.create, file uploads, background tasks, or session management with baseURL.

Lock Configuration First to Avoid Crossing Credentials and Addresses

Split environments into at least three groups: development, acceptance, and production. For each group, explicitly define the service address, the service's key, models, timeouts, and permitted features. Do not change global environment variables in one place and let other code still connecting to legacy services accidentally carry new credentials.

This document fixes the base path for EveryInfra as https://api.everyinfra.com/api/v1, which is neither the console address nor the complete /chat/completions path. The SDK continues to append endpoints. Appending /v1 again or using a complete endpoint as the base path may send requests to the wrong location.

Keep keys strictly in the server-side environment, out of browser bundle variables, repositories, screenshots, or logs. There is no need to enable options allowing browser exposure of keys just to run this document. Use an explicit whitelist for logs as well, avoiding direct output of entire SDK error objects or request headers.

Log design can be cross-referenced with OWASP's sensitive information exclusion checklist: retain location-required state and associated identifiers while excluding tokens, session values, and unnecessary personal information. This maintains consistency more easily than relying on every caller to manually delete sensitive fields.

Check models using the public catalog first; this step does not require a business key:

Bash · copyable example
set -o pipefail
curl -fsS --max-time 30 'https://api.everyinfra.com/api/v1/models' \
  | jq -e '{
      default_model,
      models: [.data[].id]
    } | if (.models | length) > 0
      and (.default_model as $m | .models | index($m)) != null
    then . else error("invalid model catalog") end'

After selecting an ID matching requirements in the catalog, explicitly configure EVERYINFRA_MODEL_ID. Stop verification if catalog reading fails rather than silently falling back to an old name; default value changes should also be evaluated as configuration changes rather than quietly switching models across runs. The catalog only proves declarations and cannot verify whether the key is authorized to call the selected model.

Start with Minimal Non-Streaming Text Requests

The following requires a server-side Node.js environment and the project-locked openai package. The code serves as a single business call template; this document performs offline checks on it and does not refer to synthetic responses as online results. Do not upgrade entire project dependencies just to run the example; record the current SDK version and lock file first.

TypeScript · copyable example
import OpenAI from "openai";

const apiKey = process.env.EVERYINFRA_API_KEY?.trim();
const model = process.env.EVERYINFRA_MODEL_ID?.trim();
if (!apiKey || !model) throw new Error("Missing EveryInfra configuration");

const client = new OpenAI({
  apiKey,
  baseURL: "https://api.everyinfra.com/api/v1",
  maxRetries: 0,
  timeout: 120_000,
});

const { data, response: http } = await client.chat.completions.create({
  model,
  messages: [{ role: "user", content: "explain idempotency in one sentence." }],
  stream: false,
}).withResponse();

const choice = data.choices?.[0];
const text = choice?.message?.content;
if (typeof text !== "string" || !text.trim()) {
  throw new Error("No usable text; inspect this attempt before retrying");
}
console.log({
  httpStatus: http.status,
  model: data.model,
  finishReason: choice.finish_reason,
  requestId: http.headers.get("x-request-id"),
  textReceived: true,
});
// hands the text text hand it to the business layer; do not write the full generated content to runtime logs by default。

The 120 seconds here represent a client-side wait budget used for demonstration, not a service response time limit or performance commitment. Applications must also coordinate wait times across entry proxies, task executors, and user interfaces. Receiving service completion information after a client timeout is an unknown result requiring verification, not an automatic determination that execution did not occur.

Disable automatic retries during initial acceptance to observe a single call. The OpenAI SDK includes built-in retry mechanisms for certain connection and service errors; before restoring retries in production, clarify the target service's conventions for duplicate requests, billing deductions, and idempotency rather than blindly reusing SDK defaults. SDK Retry and Timeout Guide

After Non-Streaming Passes, Do Not Directly Switch to stream: true

The current EveryInfra implementation processes chat requests as non-streaming. Writing stream: true in code or Google's official compatibility layer supporting streaming does not prove this address returns event streams meeting SDK expectations. This document explicitly retains stream: false and avoids connecting streaming UI to unverified paths.

If the business relies on character-by-character display, list it as an unmet migration condition first. Once a clear contract is established, separately check response types, event orders, end markers, interruption recovery, and final usage. Do not use a regular JSON success response to impersonate streaming acceptance, nor slice text on the frontend and call it a server-side stream.

Tool calls, structured outputs, multimodal inputs, reasoning controls, and other optional parameters should be confirmed separately. Client type definitions allowing a field only indicate it can be serialized; this is different from the target service understanding and executing the field as expected.

Responses Must Answer Three Questions Simultaneously

First, whether content meeting business requirements was obtained. Non-empty text may still drift off-topic, violate output formats, or fail to terminate completely due to length limits. Retain finish_reason, check the task's own completion criteria, and do not equate HTTP 200 with user task success.

Second, which data can be safely passed to downstream components. Handle missing values and type discrepancies when reading choices and usage; business logic requiring JSON must parse and validate structures independently. Do not trust third-party runtime responses to match static declarations simply because TypeScript compilation succeeds.

Third, what the cost and status of this request are. Extended billing information for EveryInfra does not belong to all SDK generic types and should be verified separately against service documentation; do not pad missing fields to zero, nor treat token usage directly as wallet charges. Saving model outputs, request identifiers, and billing associations separately helps explain situations like content failure succeeding over HTTP. EveryInfra API and Billing Documentation

Official SDK documentation states that extra response properties are not automatically removed just because they are not written into static types. Runtime checks should be performed when these fields are needed rather than using as any to skip validation; .withResponse() can obtain both parsed data and HTTP information simultaneously, but cannot manufacture request IDs not returned by the service. SDK Extended Responses and HTTP Information

When defining runtime schemas for extended fields, refer to the JSON Schema object guidelines to describe required, optional, and permitted value types separately. Passing structural checks still does not prove billing meanings or model conclusions are correct; business semantics must be verified separately.

Locate Errors First, Then Decide Whether to Retry

Unknown models and service failures should not follow the same handling branch. Referring to the deployed source code baseline of 2026-09-04, in REST requests with complete messages like this one, identify the API key first and then check the model; unknown models produce 422 unknown_model before billing occurs. Requests lacking messages are rejected by input checks first, and this order cannot be generalized to all error combinations. This is a source code check, not an observation of real authorization requests in this round.

Upon receiving unknown_model, refresh the catalog and correct configurations first; do not endlessly retry the same error name, nor automatically accept similar candidate models, as switching models may alter behavior. 401 checks target addresses and keys; hand insufficient permissions to permission owners and insufficient quotas to account owners. Applications should not switch to another key on their own to bypass limits.

Network anomalies split into two categories: failures where requests clearly did not complete, and timeouts or disconnections where requests were sent but final outcomes cannot be confirmed. Clients generally cannot determine whether charges occurred based solely on exception classes. Save business operation numbers, timestamps, actual models, and available request IDs for every attempt; verify this attempt before deciding to initiate a new request.

What Offline Checks Can and Cannot Verify

Offline testing can replace the SDK's fetch, checking final URLs, POST methods, authentication header shapes, and request bodies before returning explicitly marked synthetic responses. It is useful for uncovering duplicate paths, parameter typos, parsing assumptions, and error branch issues without sending real prompts or consuming account quotas.

The original fixed openai package version 7.10.0 isolation tests covered text, usage, and simulated 422; this round explicitly records versions and test scopes without calling them latest versions or service compatibility certifications. New minimal templates must also verify that missing configurations do not trigger requests, single calls do not automatically retry, and blank content is preserved as results requiring review.

The 2026-09-02 archives contain additional Python SDK non-production samples. They support Python requests from that era but do not prove today's TypeScript template has completed real integration. This round adds no new real TS business calls; authentication, daily text quality, and billing results still require authorized sample verification.

Restore Business Parameters by Task Sample

Once minimal requests pass, restore one feature class at a time: actual prompts and context first, output constraints second, and finally options genuinely required by the application and declared supported by the service. Save a minimally reproducible input for troubleshooting comparison instead of importing all parameters from legacy projects at once.

Task samples should include common successful inputs, empty inputs, long contexts, ambiguous questions, and business-required formats. Evaluate task completion, format parsability, and factual grounding; do not use word-for-word matching as the sole success standard for generative models. Any quality percentages should state samples and human evaluation criteria clearly, avoiding generalized gains derived from casual testing.

If real requests succeed but business outputs degrade, isolate whether it stems from prompts, models, input truncation, or parsing rules; do not attribute all discrepancies to the SDK. Conversely, when HTTP calls fail entirely, troubleshoot paths, authentication, and service status first, as adjusting prompts usually will not resolve connection issues.

Keep Migration Reversible Without Automatically Repeating Business Actions

The business calling layer can maintain explicit configurations for service addresses, models, and versions, recording which group is used for each run; save the previously verified configuration before switching. The goal of rollback is restoring controllable states for subsequent requests, not automatically resending unknown-result requests to another service.

Before expanding usage formally, confirm that an authorized real text path, a set of business samples, error handling, and billing correlation can all be reviewed. Leave streaming or tool features in the unmigrated checklist if no verification evidence exists yet. This ensures "migration complete" corresponds to specific tasks and features rather than simply changing three lines of client configuration.