Files
go-lcg/serve/execute.go

201 lines
6.1 KiB
Go

package serve
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/direct-dev-ru/linux-command-gpt/config"
"github.com/direct-dev-ru/linux-command-gpt/gpt"
"github.com/direct-dev-ru/linux-command-gpt/validation"
)
// ExecuteRequest представляет запрос на выполнение
type ExecuteRequest struct {
Prompt string `json:"prompt"` // Пользовательский промпт
SystemID int `json:"system_id"` // ID системного промпта (1-5)
SystemText string `json:"system"` // Текст системного промпта (альтернатива system_id)
Verbose string `json:"verbose"` // Степень подробности: "v", "vv", "vvv" или пустая строка
Timeout int `json:"timeout"` // Таймаут в секундах (опционально)
}
// ExecuteResponse представляет ответ
type ExecuteResponse struct {
Success bool `json:"success"`
Command string `json:"command,omitempty"`
Explanation string `json:"explanation,omitempty"`
Error string `json:"error,omitempty"`
Model string `json:"model,omitempty"`
Elapsed float64 `json:"elapsed,omitempty"`
}
// handleExecute обрабатывает POST запросы на выполнение
func handleExecute(w http.ResponseWriter, r *http.Request) {
// Проверяем User-Agent - только curl
userAgent := r.Header.Get("User-Agent")
if !strings.Contains(strings.ToLower(userAgent), "curl") {
http.Error(w, "Only curl requests are allowed", http.StatusForbidden)
return
}
// Проверяем метод
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Парсим JSON
var req ExecuteRequest
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, "Invalid JSON", http.StatusBadRequest)
return
}
// Валидация обязательных полей
if req.Prompt == "" {
http.Error(w, "Prompt is required", http.StatusBadRequest)
return
}
// Валидация длины пользовательского сообщения
if err := validation.ValidateUserMessage(req.Prompt); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// Определяем системный промпт
systemPrompt := ""
if req.SystemText != "" {
// Валидация длины пользовательского системного промпта
if err := validation.ValidateSystemPrompt(req.SystemText); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
systemPrompt = req.SystemText
} else if req.SystemID > 0 && req.SystemID <= 5 {
// Получаем системный промпт по ID
pm := gpt.NewPromptManager(config.AppConfig.PromptFolder)
prompt, err := pm.GetPromptByID(req.SystemID)
if err != nil {
http.Error(w, "Failed to get system prompt", http.StatusInternalServerError)
return
}
// Валидация длины системного промпта из базы
if err := validation.ValidateSystemPrompt(prompt.Content); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
systemPrompt = prompt.Content
} else {
// Используем промпт по умолчанию
// Валидация длины системного промпта по умолчанию
if err := validation.ValidateSystemPrompt(config.AppConfig.Prompt); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
systemPrompt = config.AppConfig.Prompt
}
// Устанавливаем таймаут
timeout := req.Timeout
if timeout <= 0 {
timeout = 120 // По умолчанию 2 минуты
}
// Создаем GPT клиент
gpt3 := gpt.NewGpt3(
config.AppConfig.ProviderType,
config.AppConfig.Host,
config.AppConfig.JwtToken,
config.AppConfig.Model,
systemPrompt,
0.01,
timeout,
)
// Выполняем запрос
response, elapsed := getCommand(*gpt3, req.Prompt)
if response == "" {
jsonResponse(w, ExecuteResponse{
Success: false,
Error: "Failed to get response from AI",
})
return
}
// Если запрошено подробное объяснение
if req.Verbose != "" {
explanation, err := getDetailedExplanation(req.Prompt, req.Verbose, timeout)
if err != nil {
jsonResponse(w, ExecuteResponse{
Success: false,
Error: fmt.Sprintf("Failed to get explanation: %v", err),
})
return
}
jsonResponse(w, ExecuteResponse{
Success: true,
Command: response,
Explanation: explanation,
Model: config.AppConfig.Model,
Elapsed: elapsed,
})
} else {
jsonResponse(w, ExecuteResponse{
Success: true,
Command: response,
Model: config.AppConfig.Model,
Elapsed: elapsed,
})
}
}
// getCommand выполняет запрос к AI
func getCommand(gpt3 gpt.Gpt3, prompt string) (string, float64) {
gpt3.InitKey()
start := time.Now()
response := gpt3.Completions(prompt)
elapsed := time.Since(start).Seconds()
return response, elapsed
}
// getDetailedExplanation получает подробное объяснение
func getDetailedExplanation(prompt, verbose string, timeout int) (string, error) {
level := len(verbose) // 1, 2, 3
// Получаем системный промпт для подробного объяснения
detailedSystem := gpt.GetVerbosePromptByLevel(level)
if detailedSystem == "" {
return "", fmt.Errorf("invalid verbose level: %s", verbose)
}
// Создаем GPT клиент для объяснения
explanationGpt := gpt.NewGpt3(
config.AppConfig.ProviderType,
config.AppConfig.Host,
config.AppConfig.JwtToken,
config.AppConfig.Model,
detailedSystem,
0.2,
timeout,
)
explanationGpt.InitKey()
explanation := explanationGpt.Completions(prompt)
if explanation == "" {
return "", fmt.Errorf("failed to get explanation")
}
return explanation, nil
}
// jsonResponse отправляет JSON ответ
func jsonResponse(w http.ResponseWriter, response ExecuteResponse) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}