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: on:
push: push:

View File

@ -1,4 +1,4 @@
name: Go Build and Release name: Go Release
on: on:
push: push:
@ -64,7 +64,7 @@ jobs:
GOOS: ${{ env.OS }} GOOS: ${{ env.OS }}
GOARCH: ${{ matrix.arch }} GOARCH: ${{ matrix.arch }}
run: | run: |
go build -o fabric-${OS}-${{ matrix.arch }}-${{ github.ref_name }} . go build -o fabric-${OS}-${{ matrix.arch }} .
- name: Build binary on Windows - name: Build binary on Windows
if: matrix.os == 'windows-latest' if: matrix.os == 'windows-latest'
@ -72,15 +72,39 @@ jobs:
GOOS: windows GOOS: windows
GOARCH: ${{ matrix.arch }} GOARCH: ${{ matrix.arch }}
run: | run: |
go build -o fabric-${OS}-${{ matrix.arch }}-${{ github.ref_name }} . go build -o fabric-windows-${{ matrix.arch }}.exe .
- name: Upload build artifact - name: Upload build artifact
if: matrix.os != 'windows-latest'
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: fabric-${{ env.OS }}-${{ matrix.arch }}-${{ github.ref_name }} name: fabric-${OS}-${{ matrix.arch }}
path: fabric-${{ env.OS }}-${{ matrix.arch }}-${{ github.ref_name }} 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 - 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: | 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) -T, --topp= Set top P (default: 0.9)
-s, --stream Stream -s, --stream Stream
-P, --presencepenalty= Set presence penalty (default: 0.0) -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) -F, --frequencypenalty= Set frequency penalty (default: 0.0)
-l, --listpatterns List all patterns -l, --listpatterns List all patterns
-L, --listmodels List all available models -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`. 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 ## Meta
> [!NOTE] > [!NOTE]

View File

@ -113,12 +113,14 @@ func Cli() (message string, err error) {
return return
} }
if currentFlags.YouTubeTranscript { if !currentFlags.YouTubeComments || currentFlags.YouTubeTranscript {
var transcript string var transcript string
if transcript, err = fabric.YouTube.GrabTranscript(videoId); err != nil { if transcript, err = fabric.YouTube.GrabTranscript(videoId); err != nil {
return return
} }
fmt.Println(transcript)
if currentFlags.Message != "" { if currentFlags.Message != "" {
currentFlags.Message = currentFlags.Message + "\n" + transcript currentFlags.Message = currentFlags.Message + "\n" + transcript
} else { } else {
@ -134,12 +136,19 @@ func Cli() (message string, err error) {
commentsString := strings.Join(comments, "\n") commentsString := strings.Join(comments, "\n")
fmt.Println(commentsString)
if currentFlags.Message != "" { if currentFlags.Message != "" {
currentFlags.Message = currentFlags.Message + "\n" + commentsString currentFlags.Message = currentFlags.Message + "\n" + commentsString
} else { } else {
currentFlags.Message = commentsString 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 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"` TopP float64 `short:"T" long:"topp" description:"Set top P" default:"0.9"`
Stream bool `short:"s" long:"stream" description:"Stream"` Stream bool `short:"s" long:"stream" description:"Stream"`
PresencePenalty float64 `short:"P" long:"presencepenalty" description:"Set presence penalty" default:"0.0"` 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"` FrequencyPenalty float64 `short:"F" long:"frequencypenalty" description:"Set frequency penalty" default:"0.0"`
ListPatterns bool `short:"l" long:"listpatterns" description:"List all patterns"` ListPatterns bool `short:"l" long:"listpatterns" description:"List all patterns"`
ListAllModels bool `short:"L" long:"listmodels" description:"List all available models"` 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, TopP: o.TopP,
PresencePenalty: o.PresencePenalty, PresencePenalty: o.PresencePenalty,
FrequencyPenalty: o.FrequencyPenalty, FrequencyPenalty: o.FrequencyPenalty,
Raw: o.Raw,
} }
return return
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,8 +3,9 @@ package core
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/danielmiessler/fabric/vendors"
"sync" "sync"
"github.com/danielmiessler/fabric/vendors"
) )
func NewVendorsManager() *VendorsManager { 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 # 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 following output sections:
Create the session summary with the following sections:
SUMMARY: SUMMARY:
@ -95,10 +93,16 @@ OUTPUT INSTRUCTIONS:
- Ensure all quotes created for each section come word-for-word from the input, with no changes. - 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" "context"
"errors" "errors"
"fmt" "fmt"
goopenai "github.com/sashabaranov/go-openai"
"github.com/danielmiessler/fabric/common" "github.com/danielmiessler/fabric/common"
"github.com/liushuangls/go-anthropic/v2" "github.com/liushuangls/go-anthropic/v2"
@ -121,10 +122,8 @@ func (an *Client) toMessages(msgs []*common.Message) (ret []anthropic.Message) {
for _, msg := range normalizedMessages { for _, msg := range normalizedMessages {
var message anthropic.Message var message anthropic.Message
switch msg.Role { switch msg.Role {
case "user": case goopenai.ChatMessageRoleUser:
message = anthropic.NewUserTextMessage(msg.Content) message = anthropic.NewUserTextMessage(msg.Content)
case "system":
message = anthropic.NewAssistantTextMessage(msg.Content)
default: default:
message = anthropic.NewAssistantTextMessage(msg.Content) message = anthropic.NewAssistantTextMessage(msg.Content)
} }

View File

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

View File

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

View File

@ -111,26 +111,23 @@ func (o *Client) buildChatCompletionRequest(
msgs []*common.Message, opts *common.ChatOptions, msgs []*common.Message, opts *common.ChatOptions,
) (ret goopenai.ChatCompletionRequest) { ) (ret goopenai.ChatCompletionRequest) {
messages := lo.Map(msgs, func(message *common.Message, _ int) goopenai.ChatCompletionMessage { messages := lo.Map(msgs, func(message *common.Message, _ int) goopenai.ChatCompletionMessage {
var role string return goopenai.ChatCompletionMessage{Role: message.Role, Content: message.Content}
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}
}) })
ret = goopenai.ChatCompletionRequest{ if opts.Raw {
Model: opts.Model, ret = goopenai.ChatCompletionRequest{
Temperature: float32(opts.Temperature), Model: opts.Model,
TopP: float32(opts.TopP), Messages: messages,
PresencePenalty: float32(opts.PresencePenalty), }
FrequencyPenalty: float32(opts.FrequencyPenalty), } else {
Messages: messages, 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 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
}