Merge branch 'main' of github.com:danielmiessler/fabric

This commit is contained in:
Daniel Miessler 2024-09-15 19:31:53 -07:00
commit 0b28847e5d
22 changed files with 346 additions and 76 deletions

View File

@ -1,4 +1,4 @@
name: Go Build and Release
name: Go Build
on:
push:

View File

@ -1,4 +1,4 @@
name: Go Build and Release
name: Go Release
on:
push:
@ -64,7 +64,7 @@ jobs:
GOOS: ${{ env.OS }}
GOARCH: ${{ matrix.arch }}
run: |
go build -o fabric-${OS}-${{ matrix.arch }}-${{ github.ref_name }} .
go build -o fabric-${OS}-${{ matrix.arch }} .
- name: Build binary on Windows
if: matrix.os == 'windows-latest'
@ -72,15 +72,39 @@ jobs:
GOOS: windows
GOARCH: ${{ matrix.arch }}
run: |
go build -o fabric-${OS}-${{ matrix.arch }}-${{ github.ref_name }} .
go build -o fabric-windows-${{ matrix.arch }}.exe .
- name: Upload build artifact
if: matrix.os != 'windows-latest'
uses: actions/upload-artifact@v3
with:
name: fabric-${{ env.OS }}-${{ matrix.arch }}-${{ github.ref_name }}
path: fabric-${{ env.OS }}-${{ matrix.arch }}-${{ github.ref_name }}
name: fabric-${OS}-${{ matrix.arch }}
path: fabric-${OS}-${{ matrix.arch }}
- name: Upload build artifact
if: matrix.os == 'windows-latest'
uses: actions/upload-artifact@v3
with:
name: fabric-windows-${{ matrix.arch }}.exe
path: fabric-windows-${{ matrix.arch }}.exe
- name: Create release if it doesn't exist
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release view ${{ github.ref_name }} || gh release create ${{ github.ref_name }} --title "Release ${{ github.ref_name }}" --notes "Automated release for ${{ github.ref_name }}"
- name: Upload release artifact
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload ${{ github.ref_name }} fabric-${{ env.OS }}-${{ matrix.arch }}-${{ github.ref_name }}
gh release upload ${{ github.ref_name }} fabric-windows-${{ matrix.arch }}.exe
- name: Upload release artifact
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') && matrix.os != 'windows-latest'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload ${{ github.ref_name }} fabric-${OS}-${{ matrix.arch }}

View File

@ -195,6 +195,7 @@ Application Options:
-T, --topp= Set top P (default: 0.9)
-s, --stream Stream
-P, --presencepenalty= Set presence penalty (default: 0.0)
-r, --raw Use the defaults of the model without sending chat options (like temperature etc.) and use the user role instead of the system role for patterns
-F, --frequencypenalty= Set frequency penalty (default: 0.0)
-l, --listpatterns List all patterns
-L, --listmodels List all available models
@ -314,6 +315,34 @@ go install github.com/danielmiessler/yt@latest
Be sure to add your `YOUTUBE_API_KEY` to `~/.config/fabric/.env`.
### `to_pdf`
`to_pdf` is a helper command that converts LaTeX files to PDF format. You can use it like this:
```bash
to_pdf input.tex
```
This will create a PDF file from the input LaTeX file in the same directory.
You can also use it with stdin which works perfectly with the `write_latex` pattern:
```bash
echo "ai security primer" | fabric --pattern write_latex | to_pdf
```
This will create a PDF file named `output.pdf` in the current directory.
### `to_pdf` Installation
To install `to_pdf`, install it the same way as you install Fabric, just with a different repo name.
```bash
go install github.com/danielmiessler/fabric/to_pdf/to_pdf@latest
```
Make sure you have a LaTeX distribution (like TeX Live or MiKTeX) installed on your system, as `to_pdf` requires `pdflatex` to be available in your system's PATH.
## Meta
> [!NOTE]

View File

