Skip to content

feat(audit): capture and play back transcription input audio#365

Merged
SantiagoDePolonia merged 2 commits into
mainfrom
feat/audio-transcription-input-playback
Jun 4, 2026
Merged

feat(audit): capture and play back transcription input audio#365
SantiagoDePolonia merged 2 commits into
mainfrom
feat/audio-transcription-input-playback

Conversation

@SantiagoDePolonia

@SantiagoDePolonia SantiagoDePolonia commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Summary

Makes audio audit logging symmetric. Previously, with LOGGING_LOG_AUDIO_BODIES enabled, only the output of /v1/audio/speech was stored as playable base64; the input audio uploaded to /v1/audio/transcriptions was recorded as metadata only. Now the uploaded audio is captured too, so the dashboard renders a player in the Request pane.

Behavior

When LOGGING_LOG_AUDIO_BODIES is on (and LOGGING_LOG_BODIES, the master switch, is on):

  • /v1/audio/transcriptions stores the uploaded audio losslessly as base64 — same 8 MB cap and too_large fallback as speech output — alongside the existing upload metadata (model, filename, language, params).
  • The dashboard shows an <audio> player for the request body, with the upload parameters listed beneath it.

When the flag is off, the upload is not captured (unchanged).

Implementation

  • internal/core/audio.go: capture FileContentType on the transcription request.
  • internal/server/audio_service.go: embed the upload via BuildAudioUploadBody; resolve the content type from the multipart part, falling back to the filename extension (audioUploadContentType).
  • internal/auditlog/audio_body.go: add AudioBodyLog.Meta and BuildAudioUploadBody (shared builder with BuildAudioResponseBody) so the audio and its parameters are recorded together.
  • internal/admin/dashboard: renderAudioBody lists attached metadata below the player; CSS for the metadata block.

Testing

  • Go: BuildAudioUploadBody unit tests (base64 + meta, placeholder keeps meta); service tests that the transcription request body is a stored, playable AudioBodyLog with metadata when enabled, and absent when disabled.
  • JS: renderAudioBody renders player + metadata rows (and metadata even when too_large). 32/32 dashboard tests pass.
  • Manual: a TTS clip uploaded to /v1/audio/transcriptions is stored and decodes byte-identical; content type resolves to audio/mpeg from the .mp3 extension; metadata (model, language) captured.
  • Pre-commit gates (test-race, lint, mint, dashboard JS) all pass.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Uploaded transcription audio can be stored (base64, ≤8 MB per endpoint) and replayed in the dashboard; metadata shown alongside playback.
  • Documentation

    • Clarified audio logging settings, defaults, per-endpoint size limits, and when uploaded audio vs. metadata is stored.
  • Tests

    • Added tests for rendering audio metadata and for transcription audit logging behavior.

When LOGGING_LOG_AUDIO_BODIES is enabled, /v1/audio/transcriptions now stores
the uploaded audio losslessly as base64 (same 8 MB cap and too_large fallback as
speech output) alongside the existing upload metadata, so the dashboard renders
a player for the request body — matching the playback already available for
speech responses.

- Resolve the upload's content type from the multipart part, falling back to the
  filename extension (capture FileContentType on the transcription request).
- Add AudioBodyLog.Meta + BuildAudioUploadBody so the audio and its parameters
  are recorded together; the dashboard lists the metadata under the player.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Captures uploaded transcription audio (optionally base64, ≤8 MB) and upload metadata into audit entries when enabled; refactors audio-body construction, records file Content-Type, normalizes MIME type from headers or filename, renders metadata in the dashboard, and updates docs and tests.

Changes

Audio Metadata Audit Logging for Transcriptions

