mirror of
https://github.com/Direct-Dev-Ru/go-lcg.git
synced 2025-11-16 01:29:55 +00:00
382 lines
12 KiB
Go
382 lines
12 KiB
Go
package gpt
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
|
||
"github.com/direct-dev-ru/linux-command-gpt/config"
|
||
)
|
||
|
||
// SystemPrompt представляет системный промпт
|
||
type SystemPrompt struct {
|
||
ID int `json:"id"`
|
||
Name string `json:"name"`
|
||
Description string `json:"description"`
|
||
Content string `json:"content"`
|
||
}
|
||
|
||
// PromptManager управляет системными промптами
|
||
type PromptManager struct {
|
||
Prompts []SystemPrompt
|
||
ConfigFile string
|
||
HomeDir string
|
||
Language string // Текущий язык для файла sys_prompts (en/ru)
|
||
}
|
||
|
||
// NewPromptManager создает новый менеджер промптов
|
||
func NewPromptManager(homeDir string) *PromptManager {
|
||
// Используем конфигурацию из модуля config
|
||
promptFolder := config.AppConfig.PromptFolder
|
||
|
||
// Путь к файлу sys_prompts
|
||
sysPromptsFile := filepath.Join(promptFolder, "sys_prompts")
|
||
|
||
pm := &PromptManager{
|
||
ConfigFile: sysPromptsFile,
|
||
HomeDir: homeDir,
|
||
}
|
||
|
||
// Проверяем, существует ли файл sys_prompts
|
||
if _, err := os.Stat(sysPromptsFile); os.IsNotExist(err) {
|
||
// Если файла нет, создаем его с системными промптами и промптами подробности
|
||
pm.createInitialPromptsFile()
|
||
}
|
||
|
||
// Загружаем все промпты из файла
|
||
pm.loadAllPrompts()
|
||
|
||
return pm
|
||
}
|
||
|
||
// createInitialPromptsFile создает начальный файл с системными промптами и промптами подробности
|
||
func (pm *PromptManager) createInitialPromptsFile() {
|
||
// Загружаем все встроенные промпты из YAML (английские по умолчанию)
|
||
pm.Prompts = GetBuiltinPrompts()
|
||
|
||
// Фикс: при первичном сохранении явно выставляем язык файла
|
||
if pm.Language == "" {
|
||
pm.Language = "en"
|
||
}
|
||
|
||
// Сохраняем все промпты в файл
|
||
pm.saveAllPrompts()
|
||
}
|
||
|
||
// loadDefaultPrompts загружает предустановленные промпты
|
||
func (pm *PromptManager) LoadDefaultPrompts() {
|
||
defaultPrompts := []SystemPrompt{
|
||
{
|
||
ID: 1,
|
||
Name: "linux-command",
|
||
Description: "Generate Linux commands (default)",
|
||
Content: "Reply with linux command and nothing else. Output with plain response - no need formatting. No need explanation. No need code blocks. No need ` symbols.",
|
||
},
|
||
{
|
||
ID: 2,
|
||
Name: "linux-command-with-explanation",
|
||
Description: "Generate Linux commands with explanation",
|
||
Content: "Generate a Linux command and provide a brief explanation of what it does. Format: COMMAND: explanation",
|
||
},
|
||
{
|
||
ID: 3,
|
||
Name: "linux-command-safe",
|
||
Description: "Generate safe Linux commands",
|
||
Content: "Generate a safe Linux command that won't cause data loss or system damage. Reply with linux command and nothing else. Output with plain response - no need formatting.",
|
||
},
|
||
{
|
||
ID: 4,
|
||
Name: "linux-command-verbose",
|
||
Description: "Generate Linux commands with detailed explanation",
|
||
Content: "Generate a Linux command and provide detailed explanation including what each flag does and potential alternatives.",
|
||
},
|
||
{
|
||
ID: 5,
|
||
Name: "linux-command-simple",
|
||
Description: "Generate simple Linux commands",
|
||
Content: "Generate a simple, easy-to-understand Linux command. Avoid complex flags and options when possible.",
|
||
},
|
||
}
|
||
|
||
pm.Prompts = defaultPrompts
|
||
}
|
||
|
||
// loadAllPrompts загружает все промпты из файла sys_prompts
|
||
func (pm *PromptManager) loadAllPrompts() {
|
||
if _, err := os.Stat(pm.ConfigFile); os.IsNotExist(err) {
|
||
return
|
||
}
|
||
|
||
data, err := os.ReadFile(pm.ConfigFile)
|
||
if err != nil {
|
||
return
|
||
}
|
||
|
||
// Новый формат: объект с полями language и prompts
|
||
var pf promptsFile
|
||
if err := json.Unmarshal(data, &pf); err == nil && len(pf.Prompts) > 0 {
|
||
pm.Language = pf.Language
|
||
pm.Prompts = pf.Prompts
|
||
return
|
||
}
|
||
|
||
// Старый формат: просто массив промптов
|
||
var prompts []SystemPrompt
|
||
if err := json.Unmarshal(data, &prompts); err == nil {
|
||
pm.Prompts = prompts
|
||
pm.Language = "en"
|
||
// Миграция в новый формат при следующем сохранении
|
||
}
|
||
}
|
||
|
||
// saveAllPrompts сохраняет все промпты в файл sys_prompts
|
||
// внутренний формат хранения файла sys_prompts
|
||
type promptsFile struct {
|
||
Language string `json:"language,omitempty"`
|
||
Prompts []SystemPrompt `json:"prompts"`
|
||
}
|
||
|
||
func (pm *PromptManager) saveAllPrompts() error {
|
||
pf := promptsFile{
|
||
Language: pm.Language,
|
||
Prompts: pm.Prompts,
|
||
}
|
||
data, err := json.MarshalIndent(pf, "", " ")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return os.WriteFile(pm.ConfigFile, data, 0644)
|
||
}
|
||
|
||
// SaveAllPrompts экспортированная версия saveAllPrompts
|
||
func (pm *PromptManager) SaveAllPrompts() error {
|
||
return pm.saveAllPrompts()
|
||
}
|
||
|
||
// GetCurrentLanguage возвращает текущий язык из файла промптов
|
||
func (pm *PromptManager) GetCurrentLanguage() string {
|
||
if pm.Language == "" {
|
||
return "en"
|
||
}
|
||
return pm.Language
|
||
}
|
||
|
||
// SetLanguage устанавливает язык для всех промптов
|
||
func (pm *PromptManager) SetLanguage(lang string) {
|
||
pm.Language = lang
|
||
}
|
||
|
||
// saveCustomPrompts сохраняет пользовательские промпты
|
||
func (pm *PromptManager) saveCustomPrompts() error {
|
||
// Находим пользовательские промпты (ID > 5)
|
||
var customPrompts []SystemPrompt
|
||
for _, prompt := range pm.Prompts {
|
||
if prompt.ID > 5 {
|
||
customPrompts = append(customPrompts, prompt)
|
||
}
|
||
}
|
||
|
||
data, err := json.MarshalIndent(customPrompts, "", " ")
|
||
if err != nil {
|
||
return err
|
||
}
|
||
|
||
return os.WriteFile(pm.ConfigFile, data, 0644)
|
||
}
|
||
|
||
// GetPromptByID возвращает промпт по ID
|
||
func (pm *PromptManager) GetPromptByID(id int) (*SystemPrompt, error) {
|
||
for _, prompt := range pm.Prompts {
|
||
if prompt.ID == id {
|
||
return &prompt, nil
|
||
}
|
||
}
|
||
return nil, fmt.Errorf("промпт с ID %d не найден", id)
|
||
}
|
||
|
||
// GetPromptByName возвращает промпт по имени
|
||
func (pm *PromptManager) GetPromptByName(name string) (*SystemPrompt, error) {
|
||
for _, prompt := range pm.Prompts {
|
||
if strings.EqualFold(prompt.Name, name) {
|
||
return &prompt, nil
|
||
}
|
||
}
|
||
return nil, fmt.Errorf("промпт с именем '%s' не найден", name)
|
||
}
|
||
|
||
// AddPrompt добавляет новый промпт
|
||
func (pm *PromptManager) AddPrompt(name, description, content string) error {
|
||
// Находим максимальный ID
|
||
maxID := 0
|
||
for _, prompt := range pm.Prompts {
|
||
if prompt.ID > maxID {
|
||
maxID = prompt.ID
|
||
}
|
||
}
|
||
|
||
newPrompt := SystemPrompt{
|
||
ID: maxID + 1,
|
||
Name: name,
|
||
Description: description,
|
||
Content: content,
|
||
}
|
||
|
||
pm.Prompts = append(pm.Prompts, newPrompt)
|
||
return pm.saveAllPrompts()
|
||
}
|
||
|
||
// UpdatePrompt обновляет существующий промпт
|
||
func (pm *PromptManager) UpdatePrompt(id int, name, description, content string) error {
|
||
for i, prompt := range pm.Prompts {
|
||
if prompt.ID == id {
|
||
pm.Prompts[i].Name = name
|
||
pm.Prompts[i].Description = description
|
||
pm.Prompts[i].Content = content
|
||
return pm.saveAllPrompts()
|
||
}
|
||
}
|
||
return fmt.Errorf("промпт с ID %d не найден", id)
|
||
}
|
||
|
||
// DeletePrompt удаляет промпт по ID
|
||
func (pm *PromptManager) DeletePrompt(id int) error {
|
||
for i, prompt := range pm.Prompts {
|
||
if prompt.ID == id {
|
||
pm.Prompts = append(pm.Prompts[:i], pm.Prompts[i+1:]...)
|
||
return pm.saveAllPrompts()
|
||
}
|
||
}
|
||
return fmt.Errorf("промпт с ID %d не найден", id)
|
||
}
|
||
|
||
// ListPrompts выводит список всех доступных промптов
|
||
func (pm *PromptManager) ListPrompts() {
|
||
pm.ListPromptsWithFull(false)
|
||
}
|
||
|
||
// ListPromptsWithFull выводит список промптов с опцией полного вывода
|
||
func (pm *PromptManager) ListPromptsWithFull(full bool) {
|
||
fmt.Println("📝 Доступные системные промпты:")
|
||
fmt.Println()
|
||
|
||
for i, prompt := range pm.Prompts {
|
||
// Разделитель между промптами
|
||
if i > 0 {
|
||
fmt.Println("─" + strings.Repeat("─", 60))
|
||
}
|
||
|
||
// Проверяем, является ли промпт встроенным и неизмененным
|
||
isDefault := pm.isDefaultPrompt(prompt)
|
||
|
||
// Заголовок промпта
|
||
if isDefault {
|
||
fmt.Printf("🔹 ID: %d | Название: %s | Встроенный\n", prompt.ID, prompt.Name)
|
||
} else {
|
||
fmt.Printf("🔹 ID: %d | Название: %s\n", prompt.ID, prompt.Name)
|
||
}
|
||
|
||
// Описание
|
||
if prompt.Description != "" {
|
||
fmt.Printf("📋 Описание: %s\n", prompt.Description)
|
||
}
|
||
|
||
// Содержимое промпта
|
||
fmt.Println("📄 Содержимое:")
|
||
fmt.Println("┌" + strings.Repeat("─", 58) + "┐")
|
||
|
||
// Разбиваем содержимое на строки и выводим с отступами
|
||
lines := strings.Split(prompt.Content, "\n")
|
||
for _, line := range lines {
|
||
if full {
|
||
// Полный вывод без обрезки - разбиваем длинные строки
|
||
if len(line) > 56 {
|
||
// Разбиваем длинную строку на части
|
||
for i := 0; i < len(line); i += 56 {
|
||
end := i + 56
|
||
if end > len(line) {
|
||
end = len(line)
|
||
}
|
||
fmt.Printf("│ %-56s │\n", line[i:end])
|
||
}
|
||
} else {
|
||
fmt.Printf("│ %-56s │\n", line)
|
||
}
|
||
} else {
|
||
// Обычный вывод с обрезкой
|
||
fmt.Printf("│ %-56s │\n", truncateString(line, 56))
|
||
}
|
||
}
|
||
|
||
fmt.Println("└" + strings.Repeat("─", 58) + "┘")
|
||
fmt.Println()
|
||
}
|
||
}
|
||
|
||
// isDefaultPrompt проверяет, является ли промпт встроенным и неизмененным
|
||
func (pm *PromptManager) isDefaultPrompt(prompt SystemPrompt) bool {
|
||
// Используем новую функцию из builtin_prompts.go
|
||
return IsBuiltinPrompt(prompt)
|
||
}
|
||
|
||
// IsDefaultPromptByID проверяет, является ли промпт встроенным только по ID (игнорирует содержимое)
|
||
func (pm *PromptManager) IsDefaultPromptByID(prompt SystemPrompt) bool {
|
||
// Проверяем, что ID находится в диапазоне встроенных промптов (1-8)
|
||
return prompt.ID >= 1 && prompt.ID <= 8
|
||
}
|
||
|
||
// GetRussianDefaultPrompts возвращает русские версии встроенных промптов
|
||
func GetRussianDefaultPrompts() []SystemPrompt {
|
||
return GetBuiltinPromptsByLanguage("ru")
|
||
}
|
||
|
||
// getDefaultPrompts возвращает оригинальные встроенные промпты
|
||
func (pm *PromptManager) GetDefaultPrompts() []SystemPrompt {
|
||
return GetBuiltinPrompts()
|
||
}
|
||
|
||
// AddCustomPrompt добавляет новый пользовательский промпт
|
||
func (pm *PromptManager) AddCustomPrompt(name, description, content string) error {
|
||
// Проверяем, что имя уникально
|
||
for _, prompt := range pm.Prompts {
|
||
if strings.EqualFold(prompt.Name, name) {
|
||
return fmt.Errorf("промпт с именем '%s' уже существует", name)
|
||
}
|
||
}
|
||
|
||
newPrompt := SystemPrompt{
|
||
ID: len(pm.Prompts) + 1,
|
||
Name: name,
|
||
Description: description,
|
||
Content: content,
|
||
}
|
||
|
||
pm.Prompts = append(pm.Prompts, newPrompt)
|
||
return pm.saveCustomPrompts()
|
||
}
|
||
|
||
// DeleteCustomPrompt удаляет пользовательский промпт
|
||
func (pm *PromptManager) DeleteCustomPrompt(id int) error {
|
||
if id <= 5 {
|
||
return fmt.Errorf("нельзя удалить предустановленный промпт")
|
||
}
|
||
|
||
for i, prompt := range pm.Prompts {
|
||
if prompt.ID == id {
|
||
pm.Prompts = append(pm.Prompts[:i], pm.Prompts[i+1:]...)
|
||
return pm.saveCustomPrompts()
|
||
}
|
||
}
|
||
|
||
return fmt.Errorf("промпт с ID %d не найден", id)
|
||
}
|
||
|
||
// truncateString обрезает строку до указанной длины
|
||
func truncateString(s string, maxLen int) string {
|
||
if len(s) <= maxLen {
|
||
return s
|
||
}
|
||
return s[:maxLen-3] + "..."
|
||
}
|