@ -113,12 +113,14 @@ func Cli() (message string, err error) {
return
}
if currentFlags.YouTubeTranscript {
if !currentFlags.YouTubeComments || currentFlags.YouTubeTranscript {
var transcript string
if transcript, err = fabric.YouTube.GrabTranscript(videoId); err != nil {
return
}
fmt.Println(transcript)
if currentFlags.Message != "" {
currentFlags.Message = currentFlags.Message + "\n" + transcript
} else {
@ -134,12 +136,19 @@ func Cli() (message string, err error) {
commentsString := strings.Join(comments, "\n")
fmt.Println(commentsString)
if currentFlags.Message != "" {
currentFlags.Message = currentFlags.Message + "\n" + commentsString
} else {
currentFlags.Message = commentsString
}
}
if currentFlags.Pattern == "" {
// if the pattern flag is not set, we wanted only to grab the transcript or comments
return
}
}
var chatter *core.Chatter

View File

@ -23,6 +23,7 @@ type Flags struct {
TopP float64 `short:"T" long:"topp" description:"Set top P" default:"0.9"`
Stream bool `short:"s" long:"stream" description:"Stream"`
PresencePenalty float64 `short:"P" long:"presencepenalty" description:"Set presence penalty" default:"0.0"`
Raw bool `short:"r" long:"raw" description:"Use the defaults of the model without sending chat options (like temperature etc.) and use the user role instead of the system role for patterns."`
FrequencyPenalty float64 `short:"F" long:"frequencypenalty" description:"Set frequency penalty" default:"0.0"`
ListPatterns bool `short:"l" long:"listpatterns" description:"List all patterns"`
ListAllModels bool `short:"L" long:"listmodels" description:"List all available models"`
@ -93,6 +94,7 @@ func (o *Flags) BuildChatOptions() (ret *common.ChatOptions) {
TopP: o.TopP,
PresencePenalty: o.PresencePenalty,
FrequencyPenalty: o.FrequencyPenalty,
Raw: o.Raw,
}
return
}

View File

@ -60,6 +60,7 @@ func TestBuildChatOptions(t *testing.T) {
TopP: 0.9,
PresencePenalty: 0.1,
FrequencyPenalty: 0.2,
Raw: false,
}
options := flags.BuildChatOptions()
assert.Equal(t, expectedOptions, options)

View File

@ -1,5 +1,7 @@
package common
import goopenai "github.com/sashabaranov/go-openai"
type Message struct {
Role string `json:"role"`
Content string `json:"content"`
@ -19,6 +21,7 @@ type ChatOptions struct {
TopP float64
PresencePenalty float64
FrequencyPenalty float64
Raw bool
}
// NormalizeMessages remove empty messages and ensure messages order user-assist-user
@ -32,8 +35,8 @@ func NormalizeMessages(msgs []*Message, defaultUserMessage string) (ret []*Messa
}
// Ensure, that each odd position shall be a user message
if fullMessageIndex%2 == 0 && message.Role != "user" {
ret = append(ret, &Message{Role: "user", Content: defaultUserMessage})
if fullMessageIndex%2 == 0 && message.Role != goopenai.ChatMessageRoleUser {
ret = append(ret, &Message{Role: goopenai.ChatMessageRoleUser, Content: defaultUserMessage})
fullMessageIndex++
}
ret = append(ret, message)

View File

@ -1,23 +1,24 @@
package common
import (
goopenai "github.com/sashabaranov/go-openai"
"github.com/stretchr/testify/assert"
"testing"
)
func TestNormalizeMessages(t *testing.T) {
msgs := []*Message{
{Role: "user", Content: "Hello"},
{Role: "bot", Content: "Hi there!"},
{Role: "bot", Content: ""},
{Role: "user", Content: ""},
{Role: "user", Content: "How are you?"},
{Role: goopenai.ChatMessageRoleUser, Content: "Hello"},
{Role: goopenai.ChatMessageRoleAssistant, Content: "Hi there!"},
{Role: goopenai.ChatMessageRoleUser, Content: ""},
{Role: goopenai.ChatMessageRoleUser, Content: ""},
{Role: goopenai.ChatMessageRoleUser, Content: "How are you?"},
}
expected := []*Message{
{Role: "user", Content: "Hello"},
{Role: "bot", Content: "Hi there!"},
{Role: "user", Content: "How are you?"},
{Role: goopenai.ChatMessageRoleUser, Content: "Hello"},
{Role: goopenai.ChatMessageRoleAssistant, Content: "Hi there!"},
{Role: goopenai.ChatMessageRoleUser, Content: "How are you?"},
}
actual := NormalizeMessages(msgs, "default")

View File

@ -3,10 +3,10 @@ package core
import (
"context"
"fmt"
"github.com/danielmiessler/fabric/common"
"github.com/danielmiessler/fabric/db"
"github.com/danielmiessler/fabric/vendors"
goopenai "github.com/sashabaranov/go-openai"
)
type Chatter struct {
@ -26,7 +26,7 @@ func (o *Chatter) Send(request *common.ChatRequest, opts *common.ChatOptions) (m
}
var session *db.Session
if session, err = chatRequest.BuildChatSession(); err != nil {
if session, err = chatRequest.BuildChatSession(opts.Raw); err != nil {
return
}
@ -53,7 +53,7 @@ func (o *Chatter) Send(request *common.ChatRequest, opts *common.ChatOptions) (m
}
if chatRequest.Session != nil && message != "" {
chatRequest.Session.Append(&common.Message{Role: "system", Content: message})
chatRequest.Session.Append(&common.Message{Role: goopenai.ChatMessageRoleAssistant, Content: message})
err = o.db.Sessions.SaveSession(chatRequest.Session)
}
return

View File

@ -10,7 +10,7 @@ func TestBuildChatSession(t *testing.T) {
Pattern: "test pattern",
Message: "test message",
}
session, err := chat.BuildChatSession()
session, err := chat.BuildChatSession(false)
if err != nil {
t.Fatalf("BuildChatSession() error = %v", err)
}

View File

@ -3,6 +3,8 @@ package core
import (
"bytes"
"fmt"
"github.com/danielmiessler/fabric/vendors/groq"
goopenai "github.com/sashabaranov/go-openai"
"os"
"strconv"
"strings"
@ -14,9 +16,10 @@ import (
"github.com/danielmiessler/fabric/vendors/azure"
"github.com/danielmiessler/fabric/vendors/dryrun"
"github.com/danielmiessler/fabric/vendors/gemini"
"github.com/danielmiessler/fabric/vendors/groc"
"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"
)
@ -58,8 +61,8 @@ func NewFabricBase(db *db.Db) (ret *Fabric) {
ret.DefaultModel = ret.AddSetupQuestionCustom("Model", true,
"Enter the index the name of your default model")
ret.VendorsAll.AddVendors(openai.NewClient(), azure.NewClient(), ollama.NewClient(), groc.NewClient(),
gemini.NewClient(), anthropic.NewClient())
ret.VendorsAll.AddVendors(openai.NewClient(), azure.NewClient(), ollama.NewClient(), groq.NewClient(),
gemini.NewClient(), anthropic.NewClient(), siliconcloud.NewClient(), openrouter.NewClient())
return
}
@ -234,7 +237,7 @@ func (o *Fabric) CreateOutputFile(message string, fileName string) (err error) {
return
}
func (o *Chat) BuildChatSession() (ret *db.Session, err error) {
func (o *Chat) BuildChatSession(raw bool) (ret *db.Session, err error) {
// new messages will be appended to the session and used to send the message
if o.Session != nil {
ret = o.Session
@ -243,14 +246,21 @@ func (o *Chat) BuildChatSession() (ret *db.Session, err error) {
}
systemMessage := strings.TrimSpace(o.Context) + strings.TrimSpace(o.Pattern)
if systemMessage != "" {
ret.Append(&common.Message{Role: "system", Content: systemMessage})
}
userMessage := strings.TrimSpace(o.Message)
if raw {
// use the user role instead of the system role in raw mode
message := systemMessage + userMessage
if message != "" {
ret.Append(&common.Message{Role: goopenai.ChatMessageRoleUser, Content: message})
}
} else {
if systemMessage != "" {
ret.Append(&common.Message{Role: goopenai.ChatMessageRoleSystem, Content: systemMessage})
}
if userMessage != "" {
ret.Append(&common.Message{Role: "user", Content: userMessage})
ret.Append(&common.Message{Role: goopenai.ChatMessageRoleUser, Content: userMessage})
}
}
if ret.IsEmpty() {

View File

@ -3,8 +3,9 @@ package core
import (
"context"
"fmt"
"github.com/danielmiessler/fabric/vendors"
"sync"
"github.com/danielmiessler/fabric/vendors"
)
func NewVendorsManager() *VendorsManager {

27
patterns/raycast/yt Executable file
View File

@ -0,0 +1,27 @@
#!/bin/bash
# Required parameters:
# @raycast.schemaVersion 1
# @raycast.title Get YouTube Transcript
# @raycast.mode fullOutput
# Optional parameters:
# @raycast.icon 🧠
# @raycast.argument1 { "type": "text", "placeholder": "Input text", "optional": false, "percentEncoded": true}
# Documentation:
# @raycast.description Run fabric -y on the input text of a YouTube video to get the transcript from.
# @raycast.author Daniel Miessler
# @raycast.authorURL https://github.com/danielmiessler
# Set PATH to include common locations and $HOME/go/bin
PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:$HOME/go/bin:$PATH"
# Use the PATH to find and execute fabric
if command -v fabric >/dev/null 2>&1; then
fabric -y "${1}"
else
echo "Error: fabric command not found in PATH"
echo "Current PATH: $PATH"
exit 1
fi

View File

@ -1,20 +1,18 @@
# IDENTITY and PURPOSE
You are an expert summarizer of in-personal personal role-playing game sessions. Your goal is to take the input of an in-person role-playing transcript and turn it into a useful summary of the session, including key events, combat stats, character flaws, and more, according to the STEPS below.
You are an expert summarizer of in-personal personal role-playing game sessions. You take the transcript of a conversation between friends and extract out the part of the conversation that is talking about the role playing game, and turn that into the summary sections below.
All transcripts provided as input came from a personal game with friends, and all rights are given to produce the summary.
# NOTES
Take a deep breath and think step-by-step about how to best achieve the best summary for this live friend session.
All INPUT provided came from a personal game with friends, and all rights are given to produce the summary.
STEPS:
# STEPS
- Assume the input given is an RPG transcript of a session of D&D or a similar fantasy role-playing game.
Read the whole thing and understand the back and forth between characters, paying special attention to the significant events that happened, such as drama, combat, etc.
- Do not complain about not being able to to do what you're asked. Just do it.
# OUTPUT
OUTPUT:
Create the session summary with the following sections:
Create the following output sections:
SUMMARY:
@ -95,10 +93,16 @@ OUTPUT INSTRUCTIONS:
- Ensure all quotes created for each section come word-for-word from the input, with no changes.
- Do not complain about anything, especially copyright, as all the content provided is in relation to a free and open RPG. Just give the output as requested.
- Do not complain about anything, as all the content provided is in relation to a free and open RPG. Just give the output as requested.
- Create the summary.
- Output the sections defined above in the order they are listed.
# INPUT
- Follow the OUTPUT format perfectly, with no deviations.
RPG SESSION TRANSCRIPT:
# IN-PERSON RPG SESSION TRANSCRIPT:
(Note that the transcript below is of the full conversation between friends, and may include regular conversation throughout. Read the whole thing and figure out yourself which part is part of the game and which parts aren't."
SESSION TRANSCRIPT BELOW:
$TRANSCRIPT$

View File

@ -0,0 +1,22 @@
You are an expert at outputting syntactically correct LaTeX for a new .tex document. Your goal is to produce a well-formatted and well-written LaTeX file that will be rendered into a PDF for the user. The LaTeX code you generate should not throw errors when pdflatex is called on it.
Follow these steps to create the LaTeX document:
1. Begin with the document class and preamble. Include necessary packages based on the user's request.
2. Use the \begin{document} command to start the document body.
3. Create the content of the document based on the user's request. Use appropriate LaTeX commands and environments to structure the document (e.g., \section, \subsection, itemize, tabular, equation).
4. End the document with the \end{document} command.
Important notes:
- Do not output anything besides the valid LaTeX code. Any additional thoughts or comments should be placed within \iffalse ... \fi sections.
- Do not use fontspec as it can make it fail to run.
- For sections and subsections, append an asterisk like this \section* in order to prevent everything from being numbered unless the user asks you to number the sections.
- Ensure all LaTeX commands and environments are properly closed.
- Use appropriate indentation for better readability.
Begin your output with the LaTeX code for the requested document. Do not include any explanations or comments outside of the LaTeX code itself.
The user's request for the LaTeX document will be included here.

105
to_pdf/to_pdf.go Normal file
View File

@ -0,0 +1,105 @@
package main
import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"strings"
)
func main() {
var input io.Reader
var outputFile string
if len(os.Args) > 1 {
// File input mode
file, err := os.Open(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "Error opening file: %v\n", err)
os.Exit(1)
}
defer file.Close()
input = file
outputFile = strings.TrimSuffix(os.Args[1], filepath.Ext(os.Args[1])) + ".pdf"
} else {
// Stdin mode
input = os.Stdin
outputFile = "output.pdf"
}
// Check if pdflatex is installed
if _, err := exec.LookPath("pdflatex"); err != nil {
fmt.Fprintln(os.Stderr, "Error: pdflatex is not installed or not in your PATH.")
fmt.Fprintln(os.Stderr, "Please install a LaTeX distribution (e.g., TeX Live or MiKTeX) and ensure pdflatex is in your PATH.")
os.Exit(1)
}
// Create a temporary directory
tmpDir, err := os.MkdirTemp("", "latex_")
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating temporary directory: %v\n", err)
os.Exit(1)
}
defer os.RemoveAll(tmpDir)
// Create a temporary .tex file
tmpFile, err := os.Create(filepath.Join(tmpDir, "input.tex"))
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating temporary file: %v\n", err)
os.Exit(1)
}
// Copy input to the temporary file
_, err = io.Copy(tmpFile, input)
if err != nil {
fmt.Fprintf(os.Stderr, "Error writing to temporary file: %v\n", err)
os.Exit(1)
}
tmpFile.Close()
// Run pdflatex with nonstopmode
cmd := exec.Command("pdflatex", "-interaction=nonstopmode", "-output-directory", tmpDir, tmpFile.Name())
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Fprintf(os.Stderr, "Error running pdflatex: %v\n", err)
fmt.Fprintf(os.Stderr, "pdflatex output:\n%s\n", output)
os.Exit(1)
}
// Check if PDF was actually created
pdfPath := filepath.Join(tmpDir, "input.pdf")
if _, err := os.Stat(pdfPath); os.IsNotExist(err) {
fmt.Fprintln(os.Stderr, "Error: PDF file was not created. There might be an issue with your LaTeX source.")
fmt.Fprintf(os.Stderr, "pdflatex output:\n%s\n", output)
os.Exit(1)
}
// Move the output PDF to the current directory
err = os.Rename(pdfPath, outputFile)
if err != nil {
fmt.Fprintf(os.Stderr, "Error moving output file: %v\n", err)
os.Exit(1)
}
// Clean up temporary files
cleanupTempFiles(tmpDir)
fmt.Printf("PDF created: %s\n", outputFile)
}
func cleanupTempFiles(dir string) {
extensions := []string{".aux", ".log", ".out", ".toc", ".lof", ".lot", ".bbl", ".blg"}
for _, ext := range extensions {
files, err := filepath.Glob(filepath.Join(dir, "*"+ext))
if err != nil {
fmt.Fprintf(os.Stderr, "Error finding %s files: %v\n", ext, err)
continue
}
for _, file := range files {
if err := os.Remove(file); err != nil {
fmt.Fprintf(os.Stderr, "Error removing file %s: %v\n", file, err)
}
}
}
}

View File

@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
goopenai "github.com/sashabaranov/go-openai"
"github.com/danielmiessler/fabric/common"
"github.com/liushuangls/go-anthropic/v2"
@ -121,10 +122,8 @@ func (an *Client) toMessages(msgs []*common.Message) (ret []anthropic.Message) {
for _, msg := range normalizedMessages {
var message anthropic.Message
switch msg.Role {
case "user":
case goopenai.ChatMessageRoleUser:
message = anthropic.NewUserTextMessage(msg.Content)
case "system":
message = anthropic.NewAssistantTextMessage(msg.Content)
default:
message = anthropic.NewAssistantTextMessage(msg.Content)
}

View File

@ -4,6 +4,7 @@ import (
"bytes"
"context"
"fmt"
goopenai "github.com/sashabaranov/go-openai"
"github.com/danielmiessler/fabric/common"
)
@ -35,9 +36,11 @@ func (c *Client) SendStream(msgs []*common.Message, opts *common.ChatOptions, ch
for _, msg := range msgs {
switch msg.Role {
case "system":
case goopenai.ChatMessageRoleSystem:
output += fmt.Sprintf("System:\n%s\n\n", msg.Content)
case "user":
case goopenai.ChatMessageRoleAssistant:
output += fmt.Sprintf("Assistant:\n%s\n\n", msg.Content)
case goopenai.ChatMessageRoleUser:
output += fmt.Sprintf("User:\n%s\n\n", msg.Content)
default:
output += fmt.Sprintf("%s:\n%s\n\n", msg.Role, msg.Content)
@ -56,14 +59,16 @@ func (c *Client) SendStream(msgs []*common.Message, opts *common.ChatOptions, ch
return nil
}
func (c *Client) Send(ctx context.Context, msgs []*common.Message, opts *common.ChatOptions) (string, error) {
func (c *Client) Send(_ context.Context, msgs []*common.Message, opts *common.ChatOptions) (string, error) {
fmt.Println("Dry run: Would send the following request:")
for _, msg := range msgs {
switch msg.Role {
case "system":
case goopenai.ChatMessageRoleSystem:
fmt.Printf("System:\n%s\n\n", msg.Content)
case "user":
case goopenai.ChatMessageRoleAssistant:
fmt.Printf("Assistant:\n%s\n\n", msg.Content)
case goopenai.ChatMessageRoleUser:
fmt.Printf("User:\n%s\n\n", msg.Content)
default:
fmt.Printf("%s:\n%s\n\n", msg.Role, msg.Content)
@ -84,6 +89,6 @@ func (c *Client) Setup() error {
return nil
}
func (c *Client) SetupFillEnvFileContent(buffer *bytes.Buffer) {
func (c *Client) SetupFillEnvFileContent(_ *bytes.Buffer) {
// No environment variables needed for dry run
}

View File

@ -1,4 +1,4 @@
package groc
package groq
import (
"github.com/danielmiessler/fabric/vendors/openai"

View File

@ -111,19 +111,15 @@ func (o *Client) buildChatCompletionRequest(
msgs []*common.Message, opts *common.ChatOptions,
) (ret goopenai.ChatCompletionRequest) {
messages := lo.Map(msgs, func(message *common.Message, _ int) goopenai.ChatCompletionMessage {
var role string
switch message.Role {
case "user":
role = goopenai.ChatMessageRoleUser
case "system":
role = goopenai.ChatMessageRoleSystem
default:
role = goopenai.ChatMessageRoleSystem
}
return goopenai.ChatCompletionMessage{Role: role, Content: message.Content}
return goopenai.ChatCompletionMessage{Role: message.Role, Content: message.Content}
})
if opts.Raw {
ret = goopenai.ChatCompletionRequest{
Model: opts.Model,
Messages: messages,
}
} else {
ret = goopenai.ChatCompletionRequest{
Model: opts.Model,
Temperature: float32(opts.Temperature),
@ -132,5 +128,6 @@ func (o *Client) buildChatCompletionRequest(
FrequencyPenalty: float32(opts.FrequencyPenalty),
Messages: messages,
}
}
return
}

16
vendors/openrouter/openrouter.go vendored Normal file
View File

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

15
vendors/siliconcloud/siliconcloud.go vendored Normal file
View File

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