OpenAI SDK
Written for openai 3.22.1 (Python) and 7.25.0 (npm), from their documentation and source. Both SDKs work against zdr.sh by changing the base URL to https://api.zdr.sh/v1; the key is a duck- key.
Python#
python
reasoning_content is not a declared field, so read it with getattr or through model_extra. Not every chunk carries it.
JavaScript and TypeScript#
typescript
Use the raw chunk loop rather than the .stream() helper: the helper copies unknown delta fields onto a snapshot and overwrites instead of appending, so reasoning_content would keep only the last fragment.
Migrating an OpenAI codebase#
What works with no change beyond the base URL and the key:
chat.completions.create, streaming and not, withmessages,tools,tool_choice,temperature,top_p,stop,response_format,max_tokensandmax_completion_tokensreasoning_efforton the models that publish efforts, andreasoning_contenton the answersusage, withprompt_tokens_details.cached_tokensandcompletion_tokens_details.reasoning_tokensfilled inresponses.create, withinput,instructions,tools,reasoningandtext.format; the endpoint is stateless, so keep sending the conversationmodels.list, and the errors, which arrive as the SDK's own error type with the zdr.sh code and message
What differs:
- The model ids are the zdr.sh ones, and
models.listis the source of truth. A key can map a name your code already sends to a model here, in its settings. Models lists the ids. - Hosted tools (
web_search,file_search,code_interpreter) are dropped from a request, withx-token-noticenaming them; the model answers without them. - A
previous_response_id, abackground: trueor a stored prompt is a 400stateless_only. - There are no audio or file APIs, and no batch or fine-tuning endpoints. Embeddings are served at
POST /v1/embeddings: Embeddings. Idempotency-Keymakes a retry bill once. Chat completions has the rules.x-token-costis extra, and a trailer on a stream.
Notes#
- Hard-code the base URL. Both SDKs fall back to the vendor when the variable is unset, and an empty string counts as unset.
- Environment route.
OPENAI_BASE_URL=https://api.zdr.sh/v1withOPENAI_API_KEY=duck-...works too, for code you would rather not touch. - Extra fields. Undocumented request fields travel as-is:
extra_bodyandextra_headersin Python, or thebodyandheadersoptions in Node. - Counting. The input counters are on the other two formats, Responses and Messages.
- Errors. A non-2xx answer raises the SDK's own error type with our code and message; Errors lists the classes.