Uh oh!
There was an error while loading. Please reload this page.
') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })();
There was an error while loading. Please reload this page.
Summary
Add native Braintrust instrumentation for the official Groq Python SDK (
groq). The integration must support package-level setup,auto_instrument(), and manualwrap_groq()instrumentation forGroqandAsyncGroqclients.Using Groq through
openai.OpenAI(base_url="https://api.groq.com/openai/v1")is not an equivalent substitute: the OpenAI integration currently recordsmetadata.provider = "openai", while Groq pricing attribution requiresmetadata.provider = "groq".The implementation should use the TypeScript Groq integration as behavioral prior art while following the current Braintrust instrumentation specification as the source of truth.
Supported versions and surfaces
Support
groq >= 1.0.0, with explicit matrix coverage for the latest pinned version and1.0.0.Instrument both sync and async forms of:
client.chat.completions.create()client.embeddings.create()client.audio.transcriptions.create()client.audio.translations.create()client.audio.speech.create()Span model and naming
Each provider API call produces exactly one
llmspan, including streaming calls.Use stable operation names:
groq.chat.completions.creategroq.embeddings.creategroq.audio.transcriptions.creategroq.audio.translations.creategroq.audio.speech.createAll spans created by the integration must include:
{ "context": { "span_origin": { "instrumentation": { "name": "groq-auto" } } } }Provider exceptions must propagate unchanged, be recorded in the top-level
errorfield, and still end the span.Chat completions
Canonical payloads
Groq does not have a dedicated Braintrust UI normalizer, so chat payloads must use OpenAI Chat Completions format:
input: the orderedmessagesarray itself, not{ "messages": [...] }output: thechoicesarray, preservingindex,finish_reason, andmessagechoices[0].message.contentTool calls must remain OpenAI-shaped.
function.argumentsmust remain a JSON-encoded string through streaming accumulation; malformed JSON must pass through unchanged rather than being parsed or dropped.Metadata
Every chat span must include:
metadata.provider = "groq"metadata.model, preferring the model returned by Groq over the requested modelOnly capture specification-approved request configuration fields when supplied and JSON-serializable:
temperaturetop_pmax_tokensfrequency_penaltypresence_penaltystopresponse_formattoolstool_choiceparallel_tool_callsmax_tool_callsif Groq adds/supports itOmit
metadata.toolswhen no tools were supplied; do not emit an empty array. Preserve provider-native built-in tool declarations and serializable configuration without inventing function schemas.Do not capture currently unsanctioned request fields such as
max_completion_tokens,seed,n,stream,stream_options,service_tier,reasoning_format, orsearch_settings.Metrics
Capture only canonical metrics reported by Groq:
prompt_tokenscompletion_tokenstokenscompletion_reasoning_tokenswhen reportedprompt_cached_tokenswhen Groq reports a canonical cache-read token counttime_to_first_tokenfor every streaming call, measured by Braintrust from request initiation to the first received chunkFor streaming, capture usage from the terminal chunk, including the
x_groq.usagefallback when needed.Do not emit Groq server timing fields (
queue_time,prompt_time,completion_time,total_time, orseconds) or provider-specific cache fields as metrics unless they are first added to the specification.Streaming
A stream must preserve the provider iterator/async-iterator interface. The span remains open until exhaustion, close, context-manager exit, or stream error. Accumulated output must be structurally equivalent to non-streaming
choices, including multi-choice content and fragmented tool calls.Vision and inline media
Recursively materialize supported inline OpenAI-style content parts as Braintrust attachments at the original media leaf. In particular, convert whole-value base64 data URLs under
image_url.url; preserve remote URLs and leave malformed or unknown media unchanged. Attachment conversion failures must not affect the provider call or prevent span export.Groq Compound and built-in tools
Groq Compound calls still go through
chat.completions.create()as one request/response. They therefore produce exactly onellmspan and no synthetic childtoolspans.Built-in tool declarations supplied in the request belong in
metadata.toolswith their provider-nativetypeand JSON-serializable configuration preserved.Groq's provider-executed
choices[].message.executed_toolsresults currently have no sanctioned representation in the instrumentation specification. Do not capture them until the specification defines one. This is not a reason to fabricate child tool spans.Reasoning
Capture
completion_reasoning_tokenswhen reported. Reasoning content placement for OpenAI-chat-shaped third-party provider output is not currently defined consistently by the specification; do not capture Groq's provider-specificmessage.reasoningfield until the specification resolves that shape.Embeddings
Follow the embedding feature specification:
llmspan per request, not per vectormetadata.provider = "groq"metadata.model{ "inputs": [{ "content": ... }] }, preserving scalar/batch order{ "count": <number of embeddings> }prompt_tokensandtokenswhen reported; omitcompletion_tokensAudio and attachments
Transcription and translation
fileinput leaf.urlinputs as URLs; do not fetch them only for tracing.metadata.provider = "groq"and the resolved/requested model.x_groq.usage.secondsis not a sanctioned metric and must be omitted.Speech
output, never inmetadata.Repository plumbing and tests
Acceptance requires:
py/src/braintrust/integrations/groq/with publicGroqIntegrationandwrap_groq()exportspy/src/braintrust/integrations/__init__.pyauto_instrument(groq=True)registration plus subprocess import-order/idempotence coverage[tool.braintrust.matrix].groqwith explicitlatestand1.0.0pins[tool.braintrust.cassette-dirs].groqand[tool.braintrust.vendor-packages]entriestest_groq(<version>)nox sessionBackend validation
Validate that emitted Groq span identity and metrics are usable by the Braintrust backend:
metadata.provider = "groq"is accepted and preservedllama-3.1-8b-instant,openai/gpt-oss-*, andgroq/compound*) are recognized by the backend model registry where pricing/model metadata exists