feat: cloud adapters E2B/Modal and billing export (SAND-WP-0010)
Add credentialed E2B and Modal extensions, burst routing fallback, fin-hub meter export hook, BYOK docs, and 77 tests.
This commit is contained in:
parent
6d0a1a8b1e
commit
15f031fd65
26 changed files with 859 additions and 75 deletions
|
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
|||
|
||||
import os
|
||||
|
||||
from sandboxer.extensions.credentials import credentials_available
|
||||
from sandboxer.extensions.registry import load_extension
|
||||
from sandboxer.models import Extension, Profile, RouteStrategy
|
||||
from sandboxer.payments.metering import estimate_cost
|
||||
|
|
@ -20,6 +21,49 @@ def _is_metered(ext: Extension) -> bool:
|
|||
return ext.capabilities.pricing_model == "metered"
|
||||
|
||||
|
||||
def _metered_available(ext: Extension) -> bool:
|
||||
if ext.id == "ext.saas-stub":
|
||||
return True
|
||||
return credentials_available(ext.id, ext.config)
|
||||
|
||||
|
||||
def _select_metered_fallback(
|
||||
loaded: list[Extension],
|
||||
profile: Profile,
|
||||
inputs: dict[str, str],
|
||||
*,
|
||||
duration_s: int,
|
||||
) -> Extension | None:
|
||||
"""Pick cheapest credentialed metered extension; stub is always last resort."""
|
||||
available = [ext for ext in loaded if _is_metered(ext) and _metered_available(ext)]
|
||||
if not available:
|
||||
return None
|
||||
|
||||
strategy = profile.route.strategy if profile.route else RouteStrategy.EXPLICIT
|
||||
if strategy == RouteStrategy.LOWEST_COST:
|
||||
best: Extension | None = None
|
||||
best_cost: float | None = None
|
||||
for ext in available:
|
||||
if not _metered_available(ext):
|
||||
continue
|
||||
cost = _quote_cost(ext, profile, inputs, duration_s)
|
||||
if cost is None:
|
||||
continue
|
||||
max_hour = profile.route.max_cost_per_hour_usd if profile.route else None
|
||||
if max_hour is not None and cost > max_hour:
|
||||
continue
|
||||
if best is None or cost < (best_cost or float("inf")):
|
||||
best, best_cost = ext, cost
|
||||
if best:
|
||||
return best
|
||||
|
||||
for ext_id in ("ext.e2b", "ext.modal", "ext.saas-stub"):
|
||||
for ext in available:
|
||||
if ext.id == ext_id:
|
||||
return ext
|
||||
return available[0]
|
||||
|
||||
|
||||
def _self_hosted_available(profile: Profile, ext: Extension, host_override: str | None) -> bool:
|
||||
if _is_metered(ext):
|
||||
return True
|
||||
|
|
@ -65,6 +109,11 @@ def resolve_extension(
|
|||
for ext in loaded:
|
||||
if not _is_metered(ext) and _self_hosted_available(profile, ext, host_override):
|
||||
return ext
|
||||
fallback = _select_metered_fallback(
|
||||
loaded, profile, inputs, duration_s=duration_s
|
||||
)
|
||||
if fallback:
|
||||
return fallback
|
||||
for ext in loaded:
|
||||
if _is_metered(ext):
|
||||
return ext
|
||||
|
|
@ -76,6 +125,8 @@ def resolve_extension(
|
|||
for ext in loaded:
|
||||
if not _is_metered(ext) and _self_hosted_available(profile, ext, host_override):
|
||||
return ext
|
||||
if not _metered_available(ext):
|
||||
continue
|
||||
cost = _quote_cost(ext, profile, inputs, duration_s)
|
||||
if cost is None:
|
||||
continue
|
||||
|
|
@ -92,6 +143,11 @@ def resolve_extension(
|
|||
for ext in loaded:
|
||||
if not _is_metered(ext) and _self_hosted_available(profile, ext, host_override):
|
||||
return ext
|
||||
fallback = _select_metered_fallback(
|
||||
loaded, profile, inputs, duration_s=duration_s
|
||||
)
|
||||
if fallback:
|
||||
return fallback
|
||||
return loaded[-1]
|
||||
|
||||
return load_extension(profile.extension)
|
||||
Loading…
Add table
Add a link
Reference in a new issue