Compare commits

...

32 Commits

Author SHA1 Message Date
github-actions[bot]
a97302d791 Update version to v1.4.68 and commit 2024-10-21 14:22:48 +00:00
Eugen Eisler
aaddc95ec0 fix: setup does not overwrites old values 2024-10-21 16:22:08 +02:00
github-actions[bot]
1b7a1fa652 Update version to v1.4.67 and commit 2024-10-19 11:10:23 +00:00
Eugen Eisler
8a3d63ef48 Merge remote-tracking branch 'origin/main' 2024-10-19 13:10:09 +02:00
Eugen Eisler
609df943dd feat: plugins arch., new setup procedure 2024-10-19 13:09:58 +02:00
github-actions[bot]
8941551f5a Update version to v1.4.66 and commit 2024-10-19 11:09:55 +00:00
Eugen Eisler
61f66f88e3 feat: plugins arch., new setup procedure 2024-10-19 13:09:37 +02:00
github-actions[bot]
15de33107b Update version to v1.4.65 and commit 2024-10-16 13:27:28 +00:00
Eugen Eisler
81f9b1dabb Merge pull request #1045 from Fenicio/patch-1
Update patterns/analyze_answers/system.md - Fixed a bunch of typos
2024-10-16 16:27:14 +03:00
Guillermo G C
888342c119 Update patterns/analyze_answers/system.md - Fixed a bunch of typos 2024-10-15 08:53:26 +02:00
github-actions[bot]
12d83dad6d Update version to v1.4.64 and commit 2024-10-14 18:17:37 +00:00
Jonathan Dunn
14ef9fd41c updated readme 2024-10-14 14:17:19 -04:00
github-actions[bot]
584e0c8547 Update version to v1.4.63 and commit 2024-10-13 14:42:39 +00:00
Daniel Miessler
914b312c2e Merge pull request #862 from Thepathakarpit/patch-1
Create setup_fabric.bat, a batch script to automate setup and running…
2024-10-13 07:42:27 -07:00
github-actions[bot]
8153d690cc Update version to v1.4.62 and commit 2024-10-13 14:40:48 +00:00
Daniel Miessler
d03bdbeb9b Merge pull request #1044 from danielmiessler/feat/rest-api
Feat/rest api
2024-10-13 07:40:32 -07:00
github-actions[bot]
87730043b5 Update version to v1.4.61 and commit 2024-10-13 02:50:02 +00:00
Daniel Miessler
3285b8e330 Updated extract sponsors. 2024-10-12 19:49:44 -07:00
Eugen Eisler
686d039392 Merge branch 'main' into feat/rest-api 2024-10-12 23:06:29 +03:00
Eugen Eisler
d7683e4c39 feat: restructure for better reuse 2024-10-12 22:49:26 +03:00
Eugen Eisler
56f995afb4 feat: restructure for better reuse 2024-10-12 22:37:35 +03:00
Eugen Eisler
17bde814cc feat: restructure for better reuse 2024-10-12 22:25:17 +03:00
github-actions[bot]
525f972d22 Update version to v1.4.60 and commit 2024-10-12 15:27:28 +00:00
Eugen Eisler
161ce65ae6 fix: IsChatRequest rule; Close #1042 is 2024-10-12 18:27:04 +03:00
Eugen Eisler
4104317b34 feat: work on Rest API 2024-10-06 16:07:32 +02:00
Eugen Eisler
401f0da689 feat: work on Rest API 2024-10-06 16:06:05 +02:00
Eugen Eisler
6efe7960cd feat: work on Rest API 2024-10-06 15:43:18 +02:00
Eugen Eisler
a6d63f4d0e feat: work on Rest API 2024-10-06 15:40:29 +02:00
Eugen Eisler
8f3928f4b2 feat: work on Rest API 2024-10-06 15:31:06 +02:00
Eugen Eisler
3380972df1 feat: work on Rest API 2024-10-06 15:29:01 +02:00
Arpit Pathak
5ad9943462 Merge branch 'main' into patch-1 2024-09-13 23:19:19 +05:30
Arpit Pathak
cf0b9d2c3d Create setup_fabric.bat, a batch script to automate setup and running fabric on windows. 2024-08-22 09:02:30 +05:30
63 changed files with 1381 additions and 1011 deletions

View File

@@ -217,8 +217,6 @@ Application Options:
-C, --context= Choose a context from the available contexts
--session= Choose a session from the available sessions
-S, --setup Run setup for all reconfigurable parts of fabric
--setup-skip-patterns Run Setup for all reconfigurable parts of fabric except patterns update
--setup-vendor= Run Setup for specific vendor, one of Ollama, OpenAI, Anthropic, Azure, Gemini, Groq, Mistral, OpenRouter, SiliconCloud. E.g. fabric --setup-vendor=OpenAI
-t, --temperature= Set temperature (default: 0.7)
-T, --topp= Set top P (default: 0.9)
-s, --stream Stream

View File

