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 ¶
const ( BasisYourReceipts = "your_receipts" BasisYourSetting = "your_setting" BasisObserved = "observed" )
Price bases for StopPrice.Basis.
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:
- the user's own receipts at that network (their paid $/kWh, at least 3 sessions);
- a rate the user set themselves in Settings (not the seeded default);
- the third-party observed price (dcfcrates.ObservedFor: its own table, source and month, hidden after 12 months);
- 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 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.