fix(media): skip path tag append for JSON content (Feishu cards/posts)
When content is structured JSON (interactive cards, post messages),
injectPathTags now skips the fallback append — only placeholder
replacement is attempted. This prevents corrupting JSON payloads
like {"schema":"2.0",...} with appended [image:/path] tags.
Adds looksLikeJSON() helper and three test cases covering JSON
objects, arrays, and an end-to-end resolveMediaRefs scenario with
Feishu card content.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9561476347
commit
77beafa6d3
2 changed files with 55 additions and 0 deletions
|
|
@ -242,7 +242,12 @@ func buildPathTag(mime, localPath string) string {
|
|||
// or appends if no matching generic tag is found. Channels emit a few different
|
||||
// placeholder formats — [image], [image: photo], [image: filename.jpg] — so we
|
||||
// match all of them via regex while leaving path tags ([image:/path]) untouched.
|
||||
//
|
||||
// When content is structured data (e.g., JSON from Feishu interactive cards or
|
||||
// post messages), tags are only injected via placeholder replacement — never
|
||||
// appended — to avoid corrupting the payload.
|
||||
func injectPathTags(content string, tags []string) string {
|
||||
isStructured := looksLikeJSON(content)
|
||||
for _, tag := range tags {
|
||||
var pattern *regexp.Regexp
|
||||
switch {
|
||||
|
|
@ -263,6 +268,10 @@ func injectPathTags(content string, tags []string) string {
|
|||
}
|
||||
}
|
||||
|
||||
if isStructured {
|
||||
continue
|
||||
}
|
||||
|
||||
if content == "" {
|
||||
content = tag
|
||||
} else {
|
||||
|
|
@ -271,3 +280,8 @@ func injectPathTags(content string, tags []string) string {
|
|||
}
|
||||
return content
|
||||
}
|
||||
|
||||
func looksLikeJSON(s string) bool {
|
||||
s = strings.TrimSpace(s)
|
||||
return len(s) > 1 && (s[0] == '{' || s[0] == '[')
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5040,6 +5040,47 @@ func TestInjectPathTags_DoesNotReplacePathTag(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestInjectPathTags_SkipsAppendForJSONContent(t *testing.T) {
|
||||
jsonContent := `{"schema":"2.0","body":{"elements":[{"tag":"img","img_key":"img_123"}]}}`
|
||||
got := injectPathTags(jsonContent, []string{"[image:/tmp/photo.png]"})
|
||||
if got != jsonContent {
|
||||
t.Fatalf("expected JSON content unchanged, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInjectPathTags_JSONArrayContent(t *testing.T) {
|
||||
jsonContent := `[{"tag":"text","text":"hello"}]`
|
||||
got := injectPathTags(jsonContent, []string{"[file:/tmp/doc.pdf]"})
|
||||
if got != jsonContent {
|
||||
t.Fatalf("expected JSON array content unchanged, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveMediaRefs_JSONContentPreservesStructure(t *testing.T) {
|
||||
store := media.NewFileMediaStore()
|
||||
dir := t.TempDir()
|
||||
|
||||
pngPath := filepath.Join(dir, "card_img.png")
|
||||
pngHeader := []byte{
|
||||
0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A,
|
||||
0x00, 0x00, 0x00, 0x0D, 0x49, 0x48, 0x44, 0x52,
|
||||
0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02,
|
||||
0x00, 0x00, 0x00, 0x90, 0x77, 0x53, 0xDE,
|
||||
}
|
||||
os.WriteFile(pngPath, pngHeader, 0o644)
|
||||
ref, _ := store.Store(pngPath, media.MediaMeta{ContentType: "image/png"}, "test")
|
||||
|
||||
jsonContent := `{"schema":"2.0","body":{"elements":[{"tag":"img","img_key":"img_123"}]}}`
|
||||
messages := []providers.Message{
|
||||
{Role: "user", Content: jsonContent, Media: []string{ref}},
|
||||
}
|
||||
result := resolveMediaRefs(messages, store, config.DefaultMaxMediaSize)
|
||||
|
||||
if result[0].Content != jsonContent {
|
||||
t.Fatalf("expected JSON content preserved, got %q", result[0].Content)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveMediaRefs_EmptyContentGetsPathTag(t *testing.T) {
|
||||
store := media.NewFileMediaStore()
|
||||
dir := t.TempDir()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue