mirror of
https://github.com/usestrix/strix.git
synced 2026-09-05 08:06:08 +00:00
* feat(cli): add strix login for managed platform sign-in (device flow) * feat(cli): add --scopes flag to strix login * docs: document strix login and managed billing in README, AGENTS, docs, and managed skill * fix(cli): handle malformed login responses and credential file failures * fix(cli): reject sign-in responses without an API token * feat(login): interactive workspace and scope selection with presets * fix(login): reject malformed API token values in sign-in responses * fix(login): skip the scope prompt when stdin is not a terminal * fix(login): tolerate malformed selection containers and remove unreadable credential files on logout * fix(login): treat overflowing timing values as invalid * fix(login): show the configured platform host in the sign-in banner * fix(login): bound device flow timing values and clean up unreplaced secret temp files * feat(cli): add the strix cloud command surface for the managed platform * feat(cli): manage workspaces and hosted onboarding links from strix cloud * fix(cli): report a leftover temporary secret file instead of hiding it * feat(cli): pass a Stripe payment method to the top-up wallet client * docs(cloud): recommend the Stripe agent wallet as the default payment path * fix(cloud): preserve API auth during MPP payment * fix(cloud): drop knowledge query and settings commands removed from the API * fix(cloud): align agent commands with API contracts * fix(cloud): send required PR review integration fields * fix(cloud): preserve scopes when switching workspaces * fix(cloud): make session command help non-destructive * feat(cloud): improve human navigation and output * feat(cli): add native shell completions * feat(cloud): tailor human list and detail views * feat(cloud): upload local source for managed scans * fix(cloud): infer scan type from local targets * Add agent-friendly managed cloud CLI * Harden cloud CLI type boundaries * Clarify cloud test user MFA options * Correct cloud vulnerability status guidance * Clarify chat file path handling * Allow signed storage upload URLs * Fix provider token request handling * Improve cloud CLI human list views * Make cloud CLI workflows actionable and safe * Make cloud workspace switching session-safe * Preserve CLI session metadata in JSON output * Remove preview protection bypass plumbing from cloud CLI
123 lines
3.9 KiB
Python
123 lines
3.9 KiB
Python
from __future__ import annotations
|
|
|
|
from unittest.mock import patch
|
|
|
|
import litellm
|
|
from agents.usage import Usage
|
|
|
|
from strix.report.pricing import resolve_litellm_model
|
|
from strix.report.usage import LLMUsageLedger
|
|
|
|
|
|
def test_resolves_common_bare_model_names() -> None:
|
|
resolve_litellm_model.cache_clear()
|
|
assert resolve_litellm_model("deepseek-v4-flash") == "deepseek/deepseek-v4-flash"
|
|
assert resolve_litellm_model("openai/deepseek-v4-flash") == "deepseek/deepseek-v4-flash"
|
|
assert resolve_litellm_model("grok-4.5") == "xai/grok-4.5"
|
|
# MiniMax-M3 is sold by several LiteLLM providers at different prices, so
|
|
# the resolver must not guess from its bare name. A provider-qualified
|
|
# model remains deterministic.
|
|
assert resolve_litellm_model("minimax/MiniMax-M3") == "minimax/MiniMax-M3"
|
|
|
|
|
|
def test_resolver_returns_none_for_unresolvable_model() -> None:
|
|
resolve_litellm_model.cache_clear()
|
|
assert resolve_litellm_model("provider/not-a-real-model") is None
|
|
|
|
|
|
def test_ledger_uses_estimate_when_routed_provider_reports_no_cost() -> None:
|
|
usage = Usage()
|
|
usage.requests = 1
|
|
usage.input_tokens = 1000
|
|
usage.output_tokens = 200
|
|
usage.total_tokens = 1200
|
|
ledger = LLMUsageLedger()
|
|
|
|
with patch("litellm.completion_cost", return_value=0.42):
|
|
ledger.record(agent_id="a", usage=usage, model="openai/deepseek-v4-flash")
|
|
|
|
assert ledger.total_cost == 0.42
|
|
|
|
|
|
def test_ledger_prefers_observed_cost_over_estimate() -> None:
|
|
usage = Usage()
|
|
usage.requests = 1
|
|
usage.input_tokens = 1000
|
|
usage.output_tokens = 200
|
|
usage.total_tokens = 1200
|
|
ledger = LLMUsageLedger()
|
|
|
|
with patch("litellm.completion_cost", return_value=0.42):
|
|
ledger.record(agent_id="a", usage=usage, model="openai/deepseek-v4-flash")
|
|
ledger.record_observed_cost(0.17)
|
|
|
|
assert ledger.total_cost == 0.17
|
|
|
|
|
|
def test_hydrated_estimate_continues_accumulating_new_estimates() -> None:
|
|
usage = Usage()
|
|
usage.requests = 1
|
|
usage.input_tokens = 1000
|
|
usage.output_tokens = 200
|
|
usage.total_tokens = 1200
|
|
ledger = LLMUsageLedger()
|
|
ledger.hydrate({"cost": 0.42})
|
|
|
|
with patch("litellm.completion_cost", return_value=0.17):
|
|
ledger.record(agent_id="a", usage=usage, model="openai/deepseek-v4-flash")
|
|
|
|
assert ledger.total_cost == 0.59
|
|
|
|
|
|
def test_zero_cost_disables_both_observed_and_estimated_costs() -> None:
|
|
usage = Usage()
|
|
usage.requests = 1
|
|
usage.input_tokens = 1000
|
|
usage.output_tokens = 200
|
|
usage.total_tokens = 1200
|
|
ledger = LLMUsageLedger()
|
|
ledger.zero_cost = True
|
|
|
|
with patch("litellm.completion_cost", return_value=0.42) as estimate:
|
|
ledger.record(agent_id="a", usage=usage, model="deepseek-v4-flash")
|
|
ledger.record_observed_cost(1.0)
|
|
|
|
estimate.assert_not_called()
|
|
assert ledger.total_cost == 0.0
|
|
|
|
|
|
def test_resolver_uses_provider_when_bare_entry_has_one() -> None:
|
|
original = litellm.model_cost
|
|
litellm.model_cost = {
|
|
"example": {
|
|
"litellm_provider": "example-provider",
|
|
"input_cost_per_token": 1.0,
|
|
"output_cost_per_token": 2.0,
|
|
}
|
|
}
|
|
try:
|
|
resolve_litellm_model.cache_clear()
|
|
assert resolve_litellm_model("example") == "example-provider/example"
|
|
finally:
|
|
litellm.model_cost = original
|
|
resolve_litellm_model.cache_clear()
|
|
|
|
|
|
def test_resolver_does_not_guess_between_differently_priced_providers() -> None:
|
|
original = litellm.model_cost
|
|
litellm.model_cost = {
|
|
"provider-a/example": {
|
|
"input_cost_per_token": 1.0,
|
|
"output_cost_per_token": 2.0,
|
|
},
|
|
"provider-b/example": {
|
|
"input_cost_per_token": 3.0,
|
|
"output_cost_per_token": 4.0,
|
|
},
|
|
}
|
|
try:
|
|
resolve_litellm_model.cache_clear()
|
|
assert resolve_litellm_model("example") is None
|
|
finally:
|
|
litellm.model_cost = original
|
|
resolve_litellm_model.cache_clear()
|