tripadvice

package
v0.38.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 22, 2026 License: MIT Imports: 9 Imported by: 0

Documentation

Overview

Package tripadvice wraps the operator-configured AI provider to generate plain-language observations about a Rivian trip plan: charging stop summary, arrival SoC commentary, adapter warnings, and drive-mode suggestions when the plan looks sub-optimal.

Structure mirrors internal/recap: side-effect-free buildPrompt, stateless Generate, structured JSON output the SPA renders as a card below the route table.

Index

Constants

View Source
const (
	BasisYourReceipts = "your_receipts"
	BasisYourSetting  = "your_setting"
	BasisObserved     = "observed"
)

Price bases for StopPrice.Basis.

View Source
const DefaultDCFCRateUSD = 0.46

DefaultDCFCRateUSD is the per-kWh fallback used in the rare case where the cost estimator runs without a route (pre-network-table API consumers, tests). The real per-stop pricing flows through the Networks table in networks.go.

Variables

This section is empty.

Functions

This section is empty.

Types

type Context

type Context struct {
	// DriveMode is the mode the user explicitly chose, or "" for
	// "let the planner pick". Included so the model can say "you
	// planned in Conserve mode" rather than treating it as a mystery.
	DriveMode   string
	StartingSoC float64
	HasAdapter  bool
	// Weather at the origin at departure time. nil when the operator
	// has not enabled the weather feature (recap.weather_enabled).
	Weather *weather.Snapshot
	// TirePressurebars holds [FL, FR, RL, RR] in bar from the live
	// vehicle state. Zero values mean unavailable for that corner.
	TirePressureBars [4]float64
	// PackKWh is the observed battery capacity in kWh from the vehicle
	// record. 0 means unknown.
	PackKWh float64
	// TirePlacardPSI is the user-configured door-jamb cold-fill
	// pressure from the vehicle profile. 0 means unconfigured; the
	// prompt then falls back to a generic "below placard" framing
	// instead of citing a specific number.
	TirePlacardPSI float64
	// HomePricePerKWh is the user's at-home charging cost in
	// HomeCurrency (typically USD). 0 means unconfigured — the cost
	// section degrades to a pure-DCFC estimate.
	HomePricePerKWh float64
	HomeCurrency    string
	// GasPricePerGallon × distance / ComparisonMPG drives the gas-
	// equivalent chip. Both must be > 0 for the chip to render.
	GasPricePerGallon float64
	ComparisonMPG     float64
	// DCFCNetworks is the user's edited price book from Settings.
	// Pass nil or empty to use the built-in defaults from dcfcrates.
	// Per-stop rates honour each row's MemberActive flag so the
	// estimate's user-totals match what the user has actually
	// signed up for.
	DCFCNetworks []dcfcrates.NetworkOverride
	// OwnRates is what the user actually paid per kWh at each network (slug), from
	// their own receipts, with how many sessions it rests on. The caller supplies
	// only figures that meet the minimum (receipts.PaidMinSessions) in this
	// context's currency. nil when the user has no receipts.
	OwnRates map[string]OwnRate
	// Now is the clock for the observed-price expiry; zero means time.Now().
	Now time.Time
}

Context bundles user-supplied labels and request parameters that would otherwise be unavailable in the plan response itself.

Privacy: what the model receives about places is limited to charger names, and locations rounded to weather.CoarseRound (0.1 deg, ~11 km). The typed origin/destination text (which may be a street address) is deliberately not part of Context, and no full-precision coordinate is ever written into the prompt.

type CostEstimate

type CostEstimate struct {
	Currency string `json:"currency"`
	// DCFCSpendLow and DCFCSpendHigh sum the priced stops' cost range. Equal when no
	// priced stop has a range; both 0 when no stop has a price (see PricedStops).
	DCFCSpendLow  float64 `json:"dcfc_spend_low"`
	DCFCSpendHigh float64 `json:"dcfc_spend_high"`
	// PricedStops and UnpricedStops count the DC fast stops with and without a price,
	// so the UI can say "partial: 2 of 3 stops priced".
	PricedStops   int `json:"priced_stops"`
	UnpricedStops int `json:"unpriced_stops"`
	// EstimateBasis is which sources fed the figure: "your_receipts", "your_setting",
	// "observed", "mixed" (more than one of them) or "none" (no stop priced). The UI
	// words the estimate from it ("estimate (typical observed price)" when observed or
	// mixed).
	EstimateBasis  string  `json:"estimate_basis"`
	HomeEquivalent float64 `json:"home_equivalent"`
	// GasEquivalent is what the same distance would cost in an ICE
	// vehicle at the user's configured gas price + MPG. Zero when
	// either input is unset.
	GasEquivalent     float64 `json:"gas_equivalent"`
	GasPricePerGallon float64 `json:"gas_price_per_gallon"`
	GasMPG            float64 `json:"gas_mpg"`
	// HomeRateUsed is the user's configured at-home rate, 0 when
	// unconfigured.
	HomeRateUsed float64 `json:"home_rate_used"`
	// Breakdown lists each DC fast stop. Empty when no DCFC stops
	// were on the route (all-from-home trip).
	Breakdown []CostStop `json:"breakdown,omitempty"`
}

