feat(cover): request chatgpt to fill out template

This commit is contained in:
Leons Aleksandrovs
2025-07-12 14:57:59 +03:00
parent b88b0cf7da
commit ea5a2f7fd0
3 changed files with 99 additions and 12 deletions

View File

@@ -3,6 +3,7 @@ package cover
import (
"backend/models/template"
"backend/utils"
"backend/utils/chatgpt"
"backend/utils/jwt"
res "backend/utils/responses"
"net/http"
@@ -43,9 +44,20 @@ func Post(c *gin.Context) {
return
}
// Call chat and ask for cover letter nicely
// Check if template exists
if len(templates) == 0 {
res.Error(c, "Template not found", http.StatusNotFound)
return
}
res.Success(c, templates)
// Call chat and ask for cover letter nicely
coverLetter, err := chatgpt.GenerateCoverLetter(templates[0].Template, data.Application)
if err != nil {
res.Error(c, err.Error(), http.StatusInternalServerError)
return
}
res.Success(c, coverLetter)
}
func Put(c *gin.Context) {

View File

@@ -1 +1,86 @@
package chatgpt
import (
"backend/config"
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
)
type ChatRequest struct {
Model string `json:"model"`
Messages []ChatMessage `json:"messages"`
}
type ChatMessage struct {
Role string `json:"role"`
Content string `json:"content"`
}
func GenerateCoverLetter(templateHTML string, jobHTML string) (string, error) {
apiKey := config.Env["CHATGPT_KEY"]
if apiKey == "" {
return "", errors.New("no API key chat gpt provided")
}
payload := ChatRequest{
Model: "gpt-4o", // o4-mini
Messages: []ChatMessage{
{
Role: "system",
Content: `You are a helpful assistant that fills out cover letter templates in HTML format. Replace all <...> tags like <company>, <experience>, etc., with appropriate content based on the job application.`,
},
{
Role: "user",
Content: fmt.Sprintf(`Template:
%s
Job description:
%s
Respond with ONLY the filled HTML cover letter.`, templateHTML, jobHTML),
},
},
}
body, err := json.Marshal(payload)
if err != nil {
return "", err
}
req, err := http.NewRequest("POST", "https://api.openai.com/v1/chat/completions", bytes.NewBuffer(body))
if err != nil {
return "", err
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+apiKey)
client := &http.Client{Timeout: 20 * time.Second}
res, err := client.Do(req)
if err != nil {
return "", err
}
defer res.Body.Close()
var result struct {
Choices []struct {
Message struct {
Content string `json:"content"`
} `json:"message"`
} `json:"choices"`
}
if err := json.NewDecoder(res.Body).Decode(&result); err != nil {
return "", err
}
if len(result.Choices) == 0 {
return "", fmt.Errorf("no response from GPT")
}
return result.Choices[0].Message.Content, nil
}

View File

@@ -1,8 +1,6 @@
package utils
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
)
@@ -10,20 +8,12 @@ import (
var validate = validator.New()
func BindAndValidate(data any, c *gin.Context) error {
fmt.Println("🔍 BindAndValidate called")
if err := c.ShouldBindJSON(data); err != nil {
fmt.Println("❌ Bind error:", err)
return err
}
fmt.Println("✅ Bind success:", data)
if err := validate.Struct(data); err != nil {
fmt.Println("❌ Validation error:", err)
return err
}
fmt.Println("✅ Validation success")
return nil
}