- Updated the Vision function and associated handlers to accept a forceUses parameter, allowing explicit control over the use of external tools regardless of model capabilities. - Modified the Assistant's BuildContent method to set the AssistantID in the context for better file tracking. - Enhanced the applyCreateResponseOptions method to merge and prioritize Uses and ForceUses configurations from the createResponse. - Added comprehensive tests to validate the adjustment of Uses and ForceUses configurations through hooks, ensuring correct application in content processing.
262 lines
6.5 KiB
Go
262 lines
6.5 KiB
Go
package content
|
|
|
|
import "github.com/yaoapp/yao/agent/context"
|
|
|
|
// FileType represents the type of file content
|
|
type FileType string
|
|
|
|
const (
|
|
// Image types
|
|
FileTypeImage FileType = "image"
|
|
|
|
// Audio types
|
|
FileTypeAudio FileType = "audio"
|
|
|
|
// Document types
|
|
FileTypeText FileType = "text"
|
|
FileTypePDF FileType = "pdf"
|
|
FileTypeWord FileType = "word"
|
|
FileTypeExcel FileType = "excel"
|
|
FileTypePPT FileType = "ppt"
|
|
FileTypeCSV FileType = "csv"
|
|
|
|
// Data types
|
|
FileTypeJSON FileType = "json"
|
|
FileTypeXML FileType = "xml"
|
|
|
|
// Binary
|
|
FileTypeBinary FileType = "binary"
|
|
|
|
// Other
|
|
FileTypeUnknown FileType = "unknown"
|
|
)
|
|
|
|
// Source represents where the content comes from
|
|
type Source string
|
|
|
|
const (
|
|
SourceHTTP Source = "http" // HTTP(S) URL
|
|
SourceUploader Source = "uploader" // Uploader wrapper: __uploader://fileid
|
|
SourceBase64 Source = "base64" // Base64 encoded data
|
|
SourceLocal Source = "local" // Local file path
|
|
)
|
|
|
|
// Result represents the result of content handling
|
|
type Result struct {
|
|
Text string // Extracted text content
|
|
ContentPart *context.ContentPart // Processed ContentPart (for model input)
|
|
Metadata map[string]interface{} // Additional metadata
|
|
Error error // Error if handling failed
|
|
}
|
|
|
|
// Info holds information about a content part to be handled
|
|
type Info struct {
|
|
Source Source // Where the content comes from
|
|
FileType FileType // Type of the file
|
|
ContentType string // MIME content type
|
|
URL string // Original URL or file ID
|
|
Filename string // Original filename (if available)
|
|
Data []byte // File data (if already fetched)
|
|
|
|
// For uploader wrapper
|
|
UploaderName string // Uploader name from wrapper
|
|
FileID string // File ID from wrapper
|
|
}
|
|
|
|
// DetectFileType detects file type from content type, filename, and file extension
|
|
func DetectFileType(contentType, filename string) FileType {
|
|
// Check by content type first
|
|
switch {
|
|
case contentType == "application/pdf":
|
|
return FileTypePDF
|
|
case contentType == "application/json":
|
|
return FileTypeJSON
|
|
case contentType == "application/xml" || contentType == "text/xml":
|
|
return FileTypeXML
|
|
case contentType == "text/csv":
|
|
return FileTypeCSV
|
|
case contentType == "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
contentType == "application/msword":
|
|
return FileTypeWord
|
|
case contentType == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
contentType == "application/vnd.ms-excel":
|
|
return FileTypeExcel
|
|
case contentType == "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
contentType == "application/vnd.ms-powerpoint":
|
|
return FileTypePPT
|
|
}
|
|
|
|
// Check image types
|
|
if isImageContentType(contentType) {
|
|
return FileTypeImage
|
|
}
|
|
|
|
// Check audio types
|
|
if isAudioContentType(contentType) {
|
|
return FileTypeAudio
|
|
}
|
|
|
|
// Check text types
|
|
if isTextContentType(contentType) {
|
|
return FileTypeText
|
|
}
|
|
|
|
// If content type doesn't help, check file extension
|
|
if filename != "" {
|
|
if ext := getFileExtension(filename); ext != "" {
|
|
return detectTypeByExtension(ext)
|
|
}
|
|
}
|
|
|
|
return FileTypeUnknown
|
|
}
|
|
|
|
// isImageContentType checks if content type is an image
|
|
func isImageContentType(contentType string) bool {
|
|
return contentType != "" &&
|
|
(contentType == "image/png" ||
|
|
contentType == "image/jpeg" ||
|
|
contentType == "image/jpg" ||
|
|
contentType == "image/gif" ||
|
|
contentType == "image/webp" ||
|
|
contentType == "image/svg+xml" ||
|
|
contentType == "image/bmp")
|
|
}
|
|
|
|
// isAudioContentType checks if content type is audio
|
|
func isAudioContentType(contentType string) bool {
|
|
return contentType != "" &&
|
|
(contentType == "audio/mpeg" ||
|
|
contentType == "audio/mp3" ||
|
|
contentType == "audio/wav" ||
|
|
contentType == "audio/ogg" ||
|
|
contentType == "audio/flac" ||
|
|
contentType == "audio/aac")
|
|
}
|
|
|
|
// isTextContentType checks if content type is text-based
|
|
func isTextContentType(contentType string) bool {
|
|
if contentType == "" {
|
|
return false
|
|
}
|
|
|
|
// Common text MIME types
|
|
textTypes := []string{
|
|
"text/plain",
|
|
"text/html",
|
|
"text/css",
|
|
"text/javascript",
|
|
"text/markdown",
|
|
"text/x-markdown",
|
|
"application/javascript",
|
|
"application/typescript",
|
|
"application/x-yaml",
|
|
"application/yaml",
|
|
"application/toml",
|
|
"application/x-sh",
|
|
"application/x-python",
|
|
"application/x-ruby",
|
|
"application/x-perl",
|
|
"application/x-php",
|
|
"application/x-go",
|
|
}
|
|
|
|
for _, t := range textTypes {
|
|
if contentType == t {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// getFileExtension extracts file extension from filename (without dot)
|
|
func getFileExtension(filename string) string {
|
|
for i := len(filename) - 1; i >= 0; i-- {
|
|
if filename[i] == '.' {
|
|
return filename[i+1:]
|
|
}
|
|
if filename[i] == '/' || filename[i] == '\\' {
|
|
break
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// detectTypeByExtension detects file type by file extension
|
|
func detectTypeByExtension(ext string) FileType {
|
|
// Normalize to lowercase
|
|
ext = toLower(ext)
|
|
|
|
// Image extensions
|
|
imageExts := []string{"png", "jpg", "jpeg", "gif", "webp", "svg", "bmp", "ico"}
|
|
for _, e := range imageExts {
|
|
if ext == e {
|
|
return FileTypeImage
|
|
}
|
|
}
|
|
|
|
// Audio extensions
|
|
audioExts := []string{"mp3", "wav", "ogg", "flac", "aac", "m4a"}
|
|
for _, e := range audioExts {
|
|
if ext == e {
|
|
return FileTypeAudio
|
|
}
|
|
}
|
|
|
|
// Document extensions
|
|
switch ext {
|
|
case "pdf":
|
|
return FileTypePDF
|
|
case "doc", "docx":
|
|
return FileTypeWord
|
|
case "xls", "xlsx":
|
|
return FileTypeExcel
|
|
case "ppt", "pptx":
|
|
return FileTypePPT
|
|
case "csv":
|
|
return FileTypeCSV
|
|
case "json":
|
|
return FileTypeJSON
|
|
case "xml":
|
|
return FileTypeXML
|
|
}
|
|
|
|
// Code and text file extensions (very comprehensive list)
|
|
textExts := []string{
|
|
"txt", "text", "md", "markdown", "rst",
|
|
// Programming languages
|
|
"go", "py", "js", "ts", "jsx", "tsx", "java", "c", "cpp", "h", "hpp",
|
|
"cs", "rb", "php", "pl", "swift", "kt", "rs", "scala", "clj",
|
|
// Web
|
|
"html", "htm", "css", "scss", "sass", "less",
|
|
// Config
|
|
"yaml", "yml", "toml", "ini", "conf", "config",
|
|
// Shell
|
|
"sh", "bash", "zsh", "fish",
|
|
// Data
|
|
"sql", "graphql", "proto",
|
|
// Others
|
|
"log", "gitignore", "env", "dockerfile",
|
|
}
|
|
for _, e := range textExts {
|
|
if ext == e {
|
|
return FileTypeText
|
|
}
|
|
}
|
|
|
|
return FileTypeUnknown
|
|
}
|
|
|
|
// toLower converts ASCII string to lowercase (simple version)
|
|
func toLower(s string) string {
|
|
result := make([]byte, len(s))
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
if c >= 'A' && c <= 'Z' {
|
|
c += 'a' - 'A'
|
|
}
|
|
result[i] = c
|
|
}
|
|
return string(result)
|
|
}
|