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 os
import matplotlib.pyplot as plt import matplotlib.pyplot as plt # type: ignore
import torch import torch
import torch.nn as nn import torch.nn as nn
from torchvision.datasets import ImageFolder # type: ignore from torchvision.datasets import ImageFolder # type: ignore
@ -19,7 +19,7 @@ def get_labels(input_dir, img_size):
transforms.Resize(img_size), transforms.Resize(img_size),
transforms.RandomHorizontalFlip(), transforms.RandomHorizontalFlip(),
transforms.ToTensor(), 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) 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 = torchvision.models.resnet50(weights=ResNet50_Weights.DEFAULT)
model.fc = nn.Sequential( model.fc = nn.Sequential(
nn.Dropout(0.5), # Регуляризация 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 return model
model = torch.load(model_path, map_location=device, weights_only=False) model = torch.load(model_path, map_location=device, weights_only=False)

View File

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

View File

@ -1,93 +1,35 @@
from pathlib import Path from pathlib import Path
import os
import markdown import markdown
from PIL import Image from litestar import (
from starlite import (
Controller, Controller,
StaticFilesConfig,
get, get,
post, post,
Body,
MediaType, MediaType,
RequestEncodingType, Litestar,
Starlite,
UploadFile,
Template,
TemplateConfig,
HTTPException
) )
from starlite.contrib.jinja import JinjaTemplateEngine from litestar.enums import RequestEncodingType
import io from litestar.datastructures import UploadFile
import os from litestar.params import Body
import json 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 from server.services.descriptions import CharactersService, Breed, CharactersRepository
import torch.nn.functional as F from server.services.recognizer import RecognizerService, RecognizerRepository
os.environ["CUDA_VISIBLE_DEVICES"] = "-1" os.environ["CUDA_VISIBLE_DEVICES"] = "-1"
def load_model(model_path, device="cpu"): recognizer_service = RecognizerService(RecognizerRepository())
model = torch.load(model_path, map_location=device, weights_only=False) characters_service = CharactersService(CharactersRepository())
model.eval()
return model
DOG_MODEL = load_model("server/models/dogs_model.pth") class BreedsController(Controller):
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):
path = "/beerds" path = "/beerds"
@post("/dogs") @post("/dogs")
@ -95,96 +37,69 @@ class BeerdsController(Controller):
self, data: UploadFile = Body(media_type=RequestEncodingType.MULTI_PART) self, data: UploadFile = Body(media_type=RequestEncodingType.MULTI_PART)
) -> dict: ) -> dict:
body = await data.read() body = await data.read()
return await recognizer_service.predict_dog_image(body)
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,
}
@post("/cats") @post("/cats")
async def beerds_cats( async def beerds_cats(
self, data: UploadFile = Body(media_type=RequestEncodingType.MULTI_PART) self, data: UploadFile = Body(media_type=RequestEncodingType.MULTI_PART)
) -> dict: ) -> dict:
body = await data.read() body = await data.read()
return await recognizer_service.predict_cat_image(body)
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,
}
class BaseController(Controller): class DescriptionController(Controller):
path = "/" path = "/"
@get("/") @get("/")
async def dogs(self) -> Template: async def dogs(self) -> Template:
return Template(name="dogs.html") return Template(template_name="dogs.html")
@get("/cats") @get("/cats")
async def cats(self) -> Template: async def cats(self) -> Template:
return Template(name="cats.html") return Template(template_name="cats.html")
@get("/contacts") @get("/contacts")
async def contacts(self) -> Template: async def contacts(self) -> Template:
return Template(name="contacts.html") return Template(template_name="contacts.html")
@get("/donate") @get("/donate")
async def donate(self) -> Template: async def donate(self) -> Template:
return Template(name="donate.html") return Template(template_name="donate.html")
@get("/dogs-characteristics") @get("/dogs-characteristics")
async def dogs_characteristics(self) -> Template: 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}") @get("/dogs-characteristics/{name:str}")
async def beer_description(self, name: str) -> Template: async def beer_description(self, name: str) -> Template:
data = [b for b in DOGS_BEERS if b.get("alias") == name] breed = await characters_service.get_character(name)
if len(data) == 0: if breed is None:
raise HTTPException(status_code=404, detail="Порода не найдена") raise HTTPException(status_code=404, detail="Порода не найдена")
return Template(name="beers-description.html", context={ images = await recognizer_service.images_dogs()
"text": markdown.markdown(data[0].get("description")), return Template(
"title": data[0].get("name"), template_name="beers-description.html",
"images": [f"/static/assets/dog/{name}/{i}" for i in IMAGES['dog'][name]], 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) @get("/sitemap.xml", media_type=MediaType.XML)
async def sitemaps(self) -> bytes: async def sitemaps(self) -> bytes:
breeds: list[Breed] = await characters_service.get_characters()
lastmod = "2025-10-04T19:01:03+00:00" lastmod = "2025-10-04T19:01:03+00:00"
beers_url = "" beers_url = ""
for b in DOGS_BEERS: for b in breeds:
beers_url += f''' beers_url += f"""
<url> <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> <lastmod>{lastmod}</lastmod>
</url> </url>
''' """
return f"""<?xml version="1.0" encoding="UTF-8"?> return f"""<?xml version="1.0" encoding="UTF-8"?>
<urlset <urlset
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
@ -215,7 +130,6 @@ class BaseController(Controller):
</urlset> </urlset>
""".encode() """.encode()
@get("/robots.txt", media_type=MediaType.TEXT) @get("/robots.txt", media_type=MediaType.TEXT)
async def robots(self) -> str: async def robots(self) -> str:
return """ return """
@ -226,11 +140,12 @@ Sitemap: https://xn-----6kcp3cadbabfh8a0a.xn--p1ai/sitemap.xml
""" """
app = Starlite( app = Litestar(
debug=True, debug=True,
route_handlers=[BeerdsController, BaseController], route_handlers=[
static_files_config=[ BreedsController,
StaticFilesConfig(directories=[Path("server/static")], path="/static"), DescriptionController,
create_static_files_router(path="/static", directories=["server/static"]),
], ],
template_config=TemplateConfig( template_config=TemplateConfig(
directory=Path("server/templates"), 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