From f4f7f7cdcb950dad500f99db8a3f5ecb4da8ddb5 Mon Sep 17 00:00:00 2001 From: Lu Ken Date: Thu, 2 Jan 2025 13:47:04 +0800 Subject: [PATCH 1/3] clean up model usage Signed-off-by: Lu Ken --- src/gentrade_server/config.py | 14 ---------- src/gentrade_server/main.py | 14 +++++----- src/gentrade_server/model.py | 32 ++++++++++++++++++++++ src/gentrade_server/routers/admin.py | 17 ++++++++++++ src/gentrade_server/routers/agent.py | 8 +++--- src/gentrade_server/routers/public.py | 39 +++++++++++---------------- src/gentrade_server/routers/secure.py | 14 ---------- 7 files changed, 76 insertions(+), 62 deletions(-) delete mode 100644 src/gentrade_server/config.py create mode 100644 src/gentrade_server/model.py create mode 100644 src/gentrade_server/routers/admin.py delete mode 100644 src/gentrade_server/routers/secure.py diff --git a/src/gentrade_server/config.py b/src/gentrade_server/config.py deleted file mode 100644 index c1e3721..0000000 --- a/src/gentrade_server/config.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -Configure -""" -from pydantic_settings import BaseSettings - -class Settings(BaseSettings): - """ - Settings - """ - OPENAI_API_KEY: str = "" - OPENAI_API_URL: str = "" - OPENAI_API_MODEL: str = "gpt-3.5-turbo" - -settings = Settings() diff --git a/src/gentrade_server/main.py b/src/gentrade_server/main.py index f322470..866af9a 100644 --- a/src/gentrade_server/main.py +++ b/src/gentrade_server/main.py @@ -9,10 +9,10 @@ from fastapi import FastAPI, Depends from fastapi.middleware.cors import CORSMiddleware -from .routers import secure, public, agent +from .routers import public, agent, admin from .auth import get_user from .util import check_server_time -from .config import settings +from .model import settings from .datahub import DataHub logging.basicConfig(level=logging.INFO, format='%(asctime)s %(message)s') @@ -51,14 +51,16 @@ def receive_signal(number, _): public.router, prefix="/api/v1/public" ) + app.include_router( - secure.router, - prefix="/api/v1/secure", + agent.router, + prefix="/api/v1/agent", dependencies=[Depends(get_user)] ) + app.include_router( - agent.router, - prefix="/api/v1/agent", + admin.router, + prefix="/api/v1/admin", dependencies=[Depends(get_user)] ) diff --git a/src/gentrade_server/model.py b/src/gentrade_server/model.py new file mode 100644 index 0000000..98d326f --- /dev/null +++ b/src/gentrade_server/model.py @@ -0,0 +1,32 @@ +from pydantic import BaseModel, Field +from pydantic_settings import BaseSettings, SettingsConfigDict +from pydantic import Field, field_validator +from typing import List + +class HealthCheck(BaseModel): + """ + Response model to validate and return when performing a health check. + """ + + status: str = Field("OK") + +class Settings(BaseSettings): + model_config = SettingsConfigDict(enable_decoding=False) + + """ + Settings + """ + openai_api_key: str = "" + openai_api_url: str = "" + openai_api_model: str = "gpt-3.5-turbo" + + ntp_servers : List[str] = Field( + "ntp.ntsc.ac.cn,ntp.sjtu.edu.cn,cn.ntp.org.cn,cn.pool.ntp.org,ntp.aliyun.com", + description="The string list of NTP server splitted via comma") + + @field_validator('ntp_servers', mode='before') + @classmethod + def decode_ntp_servers(cls, v: str) -> List[str]: + return v.split(',') + +settings = Settings() diff --git a/src/gentrade_server/routers/admin.py b/src/gentrade_server/routers/admin.py new file mode 100644 index 0000000..ee440f1 --- /dev/null +++ b/src/gentrade_server/routers/admin.py @@ -0,0 +1,17 @@ +import logging + +from fastapi import APIRouter, Depends +from pydantic import BaseModel, Field +from ..model import settings, Settings +from ..auth import get_user + +LOG = logging.getLogger(__name__) + +router = APIRouter() + +@router.get("/settings") +async def get_settings(user: dict = Depends(get_user)) -> Settings: + """ + Get server settings + """ + return settings diff --git a/src/gentrade_server/routers/agent.py b/src/gentrade_server/routers/agent.py index 96216f4..d2a5988 100644 --- a/src/gentrade_server/routers/agent.py +++ b/src/gentrade_server/routers/agent.py @@ -5,15 +5,15 @@ from openai import OpenAI from fastapi import APIRouter, Depends from ..auth import get_user -from ..config import settings +from ..model import settings LOG = logging.getLogger(__name__) router = APIRouter() client = OpenAI( - api_key=settings.OPENAI_API_KEY, - base_url=settings.OPENAI_API_URL + api_key=settings.openai_api_key, + base_url=settings.openai_api_url ) @router.get("/") @@ -22,7 +22,7 @@ async def get_answer(prompt: str, user: dict = Depends(get_user)): Prompt to OpenAI and get answer """ completion = client.chat.completions.create( - model=settings.OPENAI_API_MODEL, + model=settings.openai_api_model, messages=[ {"role": "system", "content": "You are a Lu Ken's assistant for cryptocurrency market."}, diff --git a/src/gentrade_server/routers/public.py b/src/gentrade_server/routers/public.py index a7ef678..9464092 100644 --- a/src/gentrade_server/routers/public.py +++ b/src/gentrade_server/routers/public.py @@ -9,29 +9,15 @@ from dateutil.tz import tzlocal from fastapi import APIRouter -from pydantic import BaseModel +from pydantic import BaseModel, Field -from ..config import settings from ..datahub import DataHub +from ..model import HealthCheck, settings LOG = logging.getLogger(__name__) router = APIRouter() -@router.get("/") -async def get_testroute(): - """ - Test public interface - """ - return "OK" - -class HealthCheck(BaseModel): - """ - Response model to validate and return when performing a health check. - """ - - status: str = "OK" - @router.get("/health") async def get_health() -> HealthCheck: """ @@ -39,14 +25,6 @@ async def get_health() -> HealthCheck: """ return HealthCheck(status="OK") -@router.get("/settings") -async def get_settings(): - """ - Get server settings - """ - return { - 'ntp_server': settings.ntp_server - } @router.get("/server_time") async def get_server_time(): @@ -76,6 +54,19 @@ async def get_markets(): } return retval +@router.get("/markets/") +async def get_markets2(): + """ + Get markets + """ + retval = {} + for _, market in DataHub.inst().markets.items(): + retval[market.market_id] = { + "name": market.name, + "type": market.market_type, + } + return retval + @router.get("/assets/") async def get_assets(market_id:str=""): """ diff --git a/src/gentrade_server/routers/secure.py b/src/gentrade_server/routers/secure.py deleted file mode 100644 index d47347a..0000000 --- a/src/gentrade_server/routers/secure.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -Secure API interface -""" -from fastapi import APIRouter, Depends -from ..auth import get_user - -router = APIRouter() - -@router.get("/") -async def get_testroute(user: dict = Depends(get_user)): - """ - Test secure interface - """ - return user From 01798286ad85dc9a11daf508de8840f6b0123a63 Mon Sep 17 00:00:00 2001 From: Lu Ken Date: Thu, 2 Jan 2025 14:00:20 +0800 Subject: [PATCH 2/3] create model for Market Signed-off-by: Lu Ken --- src/gentrade_server/model.py | 29 ++++++++++++++++++++++++--- src/gentrade_server/routers/admin.py | 5 ++++- src/gentrade_server/routers/public.py | 18 ++--------------- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/gentrade_server/model.py b/src/gentrade_server/model.py index 98d326f..4cc5f55 100644 --- a/src/gentrade_server/model.py +++ b/src/gentrade_server/model.py @@ -1,8 +1,11 @@ -from pydantic import BaseModel, Field -from pydantic_settings import BaseSettings, SettingsConfigDict -from pydantic import Field, field_validator +""" +Model +""" from typing import List +from pydantic import BaseModel, Field, field_validator +from pydantic_settings import BaseSettings, SettingsConfigDict + class HealthCheck(BaseModel): """ Response model to validate and return when performing a health check. @@ -11,6 +14,10 @@ class HealthCheck(BaseModel): status: str = Field("OK") class Settings(BaseSettings): + """ + Settings + """ + model_config = SettingsConfigDict(enable_decoding=False) """ @@ -27,6 +34,22 @@ class Settings(BaseSettings): @field_validator('ntp_servers', mode='before') @classmethod def decode_ntp_servers(cls, v: str) -> List[str]: + """decode function override + + Args: + v (str): input string + + Returns: + List[str]: splitted list for all NTP servers + """ return v.split(',') settings = Settings() + +class Market(BaseModel): + """ + Response model to validate and return when performing a health check. + """ + + name: str = Field(...) + type: str = Field(...) diff --git a/src/gentrade_server/routers/admin.py b/src/gentrade_server/routers/admin.py index ee440f1..6cdeeb7 100644 --- a/src/gentrade_server/routers/admin.py +++ b/src/gentrade_server/routers/admin.py @@ -1,7 +1,9 @@ +''' +Admin portal +''' import logging from fastapi import APIRouter, Depends -from pydantic import BaseModel, Field from ..model import settings, Settings from ..auth import get_user @@ -14,4 +16,5 @@ async def get_settings(user: dict = Depends(get_user)) -> Settings: """ Get server settings """ + LOG.info(user) return settings diff --git a/src/gentrade_server/routers/public.py b/src/gentrade_server/routers/public.py index 9464092..f20b7b7 100644 --- a/src/gentrade_server/routers/public.py +++ b/src/gentrade_server/routers/public.py @@ -9,10 +9,9 @@ from dateutil.tz import tzlocal from fastapi import APIRouter -from pydantic import BaseModel, Field from ..datahub import DataHub -from ..model import HealthCheck, settings +from ..model import HealthCheck, Market LOG = logging.getLogger(__name__) @@ -42,20 +41,7 @@ async def get_server_time(): } @router.get("/markets/") -async def get_markets(): - """ - Get markets - """ - retval = {} - for _, market in DataHub.inst().markets.items(): - retval[market.market_id] = { - "name": market.name, - "type": market.market_type, - } - return retval - -@router.get("/markets/") -async def get_markets2(): +async def get_markets() -> Market: """ Get markets """ From 16ac8412bc84659cf71fa098bc6b62db44befad0 Mon Sep 17 00:00:00 2001 From: Lu Ken Date: Thu, 2 Jan 2025 15:53:22 +0800 Subject: [PATCH 3/3] add more model for restful API Signed-off-by: Lu Ken --- src/gentrade_server/model.py | 27 ++++++++--- src/gentrade_server/routers/public.py | 64 +++++++++++++-------------- 2 files changed, 52 insertions(+), 39 deletions(-) diff --git a/src/gentrade_server/model.py b/src/gentrade_server/model.py index 4cc5f55..37d5dc3 100644 --- a/src/gentrade_server/model.py +++ b/src/gentrade_server/model.py @@ -10,19 +10,14 @@ class HealthCheck(BaseModel): """ Response model to validate and return when performing a health check. """ - status: str = Field("OK") class Settings(BaseSettings): """ Settings """ - model_config = SettingsConfigDict(enable_decoding=False) - """ - Settings - """ openai_api_key: str = "" openai_api_url: str = "" openai_api_model: str = "gpt-3.5-turbo" @@ -50,6 +45,28 @@ class Market(BaseModel): """ Response model to validate and return when performing a health check. """ + name: str = Field(...) + type: str = Field(...) +class Asset(BaseModel): + """ + Asset Model_ + """ name: str = Field(...) type: str = Field(...) + market: str = Field(...) + quote: str = Field(...) + cik: int = Field(None, description="only for US stock") + symbol: str = Field(None, description="only for crypto") + base: str = Field(None, description="only for crypto") + +class OHLCV(BaseModel): + """ + OHLCV model + """ + time: int = Field(..., description="UTC timestamp in seconds") + open: float = Field(...) + high: float = Field(...) + low: float = Field(...) + close: float = Field(...) + vol: float = Field(...) diff --git a/src/gentrade_server/routers/public.py b/src/gentrade_server/routers/public.py index f20b7b7..525a378 100644 --- a/src/gentrade_server/routers/public.py +++ b/src/gentrade_server/routers/public.py @@ -8,10 +8,10 @@ import datetime from dateutil.tz import tzlocal -from fastapi import APIRouter +from fastapi import APIRouter, HTTPException from ..datahub import DataHub -from ..model import HealthCheck, Market +from ..model import HealthCheck, Market, Asset, OHLCV LOG = logging.getLogger(__name__) @@ -24,7 +24,6 @@ async def get_health() -> HealthCheck: """ return HealthCheck(status="OK") - @router.get("/server_time") async def get_server_time(): """ @@ -40,8 +39,8 @@ async def get_server_time(): 'timestamp_server': int(curr_ts) } -@router.get("/markets/") -async def get_markets() -> Market: +@router.get("/markets") +async def get_markets() -> dict[str, Market]: """ Get markets """ @@ -53,36 +52,33 @@ async def get_markets() -> Market: } return retval -@router.get("/assets/") -async def get_assets(market_id:str=""): - """ - Get assets +@router.get("/markets/{market_id}/assets") +async def get_assets(market_id:str="b13a4902-ad9d-11ef-a239-00155d3ba217", + start:int=0, limit:int=1000) -> list[Asset]: + """Get assets array, The maximus lenth is 1000 + + Args: + market_id (str, optional): Market ID string. Defaults to + "b13a4902-ad9d-11ef-a239-00155d3ba217". + start (int, optional): Start index. Defaults to 0. + + Returns: + dict[str, Asset]: _description_ """ - ret = {} - markets = [] - if len(market_id) != 0 and market_id not in DataHub.inst().markets: - LOG.error("could not find the market %s", market_id) - return ret - if len(market_id) == 0: - for id_ in DataHub.inst().markets: - markets.append(id_) - else: - markets.append(market_id) - - for id_ in markets: - market_inst = DataHub.inst().markets[id_] - for asset in market_inst.assets.values(): - if market_inst.market_id == "b13a4902-ad9d-11ef-a239-00155d3ba217" and \ - asset.asset_type == "spot": - ret[asset.name] = asset.to_dict() - elif market_inst.market_id == "5784f1f5-d8f6-401d-8d24-f685a3812f2d" and \ - asset.asset_type == "stock": - ret[asset.name] = asset.to_dict() - return ret - -@router.get("/asset/fetch_ohlcv/") + markets = DataHub.inst().markets + + if market_id not in markets: + raise HTTPException(status_code=404, detail="Item not found") + + assets = list(markets[market_id].assets.values()) + + if start > len(assets) - 1: + raise HTTPException(status_code=404, detail="Item not found") + return [item.to_dict() for item in assets[start:min(start + limit, len(assets))]] + +@router.get("/asset/fetch_ohlcv") async def fetch_ohlcv(assetname:str='btc_usdt', interval="1d", - since:int=-1, to:int=-1,limit:int=300): + since:int=-1, to:int=-1,limit:int=300) -> list[OHLCV]: """fetch ohlcv Args: @@ -96,7 +92,7 @@ async def fetch_ohlcv(assetname:str='btc_usdt', interval="1d", _type_: _description_ """ retval = {} - LOG.info("fetch_ohlcv: %s", assetname) + LOG.info("fetch_ohlcv: %s, interval: %s", assetname, interval) asset = DataHub.inst().get_asset(assetname) if asset is not None: ret = asset.fetch_ohlcv(interval, since, to, limit)