mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-04-13 00:54:53 +08:00
media abstraction: add structured MediaItem model and expose media-source audit in task queue detail
This commit is contained in:
@@ -374,6 +374,8 @@ func (al *AgentLoop) appendTaskAuditEvent(taskID string, msg bus.InboundMessage,
|
||||
"retry_count": 0,
|
||||
"log": logText,
|
||||
"input_preview": truncate(strings.ReplaceAll(msg.Content, "\n", " "), 180),
|
||||
"media_count": len(msg.MediaItems),
|
||||
"media_items": msg.MediaItems,
|
||||
}
|
||||
b, _ := json.Marshal(row)
|
||||
f, err := os.OpenFile(path, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
package bus
|
||||
|
||||
type MediaItem struct {
|
||||
Source string `json:"source,omitempty"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Ref string `json:"ref,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Channel string `json:"channel,omitempty"`
|
||||
}
|
||||
|
||||
type InboundMessage struct {
|
||||
Channel string `json:"channel"`
|
||||
SenderID string `json:"sender_id"`
|
||||
ChatID string `json:"chat_id"`
|
||||
Content string `json:"content"`
|
||||
Media []string `json:"media,omitempty"`
|
||||
MediaItems []MediaItem `json:"media_items,omitempty"`
|
||||
SessionKey string `json:"session_key"`
|
||||
Metadata map[string]string `json:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package channels
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
@@ -96,6 +97,7 @@ func (c *BaseChannel) HandleMessage(senderID, chatID, content string, media []st
|
||||
ChatID: chatID,
|
||||
Content: content,
|
||||
Media: media,
|
||||
MediaItems: toMediaItems(c.name, media),
|
||||
Metadata: metadata,
|
||||
SessionKey: sessionKey,
|
||||
}
|
||||
@@ -103,6 +105,46 @@ func (c *BaseChannel) HandleMessage(senderID, chatID, content string, media []st
|
||||
c.bus.PublishInbound(msg)
|
||||
}
|
||||
|
||||
func toMediaItems(channel string, media []string) []bus.MediaItem {
|
||||
if len(media) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]bus.MediaItem, 0, len(media))
|
||||
for _, m := range media {
|
||||
item := bus.MediaItem{Channel: channel, Ref: m, Source: "raw", Type: "unknown"}
|
||||
switch {
|
||||
case strings.HasPrefix(m, "feishu:image:"):
|
||||
item.Source = "feishu"
|
||||
item.Type = "image"
|
||||
case strings.HasPrefix(m, "feishu:file:"):
|
||||
item.Source = "feishu"
|
||||
item.Type = "file"
|
||||
case strings.HasPrefix(m, "telegram:"):
|
||||
item.Source = "telegram"
|
||||
item.Type = "remote"
|
||||
case strings.HasPrefix(m, "http://") || strings.HasPrefix(m, "https://"):
|
||||
item.Source = "url"
|
||||
item.Type = "remote"
|
||||
default:
|
||||
ext := strings.ToLower(filepath.Ext(m))
|
||||
item.Path = m
|
||||
switch ext {
|
||||
case ".jpg", ".jpeg", ".png", ".gif", ".webp", ".bmp":
|
||||
item.Type = "image"
|
||||
case ".mp4", ".mov", ".webm", ".avi":
|
||||
item.Type = "video"
|
||||
case ".mp3", ".wav", ".ogg", ".m4a":
|
||||
item.Type = "audio"
|
||||
default:
|
||||
item.Type = "file"
|
||||
}
|
||||
item.Source = "local"
|
||||
}
|
||||
out = append(out, item)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (c *BaseChannel) setRunning(running bool) {
|
||||
c.running.Store(running)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ const resources = {
|
||||
taskDetail: 'Task Detail',
|
||||
taskQueue: 'Task Queue',
|
||||
taskLogs: 'Task Logs',
|
||||
mediaSources: 'Media Sources',
|
||||
error: 'Error',
|
||||
noTaskAudit: 'No task audit records',
|
||||
selectTask: 'Select a task from the left list',
|
||||
@@ -170,6 +171,7 @@ const resources = {
|
||||
taskDetail: '任务详情',
|
||||
taskQueue: '任务队列',
|
||||
taskLogs: '任务日志',
|
||||
mediaSources: '媒体来源',
|
||||
error: '错误',
|
||||
noTaskAudit: '暂无任务审计记录',
|
||||
selectTask: '请从左侧选择任务',
|
||||
|
||||
@@ -15,6 +15,7 @@ type TaskAuditItem = {
|
||||
error?: string;
|
||||
input_preview?: string;
|
||||
logs?: string[];
|
||||
media_items?: Array<{ source?: string; type?: string; ref?: string; path?: string; channel?: string }>;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
@@ -110,6 +111,21 @@ const TaskAudit: React.FC = () => {
|
||||
<div className="p-2 rounded bg-zinc-950/60 border border-zinc-800 whitespace-pre-wrap text-zinc-200">{Array.isArray(selected.logs) && selected.logs.length ? selected.logs.join('\n') : '-'}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="text-zinc-500 text-xs mb-1">{t('mediaSources')}</div>
|
||||
<div className="p-2 rounded bg-zinc-950/60 border border-zinc-800 text-xs">
|
||||
{Array.isArray(selected.media_items) && selected.media_items.length > 0 ? (
|
||||
<div className="space-y-1">
|
||||
{selected.media_items.map((m, i) => (
|
||||
<div key={i} className="font-mono break-all text-zinc-200">
|
||||
[{m.channel || '-'}] {m.source || '-'} / {m.type || '-'} · {m.path || m.ref || '-'}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : '-'}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div className="text-zinc-500 text-xs mb-1">{t('rawJson')}</div>
|
||||
<pre className="p-2 rounded bg-zinc-950/60 border border-zinc-800 text-xs overflow-auto">{selectedPretty}</pre>
|
||||
|
||||
Reference in New Issue
Block a user