Compare commits

...

25 Commits

Author SHA1 Message Date
github-actions[bot]
6ca1b5dac4 Update version to v1.4.181 and commit 2025-04-22 16:03:07 +00:00
Eugen Eisler
c85135c04e Merge pull request #1433 from ksylvan/0421-anthropic-api-update
chore: update Anthropic SDK to v0.2.0-beta.3 and migrate to V2 API
2025-04-22 18:01:53 +02:00
github-actions[bot]
31e4e42a94 Update version to v1.4.180 and commit 2025-04-22 11:40:37 +00:00
Eugen Eisler
196db04fc2 Merge pull request #1435 from ksylvan/0421-fix-raw-input-with-stratetgies
chore: Fix user input handling when using raw mode and `--strategy` flag
2025-04-22 13:39:22 +02:00
Kayvan Sylvan
b3b1b5a471 chore: unify raw mode message handling and preserve env vars in extension executor
## CHANGES

- refactor BuildSession raw mode to prepend system to user content
- ensure raw mode messages always have User role
- keep existing user message when no systemMessage provided
- append systemMessage separately in non-raw mode sessions
- store original cmd.Env before context-based exec command creation
- recreate exec command with context then restore originalEnv
- add comments clarifying raw vs non-raw handling behavior
2025-04-21 17:04:11 -07:00
Kayvan Sylvan
892439a177 chore: update Anthropic SDK to v0.2.0-beta.3 and migrate to V2 API
## CHANGES

- Upgrade Anthropic SDK from alpha.11 to beta.3
- Update API endpoint from v1 to v2
- Replace anthropic.F() with direct assignment
- Replace anthropic.F() with anthropic.Opt() for optional params
- Simplify event delta handling in streaming
- Change client type from pointer to value type
- Update comment with SDK changelog reference
2025-04-21 13:17:03 -07:00
github-actions[bot]
ba2e178e03 Update version to v1.4.179 and commit 2025-04-21 18:08:47 +00:00
Eugen Eisler
ed298bcedd Merge pull request #1432 from ksylvan/0421-fix-tools-selection-in-setup
chore: fix fabric setup mess-up introduced by sorting lists (tools and models)
2025-04-21 20:07:33 +02:00
Kayvan Sylvan
6b04e6e674 chore: sort AI models alphabetically for consistent listing
CHANGES
*   Import `sort` and `strings` packages for sorting functionality.
*   Sort retrieved AI model names alphabetically, ignoring case.
*   Ensure consistent ordering of AI models in lists.
2025-04-21 10:41:41 -07:00
Kayvan Sylvan
04c0f6a0a5 chore: alphabetize the order of plugin tools 2025-04-21 10:26:04 -07:00
github-actions[bot]
f7ab484510 Update version to v1.4.178 and commit 2025-04-21 13:21:52 +00:00
Eugen Eisler
f50a14305a Merge pull request #1427 from ksylvan/0420-refactor-openai-compatible-providers
Refactor OpenAI-compatible AI providers and add `--listvendors` flag
2025-04-21 15:20:33 +02:00
github-actions[bot]
d5f0cd7616 Update version to v1.4.177 and commit 2025-04-21 07:10:40 +00:00
Eugen Eisler
67c658f5b4 Merge pull request #1428 from ksylvan/0420-sorted-group-lists
feat: Alphabetical case-insensitive sorting for groups and items
2025-04-21 09:09:21 +02:00
github-actions[bot]
ef3bc03343 Update version to v1.4.176 and commit 2025-04-21 07:09:11 +00:00
Eugen Eisler
e31cb2b46a Merge pull request #1429 from ksylvan/0420-fix-strategies-api
feat: enhance StrategyMeta with Prompt field and dynamic naming
2025-04-21 09:07:57 +02:00
Kayvan Sylvan
6ca7142ea4 feat: enhance StrategyMeta with Prompt field and dynamic naming
### CHANGES

