') + ')', '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); } })(); })(); [bot] Mistral: Classifiers API (`client.classifiers.moderate()` / `classify()`) not instrumented · Issue #326 · braintrustdata/braintrust-sdk-python · GitHub
Skip to content

[bot] Mistral: Classifiers API (client.classifiers.moderate() / classify()) not instrumented #326

Description

@braintrust-bot

Summary

The Mistral Classifiers API (client.classifiers) is not instrumented. Calls to client.classifiers.moderate(), moderate_chat(), classify(), and classify_chat() (plus their async variants) produce zero Braintrust tracing. These are GA inference endpoints that accept text or chat messages, run a classifier model (e.g. mistral-moderation-latest), and return moderation categories/scores or classification labels.

This repo already instruments the analogous OpenAI Moderations API (ModerationsPatcher in the OpenAI integration), making this an asymmetry across providers.

What is missing

Mistral ResourceMethodInstrumented?
client.chatcomplete(), stream()Yes
client.embeddingscreate()Yes
client.fimcomplete(), stream()Yes
client.agentscomplete(), stream()Yes
client.ocrprocess()Yes
client.audio.transcriptionscomplete(), stream()Yes
client.audio.speechcomplete()Yes
client.classifiersmoderate(), moderate_async()No
client.classifiersmoderate_chat(), moderate_chat_async()No
client.classifiersclassify(), classify_async()No
client.classifiersclassify_chat(), classify_chat_async()No

Endpoints

MethodHTTP EndpointDescription
moderate()POST /v1/moderationsText moderation
moderate_chat()POST /v1/chat/moderationsChat message moderation
classify()POST /v1/classificationsText classification
classify_chat()POST /v1/chat/classificationsChat message classification

All four endpoints are listed in the main Mistral API reference without beta tags, indicating GA status.

Minimum instrumentation

At minimum, all 8 methods should create spans capturing:

  • Input: text or messages being classified/moderated
  • Output: classification/moderation results with category scores
  • Metadata: provider: "mistral", model name, any threshold or category configuration
  • Metrics: latency

Precedent in this repo

The OpenAI integration instruments the equivalent client.moderations.create() endpoint via ModerationsPatcher in py/src/braintrust/integrations/openai/patchers.py. The Mistral classifiers follow the same pattern: accept input, run a model, return inference results.

Braintrust docs status

not_found — The Mistral integration page documents chat completions, FIM, embeddings, and agents. No mention of classifiers, moderation, or classification endpoints.

Upstream sources

Local files inspected

  • py/src/braintrust/integrations/mistral/patchers.py — defines patchers for Chat, Embeddings, Fim, Agents, Ocr, Speech, Transcriptions; zero references to classifiers, moderate, or classify
  • py/src/braintrust/integrations/mistral/tracing.py — wrapper functions for chat, embeddings, FIM, agents, OCR, audio; no classifier wrappers
  • py/src/braintrust/integrations/mistral/integration.py — integration class registers 7 composite patchers; no ClassifiersPatcher
  • py/src/braintrust/integrations/mistral/test_mistral.py — no classifier test cases
  • py/src/braintrust/integrations/openai/patchers.py — OpenAI ModerationsPatcher exists as precedent for this pattern

Relationship to existing issues

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions