rentease/internal/driver/parser/client.go
Ruidy 05d4a31156
Some checks failed
CI / checks (push) Has been cancelled
fix(EX-0000): use chat completions API for DeepSeek
DeepSeek does not support the /responses endpoint.
Switch to /v1/chat/completions which is compatible.
2026-05-09 17:01:29 +02:00

133 lines
3.8 KiB
Go

package parser
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"time"
"github.com/openai/openai-go"
"github.com/openai/openai-go/option"
"github.com/openai/openai-go/shared"
"github.com/rjNemo/rentease/internal/service/booking"
)
type BookingAgentParser struct {
systemPrompt string
llmClient openai.Client
model string
}
func NewBookingAgentParser(model string) *BookingAgentParser {
return &BookingAgentParser{
llmClient: openai.NewClient(
option.WithBaseURL("https://api.deepseek.com"),
option.WithAPIKey(os.Getenv("DEEPSEEK_API_KEY")),
),
model: model,
systemPrompt: ` Extract the following fields from the booking content and return a JSON object with this exact structure (all fields required, use null or empty string if not available):
{
"data": {
"arrival_date": "YYYY-MM-DD",
"booking_fees": "number as string",
"booking_id": "string",
"departure_date": "YYYY-MM-DD",
"stay_length": int,
"guest_email": "string",
"guest_name": "string",
"guest_number": int,
"guest_phone": "string",
"room_booked": "string",
"standard_rate": "number as string",
"special_requests": "string"
}
}
Respond ONLY with the JSON object above, wrapped in a top-level "data" field as shown, no code fences.
Booking content:`,
}
}
// ResponseData is the expected structure returned by the LLM parser.
type ResponseData struct {
ArrivalDate string `json:"arrival_date"`
BookingFees json.Number `json:"booking_fees"`
BookingID string `json:"booking_id"`
DepartureDate string `json:"departure_date"`
StayLength int `json:"stay_length"`
GuestEmail string `json:"guest_email"`
GuestName string `json:"guest_name"`
GuestNumber int `json:"guest_number"`
GuestPhone string `json:"guest_phone"`
RoomBooked string `json:"room_booked"`
StandardRate json.Number `json:"standard_rate"`
SpecialRequests string `json:"special_requests"`
}
func (p *BookingAgentParser) Parse(rawContent string) (*booking.Booking, error) {
log.Println("sending request to DeepSeek LLM parser")
ctx := context.Background()
resp, err := p.llmClient.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
Model: p.model,
Messages: []openai.ChatCompletionMessageParamUnion{
openai.SystemMessage(p.systemPrompt),
openai.UserMessage(rawContent),
},
ResponseFormat: openai.ChatCompletionNewParamsResponseFormatUnion{
OfJSONObject: &shared.ResponseFormatJSONObjectParam{},
},
})
if err != nil {
return nil, fmt.Errorf("error sending request to DeepSeek: %w", err)
}
var r struct {
Data ResponseData `json:"data"`
}
err = json.Unmarshal([]byte(resp.Choices[0].Message.Content), &r)
if err != nil {
return nil, fmt.Errorf("error decoding response from DeepSeek: %w", err)
}
var b booking.Booking
b.From, err = time.Parse("2006-01-02", r.Data.ArrivalDate)
if err != nil {
return nil, fmt.Errorf("error parsing arrival_date: %w", err)
}
b.To, err = time.Parse("2006-01-02", r.Data.DepartureDate)
if err != nil {
return nil, fmt.Errorf("error parsing departure_date: %w", err)
}
b.Name = r.Data.GuestName
b.PhoneNumber = r.Data.GuestPhone
b.Email = r.Data.GuestEmail
b.CustomerNumber = r.Data.GuestNumber
bookingFees, err := r.Data.BookingFees.Float64()
if err != nil {
return nil, fmt.Errorf("error parsing booking fees: %w", err)
}
b.Platform = "Booking"
b.PlatformFees = bookingFees
b.ExternalID = &r.Data.BookingID
price, err := r.Data.StandardRate.Float64()
if err != nil {
return nil, fmt.Errorf("error parsing standard rate: %w", err)
}
b.Items = append(b.Items, booking.Item{
Item: r.Data.RoomBooked,
Quantity: r.Data.StayLength,
Price: price,
PaymentMethod: "Card",
})
return &b, nil
}