Repository navigation
feat(examples): Databricks recipe on traceai-openai — model services and serving endpoints (TH-8316) - #237
Open
nik13 wants to merge 5 commits into
Open
feat(examples): Databricks recipe on traceai-openai — model services and serving endpoints (TH-8316)#237nik13 wants to merge 5 commits into
nik13 wants to merge 5 commits into
Conversation
The Node OTLP exporter streams with Transfer-Encoding: chunked and sends no Content-Length. The Receiver read Content-Length only, so every Node export came back 400 with zero spans; two TH-8103 children had to put a de-chunking relay in front of it. Receiver now accepts both framings. It also records one entry per accepted export (path, lower-cased headers, flattened resource attributes) via requests(), so a contract test can assert X-Api-Key/X-Secret-Key and project_name through the shared harness instead of a private recorder. Verified: 6 harness tests pass, and the TanStack example's real Node exporter delivered 4 chunked exports (4 spans, collector path, both auth headers, project_name/project_type) with no relay. Refs: TH-8339, TH-8103
…nd serving endpoints) Adds python/examples/databricks/: the official openai SDK pointed at a Databricks workspace, traced by the existing traceai-openai instrumentor. No provider package. - Two documented surfaces, kept separate: Unity Gateway model services (https://<workspace-host>/ai-gateway/mlflow/v1) and Model Serving endpoints (https://<workspace-host>/serving-endpoints). model is the model service or endpoint name. - DATABRICKS_BASE_URL is required (no public default). Placeholders (literal or percent-encoded) and Databricks' sample host are refused; Databricks hosts must use one of the two suffixes (workspace root, /serving-endpoints/<name>/invocations, /api/2.0/serving-endpoints and the gateway's Gemini/Anthropic native paths are refused), including upper-case and trailing-dot spellings. URLs are never rewritten. main() exits 2 before tracing on a refused URL or missing configuration. - Databricks token only on the OpenAI client; Future AGI keys only on the tracer. - Pins current traceai-openai behaviour: provider label openai; streamed and failed spans have no gen_ai.request.model (the model stays in gen_ai.request.parameters); one embeddings case at the gateway form (EMBEDDING span, model in embedding.model_name, input text exported). - DatabricksOpenAI is documented as source-checked only (0.17.1 subclasses openai.OpenAI without overriding request); not a dependency. - Loopback only: MockTransport, a local OpenAI-shaped fake, the shared harness receiver and a socket guard with a negative control. approved: Nikhil 2026-10-03 blanket Refs: TH-8316
…s) and P3s From pr-reviewer t_fdf90ae4 (CHANGES_REQUESTED, R1 P2 blocking): - R1 (P2): check_base_url refuses plain http:// for Databricks workspace hosts (*.cloud.databricks.com, *.azuredatabricks.net, *.gcp.databricks.com); the OpenAI SDK would otherwise send the token in cleartext before any redirect. Loopback and proxy http URLs stay allowed. Six new refused cases (three host suffixes x two surfaces), also run through main() (exit 2 before tracing). - F1: host and path checks now use the URL as given (only the placeholder check decodes), and a query string or fragment on a workspace URL is refused; .../serving-endpoints%3F/x/invocations is refused. - F2: a missing DATABRICKS_TOKEN gets the same actionable "Set ..." message as an empty one. - README: one sentence on https-only workspace hosts and query/fragment refusal; pinned by the README test. RED before the change: 20 failed (fix-r1-red); GREEN after: 140 passed. Kept as P3 follow-ups: README snippet imports the recipe helper (F4), embeddings fixture returns floats while the SDK requests base64 (F5), GovCloud/sovereign workspace domains fall into the proxy branch (F6). F3 (product name): the live Databricks page (2026-10-06) says "Unity Gateway"; the README keeps that name. approved: Nikhil 2026-10-03 blanket Refs: TH-8316
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR adds a Databricks recipe at
python/examples/databricks/(Linear TH-8316, parent TH-8103). Customers who call their Databricks workspace with the officialopenaiSDK get traces through the existingtraceai-openaiinstrumentor. The PR adds no package and no instrumentor, and changes nothing outsidepython/examples/databricks/.Databricks documents two OpenAI-SDK surfaces, kept separate here:
modelhttps://<workspace-host>/ai-gateway/mlflow/v1system.ai.claude-sonnet-4-5https://<workspace-host>/serving-endpointssrc/app.py: callsregister()and instruments withOpenAIInstrumentorbefore creating the client. The Databricks token (DATABRICKS_TOKEN) goes only to the OpenAI client; the Future AGI keys go only to the tracer.DATABRICKS_BASE_URLis required, because there is no public default.check_base_url()refuses placeholders (literal or percent-encoded) and Databricks' own sample hostexample.staging.cloud.databricks.com.*.cloud.databricks.com,*.azuredatabricks.net,*.gcp.databricks.com) it refuses anything but the two suffixes: the workspace root,/api/2.0/serving-endpoints,/serving-endpoints/<name>/invocations(a REST URL, not an SDK base URL), and the gateway's native/ai-gateway/geminiand/ai-gateway/anthropicpaths. Upper-case and trailing-dot spellings are caught too. It never rewrites a URL.https(plainhttpwould send the token in cleartext before any redirect), and a query string or fragment is refused. Host and path checks run on the URL as given; only the placeholder check decodes.main()returns 2 before tracing on a refused URL or missing configuration.requirements.txt: the tested pinsopenai==3.24.0,traceAI-openai==0.1.10andfi-instrumentation-otel==1.1.0.README.md: install, configure, run, code, what you see in Future AGI, and both surfaces. It also covers:DatabricksOpenAI, source-checked only;tests/: a loopback contract on the shared harnessReceiver(test(harness): shared OTLP harness for TH-8103 contract tests #203), a socket guard with a negative control, and a fake OpenAI server.What the tests pin
Every test runs offline. In-process tests use
httpx.MockTransportat a syntactic test host that is not real (dbc-00000000-0000.cloud.databricks.com). Subprocess tests use a fake on 127.0.0.1 under the guard.Both surfaces:
<base>/chat/completionswithAuthorization: Bearer <placeholder>and no Future AGI header;ChatCompletionLLM span, with the model the fixture returns;gen_ai.provider.nameisopenai;Embeddings (gateway form): one
CreateEmbeddingResponsespan with kindEMBEDDINGand provideropenai.embedding.model_name, notgen_ai.request.model;Behaviour:
FI_HIDE_INPUTS/FI_HIDE_OUTPUTSremove a marker, with visible controls;openai.AuthenticationError, a span with ERROR status and an exception event, with no token recorded;Current
traceai-openaibehaviour, pinned and stated in the README (D-F5):gen_ai.request.model; the model is still insidegen_ai.request.parameters;include_usageand a final usage chunk, all three counts are exported.The shared fix is tracked separately (Linear TH-8402).
Tests (exact head
b46c12e80232022f482ec4512b0ce0725104c6c5, clean tree, actual exit codes + JUnit)openaitraceAI-openai==0.1.10+fi-instrumentation-otel==1.1.0(imported from site-packages, no repo source on the path)No files outside
python/examples/databricks/changed, and the head did not move during the run. The credential scan of the recipe and the evidence found 0 hits. The first commit,684708b, had the same 6/6 matrix with 122 tests.Decisions (approved: Nikhil 2026-10-03 blanket)
traceai-openai, not atraceai-databrickspackage.DATABRICKS_HOST, because it hides which surface the customer chose.DatabricksOpenAIis source-checked only. Indatabricks-openai0.17.1 it subclassesopenai.OpenAIwithout overridingrequest. It is not a dependency and is not tested.Not covered
ai_query, the MLflow Deployments SDK, Agent / Genie code, and the gateway's Gemini/Anthropic native APIs.DatabricksOpenAIexecution, and direct REST invocation.Review status
Review r1 (pr-reviewer
t_fdf90ae4, claude-opus-5-5, at684708b): CHANGES_REQUESTED on one P2.http://was accepted for Databricks workspace hosts, so the token could be sent in cleartext.Fixed in
b46c12e, test-first (20 failed before, 140 passed after):https;Kept as P3:
Verification r1 (pr-verifier
t_048d2b3a, claude-opus-5-5, atb46c12e): VERIFIED, no blocking findings.?still passes. F3–F6 accepted as P3.httpthat would send the token in cleartext. Fix: refuse non-ASCII hosts, or classify onhttpx.URL(url).host.?/#by checking the delimiters, not truthiness.urlsplitand then fails in httpx, with exit 1 after tracing has started.httpcase, and a response model distinct from the request.embedding.embeddings.FI_HIDE_EMBEDDING_VECTORSmasks onlyembedding.vectorkeys. The README makes no vector-masking claim. This is tracked with the sharedtraceai-openaiwork.Video demo
A narrated CLI walkthrough, 5:42, recorded at the verified head
b46c12e: 1080p H.264/AAC, burned captions and 10 embedded chapters. The video, captions, transcript, chapters, preview and both media-verification notes are private attachments on the Linear issue TH-8316 (Future AGI workspace access required). Every command chapter runs./run_demo.sh <chapter>live in a real terminal and ends at exit 0./ai-gateway/mlflow/v1, model service name) and Model Serving (/serving-endpoints, endpoint name) (card)b46c12e, the recipe files, the env variable names and the required URL's placeholder formsgen_ai.request.modeldbc-…,adb-…andworkspace-…hosts are syntactic test hosts), plus a gateway embeddings call (an EMBEDDING span withembedding.model_name); 0 external DNSgen_ai.request.model, the model in parameters, and no tokenFI_HIDE_INPUTS/FI_HIDE_OUTPUTSversus a control; Databricks still receives the promptTwo waits are shortened in the edit, and both are labelled on screen: about 74 s of the refusals run and about 62 s of the live pytest run. Command output itself is not edited.
Not shown: a live Databricks call, workspace authentication, a running fi-collector, storage or the trace view. All runs use loopback fakes, and the narration says so.