mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-06 04:38:22 +00:00
update models.json shape
This commit is contained in:
+7787
-204
File diff suppressed because it is too large
Load Diff
+9
-36
@@ -1,14 +1,11 @@
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
from typing import Literal
|
||||
|
||||
from fastapi import HTTPException
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from .cashu import credit_balance, pay_out
|
||||
from .db import ApiKey, AsyncSession
|
||||
from .price import btc_usd_ask_price
|
||||
from .models import MODELS
|
||||
|
||||
COST_PER_REQUEST = (
|
||||
int(os.environ.get("COST_PER_REQUEST", "1")) * 1000
|
||||
@@ -79,14 +76,17 @@ async def adjust_payment_for_tokens(
|
||||
"output_msats": 0,
|
||||
"total_msats": COST_PER_REQUEST,
|
||||
}
|
||||
|
||||
if MODEL_BASED_PRICING and os.path.exists("models.json"):
|
||||
models = read_models()
|
||||
response_model = response_data.get("model", "")
|
||||
if response_model not in [model.name for model in models]:
|
||||
if response_model not in [model.id for model in MODELS]:
|
||||
raise HTTPException(status_code=400, detail="Invalid model")
|
||||
model = next(model for model in models if model.name == response_model)
|
||||
MSATS_PER_1K_INPUT_TOKENS = await model.msats_per_1k_input_tokens()
|
||||
MSATS_PER_1K_OUTPUT_TOKENS = await model.msats_per_1k_output_tokens()
|
||||
model = next(model for model in MODELS if model.id == response_model)
|
||||
if model.sats_pricing is None:
|
||||
raise HTTPException(status_code=400, detail="Model pricing not defined")
|
||||
|
||||
MSATS_PER_1K_INPUT_TOKENS = model.sats_pricing.prompt * 1_000_000
|
||||
MSATS_PER_1K_OUTPUT_TOKENS = model.sats_pricing.completion * 1_000_000
|
||||
|
||||
if not (MSATS_PER_1K_OUTPUT_TOKENS and MSATS_PER_1K_INPUT_TOKENS):
|
||||
raise HTTPException(status_code=400, detail="Model pricing not defined")
|
||||
@@ -134,30 +134,3 @@ async def adjust_payment_for_tokens(
|
||||
await pay_out(session)
|
||||
|
||||
return cost_data
|
||||
|
||||
|
||||
class LLModel(BaseModel):
|
||||
name: str
|
||||
cost_per_1m_input_tokens: float = Field(alias="cost_per_1m_prompt_tokens")
|
||||
cost_per_1m_output_tokens: float = Field(alias="cost_per_1m_completion_tokens")
|
||||
currency: Literal["btc", "usd"]
|
||||
|
||||
async def msats_per_1k_input_tokens(self) -> float:
|
||||
if self.currency == "btc":
|
||||
return self.cost_per_1m_input_tokens * 100_000
|
||||
btc_price = await btc_usd_ask_price()
|
||||
return (self.cost_per_1m_input_tokens / 1000) * (100_000_000_000 / btc_price)
|
||||
|
||||
async def msats_per_1k_output_tokens(self) -> float:
|
||||
if self.currency == "btc":
|
||||
return self.cost_per_1m_output_tokens * 100_000
|
||||
btc_price = await btc_usd_ask_price()
|
||||
return (self.cost_per_1m_output_tokens / 1000) * (100_000_000_000 / btc_price)
|
||||
|
||||
|
||||
def read_models() -> list[LLModel]:
|
||||
if not os.path.exists("models.json"):
|
||||
raise HTTPException(status_code=400, detail="Models not defined")
|
||||
with open("models.json", "r") as f:
|
||||
models = json.load(f)["models"]
|
||||
return [LLModel(**model) for model in models]
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@ async def pay_out(session: AsyncSession) -> None:
|
||||
wallet = await _initialize_wallet()
|
||||
wallet_balance = wallet.available_balance
|
||||
|
||||
assert wallet_balance >= user_balance, "Something went deeply wrong."
|
||||
assert wallet_balance <= user_balance, "Something went deeply wrong."
|
||||
|
||||
if (revenue := wallet_balance - user_balance) <= MINIMUM_PAYOUT:
|
||||
return
|
||||
|
||||
+4
-2
@@ -1,3 +1,4 @@
|
||||
import asyncio
|
||||
import os
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
@@ -5,8 +6,8 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from .db import init_db
|
||||
from .proxy import proxy_router
|
||||
from .account import account_router
|
||||
from .auth import read_models
|
||||
from .cashu import _initialize_wallet
|
||||
from .models import MODELS, update_sats_pricing
|
||||
|
||||
__version__ = "0.0.1"
|
||||
|
||||
@@ -37,7 +38,7 @@ async def info():
|
||||
"mint": os.environ.get("MINT", ""),
|
||||
"http_url": os.environ.get("HTTP_URL", ""),
|
||||
"onion_url": os.environ.get("ONION_URL", ""),
|
||||
"models": read_models(),
|
||||
"models": MODELS,
|
||||
}
|
||||
|
||||
|
||||
@@ -49,3 +50,4 @@ app.include_router(proxy_router)
|
||||
async def startup_event():
|
||||
await init_db()
|
||||
await _initialize_wallet()
|
||||
asyncio.create_task(update_sats_pricing())
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import asyncio
|
||||
import json
|
||||
from pydantic.v1 import BaseModel
|
||||
|
||||
from .price import sats_usd_ask_price
|
||||
|
||||
|
||||
class Architecture(BaseModel):
|
||||
modality: str
|
||||
input_modalities: list[str]
|
||||
output_modalities: list[str]
|
||||
tokenizer: str
|
||||
instruct_type: str | None
|
||||
|
||||
|
||||
class Pricing(BaseModel):
|
||||
prompt: float
|
||||
completion: float
|
||||
request: float
|
||||
image: float
|
||||
web_search: float
|
||||
internal_reasoning: float
|
||||
|
||||
|
||||
class Model(BaseModel):
|
||||
id: str
|
||||
name: str
|
||||
created: int
|
||||
description: str
|
||||
context_length: int
|
||||
architecture: Architecture
|
||||
pricing: Pricing
|
||||
sats_pricing: Pricing | None
|
||||
per_request_limits: dict | None
|
||||
|
||||
|
||||
MODELS: list[Model] = []
|
||||
|
||||
with open("models.json", "r") as f:
|
||||
MODELS = [Model(**model) for model in json.load(f)["models"]]
|
||||
|
||||
|
||||
async def update_sats_pricing() -> None:
|
||||
while True:
|
||||
sats_to_usd = await sats_usd_ask_price()
|
||||
for model in MODELS:
|
||||
model.sats_pricing = Pricing(
|
||||
**{k: v / sats_to_usd for k, v in model.pricing.dict().items()}
|
||||
)
|
||||
await asyncio.sleep(10)
|
||||
+13
-9
@@ -3,27 +3,27 @@ import httpx
|
||||
import asyncio
|
||||
|
||||
# artifical spread to cover conversion fees
|
||||
EXCHANGE_FEE = os.environ.get("EXCHANGE_FEE", 1.005) # 0.5% default
|
||||
EXCHANGE_FEE = float(os.environ.get("EXCHANGE_FEE", "1.005")) # 0.5% default
|
||||
|
||||
|
||||
async def kraken_btc_usd(client: httpx.AsyncClient) -> int:
|
||||
async def kraken_btc_usd(client: httpx.AsyncClient) -> float:
|
||||
api = "https://api.kraken.com/0/public/Ticker?pair=XBTUSD"
|
||||
return int(float((await client.get(api)).json()["result"]["XXBTZUSD"]["c"][0]))
|
||||
return float((await client.get(api)).json()["result"]["XXBTZUSD"]["c"][0])
|
||||
|
||||
|
||||
async def coinbase_btc_usd(client: httpx.AsyncClient) -> int:
|
||||
async def coinbase_btc_usd(client: httpx.AsyncClient) -> float:
|
||||
api = "https://api.coinbase.com/v2/prices/BTC-USD/spot"
|
||||
return int(float((await client.get(api)).json()["data"]["amount"]))
|
||||
return float((await client.get(api)).json()["data"]["amount"])
|
||||
|
||||
|
||||
async def binance_btc_usdt(client: httpx.AsyncClient) -> int:
|
||||
async def binance_btc_usdt(client: httpx.AsyncClient) -> float:
|
||||
api = "https://api.binance.com/api/v3/ticker/price?symbol=BTCUSDT"
|
||||
return int(float((await client.get(api)).json()["price"]))
|
||||
return float((await client.get(api)).json()["price"])
|
||||
|
||||
|
||||
async def btc_usd_ask_price() -> int:
|
||||
async def btc_usd_ask_price() -> float:
|
||||
async with httpx.AsyncClient() as client:
|
||||
return int(
|
||||
return (
|
||||
max(
|
||||
await asyncio.gather(
|
||||
kraken_btc_usd(client),
|
||||
@@ -33,3 +33,7 @@ async def btc_usd_ask_price() -> int:
|
||||
)
|
||||
* EXCHANGE_FEE
|
||||
)
|
||||
|
||||
|
||||
async def sats_usd_ask_price() -> float:
|
||||
return (await btc_usd_ask_price()) / 100_000_000
|
||||
|
||||
Reference in New Issue
Block a user