diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 74280d4..938eb13 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Go Build and Release +name: Go Build on: push: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3726e58..b71c045 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -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 }} diff --git a/README.md b/README.md index 6144084..4b184c4 100644 --- a/README.md +++ b/README.md @@ -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] diff --git a/cli/cli.go b/cli/cli.go index 1a5d4f4..ef2eec5 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -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 diff --git a/cli/flags.go b/cli/flags.go index 7de2584..23303aa 100644 --- a/cli/flags.go +++ b/cli/flags.go @@ -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 } diff --git a/cli/flags_test.go b/cli/flags_test.go index 865a262..aba8dc3 100644 --- a/cli/flags_test.go +++ b/cli/flags_test.go @@ -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) diff --git a/common/domain.go b/common/domain.go index f546830..3839e8e 100644 --- a/common/domain.go +++ b/common/domain.go @@ -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) diff --git a/common/domain_test.go b/common/domain_test.go index a4b5ffe..49dcdf3 100644 --- a/common/domain_test.go +++ b/common/domain_test.go @@ -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") diff --git a/core/chatter.go b/core/chatter.go index f90a0c0..b69616b 100644 --- a/core/chatter.go +++ b/core/chatter.go @@ -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 diff --git a/core/chatter_test.go b/core/chatter_test.go index 70966e7..3336e0d 100644 --- a/core/chatter_test.go +++ b/core/chatter_test.go @@ -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) } diff --git a/core/fabric.go b/core/fabric.go index 7616ea5..5b8571b 100644 --- a/core/fabric.go +++ b/core/fabric.go @@ -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 userMessage != "" { - ret.Append(&common.Message{Role: "user", Content: userMessage}) + + 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: goopenai.ChatMessageRoleUser, Content: userMessage}) + } } if ret.IsEmpty() { diff --git a/core/vendors.go b/core/vendors.go index 82f1a71..02af986 100644 --- a/core/vendors.go +++ b/core/vendors.go @@ -3,8 +3,9 @@ package core import ( "context" "fmt" - "github.com/danielmiessler/fabric/vendors" "sync" + + "github.com/danielmiessler/fabric/vendors" ) func NewVendorsManager() *VendorsManager { diff --git a/patterns/raycast/yt b/patterns/raycast/yt new file mode 100755 index 0000000..928fc6e --- /dev/null +++ b/patterns/raycast/yt @@ -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 diff --git a/patterns/summarize_rpg_session/system.md b/patterns/summarize_rpg_session/system.md index 66222ea..4001cd6 100644 --- a/patterns/summarize_rpg_session/system.md +++ b/patterns/summarize_rpg_session/system.md @@ -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$ diff --git a/patterns/write_latex/system.md b/patterns/write_latex/system.md new file mode 100644 index 0000000..7602535 --- /dev/null +++ b/patterns/write_latex/system.md @@ -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. diff --git a/to_pdf/to_pdf.go b/to_pdf/to_pdf.go new file mode 100644 index 0000000..23b7d7c --- /dev/null +++ b/to_pdf/to_pdf.go @@ -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) + } + } + } +} diff --git a/vendors/anthropic/anthropic.go b/vendors/anthropic/anthropic.go index 5f62ac0..0da9c0f 100644 --- a/vendors/anthropic/anthropic.go +++ b/vendors/anthropic/anthropic.go @@ -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) } diff --git a/vendors/dryrun/dryrun.go b/vendors/dryrun/dryrun.go index c13350c..5d3d077 100644 --- a/vendors/dryrun/dryrun.go +++ b/vendors/dryrun/dryrun.go @@ -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 } diff --git a/vendors/groc/groq.go b/vendors/groq/groq.go similarity index 95% rename from vendors/groc/groq.go rename to vendors/groq/groq.go index bfb18ad..4d5722c 100644 --- a/vendors/groc/groq.go +++ b/vendors/groq/groq.go @@ -1,4 +1,4 @@ -package groc +package groq import ( "github.com/danielmiessler/fabric/vendors/openai" diff --git a/vendors/openai/openai.go b/vendors/openai/openai.go index e9c9755..cc70302 100644 --- a/vendors/openai/openai.go +++ b/vendors/openai/openai.go @@ -111,26 +111,23 @@ 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} }) - ret = goopenai.ChatCompletionRequest{ - Model: opts.Model, - Temperature: float32(opts.Temperature), - TopP: float32(opts.TopP), - PresencePenalty: float32(opts.PresencePenalty), - FrequencyPenalty: float32(opts.FrequencyPenalty), - Messages: messages, + if opts.Raw { + ret = goopenai.ChatCompletionRequest{ + Model: opts.Model, + Messages: messages, + } + } else { + ret = goopenai.ChatCompletionRequest{ + Model: opts.Model, + Temperature: float32(opts.Temperature), + TopP: float32(opts.TopP), + PresencePenalty: float32(opts.PresencePenalty), + FrequencyPenalty: float32(opts.FrequencyPenalty), + Messages: messages, + } } return } diff --git a/vendors/openrouter/openrouter.go b/vendors/openrouter/openrouter.go new file mode 100644 index 0000000..2c653cd --- /dev/null +++ b/vendors/openrouter/openrouter.go @@ -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 +} diff --git a/vendors/siliconcloud/siliconcloud.go b/vendors/siliconcloud/siliconcloud.go new file mode 100644 index 0000000..754a28a --- /dev/null +++ b/vendors/siliconcloud/siliconcloud.go @@ -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 +}