2025-07-12 14:38:21 +03:00
package chatgpt
2025-07-12 14:57:59 +03:00
import (
"backend/config"
"bytes"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
)
2025-07-12 15:21:42 +03:00
type ResponseFormat struct {
Type string ` json:"type" `
}
2025-07-12 14:57:59 +03:00
type ChatRequest struct {
2025-07-12 15:21:42 +03:00
Model string ` json:"model" `
Messages [ ] ChatMessage ` json:"messages" `
ResponseFormat * ResponseFormat ` json:"response_format,omitempty" `
2025-07-12 14:57:59 +03:00
}
type ChatMessage struct {
Role string ` json:"role" `
Content string ` json:"content" `
}
2025-07-12 15:21:42 +03:00
type GeneratedCover struct {
Name string ` json:"name" `
Cover string ` json:"cover" `
}
func GenerateCoverLetter ( templateHTML string , jobHTML string ) ( GeneratedCover , error ) {
2025-07-12 14:57:59 +03:00
apiKey := config . Env [ "CHATGPT_KEY" ]
if apiKey == "" {
2025-07-12 15:21:42 +03:00
return GeneratedCover { } , errors . New ( "no API key chat gpt provided" )
2025-07-12 14:57:59 +03:00
}
payload := ChatRequest {
Model : "gpt-4o" , // o4-mini
2025-07-12 15:21:42 +03:00
ResponseFormat : & ResponseFormat { Type : "json_object" } ,
2025-07-12 14:57:59 +03:00
Messages : [ ] ChatMessage {
{
Role : "system" ,
2025-07-12 15:21:42 +03:00
Content : ` You are a helpful assistant that fills out cover letter templates in HTML format and provides a name for it. Replace all <...> tags like <company>, <experience>, etc., with appropriate content based on the job application. You must respond with a JSON object with two keys: "name" for the cover letter title (e.g., "Cover Letter for a Software Engineer"), and "cover" for the filled HTML cover letter. ` ,
2025-07-12 14:57:59 +03:00
} ,
{
Role : "user" ,
2025-07-12 15:21:42 +03:00
Content : fmt . Sprintf ( ` Template:
2025-07-12 14:57:59 +03:00
%s
Job description:
2025-07-12 15:21:42 +03:00
%s ` , templateHTML , jobHTML ) ,
2025-07-12 14:57:59 +03:00
} ,
} ,
}
body , err := json . Marshal ( payload )
if err != nil {
2025-07-12 15:21:42 +03:00
return GeneratedCover { } , err
2025-07-12 14:57:59 +03:00
}
req , err := http . NewRequest ( "POST" , "https://api.openai.com/v1/chat/completions" , bytes . NewBuffer ( body ) )
if err != nil {
2025-07-12 15:21:42 +03:00
return GeneratedCover { } , err
2025-07-12 14:57:59 +03:00
}
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 {
2025-07-12 15:21:42 +03:00
return GeneratedCover { } , err
2025-07-12 14:57:59 +03:00
}
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 {
2025-07-12 15:21:42 +03:00
return GeneratedCover { } , err
2025-07-12 14:57:59 +03:00
}
if len ( result . Choices ) == 0 {
2025-07-12 15:21:42 +03:00
return GeneratedCover { } , fmt . Errorf ( "no response from GPT" )
}
var cover GeneratedCover
if err := json . Unmarshal ( [ ] byte ( result . Choices [ 0 ] . Message . Content ) , & cover ) ; err != nil {
return GeneratedCover { } , fmt . Errorf ( "failed to parse GPT response: %w" , err )
2025-07-12 14:57:59 +03:00
}
2025-07-12 15:21:42 +03:00
return cover , nil
2025-07-12 14:57:59 +03:00
}