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 } func NewBookingAgentParser() *BookingAgentParser { return &BookingAgentParser{ llmClient: openai.NewClient(), 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: openai.ChatModelChatgpt4oLatest, }) 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 }