Files
go-lcg/gpt/prompts.go
2025-10-21 18:51:30 +06:00

382 lines
12 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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] + "..."
}