CostEstimate is a deterministic, code-computed projection of trip cost. It is an ESTIMATE and says so: the dollar figures for the DC fast stops are a range (DCFCSpendLow to DCFCSpendHigh) over the stops that have a price, and a stop with none is counted in UnpricedStops, not given a made-up number. Where each stop's price comes from is on the stop (CostStop.Price.Basis), in order:

  1. the user's own receipts at that network (their paid $/kWh, at least 3 sessions);
  2. a rate the user set themselves in Settings (not the seeded default);
  3. the third-party observed price (dcfcrates.ObservedFor: its own table, source and month, hidden after 12 months);
  4. none: "price shown at the charger".

The built-in table's dated guest and member rates are never used for a dollar figure. HomeEquivalent and GasEquivalent are unchanged.

func EstimateRoute added in v0.18.85

func EstimateRoute(r *rivian.TripRoute, tc Context) CostEstimate

EstimateRoute projects DCFC spend + home-rate-equivalent for a single route. Exported so the /api/trips/plan handler can attach a cost to every route in the response without making the SPA round- trip through the AI advice endpoint just to see numbers.

type CostStop added in v0.18.83

type CostStop struct {
	NetworkSlug string  `json:"network_slug"`
	NetworkName string  `json:"network_name"`
	ChargerName string  `json:"charger_name"`
	EnergyKWh   float64 `json:"energy_kwh"`
	MemberPlan  string  `json:"member_plan,omitempty"`
	// Price is nil when there is none for this stop: "price shown at the charger".
	Price *StopPrice `json:"price"`
	// CostLow and CostHigh are EnergyKWh times the price range, nil with no price.
	CostLow  *float64 `json:"cost_low"`
	CostHigh *float64 `json:"cost_high"`
}

CostStop is one waypoint's contribution to the DC fast total.

type OwnRate added in v0.38.7

type OwnRate struct {
	PerKWh   float64
	Sessions int
}

OwnRate is the user's own paid price at a network.

type Parsed

type Parsed struct {
	Headline   string   `json:"headline"`
	Cost       []string `json:"cost"`
	Efficiency []string `json:"efficiency"`
	Weather    []string `json:"weather"`
	Vehicle    []string `json:"vehicle"`
}

Parsed is the structured JSON shape the model emits. Each list holds 1-3 short sentences; sections with nothing useful to say stay empty rather than emitting filler.

type Result

type Result struct {
	// Raw is the model's reply, stored and forwarded as-is.
	Raw          string
	Parsed       *Parsed
	Model        string
	InputTokens  int64
	OutputTokens int64
	// Cost is computed deterministically in Go (not by the LLM) so
	// the dollar figure is always accurate. The LLM gets the same
	// numbers in the prompt and writes the commentary around them.
	Cost CostEstimate
}

Result is what Generate returns to the handler.

func Generate

func Generate(ctx context.Context, a *ai.Analyzer, plan *rivian.TripPlan, tc Context) (Result, error)

Generate calls the AI provider with a compact trip-plan summary and returns structured advice. The context's deadline should be generous (30s+) — sit in the AI-bound route group, not the timed one.

type StopPrice added in v0.38.7

type StopPrice struct {
	Basis      string  `json:"basis"`
	PerKWhLow  float64 `json:"per_kwh_low"`
	PerKWhHigh float64 `json:"per_kwh_high"`
	// LowNote and HighNote say what each end of a range is (observed Tesla: with and
	// without the membership); empty when the ends are equal.
	LowNote  string `json:"low_note,omitempty"`
	HighNote string `json:"high_note,omitempty"`
	// Sessions is how many of the user's receipts a your_receipts figure rests on.
	Sessions int `json:"sessions,omitempty"`
	// Source, SourceURL and ObservedMonth name the third party behind an observed figure.
	Source        string `json:"source,omitempty"`
	SourceURL     string `json:"source_url,omitempty"`
	ObservedMonth string `json:"observed_month,omitempty"`
	// Label is the exact wording to show with the figure.
	Label string `json:"label"`
}

StopPrice is the price used for one stop and where it came from.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL