mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-10-16 02:15:37 +00:00
fix: read_audio fails when LLM passes literal <media:audio> as media_id (#156)
The read_audio tool errors with "audio not found" because: 1. BuildMediaTags() creates bare <media:audio> tags without IDs 2. persistMedia() generates UUIDs stored in context, not in message content 3. LLM sees <media:audio> and passes it literally as media_id parameter 4. resolveAudioFile() tries to match "<media:audio>" against UUIDs — fails Two-part fix: - enrichAudioIDs(): embed persisted UUIDs into <media:audio> tags (like enrichDocumentPaths does for documents), so LLM sees actual IDs - resolveAudioFile(): sanitize tag-like media_id values and fallback to most recent audio instead of hard error on unmatched IDs Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
This commit is contained in:
1 parent
b5b40e7d94
commit
7386fc8ad7
3 files changed
+54
-1
No files matched your search
@@ -259,6 +259,8 @@ func (l *Loop) runLoop(ctx context.Context, req RunRequest) (*RunResult, error)
|
||||
}
|
||||
if len(audioRefs) > 0 {
|
||||
ctx = tools.WithMediaAudioRefs(ctx, audioRefs)
|
||||
// Embed media IDs into <media:audio> tags so LLM can reference them.
|
||||
l.enrichAudioIDs(messages, mediaRefs)
|
||||
}
|
||||
|
||||
// 2d. Collect video MediaRefs (historical + current) for read_video tool.
|
||||
|
||||
@@ -163,6 +163,47 @@ func (l *Loop) enrichDocumentPaths(messages []providers.Message, refs []provider
|
||||
messages[lastIdx].Content = content
|
||||
}
|
||||
|
||||
// enrichAudioIDs updates the last user message to embed persisted media IDs
|
||||
// in <media:audio> and <media:voice> tags so the LLM can reference them.
|
||||
// Without this, the LLM sees plain <media:audio> and cannot pass a valid media_id.
|
||||
func (l *Loop) enrichAudioIDs(messages []providers.Message, refs []providers.MediaRef) {
|
||||
if len(messages) == 0 {
|
||||
return
|
||||
}
|
||||
lastIdx := -1
|
||||
for i := len(messages) - 1; i >= 0; i-- {
|
||||
if messages[i].Role == "user" {
|
||||
lastIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if lastIdx < 0 {
|
||||
return
|
||||
}
|
||||
|
||||
content := messages[lastIdx].Content
|
||||
for _, ref := range refs {
|
||||
if ref.Kind != "audio" {
|
||||
continue
|
||||
}
|
||||
idAttr := fmt.Sprintf(" id=%q", ref.ID)
|
||||
|
||||
// Replace bare <media:audio> with <media:audio id="uuid">
|
||||
bare := "<media:audio>"
|
||||
if strings.Contains(content, bare) {
|
||||
content = strings.Replace(content, bare, "<media:audio"+idAttr+">", 1)
|
||||
continue
|
||||
}
|
||||
// Replace bare <media:voice> with <media:voice id="uuid">
|
||||
bareVoice := "<media:voice>"
|
||||
if strings.Contains(content, bareVoice) {
|
||||
content = strings.Replace(content, bareVoice, "<media:voice"+idAttr+">", 1)
|
||||
continue
|
||||
}
|
||||
}
|
||||
messages[lastIdx].Content = content
|
||||
}
|
||||
|
||||
// mediaKindFromMime returns the media kind ("image", "video", "audio", "document")
|
||||
// based on MIME type prefix.
|
||||
func mediaKindFromMime(mime string) string {
|
||||
|
||||
@@ -23,6 +23,13 @@ func (t *ReadAudioTool) resolveAudioFile(ctx context.Context, mediaID string) (p
|
||||
return "", "", fmt.Errorf("no audio files available in this conversation. The user may not have sent an audio file.")
|
||||
}
|
||||
|
||||
// Sanitize media_id: LLM may pass the literal tag string (e.g. "<media:audio>")
|
||||
// instead of a UUID. Treat tag-like values as empty to fall back to most recent.
|
||||
if strings.Contains(mediaID, "<") || strings.Contains(mediaID, "media:") {
|
||||
slog.Debug("read_audio: sanitizing tag-like media_id", "raw", mediaID)
|
||||
mediaID = ""
|
||||
}
|
||||
|
||||
var ref *providers.MediaRef
|
||||
if mediaID != "" {
|
||||
for i := range refs {
|
||||
@@ -32,7 +39,10 @@ func (t *ReadAudioTool) resolveAudioFile(ctx context.Context, mediaID string) (p
|
||||
}
|
||||
}
|
||||
if ref == nil {
|
||||
return "", "", fmt.Errorf("audio with media_id %q not found in conversation", mediaID)
|
||||
// Fallback to most recent audio instead of hard error,
|
||||
// since LLM may generate invalid IDs.
|
||||
slog.Warn("read_audio: media_id not found, falling back to most recent", "media_id", mediaID)
|
||||
ref = &refs[len(refs)-1]
|
||||
}
|
||||
} else {
|
||||
ref = &refs[len(refs)-1]
|
||||
|
||||
Reference in new issue
Block a user