Compare commits

..
Author SHA1 Message Date
redshift 859b31e2bc Update tests to reflect removal of models field from base URL output
- Remove models field from required fields list in root endpoint test
- Add explicit test to ensure models field is not present
- Update test comments to reference issue #184

Related to #184
2025-10-17 04:11:48 +00:00
redshift fa0b2834c5 Remove models variable from base URL output
- Remove the deprecated models field from /v1/info endpoint
- The models field was kept for back-compatibility but is now removed
- Users should use the dedicated /v1/models endpoint instead

Fixes #184
2025-10-17 04:10:30 +00:00
redshiftandGitHub ecbe3158c0 Merge pull request #179 from Routstr/503-mint-service-bug
Fixed the 503 mint service unavailable bug
2025-09-22 16:27:36 +00:00
redshiftandGitHub bac50f2150 Fixed the 503 mint service unavailable bug
This flag fetch all keysets from the db into the wallet, which is wrong given that we're initiating it for a mint. And load_mint sets the first keyset from all the keysets from the db, irrespective of the unit and the mint. 

Fixes #175
2025-09-22 16:25:02 +00:00
shroominicandGitHub 4bf02226dc Merge pull request #172 from Routstr/fix-max-tokens-string-int
fix max-tokens coming in as string
2025-09-16 13:05:39 +01:00
Shroominic c8f4e7d3e5 fix max-tokens coming in as string 2025-09-16 13:00:38 +01:00
shroominicandGitHub 3d4a8e0e14 Merge pull request #169 from Routstr/dev
## Routstr v0.1.3 — 2025-09-15

### Highlights

- **DB-backed settings with live updates**: Env vars seed on first run; DB is the source of truth (except `DATABASE_URL`). Manage via Admin UI or `PATCH /admin/api/settings`.
- **Models moved to database** with background sats-pricing computation and optional periodic refresh.
- **Smarter max-cost reservation**: Discount based on prompt tokens and `max_tokens` using `TOLERANCE_PERCENTAGE`, with a `MIN_REQUEST_MSAT` floor.
- **Faster startup and provider discovery**; `.onion` endpoints use Tor proxy by default.

### Breaking changes

- **Configuration is DB-first** after bootstrap. Env changes won’t auto-apply (besides `DATABASE_URL`).
- **Models endpoint**: Use `/v1/models` for canonical model info; `/v1/info` keeps an empty `models` field for back-compat.
- **Legacy pricing envs** are auto-mapped but deprecated:
  - `MODEL_BASED_PRICING` → `!FIXED_PRICING`
  - `COST_PER_REQUEST` → `FIXED_COST_PER_REQUEST`
  - `COST_PER_1K_*` → `FIXED_PER_1K_*`

### Added

- **SettingsService** with env→computed→DB merge; runtime edits; app metadata (name/description) applied to OpenAPI.
- **Automatic migrations** on startup; new tables: `settings`, `models`.
- **Per-model sats pricing** and per-request maximums computed in the background.
- **Pricing controls**: `FIXED_PRICING`, `FIXED_COST_PER_REQUEST`, optional per-1k token overrides, `MIN_REQUEST_MSAT`.
- **Azure chat support**: Optional `CHAT_COMPLETIONS_API_VERSION` adds `api-version` to `/chat/completions`.
- **Derive `NPUB` from `NSEC`** during bootstrap if not provided.

### Changed / Improvements

- **Proxy header hardening**: Strip sensitive headers; replace Authorization with upstream key when configured.
- **Discovery**: Read `RELAYS` from settings; Tor proxy defaults for `.onion` via `TOR_PROXY_URL`; faster announcement fetching.
- **Admin**: Improved auth/log UX; fixed log search; show mint balances in dashboard.
- **Performance**: Optimized startup; lazy DB info loading.

### Fixed

- **Refunds**: Correct msat→sat conversion; configurable refund cache TTL via `REFUND_CACHE_TTL_SECONDS`; better error mapping for mint outages.
- **Balances**: Reserved balance reporting and negative-reserved edge cases.
- **Tests**: Updated to patch settings and use fixed-pricing flags.

### Upgrade notes (from v0.1.2)

