feat(ai): include org slug in Assistant Braintrust span metadata (#49692)

<!-- ccr-slack-attribution -->
_Requested by **Matt Rossman** · [Slack
thread](https://supabase.slack.com/archives/D0A79RYJKRB/p1787926891744399)_


# Problem

Assistant spans in Braintrust record only the numeric `orgId`, whereas
support tickets show org slug.

This incurs an extra manual step to resolve the ID through admin studio
before the trace can be found.

# Fix

Adds `orgSlug` to spans, sourced from the same verified org lookup that
produces `orgId`.

Renamed the request body's `orgSlug` to `rawOrgSlug` to distinguish the
verified slug from getAIDetails, following the existing
rawRequestedModel / requestedModel pattern.

## How to review

See sample trace
[94863b6d-aaa9-449a-a9c9-981ad40e614a](https://www.braintrust.dev/app/supabase.io/p/Assistant/trace?object_type=project_logs&object_id=5a8d02e5-b3b6-40cc-ba76-ecee286478f4&r=223112cd-33f4-45c4-a273-8d3781689448&s=223112cd-33f4-45c4-a273-8d3781689448)
produced from sending a chat from the
[Preview](https://studio-staging-git-mattrossman-ai-1149-include-698a5f-supabase.vercel.app/dashboard/org)
on this PR.

Note it now includes the org slug in span metadata:

<img width="873" height="548" alt="CleanShot 2026-08-28 at 10 59 49@2x"
src="https://github.com/user-attachments/assets/bcf47a94-6782-434a-9006-c7b9c95f1c37"
/>

If desired you can test yourself too by chatting with Assistant in the
preview and looking up the corresponding Chat ID from Braintrust
[logs](https://www.braintrust.dev/app/supabase.io/p/Assistant/logs).

Closes AI-1149

---
_Generated by [Claude
Code](https://claude.ai/code/session_01N2ziJech9dV19pJ9MisYdX)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
claude[bot]andClaude authored and GitHub committed 2026-08-28 11:48:38 -04:00
1 parent 95954ab81b
commit 8790e657e9
4 files changed
+15 -3

No files matched your search

+3
View File
@@ -91,6 +91,7 @@ describe('getAIDetails', () => {
hasAccessToAdvanceModel: false,
hasHipaaAddon: false,
orgId: 1,
orgSlug: ORG_SLUG,
planId: 'pro',
region: 'us-east-1',
isSensitive: false,
@@ -189,6 +190,7 @@ describe('getAIDetails', () => {
expect(result.aiOptInLevel).toBe('disabled')
expect(result.hasAccessToAdvanceModel).toBe(false)
expect(result.orgId).toBeUndefined()
expect(result.orgSlug).toBeUndefined()
expect(result.planId).toBeUndefined()
})
@@ -216,6 +218,7 @@ describe('getAIDetails', () => {
expect(result.aiOptInLevel).toBe('disabled')
expect(result.orgId).toBeUndefined()
expect(result.orgSlug).toBeUndefined()
})
it('falls back to the most restrictive posture when project detail is unavailable', async () => {
+3
View File
@@ -11,6 +11,7 @@ export type AIDetails = {
hasAccessToAdvanceModel: boolean
hasHipaaAddon: boolean | undefined
orgId: number | undefined
orgSlug: string | undefined
planId: string | undefined
region: string | undefined
isSensitive: boolean | null | undefined
@@ -56,6 +57,7 @@ export const getAIDetails = async ({
// Undefined rather than false so isTracingAllowed fails closed
hasHipaaAddon: undefined,
orgId: undefined,
orgSlug: undefined,
planId: undefined,
region,
isSensitive,
@@ -72,6 +74,7 @@ export const getAIDetails = async ({
hasAccessToAdvanceModel: advanceModelAccess.hasAccess,
hasHipaaAddon,
orgId: selectedOrg.id,
orgSlug: selectedOrg.slug,
planId: selectedOrg.plan.id,
region,
isSensitive,
@@ -39,6 +39,7 @@ export async function generateAssistantResponse({
supportMode,
userId,
orgId,
orgSlug,
planId,
includesLogsSnippets,
isExplorerEnabled,
@@ -60,6 +61,7 @@ export async function generateAssistantResponse({
supportMode?: boolean
userId?: string
orgId?: number
orgSlug?: string
planId?: string
/** Whether any user message in the conversation attached a logs (ClickHouse) query. */
includesLogsSnippets?: boolean
@@ -171,6 +173,7 @@ export async function generateAssistantResponse({
aiOptInLevel,
userId,
orgId,
orgSlug,
planId,
requestedModel,
gitBranch: process.env.VERCEL_GIT_COMMIT_REF,
+6 -3
View File
@@ -96,7 +96,7 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse, claims?: Jw
messages: rawMessages,
projectRef,
connectionString,
orgSlug,
orgSlug: rawOrgSlug,
chatId,
chatName,
model: rawRequestedModel,
@@ -126,6 +126,7 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse, claims?: Jw
let projectIsSensitive: boolean | null | undefined
let projectRegion: string | undefined
let orgId: number | undefined
let orgSlug: string | undefined
let planId: string | undefined
if (!IS_PLATFORM) {
@@ -133,14 +134,15 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse, claims?: Jw
hasAccessToAdvanceModel = true
}
if (IS_PLATFORM && orgSlug && authorization && projectRef) {
if (IS_PLATFORM && rawOrgSlug && authorization && projectRef) {
try {
const aiDetails = await getAIDetails({ orgSlug, projectRef, authorization })
const aiDetails = await getAIDetails({ orgSlug: rawOrgSlug, projectRef, authorization })
aiOptInLevel = aiDetails.aiOptInLevel
hasAccessToAdvanceModel = aiDetails.hasAccessToAdvanceModel
orgHasHipaaAddon = aiDetails.hasHipaaAddon
orgId = aiDetails.orgId
orgSlug = aiDetails.orgSlug
planId = aiDetails.planId
projectIsSensitive = aiDetails.isSensitive
projectRegion = aiDetails.region
@@ -234,6 +236,7 @@ async function handlePost(req: NextApiRequest, res: NextApiResponse, claims?: Jw
supportMode,
userId,
orgId,
orgSlug,
planId,
includesLogsSnippets,
isExplorerEnabled: explorerEnabled,