@@ -2,14 +2,15 @@ package cli
import (
"fmt"
"github.com/danielmiessler/fabric/converter"
"github.com/danielmiessler/fabric/core"
"github.com/danielmiessler/fabric/plugins/ai"
"github.com/danielmiessler/fabric/plugins/db/fsdb"
"github.com/danielmiessler/fabric/plugins/tools/converter"
"github.com/danielmiessler/fabric/restapi"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/danielmiessler/fabric/core"
"github.com/danielmiessler/fabric/db"
)
// Cli Controls the cli. It takes in the flags and runs the appropriate functions
@@ -29,39 +30,35 @@ func Cli(version string) (err error) {
return
}
fabricDb := db.NewDb(filepath.Join(homedir, ".config/fabric"))
fabricDb := fsdb.NewDb(filepath.Join(homedir, ".config/fabric"))
if err = fabricDb.Configure(); err != nil {
if !currentFlags.Setup {
println(err.Error())
currentFlags.Setup = true
}
}
registry := core.NewPluginRegistry(fabricDb)
// if the setup flag is set, run the setup function
if currentFlags.Setup || currentFlags.SetupSkipPatterns || currentFlags.SetupVendor != "" {
_ = fabricDb.Configure()
if currentFlags.SetupVendor != "" {
_, err = SetupVendor(fabricDb, currentFlags.SetupVendor)
} else {
_, err = Setup(fabricDb, currentFlags.SetupSkipPatterns)
}
if currentFlags.Setup {
err = registry.Setup()
return
}
var fabric *core.Fabric
if err = fabricDb.Configure(); err != nil {
fmt.Println("init is failed, run start the setup procedure", err)
if fabric, err = Setup(fabricDb, currentFlags.SetupSkipPatterns); err != nil {
return
}
} else {
if fabric, err = core.NewFabric(fabricDb); err != nil {
fmt.Println("fabric can't initialize, please run the --setup procedure", err)
return
}
if currentFlags.Serve {
err = restapi.Serve(registry, currentFlags.ServeAddress)
return
}
if currentFlags.UpdatePatterns {
err = fabric.PopulateDB()
err = registry.PatternsLoader.PopulateDB()
return
}
if currentFlags.ChangeDefaultModel {
err = fabric.SetupDefaultModel()
err = registry.Defaults.Setup()
return
}
@@ -83,7 +80,11 @@ func Cli(version string) (err error) {
}
if currentFlags.ListAllModels {
fabric.GetModels().Print()
var models *ai.VendorsModels
if models, err = registry.VendorManager.GetModels(); err != nil {
return
}
models.Print()
return
}
@@ -133,27 +134,27 @@ func Cli(version string) (err error) {
// if none of the above currentFlags are set, run the initiate chat function
if currentFlags.YouTube != "" {
if fabric.YouTube.IsConfigured() == false {
if registry.YouTube.IsConfigured() == false {
err = fmt.Errorf("YouTube is not configured, please run the setup procedure")
return
}
var videoId string
if videoId, err = fabric.YouTube.GetVideoId(currentFlags.YouTube); err != nil {
if videoId, err = registry.YouTube.GetVideoId(currentFlags.YouTube); err != nil {
return
}
if !currentFlags.YouTubeComments || currentFlags.YouTubeTranscript {
var transcript string
var language = "en"
if currentFlags.Language != "" || fabric.DefaultLanguage.Value != "" {
if currentFlags.Language != "" || registry.Language.DefaultLanguage.Value != "" {
if currentFlags.Language != "" {
language = currentFlags.Language
} else {
language = fabric.DefaultLanguage.Value
language = registry.Language.DefaultLanguage.Value
}
}
if transcript, err = fabric.YouTube.GrabTranscript(videoId, language); err != nil {
if transcript, err = registry.YouTube.GrabTranscript(videoId, language); err != nil {
return
}
@@ -162,7 +163,7 @@ func Cli(version string) (err error) {
if currentFlags.YouTubeComments {
var comments []string
if comments, err = fabric.YouTube.GrabComments(videoId); err != nil {
if comments, err = registry.YouTube.GrabComments(videoId); err != nil {
return
}
@@ -178,11 +179,11 @@ func Cli(version string) (err error) {
}
}
if (currentFlags.ScrapeURL != "" || currentFlags.ScrapeQuestion != "") && fabric.Jina.IsConfigured() {
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 = fabric.Jina.ScrapeURL(currentFlags.ScrapeURL); err != nil {
if website, err = registry.Jina.ScrapeURL(currentFlags.ScrapeURL); err != nil {
return
}
@@ -192,7 +193,7 @@ func Cli(version string) (err error) {
// Check if the scrape_question flag is set and call ScrapeQuestion
if currentFlags.ScrapeQuestion != "" {
var website string
if website, err = fabric.Jina.ScrapeQuestion(currentFlags.ScrapeQuestion); err != nil {
if website, err = registry.Jina.ScrapeQuestion(currentFlags.ScrapeQuestion); err != nil {
return
}
@@ -207,14 +208,14 @@ func Cli(version string) (err error) {
}
var chatter *core.Chatter
if chatter, err = fabric.GetChatter(currentFlags.Model, currentFlags.Stream, currentFlags.DryRun); err != nil {
if chatter, err = registry.GetChatter(currentFlags.Model, currentFlags.Stream, currentFlags.DryRun); err != nil {
return
}
var session *db.Session
var session *fsdb.Session
chatReq := currentFlags.BuildChatRequest(strings.Join(os.Args[1:], " "))
if chatReq.Language == "" {
chatReq.Language = fabric.DefaultLanguage.Value
chatReq.Language = registry.Language.DefaultLanguage.Value
}
if session, err = chatter.Send(chatReq, currentFlags.BuildChatOptions()); err != nil {
return
@@ -229,7 +230,7 @@ func Cli(version string) (err error) {
// if the copy flag is set, copy the message to the clipboard
if currentFlags.Copy {
if err = fabric.CopyToClipboard(result); err != nil {
if err = CopyToClipboard(result); err != nil {
return
}
}
@@ -238,32 +239,10 @@ func Cli(version string) (err error) {
if currentFlags.Output != "" {
if currentFlags.OutputSession {
sessionAsString := session.String()
err = fabric.CreateOutputFile(sessionAsString, currentFlags.Output)
err = CreateOutputFile(sessionAsString, currentFlags.Output)
} else {
err = fabric.CreateOutputFile(result, currentFlags.Output)
err = CreateOutputFile(result, currentFlags.Output)
}
}
return
}
func Setup(db *db.Db, skipUpdatePatterns bool) (ret *core.Fabric, err error) {
instance := core.NewFabricForSetup(db)
if err = instance.Setup(); err != nil {
return
}
if !skipUpdatePatterns {
if err = instance.PopulateDB(); err != nil {
return
}
}
ret = instance
return
}
func SetupVendor(db *db.Db, vendorName string) (ret *core.Fabric, err error) {
ret = core.NewFabricForSetup(db)
err = ret.SetupVendor(vendorName)
return
}

View File

@@ -5,7 +5,6 @@ import (
"os"
"testing"
"github.com/danielmiessler/fabric/db"
"github.com/stretchr/testify/assert"
)
@@ -19,11 +18,3 @@ func TestCli(t *testing.T) {
assert.Error(t, err)
assert.Equal(t, core.NoSessionPatternUserMessages, err.Error())
}
func TestSetup(t *testing.T) {
mockDB := db.NewDb(os.TempDir())
fabric, err := Setup(mockDB, false)
assert.Error(t, err)
assert.Nil(t, fabric)
}

View File

@@ -19,8 +19,6 @@ type Flags struct {
Context string `short:"C" long:"context" description:"Choose a context from the available contexts" default:""`
Session string `long:"session" description:"Choose a session from the available sessions"`
Setup bool `short:"S" long:"setup" description:"Run setup for all reconfigurable parts of fabric"`
SetupSkipPatterns bool `long:"setup-skip-patterns" description:"Run Setup for all reconfigurable parts of fabric except patterns update"`
SetupVendor string `long:"setup-vendor" description:"Run Setup for specific vendor, one of Ollama, OpenAI, Anthropic, Azure, Gemini, Groq, Mistral, OpenRouter, SiliconCloud. E.g. fabric --setup-vendor=OpenAI"`
Temperature float64 `short:"t" long:"temperature" description:"Set temperature" default:"0.7"`
TopP float64 `short:"T" long:"topp" description:"Set top P" default:"0.9"`
Stream bool `short:"s" long:"stream" description:"Stream"`
@@ -52,6 +50,8 @@ type Flags struct {
PrintSession string `long:"printsession" description:"Print session"`
HtmlReadability bool `long:"readability" description:"Convert HTML input into a clean, readable view"`
DryRun bool `long:"dry-run" description:"Show what would be sent to the model without actually sending it"`
Serve bool `long:"serve" description:"Serve the Fabric Rest API"`
ServeAddress string `long:"address" description:"The address to bind the REST API" default:":8080"`
Version bool `long:"version" description:"Print current version"`
}
@@ -141,6 +141,6 @@ func (o *Flags) AppendMessage(message string) {
}
func (o *Flags) IsChatRequest() (ret bool) {
ret = o.Message != "" || o.Session != ""
ret = (o.Message != "" || o.Context != "") && (o.Session != "" || o.Pattern != "")
return
}

27
cli/output.go Normal file
View File

@@ -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
cli/output_test.go Normal file
View File

@@ -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
common/groups_items.go Normal file
View File

@@ -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]
}

View File

@@ -4,23 +4,25 @@ import (
"context"
"fmt"
"github.com/danielmiessler/fabric/common"
"github.com/danielmiessler/fabric/db"
"github.com/danielmiessler/fabric/vendors"
"github.com/danielmiessler/fabric/plugins/ai"
"github.com/danielmiessler/fabric/plugins/db/fsdb"
goopenai "github.com/sashabaranov/go-openai"
"strings"
)
const NoSessionPatternUserMessages = "no session, pattern or user messages provided"
type Chatter struct {
db *db.Db
db *fsdb.Db
Stream bool
DryRun bool
model string
vendor vendors.Vendor
vendor ai.Vendor
}
func (o *Chatter) Send(request *common.ChatRequest, opts *common.ChatOptions) (session *db.Session, err error) {
func (o *Chatter) Send(request *common.ChatRequest, opts *common.ChatOptions) (session *fsdb.Session, err error) {
if session, err = o.BuildSession(request, opts.Raw); err != nil {
return
}
@@ -63,16 +65,16 @@ func (o *Chatter) Send(request *common.ChatRequest, opts *common.ChatOptions) (s
return
}
func (o *Chatter) BuildSession(request *common.ChatRequest, raw bool) (session *db.Session, err error) {
func (o *Chatter) BuildSession(request *common.ChatRequest, raw bool) (session *fsdb.Session, err error) {
if request.SessionName != "" {
var sess *db.Session
if sess, err = o.db.Sessions.GetOrCreateSession(request.SessionName); err != nil {
var sess *fsdb.Session
if sess, err = o.db.Sessions.Get(request.SessionName); err != nil {
err = fmt.Errorf("could not find session %s: %v", request.SessionName, err)
return
}
session = sess
} else {
session = &db.Session{}
session = &fsdb.Session{}
}
if request.Meta != "" {
@@ -81,8 +83,8 @@ func (o *Chatter) BuildSession(request *common.ChatRequest, raw bool) (session *
var contextContent string
if request.ContextName != "" {
var ctx *db.Context
if ctx, err = o.db.Contexts.GetContext(request.ContextName); err != nil {
var ctx *fsdb.Context
if ctx, err = o.db.Contexts.Get(request.ContextName); err != nil {
err = fmt.Errorf("could not find context %s: %v", request.ContextName, err)
return
}
@@ -91,8 +93,8 @@ func (o *Chatter) BuildSession(request *common.ChatRequest, raw bool) (session *
var patternContent string
if request.PatternName != "" {
var pattern *db.Pattern
if pattern, err = o.db.Patterns.GetPattern(request.PatternName, request.PatternVariables); err != nil {
var pattern *fsdb.Pattern
if pattern, err = o.db.Patterns.GetApplyVariables(request.PatternName, request.PatternVariables); err != nil {
err = fmt.Errorf("could not find pattern %s: %v", request.PatternName, err)
return
}

View File

@@ -1,265 +0,0 @@
package core
import (
"bytes"
"fmt"
"os"
"strconv"
"github.com/atotto/clipboard"
"github.com/danielmiessler/fabric/common"
"github.com/danielmiessler/fabric/db"
"github.com/danielmiessler/fabric/jina"
"github.com/danielmiessler/fabric/lang"
"github.com/danielmiessler/fabric/vendors/anthropic"
"github.com/danielmiessler/fabric/vendors/azure"
"github.com/danielmiessler/fabric/vendors/dryrun"
"github.com/danielmiessler/fabric/vendors/gemini"
"github.com/danielmiessler/fabric/vendors/groq"
"github.com/danielmiessler/fabric/vendors/mistral"
"github.com/danielmiessler/fabric/vendors/ollama"
"github.com/danielmiessler/fabric/vendors/openai"
"github.com/danielmiessler/fabric/vendors/openrouter"
"github.com/danielmiessler/fabric/vendors/siliconcloud"
"github.com/danielmiessler/fabric/youtube"
"github.com/pkg/errors"
)
const DefaultPatternsGitRepoUrl = "https://github.com/danielmiessler/fabric.git"
const DefaultPatternsGitRepoFolder = "patterns"
const NoSessionPatternUserMessages = "no session, pattern or user messages provided"
func NewFabric(db *db.Db) (ret *Fabric, err error) {
ret = NewFabricBase(db)
err = ret.Configure()
return
}
func NewFabricForSetup(db *db.Db) (ret *Fabric) {
ret = NewFabricBase(db)
_ = ret.Configure()
return
}
// NewFabricBase Create a new Fabric from a list of already configured VendorsController
func NewFabricBase(db *db.Db) (ret *Fabric) {
ret = &Fabric{
VendorsManager: NewVendorsManager(),
Db: db,
VendorsAll: NewVendorsManager(),
PatternsLoader: NewPatternsLoader(db.Patterns),
YouTube: youtube.NewYouTube(),
Language: lang.NewLanguage(),
Jina: jina.NewClient(),
}
label := "Default"
ret.Configurable = &common.Configurable{
Label: label,
EnvNamePrefix: common.BuildEnvVariablePrefix(label),
ConfigureCustom: ret.configure,
}
ret.DefaultVendor = ret.AddSetting("Vendor", true)
ret.DefaultModel = ret.AddSetupQuestionCustom("Model", true,
"Enter the index the name of your default model")
ret.VendorsAll.AddVendors(openai.NewClient(), azure.NewClient(), ollama.NewClient(), groq.NewClient(),
gemini.NewClient(), anthropic.NewClient(), siliconcloud.NewClient(), openrouter.NewClient(), mistral.NewClient())
return
}
type Fabric struct {
*common.Configurable
*VendorsManager
VendorsAll *VendorsManager
*PatternsLoader
*youtube.YouTube
*lang.Language
Jina *jina.Client
Db *db.Db
DefaultVendor *common.Setting
DefaultModel *common.SetupQuestion
}
type ChannelName struct {
channel chan []string
name string
}
func (o *Fabric) SaveEnvFile() (err error) {
// Now create the .env with all configured VendorsController info
var envFileContent bytes.Buffer
o.Settings.FillEnvFileContent(&envFileContent)
o.PatternsLoader.SetupFillEnvFileContent(&envFileContent)
for _, vendor := range o.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 *Fabric) Setup() (err error) {
if err = o.SetupVendors(); err != nil {
return
}
if err = o.SetupDefaultModel(); err != nil {
return
}
_ = o.YouTube.SetupOrSkip()
if err = o.Jina.SetupOrSkip(); err != nil {
return
}
if err = o.PatternsLoader.Setup(); err != nil {
return
}
if err = o.Language.SetupOrSkip(); err != nil {
return
}
err = o.SaveEnvFile()
return
}
func (o *Fabric) SetupDefaultModel() (err error) {
vendorsModels := o.GetModels()
vendorsModels.Print()
if err = o.Ask(o.Label); err != nil {
return
}
index, parseErr := strconv.Atoi(o.DefaultModel.Value)
if parseErr == nil {
o.DefaultVendor.Value, o.DefaultModel.Value = vendorsModels.GetVendorAndModelByModelIndex(index)
} else {
o.DefaultVendor.Value = vendorsModels.FindVendorsByModelFirst(o.DefaultModel.Value)
}
//verify
vendorNames := vendorsModels.FindVendorsByModel(o.DefaultModel.Value)
if len(vendorNames) == 0 {
err = errors.Errorf("You need to chose an available default model.")
return
}
fmt.Println()
o.DefaultVendor.Print()
o.DefaultModel.Print()
err = o.SaveEnvFile()
return
}
func (o *Fabric) SetupVendors() (err error) {
o.Models = nil
if o.Vendors, err = o.VendorsAll.Setup(); err != nil {
return
}
if !o.HasVendors() {
err = errors.New("No vendors configured")
return
}
err = o.SaveEnvFile()
return
}
func (o *Fabric) SetupVendor(vendorName string) (err error) {
if err = o.VendorsAll.SetupVendor(vendorName, o.Vendors); err != nil {
return
}
err = o.SaveEnvFile()
return
}
// Configure buildClient VendorsController based on the environment variables
func (o *Fabric) configure() (err error) {
for _, vendor := range o.VendorsAll.Vendors {
if vendorErr := vendor.Configure(); vendorErr == nil {
o.AddVendors(vendor)
}
}
if err = o.PatternsLoader.Configure(); err != nil {
return
}
//YouTube and Jina are not mandatory, so ignore not configured error
_ = o.YouTube.Configure()
_ = o.Jina.Configure()
_ = o.Language.Configure()
return
}
func (o *Fabric) GetChatter(model string, stream bool, dryRun bool) (ret *Chatter, err error) {
ret = &Chatter{
db: o.Db,
Stream: stream,
DryRun: dryRun,
}
if dryRun {
ret.vendor = dryrun.NewClient()
ret.model = model
if ret.model == "" {
ret.model = o.DefaultModel.Value
}
} else if model == "" {
ret.vendor = o.FindByName(o.DefaultVendor.Value)
ret.model = o.DefaultModel.Value
} else {
ret.vendor = o.FindByName(o.GetModels().FindVendorsByModelFirst(model))
ret.model = model
}
if ret.vendor == nil {
err = fmt.Errorf(
"could not find vendor.\n Model = %s\n DefaultModel = %s\n DefaultVendor = %s",
model, o.DefaultModel.Value, o.DefaultVendor.Value)
return
}
return
}
func (o *Fabric) CopyToClipboard(message string) (err error) {
if err = clipboard.WriteAll(message); err != nil {
err = fmt.Errorf("could not copy to clipboard: %v", err)
}
return
}
func (o *Fabric) 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
}

View File

@@ -1,49 +0,0 @@
package core
import (
"os"
"testing"
"github.com/danielmiessler/fabric/db"
)
func TestNewFabric(t *testing.T) {
_, err := NewFabric(db.NewDb(os.TempDir()))
if err == nil {
t.Fatal("without setup error expected")
}
}
func TestSaveEnvFile(t *testing.T) {
fabric := NewFabricBase(db.NewDb(os.TempDir()))
err := fabric.SaveEnvFile()
if err != nil {
t.Fatalf("SaveEnvFile() error = %v", err)
}
}
func TestCopyToClipboard(t *testing.T) {
t.Skip("skipping test, because of docker env. in ci.")
fabric := NewFabricBase(db.NewDb(os.TempDir()))
message := "test message"
err := fabric.CopyToClipboard(message)
if err != nil {
t.Fatalf("CopyToClipboard() error = %v", err)
}
}
func TestCreateOutputFile(t *testing.T) {
mockDb := &db.Db{}
fabric := NewFabricBase(mockDb)
fileName := "test_output.txt"
message := "test message"
err := fabric.CreateOutputFile(message, fileName)
if err != nil {
t.Fatalf("CreateOutputFile() error = %v", err)
}
defer os.Remove(fileName)
}

View File

@@ -1,97 +0,0 @@
package core
import (
"fmt"
"sort"
)
func NewVendorsModels() *VendorsModels {
return &VendorsModels{VendorsModels: make(map[string][]string)}
}
type VendorsModels struct {
Vendors []string
VendorsModels map[string][]string
Errs []error
}
func (o *VendorsModels) AddVendorModels(vendor string, models []string) {
o.Vendors = append(o.Vendors, vendor)
o.VendorsModels[vendor] = models
}
func (o *VendorsModels) GetVendorAndModelByModelIndex(modelIndex int) (vendor string, model string) {
vendorModelIndexFrom := 0
vendorModelIndexTo := 0
for _, currenVendor := range o.Vendors {
vendorModelIndexFrom = vendorModelIndexTo + 1
vendorModelIndexTo = vendorModelIndexFrom + len(o.VendorsModels[currenVendor]) - 1
if modelIndex >= vendorModelIndexFrom && modelIndex <= vendorModelIndexTo {
vendor = currenVendor
model = o.VendorsModels[currenVendor][modelIndex-vendorModelIndexFrom]
break
}
}
return
}
func (o *VendorsModels) AddError(err error) {
o.Errs = append(o.Errs, err)
}
func (o *VendorsModels) Print() {
fmt.Printf("\nAvailable vendor models:\n")
sort.Strings(o.Vendors)
var currentModelIndex int
for _, vendor := range o.Vendors {
fmt.Println()
fmt.Printf("%s\n", vendor)
fmt.Println()
currentModelIndex = o.PrintVendor(vendor, currentModelIndex)
}
return
}
func (o *VendorsModels) PrintVendor(vendor string, modelIndex int) (currentModelIndex int) {
currentModelIndex = modelIndex
models := o.VendorsModels[vendor]
for _, model := range models {
currentModelIndex++
fmt.Printf("\t[%d]\t%s\n", currentModelIndex, model)
}
fmt.Println()
return
}
func (o *VendorsModels) GetVendorModels(vendor string) (models []string) {
models = o.VendorsModels[vendor]
return
}
func (o *VendorsModels) HasVendor(vendor string) (ret bool) {
ret = o.VendorsModels[vendor] != nil
return
}
func (o *VendorsModels) FindVendorsByModelFirst(model string) (ret string) {
vendors := o.FindVendorsByModel(model)
if len(vendors) > 0 {
ret = vendors[0]
}
return
}
func (o *VendorsModels) FindVendorsByModel(model string) (vendors []string) {
for vendor, models := range o.VendorsModels {
for _, m := range models {
if m == model {
vendors = append(vendors, vendor)
continue
}
}
}
return
}

View File

@@ -1,52 +0,0 @@
package core
import (
"errors"
"testing"
)
func TestNewVendorsModels(t *testing.T) {
vendors := NewVendorsModels()
if vendors == nil {
t.Fatalf("NewVendorsModels() returned nil")
}
if len(vendors.VendorsModels) != 0 {
t.Fatalf("NewVendorsModels() returned non-empty VendorsModels map")
}
}
func TestFindVendorsByModelFirst(t *testing.T) {
vendors := NewVendorsModels()
vendors.AddVendorModels("vendor1", []string{"model1", "model2"})
vendor := vendors.FindVendorsByModelFirst("model1")
if vendor != "vendor1" {
t.Fatalf("FindVendorsByModelFirst() = %v, want %v", vendor, "vendor1")
}
}
func TestFindVendorsByModel(t *testing.T) {
vendors := NewVendorsModels()
vendors.AddVendorModels("vendor1", []string{"model1", "model2"})
foundVendors := vendors.FindVendorsByModel("model1")
if len(foundVendors) != 1 || foundVendors[0] != "vendor1" {
t.Fatalf("FindVendorsByModel() = %v, want %v", foundVendors, []string{"vendor1"})
}
}
func TestAddVendorModels(t *testing.T) {
vendors := NewVendorsModels()
vendors.AddVendorModels("vendor1", []string{"model1", "model2"})
models := vendors.GetVendorModels("vendor1")
if len(models) != 2 {
t.Fatalf("AddVendorModels() failed to add models")
}
}
func TestAddError(t *testing.T) {
vendors := NewVendorsModels()
err := errors.New("sample error")
vendors.AddError(err)
if len(vendors.Errs) != 1 {
t.Fatalf("AddError() failed to add error")
}
}

203
core/plugin_registry.go Normal file
View File

@@ -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
}

View File

@@ -0,0 +1,16 @@
package core
import (
"github.com/danielmiessler/fabric/plugins/db/fsdb"
"os"
"testing"
)
func TestSaveEnvFile(t *testing.T) {
registry := NewPluginRegistry(fsdb.NewDb(os.TempDir()))
err := registry.SaveEnvFile()
if err != nil {
t.Fatalf("SaveEnvFile() error = %v", err)
}
}

View File

@@ -1,131 +0,0 @@
package core
import (
"bytes"
"context"
"testing"
"github.com/danielmiessler/fabric/common"
)
func TestNewVendorsManager(t *testing.T) {
vendorsManager := NewVendorsManager()
if vendorsManager == nil {
t.Fatalf("NewVendorsManager() returned nil")
}
}
func TestAddVendors(t *testing.T) {
vendorsManager := NewVendorsManager()
mockVendor := &MockVendor{name: "testVendor"}
vendorsManager.AddVendors(mockVendor)
if _, exists := vendorsManager.Vendors[mockVendor.GetName()]; !exists {
t.Fatalf("AddVendors() did not add vendor")
}
}
func TestGetModels(t *testing.T) {
vendorsManager := NewVendorsManager()
mockVendor := &MockVendor{name: "testVendor"}
vendorsManager.AddVendors(mockVendor)
models := vendorsManager.GetModels()
if models == nil {
t.Fatalf("GetModels() returned nil")
}
}
func TestHasVendors(t *testing.T) {
vendorsManager := NewVendorsManager()
if vendorsManager.HasVendors() {
t.Fatalf("HasVendors() should return false for an empty manager")
}
mockVendor := &MockVendor{name: "testVendor"}
vendorsManager.AddVendors(mockVendor)
if !vendorsManager.HasVendors() {
t.Fatalf("HasVendors() should return true after adding a vendor")
}
}
func TestFindByName(t *testing.T) {
vendorsManager := NewVendorsManager()
mockVendor := &MockVendor{name: "testVendor"}
vendorsManager.AddVendors(mockVendor)
foundVendor := vendorsManager.FindByName("testVendor")
if foundVendor == nil {
t.Fatalf("FindByName() did not find added vendor")
}
}
func TestReadModels(t *testing.T) {
vendorsManager := NewVendorsManager()
mockVendor := &MockVendor{name: "testVendor"}
vendorsManager.AddVendors(mockVendor)
vendorsManager.readModels()
if vendorsManager.Models == nil || len(vendorsManager.Models.Vendors) == 0 {
t.Fatalf("readModels() did not read models correctly")
}
}
func TestSetup(t *testing.T) {
vendorsManager := NewVendorsManager()
mockVendor := &MockVendor{name: "testVendor"}
vendorsManager.AddVendors(mockVendor)
vendors, err := vendorsManager.Setup()
if err != nil {
t.Fatalf("Setup() error = %v", err)
}
if len(vendors) == 0 {
t.Fatalf("Setup() did not setup any vendors")
}
}
// MockVendor is a mock implementation of the Vendor interface for testing purposes.
type MockVendor struct {
*common.Settings
name string
}
func (o *MockVendor) SendStream(messages []*common.Message, options *common.ChatOptions, strings chan string) error {
// TODO implement me
panic("implement me")
}
func (o *MockVendor) Send(ctx context.Context, messages []*common.Message, options *common.ChatOptions) (string, error) {
// TODO implement me
panic("implement me")
}
func (o *MockVendor) SetupFillEnvFileContent(buffer *bytes.Buffer) {
// TODO implement me
panic("implement me")
}
func (o *MockVendor) IsConfigured() bool {
return false
}
func (o *MockVendor) GetSettings() *common.Settings {
return o.Settings
}
func (o *MockVendor) GetName() string {
return o.name
}
func (o *MockVendor) Configure() error {
return nil
}
func (o *MockVendor) Setup() error {
return nil
}
func (o *MockVendor) ListModels() ([]string, error) {
return []string{"model1", "model2"}, nil
}

View File

@@ -1 +0,0 @@
package db

29
go.mod
View File

@@ -5,6 +5,7 @@ go 1.22.5
require (
github.com/anaskhan96/soup v1.2.5
github.com/atotto/clipboard v0.1.4
github.com/gin-gonic/gin v1.10.0
github.com/go-git/go-git/v5 v5.12.0
github.com/go-shiori/go-readability v0.0.0-20240923125239-59a7bd165825
github.com/google/generative-ai-go v0.18.0
@@ -17,7 +18,7 @@ require (
github.com/samber/lo v1.47.0
github.com/sashabaranov/go-openai v1.30.0
github.com/stretchr/testify v1.9.0
golang.org/x/text v0.18.0
golang.org/x/text v0.19.0
google.golang.org/api v0.197.0
)
@@ -33,16 +34,26 @@ require (
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
github.com/bytedance/sonic v1.11.6 // indirect
github.com/bytedance/sonic/loader v0.1.1 // indirect
github.com/cloudflare/circl v1.4.0 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/cyphar/filepath-securejoin v0.3.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.5.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/google/s2a-go v0.1.8 // indirect
@@ -50,11 +61,20 @@ require (
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.7 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.12 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 // indirect
@@ -62,11 +82,12 @@ require (
go.opentelemetry.io/otel v1.30.0 // indirect
go.opentelemetry.io/otel/metric v1.30.0 // indirect
go.opentelemetry.io/otel/trace v1.30.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/arch v0.8.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/time v0.6.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect

74
go.sum
View File

@@ -32,11 +32,19 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkY
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.4.0 h1:BV7h5MgrktNzytKmWjpOtdYrf0lkkbF8YMlBGPhJQrY=
github.com/cloudflare/circl v1.4.0/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y=
github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cyphar/filepath-securejoin v0.3.2 h1:QhZu5AxQ+o1XZH0Ye05YzvJ0kAdK6VQc0z9NNMek7gc=
github.com/cyphar/filepath-securejoin v0.3.2/go.mod h1:F7i41x/9cBF7lzCrVsYs9fuzwRZm4NQsGTBdpp6mETc=
@@ -53,6 +61,12 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
@@ -68,10 +82,20 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8=
github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c h1:wpkoddUomPfHiOziHZixGO5ZBS73cKqVzZipfrLmO1w=
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c/go.mod h1:oVDCh3qjJMLVUSILBRwrm+Bc6RNXGZYtoh9xdvf1ffM=
github.com/go-shiori/go-readability v0.0.0-20240923125239-59a7bd165825 h1:CpSi7xiWqGaAqVn/2MsbRoDmPwXMvvQUu3hLjX1QrOM=
github.com/go-shiori/go-readability v0.0.0-20240923125239-59a7bd165825/go.mod h1:YWa00ashoPZMAOElrSn4E1cJErhDVU6PWAll4Hxzn+w=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
@@ -100,6 +124,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -115,8 +140,14 @@ github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bB
github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -124,9 +155,18 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/liushuangls/go-anthropic/v2 v2.8.0 h1:0zH2jDNycbrlszxnLrG+Gx8vVT0yJAPWU4s3ZTkWzgI=
github.com/liushuangls/go-anthropic/v2 v2.8.0/go.mod h1:8BKv/fkeTaL5R9R9bGkaknYBueyw2WxY20o7bImbOek=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/ollama/ollama v0.3.11 h1:Fs1B5WjXYUvr5bkMZZpUJfiqIAxrymujRidFABwMeV8=
github.com/ollama/ollama v0.3.11/go.mod h1:YrWoNkFnPOYsnDvsf/Ztb1wxU9/IXrNsQHqcxbY2r94=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
@@ -135,6 +175,8 @@ github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
github.com/otiai10/mint v1.5.1 h1:XaPLeE+9vGbuyEHem1JNk3bYc7KKqyI/na0/mLd/Kks=
github.com/otiai10/mint v1.5.1/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -158,15 +200,22 @@ github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
@@ -182,14 +231,17 @@ go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4Q
go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ=
go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc=
go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -211,8 +263,8 @@ golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
@@ -240,16 +292,16 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -258,8 +310,8 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -314,3 +366,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

View File

@@ -1,41 +0,0 @@
package lang
import (
"github.com/danielmiessler/fabric/common"
"golang.org/x/text/language"
)
func NewLanguage() (ret *Language) {
label := "Language"
ret = &Language{}
ret.Configurable = &common.Configurable{
Label: label,
EnvNamePrefix: common.BuildEnvVariablePrefix(label),
ConfigureCustom: ret.configure,
}
ret.DefaultLanguage = ret.Configurable.AddSetupQuestionCustom("Output", false,
"Enter your default want output lang (for example: zh_CN)")
return
}
type Language struct {
*common.Configurable
DefaultLanguage *common.SetupQuestion
}
func (o *Language) configure() error {
if o.DefaultLanguage.Value != "" {
langTag, err := language.Parse(o.DefaultLanguage.Value)
if err == nil {
o.DefaultLanguage.Value = langTag.String()
} else {
o.DefaultLanguage.Value = ""
}
}
return nil
}

View File

@@ -5,7 +5,9 @@ You are an expert at extracting the sponsors and potential sponsors from a given
# Steps
- Consume the whole transcript so you understand what is content, what is meta information, etc.
- Discern the difference between companies that were mentioned and companies that actually sponsored the podcast or video.
- Output the following:
## OFFICIAL SPONSORS
@@ -15,36 +17,20 @@ You are an expert at extracting the sponsors and potential sponsors from a given
- $SOURCE_CHANNEL$ | $SPONSOR3$ | $SPONSOR3_DESCRIPTION$ | $SPONSOR3_LINK$
- And so on…
## POTENTIAL SPONSORS
- $SOURCE_CHANNEL$ | $SPONSOR1$ | $SPONSOR1_DESCRIPTION$ | $SPONSOR1_LINK$
- $SOURCE_CHANNEL$ | $SPONSOR2$ | $SPONSOR2_DESCRIPTION$ | $SPONSOR2_LINK$
- $SOURCE_CHANNEL$ | $SPONSOR3$ | $SPONSOR3_DESCRIPTION$ | $SPONSOR3_LINK$
- And so on…
# EXAMPLE OUTPUT
## OFFICIAL SPONSORS
- AI Jason's YouTube Channel | Flair | Flair is a threat intel platform powered by AI. | https://flair.ai
- Matthew Berman's YouTube Channel | Weaviate | Weviate is an open-source knowledge graph powered by ML. | https://weaviate.com
- Unsupervised Learning Website | JunaAI | JunaAI is a platform for AI-powered content creation. | https://junaai.com
- The AI Junkie Podcast | JunaAI | JunaAI is a platform for AI-powered content creation. | https://junaai.com
## POTENTIAL SPONSORS
- AI Jason's YouTube Channel | Flair | Flair is a threat intel platform powered by AI. | https://flair.ai
- Matthew Berman's YouTube Channel | Weaviate | Weviate is an open-source knowledge graph powered by ML. | https://weaviate.com
- Unsupervised Learning Website | JunaAI | JunaAI is a platform for AI-powered content creation. | https://junaai.com
- The AI Junkie Podcast | JunaAI | JunaAI is a platform for AI-powered content creation. | https://junaai.com
- Flair | Flair is a threat intel platform powered by AI. | https://flair.ai
- Weaviate | Weviate is an open-source knowledge graph powered by ML. | https://weaviate.com
- JunaAI | JunaAI is a platform for AI-powered content creation. | https://junaai.com
- JunaAI | JunaAI is a platform for AI-powered content creation. | https://junaai.com
## END EXAMPLE OUTPUT
# OUTPUT INSTRUCTIONS
- The official sponsor list should only include companies that officially sponsored the content in question.
- The potential sponsor list should include companies that were mentioned during the content but that didn't officially sponsor.
- Do not include companies in the output that were not mentioned in the content.
- Do not output warnings or notes—just the requested sections.
# INPUT:

View File

@@ -4,6 +4,8 @@ import (
"context"
"errors"
"fmt"
"github.com/danielmiessler/fabric/plugins"
goopenai "github.com/sashabaranov/go-openai"
"github.com/danielmiessler/fabric/common"
@@ -16,15 +18,15 @@ func NewClient() (ret *Client) {
vendorName := "Anthropic"
ret = &Client{}
ret.Configurable = &common.Configurable{
Label: vendorName,
EnvNamePrefix: common.BuildEnvVariablePrefix(vendorName),
ret.PluginBase = &plugins.PluginBase{
Name: vendorName,
EnvNamePrefix: plugins.BuildEnvVariablePrefix(vendorName),
ConfigureCustom: ret.configure,
}
ret.ApiBaseURL = ret.AddSetupQuestion("API Base URL", false)
ret.ApiBaseURL.Value = baseUrl
ret.ApiKey = ret.Configurable.AddSetupQuestion("API key", true)
ret.ApiKey = ret.PluginBase.AddSetupQuestion("API key", true)
// we could provide a setup question for the following settings
ret.maxTokens = 4096
@@ -39,9 +41,9 @@ func NewClient() (ret *Client) {
}
type Client struct {
*common.Configurable
ApiBaseURL *common.SetupQuestion
ApiKey *common.SetupQuestion
*plugins.PluginBase
ApiBaseURL *plugins.SetupQuestion
ApiKey *plugins.SetupQuestion
maxTokens int
defaultRequiredUserMessage string

View File

@@ -1,10 +1,10 @@
package azure
import (
"github.com/danielmiessler/fabric/plugins"
"github.com/danielmiessler/fabric/plugins/ai/openai"
"strings"
"github.com/danielmiessler/fabric/common"
"github.com/danielmiessler/fabric/vendors/openai"
goopenai "github.com/sashabaranov/go-openai"
)
@@ -19,7 +19,7 @@ func NewClient() (ret *Client) {
type Client struct {
*openai.Client
ApiDeployments *common.SetupQuestion
ApiDeployments *plugins.SetupQuestion
apiDeployments []string
}

View File

@@ -4,27 +4,18 @@ import (
"bytes"
"context"
"fmt"
"github.com/danielmiessler/fabric/plugins"
goopenai "github.com/sashabaranov/go-openai"
"github.com/danielmiessler/fabric/common"
)
type Client struct{}
type Client struct {
*plugins.PluginBase
}
func NewClient() *Client {
return &Client{}
}
func (c *Client) GetName() string {
return "DryRun"
}
func (c *Client) IsConfigured() bool {
return true
}
func (c *Client) Configure() error {
return nil
return &Client{PluginBase: &plugins.PluginBase{Name: "DryRun"}}
}
func (c *Client) ListModels() ([]string, error) {

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/danielmiessler/fabric/plugins"
"strings"
"github.com/danielmiessler/fabric/common"
@@ -18,19 +19,19 @@ func NewClient() (ret *Client) {
vendorName := "Gemini"
ret = &Client{}
ret.Configurable = &common.Configurable{
Label: vendorName,
EnvNamePrefix: common.BuildEnvVariablePrefix(vendorName),
ret.PluginBase = &plugins.PluginBase{
Name: vendorName,
EnvNamePrefix: plugins.BuildEnvVariablePrefix(vendorName),
}
ret.ApiKey = ret.Configurable.AddSetupQuestion("API key", true)
ret.ApiKey = ret.PluginBase.AddSetupQuestion("API key", true)
return
}
type Client struct {
*common.Configurable
ApiKey *common.SetupQuestion
*plugins.PluginBase
ApiKey *plugins.SetupQuestion
}
func (o *Client) ListModels() (ret []string, err error) {

View File

@@ -1,7 +1,7 @@
package groq
import (
"github.com/danielmiessler/fabric/vendors/openai"
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {

View File

@@ -1,7 +1,7 @@
package mistral
import (
"github.com/danielmiessler/fabric/vendors/openai"
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {

13
plugins/ai/models.go Normal file
View File

@@ -0,0 +1,13 @@
package ai
import (
"github.com/danielmiessler/fabric/common"
)
func NewVendorsModels() *VendorsModels {
return &VendorsModels{GroupsItemsSelectorString: common.NewGroupsItemsSelectorString("Available models")}
}
type VendorsModels struct {
*common.GroupsItemsSelectorString
}

33
plugins/ai/models_test.go Normal file
View File

@@ -0,0 +1,33 @@
package ai
import (
"testing"
)
func TestNewVendorsModels(t *testing.T) {
vendors := NewVendorsModels()
if vendors == nil {
t.Fatalf("NewVendorsModels() returned nil")
}
if len(vendors.GroupsItems) != 0 {
t.Fatalf("NewVendorsModels() returned non-empty VendorsModels map")
}
}
func TestFindVendorsByModelFirst(t *testing.T) {
vendors := NewVendorsModels()
vendors.AddGroupItems("vendor1", []string{"model1", "model2"}...)
vendor := vendors.FindGroupsByItemFirst("model1")
if vendor != "vendor1" {
t.Fatalf("FindVendorsByModelFirst() = %v, want %v", vendor, "vendor1")
}
}
func TestFindVendorsByModel(t *testing.T) {
vendors := NewVendorsModels()
vendors.AddGroupItems("vendor1", []string{"model1", "model2"}...)
foundVendors := vendors.FindGroupsByItem("model1")
if len(foundVendors) != 1 || foundVendors[0] != "vendor1" {
t.Fatalf("FindVendorsByModel() = %v, want %v", foundVendors, []string{"vendor1"})
}
}

View File

@@ -3,6 +3,7 @@ package ollama
import (
"context"
"fmt"
"github.com/danielmiessler/fabric/plugins"
"net/http"
"net/url"
"time"
@@ -17,21 +18,21 @@ func NewClient() (ret *Client) {
vendorName := "Ollama"
ret = &Client{}
ret.Configurable = &common.Configurable{
Label: vendorName,
EnvNamePrefix: common.BuildEnvVariablePrefix(vendorName),
ret.PluginBase = &plugins.PluginBase{
Name: vendorName,
EnvNamePrefix: plugins.BuildEnvVariablePrefix(vendorName),
ConfigureCustom: ret.configure,
}
ret.ApiUrl = ret.Configurable.AddSetupQuestionCustom("API URL", true,
ret.ApiUrl = ret.AddSetupQuestionCustom("API URL", true,
"Enter your Ollama URL (as a reminder, it is usually http://localhost:11434)")
return
}
type Client struct {
*common.Configurable
ApiUrl *common.SetupQuestion
*plugins.PluginBase
ApiUrl *plugins.SetupQuestion
apiUrl *url.URL
client *ollamaapi.Client

View File

@@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"github.com/danielmiessler/fabric/plugins"
"io"
"log/slog"
@@ -24,9 +25,9 @@ func NewClientCompatible(vendorName string, defaultBaseUrl string, configureCust
configureCustom = ret.configure
}
ret.Configurable = &common.Configurable{
Label: vendorName,
EnvNamePrefix: common.BuildEnvVariablePrefix(vendorName),
ret.PluginBase = &plugins.PluginBase{
Name: vendorName,
EnvNamePrefix: plugins.BuildEnvVariablePrefix(vendorName),
ConfigureCustom: configureCustom,
}
@@ -38,9 +39,9 @@ func NewClientCompatible(vendorName string, defaultBaseUrl string, configureCust
}
type Client struct {
*common.Configurable
ApiKey *common.SetupQuestion
ApiBaseURL *common.SetupQuestion
*plugins.PluginBase
ApiKey *plugins.SetupQuestion
ApiBaseURL *plugins.SetupQuestion
ApiClient *openai.Client
}

View File

@@ -1,7 +1,7 @@
package openrouter
import (
"github.com/danielmiessler/fabric/vendors/openai"
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {

View File

@@ -1,7 +1,7 @@
package siliconcloud
import (
"github.com/danielmiessler/fabric/vendors/openai"
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {

View File

@@ -1,19 +1,15 @@
package vendors
package ai
import (
"bytes"
"context"
"github.com/danielmiessler/fabric/plugins"
"github.com/danielmiessler/fabric/common"
)
type Vendor interface {
GetName() string
IsConfigured() bool
Configure() error
plugins.Plugin
ListModels() ([]string, error)
SendStream([]*common.Message, *common.ChatOptions, chan string) error
Send(context.Context, []*common.Message, *common.ChatOptions) (string, error)
Setup() error
SetupFillEnvFileContent(*bytes.Buffer)
}

View File

@@ -1,46 +1,70 @@
package core
package ai
import (
"bytes"
"context"
"fmt"
"github.com/danielmiessler/fabric/plugins"
"sync"
"github.com/danielmiessler/fabric/vendors"
)
func NewVendorsManager() *VendorsManager {
return &VendorsManager{
Vendors: map[string]vendors.Vendor{},
Vendors: []Vendor{},
VendorsByName: map[string]Vendor{},
}
}
type VendorsManager struct {
Vendors map[string]vendors.Vendor
Models *VendorsModels
*plugins.PluginBase
Vendors []Vendor
VendorsByName map[string]Vendor
Models *VendorsModels
}
func (o *VendorsManager) AddVendors(vendors ...vendors.Vendor) {
func (o *VendorsManager) AddVendors(vendors ...Vendor) {
for _, vendor := range vendors {
o.Vendors[vendor.GetName()] = vendor
o.VendorsByName[vendor.GetName()] = vendor
o.Vendors = append(o.Vendors, vendor)
}
}
func (o *VendorsManager) GetModels() *VendorsModels {
if o.Models == nil {
o.readModels()
func (o *VendorsManager) SetupFillEnvFileContent(envFileContent *bytes.Buffer) {
for _, vendor := range o.Vendors {
vendor.SetupFillEnvFileContent(envFileContent)
}
return o.Models
}
func (o *VendorsManager) GetModels() (ret *VendorsModels, err error) {
if o.Models == nil {
err = o.readModels()
}
ret = o.Models
return
}
func (o *VendorsManager) Configure() (err error) {
for _, vendor := range o.Vendors {
_ = vendor.Configure()
}
return
}
func (o *VendorsManager) HasVendors() bool {
return len(o.Vendors) > 0
}
func (o *VendorsManager) FindByName(name string) vendors.Vendor {
return o.Vendors[name]
func (o *VendorsManager) FindByName(name string) Vendor {
return o.VendorsByName[name]
}
func (o *VendorsManager) readModels() {
func (o *VendorsManager) readModels() (err error) {
if len(o.Vendors) == 0 {
err = fmt.Errorf("no AI vendors configured to read models from. Please configure at least one AI vendor")
return
}
o.Models = NewVendorsModels()
var wg sync.WaitGroup
@@ -63,16 +87,16 @@ func (o *VendorsManager) readModels() {
for result := range resultsChan {
if result.err != nil {
fmt.Println(result.vendorName, result.err)
o.Models.AddError(result.err)
cancel() // Cancel remaining goroutines if needed
} else {
o.Models.AddVendorModels(result.vendorName, result.models)
o.Models.AddGroupItems(result.vendorName, result.models...)
}
}
return
}
func (o *VendorsManager) fetchVendorModels(
ctx context.Context, wg *sync.WaitGroup, vendor vendors.Vendor, resultsChan chan<- modelResult) {
ctx context.Context, wg *sync.WaitGroup, vendor Vendor, resultsChan chan<- modelResult) {
defer wg.Done()
@@ -86,8 +110,8 @@ func (o *VendorsManager) fetchVendorModels(
}
}
func (o *VendorsManager) Setup() (ret map[string]vendors.Vendor, err error) {
ret = map[string]vendors.Vendor{}
func (o *VendorsManager) Setup() (ret map[string]Vendor, err error) {
ret = map[string]Vendor{}
for _, vendor := range o.Vendors {
fmt.Println()
o.setupVendorTo(vendor, ret)
@@ -95,17 +119,7 @@ func (o *VendorsManager) Setup() (ret map[string]vendors.Vendor, err error) {
return
}
func (o *VendorsManager) setupVendorTo(vendor vendors.Vendor, configuredVendors map[string]vendors.Vendor) {
if vendorErr := vendor.Setup(); vendorErr == nil {
fmt.Printf("[%v] configured\n", vendor.GetName())
configuredVendors[vendor.GetName()] = vendor
} else {
delete(configuredVendors, vendor.GetName())
fmt.Printf("[%v] skipped\n", vendor.GetName())
}
}
func (o *VendorsManager) SetupVendor(vendorName string, configuredVendors map[string]vendors.Vendor) (err error) {
func (o *VendorsManager) SetupVendor(vendorName string, configuredVendors map[string]Vendor) (err error) {
vendor := o.FindByName(vendorName)
if vendor == nil {
err = fmt.Errorf("vendor %s not found", vendorName)
@@ -115,6 +129,17 @@ func (o *VendorsManager) SetupVendor(vendorName string, configuredVendors map[st
return
}
func (o *VendorsManager) setupVendorTo(vendor Vendor, configuredVendors map[string]Vendor) {
if vendorErr := vendor.Setup(); vendorErr == nil {
fmt.Printf("[%v] configured\n", vendor.GetName())
configuredVendors[vendor.GetName()] = vendor
} else {
delete(configuredVendors, vendor.GetName())
fmt.Printf("[%v] skipped\n", vendor.GetName())
}
return
}
type modelResult struct {
vendorName string
models []string

13
plugins/db/api.go Normal file
View File

@@ -0,0 +1,13 @@
package db
type Storage[T any] interface {
Configure() (err error)
Get(name string) (ret *T, err error)
GetNames() (ret []string, err error)
Delete(name string) (err error)
Exists(name string) (ret bool)
Rename(oldName, newName string) (err error)
Save(name string, content []byte) (err error)
Load(name string) (ret []byte, err error)
ListNames() (err error)
}

View File

@@ -1,13 +1,13 @@
package db
package fsdb
import "fmt"
type Contexts struct {
*Storage
type ContextsEntity struct {
*StorageEntity
}
// GetContext Load a context from file
func (o *Contexts) GetContext(name string) (ret *Context, err error) {
// Get Load a context from file
func (o *ContextsEntity) Get(name string) (ret *Context, err error) {
var content []byte
if content, err = o.Load(name); err != nil {
return
@@ -17,9 +17,9 @@ func (o *Contexts) GetContext(name string) (ret *Context, err error) {
return
}
func (o *Contexts) PrintContext(name string) (err error) {
func (o *ContextsEntity) PrintContext(name string) (err error) {
var context *Context
if context, err = o.GetContext(name); err != nil {
if context, err = o.Get(name); err != nil {
return
}
fmt.Println(context.Content)

View File

@@ -1,4 +1,4 @@
package db
package fsdb
import (
"os"
@@ -8,8 +8,8 @@ import (
func TestContexts_GetContext(t *testing.T) {
dir := t.TempDir()
contexts := &Contexts{
Storage: &Storage{Dir: dir},
contexts := &ContextsEntity{
StorageEntity: &StorageEntity{Dir: dir},
}
contextName := "testContext"
contextPath := filepath.Join(dir, contextName)
@@ -18,7 +18,7 @@ func TestContexts_GetContext(t *testing.T) {
if err != nil {
t.Fatalf("failed to write context file: %v", err)
}
context, err := contexts.GetContext(contextName)
context, err := contexts.Get(contextName)
if err != nil {
t.Fatalf("failed to get context: %v", err)
}

View File

@@ -1,4 +1,4 @@
package db
package fsdb
import (
"fmt"
@@ -14,17 +14,17 @@ func NewDb(dir string) (db *Db) {
db.EnvFilePath = db.FilePath(".env")
db.Patterns = &Patterns{
Storage: &Storage{Label: "Patterns", Dir: db.FilePath("patterns"), ItemIsDir: true},
db.Patterns = &PatternsEntity{
StorageEntity: &StorageEntity{Label: "Patterns", Dir: db.FilePath("patterns"), ItemIsDir: true},
SystemPatternFile: "system.md",
UniquePatternsFilePath: db.FilePath("unique_patterns.txt"),
}
db.Sessions = &Sessions{
&Storage{Label: "Sessions", Dir: db.FilePath("sessions"), FileExtension: ".json"}}
db.Sessions = &SessionsEntity{
&StorageEntity{Label: "Sessions", Dir: db.FilePath("sessions"), FileExtension: ".json"}}
db.Contexts = &Contexts{
&Storage{Label: "Contexts", Dir: db.FilePath("contexts")}}
db.Contexts = &ContextsEntity{
&StorageEntity{Label: "Contexts", Dir: db.FilePath("contexts")}}
return
}
@@ -32,9 +32,9 @@ func NewDb(dir string) (db *Db) {
type Db struct {
Dir string
Patterns *Patterns
Sessions *Sessions
Contexts *Contexts
Patterns *PatternsEntity
Sessions *SessionsEntity
Contexts *ContextsEntity
EnvFilePath string
}

View File

@@ -1,4 +1,4 @@
package db
package fsdb
import (
"os"

View File

@@ -1,4 +1,4 @@
package db
package fsdb
import (
"fmt"
@@ -7,14 +7,13 @@ import (
"strings"
)
type Patterns struct {
*Storage
type PatternsEntity struct {
*StorageEntity
SystemPatternFile string
UniquePatternsFilePath string
}
// GetPattern finds a pattern by name and returns the pattern as an entry or an error
func (o *Patterns) GetPattern(name string, variables map[string]string) (ret *Pattern, err error) {
func (o *PatternsEntity) Get(name string) (ret *Pattern, err error) {
patternPath := filepath.Join(o.Dir, name, o.SystemPatternFile)
var pattern []byte
@@ -23,13 +22,6 @@ func (o *Patterns) GetPattern(name string, variables map[string]string) (ret *Pa
}
patternStr := string(pattern)
if variables != nil && len(variables) > 0 {
for variableName, value := range variables {
patternStr = strings.ReplaceAll(patternStr, variableName, value)
}
}
ret = &Pattern{
Name: name,
Pattern: patternStr,
@@ -37,7 +29,22 @@ func (o *Patterns) GetPattern(name string, variables map[string]string) (ret *Pa
return
}
func (o *Patterns) PrintLatestPatterns(latestNumber int) (err error) {
// GetApplyVariables finds a pattern by name and returns the pattern as an entry or an error
func (o *PatternsEntity) GetApplyVariables(name string, variables map[string]string) (ret *Pattern, err error) {
if ret, err = o.Get(name); err != nil {
return
}
if variables != nil && len(variables) > 0 {
for variableName, value := range variables {
ret.Pattern = strings.ReplaceAll(ret.Pattern, variableName, value)
}
}
return
}
func (o *PatternsEntity) PrintLatestPatterns(latestNumber int) (err error) {
var contents []byte
if contents, err = os.ReadFile(o.UniquePatternsFilePath); err != nil {
err = fmt.Errorf("could not read unique patterns file. Pleas run --updatepatterns (%s)", err)

View File

@@ -0,0 +1 @@
package fsdb

View File

@@ -1,15 +1,15 @@
package db
package fsdb
import (
"fmt"
"github.com/danielmiessler/fabric/common"
)
type Sessions struct {
*Storage
type SessionsEntity struct {
*StorageEntity
}
func (o *Sessions) GetOrCreateSession(name string) (session *Session, err error) {
func (o *SessionsEntity) Get(name string) (session *Session, err error) {
session = &Session{Name: name}
if o.Exists(name) {
@@ -20,7 +20,7 @@ func (o *Sessions) GetOrCreateSession(name string) (session *Session, err error)
return
}
func (o *Sessions) PrintSession(name string) (err error) {
func (o *SessionsEntity) PrintSession(name string) (err error) {
if o.Exists(name) {
var session Session
if err = o.LoadAsJson(name, &session.Messages); err == nil {
@@ -30,7 +30,7 @@ func (o *Sessions) PrintSession(name string) (err error) {
return
}
func (o *Sessions) SaveSession(session *Session) (err error) {
func (o *SessionsEntity) SaveSession(session *Session) (err error) {
return o.SaveAsJson(session.Name, session.Messages)
}

View File

@@ -1,4 +1,4 @@
package db
package fsdb
import (
"testing"
@@ -8,11 +8,11 @@ import (
func TestSessions_GetOrCreateSession(t *testing.T) {
dir := t.TempDir()
sessions := &Sessions{
Storage: &Storage{Dir: dir, FileExtension: ".json"},
sessions := &SessionsEntity{
StorageEntity: &StorageEntity{Dir: dir, FileExtension: ".json"},
}
sessionName := "testSession"
session, err := sessions.GetOrCreateSession(sessionName)
session, err := sessions.Get(sessionName)
if err != nil {
t.Fatalf("failed to get or create session: %v", err)
}
@@ -23,8 +23,8 @@ func TestSessions_GetOrCreateSession(t *testing.T) {
func TestSessions_SaveSession(t *testing.T) {
dir := t.TempDir()
sessions := &Sessions{
Storage: &Storage{Dir: dir, FileExtension: ".json"},
sessions := &SessionsEntity{
StorageEntity: &StorageEntity{Dir: dir, FileExtension: ".json"},
}
sessionName := "testSession"
session := &Session{Name: sessionName, Messages: []*common.Message{{Content: "message1"}}}

View File

@@ -1,4 +1,4 @@
package db
package fsdb
import (
"encoding/json"
@@ -10,14 +10,14 @@ import (
"github.com/samber/lo"
)
type Storage struct {
type StorageEntity struct {
Label string
Dir string
ItemIsDir bool
FileExtension string
}
func (o *Storage) Configure() (err error) {
func (o *StorageEntity) Configure() (err error) {
if err = os.MkdirAll(o.Dir, os.ModePerm); err != nil {
return
}
@@ -25,7 +25,7 @@ func (o *Storage) Configure() (err error) {
}
// GetNames finds all patterns in the patterns directory and enters the id, name, and pattern into a slice of Entry structs. it returns these entries or an error
func (o *Storage) GetNames() (ret []string, err error) {
func (o *StorageEntity) GetNames() (ret []string, err error) {
var entries []os.DirEntry
if entries, err = os.ReadDir(o.Dir); err != nil {
err = fmt.Errorf("could not read items from directory: %v", err)
@@ -59,7 +59,41 @@ func (o *Storage) GetNames() (ret []string, err error) {
return
}
func (o *Storage) ListNames() (err error) {
func (o *StorageEntity) Delete(name string) (err error) {
if err = os.Remove(o.BuildFilePathByName(name)); err != nil {
err = fmt.Errorf("could not delete %s: %v", name, err)
}
return
}
func (o *StorageEntity) Exists(name string) (ret bool) {
_, err := os.Stat(o.BuildFilePathByName(name))
ret = !os.IsNotExist(err)
return
}
func (o *StorageEntity) Rename(oldName, newName string) (err error) {
if err = os.Rename(o.BuildFilePathByName(oldName), o.BuildFilePathByName(newName)); err != nil {
err = fmt.Errorf("could not rename %s to %s: %v", oldName, newName, err)
}
return
}
func (o *StorageEntity) Save(name string, content []byte) (err error) {
if err = os.WriteFile(o.BuildFilePathByName(name), content, 0644); err != nil {
err = fmt.Errorf("could not save %s: %v", name, err)
}
return
}
func (o *StorageEntity) Load(name string) (ret []byte, err error) {
if ret, err = os.ReadFile(o.BuildFilePathByName(name)); err != nil {
err = fmt.Errorf("could not load %s: %v", name, err)
}
return
}
func (o *StorageEntity) ListNames() (err error) {
var names []string
if names, err = o.GetNames(); err != nil {
return
@@ -76,55 +110,21 @@ func (o *Storage) ListNames() (err error) {
return
}
func (o *Storage) BuildFilePathByName(name string) (ret string) {
func (o *StorageEntity) BuildFilePathByName(name string) (ret string) {
ret = o.BuildFilePath(o.buildFileName(name))
return
}
func (o *Storage) BuildFilePath(fileName string) (ret string) {
func (o *StorageEntity) BuildFilePath(fileName string) (ret string) {
ret = filepath.Join(o.Dir, fileName)
return
}
func (o *Storage) buildFileName(name string) string {
func (o *StorageEntity) buildFileName(name string) string {
return fmt.Sprintf("%s%v", name, o.FileExtension)
}
func (o *Storage) Delete(name string) (err error) {
if err = os.Remove(o.BuildFilePathByName(name)); err != nil {
err = fmt.Errorf("could not delete %s: %v", name, err)
}
return
}
func (o *Storage) Exists(name string) (ret bool) {
_, err := os.Stat(o.BuildFilePathByName(name))
ret = !os.IsNotExist(err)
return
}
func (o *Storage) Rename(oldName, newName string) (err error) {
if err = os.Rename(o.BuildFilePathByName(oldName), o.BuildFilePathByName(newName)); err != nil {
err = fmt.Errorf("could not rename %s to %s: %v", oldName, newName, err)
}
return
}
func (o *Storage) Save(name string, content []byte) (err error) {
if err = os.WriteFile(o.BuildFilePathByName(name), content, 0644); err != nil {
err = fmt.Errorf("could not save %s: %v", name, err)
}
return
}
func (o *Storage) Load(name string) (ret []byte, err error) {
if ret, err = os.ReadFile(o.BuildFilePathByName(name)); err != nil {
err = fmt.Errorf("could not load %s: %v", name, err)
}
return
}
func (o *Storage) SaveAsJson(name string, item interface{}) (err error) {
func (o *StorageEntity) SaveAsJson(name string, item interface{}) (err error) {
var jsonString []byte
if jsonString, err = json.Marshal(item); err == nil {
err = o.Save(name, jsonString)
@@ -135,7 +135,7 @@ func (o *Storage) SaveAsJson(name string, item interface{}) (err error) {
return err
}
func (o *Storage) LoadAsJson(name string, item interface{}) (err error) {
func (o *StorageEntity) LoadAsJson(name string, item interface{}) (err error) {
var content []byte
if content, err = o.Load(name); err != nil {
return

View File

@@ -1,4 +1,4 @@
package db
package fsdb
import (
"testing"
@@ -6,7 +6,7 @@ import (
func TestStorage_SaveAndLoad(t *testing.T) {
dir := t.TempDir()
storage := &Storage{Dir: dir}
storage := &StorageEntity{Dir: dir}
name := "test"
content := []byte("test content")
if err := storage.Save(name, content); err != nil {
@@ -23,7 +23,7 @@ func TestStorage_SaveAndLoad(t *testing.T) {
func TestStorage_Exists(t *testing.T) {
dir := t.TempDir()
storage := &Storage{Dir: dir}
storage := &StorageEntity{Dir: dir}
name := "test"
if storage.Exists(name) {
t.Errorf("expected file to not exist")
@@ -38,7 +38,7 @@ func TestStorage_Exists(t *testing.T) {
func TestStorage_Delete(t *testing.T) {
dir := t.TempDir()
storage := &Storage{Dir: dir}
storage := &StorageEntity{Dir: dir}
name := "test"
if err := storage.Save(name, []byte("test content")); err != nil {
t.Fatalf("failed to save content: %v", err)

View File

@@ -1,4 +1,4 @@
package common
package plugins
import (
"bytes"
@@ -9,41 +9,58 @@ import (
const AnswerReset = "reset"
type Configurable struct {
type Plugin interface {
GetName() string
GetSetupDescription() string
IsConfigured() bool
Configure() error
Setup() error
SetupFillEnvFileContent(*bytes.Buffer)
}
type PluginBase struct {
Settings
SetupQuestions
Label string
EnvNamePrefix string
Name string
SetupDescription string
EnvNamePrefix string
ConfigureCustom func() error
}
func (o *Configurable) GetName() string {
return o.Label
func (o *PluginBase) GetName() string {
return o.Name
}
func (o *Configurable) AddSetting(name string, required bool) (ret *Setting) {
func (o *PluginBase) GetSetupDescription() (ret string) {
if ret = o.SetupDescription; ret == "" {
ret = o.GetName()
}
return
}
func (o *PluginBase) AddSetting(name string, required bool) (ret *Setting) {
ret = NewSetting(fmt.Sprintf("%v%v", o.EnvNamePrefix, BuildEnvVariable(name)), required)
o.Settings = append(o.Settings, ret)
return
}
func (o *Configurable) AddSetupQuestion(name string, required bool) (ret *SetupQuestion) {
func (o *PluginBase) AddSetupQuestion(name string, required bool) (ret *SetupQuestion) {
return o.AddSetupQuestionCustom(name, required, "")
}
func (o *Configurable) AddSetupQuestionCustom(name string, required bool, question string) (ret *SetupQuestion) {
func (o *PluginBase) AddSetupQuestionCustom(name string, required bool, question string) (ret *SetupQuestion) {
setting := o.AddSetting(name, required)
ret = &SetupQuestion{Setting: setting, Question: question}
if ret.Question == "" {
ret.Question = fmt.Sprintf("Enter your %v %v", o.Label, strings.ToUpper(name))
ret.Question = fmt.Sprintf("Enter your %v %v", o.Name, strings.ToUpper(name))
}
o.SetupQuestions = append(o.SetupQuestions, ret)
return
}
func (o *Configurable) Configure() (err error) {
func (o *PluginBase) Configure() (err error) {
if err = o.Settings.Configure(); err != nil {
return
}
@@ -54,8 +71,8 @@ func (o *Configurable) Configure() (err error) {
return
}
func (o *Configurable) Setup() (err error) {
if err = o.Ask(o.Label); err != nil {
func (o *PluginBase) Setup() (err error) {
if err = o.Ask(o.Name); err != nil {
return
}
@@ -63,14 +80,14 @@ func (o *Configurable) Setup() (err error) {
return
}
func (o *Configurable) SetupOrSkip() (err error) {
func (o *PluginBase) SetupOrSkip() (err error) {
if err = o.Setup(); err != nil {
fmt.Printf("[%v] skipped\n", o.GetName())
}
return
}
func (o *Configurable) SetupFillEnvFileContent(fileEnvFileContent *bytes.Buffer) {
func (o *PluginBase) SetupFillEnvFileContent(fileEnvFileContent *bytes.Buffer) {
o.Settings.FillEnvFileContent(fileEnvFileContent)
}
@@ -126,6 +143,10 @@ func (o *Setting) Print() {
fmt.Printf("%v: %v\n", o.EnvVariable, o.Value)
}
func NewSetupQuestion(question string) *SetupQuestion {
return &SetupQuestion{Setting: &Setting{}, Question: question}
}
type SetupQuestion struct {
*Setting
Question string
@@ -162,6 +183,11 @@ func (o *SetupQuestion) Ask(label string) (err error) {
func (o *SetupQuestion) OnAnswer(answer string) (err error) {
o.Value = answer
if o.EnvVariable != "" {
if err = os.Setenv(o.EnvVariable, answer); err != nil {
return
}
}
err = o.IsValidErr()
return
}

View File

@@ -1,4 +1,4 @@
package common
package plugins
import (
"bytes"
@@ -9,9 +9,9 @@ import (
)
func TestConfigurable_AddSetting(t *testing.T) {
conf := &Configurable{
conf := &PluginBase{
Settings: Settings{},
Label: "TestConfigurable",
Name: "TestConfigurable",
EnvNamePrefix: "TEST_",
}
@@ -26,9 +26,9 @@ func TestConfigurable_Configure(t *testing.T) {
EnvVariable: "TEST_SETTING",
Required: true,
}
conf := &Configurable{
conf := &PluginBase{
Settings: Settings{setting},
Label: "TestConfigurable",
Name: "TestConfigurable",
}
_ = os.Setenv("TEST_SETTING", "test_value")
@@ -42,9 +42,9 @@ func TestConfigurable_Setup(t *testing.T) {
EnvVariable: "TEST_SETTING",
Required: false,
}
conf := &Configurable{
conf := &PluginBase{
Settings: Settings{setting},
Label: "TestConfigurable",
Name: "TestConfigurable",
}
err := conf.Setup()

71
plugins/tools/defaults.go Normal file
View File

@@ -0,0 +1,71 @@
package tools
import (
"fmt"
"strconv"
"github.com/danielmiessler/fabric/plugins"
"github.com/danielmiessler/fabric/plugins/ai"
"github.com/pkg/errors"
)
func NeeDefaults(getVendorsModels func() (*ai.VendorsModels, error)) (ret *Defaults) {
vendorName := "Default"
ret = &Defaults{
PluginBase: &plugins.PluginBase{
Name: vendorName,
SetupDescription: "Default AI Vendor and Model [required]",
EnvNamePrefix: plugins.BuildEnvVariablePrefix(vendorName),
},
GetVendorsModels: getVendorsModels,
}
ret.Vendor = ret.AddSetting("Vendor", true)
ret.Model = ret.AddSetupQuestionCustom("Model", true,
"Enter the index the name of your default model")
return
}
type Defaults struct {
*plugins.PluginBase
Vendor *plugins.Setting
Model *plugins.SetupQuestion
GetVendorsModels func() (*ai.VendorsModels, error)
}
func (o *Defaults) Setup() (err error) {
var vendorsModels *ai.VendorsModels
if vendorsModels, err = o.GetVendorsModels(); err != nil {
return
}
vendorsModels.Print()
if err = o.Ask(o.Name); err != nil {
return
}
index, parseErr := strconv.Atoi(o.Model.Value)
if parseErr == nil {
if o.Vendor.Value, o.Model.Value, err = vendorsModels.GetGroupAndItemByItemNumber(index); err != nil {
return
}
} else {
o.Vendor.Value = vendorsModels.FindGroupsByItemFirst(o.Model.Value)
}
//verify
vendorNames := vendorsModels.FindGroupsByItem(o.Model.Value)
if len(vendorNames) == 0 {
err = errors.Errorf("You need to chose an available default model.")
return
}
fmt.Println()
o.Vendor.Print()
o.Model.Print()
return
}

View File

@@ -7,12 +7,12 @@ import (
"io"
"net/http"
"github.com/danielmiessler/fabric/common"
"github.com/danielmiessler/fabric/plugins"
)
type Client struct {
*common.Configurable
ApiKey *common.SetupQuestion
*plugins.PluginBase
ApiKey *plugins.SetupQuestion
}
func NewClient() (ret *Client) {
@@ -20,9 +20,10 @@ func NewClient() (ret *Client) {
label := "Jina AI"
ret = &Client{
Configurable: &common.Configurable{
Label: label,
EnvNamePrefix: common.BuildEnvVariablePrefix(label),
PluginBase: &plugins.PluginBase{
Name: label,
SetupDescription: "Jina AI Service - to grab a webpage as clean, LLM-friendly text",
EnvNamePrefix: plugins.BuildEnvVariablePrefix(label),
},
}

View File

@@ -0,0 +1,42 @@
package lang
import (
"github.com/danielmiessler/fabric/plugins"
"golang.org/x/text/language"
)
func NewLanguage() (ret *Language) {
label := "Language"
ret = &Language{}
ret.PluginBase = &plugins.PluginBase{
Name: label,
SetupDescription: "Language - Default AI Vendor Output Language",
EnvNamePrefix: plugins.BuildEnvVariablePrefix(label),
ConfigureCustom: ret.configure,
}
ret.DefaultLanguage = ret.AddSetupQuestionCustom("Output", false,
"Enter your default output language (for example: zh_CN)")
return
}
type Language struct {
*plugins.PluginBase
DefaultLanguage *plugins.SetupQuestion
}
func (o *Language) configure() error {
if o.DefaultLanguage.Value != "" {
langTag, err := language.Parse(o.DefaultLanguage.Value)
if err == nil {
o.DefaultLanguage.Value = langTag.String()
} else {
o.DefaultLanguage.Value = ""
}
}
return nil
}

View File

@@ -1,4 +1,4 @@
package core
package tools
import (
"fmt"
@@ -8,8 +8,9 @@ import (
"sort"
"strings"
"github.com/danielmiessler/fabric/common"
"github.com/danielmiessler/fabric/db"
"github.com/danielmiessler/fabric/plugins"
"github.com/danielmiessler/fabric/plugins/db/fsdb"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
@@ -17,16 +18,21 @@ import (
"github.com/otiai10/copy"
)
func NewPatternsLoader(patterns *db.Patterns) (ret *PatternsLoader) {
const DefaultPatternsGitRepoUrl = "https://github.com/danielmiessler/fabric.git"
const DefaultPatternsGitRepoFolder = "patterns"
func NewPatternsLoader(patterns *fsdb.PatternsEntity) (ret *PatternsLoader) {
label := "Patterns Loader"
ret = &PatternsLoader{
Patterns: patterns,
Patterns: patterns,
loadedFilePath: patterns.BuildFilePath("loaded"),
}
ret.Configurable = &common.Configurable{
Label: label,
EnvNamePrefix: common.BuildEnvVariablePrefix(label),
ConfigureCustom: ret.configure,
ret.PluginBase = &plugins.PluginBase{
Name: label,
SetupDescription: "Patterns - Downloads patterns [required]",
EnvNamePrefix: plugins.BuildEnvVariablePrefix(label),
ConfigureCustom: ret.configure,
}
ret.DefaultGitRepoUrl = ret.AddSetupQuestionCustom("Git Repo Url", true,
@@ -41,11 +47,13 @@ func NewPatternsLoader(patterns *db.Patterns) (ret *PatternsLoader) {
}
type PatternsLoader struct {
*common.Configurable
Patterns *db.Patterns
*plugins.PluginBase
Patterns *fsdb.PatternsEntity
DefaultGitRepoUrl *common.SetupQuestion
DefaultFolder *common.SetupQuestion
DefaultGitRepoUrl *plugins.SetupQuestion
DefaultFolder *plugins.SetupQuestion
loadedFilePath string
pathPatternsPrefix string
tempPatternsFolder string
@@ -58,6 +66,27 @@ func (o *PatternsLoader) configure() (err error) {
return
}
func (o *PatternsLoader) IsConfigured() (ret bool) {
ret = o.PluginBase.IsConfigured()
if ret {
if _, err := os.Stat(o.loadedFilePath); os.IsNotExist(err) {
ret = false
}
}
return
}
func (o *PatternsLoader) Setup() (err error) {
if err = o.PluginBase.Setup(); err != nil {
return
}
if err = o.PopulateDB(); err != nil {
return
}
return
}
// PopulateDB downloads patterns from the internet and populates the patterns folder
func (o *PatternsLoader) PopulateDB() (err error) {
fmt.Printf("Downloading patterns and Populating %s..\n", o.Patterns.Dir)
@@ -90,7 +119,7 @@ func (o *PatternsLoader) PersistPatterns() (err error) {
if currentPattern.Name() == newPattern.Name() {
break
}
copy.Copy(filepath.Join(o.Patterns.Dir, newPattern.Name()), filepath.Join(newPatternsFolder, newPattern.Name()))
err = copy.Copy(filepath.Join(o.Patterns.Dir, newPattern.Name()), filepath.Join(newPatternsFolder, newPattern.Name()))
}
}
return
@@ -98,30 +127,26 @@ func (o *PatternsLoader) PersistPatterns() (err error) {
// movePatterns copies the new patterns into the config directory
func (o *PatternsLoader) movePatterns() (err error) {
os.MkdirAll(o.Patterns.Dir, os.ModePerm)
if err = os.MkdirAll(o.Patterns.Dir, os.ModePerm); err != nil {
return
}
patternsDir := o.tempPatternsFolder
if err = o.PersistPatterns(); err != nil {
return
}
copy.Copy(patternsDir, o.Patterns.Dir) // copies the patterns to the config directory
if err = copy.Copy(patternsDir, o.Patterns.Dir); err != nil { // copies the patterns to the config directory
return
}
//create an empty file to indicate that the patterns have been updated if not exists
_, _ = os.Create(o.loadedFilePath)
err = os.RemoveAll(patternsDir)
return
}
// checks if a pattern already exists in the directory
// func DoesPatternExistAlready(name string) (bool, error) {
// entry := db.Entry{
// Label: name,
// }
// _, err := entry.GetByName()
// if err != nil {
// return false, err
// }
// return true, nil
// }
func (o *PatternsLoader) gitCloneAndCopy() (err error) {
// Clones the given repository, creating the remote, the local branches
// and fetching the objects, everything in memory:
@@ -152,10 +177,10 @@ func (o *PatternsLoader) gitCloneAndCopy() (err error) {
return err
}
var changes []db.DirectoryChange
var changes []fsdb.DirectoryChange
// ... iterates over the commits
if err = cIter.ForEach(func(c *object.Commit) (err error) {
// Get the files changed in this commit by comparing with its parents
// GetApplyVariables the files changed in this commit by comparing with its parents
parentIter := c.Parents()
if err = parentIter.ForEach(func(parent *object.Commit) (err error) {
var patch *object.Patch
@@ -167,7 +192,7 @@ func (o *PatternsLoader) gitCloneAndCopy() (err error) {
for _, fileStat := range patch.Stats() {
if strings.HasPrefix(fileStat.Name, o.pathPatternsPrefix) {
dir := filepath.Dir(fileStat.Name)
changes = append(changes, db.DirectoryChange{Dir: dir, Timestamp: c.Committer.When})
changes = append(changes, fsdb.DirectoryChange{Dir: dir, Timestamp: c.Committer.When})
}
}
return
@@ -186,7 +211,9 @@ func (o *PatternsLoader) gitCloneAndCopy() (err error) {
return changes[i].Timestamp.Before(changes[j].Timestamp)
})
o.makeUniqueList(changes)
if err = o.makeUniqueList(changes); err != nil {
return
}
var commit *object.Commit
if commit, err = r.CommitObject(ref.Hash()); err != nil {
@@ -250,7 +277,7 @@ func (o *PatternsLoader) writeBlobToFile(blob *object.Blob, path string) (err er
return
}
func (o *PatternsLoader) makeUniqueList(changes []db.DirectoryChange) {
func (o *PatternsLoader) makeUniqueList(changes []fsdb.DirectoryChange) (err error) {
uniqueItems := make(map[string]bool)
for _, change := range changes {
if strings.TrimSpace(change.Dir) != "" && !strings.Contains(change.Dir, "=>") {
@@ -271,5 +298,6 @@ func (o *PatternsLoader) makeUniqueList(changes []db.DirectoryChange) {
}
joined := strings.Join(finalList, "\n")
os.WriteFile(o.Patterns.UniquePatternsFilePath, []byte(joined), 0o644)
err = os.WriteFile(o.Patterns.UniquePatternsFilePath, []byte(joined), 0o644)
return
}

View File

@@ -5,15 +5,16 @@ import (
"encoding/json"
"flag"
"fmt"
"github.com/anaskhan96/soup"
"github.com/danielmiessler/fabric/common"
"google.golang.org/api/option"
"google.golang.org/api/youtube/v3"
"log"
"net/url"
"regexp"
"strconv"
"strings"
"github.com/anaskhan96/soup"
"github.com/danielmiessler/fabric/plugins"
"google.golang.org/api/option"
"google.golang.org/api/youtube/v3"
)
func NewYouTube() (ret *YouTube) {
@@ -21,9 +22,10 @@ func NewYouTube() (ret *YouTube) {
label := "YouTube"
ret = &YouTube{}
ret.Configurable = &common.Configurable{
Label: label,
EnvNamePrefix: common.BuildEnvVariablePrefix(label),
ret.PluginBase = &plugins.PluginBase{
Name: label,
SetupDescription: label + " - to grab video transcripts and comments",
EnvNamePrefix: plugins.BuildEnvVariablePrefix(label),
}
ret.ApiKey = ret.AddSetupQuestion("API key", true)
@@ -32,8 +34,8 @@ func NewYouTube() (ret *YouTube) {
}
type YouTube struct {
*common.Configurable
ApiKey *common.SetupQuestion
*plugins.PluginBase
ApiKey *plugins.SetupQuestion
service *youtube.Service
}

19
restapi/contexts.go Normal file
View File

@@ -0,0 +1,19 @@
package restapi
import (
"github.com/danielmiessler/fabric/plugins/db/fsdb"
"github.com/gin-gonic/gin"
)
// ContextsHandler defines the handler for contexts-related operations
type ContextsHandler struct {
*StorageHandler[fsdb.Context]
contexts *fsdb.ContextsEntity
}
// NewContextsHandler creates a new ContextsHandler
func NewContextsHandler(r *gin.Engine, contexts *fsdb.ContextsEntity) (ret *ContextsHandler) {
ret = &ContextsHandler{
StorageHandler: NewStorageHandler[fsdb.Context](r, "contexts", contexts), contexts: contexts}
return
}

35
restapi/patterns.go Normal file
View File

@@ -0,0 +1,35 @@
package restapi
import (
"github.com/danielmiessler/fabric/plugins/db/fsdb"
"github.com/gin-gonic/gin"
"net/http"
)
// PatternsHandler defines the handler for patterns-related operations
type PatternsHandler struct {
*StorageHandler[fsdb.Pattern]
patterns *fsdb.PatternsEntity
}
// NewPatternsHandler creates a new PatternsHandler
func NewPatternsHandler(r *gin.Engine, patterns *fsdb.PatternsEntity) (ret *PatternsHandler) {
ret = &PatternsHandler{
StorageHandler: NewStorageHandler[fsdb.Pattern](r, "patterns", patterns), patterns: patterns}
// TODO: Add custom, replacement routes here
//r.GET("/patterns/:name", ret.Get)
return
}
// Get handles the GET /patterns/:name route
func (h *PatternsHandler) Get(c *gin.Context) {
name := c.Param("name")
variables := make(map[string]string) // Assuming variables are passed somehow
pattern, err := h.patterns.GetApplyVariables(name, variables)
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, pattern)
}

28
restapi/serve.go Normal file
View File

@@ -0,0 +1,28 @@
package restapi
import (
"github.com/danielmiessler/fabric/core"
"github.com/gin-gonic/gin"
)
func Serve(registry *core.PluginRegistry, address string) (err error) {
r := gin.Default()
// Middleware
r.Use(gin.Logger())
r.Use(gin.Recovery())
// Register routes
fabricDb := registry.Db
NewPatternsHandler(r, fabricDb.Patterns)
NewContextsHandler(r, fabricDb.Contexts)
NewSessionsHandler(r, fabricDb.Sessions)
// Start server
err = r.Run(address)
if err != nil {
return err
}
return
}

19
restapi/sessions.go Normal file
View File

@@ -0,0 +1,19 @@
package restapi
import (
"github.com/danielmiessler/fabric/plugins/db/fsdb"
"github.com/gin-gonic/gin"
)
// SessionsHandler defines the handler for sessions-related operations
type SessionsHandler struct {
*StorageHandler[fsdb.Session]
sessions *fsdb.SessionsEntity
}
// NewSessionsHandler creates a new SessionsHandler
func NewSessionsHandler(r *gin.Engine, sessions *fsdb.SessionsEntity) (ret *SessionsHandler) {
ret = &SessionsHandler{
StorageHandler: NewStorageHandler[fsdb.Session](r, "sessions", sessions), sessions: sessions}
return ret
}

100
restapi/storage.go Normal file
View File

@@ -0,0 +1,100 @@
package restapi
import (
"fmt"
"github.com/danielmiessler/fabric/plugins/db"
"github.com/gin-gonic/gin"
"io"
"net/http"
)
// StorageHandler defines the handler for storage-related operations
type StorageHandler[T any] struct {
storage db.Storage[T]
}
// NewStorageHandler creates a new StorageHandler
func NewStorageHandler[T any](r *gin.Engine, entityType string, storage db.Storage[T]) (ret *StorageHandler[T]) {
ret = &StorageHandler[T]{storage: storage}
r.GET(fmt.Sprintf("/%s/:name", entityType), ret.Get)
r.GET(fmt.Sprintf("/%s/names", entityType), ret.GetNames)
r.DELETE(fmt.Sprintf("/%s/:name", entityType), ret.Delete)
r.GET(fmt.Sprintf("/%s/exists/:name", entityType), ret.Exists)
r.PUT(fmt.Sprintf("/%s/rename/:oldName/:newName", entityType), ret.Rename)
r.POST(fmt.Sprintf("/%s/:name", entityType), ret.Save)
return
}
// Get handles the GET /storage/:name route
func (h *StorageHandler[T]) Get(c *gin.Context) {
name := c.Param("name")
item, err := h.storage.Get(name)
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, item)
}
// GetNames handles the GET /storage/names route
func (h *StorageHandler[T]) GetNames(c *gin.Context) {
names, err := h.storage.GetNames()
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
return
}
c.JSON(http.StatusOK, names)
}
// Delete handles the DELETE /storage/:name route
func (h *StorageHandler[T]) Delete(c *gin.Context) {
name := c.Param("name")
err := h.storage.Delete(name)
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
return
}
c.Status(http.StatusOK)
}
// Exists handles the GET /storage/exists/:name route
func (h *StorageHandler[T]) Exists(c *gin.Context) {
name := c.Param("name")
exists := h.storage.Exists(name)
c.JSON(http.StatusOK, exists)
}
// Rename handles the PUT /storage/rename/:oldName/:newName route
func (h *StorageHandler[T]) Rename(c *gin.Context) {
oldName := c.Param("oldName")
newName := c.Param("newName")
err := h.storage.Rename(oldName, newName)
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
return
}
c.Status(http.StatusOK)
}
// Save handles the POST /storage/save/:name route
func (h *StorageHandler[T]) Save(c *gin.Context) {
name := c.Param("name")
// Read the request body
body := c.Request.Body
defer body.Close()
content, err := io.ReadAll(body)
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
return
}
// Save the content to storage
err = h.storage.Save(name, content)
if err != nil {
c.JSON(http.StatusInternalServerError, err.Error())
return
}
c.Status(http.StatusOK)
}

112
setup_fabric.bat Normal file
View File

@@ -0,0 +1,112 @@
@echo off
setlocal enabledelayedexpansion
:: Check if running with administrator privileges
net session >nul 2>&1
if %errorlevel% neq 0 (
echo Please run this script as an administrator.
pause
exit /b 1
)
:: Install Chocolatey (package manager for Windows)
if not exist "%ProgramData%\chocolatey\bin\choco.exe" (
echo Installing Chocolatey...
@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "[System.Net.ServicePointManager]::SecurityProtocol = 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
)
:: Install Go
where go >nul 2>&1
if %errorlevel% neq 0 (
echo Installing Go...
choco install golang -y
set "PATH=%PATH%;C:\Program Files\Go\bin"
)
:: Install Git
where git >nul 2>&1
if %errorlevel% neq 0 (
echo Installing Git...
choco install git -y
)
:: Refresh environment variables
call refreshenv
:: Install Fabric
echo Installing Fabric...
go install github.com/danielmiessler/fabric@latest
:: Run Fabric setup
echo Running Fabric setup...
fabric --setup
:: Install yt helper
echo Installing yt helper...
go install github.com/danielmiessler/yt@latest
:: Prompt user for YouTube API Key
set /p YOUTUBE_API_KEY=Enter your YouTube API Key (press Enter to skip):
if not "!YOUTUBE_API_KEY!"=="" (
echo YOUTUBE_API_KEY=!YOUTUBE_API_KEY!>> %USERPROFILE%\.config\fabric\.env
)
:: Prompt user for OpenAI API Key
set /p OPENAI_API_KEY=Enter your OpenAI API Key (press Enter to skip):
if not "!OPENAI_API_KEY!"=="" (
echo OPENAI_API_KEY=!OPENAI_API_KEY!>> %USERPROFILE%\.config\fabric\.env
)
:: Run Fabric
:run_fabric
cls
echo Fabric is now installed and ready to use.
echo.
echo Available options:
echo 1. Run Fabric with custom options
echo 2. List patterns
echo 3. List models
echo 4. Update patterns
echo 5. Exit
echo.
set /p CHOICE=Enter your choice (1-5):
if "%CHOICE%"=="1" (
set /p PATTERN=Enter pattern (or press Enter to skip):
set /p CONTEXT=Enter context (or press Enter to skip):
set /p SESSION=Enter session (or press Enter to skip):
set /p MODEL=Enter model (or press Enter to skip):
set /p TEMPERATURE=Enter temperature (or press Enter for default):
set /p STREAM=Do you want to stream output? (Y/N):
set "FABRIC_CMD=fabric"
if not "!PATTERN!"=="" set "FABRIC_CMD=!FABRIC_CMD! --pattern !PATTERN!"
if not "!CONTEXT!"=="" set "FABRIC_CMD=!FABRIC_CMD! --context !CONTEXT!"
if not "!SESSION!"=="" set "FABRIC_CMD=!FABRIC_CMD! --session !SESSION!"
if not "!MODEL!"=="" set "FABRIC_CMD=!FABRIC_CMD! --model !MODEL!"
if not "!TEMPERATURE!"=="" set "FABRIC_CMD=!FABRIC_CMD! --temperature !TEMPERATURE!"
if /i "!STREAM!"=="Y" set "FABRIC_CMD=!FABRIC_CMD! --stream"
echo Running Fabric with command: !FABRIC_CMD!
!FABRIC_CMD!
pause
goto run_fabric
) else if "%CHOICE%"=="2" (
fabric --listpatterns
pause
goto run_fabric
) else if "%CHOICE%"=="3" (
fabric --listmodels
pause
goto run_fabric
) else if "%CHOICE%"=="4" (
fabric --updatepatterns
pause
goto run_fabric
) else if "%CHOICE%"=="5" (
exit /b 0
) else (
echo Invalid choice. Please try again.
pause
goto run_fabric
)

View File

@@ -1,3 +1,3 @@
package main
var version = "v1.4.59"
var version = "v1.4.68"