mirror of
https://github.com/JohnRiceML/clawport-ui.git
synced 2026-08-14 08:51:58 +00:00
fix: return plain string for audio-only messages in buildApiContent
Voice messages were broken because buildApiContent() wrapped audio-only
messages in a ContentPart[] array instead of returning a plain string.
Since audio attachments are skipped (transcript is already in msg.content
via Whisper), the gateway received [{type:'text', text:'...'}] instead
of a simple string, which it couldn't handle properly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
353a89634f
commit
6db0895e5f
+7
-10
@@ -165,26 +165,23 @@ describe('buildApiContent — files', () => {
|
||||
// --- audio attachments ---
|
||||
|
||||
describe('buildApiContent — audio', () => {
|
||||
it('skips audio binary, keeps text content (transcript from Whisper)', () => {
|
||||
it('returns plain string for audio-only message (transcript from Whisper)', () => {
|
||||
const result = buildApiContent(msg({
|
||||
content: 'Hello this is my transcribed message',
|
||||
media: [audioAttachment()],
|
||||
}))
|
||||
const parts = result as Array<{ type: string; text?: string }>
|
||||
// Should only have the text part, no audio binary
|
||||
expect(parts).toHaveLength(1)
|
||||
expect(parts[0]).toEqual({ type: 'text', text: 'Hello this is my transcribed message' })
|
||||
// Audio is skipped and no other non-text parts exist, so return plain string
|
||||
// to avoid wrapping in ContentPart[] which the gateway may not handle
|
||||
expect(result).toBe('Hello this is my transcribed message')
|
||||
})
|
||||
|
||||
it('returns text content string when audio is the only attachment and content exists', () => {
|
||||
it('returns plain string when audio is the only attachment', () => {
|
||||
const result = buildApiContent(msg({
|
||||
content: 'transcribed words',
|
||||
media: [audioAttachment()],
|
||||
}))
|
||||
// With audio-only + text content, should return a parts array with just text
|
||||
const parts = result as Array<{ type: string }>
|
||||
expect(parts).toHaveLength(1)
|
||||
expect(parts[0].type).toBe('text')
|
||||
expect(typeof result).toBe('string')
|
||||
expect(result).toBe('transcribed words')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ export function buildApiContent(msg: Message): MessageContent {
|
||||
if (!media || media.length === 0) return msg.content
|
||||
|
||||
const parts: ContentPart[] = []
|
||||
let attachmentAdded = false
|
||||
|
||||
if (msg.content) {
|
||||
parts.push({ type: 'text', text: msg.content })
|
||||
@@ -22,6 +23,7 @@ export function buildApiContent(msg: Message): MessageContent {
|
||||
for (const attachment of media) {
|
||||
if (attachment.type === 'image') {
|
||||
parts.push({ type: 'image_url', image_url: { url: attachment.url } })
|
||||
attachmentAdded = true
|
||||
} else if (attachment.type === 'file') {
|
||||
const label = attachment.name || 'unknown'
|
||||
const sizeNote = attachment.size ? ` (${Math.round(attachment.size / 1024)} KB)` : ''
|
||||
@@ -32,10 +34,16 @@ export function buildApiContent(msg: Message): MessageContent {
|
||||
} else {
|
||||
parts.push({ type: 'text', text: `[Attached file: ${label}${sizeNote}]` })
|
||||
}
|
||||
attachmentAdded = true
|
||||
}
|
||||
// Audio: transcript already in msg.content via Whisper — skip binary
|
||||
}
|
||||
|
||||
// If no attachment actually contributed to parts (e.g., audio-only message
|
||||
// where the transcript is already in msg.content), return a plain string
|
||||
// so the gateway doesn't receive an unnecessary ContentPart[] wrapper.
|
||||
if (!attachmentAdded) return msg.content
|
||||
|
||||
return parts.length > 0 ? parts : msg.content
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user