Azure OpenAI
Trace Azure-hosted OpenAI deployments in Python and TypeScript.
Azure OpenAI is accessed through the OpenAI SDK's AzureOpenAI client. Wrapping it traces chat completions and the Responses API with provider="azure". In Python, embeddings, images, audio, and moderations are traced as well; the TypeScript wrapper covers chat completions and the Responses API.
Prerequisites
pip install -U "neatlogs[openai]" openaiUsage
import os
import neatlogs
neatlogs.init(api_key=os.environ["NEATLOGS_API_KEY"], workflow_name="azure-demo")
from openai import AzureOpenAI
client = neatlogs.wrap(
AzureOpenAI(
api_key=os.environ["AZURE_OPENAI_API_KEY"],
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
api_version="2024-10-21",
)
)
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "In one sentence, what is Azure OpenAI?"}],
)
print(resp.choices[0].message.content)
neatlogs.flush()
neatlogs.shutdown()wrap() opens a WORKFLOW root for you, so this renders with no extra wrapper. Streaming is captured too — the wrapper sets stream_options.include_usage so token counts are recorded even for streamed responses.
Making several calls in one run? Group them into one trace with a
WORKFLOWroot.
Sessions & end-user
Say your Azure OpenAI deployment is powering a back-and-forth chat and you want every reply in that conversation to land under one session, tied to the person you're talking to. Just wrap the turn in identify() — since neatlogs.wrap() already opens the trace root, that one call is enough for the Azure call to pick up the session and end-user:
# Same session_id every turn → one session; end_user_id attributes it to your user.
with neatlogs.identify(session_id=f"conv_{conversation_id}", end_user_id=user_id):
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": message}],
)Just keep handing back the same session_id on each turn and they'll all thread together.
Not every run is a conversation. A standalone workflow — a one-off job, not a back-and-forth — is a single trace with no turns to group. You still attribute it to the customer it ran for, sourcing the id and any metadata from your own user or request object:
A standalone workflow must not carry a session_id. Omit it entirely — Neatlogs sets session_id = trace_id, so the run is its own single-turn session, fully attributed to the end-user. Reusing one session_id across unrelated runs wrongly folds them into a single multi-turn conversation.
def summarize(document, user):
# Standalone workflow — attribute to the end-user; do NOT pass session_id.
with neatlogs.identify(
end_user_id=str(user.id),
end_user_metadata={"plan": user.plan},
):
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": f"Summarize:\n{document}"}],
)
return resp.choices[0].message.contentFor the whole picture, dig into Sessions and End-User Identity.