Layer / File(s) Summary
Audit body schema with metadata support
internal/auditlog/audio_body.go, internal/auditlog/audio_body_test.go
AudioBodyLog gains explicit JSON/BSON tags and a Meta map; buildAudioBody centralizes construction and BuildAudioUploadBody accepts upload metadata; tests verify base64 storage and metadata preservation or placeholder behavior.
Transcription audit capture and MIME type handling
internal/core/audio.go, internal/server/audio_service.go, internal/server/audio_service_test.go
AudioTranscriptionRequest adds FileContentType; multipart part Content-Type is recorded; CreateTranscription uses BuildAudioUploadBody with audioUploadContentType (prefers declared audio type, else maps filename extension, fallback audio/mpeg); tests cover stored audio, placeholder metadata, no-capture when bodies disabled, and content-type normalization.
Dashboard metadata display in audit UI
internal/admin/dashboard/static/css/dashboard.css, internal/admin/dashboard/static/js/modules/conversation-helpers.js, internal/admin/dashboard/static/js/modules/audit-list.test.cjs
CSS adds .audit-audio-metadata layout; conversation-helpers.js adds formatAudioMetaValue and renderAudioMeta, and renderAudioBody now includes the metadata block under the audio player; unit tests assert metadata rendering for stored and too-large cases.
Configuration and API documentation updates
CLAUDE.md, docs/advanced/audio-api.mdx, docs/advanced/configuration.mdx
Clarifies LOGGING_LOG_AUDIO_BODIES behavior: when enabled (and LOGGING_LOG_BODIES on) the system stores base64 audio payloads capped at 8 MB for /v1/audio/speech output and /v1/audio/transcriptions uploads, and transcription upload metadata is included; documents the placeholder when audio storage is disabled.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Server
  participant AuditLog
  participant Dashboard
  Client->>Server: POST /v1/audio/transcriptions (multipart file + fields)
  Server->>Server: transcriptionRequestFromForm reads part Content-Type into FileContentType
  Server->>Server: audioUploadContentType(normalize MIME or infer from filename)
  Server->>AuditLog: BuildAudioUploadBody(contentType, bytes, storeBytes, meta)
  AuditLog->>AuditLog: store `AudioBodyLog` (meta + base64 bytes or placeholder)
  Dashboard->>AuditLog: request audit entry
  AuditLog->>Dashboard: return AudioBodyLog (includes meta and playable bytes if stored)
  Dashboard->>Client: render audio player + metadata
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ENTERPILOT/GoModel#364: Directly extends the audio audit pipeline functionality (audio_body.go, audio_service.go, dashboard rendering) introduced in that PR to support transcription metadata logging.
  • ENTERPILOT/GoModel#362: Builds on the transcription request handling (CreateTranscription, AudioTranscriptionRequest) introduced in that PR to capture uploaded file bytes and content-type into audit logging.

Poem

🐰 In burrows of bytes the auditors peep,

base64 carrots snug, metadata to keep.
Transcriptions sing back, eight meg at a time,
Dashboard hums play—each model and mime.
A rabbit hops by, and gives this change a chime.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(audit): capture and play back transcription input audio' clearly and specifically describes the main change: enabling audio capture and playback for transcription uploads in audit logging.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/audio-transcription-input-playback

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mintlify

mintlify Bot commented Jun 3, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
gomodel 🟢 Ready View Preview Jun 3, 2026, 4:28 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@greptile-apps

greptile-apps Bot commented Jun 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR makes transcription audio audit logging playable in the dashboard. It changes:

  • Captures uploaded transcription audio as base64 when audio body logging is enabled.
  • Keeps transcription upload metadata with the audio audit body.
  • Renders audio metadata below dashboard audio players and placeholders.
  • Normalizes uploaded audio content types for dashboard playback.
  • Updates docs and tests for the new transcription logging behavior.

Confidence Score: 5/5

This looks safe to merge.

  • No blocking issues found in the changed code.

Reviews (2): Last reviewed commit: "fix(audit): preserve transcription metad..." | Re-trigger Greptile

