mirror of
https://github.com/danielmiessler/Fabric.git
synced 2026-01-10 23:08:06 -05:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1f3befbbbc | ||
|
|
8988206fbe | ||
|
|
1bd5f9d7e4 | ||
|
|
832fd2f718 | ||
|
|
dd0935fb70 | ||
|
|
e64bdd849c | ||
|
|
be82b4b013 | ||
|
|
6e2f00090c | ||
|
|
7d6505fe98 | ||
|
|
23c1437794 | ||
|
|
dd5e57477f | ||
|
|
2c2b374664 | ||
|
|
b884c529bd | ||
|
|
137aff2268 |
@@ -1,3 +1,3 @@
|
||||
package main
|
||||
|
||||
var version = "v1.4.242"
|
||||
var version = "v1.4.244"
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
default = self.packages.${system}.fabric;
|
||||
fabric = pkgs.callPackage ./nix/pkgs/fabric {
|
||||
go = goVersion;
|
||||
inherit self;
|
||||
inherit (gomod2nix.legacyPackages.${system}) buildGoApplication;
|
||||
};
|
||||
inherit (gomod2nix.legacyPackages.${system}) gomod2nix;
|
||||
|
||||
66
internal/cli/chat.go
Normal file
66
internal/cli/chat.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/core"
|
||||
"github.com/danielmiessler/fabric/internal/domain"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
|
||||
)
|
||||
|
||||
// handleChatProcessing handles the main chat processing logic
|
||||
func handleChatProcessing(currentFlags *Flags, registry *core.PluginRegistry, messageTools string) (err error) {
|
||||
if messageTools != "" {
|
||||
currentFlags.AppendMessage(messageTools)
|
||||
}
|
||||
|
||||
var chatter *core.Chatter
|
||||
if chatter, err = registry.GetChatter(currentFlags.Model, currentFlags.ModelContextLength,
|
||||
currentFlags.Strategy, currentFlags.Stream, currentFlags.DryRun); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var session *fsdb.Session
|
||||
var chatReq *domain.ChatRequest
|
||||
if chatReq, err = currentFlags.BuildChatRequest(strings.Join(os.Args[1:], " ")); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if chatReq.Language == "" {
|
||||
chatReq.Language = registry.Language.DefaultLanguage.Value
|
||||
}
|
||||
var chatOptions *domain.ChatOptions
|
||||
if chatOptions, err = currentFlags.BuildChatOptions(); err != nil {
|
||||
return
|
||||
}
|
||||
if session, err = chatter.Send(chatReq, chatOptions); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result := session.GetLastMessage().Content
|
||||
|
||||
if !currentFlags.Stream {
|
||||
// print the result if it was not streamed already
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// if the copy flag is set, copy the message to the clipboard
|
||||
if currentFlags.Copy {
|
||||
if err = CopyToClipboard(result); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// if the output flag is set, create an output file
|
||||
if currentFlags.Output != "" {
|
||||
if currentFlags.OutputSession {
|
||||
sessionAsString := session.String()
|
||||
err = CreateOutputFile(sessionAsString, currentFlags.Output)
|
||||
} else {
|
||||
err = CreateOutputFile(result, currentFlags.Output)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -4,18 +4,11 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/tools/youtube"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/core"
|
||||
"github.com/danielmiessler/fabric/internal/domain"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/ai"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
|
||||
restapi "github.com/danielmiessler/fabric/internal/server"
|
||||
"github.com/danielmiessler/fabric/internal/tools/converter"
|
||||
"github.com/danielmiessler/fabric/internal/tools/youtube"
|
||||
)
|
||||
|
||||
// Cli Controls the cli. It takes in the flags and runs the appropriate functions
|
||||
@@ -30,277 +23,67 @@ func Cli(version string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
var homedir string
|
||||
if homedir, err = os.UserHomeDir(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fabricDb := fsdb.NewDb(filepath.Join(homedir, ".config/fabric"))
|
||||
|
||||
if err = fabricDb.Configure(); err != nil {
|
||||
// Initialize database and registry
|
||||
var registry, err2 = initializeFabric()
|
||||
if err2 != nil {
|
||||
if !currentFlags.Setup {
|
||||
println(err.Error())
|
||||
fmt.Fprintln(os.Stderr, err2.Error())
|
||||
currentFlags.Setup = true
|
||||
}
|
||||
}
|
||||
|
||||
var registry *core.PluginRegistry
|
||||
if registry, err = core.NewPluginRegistry(fabricDb); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// if the setup flag is set, run the setup function
|
||||
if currentFlags.Setup {
|
||||
err = registry.Setup()
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.Serve {
|
||||
registry.ConfigureVendors()
|
||||
err = restapi.Serve(registry, currentFlags.ServeAddress, currentFlags.ServeAPIKey)
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.ServeOllama {
|
||||
registry.ConfigureVendors()
|
||||
err = restapi.ServeOllama(registry, currentFlags.ServeAddress, version)
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.UpdatePatterns {
|
||||
err = registry.PatternsLoader.PopulateDB()
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.ChangeDefaultModel {
|
||||
if err = registry.Defaults.Setup(); err != nil {
|
||||
return
|
||||
// Return early if registry is nil to prevent panics in subsequent handlers
|
||||
if registry == nil {
|
||||
return err2
|
||||
}
|
||||
err = registry.SaveEnvFile()
|
||||
}
|
||||
|
||||
// Handle setup and server commands
|
||||
var handled bool
|
||||
if handled, err = handleSetupAndServerCommands(currentFlags, registry, version); err != nil || handled {
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.LatestPatterns != "0" {
|
||||
var parsedToInt int
|
||||
if parsedToInt, err = strconv.Atoi(currentFlags.LatestPatterns); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = fabricDb.Patterns.PrintLatestPatterns(parsedToInt); err != nil {
|
||||
return
|
||||
}
|
||||
// Handle configuration commands
|
||||
if handled, err = handleConfigurationCommands(currentFlags, registry); err != nil || handled {
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.ListPatterns {
|
||||
err = fabricDb.Patterns.ListNames(currentFlags.ShellCompleteOutput)
|
||||
// Handle listing commands
|
||||
if handled, err = handleListingCommands(currentFlags, registry.Db, registry); err != nil || handled {
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.ListAllModels {
|
||||
var models *ai.VendorsModels
|
||||
if models, err = registry.VendorManager.GetModels(); err != nil {
|
||||
return
|
||||
}
|
||||
models.Print(currentFlags.ShellCompleteOutput)
|
||||
// Handle management commands
|
||||
if handled, err = handleManagementCommands(currentFlags, registry.Db); err != nil || handled {
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.ListAllContexts {
|
||||
err = fabricDb.Contexts.ListNames(currentFlags.ShellCompleteOutput)
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.ListAllSessions {
|
||||
err = fabricDb.Sessions.ListNames(currentFlags.ShellCompleteOutput)
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.WipeContext != "" {
|
||||
err = fabricDb.Contexts.Delete(currentFlags.WipeContext)
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.WipeSession != "" {
|
||||
err = fabricDb.Sessions.Delete(currentFlags.WipeSession)
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.PrintSession != "" {
|
||||
err = fabricDb.Sessions.PrintSession(currentFlags.PrintSession)
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.PrintContext != "" {
|
||||
err = fabricDb.Contexts.PrintContext(currentFlags.PrintContext)
|
||||
// Handle extension commands
|
||||
if handled, err = handleExtensionCommands(currentFlags, registry); err != nil || handled {
|
||||
return
|
||||
}
|
||||
|
||||
// Process HTML readability if needed
|
||||
if currentFlags.HtmlReadability {
|
||||
if msg, cleanErr := converter.HtmlReadability(currentFlags.Message); cleanErr != nil {
|
||||
fmt.Println("use original input, because can't apply html readability", err)
|
||||
fmt.Println("use original input, because can't apply html readability", cleanErr)
|
||||
} else {
|
||||
currentFlags.Message = msg
|
||||
}
|
||||
}
|
||||
|
||||
if currentFlags.ListExtensions {
|
||||
err = registry.TemplateExtensions.ListExtensions()
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.AddExtension != "" {
|
||||
err = registry.TemplateExtensions.RegisterExtension(currentFlags.AddExtension)
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.RemoveExtension != "" {
|
||||
err = registry.TemplateExtensions.RemoveExtension(currentFlags.RemoveExtension)
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.ListStrategies {
|
||||
err = registry.Strategies.ListStrategies(currentFlags.ShellCompleteOutput)
|
||||
return
|
||||
}
|
||||
|
||||
if currentFlags.ListVendors {
|
||||
err = registry.ListVendors(os.Stdout)
|
||||
return
|
||||
}
|
||||
|
||||
// if the interactive flag is set, run the interactive function
|
||||
// if currentFlags.Interactive {
|
||||
// interactive.Interactive()
|
||||
// }
|
||||
|
||||
// if none of the above currentFlags are set, run the initiate chat function
|
||||
|
||||
// Handle tool-based message processing
|
||||
var messageTools string
|
||||
|
||||
if currentFlags.YouTube != "" {
|
||||
if !registry.YouTube.IsConfigured() {
|
||||
err = fmt.Errorf("YouTube is not configured, please run the setup procedure")
|
||||
return
|
||||
}
|
||||
|
||||
var videoId string
|
||||
var playlistId string
|
||||
if videoId, playlistId, err = registry.YouTube.GetVideoOrPlaylistId(currentFlags.YouTube); err != nil {
|
||||
return
|
||||
} else if (videoId == "" || currentFlags.YouTubePlaylist) && playlistId != "" {
|
||||
if currentFlags.Output != "" {
|
||||
err = registry.YouTube.FetchAndSavePlaylist(playlistId, currentFlags.Output)
|
||||
} else {
|
||||
var videos []*youtube.VideoMeta
|
||||
if videos, err = registry.YouTube.FetchPlaylistVideos(playlistId); err != nil {
|
||||
err = fmt.Errorf("error fetching playlist videos: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, video := range videos {
|
||||
var message string
|
||||
if message, err = processYoutubeVideo(currentFlags, registry, video.Id); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !currentFlags.IsChatRequest() {
|
||||
if err = WriteOutput(message, fmt.Sprintf("%v.md", video.TitleNormalized)); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
messageTools = AppendMessage(messageTools, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if messageTools, err = processYoutubeVideo(currentFlags, registry, videoId); err != nil {
|
||||
return
|
||||
}
|
||||
if !currentFlags.IsChatRequest() {
|
||||
err = currentFlags.WriteOutput(messageTools)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (currentFlags.ScrapeURL != "" || currentFlags.ScrapeQuestion != "") && registry.Jina.IsConfigured() {
|
||||
// Check if the scrape_url flag is set and call ScrapeURL
|
||||
if currentFlags.ScrapeURL != "" {
|
||||
var website string
|
||||
if website, err = registry.Jina.ScrapeURL(currentFlags.ScrapeURL); err != nil {
|
||||
return
|
||||
}
|
||||
messageTools = AppendMessage(messageTools, website)
|
||||
}
|
||||
|
||||
// Check if the scrape_question flag is set and call ScrapeQuestion
|
||||
if currentFlags.ScrapeQuestion != "" {
|
||||
var website string
|
||||
if website, err = registry.Jina.ScrapeQuestion(currentFlags.ScrapeQuestion); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
messageTools = AppendMessage(messageTools, website)
|
||||
}
|
||||
|
||||
if !currentFlags.IsChatRequest() {
|
||||
err = currentFlags.WriteOutput(messageTools)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if messageTools != "" {
|
||||
currentFlags.AppendMessage(messageTools)
|
||||
}
|
||||
|
||||
var chatter *core.Chatter
|
||||
if chatter, err = registry.GetChatter(currentFlags.Model, currentFlags.ModelContextLength,
|
||||
currentFlags.Strategy, currentFlags.Stream, currentFlags.DryRun); err != nil {
|
||||
if messageTools, err = handleToolProcessing(currentFlags, registry); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var session *fsdb.Session
|
||||
var chatReq *domain.ChatRequest
|
||||
if chatReq, err = currentFlags.BuildChatRequest(strings.Join(os.Args[1:], " ")); err != nil {
|
||||
return
|
||||
// Return early for non-chat tool operations
|
||||
if messageTools != "" && !currentFlags.IsChatRequest() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if chatReq.Language == "" {
|
||||
chatReq.Language = registry.Language.DefaultLanguage.Value
|
||||
}
|
||||
var chatOptions *domain.ChatOptions
|
||||
if chatOptions, err = currentFlags.BuildChatOptions(); err != nil {
|
||||
return
|
||||
}
|
||||
if session, err = chatter.Send(chatReq, chatOptions); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
result := session.GetLastMessage().Content
|
||||
|
||||
if !currentFlags.Stream {
|
||||
// print the result if it was not streamed already
|
||||
fmt.Println(result)
|
||||
}
|
||||
|
||||
// if the copy flag is set, copy the message to the clipboard
|
||||
if currentFlags.Copy {
|
||||
if err = CopyToClipboard(result); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// if the output flag is set, create an output file
|
||||
if currentFlags.Output != "" {
|
||||
if currentFlags.OutputSession {
|
||||
sessionAsString := session.String()
|
||||
err = CreateOutputFile(sessionAsString, currentFlags.Output)
|
||||
} else {
|
||||
err = CreateOutputFile(result, currentFlags.Output)
|
||||
}
|
||||
}
|
||||
// Handle chat processing
|
||||
err = handleChatProcessing(currentFlags, registry, messageTools)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
28
internal/cli/configuration.go
Normal file
28
internal/cli/configuration.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/danielmiessler/fabric/internal/core"
|
||||
)
|
||||
|
||||
// handleConfigurationCommands handles configuration-related commands
|
||||
// Returns (handled, error) where handled indicates if a command was processed and should exit
|
||||
func handleConfigurationCommands(currentFlags *Flags, registry *core.PluginRegistry) (handled bool, err error) {
|
||||
if currentFlags.UpdatePatterns {
|
||||
if err = registry.PatternsLoader.PopulateDB(); err != nil {
|
||||
return true, err
|
||||
}
|
||||
// Save configuration in case any paths were migrated during pattern loading
|
||||
err = registry.SaveEnvFile()
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.ChangeDefaultModel {
|
||||
if err = registry.Defaults.Setup(); err != nil {
|
||||
return true, err
|
||||
}
|
||||
err = registry.SaveEnvFile()
|
||||
return true, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
26
internal/cli/extensions.go
Normal file
26
internal/cli/extensions.go
Normal file
@@ -0,0 +1,26 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/danielmiessler/fabric/internal/core"
|
||||
)
|
||||
|
||||
// handleExtensionCommands handles extension-related commands
|
||||
// Returns (handled, error) where handled indicates if a command was processed and should exit
|
||||
func handleExtensionCommands(currentFlags *Flags, registry *core.PluginRegistry) (handled bool, err error) {
|
||||
if currentFlags.ListExtensions {
|
||||
err = registry.TemplateExtensions.ListExtensions()
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.AddExtension != "" {
|
||||
err = registry.TemplateExtensions.RegisterExtension(currentFlags.AddExtension)
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.RemoveExtension != "" {
|
||||
err = registry.TemplateExtensions.RemoveExtension(currentFlags.RemoveExtension)
|
||||
return true, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
28
internal/cli/initialization.go
Normal file
28
internal/cli/initialization.go
Normal file
@@ -0,0 +1,28 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/core"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
|
||||
)
|
||||
|
||||
// initializeFabric initializes the fabric database and plugin registry
|
||||
func initializeFabric() (registry *core.PluginRegistry, err error) {
|
||||
var homedir string
|
||||
if homedir, err = os.UserHomeDir(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
fabricDb := fsdb.NewDb(filepath.Join(homedir, ".config/fabric"))
|
||||
if err = fabricDb.Configure(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if registry, err = core.NewPluginRegistry(fabricDb); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
62
internal/cli/listing.go
Normal file
62
internal/cli/listing.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/core"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/ai"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
|
||||
)
|
||||
|
||||
// handleListingCommands handles listing-related commands
|
||||
// Returns (handled, error) where handled indicates if a command was processed and should exit
|
||||
func handleListingCommands(currentFlags *Flags, fabricDb *fsdb.Db, registry *core.PluginRegistry) (handled bool, err error) {
|
||||
if currentFlags.LatestPatterns != "0" {
|
||||
var parsedToInt int
|
||||
if parsedToInt, err = strconv.Atoi(currentFlags.LatestPatterns); err != nil {
|
||||
return true, err
|
||||
}
|
||||
|
||||
if err = fabricDb.Patterns.PrintLatestPatterns(parsedToInt); err != nil {
|
||||
return true, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if currentFlags.ListPatterns {
|
||||
err = fabricDb.Patterns.ListNames(currentFlags.ShellCompleteOutput)
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.ListAllModels {
|
||||
var models *ai.VendorsModels
|
||||
if models, err = registry.VendorManager.GetModels(); err != nil {
|
||||
return true, err
|
||||
}
|
||||
models.Print(currentFlags.ShellCompleteOutput)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
if currentFlags.ListAllContexts {
|
||||
err = fabricDb.Contexts.ListNames(currentFlags.ShellCompleteOutput)
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.ListAllSessions {
|
||||
err = fabricDb.Sessions.ListNames(currentFlags.ShellCompleteOutput)
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.ListStrategies {
|
||||
err = registry.Strategies.ListStrategies(currentFlags.ShellCompleteOutput)
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.ListVendors {
|
||||
err = registry.ListVendors(os.Stdout)
|
||||
return true, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
31
internal/cli/management.go
Normal file
31
internal/cli/management.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
|
||||
)
|
||||
|
||||
// handleManagementCommands handles management-related commands (delete, print, etc.)
|
||||
// Returns (handled, error) where handled indicates if a command was processed and should exit
|
||||
func handleManagementCommands(currentFlags *Flags, fabricDb *fsdb.Db) (handled bool, err error) {
|
||||
if currentFlags.WipeContext != "" {
|
||||
err = fabricDb.Contexts.Delete(currentFlags.WipeContext)
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.WipeSession != "" {
|
||||
err = fabricDb.Sessions.Delete(currentFlags.WipeSession)
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.PrintSession != "" {
|
||||
err = fabricDb.Sessions.PrintSession(currentFlags.PrintSession)
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.PrintContext != "" {
|
||||
err = fabricDb.Contexts.PrintContext(currentFlags.PrintContext)
|
||||
return true, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
30
internal/cli/setup_server.go
Normal file
30
internal/cli/setup_server.go
Normal file
@@ -0,0 +1,30 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"github.com/danielmiessler/fabric/internal/core"
|
||||
restapi "github.com/danielmiessler/fabric/internal/server"
|
||||
)
|
||||
|
||||
// handleSetupAndServerCommands handles setup and server-related commands
|
||||
// Returns (handled, error) where handled indicates if a command was processed and should exit
|
||||
func handleSetupAndServerCommands(currentFlags *Flags, registry *core.PluginRegistry, version string) (handled bool, err error) {
|
||||
// if the setup flag is set, run the setup function
|
||||
if currentFlags.Setup {
|
||||
err = registry.Setup()
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.Serve {
|
||||
registry.ConfigureVendors()
|
||||
err = restapi.Serve(registry, currentFlags.ServeAddress, currentFlags.ServeAPIKey)
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.ServeOllama {
|
||||
registry.ConfigureVendors()
|
||||
err = restapi.ServeOllama(registry, currentFlags.ServeAddress, version)
|
||||
return true, err
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
90
internal/cli/tools.go
Normal file
90
internal/cli/tools.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/core"
|
||||
"github.com/danielmiessler/fabric/internal/tools/youtube"
|
||||
)
|
||||
|
||||
// handleToolProcessing handles YouTube and web scraping tool processing
|
||||
func handleToolProcessing(currentFlags *Flags, registry *core.PluginRegistry) (messageTools string, err error) {
|
||||
if currentFlags.YouTube != "" {
|
||||
if !registry.YouTube.IsConfigured() {
|
||||
err = fmt.Errorf("YouTube is not configured, please run the setup procedure")
|
||||
return
|
||||
}
|
||||
|
||||
var videoId string
|
||||
var playlistId string
|
||||
if videoId, playlistId, err = registry.YouTube.GetVideoOrPlaylistId(currentFlags.YouTube); err != nil {
|
||||
return
|
||||
} else if (videoId == "" || currentFlags.YouTubePlaylist) && playlistId != "" {
|
||||
if currentFlags.Output != "" {
|
||||
err = registry.YouTube.FetchAndSavePlaylist(playlistId, currentFlags.Output)
|
||||
} else {
|
||||
var videos []*youtube.VideoMeta
|
||||
if videos, err = registry.YouTube.FetchPlaylistVideos(playlistId); err != nil {
|
||||
err = fmt.Errorf("error fetching playlist videos: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
for _, video := range videos {
|
||||
var message string
|
||||
if message, err = processYoutubeVideo(currentFlags, registry, video.Id); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if !currentFlags.IsChatRequest() {
|
||||
if err = WriteOutput(message, fmt.Sprintf("%v.md", video.TitleNormalized)); err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
messageTools = AppendMessage(messageTools, message)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if messageTools, err = processYoutubeVideo(currentFlags, registry, videoId); err != nil {
|
||||
return
|
||||
}
|
||||
if !currentFlags.IsChatRequest() {
|
||||
err = currentFlags.WriteOutput(messageTools)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if currentFlags.ScrapeURL != "" || currentFlags.ScrapeQuestion != "" {
|
||||
if !registry.Jina.IsConfigured() {
|
||||
err = fmt.Errorf("scraping functionality is not configured. Please set up Jina to enable scraping")
|
||||
return
|
||||
}
|
||||
// Check if the scrape_url flag is set and call ScrapeURL
|
||||
if currentFlags.ScrapeURL != "" {
|
||||
var website string
|
||||
if website, err = registry.Jina.ScrapeURL(currentFlags.ScrapeURL); err != nil {
|
||||
return
|
||||
}
|
||||
messageTools = AppendMessage(messageTools, website)
|
||||
}
|
||||
|
||||
// Check if the scrape_question flag is set and call ScrapeQuestion
|
||||
if currentFlags.ScrapeQuestion != "" {
|
||||
var website string
|
||||
if website, err = registry.Jina.ScrapeQuestion(currentFlags.ScrapeQuestion); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
messageTools = AppendMessage(messageTools, website)
|
||||
}
|
||||
|
||||
if !currentFlags.IsChatRequest() {
|
||||
err = currentFlags.WriteOutput(messageTools)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@@ -55,7 +55,12 @@ type PatternsLoader struct {
|
||||
|
||||
func (o *PatternsLoader) configure() (err error) {
|
||||
o.pathPatternsPrefix = fmt.Sprintf("%v/", o.DefaultFolder.Value)
|
||||
o.tempPatternsFolder = filepath.Join(os.TempDir(), o.DefaultFolder.Value)
|
||||
// Use a consistent temp folder name regardless of the source path structure
|
||||
tempDir, err := os.MkdirTemp("", "fabric-patterns-")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create temporary patterns folder: %w", err)
|
||||
}
|
||||
o.tempPatternsFolder = tempDir
|
||||
|
||||
return
|
||||
}
|
||||
@@ -85,18 +90,38 @@ func (o *PatternsLoader) Setup() (err error) {
|
||||
func (o *PatternsLoader) PopulateDB() (err error) {
|
||||
fmt.Printf("Downloading patterns and Populating %s...\n", o.Patterns.Dir)
|
||||
fmt.Println()
|
||||
|
||||
originalPath := o.DefaultFolder.Value
|
||||
if err = o.gitCloneAndCopy(); err != nil {
|
||||
return
|
||||
return fmt.Errorf("failed to download patterns from git repository: %w", err)
|
||||
}
|
||||
|
||||
// If the path was migrated during gitCloneAndCopy, we need to save the updated configuration
|
||||
if o.DefaultFolder.Value != originalPath {
|
||||
fmt.Printf("💾 Saving updated configuration (path changed from '%s' to '%s')...\n", originalPath, o.DefaultFolder.Value)
|
||||
// The configuration will be saved by the calling code after this returns successfully
|
||||
}
|
||||
|
||||
if err = o.movePatterns(); err != nil {
|
||||
return
|
||||
return fmt.Errorf("failed to move patterns to config directory: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("✅ Successfully downloaded and installed patterns to %s\n", o.Patterns.Dir)
|
||||
return
|
||||
}
|
||||
|
||||
// PersistPatterns copies custom patterns to the updated patterns directory
|
||||
func (o *PatternsLoader) PersistPatterns() (err error) {
|
||||
// Check if patterns directory exists, if not, nothing to persist
|
||||
if _, err = os.Stat(o.Patterns.Dir); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// No existing patterns directory, nothing to persist
|
||||
return nil
|
||||
}
|
||||
// Return unexpected errors (e.g., permission issues)
|
||||
return fmt.Errorf("failed to access patterns directory '%s': %w", o.Patterns.Dir, err)
|
||||
}
|
||||
|
||||
var currentPatterns []os.DirEntry
|
||||
if currentPatterns, err = os.ReadDir(o.Patterns.Dir); err != nil {
|
||||
return
|
||||
@@ -108,15 +133,28 @@ func (o *PatternsLoader) PersistPatterns() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
for _, currentPattern := range currentPatterns {
|
||||
for _, newPattern := range newPatterns {
|
||||
if currentPattern.Name() == newPattern.Name() {
|
||||
break
|
||||
}
|
||||
err = copy.Copy(filepath.Join(o.Patterns.Dir, newPattern.Name()), filepath.Join(newPatternsFolder, newPattern.Name()))
|
||||
// Create a map of new patterns for faster lookup
|
||||
newPatternNames := make(map[string]bool)
|
||||
for _, newPattern := range newPatterns {
|
||||
if newPattern.IsDir() {
|
||||
newPatternNames[newPattern.Name()] = true
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
// Copy custom patterns that don't exist in the new download
|
||||
for _, currentPattern := range currentPatterns {
|
||||
if currentPattern.IsDir() && !newPatternNames[currentPattern.Name()] {
|
||||
// This is a custom pattern, preserve it
|
||||
src := filepath.Join(o.Patterns.Dir, currentPattern.Name())
|
||||
dst := filepath.Join(newPatternsFolder, currentPattern.Name())
|
||||
if copyErr := copy.Copy(src, dst); copyErr != nil {
|
||||
fmt.Printf("Warning: failed to preserve custom pattern '%s': %v\n", currentPattern.Name(), copyErr)
|
||||
} else {
|
||||
fmt.Printf("Preserved custom pattern: %s\n", currentPattern.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// movePatterns copies the new patterns into the config directory
|
||||
@@ -134,8 +172,29 @@ func (o *PatternsLoader) movePatterns() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Verify that patterns were actually copied before creating the loaded marker
|
||||
var entries []os.DirEntry
|
||||
if entries, err = os.ReadDir(o.Patterns.Dir); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Count actual pattern directories (exclude the loaded file itself)
|
||||
patternCount := 0
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
patternCount++
|
||||
}
|
||||
}
|
||||
|
||||
if patternCount == 0 {
|
||||
err = fmt.Errorf("no patterns were successfully copied to %s", o.Patterns.Dir)
|
||||
return
|
||||
}
|
||||
|
||||
//create an empty file to indicate that the patterns have been updated if not exists
|
||||
_, _ = os.Create(o.loadedFilePath)
|
||||
if _, err = os.Create(o.loadedFilePath); err != nil {
|
||||
return fmt.Errorf("failed to create loaded marker file '%s': %w", o.loadedFilePath, err)
|
||||
}
|
||||
|
||||
err = os.RemoveAll(patternsDir)
|
||||
return
|
||||
@@ -147,15 +206,98 @@ func (o *PatternsLoader) gitCloneAndCopy() (err error) {
|
||||
return fmt.Errorf("failed to create temp directory: %w", err)
|
||||
}
|
||||
|
||||
// Use the helper to fetch files
|
||||
fmt.Printf("Cloning repository %s (path: %s)...\n", o.DefaultGitRepoUrl.Value, o.DefaultFolder.Value)
|
||||
|
||||
// Try to fetch files with the current path
|
||||
err = githelper.FetchFilesFromRepo(githelper.FetchOptions{
|
||||
RepoURL: o.DefaultGitRepoUrl.Value,
|
||||
PathPrefix: o.DefaultFolder.Value,
|
||||
DestDir: o.tempPatternsFolder,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to download patterns: %w", err)
|
||||
return fmt.Errorf("failed to download patterns from %s: %w", o.DefaultGitRepoUrl.Value, err)
|
||||
}
|
||||
|
||||
// Check if patterns were downloaded
|
||||
if patternCount, checkErr := o.countPatternsInDirectory(o.tempPatternsFolder); checkErr != nil {
|
||||
return fmt.Errorf("failed to read temp patterns directory: %w", checkErr)
|
||||
} else if patternCount == 0 {
|
||||
// No patterns found with current path, try automatic migration
|
||||
if migrationErr := o.tryPathMigration(); migrationErr != nil {
|
||||
return fmt.Errorf("no patterns found in repository at path %s and migration failed: %w", o.DefaultFolder.Value, migrationErr)
|
||||
}
|
||||
// Migration successful, try downloading again
|
||||
return o.gitCloneAndCopy()
|
||||
} else {
|
||||
fmt.Printf("Downloaded %d patterns to temporary directory\n", patternCount)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// tryPathMigration attempts to migrate from old pattern paths to new restructured paths
|
||||
func (o *PatternsLoader) tryPathMigration() (err error) {
|
||||
// Check if current path is the old "patterns" path
|
||||
if o.DefaultFolder.Value == "patterns" {
|
||||
fmt.Println("🔄 Detected old pattern path 'patterns', trying migration to 'data/patterns'...")
|
||||
|
||||
// Try the new restructured path
|
||||
newPath := "data/patterns"
|
||||
testTempFolder := filepath.Join(os.TempDir(), "fabric-patterns-test")
|
||||
|
||||
// Clean up any existing test temp folder
|
||||
if err := os.RemoveAll(testTempFolder); err != nil {
|
||||
fmt.Printf("Warning: failed to remove test temporary folder '%s': %v\n", testTempFolder, err)
|
||||
}
|
||||
|
||||
// Test if the new path works
|
||||
testErr := githelper.FetchFilesFromRepo(githelper.FetchOptions{
|
||||
RepoURL: o.DefaultGitRepoUrl.Value,
|
||||
PathPrefix: newPath,
|
||||
DestDir: testTempFolder,
|
||||
})
|
||||
|
||||
if testErr == nil {
|
||||
// Check if patterns exist in the new path
|
||||
if patternCount, countErr := o.countPatternsInDirectory(testTempFolder); countErr == nil && patternCount > 0 {
|
||||
fmt.Printf("✅ Found %d patterns at new path '%s', updating configuration...\n", patternCount, newPath)
|
||||
|
||||
// Update the configuration
|
||||
o.DefaultFolder.Value = newPath
|
||||
// Clean up the main temp folder and replace it with the test one
|
||||
os.RemoveAll(o.tempPatternsFolder)
|
||||
if renameErr := os.Rename(testTempFolder, o.tempPatternsFolder); renameErr != nil {
|
||||
// If rename fails, try copy
|
||||
if copyErr := copy.Copy(testTempFolder, o.tempPatternsFolder); copyErr != nil {
|
||||
return fmt.Errorf("failed to move test patterns to temp folder: %w", copyErr)
|
||||
}
|
||||
os.RemoveAll(testTempFolder)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up test folder
|
||||
os.RemoveAll(testTempFolder)
|
||||
}
|
||||
|
||||
return fmt.Errorf("unable to find patterns at current path '%s' or migrate to new structure", o.DefaultFolder.Value)
|
||||
}
|
||||
|
||||
// countPatternsInDirectory counts the number of pattern directories in a given directory
|
||||
func (o *PatternsLoader) countPatternsInDirectory(dir string) (int, error) {
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
patternCount := 0
|
||||
for _, entry := range entries {
|
||||
if entry.IsDir() {
|
||||
patternCount++
|
||||
}
|
||||
}
|
||||
|
||||
return patternCount, nil
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
self,
|
||||
lib,
|
||||
buildGoApplication,
|
||||
go,
|
||||
@@ -8,8 +9,8 @@
|
||||
buildGoApplication {
|
||||
pname = "fabric-ai";
|
||||
version = import ./version.nix;
|
||||
src = ../../../.;
|
||||
pwd = ../../../.;
|
||||
src = self;
|
||||
pwd = self;
|
||||
modules = ./gomod2nix.toml;
|
||||
|
||||
doCheck = false;
|
||||
|
||||
@@ -1 +1 @@
|
||||
"1.4.242"
|
||||
"1.4.244"
|
||||
|
||||
@@ -16,16 +16,16 @@
|
||||
gomod2nix
|
||||
goEnv
|
||||
|
||||
(pkgs.writeShellScriptBin "update" ''
|
||||
(pkgs.writeShellScriptBin "update-mod" ''
|
||||
go get -u
|
||||
go mod tidy
|
||||
gomod2nix generate
|
||||
gomod2nix generate --outdir nix/pkgs/fabric
|
||||
'')
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
echo -e "\033[0;32;4mHeper commands:\033[0m"
|
||||
echo "'update' instead of 'go get -u && go mod tidy'"
|
||||
echo -e "\033[0;32;4mHelper commands:\033[0m"
|
||||
echo "'update-mod' instead of 'go get -u && go mod tidy && gomod2nix generate --outdir nix/pkgs/fabric'"
|
||||
'';
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user