mirror of
https://github.com/katanemo/plano.git
synced 2026-06-26 15:39:40 +02:00
feat: make model pricing source configurable (models.dev + DigitalOcean) (#971)
This commit is contained in:
parent
5cc4c4ee77
commit
558df0307c
9 changed files with 687 additions and 48 deletions
|
|
@ -2,9 +2,12 @@
|
|||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
|
||||
import rich_click as click
|
||||
import yaml
|
||||
from rich.console import Console
|
||||
from rich.live import Live
|
||||
|
||||
|
|
@ -15,8 +18,50 @@ from planoai.obs.collector import (
|
|||
LLMCallStore,
|
||||
ObsCollector,
|
||||
)
|
||||
from planoai.obs.pricing import PricingCatalog
|
||||
from planoai.obs.pricing import DEFAULT_PRICING_PROVIDER, PricingCatalog
|
||||
from planoai.obs.render import render
|
||||
from planoai.utils import find_config_file
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _resolve_pricing_source(
|
||||
config_file: str | None,
|
||||
provider_override: str | None,
|
||||
url_override: str | None,
|
||||
) -> tuple[str, str | None]:
|
||||
"""Pick the cost pricing source.
|
||||
|
||||
Precedence: explicit CLI overrides > the first ``type: cost`` entry in
|
||||
``model_metrics_sources`` from the Plano config > the DigitalOcean default.
|
||||
"""
|
||||
provider = DEFAULT_PRICING_PROVIDER
|
||||
url: str | None = None
|
||||
|
||||
config_path = find_config_file(file=config_file)
|
||||
if config_path and os.path.exists(config_path):
|
||||
try:
|
||||
with open(config_path, "r") as f:
|
||||
config = yaml.safe_load(f) or {}
|
||||
sources = config.get("model_metrics_sources") or []
|
||||
for source in sources:
|
||||
if isinstance(source, dict) and source.get("type") == "cost":
|
||||
if source.get("provider"):
|
||||
provider = str(source["provider"])
|
||||
if source.get("url"):
|
||||
url = str(source["url"])
|
||||
break
|
||||
except Exception as exc: # noqa: BLE001 — config is optional for obs
|
||||
logger.warning(
|
||||
"could not read pricing source from %s: %s", config_path, exc
|
||||
)
|
||||
|
||||
if provider_override:
|
||||
provider = provider_override
|
||||
if url_override:
|
||||
url = url_override
|
||||
|
||||
return provider, url
|
||||
|
||||
|
||||
@click.command(name="obs", help="Live observability console for Plano LLM traffic.")
|
||||
|
|
@ -48,13 +93,42 @@ from planoai.obs.render import render
|
|||
show_default=True,
|
||||
help="TUI refresh interval.",
|
||||
)
|
||||
def obs(port: int, host: str, capacity: int, refresh_ms: int) -> None:
|
||||
@click.option(
|
||||
"--config",
|
||||
"config_file",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Path to the Plano config to read the pricing source from "
|
||||
"(defaults to ./config.yaml or ./plano_config.yaml).",
|
||||
)
|
||||
@click.option(
|
||||
"--pricing-provider",
|
||||
type=click.Choice(["digitalocean", "models.dev"]),
|
||||
default=None,
|
||||
help="Override the cost pricing provider (otherwise read from config).",
|
||||
)
|
||||
@click.option(
|
||||
"--pricing-url",
|
||||
type=str,
|
||||
default=None,
|
||||
help="Override the pricing catalog URL (otherwise read from config / provider default).",
|
||||
)
|
||||
def obs(
|
||||
port: int,
|
||||
host: str,
|
||||
capacity: int,
|
||||
refresh_ms: int,
|
||||
config_file: str | None,
|
||||
pricing_provider: str | None,
|
||||
pricing_url: str | None,
|
||||
) -> None:
|
||||
console = Console()
|
||||
provider, url = _resolve_pricing_source(config_file, pricing_provider, pricing_url)
|
||||
console.print(
|
||||
f"[bold {PLANO_COLOR}]planoai obs[/] — loading DO pricing catalog...",
|
||||
f"[bold {PLANO_COLOR}]planoai obs[/] — loading {provider} pricing catalog...",
|
||||
end="",
|
||||
)
|
||||
pricing = PricingCatalog.fetch()
|
||||
pricing = PricingCatalog.fetch(provider=provider, url=url)
|
||||
if len(pricing):
|
||||
sample = ", ".join(pricing.sample_models(3))
|
||||
console.print(
|
||||
|
|
@ -63,7 +137,7 @@ def obs(port: int, host: str, capacity: int, refresh_ms: int) -> None:
|
|||
else:
|
||||
console.print(
|
||||
" [yellow]no pricing loaded[/] — "
|
||||
"[dim]cost column will be blank (DO catalog unreachable)[/]"
|
||||
f"[dim]cost column will be blank ({provider} catalog unreachable)[/]"
|
||||
)
|
||||
|
||||
store = LLMCallStore(capacity=capacity)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue