mirror of
https://github.com/rjNemo/rentease.git
synced 2026-06-06 02:36:49 +00:00
chore: wire APP_OPENAI_MODEL and upgrade Go/deps
This commit is contained in:
parent
584d81f7bd
commit
df1cd66cf1
13 changed files with 69 additions and 71 deletions
23
.env.example
23
.env.example
|
|
@ -1,11 +1,12 @@
|
||||||
DATABASE_URL=
|
APP_DATABASE_URL=
|
||||||
DEBUG=
|
APP_DEBUG=
|
||||||
LOG_LEVEL=
|
APP_LOG_LEVEL=
|
||||||
ORIGINS=
|
APP_ORIGINS=
|
||||||
PORT=
|
APP_PORT=
|
||||||
SENTRY_DSN=
|
APP_SENTRY_DSN=
|
||||||
ADMIN=
|
APP_ADMIN=
|
||||||
ADMIN_SECRET=
|
APP_ADMIN_SECRET=
|
||||||
API_KEY=
|
APP_API_KEY=
|
||||||
SECRET_KEY=
|
APP_SECRET_KEY=
|
||||||
SESSION_SECRET=
|
APP_SESSION_SECRET=
|
||||||
|
APP_OPENAI_MODEL=gpt-5-nano
|
||||||
|
|
|
||||||
69
AGENTS.md
69
AGENTS.md
|
|
@ -1,52 +1,43 @@
|
||||||
# Repository Guidelines
|
# Repository Guidelines
|
||||||
|
|
||||||
## Project Structure & Module Organization
|
## Project Structure & Module Organization
|
||||||
|
- `main.go`: application entrypoint (HTTP server).
|
||||||
- `main.go`: Application entrypoint (HTTP server on `:8000`).
|
- `internal/`: core app code by layer:
|
||||||
- `internal/`: Private app code (e.g., `server/`, `service/`, `repository/`, `driver/`,
|
- `server/` (HTTP handlers/routes), `service/` (business logic), `repository/` (data access), `driver/` (external integrations), `config/` (env config), `view/` (templ views/viewmodels).
|
||||||
`config/`, `view/`). Templ views live in `internal/view` and generate `*_templ.go`.
|
- `internal/view/*.templ` are source templates; generated files are `internal/view/*_templ.go`.
|
||||||
- `pkg/`: Reusable packages shared across app layers.
|
- `cmd/cron/`: cron entrypoint.
|
||||||
- `cmd/`: Optional binaries/entrypoints.
|
- `assets/`: static assets. `docs/`: documentation/images. `scripts/` and `tmp/`: tooling/artifacts.
|
||||||
- `assets/`: Static assets and images.
|
- Tests live next to code as `*_test.go`.
|
||||||
- `docs/`: Project documentation.
|
|
||||||
- `scripts/`, `tmp/`: Dev tooling and build artifacts (Air uses `tmp/`).
|
|
||||||
|
|
||||||
## Build, Test, and Development Commands
|
## Build, Test, and Development Commands
|
||||||
|
- `make dev`: run local dev stack with Docker Compose and hot reload.
|
||||||
- `make dev`: Start dev container with live reload (Air) on `http://localhost:8000`.
|
- `make run`: build and run production image locally.
|
||||||
- `make run`: Build and run the Docker image with `PORT` and `DATABASE_URL`.
|
- `make test`: run `go test ./...`.
|
||||||
- `make test`: Run `go test ./...` inside the running dev container.
|
- `make format`: run `templ generate`, `templ fmt`, and `go fmt`.
|
||||||
- `make format`: Run `templ generate`, `templ fmt`, and `go fmt`.
|
- `make lint`: run `golangci-lint run ./...`.
|
||||||
- `make lint`: Run `golangci-lint`.
|
- `make stop`: stop dev containers.
|
||||||
- Local alternative: `air -c .air.toml`, `go test ./...`, `go run .`.
|
- Local (without Docker): `go run .`, `go test ./...`.
|
||||||
|
|
||||||
## Coding Style & Naming Conventions
|
## Coding Style & Naming Conventions
|
||||||
|
- Go style is standard `go fmt` (tabs, canonical imports).
|
||||||
- Go formatting: Use `go fmt` (tabs, standard import ordering). CI helpers:
|
- Package names: lowercase, no underscores.
|
||||||
`make format`.
|
- File names: lowercase with underscores when needed.
|
||||||
- Linting: `golangci-lint run ./...` via `make lint` (fix issues before PR).
|
- Exported identifiers: `PascalCase`; unexported: `camelCase`.
|
||||||
- Packages: lowercase, no underscores; files: lowercase with underscores; exported
|
- Keep handler/controller code thin; place business rules in `internal/service`.
|
||||||
identifiers: `PascalCase`; unexported: `camelCase`.
|
|
||||||
- Templ: keep `.templ` in `internal/view`; commit sources, not generated `*_templ.go`.
|
|
||||||
|
|
||||||
## Testing Guidelines
|
## Testing Guidelines
|
||||||
|
- Use Go `testing` with table-driven tests where practical.
|
||||||
- Framework: standard `testing` with table-driven tests.
|
- Name files `*_test.go`; tests `TestXxx`; benchmarks `BenchmarkXxx`.
|
||||||
- Files: `*_test.go`; functions: `TestXxx`, benchmarks: `BenchmarkXxx`.
|
- Cover success and failure paths for changed logic.
|
||||||
- Run: `make test` (in container) or `go test ./...` locally.
|
- Run `make test` (or `go test ./...`) before opening a PR.
|
||||||
- Aim for meaningful coverage on new/changed code; include error paths.
|
|
||||||
|
|
||||||
## Commit & Pull Request Guidelines
|
## Commit & Pull Request Guidelines
|
||||||
|
- Prefer Conventional Commits: `feat(scope): ...`, `fix(scope): ...`, `chore: ...`.
|
||||||
- Messages: Prefer Conventional Commits (e.g., `feat(parser): ...`,
|
- Keep commits focused and atomic.
|
||||||
`fix(config): ...`). Short, imperative first line; scope optional.
|
- PRs should include: clear summary, linked issue (e.g., `#51`), and screenshots for UI changes.
|
||||||
- PRs: Provide clear description, link issues (e.g., `#45`), include screenshots
|
- Call out config/env changes and any migration or deployment impact.
|
||||||
for UI, and note breaking changes/migrations.
|
|
||||||
- Keep diffs focused; run `make format` and `make lint` before opening.
|
|
||||||
|
|
||||||
## Security & Configuration Tips
|
## Security & Configuration Tips
|
||||||
|
- Config is environment-driven with `APP_` prefix (see `internal/config/config.go`).
|
||||||
- Environment: Use `.env`/`prod.env`; never commit secrets.
|
- Example parser model override: `APP_OPENAI_MODEL=gpt-5-nano`.
|
||||||
Example: `DATABASE_URL="host=... user=... database=..."`.
|
- Never commit secrets; keep them in local `.env` / deployment secret manager.
|
||||||
- Ports: default `8000`; configure via `PORT`.
|
|
||||||
- Dependencies: use `go mod tidy` and `make up-deps` when updating.
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
FROM golang:1.25-alpine AS builder
|
FROM golang:1.26-alpine AS builder
|
||||||
|
|
||||||
RUN apk update && apk add --no-cache \
|
RUN apk update && apk add --no-cache \
|
||||||
build-base \
|
build-base \
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
# ----------- Builder Stage -----------
|
# ----------- Builder Stage -----------
|
||||||
FROM golang:1.25-alpine AS builder
|
FROM golang:1.26-alpine AS builder
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN apk add --no-cache build-base
|
RUN apk add --no-cache build-base
|
||||||
|
|
@ -22,7 +22,7 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
|
||||||
go build -ldflags="-s -w" -o rentease main.go
|
go build -ldflags="-s -w" -o rentease main.go
|
||||||
|
|
||||||
# ----------- Dev Stage -----------
|
# ----------- Dev Stage -----------
|
||||||
FROM golang:1.25-alpine AS dev
|
FROM golang:1.26-alpine AS dev
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install runtime dependencies
|
# Install runtime dependencies
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ APP_ORIGINS=http://localhost:8000
|
||||||
APP_PORT=8000
|
APP_PORT=8000
|
||||||
APP_DEBUG=true
|
APP_DEBUG=true
|
||||||
APP_LOG_LEVEL=debug
|
APP_LOG_LEVEL=debug
|
||||||
|
APP_OPENAI_MODEL=gpt-5-nano
|
||||||
APP_STRIPE_SECRET_KEY=
|
APP_STRIPE_SECRET_KEY=
|
||||||
APP_STRIPE_WEBHOOK_SECRET=
|
APP_STRIPE_WEBHOOK_SECRET=
|
||||||
APP_SENTRY_DSN=
|
APP_SENTRY_DSN=
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ services:
|
||||||
APP_LOG_LEVEL: debug
|
APP_LOG_LEVEL: debug
|
||||||
APP_PORT: 8000
|
APP_PORT: 8000
|
||||||
APP_ORIGINS: http://localhost:8000
|
APP_ORIGINS: http://localhost:8000
|
||||||
|
APP_OPENAI_MODEL: gpt-5-nano
|
||||||
APP_DATABASE_URL: postgres://rentease:rentease@db:5432/rentease?sslmode=disable
|
APP_DATABASE_URL: postgres://rentease:rentease@db:5432/rentease?sslmode=disable
|
||||||
APP_ADMIN: admin@example.com
|
APP_ADMIN: admin@example.com
|
||||||
APP_ADMIN_SECRET: supersecret
|
APP_ADMIN_SECRET: supersecret
|
||||||
|
|
|
||||||
10
go.mod
10
go.mod
|
|
@ -1,11 +1,11 @@
|
||||||
module github.com/rjNemo/rentease
|
module github.com/rjNemo/rentease
|
||||||
|
|
||||||
go 1.25.4
|
go 1.26
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/a-h/templ v0.3.977
|
github.com/a-h/templ v0.3.977
|
||||||
github.com/getsentry/sentry-go v0.40.0
|
github.com/getsentry/sentry-go v0.42.0
|
||||||
github.com/go-chi/chi/v5 v5.2.3
|
github.com/go-chi/chi/v5 v5.2.5
|
||||||
github.com/go-chi/cors v1.2.2
|
github.com/go-chi/cors v1.2.2
|
||||||
github.com/gorilla/sessions v1.4.0
|
github.com/gorilla/sessions v1.4.0
|
||||||
github.com/joho/godotenv v1.5.1
|
github.com/joho/godotenv v1.5.1
|
||||||
|
|
@ -36,6 +36,6 @@ require (
|
||||||
github.com/openai/openai-go v1.12.0
|
github.com/openai/openai-go v1.12.0
|
||||||
github.com/sethvargo/go-envconfig v1.3.0
|
github.com/sethvargo/go-envconfig v1.3.0
|
||||||
golang.org/x/sync v0.19.0 // indirect
|
golang.org/x/sync v0.19.0 // indirect
|
||||||
golang.org/x/sys v0.40.0 // indirect
|
golang.org/x/sys v0.41.0 // indirect
|
||||||
golang.org/x/text v0.32.0
|
golang.org/x/text v0.34.0
|
||||||
)
|
)
|
||||||
|
|
|
||||||
16
go.sum
16
go.sum
|
|
@ -5,10 +5,10 @@ github.com/a-h/templ v0.3.977/go.mod h1:oCZcnKRf5jjsGpf2yELzQfodLphd2mwecwG4Crk5
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/getsentry/sentry-go v0.40.0 h1:VTJMN9zbTvqDqPwheRVLcp0qcUcM+8eFivvGocAaSbo=
|
github.com/getsentry/sentry-go v0.42.0 h1:eeFMACuZTbUQf90RE8dE4tXeSe4CZyfvR1MBL7RLEt8=
|
||||||
github.com/getsentry/sentry-go v0.40.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s=
|
github.com/getsentry/sentry-go v0.42.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s=
|
||||||
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
|
github.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=
|
||||||
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
|
github.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=
|
||||||
github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
|
github.com/go-chi/cors v1.2.2 h1:Jmey33TE+b+rB7fT8MUy1u0I4L+NARQlK6LhzKPSyQE=
|
||||||
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
github.com/go-chi/cors v1.2.2/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
|
||||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||||
|
|
@ -75,10 +75,10 @@ golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||||
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
|
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||||
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ type Config struct {
|
||||||
Port int `env:"PORT, default=4200"`
|
Port int `env:"PORT, default=4200"`
|
||||||
// SentryDsn is the DSN for Sentry error reporting
|
// SentryDsn is the DSN for Sentry error reporting
|
||||||
SentryDsn string `env:"SENTRY_DSN"`
|
SentryDsn string `env:"SENTRY_DSN"`
|
||||||
|
// OpenAIModel is the OpenAI model used by the booking parser
|
||||||
|
OpenAIModel string `env:"OPENAI_MODEL, default=gpt-5-nano"`
|
||||||
// Auth
|
// Auth
|
||||||
// Admin is the email used to access the admin panel
|
// Admin is the email used to access the admin panel
|
||||||
Admin string `env:"ADMIN, required"`
|
Admin string `env:"ADMIN, required"`
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,13 @@ import (
|
||||||
type BookingAgentParser struct {
|
type BookingAgentParser struct {
|
||||||
systemPrompt string
|
systemPrompt string
|
||||||
llmClient openai.Client
|
llmClient openai.Client
|
||||||
|
model string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBookingAgentParser() *BookingAgentParser {
|
func NewBookingAgentParser(model string) *BookingAgentParser {
|
||||||
return &BookingAgentParser{
|
return &BookingAgentParser{
|
||||||
llmClient: openai.NewClient(),
|
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):
|
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):
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -71,7 +73,7 @@ func (p *BookingAgentParser) Parse(rawContent string) (*booking.Booking, error)
|
||||||
|
|
||||||
resp, err := p.llmClient.Responses.New(ctx, responses.ResponseNewParams{
|
resp, err := p.llmClient.Responses.New(ctx, responses.ResponseNewParams{
|
||||||
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String(prompt)},
|
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String(prompt)},
|
||||||
Model: openai.ChatModelChatgpt4oLatest,
|
Model: p.model,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error sending request to OpenAI: %w", err)
|
return nil, fmt.Errorf("error sending request to OpenAI: %w", err)
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,7 @@ func TestBookingAgentParser_Parse(t *testing.T) {
|
||||||
Les conversations avec vos clients apparaîtront ici.
|
Les conversations avec vos clients apparaîtront ici.
|
||||||
Booking.com reçoit tous les messages écrits ici et les traite selon sa Charte de confidentialité et informations sur les cookies Conditions `
|
Booking.com reçoit tous les messages écrits ici et les traite selon sa Charte de confidentialité et informations sur les cookies Conditions `
|
||||||
|
|
||||||
parser := NewBookingAgentParser()
|
parser := NewBookingAgentParser("gpt-5-nano")
|
||||||
booking, err := parser.Parse(input)
|
booking, err := parser.Parse(input)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Parse failed: %v", err)
|
t.Fatalf("Parse failed: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -165,7 +165,7 @@ func buildPaymentLinkCreateParams(params CreatePaymentLinkParams) (*stripe.Payme
|
||||||
ProductData: &stripe.PaymentLinkCreateLineItemPriceDataProductDataParams{
|
ProductData: &stripe.PaymentLinkCreateLineItemPriceDataProductDataParams{
|
||||||
Name: stripe.String(strings.TrimSpace(params.Description)),
|
Name: stripe.String(strings.TrimSpace(params.Description)),
|
||||||
},
|
},
|
||||||
UnitAmount: stripe.Int64(amountCents),
|
UnitAmount: new(amountCents),
|
||||||
},
|
},
|
||||||
Quantity: stripe.Int64(1),
|
Quantity: stripe.Int64(1),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
2
main.go
2
main.go
|
|
@ -71,7 +71,7 @@ func run(ctx context.Context, appConfig *config.Config, appLogger *slog.Logger)
|
||||||
return fmt.Errorf("error starting pdf client %w", err)
|
return fmt.Errorf("error starting pdf client %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsingClient := parser.NewBookingAgentParser()
|
parsingClient := parser.NewBookingAgentParser(appConfig.OpenAIModel)
|
||||||
|
|
||||||
var stripeClient payment.StripeClient
|
var stripeClient payment.StripeClient
|
||||||
if appConfig.StripeSecretKey != "" {
|
if appConfig.StripeSecretKey != "" {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue