refactoring
Gitea Actions Demo / build_and_push (push) Failing after 1m6s Details

This commit is contained in:
artem 2025-11-21 20:42:45 +03:00
parent 338032d6e8
commit 84229d2de9
266 changed files with 1279 additions and 1154 deletions

View File

@ -1 +1 @@
3.11
cpython-3.13.5-linux-x86_64-gnu

View File

@ -1,5 +1,5 @@
import os
import matplotlib.pyplot as plt
import matplotlib.pyplot as plt # type: ignore
import torch
import torch.nn as nn
from torchvision.datasets import ImageFolder # type: ignore
@ -19,7 +19,7 @@ def get_labels(input_dir, img_size):
transforms.Resize(img_size),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]),
]
)
dataset = ImageFolder(root=input_dir, transform=transform)
@ -47,7 +47,7 @@ def load_model(model_path: str, labels_dict: dict, device: str = "cuda") -> nn.M
model = torchvision.models.resnet50(weights=ResNet50_Weights.DEFAULT)
model.fc = nn.Sequential(
nn.Dropout(0.5), # Регуляризация
torch.nn.Linear(model.fc.in_features, len(labels_dict))
torch.nn.Linear(model.fc.in_features, len(labels_dict)),
)
return model
model = torch.load(model_path, map_location=device, weights_only=False)

View File

@ -3,21 +3,31 @@ name = "ai"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = "~=3.11"
requires-python = "~=3.13"
dependencies = [
"granian>=2.2.4",
"granian==2.5",
"jinja2>=3.1.6",
"starlite>=1.51.16",
"numpy==1.23.5",
"numpy==2.3.4",
"pillow>=11.1.0",
"markdown>=3.9",
"aiocache",
"torch>=2.9.1",
"ruff>=0.14.5",
"mypy>=1.18.2",
"uvicorn>=0.38.0",
"pydantic>=2.12.4",
"litestar==2.18.0",
"ujson>=5.11.0",
"torchvision>=0.24.1",
"types-requests>=2.32.4.20250913",
"types-markdown>=3.10.0.20251106",
]
[project.optional-dependencies]
default = [
"torch>=2.6.0",
"torch>=2.9.1",
"torchvision>=0.21.0",
"mypy>=1.15.0",
"mypy>=1.18",
"pyqt5>=5.15.11",
"requests>=2.32.3",
"ruff>=0.11.5",
@ -25,35 +35,15 @@ default = [
"matplotlib>=3.10.1",
]
api = [
"torch>=2.6.0",
"torchvision>=0.21.0",
]
[tool.uv]
conflicts = [
[
{ extra = "default" },
{ extra = "api" },
],
]
[tool.uv.sources]
torch = [
{ index = "pytorch-cu124", extra = "default" },
{ index = "pytorch-cpu", extra = "api" },
{ index = "pytorch-cpu", extra = "default" },
]
torchvision = [
{ index = "pytorch-cu124", extra = "default" },
{ index = "pytorch-cpu", extra = "api" },
{ index = "pytorch-cpu", extra = "default" },
]
[[tool.uv.index]]
name = "pytorch-cu124"
url = "https://download.pytorch.org/whl/cu124"
explicit = true
[[tool.uv.index]]
name = "pytorch-cpu"
url = "https://download.pytorch.org/whl/cpu"

View File

@ -1,93 +1,35 @@
from pathlib import Path
import os
import markdown
from PIL import Image
from starlite import (
from litestar import (
Controller,
StaticFilesConfig,
get,
post,
Body,
MediaType,
RequestEncodingType,
Starlite,
UploadFile,
Template,
TemplateConfig,
HTTPException
Litestar,
)
from starlite.contrib.jinja import JinjaTemplateEngine
import io
import os
import json
from litestar.enums import RequestEncodingType
from litestar.datastructures import UploadFile
from litestar.params import Body
from litestar.exceptions import HTTPException
from litestar.contrib.jinja import JinjaTemplateEngine
from litestar.template.config import TemplateConfig
from litestar.response import Template
from litestar.static_files import create_static_files_router
import torch
from torchvision import transforms # type: ignore
import torch.nn.functional as F
from server.services.descriptions import CharactersService, Breed, CharactersRepository
from server.services.recognizer import RecognizerService, RecognizerRepository
os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
def load_model(model_path, device="cpu"):
model = torch.load(model_path, map_location=device, weights_only=False)
model.eval()
return model
recognizer_service = RecognizerService(RecognizerRepository())
characters_service = CharactersService(CharactersRepository())
DOG_MODEL = load_model("server/models/dogs_model.pth")
CAT_MODEL = load_model("server/models/cats_model.pth")
with open("server/meta/labels_dogs.json", "r") as f:
data_labels = f.read()
labels_dogs = json.loads(data_labels)
with open("server/meta/labels_cats.json", "r") as f:
data_labels = f.read()
labels_cats = json.loads(data_labels)
with open("server/meta/images.json", "r") as f:
IMAGES = json.loads(f.read())
def predict_image(image, model, device="cuda") -> list[tuple]:
img_size = (224, 224)
preprocess = transforms.Compose(
[
transforms.Resize(img_size),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),
]
)
input_tensor = preprocess(image)
input_batch = input_tensor.unsqueeze(0).to(device) # Добавляем dimension для батча
with torch.no_grad():
output = model(input_batch)
probabilities = torch.nn.functional.softmax(output[0], dim=0)
k = 5
topk_probs, predicted_idx = torch.topk(probabilities, k)
data = []
for i in range(k):
data.append((predicted_idx[i].item(), float(topk_probs[i].item())))
return data
breed_dir = Path("server/meta/breed_descriptions")
DOGS_BEERS = []
# Идем по каждому текстовому файлу с описанием породы
for breed_file in breed_dir.glob("*.txt"):
breed_name = breed_file.stem # имя файла без расширения - название породы
description = breed_file.read_text(encoding="utf-8") # читаем описание из файла
DOGS_BEERS.append({
"name": breed_name.replace("_", " "),
"alias": breed_file.stem,
"description": description.strip()
})
DOGS_BEERS.sort(key=lambda b: b["name"])
class BeerdsController(Controller):
class BreedsController(Controller):
path = "/beerds"
@post("/dogs")
@ -95,96 +37,69 @@ class BeerdsController(Controller):
self, data: UploadFile = Body(media_type=RequestEncodingType.MULTI_PART)
) -> dict:
body = await data.read()
img_file = Image.open(io.BytesIO(body))
predicted_data = predict_image(img_file, DOG_MODEL, "cpu")
results = {}
images = []
for d in predicted_data:
predicted_idx, probabilities = d
predicted_label = labels_dogs[str(predicted_idx)]
name = predicted_label.replace("_", " ")
images.append({
"name": name,
"url": [f"/static/assets/dog/{predicted_label}/{i}" for i in IMAGES['dog'][predicted_label]]
})
results[probabilities] = name
return {
"results": results,
"images": images,
}
return await recognizer_service.predict_dog_image(body)
@post("/cats")
async def beerds_cats(
self, data: UploadFile = Body(media_type=RequestEncodingType.MULTI_PART)
) -> dict:
body = await data.read()
img_file = Image.open(io.BytesIO(body))
predicted_data = predict_image(img_file, CAT_MODEL, "cpu")
results = {}
images = []
for d in predicted_data:
predicted_idx, probabilities = d
predicted_label = labels_cats[str(predicted_idx)]
name = predicted_label.replace("_", " ")
images.append({
"name": name,
"url": [f"/static/assets/cat/{predicted_label}/{i}" for i in IMAGES['cat'][predicted_label]]
})
results[probabilities] = predicted_label
return {
"results": results,
"images": images,
}
return await recognizer_service.predict_cat_image(body)
class BaseController(Controller):
class DescriptionController(Controller):
path = "/"
@get("/")
async def dogs(self) -> Template:
return Template(name="dogs.html")
return Template(template_name="dogs.html")
@get("/cats")
async def cats(self) -> Template:
return Template(name="cats.html")
return Template(template_name="cats.html")
@get("/contacts")
async def contacts(self) -> Template:
return Template(name="contacts.html")
return Template(template_name="contacts.html")
@get("/donate")
async def donate(self) -> Template:
return Template(name="donate.html")
return Template(template_name="donate.html")
@get("/dogs-characteristics")
async def dogs_characteristics(self) -> Template:
return Template(name="dogs-characteristics.html", context={"breeds": DOGS_BEERS})
breeds = await characters_service.get_characters()
return Template(
template_name="dogs-characteristics.html", context={"breeds": breeds}
)
@get("/dogs-characteristics/{name:str}")
async def beer_description(self, name: str) -> Template:
data = [b for b in DOGS_BEERS if b.get("alias") == name]
if len(data) == 0:
breed = await characters_service.get_character(name)
if breed is None:
raise HTTPException(status_code=404, detail="Порода не найдена")
return Template(name="beers-description.html", context={
"text": markdown.markdown(data[0].get("description")),
"title": data[0].get("name"),
"images": [f"/static/assets/dog/{name}/{i}" for i in IMAGES['dog'][name]],
})
images = await recognizer_service.images_dogs()
return Template(
template_name="beers-description.html",
context={
"text": markdown.markdown(breed.description),
"title": breed.name,
"images": [f"/static/assets/dog/{name}/{i}" for i in images[name]],
},
)
@get("/sitemap.xml", media_type=MediaType.XML)
async def sitemaps(self) -> bytes:
breeds: list[Breed] = await characters_service.get_characters()
lastmod = "2025-10-04T19:01:03+00:00"
beers_url = ""
for b in DOGS_BEERS:
beers_url += f'''
for b in breeds:
beers_url += f"""
<url>
<loc>https://xn-----6kcp3cadbabfh8a0a.xn--p1ai/dogs-characteristics/{b.get("alias")}</loc>
<loc>https://xn-----6kcp3cadbabfh8a0a.xn--p1ai/dogs-characteristics/{b.alias}</loc>
<lastmod>{lastmod}</lastmod>
</url>
'''
"""
return f"""<?xml version="1.0" encoding="UTF-8"?>
<urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
@ -215,7 +130,6 @@ class BaseController(Controller):
</urlset>
""".encode()
@get("/robots.txt", media_type=MediaType.TEXT)
async def robots(self) -> str:
return """
@ -226,11 +140,12 @@ Sitemap: https://xn-----6kcp3cadbabfh8a0a.xn--p1ai/sitemap.xml
"""
app = Starlite(
app = Litestar(
debug=True,
route_handlers=[BeerdsController, BaseController],
static_files_config=[
StaticFilesConfig(directories=[Path("server/static")], path="/static"),
route_handlers=[
BreedsController,
DescriptionController,
create_static_files_router(path="/static", directories=["server/static"]),
],
template_config=TemplateConfig(
directory=Path("server/templates"),

View File

View File

@ -0,0 +1,13 @@
from server.services.descriptions.repository import (
CharactersRepository,
ACharactersRepository,
)
from server.services.descriptions.service import CharactersService
from server.services.descriptions.domain import Breed
__all__ = (
"CharactersRepository",
"ACharactersRepository",
"CharactersService",
"Breed",
)

View File

@ -0,0 +1,8 @@
from dataclasses import dataclass
@dataclass(frozen=True)
class Breed:
name: str
alias: str
description: str

View File

@ -0,0 +1,6 @@
from server.services.descriptions.repository.repository import (
CharactersRepository,
ACharactersRepository,
)
__all__ = ("CharactersRepository", "ACharactersRepository")

Some files were not shown because too many files have changed in this diff Show More