Skip to content

Compare changes

Choose two branches to see what one has that the other does not, then open a pull request for it.

Open a pull request

3 commits

9 files+74−90/9 viewed
+6−2
118118 **This month's usage**: what each kind of usage has come to so far, in
119119 dollars and in what was used (*Agents & sandboxes*, *Builds*, *Requests &
120120 CPU*, *Custom domains*, *Git operations & storage*, *Search & security
121−scans*). App traffic, custom domains, storage and git operations are
122−counted through the month and charged when it closes. **Manage on Stripe** opens the card, invoices and
121+scans*). Its **Total at price** is g1t's usage at cost plus 20%, before the
122+included usage, the trial, a pool or a free period paid their part, so it
123+can be more than what was charged. Runs on your own model provider are not
124+in it: your provider bills those. The workspace's **Usage** page shows what
125+was charged as **Spent**, and how much of the total was not charged. App traffic, custom domains, storage and git
126+operations are counted through the month and charged when it closes. **Manage on Stripe** opens the card, invoices and
123127 billing details. **End at the end of the period** ends the plan then, with
124128 nothing more charged after; **Keep the plan** takes that back until then.
125129 If a renewal payment fails, the card says **Payment failed**, with
+4−2
346346 ))}
347347 </ul>
348348 <div className="mt-1 flex items-baseline justify-between gap-4 border-t border-line pt-2.5 text-sm">
349− <span className="font-medium">Total</span>
349+ {/* At price, before the plan's included usage, the trial and the pools paid
350+ their part: Usage shows what was charged. */}
351+ <span className="font-medium">Total at price</span>
350352 <span className="font-medium tabular-nums">{dollars(total)}</span>
351353 </div>
352354 <p className="mt-2 text-xs text-faint">
354356 ? "What this workspace's usage would cost. g1t covers it."
355357 : on
356358 ? `Drawn from the included usage first, then charged up to your spend limit. Projects, previews and repositories are never charged, and the first ${freeStorage} of private storage and ${freeGit} git operations a month are free. App traffic, custom domains, storage and git operations are counted through the month and charged when it closes.`
357− : `The forge is free: ${freeStorage} of private storage and ${freeGit} git operations a month. Agents run from the trial or the open-source pool.`}
359+ : `The forge is free: ${freeStorage} of private storage and ${freeGit} git operations a month. Agents run from the trial or the open-source pool, which pay part of this total: Usage shows what was charged.`}
358360 </p>
359361 </div>
360362 );
+5−3
228228 label="Spent"
229229 value={dollars(usage.spentMicros)}
230230 note={
231− usage.providerMicros > 0
232− ? `Plus about ${dollars(usage.providerMicros)} billed by your own model provider`
233− : `${dollars(usage.costMicros)} of it the model provider's`
231+ (usage.coveredMicros ?? 0) > 0
232+ ? `Of ${dollars(usage.spentMicros + (usage.coveredMicros ?? 0))} at price: ${dollars(usage.coveredMicros ?? 0)} of it was not charged (the included usage, the trial, a pool, or a free period)`
233+ : usage.providerMicros > 0
234+ ? `Plus about ${dollars(usage.providerMicros)} billed by your own model provider`
235+ : `${dollars(usage.costMicros)} of it the model provider's`
234236 }
235237 />
236238 )}
+5−0
324324 pub since: String,
325325 /// Charged, including g1t's margin.
326326 pub spent_micros: i64,
327+ /// What g1t's usage came to at price, less what was charged: the plan's
328+ /// included usage, the trial, a pool or a free period paid it. Usage at
329+ /// price is `spent_micros` plus this.
330+ #[serde(default)]
331+ pub covered_micros: i64,
327332 /// What g1t's model provider charged, before the margin.
328333 pub cost_micros: i64,
329334 /// What runs on the workspace's own provider cost there, as the harness
+9−0
186186 (raw counts with the mapping) for `cost_operations`. Migration 0023 drops
187187 the `billable_units` table 0022 made for this, which was never written.
188188
189+**Charged at price** (a product's value) is what each day's usage was paid:
190+charged to a card or credit, or drawn from the plan's included usage, a
191+trial, a pool or a gift. Usage nothing paid for, as in a free period, is
192+valued at price (cost plus the margin), since it was given away at its price
193+rather than sold for nothing; so are g1t's own workspaces. Runs on a
194+workspace's own model provider have no cost to g1t. Every daily run
195+reconciles the whole 31-day window from what is already kept, so a change in
196+how a day is valued reaches every day sudo shows.
197+
189198 ## Alerts runbook
190199
191200 | Alert | Raised when | First steps |
+2−0
10141014 since: string;
10151015 /** Charged, including g1t's margin. */
10161016 spentMicros: number;
1017+ /** What g1t's usage came to at price, less what was charged: the plan's included usage, the trial, a pool or a free period paid it. Usage at price is `spentMicros` plus this. */
1018+ coveredMicros?: number;
10171019 /** What g1t's model provider charged, before the margin. */
10181020 costMicros: number;
10191021 /** What runs on the workspace's own provider cost there, estimated. Not charged by g1t. */
+8−0
428428 #[derive(serde::Deserialize)]
429429 struct Totals {
430430 spent: Option<i64>,
431+ g1t_cost: Option<i64>,
431432 cost: Option<i64>,
432433 provider: Option<i64>,
433434 runs: Option<u32>,
438439 .prepare(
439440 "SELECT
440441 -SUM(CASE WHEN kind = 'usage' THEN amount_micros END) AS spent,
442+ SUM(CASE WHEN kind = 'usage' AND COALESCE(billed_to, 'g1t') = 'g1t' THEN cost_micros END) AS g1t_cost,
441443 SUM(CASE WHEN kind = 'usage' AND COALESCE(billed_to, 'g1t') = 'g1t' THEN cost_micros END) AS cost,
442444 SUM(CASE WHEN kind = 'usage' AND billed_to = 'workspace' THEN cost_micros END) AS provider,
443445 SUM(CASE WHEN kind = 'usage' THEN 1 ELSE 0 END) AS runs,
463465 .await?;
464466 let totals = totals.unwrap_or(Totals {
465467 spent: None,
468+ g1t_cost: None,
466469 cost: None,
467470 provider: None,
468471 runs: None,
470473 });
471474 Ok(Outcome::Ok(Usage {
472475 spent_micros: totals.spent.unwrap_or_default(),
476+ // At price, what g1t's usage came to, less what was charged: the
477+ // part the included usage, the trial, a pool, or a free period paid.
478+ covered_micros: (crate::credits::with_margin(totals.g1t_cost.unwrap_or_default(), self.margin_percent)
479+ - totals.spent.unwrap_or_default())
480+ .max(0),
473481 cost_micros: totals.cost.unwrap_or_default(),
474482 provider_micros: totals.provider.unwrap_or_default(),
475483 used_micros: totals.cost.unwrap_or_default() + totals.provider.unwrap_or_default(),
+33−2
405405 /// Workspaces that cost g1t more than `factor` times what they paid, with
406406 /// at least `floor_micros` of cost: each (workspace, cost, revenue), the
407407 /// biggest gap first.
408+/// What a day's usage was worth at price. g1t's own workspaces are valued
409+/// at price. So is usage nothing paid for, neither charged nor drawn from
410+/// the plan, a trial, a pool or a gift (a free period): it was given away at
411+/// its price, not sold for nothing. Anything paid keeps what it was paid, so
412+/// a discount still shows as one.
413+pub(crate) fn usage_value(internal: bool, cost: i64, paid: i64, margin_percent: u32) -> i64 {
414+ if internal || (paid == 0 && cost > 0) {
415+ return crate::credits::with_margin(cost, margin_percent);
416+ }
417+ paid
418+}
419+
408420 /// What the overall alert says: the money as money, and a percentage only
409421 /// while there is enough coming in for one to mean something (a few cents
410422 /// against dollars of cost reads as -8000%).
597609 run.problems.push(format!("g1t's own counts could not be read: {error}"));
598610 }
599611 self.snapshot_pending(&until).await?;
600− run.days = self.reconcile_range(&since, &until).await?;
612+ // Reconciled over the whole window sudo shows, not only the days the
613+ // bill was read for: it reads only what is already kept, so a change
614+ // in how a day is valued reaches every day shown at the next run.
615+ let window = day_before(&until, costs::BACKFILL_DAYS - 1);
616+ let reconcile_from = if window < since { window } else { since.clone() };
617+ run.days = self.reconcile_range(&reconcile_from, &until).await?;
601618 let drift = self.find_drift(&until).await?;
602619 run.proposals = self.measure_units(&until).await?;
603620 self.apply_due_versions().await?;
668685 // to g1t. g1t's own workspaces are valued at price.
669686 let cost = if r.own_provider == 1 { 0 } else { r.cost.unwrap_or(0) };
670687 let cash = r.cash.unwrap_or(0);
671− let value = if r.internal == 1 { crate::credits::with_margin(cost, self.margin_percent) } else { cash + r.drawn.unwrap_or(0) };
688+ let paid = cash + r.drawn.unwrap_or(0);
689+ let value = usage_value(r.internal == 1, cost, paid, self.margin_percent);
672690 UsageRow { day: r.day, workspace: r.workspace, key: r.key, value, cash, cost }
673691 })
674692 .collect();
14731491 use super::*;
14741492
14751493 #[test]
1494+ fn usage_nothing_paid_for_is_valued_at_price_and_paid_usage_at_what_was_paid() {
1495+ // A free period: charged nothing, drawn from nothing.
1496+ assert_eq!(usage_value(false, 1_000_000, 0, 20), 1_200_000);
1497+ // Charged, or drawn from a trial: what was paid.
1498+ assert_eq!(usage_value(false, 1_000_000, 1_200_000, 20), 1_200_000);
1499+ assert_eq!(usage_value(false, 1_000_000, 900_000, 20), 900_000);
1500+ // g1t's own: at price.
1501+ assert_eq!(usage_value(true, 1_000_000, 0, 20), 1_200_000);
1502+ // No cost, nothing paid: nothing.
1503+ assert_eq!(usage_value(false, 0, 0, 20), 0);
1504+ }
1505+
1506+ #[test]
14761507 fn the_overall_alert_says_dollars_while_little_comes_in() {
14771508 let small = overall_detail(90_000, 7_500_000, 3, 10.0, -8239.7);
14781509 assert!(small.contains("took in $0.09 against $7.50"), "{small}");
+2−0
269269 "SELECT {METER_SQL} AS meter, COUNT(*) AS count, SUM(cost_micros) AS cost
270270 FROM ledger WHERE workspace = ?1 AND kind = 'usage' AND created_at >= ?2 AND created_at < ?3
271271 AND COALESCE(reference, '') NOT LIKE 'deployments/%'
272+ -- Runs on the workspace's own model provider are its provider's to bill, never g1t's.
273+ AND COALESCE(billed_to, 'g1t') = 'g1t'
272274 GROUP BY 1"
273275 ))
274276 .bind(&[workspace.as_str().into(), from.as_str().into(), until.as_str().into()])?