- Add `Prompt` field to `StrategyMeta` struct.
- Include `strings` package for filename processing.
- Derive strategy name from filename using `strings.TrimSuffix`.
- Store `Prompt` value from JSON data in `StrategyMeta`
2025-04-20 17:20:55 -07:00
Kayvan Sylvan
8b2174897a feat: add alphabetical sorting to groups and items in Print method**
### CHANGES
- Import `sort` and `strings` packages for sorting functionality.
- Create a copy of groups for stable sorting.
- Sort groups alphabetically in a case-insensitive manner.
- Create a copy of items within each group for sorting.
- Sort items alphabetically in a case-insensitive manner.
- Iterate over sorted groups and items for display.
2025-04-20 10:56:52 -07:00
Kayvan Sylvan
ac5eab0563 feat: add --listvendors command to list AI vendors
### CHANGES
- Introduce `--listvendors` flag to display all AI vendors.
- Refactor OpenAI-compatible providers into a unified configuration.
- Remove individual vendor packages for streamlined management.
- Add sorting for consistent vendor listing output.
- Update documentation to include new `--listvendors` option.
2025-04-20 08:53:20 -07:00
github-actions[bot]
65414dcc1c Update version to v1.4.175 and commit 2025-04-19 20:57:20 +00:00
Eugen Eisler
5db352f5be Merge pull request #1418 from danielmiessler/dependabot/go_modules/go_modules-bbb8b02913
chore(deps): bump golang.org/x/net from 0.36.0 to 0.38.0 in the go_modules group across 1 directory
2025-04-19 22:56:00 +02:00
github-actions[bot]
9e7830ff77 Update version to v1.4.174 and commit 2025-04-19 06:13:44 +00:00
Eugen Eisler
5945c0e16b Merge pull request #1425 from ksylvan/0418-cerebras-ai
feat: add Cerebras AI plugin to plugin registry
2025-04-19 08:12:32 +02:00
Kayvan Sylvan
29ee141822 feat: add Cerebras AI plugin to plugin registry
### CHANGES
- Introduce Cerebras AI plugin import in plugin registry.
- Register Cerebras client in the NewPluginRegistry function.
2025-04-18 15:14:08 -07:00
dependabot[bot]
8c84d4b3c8 chore(deps): bump golang.org/x/net
Bumps the go_modules group with 1 update in the / directory: [golang.org/x/net](https://github.com/golang/net).


Updates `golang.org/x/net` from 0.36.0 to 0.38.0
- [Commits](https://github.com/golang/net/compare/v0.36.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
  dependency-group: go_modules
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-04-16 23:25:39 +00:00
29 changed files with 272 additions and 236 deletions

View File

@@ -76,6 +76,7 @@ Fabric is graciously supported by…
- [Clipboard Support](#clipboard-support)
- [Meta](#meta)
- [Primary contributors](#primary-contributors)
- [Contributors](#contributors)
<br />
@@ -475,6 +476,7 @@ Application Options:
--rmextension= Remove a registered extension by name
--strategy= Choose a strategy from the available strategies
--liststrategies List all strategies
--listvendors List all vendors
Help Options:
-h, --help Show this help message

View File

@@ -1,20 +1,25 @@
# YAML Configuration Support
## Overview
Fabric now supports YAML configuration files for commonly used options. This allows users to persist settings and share configurations across multiple runs.
## Usage
Use the `--config` flag to specify a YAML configuration file:
```bash
fabric --config ~/.config/fabric/config.yaml "Tell me about APIs"
```
## Configuration Precedence
1. CLI flags (highest priority)
2. YAML config values
3. Default values (lowest priority)
## Supported Configuration Options
```yaml
# Model selection
model: gpt-4
@@ -36,6 +41,7 @@ raw: false
```
## Rules and Behavior
- Only long flag names are supported in YAML (e.g., `temperature` not `-t`)
- CLI flags always override YAML values
- Unknown YAML declarations are ignored
@@ -43,12 +49,15 @@ raw: false
- The order of YAML declarations doesn't matter
## Type Conversions
The following string-to-type conversions are supported:
- String to number: `"42"``42`
- String to float: `"42.5"``42.5`
- String to boolean: `"true"``true`
## Example Config
```yaml
# ~/.config/fabric/config.yaml
model: gpt-4
@@ -61,8 +70,8 @@ frequencypenalty: 0.2
```
## CLI Override Example
```bash
# Override temperature from config
fabric --config ~/.config/fabric/config.yaml --temperature 0.9 "Query"
```

View File

@@ -164,6 +164,11 @@ func Cli(version string) (err error) {
return
}
if currentFlags.ListVendors {
err = registry.ListVendors(os.Stdout)
return
}
// if the interactive flag is set, run the interactive function
// if currentFlags.Interactive {
// interactive.Interactive()
@@ -211,7 +216,9 @@ func Cli(version string) (err error) {
return
}
messageTools, err = processYoutubeVideo(currentFlags, registry, videoId)
if messageTools, err = processYoutubeVideo(currentFlags, registry, videoId); err != nil {
return
}
if !currentFlags.IsChatRequest() {
err = currentFlags.WriteOutput(messageTools)
return

View File

@@ -72,6 +72,7 @@ type Flags struct {
RemoveExtension string `long:"rmextension" description:"Remove a registered extension by name"`
Strategy string `long:"strategy" description:"Choose a strategy from the available strategies" default:""`
ListStrategies bool `long:"liststrategies" description:"List all strategies"`
ListVendors bool `long:"listvendors" description:"List all vendors"`
}
var debug = false
@@ -334,7 +335,6 @@ func (o *Flags) BuildChatRequest(Meta string) (ret *common.ChatRequest, err erro
func (o *Flags) AppendMessage(message string) {
o.Message = AppendMessage(o.Message, message)
return
}
func (o *Flags) IsChatRequest() (ret bool) {

View File

@@ -2,6 +2,8 @@ package common
import (
"fmt"
"sort"
"strings"
"github.com/samber/lo"
)
@@ -71,15 +73,31 @@ func (o *GroupsItemsSelector[I]) Print() {
fmt.Printf("\n%v:\n", o.SelectionLabel)
var currentItemIndex int
for _, groupItems := range o.GroupsItems {
// Create a copy of groups to sort
sortedGroupsItems := make([]*GroupItems[I], len(o.GroupsItems))
copy(sortedGroupsItems, o.GroupsItems)
// Sort groups alphabetically case-insensitive
sort.SliceStable(sortedGroupsItems, func(i, j int) bool {
return strings.ToLower(sortedGroupsItems[i].Group) < strings.ToLower(sortedGroupsItems[j].Group)
})
for _, groupItems := range sortedGroupsItems {
fmt.Println()
fmt.Printf("%s\n", groupItems.Group)
fmt.Println()
for _, item := range groupItems.Items {
// Create a copy of items to sort
sortedItems := make([]I, len(groupItems.Items))
copy(sortedItems, groupItems.Items)
// Sort items alphabetically case-insensitive
sort.SliceStable(sortedItems, func(i, j int) bool {
return strings.ToLower(o.GetItemKey(sortedItems[i])) < strings.ToLower(o.GetItemKey(sortedItems[j]))
})
for _, item := range sortedItems {
currentItemIndex++
fmt.Printf("\t[%d]\t%s\n", currentItemIndex, o.GetItemKey(item))
}
}
}

View File

@@ -192,17 +192,19 @@ func (o *Chatter) BuildSession(request *common.ChatRequest, raw bool) (session *
}
if raw {
if request.Message != nil {
if systemMessage != "" {
request.Message.Content = systemMessage
// system contains pattern which contains user input
// In raw mode, combine system message (potentially with strategy) and user message into a single user message
if systemMessage != "" {
if request.Message != nil {
// Prepend system message to user content, ensuring user input is preserved
request.Message.Content = fmt.Sprintf("%s\n\n%s", systemMessage, request.Message.Content)
request.Message.Role = goopenai.ChatMessageRoleUser // Ensure role is User in raw mode
} else {
// If no user message, create one with the system content, marked as User role
request.Message = &goopenai.ChatCompletionMessage{Role: goopenai.ChatMessageRoleUser, Content: systemMessage}
}
} else {
if systemMessage != "" {
request.Message = &goopenai.ChatCompletionMessage{Role: goopenai.ChatMessageRoleSystem, Content: systemMessage}
}
}
} // else: no system message, user message (if any) remains unchanged
} else {
// Not raw mode, append system message separately if it exists
if systemMessage != "" {
session.Append(&goopenai.ChatCompletionMessage{Role: goopenai.ChatMessageRoleSystem, Content: systemMessage})
}

View File

@@ -3,12 +3,14 @@ package core
import (
"bytes"
"fmt"
"io"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
"github.com/danielmiessler/fabric/plugins/ai/exolab"
"github.com/danielmiessler/fabric/plugins/ai/grokai"
"github.com/danielmiessler/fabric/plugins/strategy"
"github.com/samber/lo"
@@ -18,17 +20,12 @@ import (
"github.com/danielmiessler/fabric/plugins/ai"
"github.com/danielmiessler/fabric/plugins/ai/anthropic"
"github.com/danielmiessler/fabric/plugins/ai/azure"
"github.com/danielmiessler/fabric/plugins/ai/deepseek"
"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/litellm"
"github.com/danielmiessler/fabric/plugins/ai/lmstudio"
"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/ai/openai_compatible"
"github.com/danielmiessler/fabric/plugins/db/fsdb"
"github.com/danielmiessler/fabric/plugins/template"
"github.com/danielmiessler/fabric/plugins/tools"
@@ -57,28 +54,49 @@ func NewPluginRegistry(db *fsdb.Db) (ret *PluginRegistry, err error) {
ret.Defaults = tools.NeeDefaults(ret.GetModels)
ret.VendorsAll.AddVendors(
// Create a vendors slice to hold all vendors (order doesn't matter initially)
vendors := []ai.Vendor{}
// Add non-OpenAI compatible clients
vendors = append(vendors,
openai.NewClient(),
ollama.NewClient(),
azure.NewClient(),
groq.NewClient(),
gemini.NewClient(),
//gemini_openai.NewClient(),
anthropic.NewClient(),
siliconcloud.NewClient(),
openrouter.NewClient(),
lmstudio.NewClient(),
mistral.NewClient(),
deepseek.NewClient(),
exolab.NewClient(),
litellm.NewClient(),
grokai.NewClient(),
)
// Add all OpenAI-compatible providers
for providerName := range openai_compatible.ProviderMap {
provider, _ := openai_compatible.GetProviderByName(providerName)
vendors = append(vendors, openai_compatible.NewClient(provider))
}
// Sort vendors by name for consistent ordering (case-insensitive)
sort.Slice(vendors, func(i, j int) bool {
return strings.ToLower(vendors[i].GetName()) < strings.ToLower(vendors[j].GetName())
})
// Add all sorted vendors to VendorsAll
ret.VendorsAll.AddVendors(vendors...)
_ = ret.Configure()
return
}
func (o *PluginRegistry) ListVendors(out io.Writer) error {
vendors := lo.Map(o.VendorsAll.Vendors, func(vendor ai.Vendor, _ int) string {
return vendor.GetName()
})
fmt.Fprint(out, "Available Vendors:\n\n")
for _, vendor := range vendors {
fmt.Fprintf(out, "%s\n", vendor)
}
return nil
}
type PluginRegistry struct {
Db *fsdb.Db
@@ -131,7 +149,7 @@ func (o *PluginRegistry) Setup() (err error) {
return vendor
})...)
groupsPlugins.AddGroupItems("Tools", o.Defaults, o.PatternsLoader, o.YouTube, o.Language, o.Jina, o.Strategies)
groupsPlugins.AddGroupItems("Tools", o.Defaults, o.Jina, o.Language, o.PatternsLoader, o.Strategies, o.YouTube)
for {
groupsPlugins.Print()

12
go.mod
View File

@@ -4,7 +4,7 @@ go 1.23.4
require (
github.com/anaskhan96/soup v1.2.5
github.com/anthropics/anthropic-sdk-go v0.2.0-alpha.11
github.com/anthropics/anthropic-sdk-go v0.2.0-beta.3
github.com/atotto/clipboard v0.1.4
github.com/gabriel-vasile/mimetype v1.4.8
github.com/gin-gonic/gin v1.10.0
@@ -19,7 +19,7 @@ require (
github.com/samber/lo v1.49.1
github.com/sashabaranov/go-openai v1.38.0
github.com/stretchr/testify v1.10.0
golang.org/x/text v0.22.0
golang.org/x/text v0.23.0
google.golang.org/api v0.223.0
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
@@ -89,11 +89,11 @@ require (
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
golang.org/x/arch v0.14.0 // indirect
golang.org/x/crypto v0.35.0 // indirect
golang.org/x/net v0.36.0 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/net v0.38.0 // indirect
golang.org/x/oauth2 v0.27.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/time v0.10.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250224174004-546df14abb99 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250224174004-546df14abb99 // indirect

26
go.sum
View File

@@ -25,6 +25,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/anthropics/anthropic-sdk-go v0.2.0-alpha.11 h1:O3/AMObKntZyu1KH6Xks6E0gbE8w6HVaKHE+/vXARzM=
github.com/anthropics/anthropic-sdk-go v0.2.0-alpha.11/go.mod h1:GJxtdOs9K4neo8Gg65CjJ7jNautmldGli5/OFNabOoo=
github.com/anthropics/anthropic-sdk-go v0.2.0-beta.3 h1:b5t1ZJMvV/l99y4jbz7kRFdUp3BSDkI8EhSlHczivtw=
github.com/anthropics/anthropic-sdk-go v0.2.0-beta.3/go.mod h1:AapDW22irxK2PSumZiQXYUFvsdQgkwIWlpESweWZI/c=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@@ -224,8 +226,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs=
golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -244,8 +246,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.36.0 h1:vWF2fRbw4qslQsQzgFqZff+BItCvGFQqKzKIzx1rmoA=
golang.org/x/net v0.36.0/go.mod h1:bFmbeoIPfrw4sMHNhb4J9f6+tPziuGjq7Jk/38fxi1I=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M=
golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -255,8 +257,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -273,8 +275,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
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=
@@ -284,8 +286,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
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=
@@ -296,8 +298,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=
golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@@ -35,8 +35,8 @@ schema = 3
version = "v1.3.3"
hash = "sha256-jv7ZshpSd7FZzKKN6hqlUgiR8C3y85zNIS/hq7g76Ho="
[mod."github.com/anthropics/anthropic-sdk-go"]
version = "v0.2.0-alpha.11"
hash = "sha256-0wl62e6AVhDY3KkoYrfAHFtBrwNC4nzqrR55iyCJlwk="
version = "v0.2.0-beta.3"
hash = "sha256-QnR7MWiLPVii9jy7rjrZfg1UDgXCXkc2latdzFurrFM="
[mod."github.com/araddon/dateparse"]
version = "v0.0.0-20210429162001-6b43995a97de"
hash = "sha256-UuX84naeRGMsFOgIgRoBHG5sNy1CzBkWPKmd6VbLwFw="
@@ -239,23 +239,23 @@ schema = 3
version = "v0.14.0"
hash = "sha256-9akWthLBB+Au/JIg3WKcSx1YAfHEHOCnQF62sJoMJG4="
[mod."golang.org/x/crypto"]
version = "v0.35.0"
hash = "sha256-XT1VU0+m1nZbhrMYXN2+eaKBlScfiT4bCBgXu4mfa1Q="
[mod."golang.org/x/net"]
version = "v0.36.0"
hash = "sha256-2c9AvnizBMCaqKPzwF2IMlRfvjRNDFmxIYhrgZjhOm0="
hash = "sha256-Np+suvZdMOXALDm4m8nDNa+QsvUV0rE0PEINuxCoKGM="
[mod."golang.org/x/net"]
version = "v0.38.0"
hash = "sha256-iHmyxkZQLw1PsUZaXHMt84GrjbXPvkEc92sLoK3W++c="
[mod."golang.org/x/oauth2"]
version = "v0.27.0"
hash = "sha256-TBKV2c/m0SgPqrJSE0ltJXfImrYPafNuziLN25jgsYY="
[mod."golang.org/x/sync"]
version = "v0.11.0"
hash = "sha256-5ZBfDJvNaUBM4Vhk0fgYblCGL3eBxiJL85nIE8LiKl0="
version = "v0.12.0"
hash = "sha256-lPIbI6aXx+iKtFjPaXKNLEnuNfhHCVl7EQiE7alUvlM="
[mod."golang.org/x/sys"]
version = "v0.30.0"
hash = "sha256-BuhWtwDkciVioc03rxty6G2vcZVnPX85lI7tgQOFVP8="
version = "v0.31.0"
hash = "sha256-aulv5obCrhheMlSq7seUgP3C29nfZABwiQ4IBNisgME="
[mod."golang.org/x/text"]
version = "v0.22.0"
hash = "sha256-kUwLNFk9K/YuWmO5/u2IshrmhT2CCuk+mAShSlTTeZo="
version = "v0.23.0"
hash = "sha256-TiYX1K4DYpP1dEV06whOm43xyOntjrPFi+VAdncoeCY="
[mod."golang.org/x/time"]
version = "v0.10.0"
hash = "sha256-vnlAME3gDR6R4cbCmSYAlR1Rjc0yUpkufTOPNvCdf6Q="

View File

@@ -1 +1 @@
"1.4.173"
"1.4.181"

View File

@@ -52,18 +52,18 @@ type Client struct {
defaultRequiredUserMessage string
models []string
client *anthropic.Client
client anthropic.Client
}
func (an *Client) configure() (err error) {
if an.ApiBaseURL.Value != "" {
baseURL := an.ApiBaseURL.Value
if strings.Contains(baseURL, "-") && !strings.HasSuffix(baseURL, "/v1") {
if strings.HasSuffix(baseURL, "/") {
baseURL = strings.TrimSuffix(baseURL, "/")
}
baseURL = baseURL + "/v1"
// As of 2.0beta1, using v2 API endpoint.
// https://github.com/anthropics/anthropic-sdk-go/blob/main/CHANGELOG.md#020-beta1-2025-03-25
if strings.Contains(baseURL, "-") && !strings.HasSuffix(baseURL, "/v2") {
baseURL = strings.TrimSuffix(baseURL, "/")
baseURL = baseURL + "/v2"
}
an.client = anthropic.NewClient(
@@ -87,21 +87,19 @@ func (an *Client) SendStream(
ctx := context.Background()
stream := an.client.Messages.NewStreaming(ctx, anthropic.MessageNewParams{
Model: anthropic.F(opts.Model),
MaxTokens: anthropic.F(int64(an.maxTokens)),
TopP: anthropic.F(opts.TopP),
Temperature: anthropic.F(opts.Temperature),
Messages: anthropic.F(messages),
Model: opts.Model,
MaxTokens: int64(an.maxTokens),
TopP: anthropic.Opt(opts.TopP),
Temperature: anthropic.Opt(opts.Temperature),
Messages: messages,
})
for stream.Next() {
event := stream.Current()
switch delta := event.Delta.(type) {
case anthropic.ContentBlockDeltaEventDelta:
if delta.Text != "" {
channel <- delta.Text
}
// directly send any non-empty delta text
if event.Delta.Text != "" {
channel <- event.Delta.Text
}
}
@@ -117,11 +115,11 @@ func (an *Client) Send(ctx context.Context, msgs []*goopenai.ChatCompletionMessa
var message *anthropic.Message
if message, err = an.client.Messages.New(ctx, anthropic.MessageNewParams{
Model: anthropic.F(opts.Model),
MaxTokens: anthropic.F(int64(an.maxTokens)),
TopP: anthropic.F(opts.TopP),
Temperature: anthropic.F(opts.Temperature),
Messages: anthropic.F(messages),
Model: opts.Model,
MaxTokens: int64(an.maxTokens),
TopP: anthropic.Opt(opts.TopP),
Temperature: anthropic.Opt(opts.Temperature),
Messages: messages,
}); err != nil {
return
}

View File

@@ -1,15 +0,0 @@
package deepseek
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("DeepSeek", "https://api.deepseek.com", nil)
return
}
type Client struct {
*openai.Client
}

View File

@@ -1,13 +0,0 @@
package deepseek
// Test generated using Keploy
import (
"testing"
)
func TestNewClient_EmbeddedClientNotNil(t *testing.T) {
client := NewClient()
if client.Client == nil {
t.Fatalf("Expected embedded openai.Client to be non-nil, got nil")
}
}

View File

@@ -1,15 +0,0 @@
package grokai
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("GrokAI", "https://api.x.ai/v1", nil)
return
}
type Client struct {
*openai.Client
}

View File

@@ -1,13 +0,0 @@
package grokai
// Test generated using Keploy
import (
"testing"
)
func TestNewClient_EmbeddedClientNotNil(t *testing.T) {
client := NewClient()
if client.Client == nil {
t.Fatalf("Expected embedded openai.Client to be non-nil, got nil")
}
}

View File

@@ -1,17 +0,0 @@
package groq
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
// NewClient initializes and returns a new Groq Client.
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("Groq", "https://api.groq.com/openai/v1", nil)
return
}
// Client wraps the openai.Client to provide additional functionality specific to Groq.
type Client struct {
*openai.Client
}

View File

@@ -1,13 +0,0 @@
package groq
// Test generated using Keploy
import (
"testing"
)
func TestNewClientEmbeddedClientNotNil(t *testing.T) {
client := NewClient()
if client.Client == nil {
t.Fatalf("Expected embedded openai.Client to be non-nil, got nil")
}
}

View File

@@ -1,15 +0,0 @@
package litellm
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("LiteLLM", "http://localhost:4000", nil)
return
}
type Client struct {
*openai.Client
}

View File

@@ -1,15 +0,0 @@
package mistral
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("Mistral", "https://api.mistral.ai/v1", nil)
return
}
type Client struct {
*openai.Client
}

View File

@@ -0,0 +1,74 @@
package openai_compatible
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
// ProviderConfig defines the configuration for an OpenAI-compatible API provider
type ProviderConfig struct {
Name string
BaseURL string
}
// Client is the common structure for all OpenAI-compatible providers
type Client struct {
*openai.Client
}
// NewClient creates a new OpenAI-compatible client for the specified provider
func NewClient(providerConfig ProviderConfig) *Client {
client := &Client{}
client.Client = openai.NewClientCompatible(providerConfig.Name, providerConfig.BaseURL, nil)
return client
}
// ProviderMap is a map of provider name to ProviderConfig for O(1) lookup
var ProviderMap = map[string]ProviderConfig{
"Mistral": {
Name: "Mistral",
BaseURL: "https://api.mistral.ai/v1",
},
"LiteLLM": {
Name: "LiteLLM",
BaseURL: "http://localhost:4000",
},
"Groq": {
Name: "Groq",
BaseURL: "https://api.groq.com/openai/v1",
},
"GrokAI": {
Name: "GrokAI",
BaseURL: "https://api.x.ai/v1",
},
"DeepSeek": {
Name: "DeepSeek",
BaseURL: "https://api.deepseek.com",
},
"Cerebras": {
Name: "Cerebras",
BaseURL: "https://api.cerebras.ai/v1",
},
"OpenRouter": {
Name: "OpenRouter",
BaseURL: "https://openrouter.ai/api/v1",
},
"SiliconCloud": {
Name: "SiliconCloud",
BaseURL: "https://api.siliconflow.cn/v1",
},
}
// GetProviderByName returns the provider configuration for a given name with O(1) lookup
func GetProviderByName(name string) (ProviderConfig, bool) {
provider, found := ProviderMap[name]
return provider, found
}
// CreateClient creates a new client for a provider by name
func CreateClient(providerName string) (*Client, bool) {
providerConfig, found := GetProviderByName(providerName)
if !found {
return nil, false
}
return NewClient(providerConfig), true
}

View File

@@ -0,0 +1,42 @@
package openai_compatible
import (
"testing"
)
func TestCreateClient(t *testing.T) {
testCases := []struct {
name string
provider string
exists bool
}{
{
name: "Existing provider - Mistral",
provider: "Mistral",
exists: true,
},
{
name: "Existing provider - Groq",
provider: "Groq",
exists: true,
},
{
name: "Non-existent provider",
provider: "NonExistent",
exists: false,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
client, exists := CreateClient(tc.provider)
if exists != tc.exists {
t.Errorf("Expected exists=%v for provider %s, got %v",
tc.exists, tc.provider, exists)
}
if exists && client == nil {
t.Errorf("Expected non-nil client for provider %s", tc.provider)
}
})
}
}

View File

@@ -1,16 +0,0 @@
package openrouter
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("OpenRouter", "https://openrouter.ai/api/v1", nil)
return
}
type Client struct {
*openai.Client
}

View File

@@ -1 +0,0 @@
package openrouter

View File

@@ -1,15 +0,0 @@
package siliconcloud
import (
"github.com/danielmiessler/fabric/plugins/ai/openai"
)
func NewClient() (ret *Client) {
ret = &Client{}
ret.Client = openai.NewClientCompatible("SiliconCloud", "https://api.siliconflow.cn/v1", nil)
return
}
type Client struct {
*openai.Client
}

View File

@@ -4,6 +4,8 @@ import (
"bytes"
"context"
"fmt"
"sort"
"strings"
"sync"
"github.com/danielmiessler/fabric/plugins"
@@ -95,6 +97,9 @@ func (o *VendorsManager) readModels() (err error) {
if result.err != nil {
fmt.Println(result.vendorName, result.err)
} else {
sort.Slice(result.models, func(i, j int) bool {
return strings.ToLower(result.models[i]) < strings.ToLower(result.models[j])
})
o.Models.AddGroupItems(result.vendorName, result.models...)
}
}

View File

@@ -117,8 +117,12 @@ func (e *ExtensionExecutor) executeWithFile(cmd *exec.Cmd, ext *ExtensionDefinit
// Create context with timeout
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
// Store the original environment
originalEnv := cmd.Env
// Create a new command with context. This might reset Env, depending on the Go version.
cmd = exec.CommandContext(ctx, cmd.Path, cmd.Args[1:]...)
cmd.Env = cmd.Env
// Restore the environment variables explicitly
cmd.Env = originalEnv
fileConfig := ext.GetFileConfig()
if fileConfig == nil {

View File

@@ -6,6 +6,7 @@ import (
"net/http"
"os"
"path/filepath"
"strings"
"github.com/gin-gonic/gin"
)
@@ -14,6 +15,7 @@ import (
type StrategyMeta struct {
Name string `json:"name"`
Description string `json:"description"`
Prompt string `json:"prompt"`
}
// NewStrategiesHandler registers the /strategies GET endpoint
@@ -41,16 +43,17 @@ func NewStrategiesHandler(r *gin.Engine) {
}
var s struct {
Name string `json:"name"`
Description string `json:"description"`
Prompt string `json:"prompt"`
}
if err := json.Unmarshal(data, &s); err != nil {
continue
}
strategies = append(strategies, StrategyMeta{
Name: s.Name,
Name: strings.TrimSuffix(file.Name(), ".json"),
Description: s.Description,
Prompt: s.Prompt,
})
}

View File

@@ -1,3 +1,3 @@
package main
var version = "v1.4.173"
var version = "v1.4.181"