model2service 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- model2service-0.0.1/.devcontainer/devcontainer.json +34 -0
- model2service-0.0.1/.github/workflows/python-publish.yml +70 -0
- model2service-0.0.1/.gitignore +0 -0
- model2service-0.0.1/.vscode/settings.json +3 -0
- model2service-0.0.1/Dockerfile +17 -0
- model2service-0.0.1/PKG-INFO +17 -0
- model2service-0.0.1/README.md +1 -0
- model2service-0.0.1/model2service/__init__.py +9 -0
- model2service-0.0.1/model2service/src/__init__.py +5 -0
- model2service-0.0.1/model2service/src/model2service.py +33 -0
- model2service-0.0.1/model2service/src/model_repositories/__init__.py +2 -0
- model2service-0.0.1/model2service/src/model_repositories/mlflow_repository.py +12 -0
- model2service-0.0.1/model2service/src/model_repositories/model_repository.py +12 -0
- model2service-0.0.1/model2service/src/service_registries/__init__.py +2 -0
- model2service-0.0.1/model2service/src/service_registries/redis_registry.py +39 -0
- model2service-0.0.1/model2service/src/service_registries/service_registry.py +27 -0
- model2service-0.0.1/model2service.egg-info/PKG-INFO +17 -0
- model2service-0.0.1/model2service.egg-info/SOURCES.txt +23 -0
- model2service-0.0.1/model2service.egg-info/dependency_links.txt +1 -0
- model2service-0.0.1/model2service.egg-info/requires.txt +4 -0
- model2service-0.0.1/model2service.egg-info/top_level.txt +1 -0
- model2service-0.0.1/pyproject.toml +39 -0
- model2service-0.0.1/requirements.txt +4 -0
- model2service-0.0.1/setup.cfg +4 -0
- model2service-0.0.1/tests/client.py +28 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
|
2
|
+
// README at: https://github.com/devcontainers/templates/tree/main/src/python
|
|
3
|
+
{
|
|
4
|
+
"name": "Python 3",
|
|
5
|
+
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
|
|
6
|
+
"build": {
|
|
7
|
+
"dockerfile": "../Dockerfile"
|
|
8
|
+
},
|
|
9
|
+
// Features to add to the dev container. More info: https://containers.dev/features.
|
|
10
|
+
// "features": {},
|
|
11
|
+
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
|
12
|
+
// "forwardPorts": [],
|
|
13
|
+
// Use 'postCreateCommand' to run commands after the container is created.
|
|
14
|
+
// "postCreateCommand": "pip3 install --user -r requirements.txt",
|
|
15
|
+
// Configure tool-specific properties.
|
|
16
|
+
"customizations": {
|
|
17
|
+
"vscode": {
|
|
18
|
+
"extensions": [
|
|
19
|
+
"ms-python.black-formatter",
|
|
20
|
+
"ms-python.vscode-python-envs",
|
|
21
|
+
"ms-python.debugpy",
|
|
22
|
+
"ms-python.python",
|
|
23
|
+
"ms-python.vscode-pylance",
|
|
24
|
+
"eamodio.gitlens",
|
|
25
|
+
"ms-toolsai.datawrangler",
|
|
26
|
+
"esbenp.prettier-vscode",
|
|
27
|
+
"ms-toolsai.jupyter",
|
|
28
|
+
"ms-vsliveshare.vsliveshare"
|
|
29
|
+
],
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
|
|
33
|
+
// "remoteUser": "root"
|
|
34
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# This workflow will upload a Python Package to PyPI when a release is created
|
|
2
|
+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries
|
|
3
|
+
|
|
4
|
+
# This workflow uses actions that are not certified by GitHub.
|
|
5
|
+
# They are provided by a third-party and are governed by
|
|
6
|
+
# separate terms of service, privacy policy, and support
|
|
7
|
+
# documentation.
|
|
8
|
+
|
|
9
|
+
name: Upload Python Package
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
release:
|
|
13
|
+
types: [published]
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
release-build:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: "3.x"
|
|
28
|
+
|
|
29
|
+
- name: Build release distributions
|
|
30
|
+
run: |
|
|
31
|
+
# NOTE: put your own distribution build steps here.
|
|
32
|
+
python -m pip install build
|
|
33
|
+
python -m build
|
|
34
|
+
|
|
35
|
+
- name: Upload distributions
|
|
36
|
+
uses: actions/upload-artifact@v4
|
|
37
|
+
with:
|
|
38
|
+
name: release-dists
|
|
39
|
+
path: dist/
|
|
40
|
+
|
|
41
|
+
pypi-publish:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
needs:
|
|
44
|
+
- release-build
|
|
45
|
+
permissions:
|
|
46
|
+
# IMPORTANT: this permission is mandatory for trusted publishing
|
|
47
|
+
id-token: write
|
|
48
|
+
|
|
49
|
+
# Dedicated environments with protections for publishing are strongly recommended.
|
|
50
|
+
# For more information, see: https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment#deployment-protection-rules
|
|
51
|
+
environment:
|
|
52
|
+
name: pypi
|
|
53
|
+
# OPTIONAL: uncomment and update to include your PyPI project URL in the deployment status:
|
|
54
|
+
# url: https://pypi.org/p/YOURPROJECT
|
|
55
|
+
#
|
|
56
|
+
# ALTERNATIVE: if your GitHub Release name is the PyPI project version string
|
|
57
|
+
# ALTERNATIVE: exactly, uncomment the following line instead:
|
|
58
|
+
# url: https://pypi.org/project/YOURPROJECT/${{ github.event.release.name }}
|
|
59
|
+
|
|
60
|
+
steps:
|
|
61
|
+
- name: Retrieve release distributions
|
|
62
|
+
uses: actions/download-artifact@v4
|
|
63
|
+
with:
|
|
64
|
+
name: release-dists
|
|
65
|
+
path: dist/
|
|
66
|
+
|
|
67
|
+
- name: Publish release distributions to PyPI
|
|
68
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
69
|
+
with:
|
|
70
|
+
packages-dir: dist/
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
FROM python:3.10-slim
|
|
2
|
+
RUN apt-get update && \
|
|
3
|
+
apt-get upgrade -y && \
|
|
4
|
+
apt-get install -y build-essential \
|
|
5
|
+
git \
|
|
6
|
+
curl \
|
|
7
|
+
wget \
|
|
8
|
+
&& apt-get clean \
|
|
9
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
WORKDIR /workspace
|
|
13
|
+
|
|
14
|
+
COPY requirements.txt .
|
|
15
|
+
RUN pip install --no-cache-dir -r requirements.txt
|
|
16
|
+
|
|
17
|
+
RUN pip install --upgrade pip
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: model2service
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Author-email: Diego Pinheiro <diego.silva@unicap.br>
|
|
5
|
+
Project-URL: Homepage, https://github.com/projeto-carcara/model2service
|
|
6
|
+
Keywords: service
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: pydantic==2.13.2
|
|
13
|
+
Requires-Dist: debugpy==1.8.20
|
|
14
|
+
Requires-Dist: pika==1.3.2
|
|
15
|
+
Requires-Dist: redis==7.4.0
|
|
16
|
+
|
|
17
|
+
# model2service
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# model2service
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import abc
|
|
4
|
+
from .service_registries import ServiceRegistry, ModelServiceRegistry
|
|
5
|
+
from .model_repositories import ModelRepository, Model
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
# class Model2ServiceMapping(BaseModel):
|
|
10
|
+
# service_registry: ServiceRegistry
|
|
11
|
+
# model_repository: ModelRepository
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class Model2Service():
|
|
15
|
+
|
|
16
|
+
def __init__(self,
|
|
17
|
+
service_registry: ServiceRegistry,
|
|
18
|
+
model_repository: ModelRepository
|
|
19
|
+
):
|
|
20
|
+
self.mode
|
|
21
|
+
|
|
22
|
+
def model_save(self, model: Model):
|
|
23
|
+
self.model_repository.save(model)
|
|
24
|
+
|
|
25
|
+
def model_load(self, model_name: str) -> Model:
|
|
26
|
+
return self.model_repository.load(model_name)
|
|
27
|
+
|
|
28
|
+
def service_register(self, model_service_registry: ModelServiceRegistry):
|
|
29
|
+
self.service_registry.register(model_service_registry)
|
|
30
|
+
|
|
31
|
+
def service_discover(self, model_name: str) -> ModelServiceRegistry:
|
|
32
|
+
return self.service_registry.discovery(model_name)
|
|
33
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from mlflow.pyfunc.model import PythonModel, PythonModelContext
|
|
2
|
+
from .model_repository import Model
|
|
3
|
+
|
|
4
|
+
from .model_repository import ModelRepository
|
|
5
|
+
|
|
6
|
+
class MlflowRepository(ModelRepository):
|
|
7
|
+
def save(self, model: Model):
|
|
8
|
+
pass
|
|
9
|
+
|
|
10
|
+
def load(self) -> Model:
|
|
11
|
+
pass
|
|
12
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from .service_registry import ServiceRegistry, ModelServiceRegistry
|
|
2
|
+
import redis
|
|
3
|
+
import os
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
ENVIRONMENT_VARIABLE_HOST = "REDIS_HOST"
|
|
7
|
+
ENVIRONMENT_VARIABLE_PORT = "REDIS_PORT"
|
|
8
|
+
|
|
9
|
+
class RedisServices(ServiceRegistry):
|
|
10
|
+
_instace = None
|
|
11
|
+
|
|
12
|
+
def __new__(cls):
|
|
13
|
+
if cls._instance is None:
|
|
14
|
+
instance = super().__new__(cls)
|
|
15
|
+
instance.client = redis.Redis(
|
|
16
|
+
host=instance.host,
|
|
17
|
+
port=int(instance.port),
|
|
18
|
+
decode_responses=True
|
|
19
|
+
)
|
|
20
|
+
cls._instance = instance
|
|
21
|
+
return cls._instance
|
|
22
|
+
|
|
23
|
+
def register(self, model_service: ModelServiceRegistry):
|
|
24
|
+
# registra o nome do modelo e a fila no serviço
|
|
25
|
+
key = f"model_queue:{model_service.service_name}"
|
|
26
|
+
self.client.set(key, model_service.queue_name)
|
|
27
|
+
|
|
28
|
+
def discovery(self, service_name: str) -> Optional[str]:
|
|
29
|
+
# retorna o nome da fila associada ao modelo
|
|
30
|
+
key = f"model_queue:{service_name}"
|
|
31
|
+
return self.client.get(key)
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def host(self):
|
|
35
|
+
return os.getenv(ENVIRONMENT_VARIABLE_HOST)
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def port(self):
|
|
39
|
+
return os.getenv(ENVIRONMENT_VARIABLE_PORT)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
from pydantic import BaseModel
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Optional
|
|
5
|
+
from pika.spec import BasicProperties
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
#TODO this class does not belong in this file, maybe a MessageConsumer interface
|
|
9
|
+
class CallBackArguments(BaseModel):
|
|
10
|
+
body : bytes
|
|
11
|
+
#TODO make less coupling, maybe use dict[str, Any] for now
|
|
12
|
+
properties: BasicProperties
|
|
13
|
+
|
|
14
|
+
class ModelServiceRegistry(BaseModel):
|
|
15
|
+
service_name: str
|
|
16
|
+
queue_name: str
|
|
17
|
+
#TODO 'process' does not belong in this class, this class is only for registration
|
|
18
|
+
process: Callable[[CallBackArguments], Optional[BaseModel]]
|
|
19
|
+
|
|
20
|
+
@abc.abstractmethod
|
|
21
|
+
class ServiceRegistry(abc.ABC):
|
|
22
|
+
|
|
23
|
+
@abc.abstractmethod
|
|
24
|
+
def register(self, model_service: ModelServiceRegistry): ...
|
|
25
|
+
|
|
26
|
+
@abc.abstractmethod
|
|
27
|
+
def discovery(self, service_name: str) -> Optional[str]: ...
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: model2service
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Author-email: Diego Pinheiro <diego.silva@unicap.br>
|
|
5
|
+
Project-URL: Homepage, https://github.com/projeto-carcara/model2service
|
|
6
|
+
Keywords: service
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Requires-Python: >=3.9
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: pydantic==2.13.2
|
|
13
|
+
Requires-Dist: debugpy==1.8.20
|
|
14
|
+
Requires-Dist: pika==1.3.2
|
|
15
|
+
Requires-Dist: redis==7.4.0
|
|
16
|
+
|
|
17
|
+
# model2service
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
.gitignore
|
|
2
|
+
Dockerfile
|
|
3
|
+
README.md
|
|
4
|
+
pyproject.toml
|
|
5
|
+
requirements.txt
|
|
6
|
+
.devcontainer/devcontainer.json
|
|
7
|
+
.github/workflows/python-publish.yml
|
|
8
|
+
.vscode/settings.json
|
|
9
|
+
model2service/__init__.py
|
|
10
|
+
model2service.egg-info/PKG-INFO
|
|
11
|
+
model2service.egg-info/SOURCES.txt
|
|
12
|
+
model2service.egg-info/dependency_links.txt
|
|
13
|
+
model2service.egg-info/requires.txt
|
|
14
|
+
model2service.egg-info/top_level.txt
|
|
15
|
+
model2service/src/__init__.py
|
|
16
|
+
model2service/src/model2service.py
|
|
17
|
+
model2service/src/model_repositories/__init__.py
|
|
18
|
+
model2service/src/model_repositories/mlflow_repository.py
|
|
19
|
+
model2service/src/model_repositories/model_repository.py
|
|
20
|
+
model2service/src/service_registries/__init__.py
|
|
21
|
+
model2service/src/service_registries/redis_registry.py
|
|
22
|
+
model2service/src/service_registries/service_registry.py
|
|
23
|
+
tests/client.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
model2service
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools", "wheel", "setuptools-scm[toml]>=6.2"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "model2service"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Diego Pinheiro", email="diego.silva@unicap.br" },
|
|
10
|
+
]
|
|
11
|
+
description = ""
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
license = { file = "LICENSE" }
|
|
14
|
+
requires-python = ">=3.9"
|
|
15
|
+
keywords = ["service"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Programming Language :: Python :: 3",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
dependencies = [
|
|
23
|
+
"pydantic==2.13.2",
|
|
24
|
+
"debugpy==1.8.20",
|
|
25
|
+
"pika==1.3.2",
|
|
26
|
+
"redis==7.4.0"
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.optional-dependencies]
|
|
30
|
+
|
|
31
|
+
[project.urls]
|
|
32
|
+
Homepage = "https://github.com/projeto-carcara/model2service"
|
|
33
|
+
|
|
34
|
+
[tool.setuptools.packages.find]
|
|
35
|
+
include = ["model2service", "model2service.*"]
|
|
36
|
+
|
|
37
|
+
[tool.setuptools_scm]
|
|
38
|
+
version_scheme = "guess-next-dev"
|
|
39
|
+
local_scheme = "node-and-date"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from model2service import *
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
# 1: Save Model Save Model After Training
|
|
5
|
+
|
|
6
|
+
model = Model()
|
|
7
|
+
DefaultModel2Service.model_save(model)
|
|
8
|
+
|
|
9
|
+
# 2: Register Models When Container Starts
|
|
10
|
+
|
|
11
|
+
service_name = "myservice"
|
|
12
|
+
def mymodels() -> List[Model]:
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
for model in mymodels():
|
|
16
|
+
model_service_registry: ModelServiceRegistry = ModelServiceRegistry(
|
|
17
|
+
model_name=model.name,
|
|
18
|
+
service_name="service_name"
|
|
19
|
+
)
|
|
20
|
+
DefaultModel2Service.service_register(model_service_registry=model)
|
|
21
|
+
|
|
22
|
+
# 3: Discover service for a given model_name
|
|
23
|
+
model_name: str = "mymodel"
|
|
24
|
+
serviceResgistry: ServiceRegistry = DefaultModel2Service.service_discover(model_name=model_name)
|
|
25
|
+
|
|
26
|
+
# 4: Load model within the container provisioning the service for a given model
|
|
27
|
+
model_name = "mymodel"
|
|
28
|
+
model: Model = DefaultModel2Service.model_load(model_name=model_name)
|