mirror of
https://github.com/Direct-Dev-Ru/go-lcg.git
synced 2025-11-16 01:29:55 +00:00
added new proxy llm provider
This commit is contained in:
272
main.go
272
main.go
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"path"
|
||||
"strings"
|
||||
@@ -27,33 +28,44 @@ var (
|
||||
PROMPT = getEnv("LCG_PROMPT", "Reply with linux command and nothing else. Output with plain response - no need formatting. No need explanation. No need code blocks. No need ` symbols.")
|
||||
API_KEY_FILE = getEnv("LCG_API_KEY_FILE", ".openai_api_key")
|
||||
RESULT_FOLDER = getEnv("LCG_RESULT_FOLDER", path.Join(cwd, "gpt_results"))
|
||||
PROVIDER_TYPE = getEnv("LCG_PROVIDER", "ollama") // "ollama", "proxy"
|
||||
JWT_TOKEN = getEnv("LCG_JWT_TOKEN", "")
|
||||
)
|
||||
|
||||
const (
|
||||
colorRed = "\033[31m"
|
||||
colorGreen = "\033[32m"
|
||||
colorYellow = "\033[33m"
|
||||
colorBlue = "\033[34m"
|
||||
colorPurple = "\033[35m"
|
||||
colorCyan = "\033[36m"
|
||||
colorReset = "\033[0m"
|
||||
colorBold = "\033[1m"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "lcg",
|
||||
Usage: "Linux Command GPT - Generate Linux commands from descriptions",
|
||||
Usage: "Linux Command GPT - Генерация Linux команд из описаний",
|
||||
Version: Version,
|
||||
Commands: getCommands(),
|
||||
UsageText: `
|
||||
lcg [global options] <command description>
|
||||
lcg [опции] <описание команды>
|
||||
|
||||
Examples:
|
||||
lcg "I want to extract linux-command-gpt.tar.gz file"
|
||||
lcg --file /path/to/file.txt "I want to list all directories with ls"
|
||||
Примеры:
|
||||
lcg "хочу извлечь файл linux-command-gpt.tar.gz"
|
||||
lcg --file /path/to/file.txt "хочу вывести все директории с помощью ls"
|
||||
`,
|
||||
Description: `
|
||||
Linux Command GPT is a tool for generating Linux commands from natural language descriptions.
|
||||
It supports reading parts of the prompt from files and allows saving, copying, or regenerating results.
|
||||
Additional commands are available for managing API keys.
|
||||
Linux Command GPT - инструмент для генерации Linux команд из описаний на естественном языке.
|
||||
Поддерживает чтение частей промпта из файлов и позволяет сохранять, копировать или перегенерировать результаты.
|
||||
|
||||
Environment Variables:
|
||||
LCG_HOST Endpoint for LLM API (default: http://192.168.87.108:11434/)
|
||||
LCG_COMPLETIONS_PATH Relative API path (default: api/chat)
|
||||
LCG_MODEL Model name (default: codegeex4)
|
||||
LCG_PROMPT Default prompt text
|
||||
LCG_API_KEY_FILE API key storage file (default: ~/.openai_api_key)
|
||||
LCG_RESULT_FOLDER Results folder (default: ./gpt_results)
|
||||
Переменные окружения:
|
||||
LCG_HOST Endpoint для LLM API (по умолчанию: http://192.168.87.108:11434/)
|
||||
LCG_MODEL Название модели (по умолчанию: codegeex4)
|
||||
LCG_PROMPT Текст промпта по умолчанию
|
||||
LCG_PROVIDER Тип провайдера: "ollama" или "proxy" (по умолчанию: ollama)
|
||||
LCG_JWT_TOKEN JWT токен для proxy провайдера
|
||||
`,
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
@@ -75,6 +87,7 @@ Environment Variables:
|
||||
args := c.Args().Slice()
|
||||
if len(args) == 0 {
|
||||
cli.ShowAppHelp(c)
|
||||
showTips()
|
||||
return nil
|
||||
}
|
||||
executeMain(file, system, strings.Join(args, " "))
|
||||
@@ -121,67 +134,174 @@ func getCommands() []*cli.Command {
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "update-jwt",
|
||||
Aliases: []string{"j"},
|
||||
Usage: "Update the JWT token for proxy API",
|
||||
Action: func(c *cli.Context) error {
|
||||
if PROVIDER_TYPE != "proxy" {
|
||||
fmt.Println("JWT token is only needed for proxy provider")
|
||||
return nil
|
||||
}
|
||||
|
||||
var jwtToken string
|
||||
fmt.Print("JWT Token: ")
|
||||
fmt.Scanln(&jwtToken)
|
||||
|
||||
currentUser, _ := user.Current()
|
||||
jwtFile := currentUser.HomeDir + "/.proxy_jwt_token"
|
||||
if err := os.WriteFile(jwtFile, []byte(strings.TrimSpace(jwtToken)), 0600); err != nil {
|
||||
fmt.Printf("Ошибка сохранения JWT токена: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("JWT token updated.")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "delete-jwt",
|
||||
Aliases: []string{"dj"},
|
||||
Usage: "Delete the JWT token for proxy API",
|
||||
Action: func(c *cli.Context) error {
|
||||
if PROVIDER_TYPE != "proxy" {
|
||||
fmt.Println("JWT token is only needed for proxy provider")
|
||||
return nil
|
||||
}
|
||||
|
||||
currentUser, _ := user.Current()
|
||||
jwtFile := currentUser.HomeDir + "/.proxy_jwt_token"
|
||||
if err := os.Remove(jwtFile); err != nil && !os.IsNotExist(err) {
|
||||
fmt.Printf("Ошибка удаления JWT токена: %v\n", err)
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println("JWT token deleted.")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "health",
|
||||
Aliases: []string{"he"}, // Изменено с "h" на "he"
|
||||
Usage: "Check API health",
|
||||
Action: func(c *cli.Context) error {
|
||||
gpt3 := initGPT(PROMPT)
|
||||
if err := gpt3.Health(); err != nil {
|
||||
fmt.Printf("Health check failed: %v\n", err)
|
||||
return err
|
||||
}
|
||||
fmt.Println("API is healthy.")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "config",
|
||||
Aliases: []string{"co"}, // Изменено с "c" на "co"
|
||||
Usage: "Show current configuration",
|
||||
Action: func(c *cli.Context) error {
|
||||
fmt.Printf("Provider: %s\n", PROVIDER_TYPE)
|
||||
fmt.Printf("Host: %s\n", HOST)
|
||||
fmt.Printf("Model: %s\n", MODEL)
|
||||
fmt.Printf("Prompt: %s\n", PROMPT)
|
||||
if PROVIDER_TYPE == "proxy" {
|
||||
fmt.Printf("JWT Token: %s\n", func() string {
|
||||
if JWT_TOKEN != "" {
|
||||
return "***set***"
|
||||
}
|
||||
currentUser, _ := user.Current()
|
||||
jwtFile := currentUser.HomeDir + "/.proxy_jwt_token"
|
||||
if _, err := os.Stat(jwtFile); err == nil {
|
||||
return "***from file***"
|
||||
}
|
||||
return "***not set***"
|
||||
}())
|
||||
}
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "history",
|
||||
Aliases: []string{"hist"},
|
||||
Usage: "Show command history",
|
||||
Action: func(c *cli.Context) error {
|
||||
showHistory()
|
||||
return nil
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func executeMain(file, system, commandInput string) {
|
||||
// fmt.Println(system, commandInput)
|
||||
// os.Exit(0)
|
||||
if file != "" {
|
||||
if err := reader.FileToPrompt(&commandInput, file); err != nil {
|
||||
fmt.Println("Error reading file:", err)
|
||||
printColored(fmt.Sprintf("❌ Ошибка чтения файла: %v\n", err), colorRed)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(RESULT_FOLDER); os.IsNotExist(err) {
|
||||
os.MkdirAll(RESULT_FOLDER, 0755)
|
||||
if err := os.MkdirAll(RESULT_FOLDER, 0755); err != nil {
|
||||
printColored(fmt.Sprintf("❌ Ошибка создания папки результатов: %v\n", err), colorRed)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
gpt3 := initGPT(system)
|
||||
|
||||
// if system != PROMPT {
|
||||
// commandInput += ". " + system
|
||||
// }
|
||||
fmt.Println(commandInput)
|
||||
printColored("🤖 Запрос: ", colorCyan)
|
||||
fmt.Printf("%s\n", commandInput)
|
||||
|
||||
response, elapsed := getCommand(gpt3, commandInput)
|
||||
if response == "" {
|
||||
fmt.Println("No response received.")
|
||||
printColored("❌ Ответ не получен. Проверьте подключение к API.\n", colorRed)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Completed in %v seconds\n\n%s\n", elapsed, response)
|
||||
printColored(fmt.Sprintf("✅ Выполнено за %.2f сек\n", elapsed), colorGreen)
|
||||
printColored("\n📋 Команда:\n", colorYellow)
|
||||
printColored(fmt.Sprintf(" %s\n\n", response), colorBold+colorGreen)
|
||||
|
||||
saveToHistory(commandInput, response)
|
||||
handlePostResponse(response, gpt3, system, commandInput)
|
||||
}
|
||||
|
||||
func initGPT(system string) gpt.Gpt3 {
|
||||
currentUser, _ := user.Current()
|
||||
return gpt.Gpt3{
|
||||
CompletionUrl: HOST + COMPLETIONS,
|
||||
Model: MODEL,
|
||||
Prompt: system,
|
||||
HomeDir: currentUser.HomeDir,
|
||||
ApiKeyFile: API_KEY_FILE,
|
||||
Temperature: 0.01,
|
||||
|
||||
// Загружаем JWT токен в зависимости от провайдера
|
||||
var jwtToken string
|
||||
if PROVIDER_TYPE == "proxy" {
|
||||
jwtToken = JWT_TOKEN
|
||||
if jwtToken == "" {
|
||||
// Пытаемся загрузить из файла
|
||||
jwtFile := currentUser.HomeDir + "/.proxy_jwt_token"
|
||||
if data, err := os.ReadFile(jwtFile); err == nil {
|
||||
jwtToken = strings.TrimSpace(string(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *gpt.NewGpt3(PROVIDER_TYPE, HOST, jwtToken, MODEL, system, 0.01)
|
||||
}
|
||||
|
||||
func getCommand(gpt3 gpt.Gpt3, cmd string) (string, float64) {
|
||||
gpt3.InitKey()
|
||||
start := time.Now()
|
||||
done := make(chan bool)
|
||||
|
||||
go func() {
|
||||
loadingChars := []rune{'-', '\\', '|', '/'}
|
||||
loadingChars := []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}
|
||||
i := 0
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
fmt.Printf("\r")
|
||||
fmt.Printf("\r%s", strings.Repeat(" ", 50))
|
||||
fmt.Print("\r")
|
||||
return
|
||||
default:
|
||||
fmt.Printf("\rLoading %c", loadingChars[i])
|
||||
fmt.Printf("\r%s Обрабатываю запрос...", loadingChars[i])
|
||||
i = (i + 1) % len(loadingChars)
|
||||
time.Sleep(30 * time.Millisecond)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}()
|
||||
@@ -194,20 +314,23 @@ func getCommand(gpt3 gpt.Gpt3, cmd string) (string, float64) {
|
||||
}
|
||||
|
||||
func handlePostResponse(response string, gpt3 gpt.Gpt3, system, cmd string) {
|
||||
fmt.Print("\nOptions: (c)opy, (s)ave, (r)egenerate, (n)one: ")
|
||||
fmt.Printf("Действия: (c)копировать, (s)сохранить, (r)перегенерировать, (e)выполнить, (n)ничего: ")
|
||||
var choice string
|
||||
fmt.Scanln(&choice)
|
||||
|
||||
switch strings.ToLower(choice) {
|
||||
case "c":
|
||||
clipboard.WriteAll(response)
|
||||
fmt.Println("Response copied to clipboard.")
|
||||
fmt.Println("✅ Команда скопирована в буфер обмена")
|
||||
case "s":
|
||||
saveResponse(response, gpt3, cmd)
|
||||
case "r":
|
||||
fmt.Println("🔄 Перегенерирую...")
|
||||
executeMain("", system, cmd)
|
||||
case "e":
|
||||
executeCommand(response)
|
||||
default:
|
||||
fmt.Println("No action taken.")
|
||||
fmt.Println(" До свидания!")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,9 +347,80 @@ func saveResponse(response string, gpt3 gpt.Gpt3, cmd string) {
|
||||
}
|
||||
}
|
||||
|
||||
func executeCommand(command string) {
|
||||
fmt.Printf("🚀 Выполняю: %s\n", command)
|
||||
fmt.Print("Продолжить? (y/N): ")
|
||||
var confirm string
|
||||
fmt.Scanln(&confirm)
|
||||
|
||||
if strings.ToLower(confirm) == "y" || strings.ToLower(confirm) == "yes" {
|
||||
cmd := exec.Command("bash", "-c", command)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Printf("❌ Ошибка выполнения: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("✅ Команда выполнена успешно")
|
||||
}
|
||||
} else {
|
||||
fmt.Println("❌ Выполнение отменено")
|
||||
}
|
||||
}
|
||||
|
||||
func getEnv(key, defaultValue string) string {
|
||||
if value, exists := os.LookupEnv(key); exists {
|
||||
return value
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
type CommandHistory struct {
|
||||
Command string
|
||||
Response string
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
var commandHistory []CommandHistory
|
||||
|
||||
func saveToHistory(cmd, response string) {
|
||||
commandHistory = append(commandHistory, CommandHistory{
|
||||
Command: cmd,
|
||||
Response: response,
|
||||
Timestamp: time.Now(),
|
||||
})
|
||||
|
||||
// Ограничиваем историю 100 командами
|
||||
if len(commandHistory) > 100 {
|
||||
commandHistory = commandHistory[1:]
|
||||
}
|
||||
}
|
||||
|
||||
func showHistory() {
|
||||
if len(commandHistory) == 0 {
|
||||
printColored("📝 История пуста\n", colorYellow)
|
||||
return
|
||||
}
|
||||
|
||||
printColored("📝 История команд:\n", colorYellow)
|
||||
for i, hist := range commandHistory {
|
||||
fmt.Printf("%d. %s → %s (%s)\n",
|
||||
i+1,
|
||||
hist.Command,
|
||||
hist.Response,
|
||||
hist.Timestamp.Format("15:04:05"))
|
||||
}
|
||||
}
|
||||
|
||||
func printColored(text, color string) {
|
||||
fmt.Printf("%s%s%s", color, text, colorReset)
|
||||
}
|
||||
|
||||
func showTips() {
|
||||
printColored("💡 Подсказки:\n", colorCyan)
|
||||
fmt.Println(" • Используйте --file для чтения из файла")
|
||||
fmt.Println(" • Используйте --sys для изменения системного промпта")
|
||||
fmt.Println(" • Команда 'history' покажет историю запросов")
|
||||
fmt.Println(" • Команда 'config' покажет текущие настройки")
|
||||
fmt.Println(" • Команда 'health' проверит доступность API")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user