Normalize line endings to LF via .gitattributes
Adds a committed .gitattributes so Windows and Linux checkouts converge on LF for all text files, overriding each developer's local core.autocrlf. Renormalizes existing blobs (server.py, dashboard.html, etc.) that had CRLF baked in, clearing the repo-wide phantom "modified" churn. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,419 +1,419 @@
|
||||
"""meshnet-tracker CLI entry point."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from .accounts import DEFAULT_ACCOUNTS_DB_PATH
|
||||
from .billing import DEFAULT_BILLING_DB_PATH
|
||||
from .hf_pricing import DEFAULT_HF_PRICING_LOG_DB_PATH
|
||||
from .logging_setup import (
|
||||
DEFAULT_LOG_BACKUP_COUNT,
|
||||
DEFAULT_LOG_DIR,
|
||||
DEFAULT_LOG_MAX_BYTES,
|
||||
configure_tracker_file_logging,
|
||||
)
|
||||
from .routing_stats import RoutingConfig
|
||||
from .server import (
|
||||
DEFAULT_CALLER_CREDIT_USDT,
|
||||
DEFAULT_DEVNET_TOPUP_USDT,
|
||||
TrackerServer,
|
||||
derive_relay_url_from_public_tracker_url,
|
||||
)
|
||||
|
||||
DEFAULT_REGISTRY_DB_PATH = "meshnet_registry.sqlite3"
|
||||
|
||||
|
||||
def _load_env_file(path: Path) -> None:
|
||||
"""Load simple KEY=VALUE pairs from an env file without overriding env vars."""
|
||||
if not path.exists():
|
||||
return
|
||||
try:
|
||||
lines = path.read_text().splitlines()
|
||||
except OSError:
|
||||
return
|
||||
for line in lines:
|
||||
text = line.strip()
|
||||
if not text or text.startswith("#"):
|
||||
continue
|
||||
if text.startswith("export "):
|
||||
text = text[len("export "):].strip()
|
||||
if "=" not in text:
|
||||
continue
|
||||
key, value = text.split("=", 1)
|
||||
key = key.strip()
|
||||
if not key or key in os.environ:
|
||||
continue
|
||||
value = value.strip()
|
||||
if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
|
||||
value = value[1:-1]
|
||||
os.environ[key] = value
|
||||
|
||||
|
||||
def _load_env_defaults() -> None:
|
||||
"""Load local and user-level tracker env defaults before parsing arguments."""
|
||||
_load_env_file(Path.cwd() / ".env")
|
||||
_load_env_file(Path.home() / ".config" / "meshnet" / "secrets.env")
|
||||
|
||||
|
||||
def _routing_config_from_args(args: argparse.Namespace) -> RoutingConfig | None:
|
||||
"""Build a RoutingConfig from CLI flags; None keeps env-var/server defaults."""
|
||||
overrides = {
|
||||
"explore_share": args.route_explore_share,
|
||||
"weight_alpha": args.route_weight_alpha,
|
||||
"stats_half_life_seconds": args.route_stats_half_life,
|
||||
}
|
||||
set_values = {key: value for key, value in overrides.items() if value is not None}
|
||||
if not set_values:
|
||||
return None
|
||||
return RoutingConfig(**set_values)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
_load_env_defaults()
|
||||
common = argparse.ArgumentParser(add_help=False)
|
||||
common.add_argument("--host", default="0.0.0.0", help="Host interface to listen on")
|
||||
common.add_argument("--port", type=int, default=8080, help="Port to listen on")
|
||||
common.add_argument(
|
||||
"--heartbeat-timeout",
|
||||
type=float,
|
||||
default=30.0,
|
||||
help="Seconds before a node is removed from the registry after missed heartbeat",
|
||||
)
|
||||
common.add_argument(
|
||||
"--cluster-peers",
|
||||
default="",
|
||||
help="Comma-separated URLs of peer tracker nodes (enables Raft cluster mode)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--self-url",
|
||||
default=None,
|
||||
help="This tracker's own URL as seen by peers (auto-derived from --host/--port if omitted)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--stats-db",
|
||||
default=None,
|
||||
metavar="PATH",
|
||||
help="SQLite database path for persistent model usage statistics",
|
||||
)
|
||||
common.add_argument(
|
||||
"--relay-url",
|
||||
default=None,
|
||||
help="Public ws(s):// relay URL advertised to nodes, for example wss://ai.neuron.d-popov.com/ws",
|
||||
)
|
||||
common.add_argument(
|
||||
"--billing-db",
|
||||
default=DEFAULT_BILLING_DB_PATH,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"SQLite database path for the USDT billing ledger "
|
||||
f"(default: {DEFAULT_BILLING_DB_PATH}; ADR-0015)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--no-billing",
|
||||
action="store_true",
|
||||
help="Disable the USDT billing ledger",
|
||||
)
|
||||
common.add_argument(
|
||||
"--max-charge-per-request",
|
||||
type=float,
|
||||
default=None,
|
||||
help=(
|
||||
"Reject chat completion requests whose prompt plus requested completion "
|
||||
"token bound would cost more than this many USDT"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--starting-credit",
|
||||
type=float,
|
||||
default=DEFAULT_CALLER_CREDIT_USDT,
|
||||
metavar="USDT",
|
||||
help=(
|
||||
"One-time Caller Credit granted when an account creates its first "
|
||||
f"API key (default: {DEFAULT_CALLER_CREDIT_USDT}; set 0 to require "
|
||||
"deposits before inference)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--devnet-topup",
|
||||
type=float,
|
||||
default=DEFAULT_DEVNET_TOPUP_USDT,
|
||||
metavar="USDT",
|
||||
help=(
|
||||
"Dashboard devnet top-up faucet: each click credits this many USDT "
|
||||
f"to one of the account's keys (default: {DEFAULT_DEVNET_TOPUP_USDT}; "
|
||||
"MUST be 0 on mainnet deployments)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--registry-db",
|
||||
default=DEFAULT_REGISTRY_DB_PATH,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"SQLite database path for persisted strike/ban/reputation registry "
|
||||
f"state (default: {DEFAULT_REGISTRY_DB_PATH})"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--no-registry-contracts",
|
||||
action="store_true",
|
||||
help="Disable the local contract registry used for strike/ban/reputation enforcement",
|
||||
)
|
||||
common.add_argument(
|
||||
"--accounts-db",
|
||||
default=DEFAULT_ACCOUNTS_DB_PATH,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"SQLite database path for dashboard user accounts "
|
||||
f"(default: {DEFAULT_ACCOUNTS_DB_PATH})"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--no-accounts",
|
||||
action="store_true",
|
||||
help="Disable dashboard user accounts (registration/login)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--solana-rpc-url",
|
||||
default=None,
|
||||
help="Solana RPC URL (e.g. https://api.devnet.solana.com); enables the on-chain treasury",
|
||||
)
|
||||
common.add_argument(
|
||||
"--usdt-mint",
|
||||
default=None,
|
||||
help="SPL mint address of (mock) USDT — see scripts/devnet_setup.py",
|
||||
)
|
||||
common.add_argument(
|
||||
"--treasury-keypair",
|
||||
default=None,
|
||||
metavar="PATH",
|
||||
help="Treasury keypair JSON path (only on settlement-capable trackers)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--settle-period",
|
||||
type=float,
|
||||
default=86400.0,
|
||||
help="Max seconds between payouts to a node (dev: 60, prod: 86400)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--payout-threshold",
|
||||
type=float,
|
||||
default=5.0,
|
||||
help="Pending USDT that triggers an immediate payout (dev: 0)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--payout-dust-floor",
|
||||
type=float,
|
||||
default=0.01,
|
||||
help="Never pay out less than this many USDT",
|
||||
)
|
||||
common.add_argument(
|
||||
"--validator-service-token",
|
||||
default=None,
|
||||
help=(
|
||||
"Service token the validator uses on POST /v1/billing/forfeit "
|
||||
"(default: MESHNET_VALIDATOR_SERVICE_TOKEN env; ADR-0017)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--hive-secret",
|
||||
default=None,
|
||||
help=(
|
||||
"Shared secret authenticating gossip between tracker peers "
|
||||
"(default: MESHNET_HIVE_SECRET env; required for multi-tracker replication)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--toploc-calibration-db",
|
||||
default=None,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"SQLite path for the AH-021 honest-noise TOPLOC calibration corpus "
|
||||
"(enables POST /v1/calibration/toploc/run + GET /v1/calibration/toploc/results)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--toploc-reference-node-url",
|
||||
default=None,
|
||||
help="Reference node the calibration job teacher-forces claimed tokens against (see validator README)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--toploc-calibration-gate-min-hardware-profiles",
|
||||
type=int,
|
||||
default=1,
|
||||
help=(
|
||||
"Distinct (GPU model, dtype) profiles the corpus must cover before "
|
||||
"gate_status.ready is true (alpha exception: fleet size is acceptable)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--enable-hf-pricing",
|
||||
action="store_true",
|
||||
help=(
|
||||
"Enable the daily dynamic pricing refresh (issue 23): for presets with a "
|
||||
"curated hf_aliases list, sets the client price to 80%% of the cheapest "
|
||||
"matching HuggingFace inference-marketplace rate. Presets without "
|
||||
"hf_aliases are unaffected and keep their static price."
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--hf-pricing-log-db",
|
||||
default=None,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"SQLite database path for the dynamic pricing change log "
|
||||
f"(default when --enable-hf-pricing is set: {DEFAULT_HF_PRICING_LOG_DB_PATH}; "
|
||||
"enables GET /v1/pricing/hf/history)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--hf-pricing-refresh-interval",
|
||||
type=float,
|
||||
default=86400.0,
|
||||
help="Seconds between dynamic pricing refresh passes (default: daily)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--models-dir",
|
||||
default=None,
|
||||
metavar="PATH",
|
||||
help="Local HuggingFace snapshot root advertised as tracker model-file source (default: MESHNET_MODELS_DIR)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--route-explore-share",
|
||||
type=float,
|
||||
default=None,
|
||||
metavar="FRACTION",
|
||||
help=(
|
||||
"Fraction of requests routed down unproven/stale routes to gather "
|
||||
"throughput statistics (ADR-0021; default 0.3, lower once traffic grows)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--route-weight-alpha",
|
||||
type=float,
|
||||
default=None,
|
||||
metavar="ALPHA",
|
||||
help=(
|
||||
"Traffic weight exponent among proven routes: share ∝ tps^alpha "
|
||||
"(default 1.0 — a 1.5x-faster route gets 1.5x the traffic)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--route-stats-half-life",
|
||||
type=float,
|
||||
default=None,
|
||||
metavar="SECONDS",
|
||||
help="Half-life for decaying route throughput observations (default 600)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--log-dir",
|
||||
default=DEFAULT_LOG_DIR,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"Directory for rotating tracker logs "
|
||||
f"(default: {DEFAULT_LOG_DIR}; files: info.log, warning.log, error.log)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--log-max-bytes",
|
||||
type=int,
|
||||
default=DEFAULT_LOG_MAX_BYTES,
|
||||
metavar="BYTES",
|
||||
help=f"Rotate each tracker log file after this many bytes (default: {DEFAULT_LOG_MAX_BYTES})",
|
||||
)
|
||||
common.add_argument(
|
||||
"--log-backup-count",
|
||||
type=int,
|
||||
default=DEFAULT_LOG_BACKUP_COUNT,
|
||||
metavar="N",
|
||||
help=f"Number of rotated tracker log files to keep per level (default: {DEFAULT_LOG_BACKUP_COUNT})",
|
||||
)
|
||||
common.add_argument(
|
||||
"--no-file-logs",
|
||||
action="store_true",
|
||||
help="Disable rotating tracker log files and only write to the terminal",
|
||||
)
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="meshnet-tracker",
|
||||
description="Distributed Inference Network node registry and route selection",
|
||||
parents=[common],
|
||||
)
|
||||
subparsers = parser.add_subparsers(dest="command")
|
||||
|
||||
subparsers.add_parser("start", help="Start the tracker server", parents=[common])
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command in {None, "start"}:
|
||||
if not args.no_file_logs:
|
||||
log_dir = configure_tracker_file_logging(
|
||||
args.log_dir,
|
||||
max_bytes=args.log_max_bytes,
|
||||
backup_count=args.log_backup_count,
|
||||
)
|
||||
print(f"meshnet-tracker logs: {log_dir}", flush=True)
|
||||
cluster_peers = [u.strip() for u in args.cluster_peers.split(",") if u.strip()]
|
||||
relay_url = args.relay_url or derive_relay_url_from_public_tracker_url(args.self_url)
|
||||
treasury = None
|
||||
if args.solana_rpc_url and args.usdt_mint and args.treasury_keypair:
|
||||
from meshnet_contracts.solana_adapter import SolanaCustodialTreasury
|
||||
|
||||
treasury = SolanaCustodialTreasury(
|
||||
args.solana_rpc_url, args.usdt_mint, args.treasury_keypair,
|
||||
)
|
||||
contracts = None
|
||||
if not args.no_registry_contracts:
|
||||
from meshnet_contracts import LocalSolanaContracts # type: ignore[import-not-found]
|
||||
|
||||
contracts = LocalSolanaContracts(registry_db=args.registry_db)
|
||||
server = TrackerServer(
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
heartbeat_timeout=args.heartbeat_timeout,
|
||||
cluster_peers=cluster_peers or None,
|
||||
cluster_self_url=args.self_url,
|
||||
stats_db=getattr(args, "stats_db", None),
|
||||
relay_url=relay_url,
|
||||
enable_billing=not args.no_billing,
|
||||
billing_db=None if args.no_billing else args.billing_db,
|
||||
max_charge_per_request=args.max_charge_per_request,
|
||||
starting_credit=args.starting_credit,
|
||||
devnet_topup_amount=args.devnet_topup,
|
||||
contracts=contracts,
|
||||
accounts_db=None if args.no_accounts else args.accounts_db,
|
||||
treasury=treasury,
|
||||
settle_period=args.settle_period,
|
||||
payout_threshold=args.payout_threshold,
|
||||
payout_dust_floor=args.payout_dust_floor,
|
||||
validator_service_token=args.validator_service_token,
|
||||
hive_secret=args.hive_secret,
|
||||
toploc_calibration_db=args.toploc_calibration_db,
|
||||
toploc_reference_node_url=args.toploc_reference_node_url,
|
||||
toploc_calibration_gate_min_hardware_profiles=args.toploc_calibration_gate_min_hardware_profiles,
|
||||
enable_hf_pricing=args.enable_hf_pricing,
|
||||
hf_pricing_log_db=(
|
||||
args.hf_pricing_log_db
|
||||
or (DEFAULT_HF_PRICING_LOG_DB_PATH if args.enable_hf_pricing else None)
|
||||
),
|
||||
hf_pricing_refresh_interval=args.hf_pricing_refresh_interval,
|
||||
models_dir=args.models_dir,
|
||||
routing_config=_routing_config_from_args(args),
|
||||
)
|
||||
port = server.start()
|
||||
print(f"meshnet-tracker listening on http://{args.host}:{port}", flush=True)
|
||||
try:
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
server.stop()
|
||||
sys.exit(0)
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
"""meshnet-tracker CLI entry point."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from .accounts import DEFAULT_ACCOUNTS_DB_PATH
|
||||
from .billing import DEFAULT_BILLING_DB_PATH
|
||||
from .hf_pricing import DEFAULT_HF_PRICING_LOG_DB_PATH
|
||||
from .logging_setup import (
|
||||
DEFAULT_LOG_BACKUP_COUNT,
|
||||
DEFAULT_LOG_DIR,
|
||||
DEFAULT_LOG_MAX_BYTES,
|
||||
configure_tracker_file_logging,
|
||||
)
|
||||
from .routing_stats import RoutingConfig
|
||||
from .server import (
|
||||
DEFAULT_CALLER_CREDIT_USDT,
|
||||
DEFAULT_DEVNET_TOPUP_USDT,
|
||||
TrackerServer,
|
||||
derive_relay_url_from_public_tracker_url,
|
||||
)
|
||||
|
||||
DEFAULT_REGISTRY_DB_PATH = "meshnet_registry.sqlite3"
|
||||
|
||||
|
||||
def _load_env_file(path: Path) -> None:
|
||||
"""Load simple KEY=VALUE pairs from an env file without overriding env vars."""
|
||||
if not path.exists():
|
||||
return
|
||||
try:
|
||||
lines = path.read_text().splitlines()
|
||||
except OSError:
|
||||
return
|
||||
for line in lines:
|
||||
text = line.strip()
|
||||
if not text or text.startswith("#"):
|
||||
continue
|
||||
if text.startswith("export "):
|
||||
text = text[len("export "):].strip()
|
||||
if "=" not in text:
|
||||
continue
|
||||
key, value = text.split("=", 1)
|
||||
key = key.strip()
|
||||
if not key or key in os.environ:
|
||||
continue
|
||||
value = value.strip()
|
||||
if len(value) >= 2 and value[0] == value[-1] and value[0] in {"'", '"'}:
|
||||
value = value[1:-1]
|
||||
os.environ[key] = value
|
||||
|
||||
|
||||
def _load_env_defaults() -> None:
|
||||
"""Load local and user-level tracker env defaults before parsing arguments."""
|
||||
_load_env_file(Path.cwd() / ".env")
|
||||
_load_env_file(Path.home() / ".config" / "meshnet" / "secrets.env")
|
||||
|
||||
|
||||
def _routing_config_from_args(args: argparse.Namespace) -> RoutingConfig | None:
|
||||
"""Build a RoutingConfig from CLI flags; None keeps env-var/server defaults."""
|
||||
overrides = {
|
||||
"explore_share": args.route_explore_share,
|
||||
"weight_alpha": args.route_weight_alpha,
|
||||
"stats_half_life_seconds": args.route_stats_half_life,
|
||||
}
|
||||
set_values = {key: value for key, value in overrides.items() if value is not None}
|
||||
if not set_values:
|
||||
return None
|
||||
return RoutingConfig(**set_values)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
_load_env_defaults()
|
||||
common = argparse.ArgumentParser(add_help=False)
|
||||
common.add_argument("--host", default="0.0.0.0", help="Host interface to listen on")
|
||||
common.add_argument("--port", type=int, default=8080, help="Port to listen on")
|
||||
common.add_argument(
|
||||
"--heartbeat-timeout",
|
||||
type=float,
|
||||
default=30.0,
|
||||
help="Seconds before a node is removed from the registry after missed heartbeat",
|
||||
)
|
||||
common.add_argument(
|
||||
"--cluster-peers",
|
||||
default="",
|
||||
help="Comma-separated URLs of peer tracker nodes (enables Raft cluster mode)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--self-url",
|
||||
default=None,
|
||||
help="This tracker's own URL as seen by peers (auto-derived from --host/--port if omitted)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--stats-db",
|
||||
default=None,
|
||||
metavar="PATH",
|
||||
help="SQLite database path for persistent model usage statistics",
|
||||
)
|
||||
common.add_argument(
|
||||
"--relay-url",
|
||||
default=None,
|
||||
help="Public ws(s):// relay URL advertised to nodes, for example wss://ai.neuron.d-popov.com/ws",
|
||||
)
|
||||
common.add_argument(
|
||||
"--billing-db",
|
||||
default=DEFAULT_BILLING_DB_PATH,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"SQLite database path for the USDT billing ledger "
|
||||
f"(default: {DEFAULT_BILLING_DB_PATH}; ADR-0015)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--no-billing",
|
||||
action="store_true",
|
||||
help="Disable the USDT billing ledger",
|
||||
)
|
||||
common.add_argument(
|
||||
"--max-charge-per-request",
|
||||
type=float,
|
||||
default=None,
|
||||
help=(
|
||||
"Reject chat completion requests whose prompt plus requested completion "
|
||||
"token bound would cost more than this many USDT"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--starting-credit",
|
||||
type=float,
|
||||
default=DEFAULT_CALLER_CREDIT_USDT,
|
||||
metavar="USDT",
|
||||
help=(
|
||||
"One-time Caller Credit granted when an account creates its first "
|
||||
f"API key (default: {DEFAULT_CALLER_CREDIT_USDT}; set 0 to require "
|
||||
"deposits before inference)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--devnet-topup",
|
||||
type=float,
|
||||
default=DEFAULT_DEVNET_TOPUP_USDT,
|
||||
metavar="USDT",
|
||||
help=(
|
||||
"Dashboard devnet top-up faucet: each click credits this many USDT "
|
||||
f"to one of the account's keys (default: {DEFAULT_DEVNET_TOPUP_USDT}; "
|
||||
"MUST be 0 on mainnet deployments)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--registry-db",
|
||||
default=DEFAULT_REGISTRY_DB_PATH,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"SQLite database path for persisted strike/ban/reputation registry "
|
||||
f"state (default: {DEFAULT_REGISTRY_DB_PATH})"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--no-registry-contracts",
|
||||
action="store_true",
|
||||
help="Disable the local contract registry used for strike/ban/reputation enforcement",
|
||||
)
|
||||
common.add_argument(
|
||||
"--accounts-db",
|
||||
default=DEFAULT_ACCOUNTS_DB_PATH,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"SQLite database path for dashboard user accounts "
|
||||
f"(default: {DEFAULT_ACCOUNTS_DB_PATH})"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--no-accounts",
|
||||
action="store_true",
|
||||
help="Disable dashboard user accounts (registration/login)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--solana-rpc-url",
|
||||
default=None,
|
||||
help="Solana RPC URL (e.g. https://api.devnet.solana.com); enables the on-chain treasury",
|
||||
)
|
||||
common.add_argument(
|
||||
"--usdt-mint",
|
||||
default=None,
|
||||
help="SPL mint address of (mock) USDT — see scripts/devnet_setup.py",
|
||||
)
|
||||
common.add_argument(
|
||||
"--treasury-keypair",
|
||||
default=None,
|
||||
metavar="PATH",
|
||||
help="Treasury keypair JSON path (only on settlement-capable trackers)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--settle-period",
|
||||
type=float,
|
||||
default=86400.0,
|
||||
help="Max seconds between payouts to a node (dev: 60, prod: 86400)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--payout-threshold",
|
||||
type=float,
|
||||
default=5.0,
|
||||
help="Pending USDT that triggers an immediate payout (dev: 0)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--payout-dust-floor",
|
||||
type=float,
|
||||
default=0.01,
|
||||
help="Never pay out less than this many USDT",
|
||||
)
|
||||
common.add_argument(
|
||||
"--validator-service-token",
|
||||
default=None,
|
||||
help=(
|
||||
"Service token the validator uses on POST /v1/billing/forfeit "
|
||||
"(default: MESHNET_VALIDATOR_SERVICE_TOKEN env; ADR-0017)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--hive-secret",
|
||||
default=None,
|
||||
help=(
|
||||
"Shared secret authenticating gossip between tracker peers "
|
||||
"(default: MESHNET_HIVE_SECRET env; required for multi-tracker replication)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--toploc-calibration-db",
|
||||
default=None,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"SQLite path for the AH-021 honest-noise TOPLOC calibration corpus "
|
||||
"(enables POST /v1/calibration/toploc/run + GET /v1/calibration/toploc/results)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--toploc-reference-node-url",
|
||||
default=None,
|
||||
help="Reference node the calibration job teacher-forces claimed tokens against (see validator README)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--toploc-calibration-gate-min-hardware-profiles",
|
||||
type=int,
|
||||
default=1,
|
||||
help=(
|
||||
"Distinct (GPU model, dtype) profiles the corpus must cover before "
|
||||
"gate_status.ready is true (alpha exception: fleet size is acceptable)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--enable-hf-pricing",
|
||||
action="store_true",
|
||||
help=(
|
||||
"Enable the daily dynamic pricing refresh (issue 23): for presets with a "
|
||||
"curated hf_aliases list, sets the client price to 80%% of the cheapest "
|
||||
"matching HuggingFace inference-marketplace rate. Presets without "
|
||||
"hf_aliases are unaffected and keep their static price."
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--hf-pricing-log-db",
|
||||
default=None,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"SQLite database path for the dynamic pricing change log "
|
||||
f"(default when --enable-hf-pricing is set: {DEFAULT_HF_PRICING_LOG_DB_PATH}; "
|
||||
"enables GET /v1/pricing/hf/history)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--hf-pricing-refresh-interval",
|
||||
type=float,
|
||||
default=86400.0,
|
||||
help="Seconds between dynamic pricing refresh passes (default: daily)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--models-dir",
|
||||
default=None,
|
||||
metavar="PATH",
|
||||
help="Local HuggingFace snapshot root advertised as tracker model-file source (default: MESHNET_MODELS_DIR)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--route-explore-share",
|
||||
type=float,
|
||||
default=None,
|
||||
metavar="FRACTION",
|
||||
help=(
|
||||
"Fraction of requests routed down unproven/stale routes to gather "
|
||||
"throughput statistics (ADR-0021; default 0.3, lower once traffic grows)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--route-weight-alpha",
|
||||
type=float,
|
||||
default=None,
|
||||
metavar="ALPHA",
|
||||
help=(
|
||||
"Traffic weight exponent among proven routes: share ∝ tps^alpha "
|
||||
"(default 1.0 — a 1.5x-faster route gets 1.5x the traffic)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--route-stats-half-life",
|
||||
type=float,
|
||||
default=None,
|
||||
metavar="SECONDS",
|
||||
help="Half-life for decaying route throughput observations (default 600)",
|
||||
)
|
||||
common.add_argument(
|
||||
"--log-dir",
|
||||
default=DEFAULT_LOG_DIR,
|
||||
metavar="PATH",
|
||||
help=(
|
||||
"Directory for rotating tracker logs "
|
||||
f"(default: {DEFAULT_LOG_DIR}; files: info.log, warning.log, error.log)"
|
||||
),
|
||||
)
|
||||
common.add_argument(
|
||||
"--log-max-bytes",
|
||||
type=int,
|
||||
default=DEFAULT_LOG_MAX_BYTES,
|
||||
metavar="BYTES",
|
||||
help=f"Rotate each tracker log file after this many bytes (default: {DEFAULT_LOG_MAX_BYTES})",
|
||||
)
|
||||
common.add_argument(
|
||||
"--log-backup-count",
|
||||
type=int,
|
||||
default=DEFAULT_LOG_BACKUP_COUNT,
|
||||
metavar="N",
|
||||
help=f"Number of rotated tracker log files to keep per level (default: {DEFAULT_LOG_BACKUP_COUNT})",
|
||||
)
|
||||
common.add_argument(
|
||||
"--no-file-logs",
|
||||
action="store_true",
|
||||
help="Disable rotating tracker log files and only write to the terminal",
|
||||
)
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="meshnet-tracker",
|
||||
description="Distributed Inference Network node registry and route selection",
|
||||
parents=[common],
|
||||
)
|
||||
subparsers = parser.add_subparsers(dest="command")
|
||||
|
||||
subparsers.add_parser("start", help="Start the tracker server", parents=[common])
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.command in {None, "start"}:
|
||||
if not args.no_file_logs:
|
||||
log_dir = configure_tracker_file_logging(
|
||||
args.log_dir,
|
||||
max_bytes=args.log_max_bytes,
|
||||
backup_count=args.log_backup_count,
|
||||
)
|
||||
print(f"meshnet-tracker logs: {log_dir}", flush=True)
|
||||
cluster_peers = [u.strip() for u in args.cluster_peers.split(",") if u.strip()]
|
||||
relay_url = args.relay_url or derive_relay_url_from_public_tracker_url(args.self_url)
|
||||
treasury = None
|
||||
if args.solana_rpc_url and args.usdt_mint and args.treasury_keypair:
|
||||
from meshnet_contracts.solana_adapter import SolanaCustodialTreasury
|
||||
|
||||
treasury = SolanaCustodialTreasury(
|
||||
args.solana_rpc_url, args.usdt_mint, args.treasury_keypair,
|
||||
)
|
||||
contracts = None
|
||||
if not args.no_registry_contracts:
|
||||
from meshnet_contracts import LocalSolanaContracts # type: ignore[import-not-found]
|
||||
|
||||
contracts = LocalSolanaContracts(registry_db=args.registry_db)
|
||||
server = TrackerServer(
|
||||
host=args.host,
|
||||
port=args.port,
|
||||
heartbeat_timeout=args.heartbeat_timeout,
|
||||
cluster_peers=cluster_peers or None,
|
||||
cluster_self_url=args.self_url,
|
||||
stats_db=getattr(args, "stats_db", None),
|
||||
relay_url=relay_url,
|
||||
enable_billing=not args.no_billing,
|
||||
billing_db=None if args.no_billing else args.billing_db,
|
||||
max_charge_per_request=args.max_charge_per_request,
|
||||
starting_credit=args.starting_credit,
|
||||
devnet_topup_amount=args.devnet_topup,
|
||||
contracts=contracts,
|
||||
accounts_db=None if args.no_accounts else args.accounts_db,
|
||||
treasury=treasury,
|
||||
settle_period=args.settle_period,
|
||||
payout_threshold=args.payout_threshold,
|
||||
payout_dust_floor=args.payout_dust_floor,
|
||||
validator_service_token=args.validator_service_token,
|
||||
hive_secret=args.hive_secret,
|
||||
toploc_calibration_db=args.toploc_calibration_db,
|
||||
toploc_reference_node_url=args.toploc_reference_node_url,
|
||||
toploc_calibration_gate_min_hardware_profiles=args.toploc_calibration_gate_min_hardware_profiles,
|
||||
enable_hf_pricing=args.enable_hf_pricing,
|
||||
hf_pricing_log_db=(
|
||||
args.hf_pricing_log_db
|
||||
or (DEFAULT_HF_PRICING_LOG_DB_PATH if args.enable_hf_pricing else None)
|
||||
),
|
||||
hf_pricing_refresh_interval=args.hf_pricing_refresh_interval,
|
||||
models_dir=args.models_dir,
|
||||
routing_config=_routing_config_from_args(args),
|
||||
)
|
||||
port = server.start()
|
||||
print(f"meshnet-tracker listening on http://{args.host}:{port}", flush=True)
|
||||
try:
|
||||
while True:
|
||||
time.sleep(1)
|
||||
except KeyboardInterrupt:
|
||||
server.stop()
|
||||
sys.exit(0)
|
||||
else:
|
||||
parser.print_help()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user