Commit
ยท
4c52fbf
1
Parent(s):
bcffe48
- .dockerignore +8 -0
- .gitignore +6 -0
- README.md +8 -4
- app.py +172 -0
- database_conn.py +52 -0
- requirements.txt +9 -0
.dockerignore
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/
|
| 2 |
+
*.pyc
|
| 3 |
+
*.pyo
|
| 4 |
+
*.pyd
|
| 5 |
+
*.log
|
| 6 |
+
.git
|
| 7 |
+
.gitignore
|
| 8 |
+
.venv/
|
.gitignore
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
__pycache__/
|
| 2 |
+
*.pyc
|
| 3 |
+
*.pyo
|
| 4 |
+
*.pyd
|
| 5 |
+
*.log
|
| 6 |
+
.venv/
|
README.md
CHANGED
|
@@ -1,10 +1,14 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
| 10 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: FastAPI Hgf Test1
|
| 3 |
+
emoji: ๐ฆ
|
| 4 |
+
colorFrom: green
|
| 5 |
+
colorTo: green
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
---
|
| 9 |
|
| 10 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 11 |
+
|
| 12 |
+
py -3.10 -m uv venv venv
|
| 13 |
+
|
| 14 |
+
.\venv\Scripts\Activate.ps1
|
app.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from fastapi import FastAPI, Depends, HTTPException
|
| 2 |
+
from pydantic import BaseModel
|
| 3 |
+
import uvicorn
|
| 4 |
+
import asyncpg
|
| 5 |
+
from sentence_transformers import SentenceTransformer
|
| 6 |
+
import torch
|
| 7 |
+
from typing import List, Dict, Any, Union
|
| 8 |
+
|
| 9 |
+
from database_conn import connect_to_db, close_db_connection, get_db_connection
|
| 10 |
+
|
| 11 |
+
# 1. FastAPI ์ ํ๋ฆฌ์ผ์ด์
์ธ์คํด์ค ์์ฑ
|
| 12 |
+
app = FastAPI(title="Simple FastAPI Demo")
|
| 13 |
+
|
| 14 |
+
# --- 1. Initialization and Model Loading ---
|
| 15 |
+
# Initialize the FastAPI application
|
| 16 |
+
app = FastAPI(
|
| 17 |
+
title="Sentence Transformer Embedding Gemma API",
|
| 18 |
+
description="An API for semantic similarity using google/embeddinggemma-300m.",
|
| 19 |
+
version="1.0.0"
|
| 20 |
+
)
|
| 21 |
+
|
| 22 |
+
# Load the SentenceTransformer model once when the application starts
|
| 23 |
+
# This is a synchronous operation, which is fine for app startup.
|
| 24 |
+
try:
|
| 25 |
+
model = SentenceTransformer("google/embeddinggemma-300m")
|
| 26 |
+
# Set the model to evaluation mode
|
| 27 |
+
model.eval()
|
| 28 |
+
print("Model 'google/embeddinggemma-300m' loaded successfully.")
|
| 29 |
+
except Exception as e:
|
| 30 |
+
print(f"Error loading model: {e}")
|
| 31 |
+
# In a real application, you might want to raise an exception or handle this more gracefully
|
| 32 |
+
# For simplicity, we'll let the app potentially fail if the model can't load.
|
| 33 |
+
model = None # Keep model as None if loading failed
|
| 34 |
+
|
| 35 |
+
# 2. ๋ฐ์ดํฐ ์ ํจ์ฑ ๊ฒ์ฌ๋ฅผ ์ํ Pydantic ๋ชจ๋ธ ์ ์
|
| 36 |
+
# ํด๋ผ์ด์ธํธ๋ก๋ถํฐ ๋ฐ์ ์์ฒญ(Request) ๋ฐ์ดํฐ ๊ตฌ์กฐ๋ฅผ ์ ์ํฉ๋๋ค.
|
| 37 |
+
class Item(BaseModel):
|
| 38 |
+
name: str
|
| 39 |
+
price: float
|
| 40 |
+
is_offer: bool | None = None
|
| 41 |
+
|
| 42 |
+
@app.on_event("startup")
|
| 43 |
+
async def startup_event():
|
| 44 |
+
"""์๋ฒ ์์ ์ DB ์ฐ๊ฒฐ ํ ์ด๊ธฐํ"""
|
| 45 |
+
await connect_to_db()
|
| 46 |
+
|
| 47 |
+
@app.on_event("shutdown")
|
| 48 |
+
async def shutdown_event():
|
| 49 |
+
"""์๋ฒ ์ข
๋ฃ ์ DB ์ฐ๊ฒฐ ํ ์ข
๋ฃ"""
|
| 50 |
+
await close_db_connection()
|
| 51 |
+
|
| 52 |
+
# --- API ์๋ํฌ์ธํธ ์ ์ ---
|
| 53 |
+
|
| 54 |
+
# 3. ๋ฃจํธ ์๋ํฌ์ธํธ (GET /)
|
| 55 |
+
@app.get("/")
|
| 56 |
+
def read_root():
|
| 57 |
+
result={"success":True,"data":None,"msg":""}
|
| 58 |
+
try:
|
| 59 |
+
result["data"]="ok"
|
| 60 |
+
return result
|
| 61 |
+
except Exception as e:
|
| 62 |
+
result["success"] = False
|
| 63 |
+
result["msg"]=f"server error. {e!r}"
|
| 64 |
+
return result
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
class MakeTextEmbedding(BaseModel):
|
| 68 |
+
"""
|
| 69 |
+
Input structure for the similarity endpoint.
|
| 70 |
+
"""
|
| 71 |
+
query: str
|
| 72 |
+
documents: List[str]
|
| 73 |
+
|
| 74 |
+
# ์ถ๋ ฅ ํด๋์ค: ๊ฒฐ๊ณผ๊ฐ ์ฑ๊ณต ์ฌ๋ถ์ ์๋ฒ ๋ฉ ๋ฐ์ดํฐ ๋ฆฌ์คํธ๋ฅผ ํฌํจํ๋๋ก ์ ์
|
| 75 |
+
class EmbeddingOutput(BaseModel):
|
| 76 |
+
success: bool
|
| 77 |
+
msg: str
|
| 78 |
+
# ์๋ฒ ๋ฉ ๊ฒฐ๊ณผ๋ ์ค์ฒฉ ๋ฆฌ์คํธ ํํ๊ฐ ๋ฉ๋๋ค. (๋ฌธ์ ์, ์๋ฒ ๋ฉ ์ฐจ์)
|
| 79 |
+
data: Union[List[List[float]], None] = None
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
@app.post("/make_text_embedding", summary="Calculate semantic similarity and find the best match")
|
| 83 |
+
async def calculate_similarity(data: MakeTextEmbedding):
|
| 84 |
+
result={"success":True,"data":None,"msg":""}
|
| 85 |
+
try:
|
| 86 |
+
if model is None:
|
| 87 |
+
result["success"] = False
|
| 88 |
+
result["msg"]=f"Model not loaded. Service is unavailable."
|
| 89 |
+
return result
|
| 90 |
+
|
| 91 |
+
# The 'with torch.no_grad():' block is essential for efficient inference
|
| 92 |
+
with torch.no_grad():
|
| 93 |
+
# Encode the query (single vector)
|
| 94 |
+
document_embeddings = model.encode_document(data.documents)
|
| 95 |
+
embeddings_list = document_embeddings.tolist()
|
| 96 |
+
|
| 97 |
+
result["data"]=embeddings_list
|
| 98 |
+
return result
|
| 99 |
+
except Exception as e:
|
| 100 |
+
result["success"] = False
|
| 101 |
+
result["msg"]=f"server error. {e!r}"
|
| 102 |
+
return result
|
| 103 |
+
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
# ----------------------------------------------------
|
| 110 |
+
# 2. API ์๋ํฌ์ธํธ: SELECT NOW() (์ฉ ์ฟผ๋ฆฌ ์คํ)
|
| 111 |
+
# ----------------------------------------------------
|
| 112 |
+
@app.get("/time", response_model=Dict[str, Any])
|
| 113 |
+
async def get_db_time(
|
| 114 |
+
# get_db_connection์ ํตํด asyncpg.Connection ๊ฐ์ฒด๋ง ์ฃผ์
๋ฐ์ต๋๋ค.
|
| 115 |
+
conn: asyncpg.Connection = Depends(get_db_connection)
|
| 116 |
+
):
|
| 117 |
+
"""
|
| 118 |
+
DB์ ์ ์ํ์ฌ ํ์ฌ ์๊ฐ์ ์กฐํํ๋ ์ฉ SQL ์ฟผ๋ฆฌ(SELECT NOW())๋ฅผ ์คํํฉ๋๋ค.
|
| 119 |
+
"""
|
| 120 |
+
result = {"success": True, "data": None, "msg": ""}
|
| 121 |
+
|
| 122 |
+
try:
|
| 123 |
+
# **์ฌ๊ธฐ์ ์ฉ SQL ์ฟผ๋ฆฌ๋ฅผ ์ง์ ์์ฑํ๊ณ ์คํํฉ๋๋ค.**
|
| 124 |
+
query = "SELECT NOW();"
|
| 125 |
+
|
| 126 |
+
# ์ฟผ๋ฆฌ ์คํ (fetchval()์ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ์ ์ฒซ ํ, ์ฒซ ์ด์ ๊ฐ๋ง ๋ฐํํฉ๋๋ค.)
|
| 127 |
+
# asyncpg๊ฐ DB ์๊ฐ์ Python datetime ๊ฐ์ฒด๋ก ๋ณํํด ์ค๋๋ค.
|
| 128 |
+
records = await conn.fetch(query)
|
| 129 |
+
data_list_of_dicts: List[Dict[str, Any]] = [
|
| 130 |
+
# dict(record)๋ฅผ ์ฌ์ฉํ์ฌ Record ๊ฐ์ฒด๋ฅผ ์ผ๋ฐ ํ์ด์ฌ ๋์
๋๋ฆฌ๋ก ๋ณํ
|
| 131 |
+
dict(record) for record in records
|
| 132 |
+
]
|
| 133 |
+
|
| 134 |
+
# ๊ฒฐ๊ณผ๋ฅผ ๋ฌธ์์ด๋ก ๋ณํํ์ฌ JSON ์๋ต์ ๋ด์ต๋๋ค.
|
| 135 |
+
result["data"] = data_list_of_dicts[0]
|
| 136 |
+
|
| 137 |
+
except Exception as e:
|
| 138 |
+
# ์ฟผ๋ฆฌ ์คํ ์ค ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ฉด 500 ์๋ฌ๋ฅผ ๋ฐํ
|
| 139 |
+
result["success"] = False
|
| 140 |
+
result["msg"] = f"Database query error: {e!r}"
|
| 141 |
+
|
| 142 |
+
return result
|
| 143 |
+
|
| 144 |
+
# 4. ์ฟผ๋ฆฌ ํ๋ผ๋ฏธํฐ๋ฅผ ๋ฐ๋ ์๋ํฌ์ธํธ (GET /items/{item_id})
|
| 145 |
+
@app.get("/items")
|
| 146 |
+
def read_item(q: str | None = None):
|
| 147 |
+
"""
|
| 148 |
+
ํน์ ID์ ์์ดํ
์ ๋ณด๋ฅผ ๊ฐ์ ธ์ต๋๋ค.
|
| 149 |
+
|
| 150 |
+
:param item_id: ์์ดํ
์ ๊ณ ์ ID (๊ฒฝ๋ก ๋งค๊ฐ๋ณ์)
|
| 151 |
+
:param q: ์ ํ์ ์ธ ๊ฒ์ ๋ฌธ์์ด (์ฟผ๋ฆฌ ๋งค๊ฐ๋ณ์)
|
| 152 |
+
"""
|
| 153 |
+
return {"q": q, "description": "This is a query test."}
|
| 154 |
+
|
| 155 |
+
# 5. ์์ฒญ ๋ณธ๋ฌธ(Body)์ ๋ฐ๋ ์๋ํฌ์ธํธ (POST /items/)
|
| 156 |
+
@app.post("/items/")
|
| 157 |
+
def create_item(item: Item):
|
| 158 |
+
"""
|
| 159 |
+
์๋ก์ด ์์ดํ
์ ์์ฑํ๊ณ ์ ๋ณด๋ฅผ ๋ฐํํฉ๋๋ค.
|
| 160 |
+
|
| 161 |
+
:param item: Item Pydantic ๋ชจ๋ธ์ ์ ์๋ ๋ฐ์ดํฐ ๊ตฌ์กฐ
|
| 162 |
+
"""
|
| 163 |
+
# ์ค์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํ๋ ๋์ , ๊ฐ๋จํ ์ฒ๋ฆฌ๋ฅผ ์ํ
|
| 164 |
+
if item.price > 100.0:
|
| 165 |
+
item.name = f"Premium {item.name}"
|
| 166 |
+
|
| 167 |
+
return {"message": "Item created successfully", "item_data": item}
|
| 168 |
+
|
| 169 |
+
|
| 170 |
+
if __name__ == "__main__":
|
| 171 |
+
# --reload ์ต์
์ ์ถ๊ฐํ์ฌ ์ฝ๋๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์๋ ์ฌ์์๋๊ฒ ์ค์ ํฉ๋๋ค.
|
| 172 |
+
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True)
|
database_conn.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# database_conn.py
|
| 2 |
+
|
| 3 |
+
import asyncpg
|
| 4 |
+
from typing import AsyncGenerator
|
| 5 |
+
|
| 6 |
+
# [์ฃผ์] ์ด ์ ๋ณด๋ ์ค์ ์๋น์ค์์๋ ํ๊ฒฝ ๋ณ์๋ก ๊ด๋ฆฌํด์ผ ํฉ๋๋ค.
|
| 7 |
+
DB_USER = "neondb_owner"
|
| 8 |
+
DB_PASSWORD = "npg_rFA9DgloybO0" # ์ค์ ๋น๋ฐ๋ฒํธ ์ฌ์ฉ
|
| 9 |
+
DB_HOST = "ep-rapid-truth-ad2e6rnq-pooler.c-2.us-east-1.aws.neon.tech"
|
| 10 |
+
DB_NAME = "neondb"
|
| 11 |
+
DB_PORT = 5432
|
| 12 |
+
DB_SSL = 'require'
|
| 13 |
+
|
| 14 |
+
# ๊ธ๋ก๋ฒ DB ์ฐ๊ฒฐ ํ ๋ณ์
|
| 15 |
+
pool: asyncpg.Pool = None
|
| 16 |
+
|
| 17 |
+
# ์๋ฒ ์์ ์ ํธ์ถ (FastAPI @app.on_event("startup")์์ ์ฌ์ฉ)
|
| 18 |
+
async def connect_to_db():
|
| 19 |
+
global pool
|
| 20 |
+
print("DB ์ฐ๊ฒฐ ํ ์ด๊ธฐํ ์ค...")
|
| 21 |
+
try:
|
| 22 |
+
pool = await asyncpg.create_pool(
|
| 23 |
+
user=DB_USER,
|
| 24 |
+
password=DB_PASSWORD,
|
| 25 |
+
host=DB_HOST,
|
| 26 |
+
database=DB_NAME,
|
| 27 |
+
port=DB_PORT,
|
| 28 |
+
ssl=DB_SSL
|
| 29 |
+
)
|
| 30 |
+
print("DB ์ฐ๊ฒฐ ์ฑ๊ณต.")
|
| 31 |
+
except Exception as e:
|
| 32 |
+
print(f"DB ์ฐ๊ฒฐ ์คํจ: {e}")
|
| 33 |
+
raise e
|
| 34 |
+
|
| 35 |
+
# ์๋ฒ ์ข
๋ฃ ์ ํธ์ถ (FastAPI @app.on_event("shutdown")์์ ์ฌ์ฉ)
|
| 36 |
+
async def close_db_connection():
|
| 37 |
+
global pool
|
| 38 |
+
if pool:
|
| 39 |
+
print("DB ์ฐ๊ฒฐ ํ ์ข
๋ฃ ์ค...")
|
| 40 |
+
await pool.close()
|
| 41 |
+
print("DB ์ฐ๊ฒฐ ํ ์ข
๋ฃ ์๋ฃ.")
|
| 42 |
+
|
| 43 |
+
# API ์๋ํฌ์ธํธ์ DB ์ฐ๊ฒฐ ๊ฐ์ฒด๋ฅผ ์ฃผ์
ํ๋ ํจ์ (FastAPI Depends์์ ์ฌ์ฉ)
|
| 44 |
+
async def get_db_connection() -> AsyncGenerator[asyncpg.Connection, None]:
|
| 45 |
+
"""DB ์ฐ๊ฒฐ ํ์์ ์ปค๋ฅ์
์ ๊ฐ์ ธ์ ์๋ํฌ์ธํธ์ ์ ๊ณตํฉ๋๋ค."""
|
| 46 |
+
global pool
|
| 47 |
+
if not pool:
|
| 48 |
+
raise ConnectionError("DB ์ฐ๊ฒฐ ํ์ด ์ด๊ธฐํ๋์ง ์์์ต๋๋ค.")
|
| 49 |
+
|
| 50 |
+
# async with ๊ตฌ๋ฌธ์ผ๋ก ์ฐ๊ฒฐ์ ๊ฐ์ ธ์ yield ํ, ์๋์ผ๋ก ํ์ ๋ฐํํฉ๋๋ค.
|
| 51 |
+
async with pool.acquire() as connection:
|
| 52 |
+
yield connection
|
requirements.txt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
fastapi
|
| 2 |
+
uvicorn[standard]
|
| 3 |
+
sqlalchemy
|
| 4 |
+
asyncpg # ๋น๋๊ธฐ PostgreSQL ๋๋ผ์ด๋ฒ (psycopg2-binary ๋์ ์ฌ์ฉ)
|
| 5 |
+
pydantic-settings # ํ๊ฒฝ ๋ณ์ ๊ด๋ฆฌ๋ฅผ ์ํ Pydantic ๋ผ์ด๋ธ๋ฌ๋ฆฌ
|
| 6 |
+
python-dotenv # ๋ก์ปฌ ๊ฐ๋ฐ ํ๊ฒฝ์์ .env ํ์ผ ๋ก๋๋ฅผ ์ํด ํ์ (์ ํ ์ฌํญ)
|
| 7 |
+
pydantic # fastapi req ์ ํจ์ฑ ๊ฒ์ฌ
|
| 8 |
+
scikit-learn
|
| 9 |
+
sentence-transformers
|