kopia lustrzana https://github.com/biobootloader/wolverine
Merge 288af47ca3
into 9418f0fefb
commit
6552cc4dd3
|
@ -3,3 +3,4 @@ venv
|
|||
.env
|
||||
env/
|
||||
.vscode/
|
||||
wolverine/GoLang/internal/main/.env
|
||||
|
|
28
README.md
28
README.md
|
@ -62,3 +62,31 @@ This is just a quick prototype I threw together in a few hours. There are many p
|
|||
## Star History
|
||||
|
||||
[](https://star-history.com/#biobootloader/wolverine)
|
||||
|
||||
# Go Usage:
|
||||
#### The Wolverine supports .go files to be executed now!
|
||||
## Setup step by step:
|
||||
|
||||
1) git clone https://....<the_link>
|
||||
2) cd wolverine/wolverine/GoLang/internal/main
|
||||
3) go mod install
|
||||
4) go build -o ../../../../../your_wish_folder/wolverine.exe
|
||||
5) cd ../../../../../your_wish_folder
|
||||
6) > .env
|
||||
7) fill the .env file like in the example below, but with your personal data
|
||||
|
||||
### Example .env:
|
||||
OPENAI_API_KEY=gorinfwe:mfwbevnmowemo9fn20f439vn03v4
|
||||
GPT_MODEL=text-davinci-003
|
||||
ATTEMPTS_TO_TRY=15
|
||||
|
||||
#### Remember that each attempt is a request to GPT so think twice about \<ATTEMPTS_TO_TRY> value
|
||||
#### GPT 4 is the most preferable model here, but you can try to use any model
|
||||
|
||||
## Example Usage
|
||||
|
||||
./wolverine.exe main
|
||||
|
||||
#### main here is your filename WITHOUT .go extension
|
||||
#### If you see the "Success" message, then you must have obtained a file enterFilename+"__fixed".go>, and it's free of any compile errors. So you can freely run it
|
||||
go run main__fixed.go
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
module wolverine
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.15.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.17 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng=
|
||||
github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
@ -0,0 +1,84 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"fmt"
|
||||
"github.com/joho/godotenv"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"wolverine/internal/service"
|
||||
"wolverine/internal/service/healFile"
|
||||
)
|
||||
|
||||
//go:embed prompt.txt
|
||||
var promptContent embed.FS
|
||||
|
||||
func main() {
|
||||
content, err := promptContent.ReadFile("prompt.txt")
|
||||
if err != nil {
|
||||
fmt.Println("Failed to read prompt.txt file")
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
prompt := string(content)
|
||||
|
||||
godotenv.Load()
|
||||
|
||||
gptModel := os.Getenv("GPT_MODEL")
|
||||
apiKey := os.Getenv("OPENAI_API_KEY")
|
||||
attemptsToTryString := os.Getenv("ATTEMPTS_TO_TRY")
|
||||
if gptModel == "" || apiKey == "" {
|
||||
log.Println("You need to set GPT_MODEL and OPENAI_API_KEY environment variables to run the program.")
|
||||
return
|
||||
}
|
||||
|
||||
attemptsToTry, err := strconv.Atoi(attemptsToTryString)
|
||||
if err != nil || attemptsToTryString == "" {
|
||||
log.Println("ATTEMPTS_TO_TRY environment variable is invalid or not set. It should be an integer.")
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
sourceFilename, err := service.ReceiveFile()
|
||||
if err != nil {
|
||||
fmt.Println("Failed to extract a filename from the inputted string")
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
healedFilename := sourceFilename + "__fixed.go"
|
||||
sourceFilename += ".go"
|
||||
|
||||
_, err = os.Stat(sourceFilename)
|
||||
if os.IsNotExist(err) {
|
||||
log.Println("The file you entered doesn't exist. Enter another one and try again.")
|
||||
return
|
||||
}
|
||||
|
||||
// prepare file to be filled code with changes
|
||||
err = service.PrepareNewFile(healedFilename)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to prepare a new file to have the code with changes")
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
// check the inputted model
|
||||
modelIsValid := service.ValidateGPTModel(apiKey, gptModel)
|
||||
if !modelIsValid {
|
||||
log.Println("The GPT model you entered is invalid or doesn't exist. Enter another one and try again.")
|
||||
return
|
||||
}
|
||||
|
||||
err = healFile.HealFile(sourceFilename, healedFilename, apiKey, gptModel, prompt, attemptsToTry)
|
||||
if err != nil {
|
||||
fmt.Println("Failed to heal the file")
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println("\n+++ <<-- Success! -->> +++")
|
||||
fmt.Println("Now you can successfully run " + healedFilename)
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
You are part of an elite automated software fixing team. You will be given a script followed by the arguments it provided and the stack trace of the error it produced. Your job is to figure out what went wrong and suggest changes to the code.
|
||||
|
||||
Because you are part of an automated system, the format you respond in is very strict and sharp. You must provide changes in JSON format, using one of 3 actions: 'Replace', 'Delete', or 'InsertAfter'. 'Delete' will remove that line from the code. 'Replace' will replace the existing line with the content you provide. 'InsertAfter' will insert the new lines you provide AFTER the code is already at the specified line number. For instance, if you want to write line as <x>th, so "line" field should be <x-1> to write to the <x> line etc. For multi-line insertions or replacements, provide the content as a single string with '\n' as the newline character. The first line in each file is given line number 1. Edits will be applied in reverse line order so that line numbers won't be impacted by other edits.
|
||||
|
||||
In addition to the changes, please also provide short explanations of what went wrong. A single explanation message is required, but if you think it's helpful, feel free to provide more explanation messages groups of more complicated changes, but each explanations object must have corresponding action object, connected with unique id field. Each operation item from operations block must have same operation type. Be careful to use proper indentation and spacing in your changes. An example response could be:
|
||||
|
||||
Be ABSOLUTELY SURE to include the CORRECT INDENTATION when making replacements and respond exceptionally in JSON format.
|
||||
example response:
|
||||
|
||||
{
|
||||
"explanations": [
|
||||
{
|
||||
"id": 1,
|
||||
"messages": [
|
||||
"This is just an example, this would usually be a brief explanation of what went wrong"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"messages": [
|
||||
"This is another example, this would usually be a brief explanation of what went wrong",
|
||||
"another one"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"messages": [
|
||||
"This is a third example, this would usually be a brief explanation of what went wrong",
|
||||
"another one",
|
||||
"another one"
|
||||
]
|
||||
}
|
||||
],
|
||||
"actions": [
|
||||
{
|
||||
"id": 1,
|
||||
"operations": [
|
||||
{"operation": "Delete", "line": 20, "content": ""},
|
||||
{"operation": "Delete", "line": 21, "content": ""},
|
||||
{"operation": "Delete", "line": 22, "content": ""}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"operations": [
|
||||
{"operation": "InsertAfter", "line": 48, "content": "x := 1\ny := 2\nz := x * y"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"operations": [
|
||||
{"operation": "Replace", "line": 50, "content": "return nil"}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
package healFile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"wolverine/pkg/cli"
|
||||
)
|
||||
|
||||
func applyChanges(sourceFilename, targetFilename string, changes GPTResponse) error {
|
||||
fileLines, err := readFileLines(sourceFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sortChanges(&changes)
|
||||
|
||||
// define entities only after sorting the object
|
||||
explanations := changes.Explanations
|
||||
actions := changes.Actions
|
||||
|
||||
for _, action := range actions {
|
||||
switch action.Operations[0].Operation {
|
||||
case DeleteOperationType:
|
||||
fmt.Println("Delete Operation:")
|
||||
printExplanations(explanations, action, cli.PrintRed)
|
||||
|
||||
for _, operation := range action.Operations {
|
||||
deleteLine(&fileLines, operation.Line)
|
||||
}
|
||||
|
||||
case InsertOperationType:
|
||||
fmt.Println("Insert Operation:")
|
||||
printExplanations(explanations, action, cli.PrintGreen)
|
||||
|
||||
for _, operation := range action.Operations {
|
||||
insertLine(&fileLines, operation.Line, operation.Content)
|
||||
}
|
||||
case ReplaceOperationType:
|
||||
fmt.Println("Replace Operation:")
|
||||
printExplanations(explanations, action, cli.PrintYellow)
|
||||
|
||||
for _, operation := range action.Operations {
|
||||
replaceLine(&fileLines, operation.Line, operation.Content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
entireCode := strings.Join(fileLines, "\n")
|
||||
err = writeToExistingFile(targetFilename, entireCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sortChanges(changes *GPTResponse) {
|
||||
// reverse the actions
|
||||
for i := 0; i < len(changes.Actions)/2; i++ {
|
||||
changes.Actions[i], changes.Actions[len(changes.Actions)-i-1] = changes.Actions[len(changes.Actions)-i-1], changes.Actions[i]
|
||||
}
|
||||
|
||||
// reverse the explanations
|
||||
for i := 0; i < len(changes.Explanations)/2; i++ {
|
||||
changes.Explanations[i], changes.Explanations[len(changes.Explanations)-i-1] = changes.Explanations[len(changes.Explanations)-i-1], changes.Explanations[i]
|
||||
}
|
||||
}
|
||||
|
||||
func deleteLine(fileLines *[]string, line int) {
|
||||
// GPT would respond like 1st line has an error, but we work with 0th item, not 1st
|
||||
line--
|
||||
*fileLines = append((*fileLines)[:line], (*fileLines)[line+1:]...)
|
||||
}
|
||||
|
||||
func insertLine(fileLines *[]string, line int, content string) {
|
||||
// GPT would respond like 1st line has an error, but we work with 0th item, not 1st
|
||||
line--
|
||||
*fileLines = append((*fileLines)[:line+1], append([]string{content}, (*fileLines)[line+1:]...)...)
|
||||
}
|
||||
|
||||
func replaceLine(fileLines *[]string, line int, content string) {
|
||||
// GPT would respond like 1st line has an error, but we work with 0th item, not 1st
|
||||
line--
|
||||
(*fileLines)[line] = content
|
||||
}
|
||||
|
||||
func printExplanations(explanations []GPTExplanation, action GPTAction, printFunc func(string)) {
|
||||
currentExplanationIndex := -1
|
||||
|
||||
for i, explanation := range explanations {
|
||||
if explanation.Id == action.Id {
|
||||
currentExplanationIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if currentExplanationIndex == -1 {
|
||||
fmt.Println("The explanation is not provided.")
|
||||
} else {
|
||||
for _, explanation := range explanations[currentExplanationIndex].Messages {
|
||||
fmt.Println(explanation)
|
||||
}
|
||||
}
|
||||
|
||||
for _, operation := range action.Operations {
|
||||
printFunc(operation.Content)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package healFile
|
||||
|
||||
func attempt(sourceFilename, targetFilename, compileError, apiToken, model, prompt string) error {
|
||||
// get file content
|
||||
code, err := getFileContent(sourceFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// make request to gpt
|
||||
gptResponse, err := makeRequestToGPT(code, compileError, apiToken, model, prompt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// apply changes: write them to the targetFilename
|
||||
err = applyChanges(sourceFilename, targetFilename, gptResponse)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package healFile
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func writeToExistingFile(filename, content string) error {
|
||||
// Open file with O_TRUNC flag to truncate the file before writing
|
||||
file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0660)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.WriteString(content)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFileContent(filename string) (string, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
code, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(code), nil
|
||||
}
|
||||
|
||||
func readFileLines(filename string) ([]string, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var lines []string
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
lines = append(lines, scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return lines, nil
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package healFile
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
func HealFile(sourceFilename, resultFilename, apiToken, model, prompt string, attemptsToTry int) error {
|
||||
var compileError string
|
||||
|
||||
if isCompilable(sourceFilename, &compileError) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// initial attempt
|
||||
err := attempt(sourceFilename, resultFilename, compileError, apiToken, model, prompt)
|
||||
if err != nil {
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if isCompilable(resultFilename, &compileError) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if didn't work, try to heal file, which you are working with
|
||||
sourceFilename = resultFilename
|
||||
|
||||
attempts := 1
|
||||
|
||||
for {
|
||||
if attempts >= attemptsToTry {
|
||||
return errors.New(string(attemptsToTry) + " attempts to heal file failed")
|
||||
}
|
||||
|
||||
attempts++
|
||||
|
||||
err = attempt(sourceFilename, resultFilename, compileError, apiToken, model, prompt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if isCompilable(resultFilename, &compileError) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package healFile
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func isCompilable(filename string, compileError *string) bool {
|
||||
cmd := exec.Command("go", "run", filename)
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
*compileError = string(output)
|
||||
fmt.Println("The file contains compile errors:")
|
||||
fmt.Println(*compileError)
|
||||
|
||||
fmt.Printf("\nWait for our brainstorm outcome...\n\n")
|
||||
}
|
||||
|
||||
return err == nil
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package healFile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func makeRequestToGPT(code, compileError, apiToken, model string, prompt string) (GPTResponse, error) {
|
||||
|
||||
if compileError == "" {
|
||||
prompt += "\n\nHere is the script that needs fixing:\n\n" +
|
||||
code + "\n\n" +
|
||||
"Please provide your suggested changes, and remember to stick to the " +
|
||||
"exact format as described above."
|
||||
} else {
|
||||
prompt += "\n\nHere is the script that needs fixing:\n\n" +
|
||||
code + "\n\n" +
|
||||
"Here is the error message:\n\n" +
|
||||
compileError + "\n" +
|
||||
"Please provide your suggested changes, and remember to stick to the " +
|
||||
"exact format as described above."
|
||||
}
|
||||
|
||||
var request = Request{
|
||||
Model: model,
|
||||
Prompt: prompt,
|
||||
MaxTokens: 1000,
|
||||
}
|
||||
|
||||
reqBody, err := json.Marshal(request)
|
||||
if err != nil {
|
||||
return GPTResponse{}, err
|
||||
}
|
||||
|
||||
url := "https://api.openai.com/v1/completions"
|
||||
|
||||
req, err := http.NewRequest("POST", url, bytes.NewBuffer(reqBody))
|
||||
if err != nil {
|
||||
return GPTResponse{}, err
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+apiToken)
|
||||
|
||||
client := &http.Client{}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return GPTResponse{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return GPTResponse{}, err
|
||||
}
|
||||
|
||||
var response Response
|
||||
err = json.NewDecoder(resp.Body).Decode(&response)
|
||||
if err != nil {
|
||||
return GPTResponse{}, err
|
||||
}
|
||||
|
||||
gptResponse, err := validateResponse(response.Choices[0].Text)
|
||||
if err != nil {
|
||||
return GPTResponse{}, err
|
||||
}
|
||||
|
||||
return gptResponse, nil
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package healFile
|
||||
|
||||
type Request struct {
|
||||
Model string `json:"model"`
|
||||
Prompt string `json:"prompt"`
|
||||
MaxTokens int `json:"max_tokens"`
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Model string `json:"model"`
|
||||
Choices []struct {
|
||||
Text string `json:"text"`
|
||||
FinishReason string `json:"finish_reason"`
|
||||
} `json:"choices"`
|
||||
Usage struct {
|
||||
PromptTokens int `json:"prompt_tokens"`
|
||||
CompletionTokens int `json:"completion_tokens"`
|
||||
TotalTokens int `json:"total_tokens"`
|
||||
} `json:"usage"`
|
||||
}
|
||||
|
||||
type GPTExplanation struct {
|
||||
Id int `json:"id"`
|
||||
Messages []string `json:"messages"`
|
||||
}
|
||||
|
||||
type GPTAction struct {
|
||||
Id int `json:"id"`
|
||||
Operations []GPTOperation `json:"operations"`
|
||||
}
|
||||
type GPTOperation struct {
|
||||
Operation string `json:"operation"`
|
||||
Line int `json:"line"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type GPTResponse struct {
|
||||
Explanations []GPTExplanation `json:"explanations"`
|
||||
Actions []GPTAction `json:"actions"`
|
||||
}
|
||||
|
||||
const DeleteOperationType = "Delete"
|
||||
const InsertOperationType = "InsertAfter"
|
||||
const ReplaceOperationType = "Replace"
|
|
@ -0,0 +1,22 @@
|
|||
package healFile
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
func validateResponse(response string) (GPTResponse, error) {
|
||||
/*
|
||||
The original idea of recursive forcing response
|
||||
to be in JSON format was abandoned, because there is
|
||||
no need to make it so complicated. Anyway we make
|
||||
requests until we get valid JSON response.
|
||||
*/
|
||||
|
||||
var jsonGPTResponse GPTResponse
|
||||
err := json.Unmarshal([]byte(response), &jsonGPTResponse)
|
||||
if err != nil {
|
||||
return GPTResponse{}, err
|
||||
}
|
||||
|
||||
return jsonGPTResponse, nil
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
func PrepareNewFile(backupFilename string) error {
|
||||
err := createNewFile(backupFilename)
|
||||
if err != nil {
|
||||
return errors.New("failed to prepare file with fixes")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createNewFile(backupFilename string) error {
|
||||
// Create the file
|
||||
file, err := os.Create(backupFilename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
OPENAI_API_KEY string
|
||||
GPT_MODEL string
|
||||
}
|
||||
|
||||
func ReadConfig(path string, container *Config) error {
|
||||
configFile, err := os.Open(path)
|
||||
if err != nil {
|
||||
return errors.New("Failed to open config.json\nMake sure you have set all necessary fields to it and try again.")
|
||||
}
|
||||
defer configFile.Close()
|
||||
|
||||
err = json.NewDecoder(configFile).Decode(container)
|
||||
if err != nil {
|
||||
return errors.New("Failed to read and decode config.json\nMake sure you have set all necessary fields to it and try again.")
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
func ReceiveFile() (string, error) {
|
||||
args := os.Args[1:]
|
||||
if len(args) != 1 {
|
||||
return "", errors.New("entered invalid flags")
|
||||
} else {
|
||||
filename := args[0]
|
||||
if !isValidFilename(filename) {
|
||||
return "", errors.New("entered invalid filename")
|
||||
}
|
||||
|
||||
return filename, nil
|
||||
}
|
||||
}
|
||||
|
||||
func isValidFilename(filename string) bool {
|
||||
validFilenameRegex := regexp.MustCompile(`^[a-zA-Z0-9_.-]*$`)
|
||||
return validFilenameRegex.MatchString(filename)
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"wolverine/pkg/request"
|
||||
)
|
||||
|
||||
func ValidateGPTModel(apiKey string, model string) bool {
|
||||
modelsList, err := request.ModelsList(apiKey)
|
||||
// if something went wrong due to requesting the list -> acts like invalid inp
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// looking for the model
|
||||
for _, m := range modelsList {
|
||||
if m.Id == model {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
func PrintRed(message string) {
|
||||
fmt.Println(" ")
|
||||
color.Red(message)
|
||||
fmt.Println(" ")
|
||||
}
|
||||
|
||||
func PrintGreen(message string) {
|
||||
fmt.Println(" ")
|
||||
color.Green(message)
|
||||
fmt.Println(" ")
|
||||
}
|
||||
|
||||
func PrintYellow(message string) {
|
||||
fmt.Println(" ")
|
||||
color.Yellow(message)
|
||||
fmt.Println(" ")
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package request
|
||||
|
||||
func Completion() {
|
||||
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type ModelStruct struct {
|
||||
Id string `json:"id"`
|
||||
}
|
||||
|
||||
type ModelsListStruct struct {
|
||||
Data []ModelStruct `json:"data"`
|
||||
}
|
||||
|
||||
var targetUrl = "https://api.openai.com/v1/models"
|
||||
|
||||
func ModelsList(apiKey string) ([]ModelStruct, error) {
|
||||
client := &http.Client{}
|
||||
|
||||
req, err := http.NewRequest("GET", targetUrl, nil)
|
||||
if err != nil {
|
||||
return []ModelStruct{}, err
|
||||
}
|
||||
|
||||
req.Header.Add("Authorization", "Bearer "+apiKey)
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return []ModelStruct{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return []ModelStruct{}, err
|
||||
}
|
||||
|
||||
var modelsList ModelsListStruct
|
||||
err = json.Unmarshal(body, &modelsList)
|
||||
if err != nil {
|
||||
return []ModelStruct{}, err
|
||||
}
|
||||
|
||||
return modelsList.Data, nil
|
||||
}
|
Ładowanie…
Reference in New Issue