package api import ( "fmt" "strings" ) const ( aiModeChat = "chat" aiModeCodegen = "codegen" aiModeExplain = "explain" ) type llmMessage struct { Role string `json:"role"` Content string `json:"content"` } type aiPromptRequest struct { Mode string `json:"mode"` Language string `json:"language"` Name string `json:"name"` Description string `json:"description"` Code string `json:"code"` Question string `json:"question"` } type llmChatRequest struct { Model string `json:"model"` Messages []llmMessage `json:"messages"` MaxTokens int `json:"max_tokens"` } func buildLLMChatRequest(req aiPromptRequest) (llmChatRequest, error) { mode := normalizeAIMode(req.Mode) lang := normalizeLanguage(req.Language) switch mode { case aiModeCodegen: if lang == "" { return llmChatRequest{}, fmt.Errorf("language is required") } return llmChatRequest{ Model: "gpt-oss-120b", Messages: []llmMessage{ {Role: "system", Content: buildCodegenSystemPrompt(lang)}, {Role: "user", Content: buildCodegenUserPrompt(lang, req.Name, req.Description)}, }, MaxTokens: 1024, }, nil case aiModeExplain: if lang == "" { return llmChatRequest{}, fmt.Errorf("language is required") } if strings.TrimSpace(req.Code) == "" { return llmChatRequest{}, fmt.Errorf("code is required") } return llmChatRequest{ Model: "gpt-oss-120b", Messages: []llmMessage{ {Role: "system", Content: buildExplainSystemPrompt(lang)}, {Role: "user", Content: buildExplainUserPrompt(lang, req.Code)}, }, MaxTokens: 1024, }, nil default: question := strings.TrimSpace(req.Question) if question == "" { return llmChatRequest{}, fmt.Errorf("question is required") } return llmChatRequest{ Model: "gpt-oss-120b", Messages: []llmMessage{ {Role: "system", Content: buildChatSystemPrompt()}, {Role: "user", Content: question}, }, MaxTokens: 1024, }, nil } } func normalizeAIMode(mode string) string { switch strings.ToLower(strings.TrimSpace(mode)) { case aiModeCodegen: return aiModeCodegen case aiModeExplain: return aiModeExplain default: return aiModeChat } } func normalizeLanguage(language string) string { return strings.ToLower(strings.TrimSpace(language)) } func buildCodegenSystemPrompt(lang string) string { switch lang { case "python": return strings.TrimSpace(`Ты генератор кода для Fission Console. Возвращай ТОЛЬКО код, без markdown, без пояснений, без fenced-блоков. Генерируй только Fission-usable Python code. Канон для Python: файл main.py, entrypoint main.main, функция def main(): без аргументов. Не генерируй def main(ctx), def handler, Flask-приложение или обычный CLI-main. Если нужен ответ HTTP-подобного вида, возвращай dict со status/body/headers или строку; не выдумывай лишние зависимости. Если описание пользователя расплывчатое, выбери минимальную рабочую Fission-функцию.`) case "nodejs": return strings.TrimSpace(`Ты генератор кода для Fission Console. Возвращай ТОЛЬКО код, без markdown, без пояснений, без fenced-блоков. Генерируй только Fission-usable Node.js code. Канон для Node.js: user code должен быть CommonJS-style, обычно module.exports = async function (context) { ... }. Не используй ESM import/export в user code. Не пиши обычный CLI main. Возвращай объект вида { status, body, headers } или строку/JSON, совместимые с Fission node wrapper. Если описание пользователя расплывчатое, выбери минимальную рабочую Fission-функцию.`) case "go": return strings.TrimSpace(`Ты генератор кода для Fission Console. Возвращай ТОЛЬКО код, без markdown, без пояснений, без fenced-блоков. Генерируй только Fission-usable Go code. Канон для Go: package main, экспортированная функция func Handler(w http.ResponseWriter, r *http.Request). Не генерируй func main() как entrypoint и не делай CLI-программу. Импортируй net/http и другие пакеты только если они реально нужны. Если описание пользователя расплывчатое, выбери минимальную рабочую Fission-функцию.`) case "php": return strings.TrimSpace(`Ты генератор кода для Fission Console. Возвращай ТОЛЬКО код, без markdown, без пояснений, без fenced-блоков. Генерируй только Fission-usable PHP code. Канон для PHP: файл main.php, entrypoint main.php::handler, функция function handler($context). Не пиши CLI-скрипт и не выдумывай другой entrypoint. Для ответа используй $context["response"] или совместимый Fission response pattern. Если описание пользователя расплывчатое, выбери минимальную рабочую Fission-функцию.`) case "ruby": return strings.TrimSpace(`Ты генератор кода для Fission Console. Возвращай ТОЛЬКО код, без markdown, без пояснений, без fenced-блоков. Генерируй только Fission-usable Ruby code. Канон для Ruby: файл handler.rb, entrypoint handler, функция def handler. Не пиши CLI-скрипт и не выдумывай другой entrypoint. Если описание пользователя расплывчатое, выбери минимальную рабочую Fission-функцию.`) default: return strings.TrimSpace(`Ты генератор кода для Fission Console. Возвращай ТОЛЬКО код, без markdown, без пояснений, без fenced-блоков. Генерируй только Fission-usable code. Если язык не распознан, выбери минимальную рабочую реализацию без лишней обвязки. Если описание пользователя расплывчатое, выбери минимальную рабочую Fission-функцию.`) } } func buildCodegenUserPrompt(lang, name, description string) string { funcName := strings.TrimSpace(name) if funcName == "" { funcName = defaultPromptFunctionName(lang) } goal := strings.TrimSpace(description) if goal == "" { goal = "Сгенерируй минимальную рабочую Fission-функцию." } return strings.TrimSpace(fmt.Sprintf(`Сгенерируй функцию для Fission. Language: %s Function name: %s What to do: %s Правила: 1) Верни только код. 2) Соблюдай канон Fission для этого языка. 3) Если пользователь описал задачу плохо или противоречиво, выбери самый безопасный и минимальный рабочий вариант. 4) Не добавляй markdown, пояснений и лишних файлов. 5) Код должен быть пригоден для вставки в console и запуска в Fission без ручной правки entrypoint.`, lang, funcName, goal)) } func buildExplainSystemPrompt(lang string) string { return strings.TrimSpace(fmt.Sprintf(`Ты анализатор кода для Fission Console. Отвечай кратко, по делу и без markdown-блоков. Сначала объясни, что делает код. Затем отдельно скажи, подходит ли он для Fission %s и почему. Если entrypoint, сигнатура или return shape не совпадают с каноном Fission, скажи это прямо. Не переписывай код и не выдумывай поведение, которого нет.`, prettyLanguageName(lang))) } func buildExplainUserPrompt(lang, code string) string { return strings.TrimSpace(fmt.Sprintf(`Объясни этот %s код и проверь его на совместимость с Fission. Требования к ответу: 1) Что делает код. 2) Совместим ли он с Fission. 3) Если не совместим — что именно сломано (entrypoint, сигнатура, return, экспорт, файл). Код: %s`, prettyLanguageName(lang), strings.TrimSpace(code))) } func buildChatSystemPrompt() string { return "Ты краткий и полезный ассистент для Fission Console. Отвечай по делу и без лишней болтовни." } func prettyLanguageName(lang string) string { switch normalizeLanguage(lang) { case "nodejs": return "Node.js" case "python": return "Python" case "go": return "Go" case "php": return "PHP" case "ruby": return "Ruby" default: return "этот язык" } } func defaultPromptFunctionName(lang string) string { switch normalizeLanguage(lang) { case "go": return "Handler" case "php": return "handler" case "ruby": return "handler" case "nodejs": return "main" default: return "main" } }