{"id":34524846,"url":"https://github.com/dpguthrie/braintrust-trace-embed-demo","last_synced_at":"2026-05-26T18:31:50.129Z","repository":{"id":329345481,"uuid":"1119158757","full_name":"dpguthrie/braintrust-trace-embed-demo","owner":"dpguthrie","description":"Demo showing how to embed the trace viewer into your application","archived":false,"fork":false,"pushed_at":"2025-12-18T23:45:12.000Z","size":324,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-21T22:55:35.058Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://braintrust-trace-embed-demo.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dpguthrie.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-18T20:54:00.000Z","updated_at":"2025-12-18T23:45:15.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dpguthrie/braintrust-trace-embed-demo","commit_stats":null,"previous_names":["dpguthrie/braintrust-trace-embed-demo"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/dpguthrie/braintrust-trace-embed-demo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpguthrie%2Fbraintrust-trace-embed-demo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpguthrie%2Fbraintrust-trace-embed-demo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpguthrie%2Fbraintrust-trace-embed-demo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpguthrie%2Fbraintrust-trace-embed-demo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dpguthrie","download_url":"https://codeload.github.com/dpguthrie/braintrust-trace-embed-demo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dpguthrie%2Fbraintrust-trace-embed-demo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33534562,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"ssl_error","status_checked_at":"2026-05-26T15:22:15.568Z","response_time":63,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-24T05:06:55.820Z","updated_at":"2026-05-26T18:31:50.105Z","avatar_url":"https://github.com/dpguthrie.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Braintrust Trace Viewer Embed Demo\n\nA modern React + TypeScript application demonstrating how to embed the Braintrust trace viewer as an iframe in your own application. **Browse logs via the Braintrust API and click to view traces** - no manual ID entry required!\n\n![Braintrust Trace Viewer Embed Demo](images/trace_viewer_iframe.png)\n\n## Features\n\n- **Modern Stack**: React 19 + TypeScript + Vite\n- **Tailwind CSS**: Beautiful, responsive UI\n- **Environment Variables**: Secure configuration using .env files\n- **API-Driven**: Automatically fetches recent logs from Braintrust API\n- **Interactive Logs Table**: Browse recent logs and click to view traces\n- **iframe Embedding**: Full Braintrust trace viewer embedded in your app\n- **API Key Authentication**: Secure authentication via URL parameters\n- **TypeScript Types**: Fully typed for better developer experience\n- **Reusable Components**: TraceViewer component with ref API for programmatic control\n\n## Tech Stack\n\n- **React 19**: Latest React with hooks\n- **TypeScript**: Type-safe development\n- **Vite**: Fast build tool and dev server\n- **Tailwind CSS**: Utility-first CSS framework\n- **pnpm**: Fast, disk-efficient package manager\n- **Braintrust BTQL API**: Query logs and traces\n\n## Quick Start\n\n### 1. Install Dependencies\n\n```bash\ncd ~/repos/braintrust-trace-embed-demo\npnpm install\n```\n\n### 2. Configure Environment (Optional)\n\n**Option A: Use Environment Variables (for development)**\n\nCopy the example environment file and fill in your Braintrust details:\n\n```bash\ncp .env.example .env\n```\n\nEdit `.env` and add your configuration:\n\n```env\nVITE_BRAINTRUST_URL=https://www.braintrust.dev\nVITE_BRAINTRUST_ORG=your-org\nVITE_BRAINTRUST_PROJECT=your-project\nVITE_BRAINTRUST_API_KEY=sk_your_api_key\n```\n\n**Option B: Enter Credentials in UI (works in production)**\n\nSkip the `.env` file and enter your credentials directly in the web interface. This is how the app works when deployed to Vercel or other hosting platforms.\n\nThat's it! No need to manually enter trace IDs.\n\n### 3. Run Development Server\n\n```bash\npnpm dev\n```\n\nThe app will open at `http://localhost:5173`\n\n### 4. Using the App\n\n1. **Project ID is auto-detected!** The app automatically fetches your project ID from the project slug\n2. **Click \"Fetch Recent Logs\"** to load the last 50 logs from the past 7 days\n3. **Browse the logs table** - see names, types, durations, tags, and status\n4. **Click any row** to open the trace viewer in a side panel\n\nNo manual ID copying required! 🎉\n\n**Note:** If auto-detection fails, you can manually enter the project UUID.\n\n## What is Braintrust?\n\n**Braintrust** is an end-to-end platform for building AI applications. It provides:\n\n### Core Capabilities\n\n1. **Logging \u0026 Observability**: Capture every LLM call, function execution, and trace\n2. **Evaluations (Evals)**: Run experiments to test prompts, models, and parameters\n3. **Datasets**: Manage test cases and golden datasets for evaluation\n4. **Prompt Management**: Version control and deploy prompts as code\n5. **AI Proxy**: Route requests across providers with fallbacks and load balancing\n\n### The Trace Viewer\n\nThe **trace viewer** is Braintrust's UI for visualizing execution traces. It shows:\n\n- **Hierarchical span trees**: Parent-child relationships between operations\n- **Timing information**: Duration, start/end times for each operation\n- **Input/Output data**: What went into and came out of each step\n- **Metadata**: Tags, scores, custom attributes\n- **Errors**: Stack traces and error messages when things go wrong\n\nTraces are essential for:\n- **Debugging**: Understanding what happened during an LLM call\n- **Performance analysis**: Finding bottlenecks in your AI pipeline\n- **Cost tracking**: Monitoring token usage and API costs\n- **Quality assurance**: Validating outputs meet expectations\n\n## Understanding iframe Embedding\n\n### What is iframe Embedding?\n\nAn **iframe** (inline frame) is an HTML element that embeds another HTML document within the current page:\n\n```html\n\u003ciframe src=\"https://example.com/page\" width=\"100%\" height=\"600px\"\u003e\u003c/iframe\u003e\n```\n\n**Key characteristics:**\n- Creates a completely isolated browsing context\n- Has its own DOM, JavaScript environment, and CSS\n- Subject to same-origin policy for security\n- Can communicate with parent page via `postMessage` API\n\n### Why iframe for Trace Viewer?\n\nEmbedding the Braintrust trace viewer as an iframe provides:\n\n1. **Complete Isolation**: The trace viewer's complex UI doesn't interfere with your app's styles or JavaScript\n2. **Maintained by Braintrust**: You get automatic updates and bug fixes without rebuilding\n3. **Full Functionality**: Access to all features (zoom, filter, search, export)\n4. **Security**: Braintrust handles authentication and data access control\n5. **Zero Maintenance**: No need to replicate or maintain the trace visualization logic\n\n**iframe embedding** gives you the best of all worlds: full functionality, zero maintenance, and seamless integration into your application.\n\n## Project Structure\n\n```\nbraintrust-trace-embed-demo/\n├── src/\n│   ├── api/\n│   │   └── braintrust.ts           # Braintrust API client\n│   ├── components/\n│   │   ├── TraceViewer.tsx         # Reusable trace viewer component\n│   │   └── LogsTable.tsx           # Logs table with click-to-view\n│   ├── hooks/\n│   │   └── useLogs.ts              # React hook for fetching logs\n│   ├── types.ts                    # TypeScript type definitions\n│   ├── App.tsx                     # Main application component\n│   ├── index.css                   # Tailwind CSS imports\n│   └── main.tsx                    # Application entry point\n├── .env.example                    # Example environment variables\n├── .env                            # Your environment variables (gitignored)\n├── tailwind.config.js              # Tailwind CSS configuration\n├── vite.config.ts                  # Vite configuration\n├── package.json                    # Project dependencies\n└── README.md                       # This file\n```\n\n## How It Works\n\n### API-Driven Log Fetching\n\nInstead of manually entering trace IDs, the app uses the **Braintrust BTQL API** to fetch recent logs:\n\n```typescript\n// Automatically fetches logs from the API\nconst query = `\n  SELECT *\n  FROM project_logs('${projectId}')\n  WHERE created \u003e= NOW() - INTERVAL ${daysBack} DAY\n    AND is_root\n  ORDER BY _pagination_key DESC\n  LIMIT ${limit}\n`;\n```\n\n**Note:** The query uses `is_root` to filter for only root spans (traces), not individual child spans. This gives a cleaner view in the table. When you click a root span, the trace viewer automatically loads and displays the entire trace tree with all child spans.\n\nThe API returns log records with all the information needed:\n- `id` - Log identifier\n- `root_span_id` - Root span for the trace (used for the `r` parameter)\n- `span_attributes` - Contains name, type, and other metadata\n- `created` - Timestamp\n- `metadata` - Custom metadata\n- `tags` - Tags for filtering\n- `error` - Error information if present\n\n### Click-to-View Traces\n\nWhen you click a log row, the app automatically:\n1. Extracts the `root_span_id` from the log\n2. Constructs the trace viewer URL with all parameters\n3. Loads the trace in the iframe below\n\n**Important: Two Types of Traces**\n\nBraintrust has two routes for viewing traces:\n\n**1. Project Logs Route** (used in this demo)\n```\nhttps://[instance]/app/[org]/p/[project]/logs?\n  api_key=sk_...\u0026\n  r=[root_span_id]\n```\nUsed for: Raw traces logged directly to a project (via `initLogger`, SDKs, or OpenTelemetry)\n\n**2. Experiment/Eval Trace Route**\n```\nhttps://[instance]/app/[org]/p/[project]/trace?\n  api_key=sk_...\u0026\n  object_type=experiment\u0026\n  object_id=[experiment_id]\u0026\n  r=[root_span_id]\n```\nUsed for: Traces associated with specific experiments, prompts, or datasets\n\nThis demo uses the **project logs route** because we're fetching raw logs from `project_logs()`. If you're embedding traces from experiments or evals, use the trace route with appropriate `object_type` and `object_id`.\n\n### Security\n\nBraintrust handles API key authentication securely when embedding trace viewers. The API key is passed via query parameters and processed server-side to ensure secure access to your traces while allowing iframe embedding.\n\n## Technical Architecture\n\n### Complete Data Flow\n\nHere's the end-to-end flow when you use this demo:\n\n```\n┌─────────────┐      1. Fetch Logs       ┌──────────────┐\n│   Your App  │ ───────────────────────\u003e │  Braintrust  │\n│             │                           │  BTQL API    │\n│  (React UI) │ \u003c─────────────────────── │              │\n└─────────────┘   2. Return Log Records  └──────────────┘\n       │\n       │ 3. User Clicks Log Row\n       ↓\n┌─────────────┐\n│  Extract    │\n│  root_span  │\n│    _id      │\n└─────────────┘\n       │\n       │ 4. Build iframe URL\n       ↓\n┌─────────────────────────────────────────────────────┐\n│  iframe src=\"https://braintrust.dev/app/org/p/     │\n│    project/trace?api_key=...\u0026object_type=project   │\n│    \u0026object_id=xxx\u0026r=root_span_id\"                  │\n└─────────────────────────────────────────────────────┘\n       │\n       │ 5. iframe loads\n       ↓\n┌──────────────┐      6. Middleware       ┌──────────────┐\n│  Braintrust  │ ───────────────────────\u003e │  Braintrust  │\n│  Middleware  │      Signs API Key       │   Backend    │\n│              │                           │              │\n│              │ \u003c─────────────────────── │              │\n└──────────────┘   7. Return Trace HTML   └──────────────┘\n       │\n       │ 8. Render Trace\n       ↓\n┌─────────────┐\n│   Trace     │\n│   Viewer    │\n│   (in       │\n│   iframe)   │\n└─────────────┘\n       │\n       │ 9. postMessage (optional)\n       ↓\n┌─────────────┐\n│  Your App   │  ← Can control trace viewer\n└─────────────┘     (change selected span, etc.)\n```\n\n### Component Breakdown\n\n**1. API Layer (`src/api/braintrust.ts`)**\n- Constructs BTQL queries\n- Makes authenticated requests to Braintrust API\n- Parses and normalizes responses\n- Handles errors and retries\n\n**2. React Hook (`src/hooks/useLogs.ts`)**\n- Manages loading state\n- Caches log data\n- Triggers refetch on parameter changes\n- Provides clean API to components\n\n**3. Logs Table (`src/components/LogsTable.tsx`)**\n- Renders log records in tabular format\n- Handles row selection\n- Displays metadata (tags, status, duration)\n- Responsive design\n\n**4. Trace Viewer Component (`src/components/TraceViewer.tsx`)**\n- Constructs iframe URL with authentication\n- Manages iframe lifecycle\n- Implements postMessage communication\n- Exposes imperative API via ref\n\n**5. Main App (`src/App.tsx`)**\n- Orchestrates data flow\n- Manages global state\n- Handles user interactions\n- Coordinates between components\n\n### How Embedding Works\n\nBraintrust provides dedicated routes for embedding trace viewers via iframe. These routes:\n- Accept API key authentication via query parameters\n- Support iframe embedding with appropriate security headers\n- Accept parameters like `r` (root_span) and `s` (selected_span) for navigation\n- Provide full trace visualization functionality\n\nThe trace viewer also supports `postMessage` communication, allowing your application to programmatically control which span is selected or navigate between traces.\n\n## How to Extend This Demo\n\n### 1. Add Filtering to Logs Table\n\nAdd filters for tags, date ranges, or metadata:\n\n```typescript\n// src/api/braintrust.ts\nexport async function fetchFilteredLogs(params: FetchLogsParams \u0026 {\n  tags?: string[];\n  startDate?: Date;\n  endDate?: Date;\n}) {\n  const { tags, startDate, endDate } = params;\n\n  let whereClause = `WHERE created \u003e= NOW() - INTERVAL ${params.daysBack} DAY`;\n\n  if (tags \u0026\u0026 tags.length \u003e 0) {\n    whereClause += ` AND tags @\u003e ARRAY[${tags.map(t =\u003e `'${t}'`).join(',')}]`;\n  }\n\n  if (startDate) {\n    whereClause += ` AND created \u003e= '${startDate.toISOString()}'`;\n  }\n\n  const query = `\n    SELECT * FROM project_logs('${params.projectId}')\n    ${whereClause}\n    ORDER BY created DESC\n    LIMIT ${params.limit}\n  `;\n  // ... rest of implementation\n}\n```\n\n### 2. Add Pagination\n\nImplement cursor-based pagination for large result sets:\n\n```typescript\n// src/api/braintrust.ts\nexport async function fetchLogsPage(params: FetchLogsParams \u0026 {\n  cursor?: string;\n}) {\n  // Use cursor from previous response\n  const query = params.cursor\n    ? `SELECT * FROM project_logs('${params.projectId}')\n       WHERE _pagination_key \u003e '${params.cursor}'\n       ORDER BY _pagination_key LIMIT ${params.limit}`\n    : `SELECT * FROM project_logs('${params.projectId}')\n       ORDER BY created DESC LIMIT ${params.limit}`;\n\n  const response = await fetch(/* ... */);\n  const data = await response.json();\n\n  return {\n    logs: data.data,\n    cursor: data.cursor,  // Use for next page\n    hasMore: !!data.cursor\n  };\n}\n```\n\n### 3. Add Real-Time Updates\n\nUse WebSockets or polling to show new logs as they arrive:\n\n```typescript\n// src/hooks/useRealtimeLogs.ts\nexport function useRealtimeLogs(params: FetchLogsParams) {\n  const [logs, setLogs] = useState\u003cLogRecord[]\u003e([]);\n\n  useEffect(() =\u003e {\n    const interval = setInterval(async () =\u003e {\n      const latestLogs = await fetchRecentLogs({\n        ...params,\n        limit: 10,  // Just fetch latest 10\n      });\n\n      setLogs(prev =\u003e {\n        // Merge new logs, dedupe by ID\n        const merged = [...latestLogs, ...prev];\n        const unique = Array.from(\n          new Map(merged.map(l =\u003e [l.id, l])).values()\n        );\n        return unique.slice(0, 100);  // Keep last 100\n      });\n    }, 5000);  // Poll every 5 seconds\n\n    return () =\u003e clearInterval(interval);\n  }, [params]);\n\n  return logs;\n}\n```\n\n### 4. Add Multi-Trace Comparison\n\nShow multiple traces side-by-side:\n\n```typescript\n// src/components/TraceComparison.tsx\nexport function TraceComparison({ traces }: { traces: LogRecord[] }) {\n  return (\n    \u003cdiv className=\"grid grid-cols-2 gap-4\"\u003e\n      {traces.map(trace =\u003e (\n        \u003cTraceViewer\n          key={trace.id}\n          config={{\n            // ... build config from trace\n          }}\n        /\u003e\n      ))}\n    \u003c/div\u003e\n  );\n}\n```\n\n### 5. Add Export Functionality\n\nExport trace data for analysis:\n\n```typescript\n// src/api/braintrust.ts\nexport async function exportTraceData(\n  config: TraceConfig,\n  spanId: string\n): Promise\u003cvoid\u003e {\n  // Fetch full trace data\n  const query = `\n    SELECT * FROM project_logs('${config.objectId}')\n    WHERE root_span_id = '${spanId}'\n    OR span_id = '${spanId}'\n  `;\n\n  const data = await fetchBTQL(config, query);\n\n  // Convert to CSV\n  const csv = convertToCSV(data);\n\n  // Download\n  const blob = new Blob([csv], { type: 'text/csv' });\n  const url = URL.createObjectURL(blob);\n  const a = document.createElement('a');\n  a.href = url;\n  a.download = `trace-${spanId}.csv`;\n  a.click();\n}\n```\n\n### 6. Add Custom Visualizations\n\nCreate custom charts based on trace data:\n\n```typescript\n// Fetch trace data and create custom visualizations\nconst traceData = await fetchBTQL(config, `\n  SELECT\n    span_attributes-\u003e\u003e'type' as span_type,\n    AVG((metrics-\u003e\u003e'duration')::float) as avg_duration,\n    COUNT(*) as count\n  FROM project_logs('${projectId}')\n  WHERE created \u003e= NOW() - INTERVAL 7 DAY\n  GROUP BY span_attributes-\u003e\u003e'type'\n`);\n\n// Use with a charting library like recharts or d3\n\u003cBarChart data={traceData}\u003e\n  \u003cBar dataKey=\"avg_duration\" /\u003e\n  \u003cXAxis dataKey=\"span_type\" /\u003e\n\u003c/BarChart\u003e\n```\n\n### 7. Add Workspace/Team Management\n\nSupport multiple projects and teams:\n\n```typescript\n// src/contexts/WorkspaceContext.tsx\ninterface Workspace {\n  org: string;\n  projects: Array\u003c{\n    id: string;\n    name: string;\n    slug: string;\n  }\u003e;\n}\n\nexport function WorkspaceProvider({ children }: { children: ReactNode }) {\n  const [workspace, setWorkspace] = useState\u003cWorkspace | null\u003e(null);\n\n  // Fetch projects for org\n  const loadWorkspace = async (org: string, apiKey: string) =\u003e {\n    const projects = await fetchProjects(org, apiKey);\n    setWorkspace({ org, projects });\n  };\n\n  return (\n    \u003cWorkspaceContext.Provider value={{ workspace, loadWorkspace }}\u003e\n      {children}\n    \u003c/WorkspaceContext.Provider\u003e\n  );\n}\n```\n\n### 8. Add Bookmarking/Favorites\n\nLet users save interesting traces:\n\n```typescript\n// src/hooks/useBookmarks.ts\nexport function useBookmarks() {\n  const [bookmarks, setBookmarks] = useState\u003cLogRecord[]\u003e(() =\u003e {\n    // Load from localStorage\n    const saved = localStorage.getItem('trace-bookmarks');\n    return saved ? JSON.parse(saved) : [];\n  });\n\n  const addBookmark = (log: LogRecord) =\u003e {\n    const updated = [...bookmarks, log];\n    setBookmarks(updated);\n    localStorage.setItem('trace-bookmarks', JSON.stringify(updated));\n  };\n\n  const removeBookmark = (logId: string) =\u003e {\n    const updated = bookmarks.filter(b =\u003e b.id !== logId);\n    setBookmarks(updated);\n    localStorage.setItem('trace-bookmarks', JSON.stringify(updated));\n  };\n\n  return { bookmarks, addBookmark, removeBookmark };\n}\n```\n\n### 9. Add Custom Metadata Columns\n\nShow custom metadata fields in the table:\n\n```typescript\n// src/components/LogsTable.tsx\ninterface LogsTableProps {\n  logs: LogRecord[];\n  metadataColumns?: Array\u003c{\n    key: string;\n    label: string;\n    render?: (value: unknown) =\u003e ReactNode;\n  }\u003e;\n}\n\n// Usage\n\u003cLogsTable\n  logs={logs}\n  metadataColumns={[\n    { key: 'user_id', label: 'User' },\n    { key: 'model', label: 'Model' },\n    {\n      key: 'cost',\n      label: 'Cost',\n      render: (value) =\u003e `$${Number(value).toFixed(4)}`\n    }\n  ]}\n/\u003e\n```\n\n### 10. Integrate with Your Auth System\n\nReplace API key with your own authentication:\n\n```typescript\n// src/api/braintrust.ts\nexport async function fetchLogsWithCustomAuth(\n  params: FetchLogsParams,\n  getToken: () =\u003e Promise\u003cstring\u003e  // Your auth function\n) {\n  const token = await getToken();\n\n  // Your backend proxies to Braintrust\n  const response = await fetch('/api/braintrust/logs', {\n    method: 'POST',\n    headers: {\n      'Authorization': `Bearer ${token}`,  // Your token\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify(params),\n  });\n\n  return response.json();\n}\n\n// Your backend (e.g., Next.js API route)\nexport async function POST(req: Request) {\n  const session = await getSession(req);\n  if (!session) return new Response('Unauthorized', { status: 401 });\n\n  // Use your stored Braintrust API key\n  const btApiKey = process.env.BRAINTRUST_API_KEY;\n\n  // Forward to Braintrust\n  const data = await req.json();\n  const btResponse = await fetchRecentLogs({\n    ...data,\n    apiKey: btApiKey,\n  });\n\n  return Response.json(btResponse);\n}\n```\n\n## Extension Possibilities Summary\n\nThe demo can be extended in many directions:\n\n| Extension | Complexity | Value |\n|-----------|------------|-------|\n| Filtering \u0026 Search | Low | High |\n| Pagination | Low | Medium |\n| Real-time Updates | Medium | High |\n| Multi-trace Comparison | Medium | Medium |\n| Export Functionality | Low | Medium |\n| Custom Visualizations | High | High |\n| Workspace Management | Medium | High |\n| Bookmarks/Favorites | Low | Medium |\n| Custom Metadata Columns | Low | High |\n| Custom Authentication | Medium | High |\n\nPick the extensions that best serve your use case!\n\n## Environment Variables\n\nEnvironment variables are **optional** and only used to pre-fill the UI fields. Users can enter credentials directly in the web interface.\n\nAll environment variables must be prefixed with `VITE_` to be accessible in the browser:\n\n| Variable | Required | Description | Example |\n|----------|----------|-------------|---------|\n| `VITE_BRAINTRUST_URL` | No | Your Braintrust instance URL | `https://www.braintrust.dev` |\n| `VITE_BRAINTRUST_ORG` | No | Your organization name | `my-company` |\n| `VITE_BRAINTRUST_PROJECT` | No | Your project name | `my-project` |\n| `VITE_BRAINTRUST_API_KEY` | No | Your API key | `sk_1234567890abcdef` |\n\n## Finding Your Project ID\n\nThe app automatically fetches your project ID (UUID) from the project slug when you provide your organization name, project slug, and API key. If needed, you can also manually enter the project UUID which can be found in your project settings in the Braintrust UI.\n\n## Use Cases\n\nThis embedding approach is useful for:\n\n1. **Internal Tools**: Embed traces in your internal debugging dashboards\n2. **Customer Support**: Show traces to customers in your support interface\n3. **Documentation**: Embed example traces in your documentation\n4. **IDE Integration**: Companies like Replit can embed traces directly in their IDE\n5. **Custom Workflows**: Build custom UIs that incorporate Braintrust traces\n6. **Debugging Tools**: Create specialized debugging interfaces with trace viewing\n7. **Log Exploration**: Browse and filter logs before diving into specific traces\n\n## Development\n\n### CORS and API Proxy\n\n**Important:** The Braintrust API does not allow direct browser requests due to CORS (Cross-Origin Resource Sharing) restrictions. This is a security feature that prevents unauthorized cross-origin requests.\n\n**Development Solution:**\n\nWe use **Vite's built-in proxy** to forward API requests through the dev server:\n\n```typescript\n// vite.config.ts\nexport default defineConfig({\n  server: {\n    proxy: {\n      '/api/btql': {\n        target: 'https://api.braintrust.dev',\n        changeOrigin: true,\n        rewrite: (path) =\u003e path.replace(/^\\/api/, ''),\n      },\n    },\n  },\n});\n```\n\nThis means:\n- Browser requests go to `http://localhost:5173/api/btql` (same origin, no CORS)\n- Vite forwards them to `https://api.braintrust.dev/btql` (server-side, no CORS)\n- Response comes back through Vite to the browser\n\n**Production Solution:**\n\nFor production deployments, you have several options:\n\n**Option 1: Backend Proxy (Recommended)**\n```typescript\n// Your backend API route (e.g., Next.js API route)\nexport async function POST(req: Request) {\n  const { projectId, apiKey } = await req.json();\n\n  // Your backend makes the request (no CORS issues)\n  const response = await fetch('https://api.braintrust.dev/btql', {\n    method: 'POST',\n    headers: {\n      'Authorization': `Bearer ${process.env.BRAINTRUST_API_KEY}`,\n      'Content-Type': 'application/json',\n    },\n    body: JSON.stringify({ query: '...' }),\n  });\n\n  return Response.json(await response.json());\n}\n```\n\n**Option 2: Serverless Function**\nDeploy a serverless function (Vercel, Netlify, AWS Lambda) that proxies requests.\n\n**Option 3: Nginx/Apache Proxy**\nConfigure your web server to proxy `/api/btql` to `https://api.braintrust.dev/btql`.\n\n**Why Backend Proxy is Recommended:**\n- ✅ Keeps API keys secure (never exposed to browser)\n- ✅ No CORS issues\n- ✅ Can add authentication/authorization\n- ✅ Can add caching, rate limiting, logging\n- ✅ More control over API usage\n\n### Available Scripts\n\n```bash\n# Start development server\npnpm dev\n\n# Build for production\npnpm build\n\n# Preview production build\npnpm preview\n\n# Lint code\npnpm lint\n\n# Type check\npnpm type-check\n```\n\n### Adding New Features\n\nThe codebase is structured to be easily extensible:\n\n1. **Add new API endpoints**: Update `src/api/braintrust.ts`\n2. **Create new hooks**: Add to `src/hooks/`\n3. **Add new components**: Add to `src/components/`\n4. **Extend types**: Update `src/types.ts`\n5. **Custom styling**: Modify Tailwind classes in components or extend `tailwind.config.js`\n\n### Customizing Log Queries\n\nYou can customize the BTQL query in `src/api/braintrust.ts`:\n\n```typescript\n// Change the time window\nWHERE created \u003e= NOW() - INTERVAL 30 DAY\n\n// Filter by specific criteria\nWHERE created \u003e= NOW() - INTERVAL 7 DAY\n  AND metadata.user_id = 'user_123'\n  AND tags @\u003e ARRAY['production']\n\n// Change result limit\nLIMIT 100\n```\n\n## Troubleshooting\n\n**Logs not loading:**\n- Verify your API key is valid and has permissions\n- Check that the Project ID is correct (UUID format)\n- Ensure logs exist for your project in the last 7 days\n- Check browser console for API errors\n\n**iframe not loading:**\n- Check that the org and project slugs are correct\n- Verify the API key has permissions to view traces\n- Look for CORS or security errors in the browser console\n\n**Build errors:**\n- Run `pnpm install` to ensure all dependencies are installed\n- Check for TypeScript errors with `pnpm type-check`\n- Clear the build cache: `rm -rf dist node_modules/.vite`\n\n**API authentication errors:**\n- Make sure your API key starts with `sk_`\n- Verify the API key hasn't expired or been revoked\n- Check that your Braintrust instance URL is correct\n\n## Technical Details\n\n### BTQL API\n\nThe app uses Braintrust's BTQL (Braintrust Query Language) API to fetch logs:\n\n**Endpoint**: `POST https://api.braintrust.dev/btql`\n\n**Authentication**: Bearer token in Authorization header\n\n**Query Format**:\n```json\n{\n  \"query\": \"SELECT * FROM project_logs('project-id') WHERE ...\",\n  \"fmt\": \"json\"\n}\n```\n\n**Response Format**:\n```json\n{\n  \"data\": [\n    {\n      \"id\": \"log_id\",\n      \"root_span_id\": \"span_id\",\n      \"span_attributes\": { \"name\": \"...\", \"type\": \"...\" },\n      \"created\": \"2025-01-15T10:30:00Z\",\n      \"metadata\": {},\n      \"tags\": [],\n      \"error\": null\n    }\n  ],\n  \"cursor\": \"optional_pagination_cursor\"\n}\n```\n\n### Browser Compatibility\n\n- Modern browsers with ES2020 support\n- iframe support required\n- postMessage API support required\n- All major browsers (Chrome, Firefox, Safari, Edge) are supported\n\n### Performance\n\n- Lazy loading: iframe content loads on demand\n- No polling: Uses postMessage for efficient communication\n- Fast dev server: Vite provides instant HMR\n- Optimized production build: Minified and tree-shaken\n- Efficient API calls: Fetches only when needed\n\n## Deployment\n\n### Static Hosting\n\nThis app can be deployed to any static hosting service:\n\n```bash\npnpm build\n# Deploy the 'dist' folder to your hosting service\n```\n\n**Recommended services:**\n- Vercel\n- Netlify\n- Cloudflare Pages\n- AWS S3 + CloudFront\n- GitHub Pages\n\n### Docker\n\nCreate a `Dockerfile`:\n\n```dockerfile\nFROM node:20-alpine AS builder\nWORKDIR /app\nCOPY package.json pnpm-lock.yaml ./\nRUN npm install -g pnpm \u0026\u0026 pnpm install\nCOPY . .\nRUN pnpm build\n\nFROM nginx:alpine\nCOPY --from=builder /app/dist /usr/share/nginx/html\nEXPOSE 80\nCMD [\"nginx\", \"-g\", \"daemon off;\"]\n```\n\nBuild and run:\n\n```bash\ndocker build -t braintrust-trace-embed-demo .\ndocker run -p 8080:80 braintrust-trace-embed-demo\n```\n\n## API Documentation\n\nFor more information about the Braintrust API:\n\n- [API Walkthrough](https://www.braintrust.dev/docs/guides/api)\n- [View Logs Documentation](https://www.braintrust.dev/docs/guides/logs/view)\n- [BTQL Query Language](https://www.braintrust.dev/docs/reference/btql)\n\n## Resources\n\n- [Braintrust Documentation](https://www.braintrust.dev/docs)\n- [React Documentation](https://react.dev)\n- [TypeScript Documentation](https://www.typescriptlang.org/docs)\n- [Vite Documentation](https://vite.dev)\n- [Tailwind CSS Documentation](https://tailwindcss.com/docs)\n\n## Support\n\nFor questions or issues:\n- Contact your Braintrust account team\n- Check the [Braintrust documentation](https://www.braintrust.dev/docs)\n- Open an issue in your organization's support channel\n\n## What's Next?\n\nIdeas for extending this demo:\n\n- [ ] Add filtering by tags, metadata, or date range\n- [ ] Implement pagination for large result sets\n- [ ] Add search functionality for logs\n- [ ] Export logs to CSV or JSON\n- [ ] Add comparison view for multiple traces\n- [ ] Implement real-time log updates\n- [ ] Add keyboard shortcuts for navigation\n- [ ] Implement dark mode toggle\n- [ ] Add bookmarking/favoriting of traces\n- [ ] Create custom log grouping and aggregation\n\n## License\n\nThis demo is provided as-is for demonstration purposes.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdpguthrie%2Fbraintrust-trace-embed-demo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdpguthrie%2Fbraintrust-trace-embed-demo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdpguthrie%2Fbraintrust-trace-embed-demo/lists"}