Merge branch 'main' of github.com:danielmiessler/fabric
This commit is contained in:
commit
0b28847e5d
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -1,4 +1,4 @@
|
||||
name: Go Build and Release
|
||||
name: Go Build
|
||||
|
||||
on:
|
||||
push:
|
||||
|
38
.github/workflows/release.yml
vendored
38
.github/workflows/release.yml
vendored
@ -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 }}
|
||||
|
29
README.md
29
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]
|
||||
|
11
cli/cli.go
11
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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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
27
patterns/raycast/yt
Executable 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
|
@ -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$
|
||||
|
22
patterns/write_latex/system.md
Normal file
22
patterns/write_latex/system.md
Normal 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
105
to_pdf/to_pdf.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
vendors/anthropic/anthropic.go
vendored
5
vendors/anthropic/anthropic.go
vendored
@ -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)
|
||||
}
|
||||
|
17
vendors/dryrun/dryrun.go
vendored
17
vendors/dryrun/dryrun.go
vendored
@ -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
|
||||
}
|
||||
|
2
vendors/groc/groq.go → vendors/groq/groq.go
vendored
2
vendors/groc/groq.go → vendors/groq/groq.go
vendored
@ -1,4 +1,4 @@
|
||||
package groc
|
||||
package groq
|
||||
|
||||
import (
|
||||
"github.com/danielmiessler/fabric/vendors/openai"
|
33
vendors/openai/openai.go
vendored
33
vendors/openai/openai.go
vendored
@ -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
|
||||
}
|
||||
|
16
vendors/openrouter/openrouter.go
vendored
Normal file
16
vendors/openrouter/openrouter.go
vendored
Normal 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
15
vendors/siliconcloud/siliconcloud.go
vendored
Normal 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
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user