1. Ensure `DATABASE_URL`, `UPSTREAM_BASE_URL`, and `ADMIN_PASSWORD` are set. Optionally set `UPSTREAM_API_KEY`, `RELAYS`, `CASHU_MINTS`, `TOR_PROXY_URL`.
2. Start the service; automatic Alembic migrations will run (`settings`, `models` tables).
3. Configure settings via Admin UI (`/admin`) or `PATCH /admin/api/settings`. Env changes after first run won’t apply automatically.
4. Prefer new pricing envs or a `models.json` at `MODELS_PATH`. Legacy envs are mapped but will be removed in a future release.
5. Optional tuning: `TOLERANCE_PERCENTAGE`, `MIN_REQUEST_MSAT`, `PRICING_REFRESH_INTERVAL_SECONDS`, `MODELS_REFRESH_INTERVAL_SECONDS`, `ENABLE_*_REFRESH`.
6. For Azure, set `CHAT_COMPLETIONS_API_VERSION`.

### References

- Configuration: `docs/getting-started/configuration.md`
- API: `docs/api/endpoints.md`
- Custom pricing: `docs/advanced/custom-pricing.md`

### Stats and credits

- 42 files changed, +1819/−832 lines.
- Merges: #170 token-discount-max-cost, #171 move-models-to-db.
- Contributors: Shroominic.
2025-09-16 12:43:54 +01:00
4 changed files with 21 additions and 20 deletions
+1 -2
View File
@@ -149,7 +149,6 @@ async def info() -> dict:
"mints": global_settings.cashu_mints,
"http_url": global_settings.http_url,
"onion_url": global_settings.onion_url,
"models": [], # kept for back-compat; prefer /v1/models
}
@@ -163,4 +162,4 @@ app.include_router(admin_router)
app.include_router(balance_router)
app.include_router(deprecated_wallet_router)
app.include_router(providers_router)
app.include_router(proxy_router)
app.include_router(proxy_router)
+16 -7
View File
@@ -183,14 +183,23 @@ async def calculate_discounted_max_cost(
else:
adjusted = adjusted + math.ceil(-estimated_prompt_delta_sats * 1000)
if max_tokens := body.get("max_tokens"):
estimated_completion_delta_sats = (
max_completion_allowed_sats - max_tokens * model_pricing.completion
)
if estimated_completion_delta_sats >= 0:
adjusted = adjusted - math.floor(estimated_completion_delta_sats * 1000)
max_tokens_raw = body.get("max_tokens", None)
if max_tokens_raw is not None:
try:
max_tokens_int = int(max_tokens_raw)
except (TypeError, ValueError):
logger.warning(
"Invalid max_tokens; ignoring in cost adjustment",
extra={"max_tokens": str(max_tokens_raw)[:64], "model": model},
)
else:
adjusted = adjusted + math.ceil(-estimated_completion_delta_sats * 1000)
estimated_completion_delta_sats = (
max_completion_allowed_sats - max_tokens_int * model_pricing.completion
)
if estimated_completion_delta_sats >= 0:
adjusted = adjusted - math.floor(estimated_completion_delta_sats * 1000)
else:
adjusted = adjusted + math.ceil(-estimated_completion_delta_sats * 1000)
logger.debug(
"Discounted max cost computed",
+1 -1
View File
@@ -153,7 +153,7 @@ async def get_wallet(mint_url: str, unit: str = "sat", load: bool = True) -> Wal
id = f"{mint_url}_{unit}"
if id not in _wallets:
_wallets[id] = await Wallet.with_db(
mint_url, db=".wallet", load_all_keysets=True, unit=unit
mint_url, db=".wallet", unit=unit
)
if load:
@@ -62,7 +62,6 @@ async def test_root_endpoint_structure_and_performance(
"mints",
"http_url",
"onion_url",
"models",
]
for field in required_fields:
assert field in data, f"Missing required field: {field}"
@@ -75,15 +74,9 @@ async def test_root_endpoint_structure_and_performance(
assert isinstance(data["mints"], list)
assert isinstance(data["http_url"], str)
assert isinstance(data["onion_url"], str)
assert isinstance(data["models"], list)
# Validate models structure if any exist
for model in data["models"]:
assert isinstance(model, dict)
# Models should have at least basic fields
model_required_fields = ["id", "name"]
for field in model_required_fields:
assert field in model, f"Model missing required field: {field}"
# Ensure models field is not present (removed as per issue #184)
assert "models" not in data, "Models field should not be present in base URL output"
# Verify no database state changes
diff = await db_snapshot.diff()
@@ -457,4 +450,4 @@ async def test_info_endpoints_response_consistency(
# Model IDs should be the same
first_ids = {m["id"] for m in first_models}
response_ids = {m["id"] for m in models}
assert first_ids == response_ids
assert first_ids == response_ids