Skip to content

Commit ae1a88e

Browse files
committed
Add promotional credit notifications and bump version
1 parent 24c3029 commit ae1a88e

File tree

3 files changed

+84
-3
lines changed

3 files changed

+84
-3
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ name = "weco"
88
authors = [{ name = "Weco AI Team", email = "contact@weco.ai" }]
99
description = "Documentation for `weco`, a CLI for using Weco AI's code optimizer."
1010
readme = "README.md"
11-
version = "0.3.15"
11+
version = "0.3.16"
1212
license = { file = "LICENSE" }
1313
requires-python = ">=3.8"
1414
dependencies = [

weco/cli.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,11 @@ def execute_run_command(args: argparse.Namespace) -> None:
294294
if api_keys:
295295
console.print(f"[bold yellow]Custom API keys provided. Using default model: {model} for the run.[/]")
296296

297+
# Check for promotional credits and prompt user if applicable
298+
from .credits import check_promotional_credits
299+
300+
model = check_promotional_credits(model, api_keys, console)
301+
297302
# Send run attempt event before starting (helps measure dropoff before server)
298303
send_event(
299304
RunStartAttemptedEvent(

weco/credits.py

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,82 @@
33
import webbrowser
44
import requests
55
from rich.console import Console
6+
from rich.prompt import Confirm
67
from rich.table import Table
78
from . import __base_url__
89
from .api import handle_api_error
910
from .auth import handle_authentication
1011
from .config import load_weco_api_key
1112

1213

14+
def check_promotional_credits(model: str, api_keys: dict[str, str] | None, console: Console) -> str:
15+
"""Check for promotional credits and prompt user to use them if applicable.
16+
17+
Returns the (possibly modified) model string with provider prefix.
18+
"""
19+
# Skip if user is using their own API keys (BYOK — not billed)
20+
if api_keys:
21+
return model
22+
23+
# If model already has provider prefix, inform user about matching credits
24+
if "/" in model:
25+
provider = model.split("/", 1)[0]
26+
try:
27+
weco_api_key = load_weco_api_key()
28+
if not weco_api_key:
29+
return model
30+
resp = requests.get(
31+
f"{__base_url__}/billing/balance", headers={"Authorization": f"Bearer {weco_api_key}"}, timeout=5
32+
)
33+
if resp.ok:
34+
promo_credits = resp.json().get("promotional_credits", [])
35+
matching = [g for g in promo_credits if g.get("provider") == provider]
36+
if matching:
37+
total = sum(g.get("remaining_credits", 0) for g in matching)
38+
expires = matching[0].get("expires_at", "")[:10]
39+
console.print(
40+
f"[green]✅ Using {provider.capitalize()} promotional credits "
41+
f"(${total:.2f} remaining, expires {expires})[/]"
42+
)
43+
except Exception:
44+
pass # Non-critical; don't block the run
45+
return model
46+
47+
# Bare model name — check for promotional credits
48+
try:
49+
weco_api_key = load_weco_api_key()
50+
if not weco_api_key:
51+
return model
52+
resp = requests.get(f"{__base_url__}/billing/balance", headers={"Authorization": f"Bearer {weco_api_key}"}, timeout=5)
53+
if not resp.ok:
54+
return model
55+
56+
promo_credits = resp.json().get("promotional_credits", [])
57+
if not promo_credits:
58+
return model
59+
60+
# Check if any grant provider could serve this model
61+
for grant in promo_credits:
62+
grant_provider = grant.get("provider", "")
63+
remaining = grant.get("remaining_credits", 0)
64+
expires = grant.get("expires_at", "")[:10]
65+
66+
# Suggest using the grant's provider
67+
prefixed_model = f"{grant_provider}/{model}"
68+
console.print(
69+
f"\n[yellow]💡 You have ${remaining:.2f} in {grant_provider.capitalize()} credits (expires {expires}).[/]"
70+
)
71+
use_credits = Confirm.ask(f" Use [bold]{prefixed_model}[/] to apply them?", default=True)
72+
if use_credits:
73+
console.print(f"[green]✅ Using {grant_provider.capitalize()} promotional credits[/]\n")
74+
return prefixed_model
75+
76+
except Exception:
77+
pass # Non-critical; don't block the run
78+
79+
return model
80+
81+
1382
def handle_credits_command(args, console: Console) -> None:
1483
"""Handle the credits command and its subcommands."""
1584
# Ensure user is authenticated
@@ -55,8 +124,15 @@ def check_balance(console: Console, auth_headers: dict) -> None:
55124

56125
console.print(table)
57126

58-
if balance < 10:
59-
console.print("\n[yellow]💡 Tip: You're running low on credits. Run 'weco credits topup' to add more.[/]")
127+
# Show promotional credits if any
128+
promo_credits = data.get("promotional_credits", [])
129+
if promo_credits:
130+
console.print("\n[bold cyan]Promotional Credits:[/]")
131+
for grant in promo_credits:
132+
provider = grant.get("provider", "Unknown")
133+
remaining = grant.get("remaining_credits", 0)
134+
expires_at = grant.get("expires_at", "")[:10] # Date portion only
135+
console.print(f" {provider.capitalize()} models: [green]${remaining:.2f}[/] (expires {expires_at})")
60136

61137
except requests.exceptions.HTTPError as e:
62138
if e.response.status_code == 401:

0 commit comments

Comments
 (0)