rentease/internal/driver/parser/client.go

125 lines
3.6 KiB
Go

package parser
import (
"context"
"encoding/json"
"fmt"
"log"
"time"
"github.com/openai/openai-go"
"github.com/openai/openai-go/responses"
"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(),
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 OpenAI LLM parser")
ctx := context.Background()
prompt := p.systemPrompt + "\n" + rawContent
resp, err := p.llmClient.Responses.New(ctx, responses.ResponseNewParams{
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String(prompt)},
Model: p.model,
})
if err != nil {
return nil, fmt.Errorf("error sending request to OpenAI: %w", err)
}
var r struct {
Data ResponseData `json:"data"`
}
// The LLM is instructed to return the JSON for ResponseData directly, so parse it
err = json.Unmarshal([]byte(resp.OutputText()), &r)
if err != nil {
return nil, fmt.Errorf("error decoding response from OpenAI: %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
}