Comment thread internal/server/audio_service.go Outdated
Comment thread internal/server/audio_service.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/server/audio_service.go`:
- Around line 232-235: audioUploadContentType currently returns the raw
FileContentType (e.g. "audio/webm; codecs=opus") which then fails later in
renderAudioBody; update audioUploadContentType to parse and persist only the
base MIME type (strip any parameters after ';') — e.g. trim whitespace, use
mime.ParseMediaType or split on ';' and take the first token, validate with
auditlog.IsAudioContentType on that base type, and return that canonical type so
renderAudioBody and dashboard data URLs receive a clean MIME like "audio/webm".
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c041ce26-0a77-40e5-9b93-0f4040baeab6

📥 Commits

Reviewing files that changed from the base of the PR and between d8f5eff and 53cd47d.

📒 Files selected for processing (11)
  • CLAUDE.md
  • docs/advanced/audio-api.mdx
  • docs/advanced/configuration.mdx
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/modules/audit-list.test.cjs
  • internal/admin/dashboard/static/js/modules/conversation-helpers.js
  • internal/auditlog/audio_body.go
  • internal/auditlog/audio_body_test.go
  • internal/core/audio.go
  • internal/server/audio_service.go
  • internal/server/audio_service_test.go

Comment thread internal/server/audio_service.go
@codecov-commenter

codecov-commenter commented Jun 3, 2026

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 86.20690% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/server/audio_service.go 83.33% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

… normalize upload MIME

Address PR review:

- Gate transcription request capture on LogBodies (the master switch) rather than
  requiring LogAudioBodies too. Metadata (model, filename, language, params) is now
  recorded whenever body logging is on; LogAudioBodies only controls whether the
  raw audio is embedded as base64 vs a metadata-only placeholder — mirroring the
  speech-response behavior, so keeping raw audio off no longer drops the params.
- Strip MIME parameters from the uploaded part's Content-Type (e.g.
  "audio/webm; codecs=opus" -> "audio/webm") so the stored type is a bare media
  type the dashboard accepts; a parameterized type previously fell back to
  audio/mpeg and broke playback for wav/ogg/webm uploads.

Add a content-type resolver unit test and update the gating tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/server/audio_service_test.go (1)

232-249: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add one handler-level test for multipart Content-Type propagation.

These tests only cover extension fallback and the normalization helper in isolation. They do not verify that transcriptionRequestFromForm() actually copies the file part header into core.AudioTranscriptionRequest.FileContentType, so a regression on Line 180 in internal/server/audio_service.go would still pass. Please add an end-to-end case that builds a multipart part with an explicit header such as audio/webm; codecs=opus, calls CreateTranscription, and asserts both the captured request field and the normalized audit ContentType.

Suggested test shape
+func TestAudioTranscription_UsesMultipartFileContentTypeForAuditBody(t *testing.T) {
+	svc := &audioService{provider: newTranscriptionMock(), logBodies: true, logAudioBodies: true}
+
+	var buf bytes.Buffer
+	w := multipart.NewWriter(&buf)
+	_ = w.WriteField("model", "gpt-4o-transcribe")
+	h := make(textproto.MIMEHeader)
+	h.Set("Content-Disposition", `form-data; name="file"; filename="speech.bin"`)
+	h.Set("Content-Type", "audio/webm; codecs=opus")
+	part, err := w.CreatePart(h)
+	if err != nil {
+		t.Fatalf("CreatePart: %v", err)
+	}
+	_, _ = part.Write([]byte("uploaded-audio-bytes"))
+	_ = w.Close()
+
+	req := httptest.NewRequest(http.MethodPost, "/v1/audio/transcriptions", &buf)
+	req.Header.Set("Content-Type", w.FormDataContentType())
+	rec := httptest.NewRecorder()
+	c := echo.New().NewContext(req, rec)
+	entry := &auditlog.LogEntry{}
+	c.Set(string(auditlog.LogEntryKey), entry)
+
+	if err := svc.CreateTranscription(c); err != nil {
+		t.Fatalf("CreateTranscription returned error: %v", err)
+	}
+
+	if got := svc.provider.(*audioMockProvider).capturedTranscription.FileContentType; got != "audio/webm; codecs=opus" {
+		t.Fatalf("FileContentType = %q", got)
+	}
+	body := entry.Data.RequestBody.(auditlog.AudioBodyLog)
+	if body.ContentType != "audio/webm" {
+		t.Fatalf("audit content type = %q, want audio/webm", body.ContentType)
+	}
+}

As per coding guidelines, **/*_test.go: "Add or update tests for behavior changes. Tests should cover request translation, response normalization, error handling, default configuration, and provider-specific parameter mapping."

Also applies to: 263-347

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/server/audio_service_test.go` around lines 232 - 249, Add a
handler-level test that ensures multipart file part headers propagate into the
created transcription request and the audit entry: update or add to the tests
that call newTranscriptionRequestWithAuditEntry (or create a variant) to build a
multipart part with an explicit Content-Type like "audio/webm; codecs=opus",
invoke the server handler (CreateTranscription) so transcriptionRequestFromForm
runs, then assert that the returned/recorded
core.AudioTranscriptionRequest.FileContentType equals the explicit header and
that the auditlog.LogEntry stored on the context has its ContentType
normalized/recorded accordingly; reference
newTranscriptionRequestWithAuditEntry, CreateTranscription,
transcriptionRequestFromForm, core.AudioTranscriptionRequest.FileContentType and
auditlog.LogEntry when locating where to add the test.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@internal/server/audio_service_test.go`:
- Around line 232-249: Add a handler-level test that ensures multipart file part
headers propagate into the created transcription request and the audit entry:
update or add to the tests that call newTranscriptionRequestWithAuditEntry (or
create a variant) to build a multipart part with an explicit Content-Type like
"audio/webm; codecs=opus", invoke the server handler (CreateTranscription) so
transcriptionRequestFromForm runs, then assert that the returned/recorded
core.AudioTranscriptionRequest.FileContentType equals the explicit header and
that the auditlog.LogEntry stored on the context has its ContentType
normalized/recorded accordingly; reference
newTranscriptionRequestWithAuditEntry, CreateTranscription,
transcriptionRequestFromForm, core.AudioTranscriptionRequest.FileContentType and
auditlog.LogEntry when locating where to add the test.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 585fe64f-5bd4-48de-be89-5145d8221926

📥 Commits

Reviewing files that changed from the base of the PR and between 53cd47d and 8de3fde.

📒 Files selected for processing (3)
  • docs/advanced/configuration.mdx
  • internal/server/audio_service.go
  • internal/server/audio_service_test.go

@SantiagoDePolonia SantiagoDePolonia merged commit b22afc4 into main Jun 4, 2026
19 checks passed
@SantiagoDePolonia SantiagoDePolonia deleted the feat/audio-transcription-input-playback branch June 11, 2026 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants