Skip to content

Commit

Permalink
feat: plugins arch., new setup procedure
Browse files Browse the repository at this point in the history
  • Loading branch information
eugeis committed Oct 19, 2024
1 parent 61f66f8 commit 609df94
Show file tree
Hide file tree
Showing 22 changed files with 1,995 additions and 0 deletions.
27 changes: 27 additions & 0 deletions cli/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package cli

import (
"fmt"
"github.com/atotto/clipboard"
"os"
)

func CopyToClipboard(message string) (err error) {
if err = clipboard.WriteAll(message); err != nil {
err = fmt.Errorf("could not copy to clipboard: %v", err)
}
return
}

func CreateOutputFile(message string, fileName string) (err error) {
var file *os.File
if file, err = os.Create(fileName); err != nil {
err = fmt.Errorf("error creating file: %v", err)
return
}
defer file.Close()
if _, err = file.WriteString(message); err != nil {
err = fmt.Errorf("error writing to file: %v", err)
}
return
}
28 changes: 28 additions & 0 deletions cli/output_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package cli

import (
"os"
"testing"
)

func TestCopyToClipboard(t *testing.T) {
t.Skip("skipping test, because of docker env. in ci.")

message := "test message"
err := CopyToClipboard(message)
if err != nil {
t.Fatalf("CopyToClipboard() error = %v", err)
}
}

func TestCreateOutputFile(t *testing.T) {

fileName := "test_output.txt"
message := "test message"
err := CreateOutputFile(message, fileName)
if err != nil {
t.Fatalf("CreateOutputFile() error = %v", err)
}

defer os.Remove(fileName)
}
134 changes: 134 additions & 0 deletions common/groups_items.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package common

import (
"fmt"
"github.com/samber/lo"
)

func NewGroupsItemsSelector[I any](selectionLabel string,
getItemLabel func(I) string) *GroupsItemsSelector[I] {

return &GroupsItemsSelector[I]{SelectionLabel: selectionLabel,
GetItemKey: getItemLabel,
GroupsItems: make([]*GroupItems[I], 0),
}
}

type GroupItems[I any] struct {
Group string
Items []I
}

func (o *GroupItems[I]) Count() int {
return len(o.Items)
}

func (o *GroupItems[I]) ContainsItemBy(predicate func(item I) bool) (ret bool) {
ret = lo.ContainsBy(o.Items, predicate)
return
}

type GroupsItemsSelector[I any] struct {
SelectionLabel string
GetItemKey func(I) string

GroupsItems []*GroupItems[I]
}

func (o *GroupsItemsSelector[I]) AddGroupItems(group string, items ...I) {
o.GroupsItems = append(o.GroupsItems, &GroupItems[I]{group, items})
}

func (o *GroupsItemsSelector[I]) GetGroupAndItemByItemNumber(number int) (group string, item I, err error) {
var currentItemNumber int
found := false

for _, groupItems := range o.GroupsItems {
if currentItemNumber+groupItems.Count() < number {
currentItemNumber += groupItems.Count()
continue
}

for _, groupItem := range groupItems.Items {
currentItemNumber++
if currentItemNumber == number {
group = groupItems.Group
item = groupItem
found = true
break
}
}
}

if !found {
err = fmt.Errorf("number %d is out of range", number)
}
return
}

func (o *GroupsItemsSelector[I]) Print() {
fmt.Printf("\n%v:\n", o.SelectionLabel)

var currentItemIndex int
for _, groupItems := range o.GroupsItems {
fmt.Println()
fmt.Printf("%s\n", groupItems.Group)
fmt.Println()

for _, item := range groupItems.Items {
currentItemIndex++
fmt.Printf("\t[%d]\t%s\n", currentItemIndex, o.GetItemKey(item))

}
}
}

func (o *GroupsItemsSelector[I]) HasGroup(group string) (ret bool) {
for _, groupItems := range o.GroupsItems {
if ret = groupItems.Group == group; ret {
break
}
}
return
}

func (o *GroupsItemsSelector[I]) FindGroupsByItemFirst(item I) (ret string) {
itemKey := o.GetItemKey(item)

for _, groupItems := range o.GroupsItems {
if groupItems.ContainsItemBy(func(groupItem I) bool {
groupItemKey := o.GetItemKey(groupItem)
return groupItemKey == itemKey
}) {
ret = groupItems.Group
break
}
}
return
}

func (o *GroupsItemsSelector[I]) FindGroupsByItem(item I) (groups []string) {
itemKey := o.GetItemKey(item)

for _, groupItems := range o.GroupsItems {
if groupItems.ContainsItemBy(func(groupItem I) bool {
groupItemKey := o.GetItemKey(groupItem)
return groupItemKey == itemKey
}) {
groups = append(groups, groupItems.Group)
}
}
return
}

func ReturnItem(item string) string {
return item
}

func NewGroupsItemsSelectorString(selectionLabel string) *GroupsItemsSelectorString {
return &GroupsItemsSelectorString{GroupsItemsSelector: NewGroupsItemsSelector(selectionLabel, ReturnItem)}
}

type GroupsItemsSelectorString struct {
*GroupsItemsSelector[string]
}
203 changes: 203 additions & 0 deletions core/plugin_registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
package core

import (
"bytes"
"fmt"
"github.com/danielmiessler/fabric/common"
"github.com/danielmiessler/fabric/plugins/ai/azure"
"github.com/danielmiessler/fabric/plugins/tools"
"github.com/samber/lo"
"strconv"

"github.com/danielmiessler/fabric/plugins"
"github.com/danielmiessler/fabric/plugins/ai"
"github.com/danielmiessler/fabric/plugins/ai/anthropic"
"github.com/danielmiessler/fabric/plugins/ai/dryrun"
"github.com/danielmiessler/fabric/plugins/ai/gemini"
"github.com/danielmiessler/fabric/plugins/ai/groq"
"github.com/danielmiessler/fabric/plugins/ai/mistral"
"github.com/danielmiessler/fabric/plugins/ai/ollama"
"github.com/danielmiessler/fabric/plugins/ai/openai"
"github.com/danielmiessler/fabric/plugins/ai/openrouter"
"github.com/danielmiessler/fabric/plugins/ai/siliconcloud"
"github.com/danielmiessler/fabric/plugins/db/fsdb"
"github.com/danielmiessler/fabric/plugins/tools/jina"
"github.com/danielmiessler/fabric/plugins/tools/lang"
"github.com/danielmiessler/fabric/plugins/tools/youtube"
)

func NewPluginRegistry(db *fsdb.Db) (ret *PluginRegistry) {
ret = &PluginRegistry{
Db: db,
VendorManager: ai.NewVendorsManager(),
VendorsAll: ai.NewVendorsManager(),
PatternsLoader: tools.NewPatternsLoader(db.Patterns),
YouTube: youtube.NewYouTube(),
Language: lang.NewLanguage(),
Jina: jina.NewClient(),
}

ret.Defaults = tools.NeeDefaults(ret.VendorManager.GetModels)

ret.VendorsAll.AddVendors(openai.NewClient(), ollama.NewClient(), azure.NewClient(), groq.NewClient(),
gemini.NewClient(), anthropic.NewClient(), siliconcloud.NewClient(), openrouter.NewClient(), mistral.NewClient())
_ = ret.Configure()

return
}

type PluginRegistry struct {
Db *fsdb.Db

VendorManager *ai.VendorsManager
VendorsAll *ai.VendorsManager
Defaults *tools.Defaults
PatternsLoader *tools.PatternsLoader
YouTube *youtube.YouTube
Language *lang.Language
Jina *jina.Client
}

func (o *PluginRegistry) SaveEnvFile() (err error) {
// Now create the .env with all configured VendorsController info
var envFileContent bytes.Buffer

o.Defaults.Settings.FillEnvFileContent(&envFileContent)
o.PatternsLoader.SetupFillEnvFileContent(&envFileContent)

for _, vendor := range o.VendorManager.Vendors {
vendor.SetupFillEnvFileContent(&envFileContent)
}

o.YouTube.SetupFillEnvFileContent(&envFileContent)
o.Jina.SetupFillEnvFileContent(&envFileContent)
o.Language.SetupFillEnvFileContent(&envFileContent)

err = o.Db.SaveEnv(envFileContent.String())
return
}

func (o *PluginRegistry) Setup() (err error) {
setupQuestion := plugins.NewSetupQuestion("Enter the number of the plugin to setup")
groupsPlugins := common.NewGroupsItemsSelector[plugins.Plugin]("Available plugins",
func(plugin plugins.Plugin) string {
var configuredLabel string
if plugin.IsConfigured() {
configuredLabel = " (configured)"
} else {
configuredLabel = ""
}
return fmt.Sprintf("%v%v", plugin.GetSetupDescription(), configuredLabel)
})

groupsPlugins.AddGroupItems("AI Vendors [at least one, required]", lo.Map(o.VendorsAll.Vendors,
func(vendor ai.Vendor, _ int) plugins.Plugin {
return vendor
})...)

groupsPlugins.AddGroupItems("Tools", o.Defaults, o.PatternsLoader, o.YouTube, o.Language, o.Jina)

for {
groupsPlugins.Print()

if answerErr := setupQuestion.Ask("Plugin Number"); answerErr != nil {
break
}

if setupQuestion.Value == "" {
break
}
number, parseErr := strconv.Atoi(setupQuestion.Value)
setupQuestion.Value = ""

if parseErr == nil {
var plugin plugins.Plugin
if _, plugin, err = groupsPlugins.GetGroupAndItemByItemNumber(number); err != nil {
return
}

if pluginSetupErr := plugin.Setup(); pluginSetupErr != nil {
println(pluginSetupErr.Error())
} else {
if err = o.SaveEnvFile(); err != nil {
break
}
}

if _, ok := o.VendorManager.VendorsByName[plugin.GetName()]; !ok {
if vendor, ok := plugin.(ai.Vendor); ok {
o.VendorManager.AddVendors(vendor)
}
}
} else {
break
}
}

err = o.SaveEnvFile()

return
}

func (o *PluginRegistry) SetupVendor(vendorName string) (err error) {
if err = o.VendorsAll.SetupVendor(vendorName, o.VendorManager.VendorsByName); err != nil {
return
}
err = o.SaveEnvFile()
return
}

// Configure buildClient VendorsController based on the environment variables
func (o *PluginRegistry) Configure() (err error) {
for _, vendor := range o.VendorsAll.Vendors {
if vendorErr := vendor.Configure(); vendorErr == nil {
o.VendorManager.AddVendors(vendor)
}
}
_ = o.Defaults.Configure()
_ = o.PatternsLoader.Configure()

//YouTube and Jina are not mandatory, so ignore not configured error
_ = o.YouTube.Configure()
_ = o.Jina.Configure()
_ = o.Language.Configure()
return
}

func (o *PluginRegistry) GetChatter(model string, stream bool, dryRun bool) (ret *Chatter, err error) {
ret = &Chatter{
db: o.Db,
Stream: stream,
DryRun: dryRun,
}

defaultModel := o.Defaults.Model.Value
defaultVendor := o.Defaults.Vendor.Value
vendorManager := o.VendorManager

if dryRun {
ret.vendor = dryrun.NewClient()
ret.model = model
if ret.model == "" {
ret.model = defaultModel
}
} else if model == "" {
ret.vendor = vendorManager.FindByName(defaultVendor)
ret.model = defaultModel
} else {
var models *ai.VendorsModels
if models, err = vendorManager.GetModels(); err != nil {
return
}
ret.vendor = vendorManager.FindByName(models.FindGroupsByItemFirst(model))
ret.model = model
}

if ret.vendor == nil {
err = fmt.Errorf(
"could not find vendor.\n Model = %s\n Model = %s\n Vendor = %s",
model, defaultModel, defaultVendor)
return
}
return
}
Loading

0 comments on commit 609df94

Please sign in to comment.