SOLWYN
Providers

OpenAI

Use Solwyn with OpenAI — sync and async examples, token details, model support

import os
from openai import OpenAI
from solwyn import Solwyn

client = Solwyn(
    OpenAI(),
    api_key=os.environ["SOLWYN_API_KEY"],
)

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Explain how a CPU works in two sentences."}],
)

print(response.choices[0].message.content)
client.close()

Sync usage

Pass an openai.OpenAI client to Solwyn(). Calls use client.chat.completions.create(), exactly like the standard OpenAI SDK:

import os
from openai import OpenAI
from solwyn import Solwyn

with Solwyn(
    OpenAI(),
    api_key=os.environ["SOLWYN_API_KEY"],
) as client:
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "Explain how a CPU works in two sentences."},
        ],
    )
    print(response.choices[0].message.content)

Async usage

Pass an openai.AsyncOpenAI client to AsyncSolwyn():

import asyncio
import os
from openai import AsyncOpenAI
from solwyn import AsyncSolwyn

async def main():
    async with AsyncSolwyn(
        AsyncOpenAI(),
        api_key=os.environ["SOLWYN_API_KEY"],
    ) as client:
        response = await client.chat.completions.create(
            model="gpt-4o",
            messages=[
                {"role": "system", "content": "You are a helpful assistant."},
                {"role": "user", "content": "Explain how a CPU works in two sentences."},
            ],
        )
        print(response.choices[0].message.content)

asyncio.run(main())

Supported models

The SDK recognizes OpenAI models by the following prefixes:

PrefixExamples
gpt-*gpt-4o, gpt-4o-mini, gpt-4.1, gpt-5
o3*o3, o3-mini
o4*o4-mini

Model detection is used for token estimation before calls. After each call, the SDK reads exact token counts from the provider response regardless of model prefix.

OpenAI is priced for all five modalities — text, embedding, image, audio, and video. See the provider × modality matrix.

Token detail fields

The SDK extracts the following fields from OpenAI responses:

Normalized fieldChat Completions API sourceResponses API source
input_tokensusage.prompt_tokensusage.input_tokens
output_tokensusage.completion_tokensusage.output_tokens
cached_input_tokensusage.prompt_tokens_details.cached_tokensusage.input_tokens_details.cached_tokens
reasoning_tokensusage.completion_tokens_details.reasoning_tokensusage.output_tokens_details.reasoning_tokens
audio_input_tokensusage.prompt_tokens_details.audio_tokens--
audio_output_tokensusage.completion_tokens_details.audio_tokens--
accepted_prediction_tokensusage.completion_tokens_details.accepted_prediction_tokens--
rejected_prediction_tokensusage.completion_tokens_details.rejected_prediction_tokens--
cache_creation_5m_tokensusage.prompt_tokens_details.cache_write_tokensusage.input_tokens_details.cache_write_tokens
cache_creation_1h_tokens----

The SDK handles both the Chat Completions API response shape (prompt_tokens / completion_tokens) and the Responses API shape (input_tokens / output_tokens) automatically. Detail sub-objects may be None on older responses -- all missing fields default to 0.

New in 0.3.0: the SDK now extracts prompt-cache writes from cache_write_tokens and reports them as cache_creation_5m_tokens. OpenAI's cache writes carry a 30-minute default/minimum TTL, not 5 minutes — the field name is a wire-contract artifact that maps OpenAI's cache writes onto the existing cache_creation_5m_tokens bucket to preserve the cross-provider contract, not a claim about OpenAI's TTL. cache_creation_1h_tokens always reports 0 for OpenAI. Cache-write tracking is independent of cache reads (cached_input_tokens, unchanged above) and requires no opt-in or configuration. Missing or unusable values (wrong type, negative, boolean, string) degrade to 0 rather than raising.

Service tier

When OpenAI returns a service_tier on the response, Solwyn captures it (bounded and string-only) and reports it on the metadata event's service_tier field, so the dashboard can distinguish standard, flex, and priority traffic. It is absent when the provider does not return one. See Privacy.

Image generation

client.images.generate(...) and client.images.edit(...) are intercepted. The pre-flight budget check is exact — computed from the request's image count, size, and quality — so an over-budget request is denied before OpenAI is called. gpt-image models bill text and image tokens at separate rates and return image-token usage on the response, which the SDK reports as image_input_tokens / image_output_tokens. Each call is recorded as a cost event with modality image and bounded media details — image count, resolution, and quality — never the prompt and never the returned image bytes.

Audio

Both audio surfaces are intercepted, budget-checked, and recorded as cost events with modality audio.

Transcriptionclient.audio.transcriptions.create(...) is intercepted. Token-billed transcription models (the gpt-4o-transcribe family) price from their usage token buckets, with audio input tokens reported separately as audio_input_tokens. whisper-1 prices from the whole-second audio duration the provider reports on its usage block — present whenever you request a JSON response_format (json or verbose_json). A transcription made with a non-JSON response_format (text, srt, or vtt) returns no usage, so the call is recorded but left unpriced, and the SDK warns once suggesting a JSON response_format.

Speech (TTS)client.audio.speech.create(...) is intercepted. tts-1 and tts-1-hd price exactly from the input's character count, measured inside the privacy firewall — the input text never leaves your process — so the pre-flight budget check is exact (characters × the model's per-character rate) and an over-budget call is denied before OpenAI is reached. Token-billed TTS models (gpt-4o-mini-tts) return no usage metadata of any kind, so they are deliberately not tracked: the SDK warns once and passes the call through untracked — never a fabricated $0 or an estimate.

client.audio.translations is recognized but untracked — it warns once and passes through.

Only token counts, whole-second durations, and character counts leave your process — never audio bytes, never the input text, never the transcript.

Video

client.videos.create(...) (Sora) is intercepted, budget-checked, and recorded as a cost event with modality video. The pre-flight budget check is exact — the request's seconds at the resolution derived from size, priced at that resolution variant's per-second rate — so an over-budget generation is denied before OpenAI is called. OpenAI's documented defaults apply when omitted: seconds defaults to 4 and size to 720x1280.

Video generation is asynchronous. videos.create returns a video job that carries no usage, and Solwyn passes that job through untouched — you poll or retrieve it as usual. Because the job offers no billable basis and OpenAI does not charge for failed or blocked generations, billing settles at initiation: the cost event is recorded at request time from the requested duration and resolution and is always flagged is_estimated, a deliberate and conservative over-count. Every other client.videos attribute (retrieve, list, download_content, ...) passes through to the underlying client.

Only the requested duration and the resolution selector leave your process — never the prompt, never a reference image, never the generated video.

The Sora price cards are lifecycle-deprecated in Solwyn's pricing dataset but still bill: historical and present traffic on those ids prices normally, never rejected as an unknown model.

tiktoken extra

For accurate pre-call token estimation with OpenAI models, install the tiktoken extra:

pip install "solwyn[openai]"

Without tiktoken, the SDK uses a heuristic estimator (approximately 4 characters per token). This is sufficient for budget enforcement but less precise. Post-call token counts are always exact regardless of this extra, since they come directly from the OpenAI response.

Passthrough attributes

Any attribute not intercepted by Solwyn is passed through to the underlying openai.OpenAI client. For example, you can still access client.models.list() or any other OpenAI API method directly.

On this page