LocalAdapt 0.1.0__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.
@@ -0,0 +1,19 @@
1
+ import os
2
+
3
+ def _ensure_runtime():
4
+ try:
5
+ from . import adapters, test
6
+ return True
7
+ except ImportError:
8
+ return False
9
+
10
+ if not _ensure_runtime():
11
+ try:
12
+ from . import install
13
+ install.main()
14
+
15
+ # Nach Installation nochmal versuchen
16
+ from . import adapters, test
17
+
18
+ except Exception as e:
19
+ print("LocalAdapt initialization failed:", e)
@@ -0,0 +1,7 @@
1
+ from .router import Router
2
+ from .llm import LLMAdapter
3
+
4
+ router = Router()
5
+ llmAdapt = LLMAdapter()
6
+
7
+ router.register_adapter(llmAdapt)
@@ -0,0 +1,41 @@
1
+ # adapters/router.py
2
+ from abc import ABC, abstractmethod
3
+ from dataclasses import dataclass
4
+ from PIL import Image
5
+
6
+ @dataclass
7
+ class Value:
8
+ text:str = None
9
+ image:Image = None
10
+ path:str = None
11
+ done:bool = False
12
+
13
+ class BaseAdapter(ABC):
14
+ def __init__(self):
15
+ super().__init__()
16
+ self.model = None
17
+
18
+ @abstractmethod
19
+ def supports(self, request: dict) -> bool:
20
+ """Return True if this adapter can handle the request."""
21
+ pass
22
+
23
+ @abstractmethod
24
+ def register_model(self, **kwargs):
25
+ """Return a Dataclass for the given model and parameters."""
26
+ pass
27
+
28
+ @abstractmethod
29
+ def load(self, model):
30
+ """Loads an model into the Adapter(Lazy load)."""
31
+ pass
32
+
33
+ @abstractmethod
34
+ def unload(self):
35
+ """Unloads the loaded model."""
36
+ self.model = None
37
+
38
+ @abstractmethod
39
+ def run(self, request) -> Value:
40
+ """Runs the loaded model with the given request and returns the result as an Value object."""
41
+ pass
File without changes
File without changes
@@ -0,0 +1,121 @@
1
+ from .base_adapter import BaseAdapter, Value
2
+ from dataclasses import dataclass
3
+ import torch
4
+ from transformers import AutoModelForCausalLM, AutoTokenizer, TextIteratorStreamer
5
+ import threading
6
+
7
+
8
+ @dataclass
9
+ class LLM:
10
+ model_path: str
11
+ device: str = "cuda" if torch.cuda.is_available() else "cpu"
12
+
13
+
14
+ class LLMAdapter(BaseAdapter):
15
+ def __init__(self):
16
+ self.model = None
17
+ self.tokenizer = None
18
+
19
+ def supports(self, request):
20
+ return request.get("type") == "llm"
21
+
22
+ def register_model(self, **kwargs) -> LLM:
23
+ return LLM(**kwargs)
24
+
25
+ def load(self, model: LLM):
26
+ self.tokenizer = AutoTokenizer.from_pretrained(model.model_path)
27
+ self.model = AutoModelForCausalLM.from_pretrained(
28
+ model.model_path
29
+ ).to(model.device)
30
+
31
+ print(f"Model {model.model_path} loaded")
32
+ print(f"You can now run it on {model.device}")
33
+
34
+ def unload(self):
35
+ if self.model:
36
+ del self.model
37
+ self.model = None
38
+
39
+ if self.tokenizer:
40
+ del self.tokenizer
41
+ self.tokenizer = None
42
+
43
+ torch.cuda.empty_cache()
44
+ return super().unload()
45
+
46
+ def run(self, request: dict) -> Value:
47
+ if not self.model or not self.tokenizer:
48
+ raise ValueError("You have to load a model first!")
49
+
50
+ prompt = request.get("prompt", "")
51
+ max_new_tokens = request.get("max_length", 100)
52
+ stream = request.get("stream", False)
53
+ do_sample = request.get("do_sample", True)
54
+ temperature = request.get("temperature", 1.0)
55
+
56
+ result = Value(text="")
57
+
58
+ messages = request.get("history", []).copy()
59
+
60
+ inputs = self.tokenizer.apply_chat_template(
61
+ messages,
62
+ return_tensors="pt",
63
+ add_generation_prompt=True
64
+ ).to(self.model.device)
65
+
66
+ if stream:
67
+ streamer = TextIteratorStreamer(
68
+ self.tokenizer,
69
+ skip_special_tokens=True,
70
+ clean_up_tokenization_spaces=False
71
+ )
72
+
73
+ def generate():
74
+ with torch.no_grad():
75
+ top_p = request.get("top_p", 0.9)
76
+ repetition_penalty = request.get("repetition_penalty", 1.1)
77
+ self.model.generate(
78
+ **inputs,
79
+ max_new_tokens=max_new_tokens,
80
+ do_sample=do_sample,
81
+ temperature=temperature,
82
+ eos_token_id=self.tokenizer.eos_token_id,
83
+ pad_token_id=self.tokenizer.eos_token_id,
84
+ streamer=streamer,
85
+ top_p=top_p,
86
+ repetition_penalty=repetition_penalty
87
+ )
88
+
89
+ threading.Thread(target=generate, daemon=True).start()
90
+
91
+ def stream_output():
92
+ for text in streamer:
93
+ result.text += text
94
+ result.done = True
95
+
96
+ threading.Thread(target=stream_output, daemon=True).start()
97
+
98
+ return result
99
+
100
+ else:
101
+ with torch.no_grad():
102
+ output_ids = self.model.generate(
103
+ **inputs,
104
+ max_new_tokens=max_new_tokens,
105
+ do_sample=do_sample,
106
+ temperature=temperature,
107
+ eos_token_id=self.tokenizer.eos_token_id,
108
+ pad_token_id=self.tokenizer.eos_token_id
109
+ )
110
+
111
+ input_length = inputs["input_ids"].shape[1]
112
+ new_tokens = output_ids[0][input_length:]
113
+
114
+ result.text = self.tokenizer.decode(
115
+ new_tokens,
116
+ skip_special_tokens=True,
117
+ clean_up_tokenization_spaces=False
118
+ )
119
+ result.done = True
120
+
121
+ return result
@@ -0,0 +1,16 @@
1
+ from .base_adapter import BaseAdapter, Value
2
+
3
+ class Router:
4
+ def __init__(self):
5
+ self.adapters:list[BaseAdapter] = []
6
+
7
+ def register_adapter(self, adapter:BaseAdapter):
8
+ self.adapters.append(adapter)
9
+
10
+ def route(self, request:dict):
11
+ for adapter in self.adapters:
12
+ if adapter.supports(request):
13
+ if adapter.model is None:
14
+ adapter.load(adapter.register_model(**request.get("model_kwargs", {})))
15
+ return adapter.run(request)
16
+ raise ValueError(f"Couldn't find adapter for request: {request}")
@@ -0,0 +1,14 @@
1
+ from enum import Enum
2
+
3
+ class Color(Enum):
4
+ RESET = "\033[0m"
5
+ RED = "\033[31m"
6
+ GREEN = "\033[32m"
7
+ YELLOW = "\033[33m"
8
+ BLUE = "\033[34m"
9
+ MAGENTA = "\033[35m"
10
+ CYAN = "\033[36m"
11
+ WHITE = "\033[37m"
12
+
13
+ def print_col(txt, col:Color, pref="", suff=""):
14
+ print(f"{pref}{col.value}{txt}{Color.RESET.value}{suff}")
@@ -0,0 +1,52 @@
1
+ import subprocess
2
+ import sys
3
+ from . import common
4
+ import shutil
5
+
6
+ # ===========================
7
+ # Hilfsfunktionen
8
+ # ===========================
9
+
10
+ def run(cmd):
11
+ """Führt Shell-Befehle aus und bricht bei Fehlern ab."""
12
+ common.print_col(cmd, common.Color.CYAN, pref="> ")
13
+ result = subprocess.run(cmd, shell=True)
14
+ if result.returncode != 0:
15
+ common.print_col(f"Fehler beim Ausführen: {cmd}", common.Color.RED)
16
+ sys.exit(1)
17
+
18
+ def write_requirements(packages):
19
+ with open("requirements.txt", "w") as f:
20
+ for pkg in packages:
21
+ f.write(f"{pkg}\n")
22
+ common.print_col("requirements.txt erstellt!", common.Color.BLUE)
23
+
24
+ def main():
25
+ common.print_col("Upgrading pip if necessary...", common.Color.BLUE)
26
+ run("python.exe -m pip install --upgrade pip")
27
+
28
+ common.print_col("Checking CUDA avaibility...", common.Color.BLUE)
29
+ cuda_available = False
30
+ if shutil.which("nvidia-smi") is not None:
31
+ try:
32
+ output = subprocess.check_output("nvidia-smi", shell=True)
33
+ if b"NVIDIA" in output:
34
+ cuda_available = True
35
+ except subprocess.CalledProcessError:
36
+ cuda_available = False
37
+
38
+ packages = ["transformers", "Pillow", "PySide6"]
39
+
40
+ if cuda_available:
41
+ common.print_col("CUDA-enabled GPU detected! Install PyTorch with CUDA...", common.Color.BLUE)
42
+ packages.append("torch --index-url https://download.pytorch.org/whl/cu121")
43
+ else:
44
+ common.print_col("No CUDA GPU found. Installing CPU version of PyTorch...", common.Color.YELLOW)
45
+ packages.append("torch")
46
+
47
+ for pkg in packages:
48
+ run(f"{sys.executable} -m pip install {pkg}")
49
+
50
+ write_requirements(packages)
51
+
52
+ common.print_col("Installation abgeschlossen ✅", common.Color.GREEN)
@@ -0,0 +1,53 @@
1
+ from .adapters import router
2
+ import os
3
+ import time
4
+
5
+ def main():
6
+ print("Start")
7
+
8
+ os.environ["HF_HOME"] = "D:/AnyAI/models"
9
+
10
+ model_path = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
11
+ max_length = 200
12
+
13
+ request = {
14
+ "type": "llm",
15
+ "max_length": max_length,
16
+ "do_sample": True,
17
+ "temperature": 0.8,
18
+ "stream": True,
19
+ "history": [],
20
+ "model_kwargs": {
21
+ "model_path": model_path
22
+ }
23
+ }
24
+ router.adapters[0].load(router.adapters[0].register_model(**request["model_kwargs"]))
25
+ os.system("cls")
26
+ # Repl-artige Schleife
27
+ history = [{"role": "system", "content": """You are a helpful and concise assistant.
28
+ Answer the user questions factually and briefly.
29
+ Do not provide examples, do not speculate, do not invent code or explanations unless explicitly asked.
30
+ Stop after giving a direct answer."""}]
31
+ while True:
32
+ prompt = input(">>> ")
33
+ request["prompt"] = prompt
34
+ history.append({"role": "user", "content": prompt})
35
+ request["history"] = history
36
+ result = router.route(request)
37
+
38
+ last_len = 0
39
+ while True:
40
+ if len(result.text) > last_len:
41
+ print(result.text[last_len:], end="", flush=True)
42
+ last_len = len(result.text)
43
+
44
+ # Check if stream finished
45
+ if getattr(result, "done", False):
46
+ print() # Neue Zeile am Ende
47
+ break
48
+
49
+ time.sleep(0.05)
50
+ history.append({"role": "assistant", "content": result.text})
51
+
52
+ if __name__ == '__main__':
53
+ main()
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: LocalAdapt
3
+ Version: 0.1.0
4
+ Summary: Local AI Model adapters
5
+ Author: Nathan Lange
@@ -0,0 +1,15 @@
1
+ pyproject.toml
2
+ LocalAdapt/__init__.py
3
+ LocalAdapt/common.py
4
+ LocalAdapt/install.py
5
+ LocalAdapt/test.py
6
+ LocalAdapt.egg-info/PKG-INFO
7
+ LocalAdapt.egg-info/SOURCES.txt
8
+ LocalAdapt.egg-info/dependency_links.txt
9
+ LocalAdapt.egg-info/top_level.txt
10
+ LocalAdapt/adapters/__init__.py
11
+ LocalAdapt/adapters/base_adapter.py
12
+ LocalAdapt/adapters/diffusion.py
13
+ LocalAdapt/adapters/ersgan.py
14
+ LocalAdapt/adapters/llm.py
15
+ LocalAdapt/adapters/router.py
@@ -0,0 +1 @@
1
+ LocalAdapt
@@ -0,0 +1,5 @@
1
+ Metadata-Version: 2.4
2
+ Name: LocalAdapt
3
+ Version: 0.1.0
4
+ Summary: Local AI Model adapters
5
+ Author: Nathan Lange
@@ -0,0 +1,11 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "LocalAdapt"
7
+ version = "0.1.0"
8
+ description = "Local AI Model adapters"
9
+ authors = [{name = "Nathan Lange"}]
10
+
11
+ dependencies = []
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+