mirror of
https://github.com/Direct-Dev-Ru/go-lcg.git
synced 2025-11-16 01:29:55 +00:00
190 lines
8.9 KiB
Go
190 lines
8.9 KiB
Go
package cmd
|
||
|
||
import (
|
||
"fmt"
|
||
"os"
|
||
"path"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/atotto/clipboard"
|
||
"github.com/direct-dev-ru/linux-command-gpt/config"
|
||
"github.com/direct-dev-ru/linux-command-gpt/gpt"
|
||
)
|
||
|
||
// ExplainDeps инъекция зависимостей для вывода и окружения
|
||
type ExplainDeps struct {
|
||
DisableHistory bool
|
||
PrintColored func(string, string)
|
||
ColorPurple string
|
||
ColorGreen string
|
||
ColorRed string
|
||
ColorYellow string
|
||
GetCommand func(gpt.Gpt3, string) (string, float64)
|
||
}
|
||
|
||
// ShowDetailedExplanation делает дополнительный запрос с подробным описанием и альтернативами
|
||
func ShowDetailedExplanation(command string, gpt3 gpt.Gpt3, system, originalCmd string, timeout int, level int, deps ExplainDeps) {
|
||
// Получаем домашнюю директорию пользователя
|
||
homeDir, err := os.UserHomeDir()
|
||
if err != nil {
|
||
// Fallback к встроенным промптам
|
||
detailedSystem := getBuiltinVerbosePrompt(level)
|
||
ask := getBuiltinAsk(originalCmd, command)
|
||
processExplanation(detailedSystem, ask, gpt3, timeout, deps, originalCmd, command, system, level)
|
||
return
|
||
}
|
||
|
||
// Создаем менеджер промптов
|
||
pm := gpt.NewPromptManager(homeDir)
|
||
|
||
// Получаем промпт подробности по уровню
|
||
verbosePrompt := getVerbosePromptByLevel(pm.Prompts, level)
|
||
|
||
// Формируем ask в зависимости от языка
|
||
ask := getAskByLanguage(pm.GetCurrentLanguage(), originalCmd, command)
|
||
|
||
processExplanation(verbosePrompt, ask, gpt3, timeout, deps, originalCmd, command, system, level)
|
||
}
|
||
|
||
// getVerbosePromptByLevel возвращает промпт подробности по уровню
|
||
func getVerbosePromptByLevel(prompts []gpt.SystemPrompt, level int) string {
|
||
// Ищем промпт подробности по ID
|
||
for _, prompt := range prompts {
|
||
if prompt.ID >= 6 && prompt.ID <= 8 {
|
||
switch level {
|
||
case 1: // v
|
||
if prompt.ID == 6 {
|
||
return prompt.Content
|
||
}
|
||
case 2: // vv
|
||
if prompt.ID == 7 {
|
||
return prompt.Content
|
||
}
|
||
default: // vvv
|
||
if prompt.ID == 8 {
|
||
return prompt.Content
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// Fallback к встроенным промптам
|
||
return getBuiltinVerbosePrompt(level)
|
||
}
|
||
|
||
// getBuiltinVerbosePrompt возвращает встроенный промпт подробности
|
||
func getBuiltinVerbosePrompt(level int) string {
|
||
switch level {
|
||
case 1: // v — кратко
|
||
return "Ты опытный Linux-инженер. Объясни КРАТКО, по делу: что делает команда и самые важные ключи. Без сравнений и альтернатив. Минимум текста. Пиши на русском."
|
||
case 2: // vv — средне
|
||
return "Ты опытный Linux-инженер. Дай сбалансированное объяснение: назначение команды, разбор основных ключей, 1-2 примера. Кратко упомяни 1-2 альтернативы без глубокого сравнения. Пиши на русском."
|
||
default: // vvv — максимально подробно
|
||
return "Ты опытный Linux-инженер. Дай подробное объяснение команды с полным разбором ключей, подкоманд, сценариев применения, примеров. Затем предложи альтернативные способы решения задачи другой командой/инструментами (со сравнениями и когда что лучше применять). Пиши на русском."
|
||
}
|
||
}
|
||
|
||
// getAskByLanguage формирует ask в зависимости от языка
|
||
func getAskByLanguage(lang, originalCmd, command string) string {
|
||
if lang == "ru" {
|
||
return fmt.Sprintf("Объясни подробно команду и предложи альтернативы. Исходная команда: %s. Исходное задание пользователя: %s", command, originalCmd)
|
||
}
|
||
// Английский
|
||
return fmt.Sprintf("Explain the command in detail and suggest alternatives. Original command: %s. Original user request: %s", command, originalCmd)
|
||
}
|
||
|
||
// getBuiltinAsk возвращает встроенный ask
|
||
func getBuiltinAsk(originalCmd, command string) string {
|
||
return fmt.Sprintf("Объясни подробно команду и предложи альтернативы. Исходная команда: %s. Исходное задание пользователя: %s", command, originalCmd)
|
||
}
|
||
|
||
// processExplanation обрабатывает объяснение
|
||
func processExplanation(detailedSystem, ask string, gpt3 gpt.Gpt3, timeout int, deps ExplainDeps, originalCmd string, command string, system string, level int) {
|
||
// Выводим debug информацию если включен флаг
|
||
if config.AppConfig.MainFlags.Debug {
|
||
printVerboseDebugInfo(detailedSystem, ask, gpt3, timeout, level)
|
||
}
|
||
detailed := gpt.NewGpt3(gpt3.ProviderType, config.AppConfig.Host, gpt3.ApiKey, gpt3.Model, detailedSystem, 0.2, timeout)
|
||
|
||
deps.PrintColored("\n🧠 Получаю подробное объяснение...\n", deps.ColorPurple)
|
||
explanation, elapsed := deps.GetCommand(*detailed, ask)
|
||
if explanation == "" {
|
||
deps.PrintColored("❌ Не удалось получить подробное объяснение.\n", deps.ColorRed)
|
||
return
|
||
}
|
||
|
||
deps.PrintColored(fmt.Sprintf("✅ Готово за %.2f сек\n", elapsed), deps.ColorGreen)
|
||
deps.PrintColored("\nВНИМАНИЕ: ОТВЕТ СФОРМИРОВАН ИИ. ТРЕБУЕТСЯ ПРОВЕРКА И КРИТИЧЕСКИЙ АНАЛИЗ. ВОЗМОЖНЫ ОШИБКИ И ГАЛЛЮЦИНАЦИИ.\n", deps.ColorRed)
|
||
deps.PrintColored("\n📖 Подробное объяснение и альтернативы:\n\n", deps.ColorYellow)
|
||
fmt.Println(explanation)
|
||
|
||
fmt.Printf("\nДействия: (c)копировать, (s)сохранить, (r)перегенерировать, (n)ничего: ")
|
||
var choice string
|
||
fmt.Scanln(&choice)
|
||
switch strings.ToLower(choice) {
|
||
case "c":
|
||
clipboard.WriteAll(explanation)
|
||
fmt.Println("✅ Объяснение скопировано в буфер обмена")
|
||
case "s":
|
||
saveExplanation(explanation, gpt3.Model, originalCmd, command, config.AppConfig.ResultFolder)
|
||
case "r":
|
||
fmt.Println("🔄 Перегенерирую подробное объяснение...")
|
||
ShowDetailedExplanation(command, gpt3, system, originalCmd, timeout, level, deps)
|
||
default:
|
||
fmt.Println(" Возврат в основное меню.")
|
||
}
|
||
|
||
if !deps.DisableHistory && (strings.ToLower(choice) == "c" || strings.ToLower(choice) == "s" || strings.ToLower(choice) == "n") {
|
||
SaveToHistory(config.AppConfig.ResultHistory, config.AppConfig.ResultFolder, originalCmd, command, system, explanation)
|
||
}
|
||
}
|
||
|
||
// saveExplanation сохраняет подробное объяснение и альтернативные способы
|
||
func saveExplanation(explanation string, model string, originalCmd string, commandResponse string, resultFolder string) {
|
||
timestamp := time.Now().Format("2006-01-02_15-04-05")
|
||
filename := fmt.Sprintf("gpt_explanation_%s_%s.md", model, timestamp)
|
||
filePath := path.Join(resultFolder, filename)
|
||
title := truncateTitle(originalCmd)
|
||
content := fmt.Sprintf(
|
||
"# %s\n\n## Prompt\n\n%s\n\n## Command\n\n%s\n\n## Explanation and Alternatives (model: %s)\n\n%s\n",
|
||
title,
|
||
originalCmd,
|
||
commandResponse,
|
||
model,
|
||
explanation,
|
||
)
|
||
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
|
||
fmt.Println("Failed to save explanation:", err)
|
||
} else {
|
||
fmt.Printf("Saved to %s\n", filePath)
|
||
}
|
||
}
|
||
|
||
// truncateTitle сокращает строку до 120 символов (по рунам), добавляя " ..." при усечении
|
||
func truncateTitle(s string) string {
|
||
const maxLen = 120
|
||
if runeCount := len([]rune(s)); runeCount <= maxLen {
|
||
return s
|
||
}
|
||
const head = 116
|
||
r := []rune(s)
|
||
if len(r) <= head {
|
||
return s
|
||
}
|
||
return string(r[:head]) + " ..."
|
||
}
|
||
|
||
// printVerboseDebugInfo выводит отладочную информацию для режимов v/vv/vvv
|
||
func printVerboseDebugInfo(detailedSystem, ask string, gpt3 gpt.Gpt3, timeout int, level int) {
|
||
fmt.Printf("\n🔍 DEBUG VERBOSE (v%d):\n", level)
|
||
fmt.Printf("📝 Системный промпт подробности:\n%s\n", detailedSystem)
|
||
fmt.Printf("💬 Запрос подробности:\n%s\n", ask)
|
||
fmt.Printf("⏱️ Таймаут: %d сек\n", timeout)
|
||
fmt.Printf("🌐 Провайдер: %s\n", gpt3.ProviderType)
|
||
fmt.Printf("🏠 Хост: %s\n", config.AppConfig.Host)
|
||
fmt.Printf("🧠 Модель: %s\n", gpt3.Model)
|
||
fmt.Printf("🎯 Уровень подробности: %d\n", level)
|
||
fmt.Printf("────────────────────────────────────────\n")
|
||
}
|