From 2ccb1725c6a2355490461ca2660cd65035157d60 Mon Sep 17 00:00:00 2001 From: exdysa <91800957+exdysa@users.noreply.github.com> Date: Sat, 19 Apr 2025 19:03:54 -0400 Subject: [PATCH 1/3] +More model selector adj --- nnll_08/__init__.py | 8 +-- nnll_10/__init__.py | 47 --------------- nnll_10/package/combo.tcss | 106 +++++++++++++++++++-------------- nnll_10/package/main_screen.py | 13 ++-- nnll_10/package/selectah.py | 63 ++++++++++++++++---- nnll_10/package/voice_panel.py | 4 +- nnll_13/__init__.py | 4 +- nnll_16/__init__.py | 6 +- 8 files changed, 131 insertions(+), 120 deletions(-) diff --git a/nnll_08/__init__.py b/nnll_08/__init__.py index 40efec97..3c136f12 100644 --- a/nnll_08/__init__.py +++ b/nnll_08/__init__.py @@ -30,13 +30,13 @@ def seed_planter(seed, deterministic=True) -> int: torch.manual_seed(seed) random.seed(seed) - if torch.cuda.is_available() is True: - if deterministic is True: + if torch.cuda.is_available(): + if deterministic: return {"torch.backends.cudnn.deterministic": "True", "torch.backends.cudnn.benchmark": "False"} return torch.cuda.manual_seed(seed), torch.cuda.manual_seed_all(seed) - elif torch.backends.mps.is_available() is True: + elif torch.backends.mps.is_available(): return torch.mps.manual_seed(seed) - elif torch.xpu.is_available() is True: + elif torch.xpu.is_available(): return torch.xpu.manual_seed(seed) diff --git a/nnll_10/__init__.py b/nnll_10/__init__.py index 4b2ce01c..ae4e8a0c 100644 --- a/nnll_10/__init__.py +++ b/nnll_10/__init__.py @@ -136,50 +136,3 @@ def edit_weight(self, selection: str, mode_in: str, mode_out: str) -> None: nfo("Weight changed for: ", self.intent_graph[mode_in][mode_out][index[0]]["entry"].model, f"model # {index[0]}") self.set_ckpts() dbug("Confirm :", self.intent_graph[mode_in][mode_out]) - - -# model_name = entry[2].get("entry").model -# index_name = (os.path.basename(model_name), model_name) -# self.model_names.insert(self.model_names.index(index_name), (f"*{os.path.basename(model_name)}", model_name)) -# what should happen next is setckpts - -# [reg_id[2] for reg_id in self.intent_graph.edges(data=True) if selection in reg_id[2].get("entry").model] -# right = left - weight_value -# if weight_value -# pass - -# left = int(weight_value) -# right = left - weight_value - -# # def check_weights(self, entry: str) -> None: - -# registry_data = [reg_id[2].get('entry').model for reg_id in self.intent_graph.edges(data=True).model if 'ibm' in reg_id[2].get('entry').model] - -# def -# add weight -# check weight - -# async def walk_intent(self, send: bool = False, composer: Callable = None, processor: Callable = None) -> None: -# """Provided the coordinates in the intent processor, follow the list of in and out methods""" -# await self.confirm_available_graph() -# await self.confirm_coordinates_path() -# coordinates = self.coordinates_path -# if not coordinates: -# coordinates = ["text", "text"] -# hop_length = len(coordinates) - 1 -# for i in range(hop_length): -# if i + 1 < hop_length: -# await self.confirm_coordinates_path() -# await self.confirm_model_waypoints() -# if send: -# await processor(last_hop=False) -# composer(mode_in=coordinates[i + 1], mode_out=coordinates[i + 2]) -# else: -# old_model_names = self.model_names if self.model_names else [] -# composer(mode_in=coordinates[i + 1], mode_out=coordinates[i + 2], io_only=True) -# self.model_names.extend(old_model_names) - -# elif send: -# await self.confirm_coordinates_path() -# await self.confirm_model_waypoints() -# processor() diff --git a/nnll_10/package/combo.tcss b/nnll_10/package/combo.tcss index 6fb93b9f..7cd3dba1 100644 --- a/nnll_10/package/combo.tcss +++ b/nnll_10/package/combo.tcss @@ -80,34 +80,64 @@ $selection-fg: $background; #seam { layout: grid; grid-size: 2 1; - grid-columns: 60fr 60fr; + grid-columns: 60fr 30fr; keyline: none; border: none; background:$negative-space 7%; height: 1; + text-wrap: nowrap; } #selectah { color: $sunken-text; - border: vkey $negative-space 7%; height: 2; margin: 0 1; padding: -1 0; overflow: hidden; text-align: end; - # align: right top; width: 100%; layer: above; - text-wrap:nowrap; + text-overflow: ellipsis; + &:focus { + background: white 10%; + } + &.active { + Static#label { + text-overflow:ellipsis; + overflow-x: hidden; + &:blur { + color: $accent; + text-align: end; + } + &:focus { + color: $accent; + } + &:hover { + color: $accent; + } + } + } + &.-expanded { + + color: $raised-text; + text-align: end; + content-align: right top; + background:$negative-space 7%; + overflow-x: hidden; + &.-selected { + background: $selection-bg; + + } + } Static#label { min-width: 10; + text-overflow:ellipsis; + overflow: hidden; &:blur { color: $sunken-text; text-align: end; - } &:focus { - border: none; - color: $foreground-darken-1; + color: $raised-text; } &:hover { color: $foreground-darken-1; @@ -116,70 +146,55 @@ $selection-fg: $background; background: $selection-bg; } } - &.active { - Static#label { - &:blur { - color: $accent; - text-align: end; - } - &:focus { - color: $accent; - } - &:hover { - color: $accent; - } - &.-selected { - color: $accent; - background: $selection-bg; - } - } - } + SelectCurrent { background:$negative-space 7%; - color: $sunken-text; - border:none; + border: vkey $focus-surface; + color: $raised-text; overflow: hidden; + text-overflow: ellipsis; + border: vkey $surface; + + &:blur { + border: vkey $surface; + } &:hover { color: $raised-text; } + &:focus { + color: $background-lighten-3; + } .arrow { color: $sunken-text; - &:hover { - color: $raised-text; - } - } - } - &.-expanded { - text-align: end; - content-align: right top; - background:$negative-space 7%; - text-wrap: nowrap; - &.-selected { - background: $selection-bg; } } SelectOverlay { - - text-wrap: nowrap; + text-overflow:ellipsis; + border: vkey $negative-space 7%; overflow-x: hidden; overflow-y: scroll; text-align: end; color: $sunken-text; overlay: screen; - scrollbar-size: 0 0; + scrollbar-size: 0 1; + scrollbar-background: $negative-space 7%; + scrollbar-background-active: $negative-space 7%; + scrollbar-color: $panel; + scrollbar-color-active: $focus-surface; + scrollbar-color-hover: $background-lighten-3; background: $negative-space 7%; - border: vkey $background-lighten-3; &.-selected { + overflow: hidden; background: $selection-bg; + border: vkey $background-lighten-3; } & > .option-list--option-selected { - + overflow: hidden; background: $selection-bg; color: $selection-fg; } & > .option-list--option { - text-wrap:nowrap; overflow: hidden; } & > .option-list--option-highlighted { @@ -223,7 +238,6 @@ SidebarLeft, SidebarRight { border-subtitle-color: $foreground-darken-1; } &:focus { - border: none; border: $border-shape $focus-surface; border-subtitle-color: $foreground; } diff --git a/nnll_10/package/main_screen.py b/nnll_10/package/main_screen.py index 82f0969d..dbbd1ff3 100644 --- a/nnll_10/package/main_screen.py +++ b/nnll_10/package/main_screen.py @@ -16,6 +16,7 @@ # from textual.widget import Widget from textual.widgets import Static, ContentSwitcher # , DataTable +from textual.widget import Widget from nnll_01 import debug_monitor, info_message as nfo, debug_message as dbug @@ -46,6 +47,7 @@ class Fold(Screen[bool]): # model_names = [("model", 0), ("x", 0)] tx_data: dict = {} counter = 0 + hover_name: reactive[str] = reactive("") input_map: dict = { "text": "message_panel", "image": "message_panel", @@ -126,17 +128,12 @@ def is_ui_ready(self): return True @on(events.Focus) - def on_focus(self, event: events.Focus) -> None: - """Textual API event, refresh pathing""" - if self.ui["sl"].has_focus and self.ui["sl"].expanded: + async def on_focus(self, event=events.Focus): + if event.control.id == "selectah": self.ready_tx() self.walk_intent() self.ui["sl"].prompt = next(iter(self.int_proc.models))[0] - # selection = next(iter(self.graph.models))[1] - # else: - # selection = self.selection - @work(exclusive=True) async def _on_key(self, event: events.Key) -> None: """Textual API event trigger, Suppress/augment default key actions to trigger keybindings""" @@ -264,7 +261,7 @@ async def send_tx(self, last_hop=True) -> None: ckpt = self.ui["sl"].selection if ckpt is None: - ckpt = next(iter(self.int_proc.ckpts))[1] + ckpt = next(iter(self.int_proc.ckpts)).get("entry") chat = ChatMachineWithMemory(sig=QASignature) self.ui["rp"].on_text_area_changed() self.ui["rp"].insert("\n---\n") diff --git a/nnll_10/package/selectah.py b/nnll_10/package/selectah.py index 18d8e41f..913b1ae6 100644 --- a/nnll_10/package/selectah.py +++ b/nnll_10/package/selectah.py @@ -5,13 +5,14 @@ import os import networkx as nx -from textual import on, events +from rich.color import Color +from textual import on, events, work from textual.reactive import reactive from textual.screen import Screen -from textual.widgets import Select +from textual.widgets import Select, Label from textual.widgets._select import SelectCurrent, SelectOverlay -from nnll_01 import debug_message as dbug # , debug_monitor +from nnll_01 import debug_message as dbug, debug_monitor, info_message as nfo class Selectah(Select): @@ -19,20 +20,16 @@ class Selectah(Select): graph: nx.Graph = None mode_in: str = "text" mode_out: str = "text" + focused = True + hover = True def on_mount(self) -> None: # self.options = self.graph.models self.graph = self.query_ancestor(Screen).int_proc # self.prompt = os.path.basename(next(iter(self.graph.models))[0]) - @on(events.Focus) - async def on_focus(self) -> None: - """Expand panel immediately when clicked in terminal""" - if SelectOverlay.has_focus: - self.set_options(self.graph.models) - @on(Select.Changed) - def on_changed(self) -> None: # event: Select.Changed) -> None: + async def on_changed(self) -> None: # event: Select.Changed) -> None: """Rearrange models""" try: assert self.query_one(SelectCurrent).has_value @@ -42,7 +39,53 @@ def on_changed(self) -> None: # event: Select.Changed) -> None: self.graph.edit_weight(selection=self.value, mode_in=self.mode_in, mode_out=self.mode_out) self.set_options(self.graph.models) self.prompt = next(iter(self.graph.models))[0] + self.expanded = False + + @debug_monitor + @work(exclusive=True) + @on(events.Enter) + async def on_enter(self, event: events.Enter) -> None: + """Force terminal mouse event monitoring""" + if event.node == SelectOverlay or event.node == self: + self.hover = True + + @debug_monitor + @work(exclusive=True) + @on(events.Leave) + async def on_leave(self, event: events.Leave) -> None: + """Force terminal mouse event monitoring""" + if event.node == SelectOverlay or event.node == self: + self.hover = True + + @work(exclusive=True) + @on(events.Focus) + async def on_focus(self, event: events.Focus) -> None: + """Expand panel immediately when clicked in terminal""" + if SelectOverlay.has_focus or self.has_focus: + self.focused = True + self.set_options(self.graph.models) + + @work(exclusive=True) + @on(events.MouseDown) + async def on_mouse_down(self, event: events.MouseDown) -> None: + """Expand panel immediately when clicked in terminal""" + if self.hover and not self.expanded: + self.expanded = True + elif (SelectOverlay.has_focus or self.has_focus) and self.expanded: + self.blur() + + @work(exclusive=True) + @on(events.MouseUp) + async def on_mouse_up(self, event: events.MouseUp) -> None: + """Expand panel immediately when clicked in terminal""" + if self.hover and self.focus and self.expanded: + self.expanded = False + elif self.hover and not self.expanded: + self.expanded = True + +# if self.expanded is False: +# self.expanded = True # @on(SelectOverlay.blur) # def on_select_overlay_blur(self, event: events.Blur) -> None: diff --git a/nnll_10/package/voice_panel.py b/nnll_10/package/voice_panel.py index 0a5f4e98..e36f704a 100644 --- a/nnll_10/package/voice_panel.py +++ b/nnll_10/package/voice_panel.py @@ -9,7 +9,7 @@ from textual_plotext import PlotextPlot -# from nnll_01 import debug_monitor +from nnll_01 import info_message as nfo # , debug_monitor class VoicePanel(PlotextPlot): # (PlotWidget) @@ -54,7 +54,7 @@ async def play_audio(self): sd.play(self.audio, samplerate=self.sample_freq) sd.wait() except TypeError as error_log: - print(error_log) + nfo(error_log) @work(exclusive=True) async def erase_audio(self): diff --git a/nnll_13/__init__.py b/nnll_13/__init__.py index 23255d57..3e3b64b3 100644 --- a/nnll_13/__init__.py +++ b/nnll_13/__init__.py @@ -10,7 +10,7 @@ from textual_plotext import PlotextPlot -# from nnll_01 import debug_monitor +from nnll_01 import info_message as nfo # , debug_monitor class VoicePanel(PlotextPlot): # (PlotWidget) @@ -60,7 +60,7 @@ async def play_audio(self): sd.play(self.audio, samplerate=self.sample_freq) sd.wait() except TypeError as error_log: - print(error_log) + nfo(error_log) @work(exclusive=True) async def erase_audio(self): diff --git a/nnll_16/__init__.py b/nnll_16/__init__.py index 79c37337..b8061fc2 100644 --- a/nnll_16/__init__.py +++ b/nnll_16/__init__.py @@ -12,7 +12,11 @@ def first_available(processor=None): if not processor: processor = reduce( lambda acc, check: check() if acc == "cpu" else acc, - [lambda: "cuda" if torch.cuda.is_available() else "cpu", lambda: "mps" if torch.backends.mps.is_available() else "cpu", lambda: "xpu" if torch.xpu.is_available() else "cpu"], + [ + lambda: "cuda" if torch.cuda.is_available() else "cpu", + lambda: "mps" if torch.backends.mps.is_available() else "cpu", + lambda: "xpu" if torch.xpu.is_available() else "cpu", + ], "cpu", ) return torch.device(processor) From 0b3413a5d380a70cc7cd74e64c132b97c3630a9a Mon Sep 17 00:00:00 2001 From: exdysa <91800957+exdysa@users.noreply.github.com> Date: Tue, 22 Apr 2025 17:42:13 -0400 Subject: [PATCH 2/3] +extract zodiac --- README.md | 4 +- _version.py | 4 +- nnll.egg-info/PKG-INFO | 16 +- nnll.egg-info/SOURCES.txt | 18 +- nnll.egg-info/entry_points.txt | 1 - nnll.egg-info/requires.txt | 9 +- nnll.egg-info/top_level.txt | 1 + nnll_10/__init__.py | 138 ------------ nnll_10/package/__init__.py | 39 ---- nnll_10/package/__main__.py | 42 ---- nnll_10/package/carousel.py | 78 ------- nnll_10/package/combo.tcss | 354 ------------------------------ nnll_10/package/display_bar.py | 45 ---- nnll_10/package/input_tag.py | 24 -- nnll_10/package/main_screen.py | 325 --------------------------- nnll_10/package/message_panel.py | 19 -- nnll_10/package/output_tag.py | 24 -- nnll_10/package/response_panel.py | 27 --- nnll_10/package/selectah.py | 130 ----------- nnll_10/package/token_counters.py | 55 ----- nnll_10/package/voice_panel.py | 80 ------- nnll_13/__init__.py | 4 +- nnll_21/__init__.py | 114 ++++++++++ pyproject.toml | 5 +- tests/test_14_draw_graph.py | 108 --------- tests/test_14_graph.py | 339 ---------------------------- 26 files changed, 141 insertions(+), 1862 deletions(-) delete mode 100644 nnll_10/__init__.py delete mode 100644 nnll_10/package/__init__.py delete mode 100644 nnll_10/package/__main__.py delete mode 100644 nnll_10/package/carousel.py delete mode 100644 nnll_10/package/combo.tcss delete mode 100644 nnll_10/package/display_bar.py delete mode 100644 nnll_10/package/input_tag.py delete mode 100644 nnll_10/package/main_screen.py delete mode 100644 nnll_10/package/message_panel.py delete mode 100644 nnll_10/package/output_tag.py delete mode 100644 nnll_10/package/response_panel.py delete mode 100644 nnll_10/package/selectah.py delete mode 100644 nnll_10/package/token_counters.py delete mode 100644 nnll_10/package/voice_panel.py create mode 100644 nnll_21/__init__.py delete mode 100644 tests/test_14_draw_graph.py delete mode 100644 tests/test_14_graph.py diff --git a/README.md b/README.md index 9235b316..a0e00e75 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ * Extracting and classifying metadata from images/models * Consumer-grade GPU/CPU inference optimization * Misc UX/UI Experimentation -* 🧨Diffusers, 🤗Transformers, 🦙Ollama, 🍏MLX, 🌀DSPy, 🚅LiteLLM

+* 🧨Diffusers, 🤗Transformers, 🦙Ollama, 🍏MLX, 🌀DSPy, 🚅LiteLLM +* :shipit:

-# :shipit: [![Python application test status](https://github.com/darkshapes/nnll/actions/workflows/python-app.yml/badge.svg)](https://github.com/darkshapes/nnll/actions/workflows/python-app.yml)
![commits per month](https://img.shields.io/github/commit-activity/m/darkshapes/nnll?color=indigo)
![code size](https://img.shields.io/github/languages/code-size/darkshapes/nnll?color=navy)
diff --git a/_version.py b/_version.py index 2982e340..9ab9509d 100644 --- a/_version.py +++ b/_version.py @@ -17,5 +17,5 @@ __version_tuple__: VERSION_TUPLE version_tuple: VERSION_TUPLE -__version__ = version = '0.1.dev257+gee3125e.d20250412' -__version_tuple__ = version_tuple = (0, 1, 'dev257', 'gee3125e.d20250412') +__version__ = version = '0.1.dev272+g2ccb172.d20250422' +__version_tuple__ = version_tuple = (0, 1, 'dev272', 'g2ccb172.d20250422') diff --git a/nnll.egg-info/PKG-INFO b/nnll.egg-info/PKG-INFO index dc4b0bbe..07850c2e 100644 --- a/nnll.egg-info/PKG-INFO +++ b/nnll.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: nnll -Version: 0.1.dev257+gee3125e.d20250412 +Version: 0.1.dev272+g2ccb172.d20250422 Summary: Neural Network Link Library : A comprehensive modular toolkit for Diffusion and Large Language Model inference processes. Author-email: darkshapes <91800957+exdysa@users.noreply.github.com> License: #// SPDX-License-Identifier: blessing @@ -16,8 +16,10 @@ Keywords: ML,AI,neural network,library,diffusion,LLM,torch Requires-Python: >=3.10 Description-Content-Type: text/markdown License-File: LICENSE -Requires-Dist: structlog>=25.2.0 -Requires-Dist: viztracer>=1.0.3 +Requires-Dist: nnll_01 +Provides-Extra: nnll-01 +Requires-Dist: structlog>=25.2.0; extra == "nnll-01" +Requires-Dist: viztracer>=1.0.3; extra == "nnll-01" Provides-Extra: nnll-02 Requires-Dist: huggingface-hub>=0.29.3; extra == "nnll-02" Requires-Dist: litellm>=1.65.0; extra == "nnll-02" @@ -27,7 +29,6 @@ Requires-Dist: aiohttp<=3.11.13,>=3.9.5; extra == "nnll-03" Requires-Dist: tqdm>=4.67.1; extra == "nnll-03" Provides-Extra: nnll-04 Requires-Dist: gguf>=0.14.0; extra == "nnll-04" -Requires-Dist: llama-cpp-python>=0.3.8; extra == "nnll-04" Requires-Dist: safetensors>=0.5.3; extra == "nnll-04" Provides-Extra: nnll-05 Requires-Dist: networkx>=3.4.2; extra == "nnll-05" @@ -102,6 +103,7 @@ Requires-Dist: torchvision>=0.21.0; extra == "nnll-62" Provides-Extra: nnll-64 Requires-Dist: pillow>=11.1.0; extra == "nnll-64" Provides-Extra: all +Requires-Dist: nnll[nnll_01]; extra == "all" Requires-Dist: nnll[nnll_02]; extra == "all" Requires-Dist: nnll[nnll_03]; extra == "all" Requires-Dist: nnll[nnll_04]; extra == "all" @@ -150,9 +152,9 @@ Dynamic: license-file * Extracting and classifying metadata from images/models * Consumer-grade GPU/CPU inference optimization * Misc UX/UI Experimentation -* 🧨Diffusers, 🤗Transformers, 🦙Ollama, 🍏MLX, 🌀DSPy, 🚅LiteLLM

+* 🧨Diffusers, 🤗Transformers, 🦙Ollama, 🍏MLX, 🌀DSPy, 🚅LiteLLM +* :shipit:

-# :shipit: [![Python application test status](https://github.com/darkshapes/nnll/actions/workflows/python-app.yml/badge.svg)](https://github.com/darkshapes/nnll/actions/workflows/python-app.yml)
![commits per month](https://img.shields.io/github/commit-activity/m/darkshapes/nnll?color=indigo)
![code size](https://img.shields.io/github/languages/code-size/darkshapes/nnll?color=navy)
@@ -205,7 +207,7 @@ Dynamic: license-file Some modules are full scripts and can be run from command line. These are written here: `zodiac` - Experimental generative system
-`astra` - Experimental generative system
+`astra` - Live diagnostic console
`nnll-hash` - Hash the layer metadata from models within a directory and write out to console.
`nnll-parse` - Process metadata headers from a model file or directory of models and write out to individual JSON files.
`nnll-find` - Search a local directory of model layer files (HuggingFace🤗 index.json, JSON from `nnll-parse`)
diff --git a/nnll.egg-info/SOURCES.txt b/nnll.egg-info/SOURCES.txt index 5957aad0..883c6628 100644 --- a/nnll.egg-info/SOURCES.txt +++ b/nnll.egg-info/SOURCES.txt @@ -4,7 +4,6 @@ README.md _version.py pyproject.toml pytest.ini -textual.log uv.lock .github/workflows/python-app.yml nnll.egg-info/PKG-INFO @@ -23,19 +22,6 @@ nnll_06/__init__.py nnll_07/__init__.py nnll_08/__init__.py nnll_09/__init__.py -nnll_10/__init__.py -nnll_10/package/__init__.py -nnll_10/package/__main__.py -nnll_10/package/carousel.py -nnll_10/package/combo.tcss -nnll_10/package/display_bar.py -nnll_10/package/input_tag.py -nnll_10/package/main_screen.py -nnll_10/package/message_panel.py -nnll_10/package/output_tag.py -nnll_10/package/response_panel.py -nnll_10/package/token_counters.py -nnll_10/package/voice_panel.py nnll_11/__init__.py nnll_12/__init__.py nnll_12/__main__.py @@ -50,6 +36,7 @@ nnll_17/__main__.py nnll_18/__init__.py nnll_19/__init__.py nnll_20/__init__.py +nnll_21/__init__.py nnll_24/__init__.py nnll_25/__init__.py nnll_26/__init__.py @@ -101,8 +88,7 @@ tests/test_05.py tests/test_07.py tests/test_08.py tests/test_10_register_types.py -tests/test_14_draw_graph.py -tests/test_14_graph.py +tests/test_10_weight.py tests/test_15_register_types.py tests/test_24.py tests/test_25.py diff --git a/nnll.egg-info/entry_points.txt b/nnll.egg-info/entry_points.txt index e8e2a7b2..f4a3fbce 100644 --- a/nnll.egg-info/entry_points.txt +++ b/nnll.egg-info/entry_points.txt @@ -4,4 +4,3 @@ nnll-find = nnll_31:main nnll-hash = nnll_17:main nnll-index = nnll_43:main nnll-parse = nnll_36:main -zodiac = nnll_10.package.__init__:main diff --git a/nnll.egg-info/requires.txt b/nnll.egg-info/requires.txt index 0ccf8b8f..fedd3fdc 100644 --- a/nnll.egg-info/requires.txt +++ b/nnll.egg-info/requires.txt @@ -1,7 +1,7 @@ -structlog>=25.2.0 -viztracer>=1.0.3 +nnll_01 [all] +nnll[nnll_01] nnll[nnll_02] nnll[nnll_03] nnll[nnll_04] @@ -50,7 +50,6 @@ tqdm>=4.67.1 [nnll-04] gguf>=0.14.0 -llama-cpp-python>=0.3.8 safetensors>=0.5.3 [nnll-05] @@ -146,3 +145,7 @@ torchvision>=0.21.0 [nnll-64] pillow>=11.1.0 + +[nnll_01] +structlog>=25.2.0 +viztracer>=1.0.3 diff --git a/nnll.egg-info/top_level.txt b/nnll.egg-info/top_level.txt index 94e3d920..15087d41 100644 --- a/nnll.egg-info/top_level.txt +++ b/nnll.egg-info/top_level.txt @@ -18,6 +18,7 @@ nnll_17 nnll_18 nnll_19 nnll_20 +nnll_21 nnll_24 nnll_25 nnll_26 diff --git a/nnll_10/__init__.py b/nnll_10/__init__.py deleted file mode 100644 index ae4e8a0c..00000000 --- a/nnll_10/__init__.py +++ /dev/null @@ -1,138 +0,0 @@ -# # # -# # # - -# pylint: disable=import-outside-toplevel - - -import sys -import os - - -# pylint:disable=import-outside-toplevel -sys.path.append(os.getcwd()) - -import networkx as nx -from nnll_01 import debug_monitor, info_message as nfo, debug_message as dbug - -# from nnll_15.constants import ModeType -from nnll_15 import RegistryEntry - - -class IntentProcessor: - intent_graph: nx.Graph = None - coord_path: list[str] = None - ckpts: list[dict[RegistryEntry]] = None - models: list[tuple[str]] = None - # additional_model_names: dict = None - - def __init__(self): - """ - Create instance of graph processor & initialize objectives for tracing paths - """ - - @debug_monitor - def calc_graph(self) -> None: - """Generate graph of coordinate pairs from valid conversions\n - Model libraries are auto-detected from cache loading\n - :param nx_graph: Preassembled graph of models to label - :return: Graph modeling all current ML/AI tasks appended with model data""" - from nnll_15 import VALID_CONVERSIONS, from_cache - - self.intent_graph = nx.MultiDiGraph() - self.intent_graph.add_nodes_from(VALID_CONVERSIONS) - registry_entries = from_cache() - if registry_entries: - for model in registry_entries: - self.intent_graph.add_edges_from(model.available_tasks, entry=model, weight=1.0) - else: - nfo("Registry error, graph attributes not applied") - return self.intent_graph - - @debug_monitor - def has_graph(self) -> None: - """Verify the graph has been created""" - try: - assert self.intent_graph is not None - except AssertionError as error_log: - dbug(error_log) - return False - return True - - @debug_monitor - def has_path(self) -> None: - """Verify the path has been created""" - try: - assert self.coord_path is not None - except AssertionError as error_log: - dbug(error_log) - return False - return True - - @debug_monitor - def has_ckpt(self) -> None: - """Verify the model checkpoints are known""" - try: - assert self.ckpts is not None - except AssertionError as error_log: - dbug(error_log) - return False - return True - - @debug_monitor - def set_path(self, mode_in: str, mode_out: str) -> None: - """ - Find a valid path from current state (mode_in) to designated state (mode_out)\n - :param mode_in: Input prompt type or starting state/states - :param mode_out: The user-selected ending-state - :return: An iterator for the edges forming a way towards the mode out, or Note - """ - - self.has_graph() - if nx.has_path(self.intent_graph, mode_in, mode_out): # Ensure path exists (otherwise 'bidirectional' may loop infinitely) - if mode_in == mode_out and mode_in != "text": - orig_mode_out = mode_out # Solve case of non-text self-loop edge being incomplete transformation - mode_out = "text" - self.coord_path = nx.bidirectional_shortest_path(self.intent_graph, mode_in, mode_out) - self.coord_path.append(orig_mode_out) - else: - self.coord_path = nx.bidirectional_shortest_path(self.intent_graph, mode_in, mode_out) - if len(self.coord_path) == 1: - self.coord_path.append(mode_out) # this behaviour likely to change in future - - @debug_monitor - def set_ckpts(self) -> None: - from nnll_05 import pull_path_entries - - self.has_graph() - self.has_path() - self.ckpts = pull_path_entries(self.intent_graph, self.coord_path) - self.models = [] - nfo(self.ckpts) - self.ckpts = sorted(self.ckpts, key=lambda x: x["weight"]) - nfo([x["weight"] for x in self.ckpts]) - for registry in self.ckpts: - model = registry["entry"].model - weight = registry.get("weight") - if weight != 1.0: - self.models.insert(0, (f"*{os.path.basename(model)}", model)) - nfo("adjusted model :", f"*{os.path.basename(model)}", weight) - else: - self.models.append((os.path.basename(model), model)) - # nfo("model : ", model, weight) - self.models = sorted(self.models, key=lambda x: "*" in x) - - @debug_monitor - def edit_weight(self, selection: str, mode_in: str, mode_out: str) -> None: - """Determine entry edge, determine index, then adjust weight""" - reg_entries = [nbrdict for n, nbrdict in self.intent_graph.adjacency()] - index = [x for x in reg_entries[0][mode_out] if selection in reg_entries[0][mode_out][x].get("entry").model] - model = reg_entries[0][mode_out][index[0]].get("entry").model - weight = reg_entries[0][mode_out][index[0]].get("weight") - nfo(model, index, weight) - if weight < 1.0: - self.intent_graph[mode_in][mode_out][index[0]]["weight"] = weight + 0.1 - else: - self.intent_graph[mode_in][mode_out][index[0]]["weight"] = weight - 0.1 - nfo("Weight changed for: ", self.intent_graph[mode_in][mode_out][index[0]]["entry"].model, f"model # {index[0]}") - self.set_ckpts() - dbug("Confirm :", self.intent_graph[mode_in][mode_out]) diff --git a/nnll_10/package/__init__.py b/nnll_10/package/__init__.py deleted file mode 100644 index b766124d..00000000 --- a/nnll_10/package/__init__.py +++ /dev/null @@ -1,39 +0,0 @@ -import sys -import os - -# pylint:disable=import-outside-toplevel -sys.path.append(os.getcwd()) - - -def main(): - """Launch textual UI""" - from nnll_10.package.__main__ import Combo - from nnll_01 import info_message as nfo - - trace = False - if sys.argv[0] == "-t" or sys.argv[0] == "--trace": - import litellm - from viztracer import VizTracer - - litellm.suppress_debug_info = False - trace = True - tracer = VizTracer() - tracer.start() - - app = Combo(ansi_color=False) - - nfo("Launching...") - app.run() - if trace: - from datetime import datetime - - os.makedirs("log", exist_ok=True) - assembled_path = os.path.join("log", f".nnll{datetime.now().strftime('%Y%m%d')}_trace.json") - tracer.stop() - tracer.save(output_file=assembled_path) # also takes output_file as an optional argument - - -if __name__ == "__main__": - # import asyncio - main() - # asyncio.run(main()) diff --git a/nnll_10/package/__main__.py b/nnll_10/package/__main__.py deleted file mode 100644 index 037794b7..00000000 --- a/nnll_10/package/__main__.py +++ /dev/null @@ -1,42 +0,0 @@ -# # # -# # # - -# pylint: disable=missing-module-docstring, disable=missing-class-docstring - -from textual import work, events, on -from textual.app import App -from textual.binding import Binding -from textual.reactive import reactive - -# from nnll_01 import info_message as nfo -from nnll_10.package.main_screen import Fold # pylint: disable=import-error - -# from theme import fluoresce_theme - - -class Combo(App): - SCREENS = {"fold": Fold} - CSS_PATH = "combo.tcss" - BINDINGS = [Binding("escape", "safe_exit", "◼︎ / ⏏︎")] # Cancel response - - safety: reactive[int] = reactive(1) - - def on_mount(self) -> None: - """Draw screen""" - self.push_screen("fold") - self.scroll_sensitivity_y = 1 - self.supports_smooth_scrolling = True - self.theme = "flexoki" - - @work(exit_on_error=True) - @on(events.Key) - async def _on_key(self, event: events.Key): - """Window for triggering key bindings""" - if event.key not in ["escape", "ctrl+left_square_brace"]: - self.safety += 1 - else: - self.safety -= 1 - if self.safety < 0: - event.prevent_default() - - await self.app.action_quit() diff --git a/nnll_10/package/carousel.py b/nnll_10/package/carousel.py deleted file mode 100644 index 7a5f4e7c..00000000 --- a/nnll_10/package/carousel.py +++ /dev/null @@ -1,78 +0,0 @@ -# # # -# # # - -"""Selection Function""" - -from textual.widgets import DataTable -from textual.screen import Screen -from textual.reactive import reactive - -from nnll_01 import debug_monitor, info_message as nfo # debug_message as dbug, - - -class Carousel(DataTable): - """Revolving text-line component based on default DataTable widget class""" - - nx_graph: dict - content_cell: reactive[int] = reactive(0) - - up = "[@click='scroll_button(1)']▲[/]" - dwn = "[@click='scroll_button']▼[/]" - current_cell = "text" - current_row = 0 - y_coord = 1 - scroll_counter = 10 - - def on_mount(self) -> None: - self.show_header = False - self.cursor_type = "cell" - - @debug_monitor - def emulate_scroll_down(self, interval: int = 1) -> str: - """Trigger datatable cursor movement using fractional sensitivity - :param ceiling: Total entry count of the table *column* - :return: The datata in the table *row* - """ - - if (self.row_count - 1) >= (self.current_row + interval): - if self.y_coord < self.scroll_counter: - self.y_coord += interval - else: - self.y_coord = interval - self.current_row += interval - self.move_cursor(row=self.current_row, column=1) - self.current_cell = self.get_cell_at((self.current_row, 1)) - return self.current_cell - - @debug_monitor - def emulate_scroll_up(self, interval: int = 1) -> str: - """Trigger datatable cursor movement using fractional sensitivity - :param ceiling: Total entry count of the table *column* - :return: The datata in the table *row* - """ - - if 0 <= self.current_row - interval: - if self.y_coord >= 1: - self.y_coord -= interval - else: - self.y_coord = self.scroll_counter - self.current_row -= interval - self.move_cursor(row=self.current_row, column=1) - self.current_cell = self.get_cell_at((self.current_row, 1)) - return self.current_cell - - @debug_monitor - def action_scroll_button(self, up: bool = False) -> None: - """Manually trigger scrolling and panel switching - :param up: Scroll direction, defaults to False - """ - if up: - self.current_cell = self.emulate_scroll_up(interval=self.y_coord + 1) - if self.id == "input_tag": - self.query_ancestor(Screen).foldr["ps"].current = self.query_ancestor(Screen).input_map[self.current_cell] - self.query_ancestor(Screen).ready_tx(io_only=True) - else: - self.current_cell = self.emulate_scroll_down(interval=self.scroll_counter - self.y_coord - 1) - if self.id == "input_tag": - self.query_ancestor(Screen).foldr["ps"].current = self.query_ancestor(Screen).input_map[self.current_cell] - self.query_ancestor(Screen).ready_tx(io_only=True) diff --git a/nnll_10/package/combo.tcss b/nnll_10/package/combo.tcss deleted file mode 100644 index 7cd3dba1..00000000 --- a/nnll_10/package/combo.tcss +++ /dev/null @@ -1,354 +0,0 @@ -# # # # -# # # # - - - - -$min-height: 3; -# #dedede; -# #000000; -# #1f1f1f; #333333; -# #0F0F0F; #888888; -# #48D1CC; #8c92ac; -# #EF8E35 #eb4983; -# #F2C641;# #9a80cF; - # rgb(28, 26, 26) -# rgb(31, 0, 65) -# surfaces -$negative-space: $background-darken-3; -$main-background: $background; -$top-surface: $surface; -$focus-surface: $foreground; - -# text -$sunken-text: $background-lighten-3; -$raised-text: $foreground-darken-3; # $secondary; -$hover-focus: $foreground; - - -# layout -$layout-type: grid; -$centre-frame-margin: 0 1 0 1; -$vertical-grid: 1 3; -$horizontal-grid: 3 1; -$proportions: 0.7fr 4.5fr 0.7fr; -$vertical-proportions: 0.6fr 5fr 0.55fr; -$grid-fraction: 1fr; -$invisible-scrollbar: 0 0; -$margin-size: 0; - -# alignment -$title-align: left; -$border-shape: round; -$hatch-border: horizontal; -$hatch-align: left; - -# color -$message-panel-subtitle: $raised-text; -$selection-bg: $foreground; -$selection-fg: $background; - - - -#footer { - background: $main-background; -} - -.app-grid-vertical { # mobile layout - layout: $layout-type; - grid-size: $vertical-grid; - grid-rows: $vertical-proportions; - grid-columns: $grid-fraction; -} - -.app-grid-horizontal { # laptop layout - layout: $layout-type; - grid-size: $horizontal-grid; - grid-columns: $proportions; - grid-rows: $grid-fraction; - min-height: $min-height; -} - -#centre-frame { - layout: grid; - grid-size: $vertical-grid; - grid-rows: 3fr 1 3fr; - margin: $centre-frame-margin; - keyline: none; - -} -#seam { - layout: grid; - grid-size: 2 1; - grid-columns: 60fr 30fr; - keyline: none; - border: none; - background:$negative-space 7%; - height: 1; - text-wrap: nowrap; -} -#selectah { - color: $sunken-text; - height: 2; - margin: 0 1; - padding: -1 0; - overflow: hidden; - text-align: end; - width: 100%; - layer: above; - text-overflow: ellipsis; - &:focus { - background: white 10%; - } - &.active { - Static#label { - text-overflow:ellipsis; - overflow-x: hidden; - &:blur { - color: $accent; - text-align: end; - } - &:focus { - color: $accent; - } - &:hover { - color: $accent; - } - } - } - &.-expanded { - - color: $raised-text; - text-align: end; - content-align: right top; - background:$negative-space 7%; - overflow-x: hidden; - &.-selected { - background: $selection-bg; - - } - } - Static#label { - min-width: 10; - text-overflow:ellipsis; - overflow: hidden; - &:blur { - color: $sunken-text; - text-align: end; - } - &:focus { - color: $raised-text; - } - &:hover { - color: $foreground-darken-1; - } - &.-selected { - background: $selection-bg; - } - } - - - SelectCurrent { - background:$negative-space 7%; - border: vkey $focus-surface; - color: $raised-text; - overflow: hidden; - text-overflow: ellipsis; - border: vkey $surface; - - &:blur { - border: vkey $surface; - } - &:hover { - color: $raised-text; - } - &:focus { - color: $background-lighten-3; - } - .arrow { - color: $sunken-text; - } - } - SelectOverlay { - text-overflow:ellipsis; - border: vkey $negative-space 7%; - overflow-x: hidden; - overflow-y: scroll; - text-align: end; - color: $sunken-text; - overlay: screen; - scrollbar-size: 0 1; - scrollbar-background: $negative-space 7%; - scrollbar-background-active: $negative-space 7%; - scrollbar-color: $panel; - scrollbar-color-active: $focus-surface; - scrollbar-color-hover: $background-lighten-3; - background: $negative-space 7%; - &.-selected { - overflow: hidden; - background: $selection-bg; - border: vkey $background-lighten-3; - } - & > .option-list--option-selected { - overflow: hidden; - background: $selection-bg; - color: $selection-fg; - } - & > .option-list--option { - overflow: hidden; - } - & > .option-list--option-highlighted { - overflow: hidden; - background: $accent 75%; - text-style: none; - } - } -} -#top-frame, #bottom-frame, #left-frame, #right-frame { - background: $negative-space; -} - -#bottom-frame:focus-within { - border: $border-shape $focus-surface; # orange highlight - -} - -SidebarLeft, SidebarRight { - margin-left: $margin-size; - margin-right: $margin-size; - background: $negative-space; -} - -#message_panel > .textarea--fixed-cursor { - color: transparent; - -} - -#message_panel, #response_panel { - background: $top-surface; - border: $border-shape $main-background; - border-subtitle-align: $title-align; - border-subtitle-color: $background-lighten-3; - scrollbar-size: $invisible-scrollbar; - .text-area--selection { - background: $selection-bg; - color: $selection-fg; - } - &:hover { - border-subtitle-color: $foreground-darken-1; - } - &:focus { - border: $border-shape $focus-surface; - border-subtitle-color: $foreground; - } - - -} - -#response_panel { - width: 100%; - margin: 0 0; - height: 100%; - border: round $main-background; - hatch: $hatch-align $main-background; - &:focus-within { - border: round $focus-surface; - } -} -#response_panel, #output_tag, { - scrollbar-size: $invisible-scrollbar; - -} - -#display_bar { - color: $panel-lighten-1; - margin: 0 0; - padding: 0 0; - height: 1; - min-width: 10; - content-align: left top; - align: left top; - text-align: start; - width: 100%; - layer: above; - overflow: hidden; - scrollbar-size: $invisible-scrollbar; - background: $negative-space 7%; - &:blur { - color: $panel-lighten-1; - } - &:hover { - color: $panel-lighten-1; - } - &:focus { - color: $sunken-text; - } -} - -#output_tag { - margin: 0 2; -} - -#output_tag, #input_tag { - color: $background-lighten-3; - width: auto; - hatch: $hatch-border $main-background; - border: none; - overflow: hidden; - height: 1; - layer: above; - background: $top-surface; - background-tint: 0%; - .datatable--cursor { - color: $background-lighten-3; - background: $top-surface; - } - &:hover { - .datatable--cursor { - color: $foreground-darken-1; - background: $top-surface; - } - } - &:focus { - .datatable--cursor { - color: $foreground; - text-style: none; - background: $top-surface; - } - } -} - -#input_tag { - margin-bottom: 0; - margin-left: 2; - padding: 0; - border: none; - background: $top-surface; -} - -#responsive_input{ - align: left bottom; - -} -#panel_swap { - align: center top; - # scrollbar-gutter: stable; - scrollbar-size: 0 0; -} -#voice_panel { - background: $top-surface; - box-sizing: content-box; - overflow: hidden; - padding: 0; - margin: 0; - scrollbar-size: 0 0; - # scrollbar-gutter: stable; - color: $accent; - outline: round $background; # round $top-surface; - # border: none; - &:focus { - outline: round $focus-surface; - } - -} - diff --git a/nnll_10/package/display_bar.py b/nnll_10/package/display_bar.py deleted file mode 100644 index 9ff7af40..00000000 --- a/nnll_10/package/display_bar.py +++ /dev/null @@ -1,45 +0,0 @@ -# # # -# # # - -from typing import Callable -from textual import work -from textual.reactive import reactive -from textual.screen import Screen -from textual.widgets import DataTable -from nnll_10.package.token_counters import tk_count - - -class DisplayBar(DataTable): - """Thin instant user feedback display""" - - UNIT1 = "chr / " # Display Bar Units - UNIT2 = "tkn / " - UNIT3 = "″" - unit_labels = [UNIT1, UNIT2, UNIT3] - duration: reactive[float] = reactive(0.0, recompose=True) - - def on_mount(self): - rows: list[tuple] = [ - (0, 0, 0, 0), - (f" 0{self.UNIT1}", f"0{self.UNIT2}", f"0.0{self.UNIT3}", " "), - ] - self.add_columns(*rows[0]) - self.add_rows(rows[1:]) - self.show_header = False - self.show_row_labels = False - self.cursor_type = None - - @work(exclusive=True) - async def calculate_tokens(self, tokenizer_model: str, message: str): - """Live display of tokens and characters""" - token_count = await tk_count(tokenizer_model, message) - character_count = len(message) - self.update_cell_at((0, 0), f" {character_count}{self.unit_labels[0]}") - self.update_cell_at((0, 1), f"{token_count}{self.unit_labels[1]}") - self.update_cell_at((0, 2), f"{self.duration}{self.unit_labels[2]}") - - @work(exclusive=True) - async def calculate_audio(self, duration): - """Live display of sound recording length""" - self.duration = duration if duration > 0.0 else 0.0 - self.update_cell_at((0, 2), f"{self.duration}{self.unit_labels[2]}", update_width=True) diff --git a/nnll_10/package/input_tag.py b/nnll_10/package/input_tag.py deleted file mode 100644 index b5680a0c..00000000 --- a/nnll_10/package/input_tag.py +++ /dev/null @@ -1,24 +0,0 @@ -# # # -# # # - -from textual.reactive import reactive -from textual.screen import Screen - -from nnll_10.package.carousel import Carousel - - -class InputTag(Carousel): - """Populate Input Types List""" - - target_options: reactive[set] = reactive({}) - - def on_mount(self): - scrn = self.query_ancestor(Screen) - if scrn.int_proc.has_graph(): - graph_edges = scrn.int_proc.intent_graph.edges - # note: Length sort on target_options kinda poor way to get text on top, works for the moment tho - self.target_options = sorted({edge[1] for edge in graph_edges}, key=len) - self.add_columns("0", "1", "2") - self.add_rows([self.up.strip(), row.strip(), self.dwn.strip()] for row in self.target_options) - self.cursor_foreground_priority = "css" - self.cursor_background_priority = "css" diff --git a/nnll_10/package/main_screen.py b/nnll_10/package/main_screen.py deleted file mode 100644 index dbbd1ff3..00000000 --- a/nnll_10/package/main_screen.py +++ /dev/null @@ -1,325 +0,0 @@ -# # # -# # # - -"""Auto-Orienting Split screen""" - -import os -from collections import defaultdict -from typing import Callable # , Any -from textual import events, on, work -from textual.app import ComposeResult -from textual.binding import Binding -from textual.containers import Container -from textual.message import Message -from textual.reactive import reactive -from textual.screen import Screen - -# from textual.widget import Widget -from textual.widgets import Static, ContentSwitcher # , DataTable -from textual.widget import Widget - - -from nnll_01 import debug_monitor, info_message as nfo, debug_message as dbug -from nnll_10.package.message_panel import MessagePanel -from nnll_10 import IntentProcessor -from nnll_10.package.input_tag import InputTag -from nnll_10.package.output_tag import OutputTag -from nnll_10.package.selectah import Selectah - - -class Fold(Screen[bool]): - """Orienting display Horizontal - Main interface container""" - - DEFAULT_CSS = """Screen { min-height: 5; }""" - - BINDINGS = [ - Binding("bk", "alternate_panel('text',0)", "⌨️"), # Return to text input panel - Binding("alt+bk", "clear_input", "del"), # Empty focused prompt panel - Binding("ent", "start_recording", "◉", priority=True), # Start audio prompt - Binding("space", "play", "▶︎", priority=True), # Listen to prompt audio - Binding("escape", "cancel_generation", "◼︎ / ⏏︎"), # Cancel response - Binding("`", "loop_sender", "✎", priority=True), # Send to LLM - ] - - ui: dict = defaultdict(dict) - int_proc: reactive[Callable] = reactive(None) - # model_names = [("model", 0), ("x", 0)] - tx_data: dict = {} - counter = 0 - hover_name: reactive[str] = reactive("") - input_map: dict = { - "text": "message_panel", - "image": "message_panel", - "speech": "voice_panel", - } - - def compose(self) -> ComposeResult: - """Textual API widget constructor, build graph, apply custom widget classes""" - from textual.containers import Horizontal - from textual.widgets import Footer - from nnll_10.package.display_bar import DisplayBar - - from nnll_10.package.response_panel import ResponsePanel - from nnll_10.package.voice_panel import VoicePanel - - self.int_proc = IntentProcessor() - self.int_proc.calc_graph() - self.ready_tx(mode_in="text", mode_out="text") - yield Footer(id="footer") - with Horizontal(id="app-grid", classes="app-grid-horizontal"): - yield ResponsiveLeftTop(id="left-frame") - with Container(id="centre-frame"): # 3:1:3 ratio - with Container(id="responsive_input"): # 3: - with ContentSwitcher(id="panel_swap", initial="message_panel"): - yield MessagePanel("""""", id="message_panel", max_checkpoints=100) - yield VoicePanel(id="voice_panel") - yield InputTag(id="input_tag", classes="input_tag") - with Horizontal(id="seam"): - yield DisplayBar(id="display_bar") # 1: - yield Selectah( - id="selectah", - classes="selectah", - prompt=os.path.basename(next(iter(self.int_proc.models))[0]), - options=self.int_proc.models, - type_to_search=True, - ) - with Container(id="responsive_display"): # - yield ResponsePanel("\n", id="response_panel", language="markdown") - yield OutputTag(id="output_tag", classes="output_tag") - yield ResponsiveRightBottom(id="right-frame") - - @work(exclusive=True) - async def on_mount(self) -> None: - """Textual API, Query all available widgets at once""" - self.ui["db"] = self.query_one("#display_bar") - self.ui["it"] = self.query_one("#input_tag") - self.ui["mp"] = self.query_one("#message_panel") - self.ui["ot"] = self.query_one("#output_tag") # type : ignore - self.ui["ps"] = self.query_one(ContentSwitcher) - self.ui["rd"] = self.query_one("#responsive_display") - self.ui["rp"] = self.query_one("#response_panel") - self.ui["vp"] = self.query_one("#voice_panel") - self.ui["sl"] = self.query_one("#selectah") - self.ready_tx() - self.walk_intent() - # id_name = self.input_tag.highlight_link_id - - @work(exit_on_error=False) - async def on_resize(self, event=events.Resize) -> None: - """Textual API, scale/orientation screen responsivity""" - if self.is_ui_ready(): - display = self.query_one("#app-grid") - width = event.container_size.width - height = event.container_size.height - if width / 2 >= height: # Screen is wide - display.set_classes("app-grid-horizontal") - elif width / 2 < height: # Screen is tall - display.set_classes("app-grid-vertical") - - @debug_monitor - def is_ui_ready(self): - """Confirm UI is active""" - try: - assert hasattr(self.ui["sl"], "is_mounted") - except AssertionError as error_log: - dbug(error_log) - return False - return True - - @on(events.Focus) - async def on_focus(self, event=events.Focus): - if event.control.id == "selectah": - self.ready_tx() - self.walk_intent() - self.ui["sl"].prompt = next(iter(self.int_proc.models))[0] - - @work(exclusive=True) - async def _on_key(self, event: events.Key) -> None: - """Textual API event trigger, Suppress/augment default key actions to trigger keybindings""" - if (hasattr(event, "character") and event.character == "`") or event.key == "grave_accent": - event.prevent_default() - self.ready_tx(io_only=False) - if self.int_proc.has_graph() and self.int_proc.has_path(): - self.walk_intent(send=True) - elif event.key == "escape" and "active" in self.ui["sl"].classes: - Message.stop(True) - self.stop_gen() - elif (hasattr(event, "character") and event.character == "\r") or event.key == "enter": - self.flip_panel("voice_panel", 1) - self.ui["vp"].record_audio() - self.audio_to_token() - elif (hasattr(event, "character") and event.character == " ") or event.key == "space": - self.flip_panel("voice_panel", 1) - self.ui["vp"].play_audio() - elif (event.name) == "ctrl_w" or event.key == "ctrl+w": - self.clear_input() - elif not self.ui["rp"].has_focus and ((hasattr(event, "character") and event.character == "\x7f") or event.key == "backspace"): - self.flip_panel("message_panel", 0) - - @debug_monitor - def _on_mouse_scroll_down(self, event: events.MouseScrollUp) -> None: - """Textual API event trigger, Translate scroll events into datatable cursor movement - Trigger scroll at 1/10th intensity when menu has focus - :param event: Event data for the trigger""" - - scroll_delta = [self.ui["it"].current_cell, self.ui["ot"].current_cell] - if self.ui["rd"].has_focus_within != self.ui["rp"].has_focus and not self.ui["sl"].has_focus: - event.prevent_default() - self.ui["ot"].emulate_scroll_down() - elif self.ui["it"].has_focus: - event.prevent_default() - mode_in_name = self.ui["it"].emulate_scroll_down() - self.ui["ps"].current = self.input_map.get(mode_in_name) - if scroll_delta != [self.ui["it"].current_cell, self.ui["ot"].current_cell]: - self.ready_tx() - self.walk_intent() - self.ui["sl"].mode_in = self.ui["it"].current_cell - self.ui["sl"].mode_out = self.ui["ot"].current_cell - self.ui["sl"].prompt = next(iter(self.int_proc.models))[0] - - @debug_monitor - def _on_mouse_scroll_up(self, event: events.MouseScrollUp) -> None: - """Textual API event trigger,Translate scroll events into datatable cursor movement - Trigger scroll at 1/10th intensity when menu has focus - :param event: Event data for the trigger""" - - scroll_delta = [self.ui["it"].current_cell, self.ui["ot"].current_cell] - if self.ui["rd"].has_focus_within != self.ui["rp"].has_focus and not self.ui["sl"].has_focus: - event.prevent_default() - self.ui["ot"].emulate_scroll_up() - elif self.ui["it"].has_focus: - event.prevent_default() - mode_name = self.ui["it"].emulate_scroll_up() - self.ui["ps"].current = self.input_map.get(mode_name) - if scroll_delta != [self.ui["it"].current_cell, self.ui["ot"].current_cell]: - self.ready_tx() - self.walk_intent() - self.ui["sl"].mode_in = self.ui["it"].current_cell - self.ui["sl"].mode_out = self.ui["ot"].current_cell - self.ui["sl"].prompt = next(iter(self.int_proc.models))[0] - - # @work(exclusive=True) - @on(MessagePanel.Changed, "#message_panel") - async def txt_to_token(self) -> None: - """Transmit info to token calculation""" - message = self.ui["mp"].text - next_model = next(iter(self.int_proc.models))[1] - self.ui["db"].calculate_tokens(next_model, message=message) - - @work(exclusive=True) - async def audio_to_token(self) -> None: - """Transmit audio to sample length""" - duration = self.ui["vp"].calculate_sample_length() - self.ui["db"].calculate_audio(duration) - - # @work(exclusive=True) - def ready_tx(self, io_only: bool = True, mode_in: str = None, mode_out: str = None) -> None: - """Retrieve graph""" - if not mode_in: - mode_in = self.ui["it"].get_cell_at((self.ui["it"].current_row, 1)) - if not mode_out: - mode_out = self.ui["ot"].get_cell_at((self.ui["ot"].current_row, 1)) - self.int_proc.set_path(mode_in=mode_in, mode_out=mode_out) - self.int_proc.set_ckpts() - if not io_only: - self.tx_data = { - "text": self.ui["mp"].text, - "audio": self.ui["vp"].audio, - # "attachment": self.message_panel.file # drag and drop from external window - # "image": self.image_panel.image # active video feed / screenshot / import file - } - - # @work(exclusive=True) - def walk_intent(self, send=False) -> None: - """Provided the coordinates in the intent processor, follow the list of in and out methods""" - coords = self.int_proc.coord_path - if not coords: - coords = ["text", "text"] - hops = len(coords) - 1 - for i in range(hops): - if i + 1 < hops: - if send: - self.tx_data = self.send_tx(last_hop=False) - self.ready_tx(mode_in=coords[i + 1], mode_out=coords[i + 2]) - else: - old_models = self.int_proc.models if self.int_proc.models else [] - dbug(old_models, "walk_intent") - self.ready_tx(mode_in=coords[i + 1], mode_out=coords[i + 2]) - self.int_proc.models.extend(old_models) - self.model_names = self.int_proc.models - dbug(self.int_proc.models) - - elif send: - self.send_tx() - - @work(exclusive=True) - async def send_tx(self, last_hop=True) -> None: - """Transfer path and promptmedia to generative processing endpoint""" - - from nnll_11 import ChatMachineWithMemory, QASignature - - ckpt = self.ui["sl"].selection - if ckpt is None: - ckpt = next(iter(self.int_proc.ckpts)).get("entry") - chat = ChatMachineWithMemory(sig=QASignature) - self.ui["rp"].on_text_area_changed() - self.ui["rp"].insert("\n---\n") - self.ui["sl"].add_class("active") - if last_hop: - nfo(ckpt) - async for chunk in chat.forward( - tx_data=self.tx_data, - model=ckpt.model, - library=ckpt.library, - max_workers=8, - ): - if chunk is not None: - self.ui["rp"].insert(chunk) - self.ui["sl"].set_classes(["selectah"]) - else: - self.tx_data = chat.forward( - tx_data=self.tx_data, - model=ckpt.model, - library=ckpt.library, - max_workers=8, - ) - - @work(exclusive=True) - async def stop_gen(self) -> None: - """Cancel the inference processing of a model""" - self.ui["rp"].workers.cancel_all() - self.ui["ot"].set_classes("output_tag") - - @work(exclusive=True) - async def clear_input(self) -> None: - """Clear the input on the focused panel""" - if self.ui["vp"].has_focus: - self.ui["vp"].erase_audio() - self.audio_to_token() - elif self.ui["mp"].has_focus: - self.ui["mp"].erase_message() - - @work(exclusive=True) - async def flip_panel(self, id_name: str, y_coord: int) -> None: - """Switch between text input and audio input - :param id_name: The panel to switch to - :param y_coordinate: _description_ - """ - self.ui["it"].scroll_to(x=1, y=y_coord, force=True, immediate=True, on_complete=self.ui["it"].refresh) - self.ui["ps"].current = id_name - - -class ResponsiveLeftTop(Container): - """Sidebar Left/Top""" - - def compose(self) -> ComposeResult: - yield Static() - - -class ResponsiveRightBottom(Container): - """Sidebar Right/Bottom""" - - def compose(self) -> ComposeResult: - yield Static() - yield Static() diff --git a/nnll_10/package/message_panel.py b/nnll_10/package/message_panel.py deleted file mode 100644 index f248acdb..00000000 --- a/nnll_10/package/message_panel.py +++ /dev/null @@ -1,19 +0,0 @@ -# # -# # -from textual import work -from textual.widgets import TextArea - - -# from nnll_01 import debug_monitor - - -class MessagePanel(TextArea): - """User entry field""" - - def on_mount(self): - self.cursor_blink = True - - @work(exclusive=True) - async def erase_message(self): - """Empty panel contents""" - self.clear() diff --git a/nnll_10/package/output_tag.py b/nnll_10/package/output_tag.py deleted file mode 100644 index 76417573..00000000 --- a/nnll_10/package/output_tag.py +++ /dev/null @@ -1,24 +0,0 @@ -# # # -# # # - -from textual.reactive import reactive -from textual.screen import Screen - -from nnll_10.package.carousel import Carousel - - -class OutputTag(Carousel): - """Populate Output types list""" - - target_options: reactive[set] = reactive({}) - - def on_mount(self): - scrn = self.query_ancestor(Screen) - if scrn.int_proc.has_graph(): - graph_edges = scrn.int_proc.intent_graph.edges - - self.target_options = sorted({edge[0] for edge in graph_edges}, key=len) - self.add_columns("0", "1", "2") - self.add_rows([self.up.strip(), row.strip(), self.dwn.strip()] for row in self.target_options) - self.cursor_foreground_priority = "css" - self.cursor_background_priority = "css" diff --git a/nnll_10/package/response_panel.py b/nnll_10/package/response_panel.py deleted file mode 100644 index 89d48164..00000000 --- a/nnll_10/package/response_panel.py +++ /dev/null @@ -1,27 +0,0 @@ -# # # -# # # - - -# from typing import Callable -from textual import work -from textual.widgets import TextArea - - -# from nnll_01 import debug_message as dbug -# from nnll_11 import chat_machine - - -class ResponsePanel(TextArea): - """Machine response field""" - - def on_mount(self) -> None: - self.language = "markdown" - self.read_only = True - self.soft_wrap = True - - @work(group="chat") - async def on_text_area_changed(self): - """Send cursor to end of document and animate scroll""" - self.move_cursor(self.document.end) - self.scroll_cursor_visible(center=True, animate=True) - self.scroll_end(animate=True) diff --git a/nnll_10/package/selectah.py b/nnll_10/package/selectah.py deleted file mode 100644 index 913b1ae6..00000000 --- a/nnll_10/package/selectah.py +++ /dev/null @@ -1,130 +0,0 @@ -# # # -# # # - -# pylint: disable=import-outside-toplevel - -import os -import networkx as nx -from rich.color import Color -from textual import on, events, work -from textual.reactive import reactive -from textual.screen import Screen -from textual.widgets import Select, Label -from textual.widgets._select import SelectCurrent, SelectOverlay - -from nnll_01 import debug_message as dbug, debug_monitor, info_message as nfo - - -class Selectah(Select): - models: reactive[list[tuple[str, str]]] = reactive([("", "")]) - graph: nx.Graph = None - mode_in: str = "text" - mode_out: str = "text" - focused = True - hover = True - - def on_mount(self) -> None: - # self.options = self.graph.models - self.graph = self.query_ancestor(Screen).int_proc - # self.prompt = os.path.basename(next(iter(self.graph.models))[0]) - - @on(Select.Changed) - async def on_changed(self) -> None: # event: Select.Changed) -> None: - """Rearrange models""" - try: - assert self.query_one(SelectCurrent).has_value - except AssertionError as error_log: - dbug(error_log) - else: - self.graph.edit_weight(selection=self.value, mode_in=self.mode_in, mode_out=self.mode_out) - self.set_options(self.graph.models) - self.prompt = next(iter(self.graph.models))[0] - self.expanded = False - - @debug_monitor - @work(exclusive=True) - @on(events.Enter) - async def on_enter(self, event: events.Enter) -> None: - """Force terminal mouse event monitoring""" - if event.node == SelectOverlay or event.node == self: - self.hover = True - - @debug_monitor - @work(exclusive=True) - @on(events.Leave) - async def on_leave(self, event: events.Leave) -> None: - """Force terminal mouse event monitoring""" - if event.node == SelectOverlay or event.node == self: - self.hover = True - - @work(exclusive=True) - @on(events.Focus) - async def on_focus(self, event: events.Focus) -> None: - """Expand panel immediately when clicked in terminal""" - if SelectOverlay.has_focus or self.has_focus: - self.focused = True - self.set_options(self.graph.models) - - @work(exclusive=True) - @on(events.MouseDown) - async def on_mouse_down(self, event: events.MouseDown) -> None: - """Expand panel immediately when clicked in terminal""" - if self.hover and not self.expanded: - self.expanded = True - elif (SelectOverlay.has_focus or self.has_focus) and self.expanded: - self.blur() - - @work(exclusive=True) - @on(events.MouseUp) - async def on_mouse_up(self, event: events.MouseUp) -> None: - """Expand panel immediately when clicked in terminal""" - if self.hover and self.focus and self.expanded: - self.expanded = False - elif self.hover and not self.expanded: - self.expanded = True - - -# if self.expanded is False: -# self.expanded = True - -# @on(SelectOverlay.blur) -# def on_select_overlay_blur(self, event: events.Blur) -> None: -# from rich.text import Text - -# nfo(event.control.id, "blah blah blah") -# # if event.control == SelectOverlay: -# self.ui["sl"].prompt = next(iter(self.int_proc.models))[0] -# label = self.ui["sl"].query_one("#label", Static) -# try: -# assert label.renderable == next(iter(self.int_proc.models))[0] -# except AssertionError as error_log: -# dbug(error_log) -# self.ui["sl"].prompt = next(iter(self.int_proc.models))[0] - -# @on(OptionList.OptionSelected) -# def on_select_overlay_option_selected(self, event: OptionList.OptionSelected) -> None: -# """Textual API event, refresh pathing, Switch checkpoint assignments""" -# nfo(" test write") -# overlay = self.ui["sl"].query_one(SelectOverlay) -# mode_in = self.ui["it"].get_cell_at((self.ui["it"].current_row, 1)) -# mode_out = self.ui["ot"].get_cell_at((self.ui["ot"].current_row, 1)) -# if self.ui["sl"].selection == Select.BLANK or self.ui["sl"].selection is None: -# selection = next(iter(self.int_proc.models))[1] -# else: -# selection = self.ui["sl"].selection -# self.int_proc.edit_weight(selection=selection, mode_in=mode_in, mode_out=mode_out) -# self.ready_tx() -# self.walk_intent() -# overlay.recompose() -# # self.ui["sl"].set_options(self.int_proc.models) - -# self.ready_tx() -# if self.int_proc.has_graph() and self.int_proc.has_path(): -# self.walk_intent() -# if self.int_proc.has_ckpt(): - -# self.ui["sl"].set_options(options=self.int_proc.models) -# nfo(self.int_proc.has_ckpt()) -# self.ui["sl"].expanded = False - -# @work(exclusive=True) diff --git a/nnll_10/package/token_counters.py b/nnll_10/package/token_counters.py deleted file mode 100644 index 5c711bf2..00000000 --- a/nnll_10/package/token_counters.py +++ /dev/null @@ -1,55 +0,0 @@ -# # # -# # # - - -# from nnll_30 import read_json_file - - -async def tk_count(model: str, message: str): - """Pass message to model routine - :param model: Path to model - :param message: Text to encode - :return: Token embeddings - """ - return await litellm_counter(model, message) - - -async def litellm_counter(model: str, message: str): - """ - Return token count of message based on model\n - :param model: Model path to lookup tokenizer for - :param message: Message to tokenize - :return: `int` Number of tokens needed to represent message - """ - from litellm.utils import token_counter - import os - - model_name = os.path.split(model) - model_name = os.path.join(os.path.split(model_name[0])[-1], model_name[-1]) - return token_counter(model_name, text=message) - - -async def ollama_counter(model: str, message: str): - """ - Return token count of message based on ollama model\n - :param model: Model to lookup tokenizer for - :param message: Message to tokenize - :return: `int` Number of tokens needed to represent message - """ - from ollama import embed - - response = embed(model, input=message) - return len(response["embeddings"]) - - -async def tiktoken_counter(model="cl100k_base", message: str = ""): - """ - Return token count of gpt based on model\n - :param model: Model path to lookup tokenizer for - :param message: Message to tokenize - :return: `int` Number of tokens needed to represent message - """ - import tiktoken - - encoding = tiktoken.get_encoding(model) - return len(encoding.encode(message)) diff --git a/nnll_10/package/voice_panel.py b/nnll_10/package/voice_panel.py deleted file mode 100644 index e36f704a..00000000 --- a/nnll_10/package/voice_panel.py +++ /dev/null @@ -1,80 +0,0 @@ -# # # -# # # - -# import array -import sounddevice as sd - -from textual import work -from textual.reactive import reactive - -from textual_plotext import PlotextPlot - -from nnll_01 import info_message as nfo # , debug_monitor - - -class VoicePanel(PlotextPlot): # (PlotWidget) - """Create an unselectable display element""" - - ALLOW_SELECT = False - audio = [0] - sample_freq: int = 16000 - duration: float = 3.0 - sample_len: reactive[float] = reactive(0.0, recompose=True) - - def on_mount(self): - self.can_focus = True - # self.theme = "flexoki" - - @work(exclusive=True) - async def record_audio(self) -> None: - """Get audio from mic""" - self.plt.clear_data() - precision = self.duration * self.sample_freq - self.audio = [0] - self.audio = sd.rec(int(precision), samplerate=self.sample_freq, channels=1) - sd.wait() - self.graph_audio() - # self.calculate_sample_length() - - @work(exclusive=True) - async def graph_audio(self): - """Draw audio waveform""" - self.plt.frame(0) - self.plt.canvas_color((0, 0, 0)) - self.can_focus = True - self.plt.xfrequency("0", "0") - self.plt.yfrequency("0", "0") - self.plt.scatter(self.audio[:, 0], marker="braille", color=(128, 0, 255)) - # self.calculate_sample_length() - - @work(exclusive=True) - async def play_audio(self): - """Playback audio recordings""" - try: - sd.play(self.audio, samplerate=self.sample_freq) - sd.wait() - except TypeError as error_log: - nfo(error_log) - - @work(exclusive=True) - async def erase_audio(self): - """Clear audio graph and recording""" - self.plt.clear_data() - self.audio = [0] - - def calculate_sample_length(self): - sample_length = len(self.audio) - duration = float(sample_length / self.sample_freq) if sample_length > 1 else 0.0 - return duration - - # from textual_plot import PlotWidget, HiResMode - # to use PlotWidget - # self.clear() - # self.set_xticks([]) - # self.set_yticks([]) - # self.set_xlabel("") - # self.set_ylabel("") - # self.set_styles("background: #333333;") - # self.scatter([i for i in range(0, len(self.audio))], self.audio[:, 0], hires_mode=HiResMode.BRAILLE, marker_style="purple") - - # self.clear() diff --git a/nnll_13/__init__.py b/nnll_13/__init__.py index 3e3b64b3..02f5c7ee 100644 --- a/nnll_13/__init__.py +++ b/nnll_13/__init__.py @@ -51,7 +51,7 @@ async def graph_audio(self): self.plt.xfrequency("0", "0") self.plt.yfrequency("0", "0") self.plt.scatter(self.audio[:, 0], marker="braille", color=(128, 0, 255)) - self.calculate_sample_length() + self.time_audio() @work(exclusive=True) async def play_audio(self): @@ -69,7 +69,7 @@ async def erase_audio(self): self.audio = [0] @work(exclusive=True) - async def calculate_sample_length(self): + async def time_audio(self): sample_len = float(len(self.audio) / self.sample_freq) self.sample_len = sample_len self.refresh() diff --git a/nnll_21/__init__.py b/nnll_21/__init__.py new file mode 100644 index 00000000..a60fa89b --- /dev/null +++ b/nnll_21/__init__.py @@ -0,0 +1,114 @@ +from enum import Enum +from typing import Literal + + +class AspectImage(str, Enum): + """Aspects for 2D image models + incl. Flux, SD3, SDXL, AuraFlow""" + + RATIOS = { + "1:1___1024x1024": (1024, 1024), + "16:15_1024x960": (1024, 960), + "17:15_1088x960": (1088, 960), + "17:14_1088x896": (1088, 896), + "18:13_1152x832": (1152, 832), + "4:3___1152x896": (1152, 896), + "3:2___1216x832": (1216, 832), + # "72:32_1232x832" : ( 1232, 832), + "5:3___1280x768": (1280, 768), + "21:11_1344x704": (1344, 704), + "7:4___1344x768": (1344, 768), + "2:1___1408x704": (1408, 704), + "23:11_1472x704": (1472, 704), + "21:9__1536x640": (1536, 640), + "2:1___1536x768": (1536, 768), + "5:2___1600x640": (1600, 640), + "26:9__1664x576": (1664, 576), + "3:1___1728x576": (1728, 576), + "28:9__1792x576": (1792, 576), + "29:8__1856x512": (1856, 512), + "15:4__1920x512": (1920, 512), + "31:8__1984x512": (1984, 512), + "4:1___2048x512": (2048, 512), + } + + +class AspectVideo(str, Enum): + """Aspects for Video models + incl. HunyuanVideo, Pyramid, Sora""" + + RATIOS = { + "1:1___V_256x256": (256, 256), + "4:3___V_320x240": (320, 240), + "32:27_V_576x486": (576, 486), + "22:15_V_704x480": (704, 480), + "9:5___V_720x400": (720, 400), + "3:2___V_720x480": (720, 480), + "5:4___V_720x576": (720, 576), + "3:2___V_768x512": (768, 512), + "4:3___V_832x624": (832, 624), + "53:30_V_848x480": (848, 480), + "4:3___V 960x704": (960, 704), + "1:1___V_960x960": (960, 960), + "20:11_V_1280x704": (1280, 704), + "16:9__V_1024X576": (1024, 576), + } + + +class AspectRender(str, Enum): + """Aspects for 3d-generative models + incl. SV3D""" + + RATIOS = { + "1:1__SV3D_576x576": (576, 576), + } + + +class AspectLegacy(str, Enum): + """Aspect ratios for earlier 2d Diffusion models + incl. Latent/Stable Diffusion, Pixart A, Playground 1, etc""" + + RATIOS = { + "1:1____SD_512x512": (512, 512), + "4:3____SD_682x512": (682, 512), + "3:2____SD_768x512": (768, 512), + "1:1____SD_768x768": (768, 768), + "16:9___SD_910x512": (910, 512), + "1:85:1_SD_952x512": (952, 512), + "2:1____SD_1024x512": (1024, 512), + } + + +class Precision(str, Enum): + MIXED = "mixed" + FP64 = "float64" + FP32 = "float32" + FP16 = "float16" + BF16 = "bfloat16" + FP8E4M3FN = "float8_e4m3fn" + FP8E5M2 = "float8_e5m2" + IN64 = "int64" + IN32 = "int32" + IN16 = "int16" + IN8 = "int8" + UN8 = "uint8" + NF4 = "nf4" + + +class TensorDataType: + TYPE_T = Literal["F64", "F32", "F16", "BF16", "F8_E4M3", "F8_E5M2", "I64", "I32", "I16", "I8", "U8", "nf4", "BOOL"] + TYPE_R = Literal["fp64", "fp32", "fp16", "bf16", "fp8_e4m3fn", "fp8_e5m2", "i64", "i32", "i16", "i8", "u8", "nf4", "bool"] + + +# class TensorData: +# dtype: DTYPE_T +# shape: List[int] +# data_offsets: Tuple[int, int] +# parameter_count: int = field(init=False) + +# def __post_init__(self) -> None: +# # Taken from https://stackoverflow.com/a/13840436 +# try: +# self.parameter_count = functools.reduce(operator.mul, self.shape) +# except TypeError: +# self.parameter_count = 1 # scalar value has no shape diff --git a/pyproject.toml b/pyproject.toml index 3607c302..0f568ceb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,8 +26,9 @@ keywords = [ "torch", ] -dependencies = ["structlog>=25.2.0", "viztracer>=1.0.3"] +dependencies = ["nnll_01"] [project.optional-dependencies] +nnll_01 = ["structlog>=25.2.0", "viztracer>=1.0.3"] nnll-02 = ["huggingface-hub>=0.29.3", "litellm>=1.65.0"] nnll-03 = ["aiofiles>=24.1.0", "aiohttp>=3.9.5, <=3.11.13", "tqdm>=4.67.1"] nnll-04 = ["gguf>=0.14.0", "safetensors>=0.5.3"] @@ -73,6 +74,7 @@ nnll-56 = ["hidiffusion>=0.1.10", "torch>=2.6.0", "torchvision>=0.21.0"] nnll-62 = ["diffusers>=0.32.2", "torch>=2.6.0", "torchvision>=0.21.0"] nnll-64 = ["pillow>=11.1.0"] all = [ + "nnll[nnll_01]", "nnll[nnll_02]", "nnll[nnll_03]", "nnll[nnll_04]", @@ -112,7 +114,6 @@ dev = [ ] [project.scripts] -zodiac = "nnll_10.package.__init__:main" astra = "nnll_12:__main__" nnll-find = "nnll_31:main" nnll-hash = "nnll_17:main" diff --git a/tests/test_14_draw_graph.py b/tests/test_14_draw_graph.py deleted file mode 100644 index 79d2c025..00000000 --- a/tests/test_14_draw_graph.py +++ /dev/null @@ -1,108 +0,0 @@ -# import networkx as nx -# from nnll_14 import calculate_graph -# import matplotlib.pyplot as plt -# import numpy as np - -# from nnll_15.constants import LibType - - -# def draw_matplot_labeled(nx_graph: nx.Graph) -> None: -# nx_graph = calculate_graph() -# path = nx.bidirectional_shortest_path(nx_graph, "text", "image") -# path_edges = list(zip(path, path[1:])) -# edge_colors = ["red" if edge in path_edges or tuple(reversed(edge)) in path_edges else "black" for edge in nx_graph.edges()] -# pos = nx.spring_layout(nx_graph) -# nx.draw_networkx_nodes(nx_graph, pos) -# nx.draw_networkx_edges(nx_graph, pos, edge_color=edge_colors) -# nx.draw_networkx_labels(nx_graph, pos) -# # nx.draw_networkx_edges(nx_graph, pos) -# # nx.draw_networkx_edges(nx_graph, pos, edge_labels={(u, v): d for u, v, d in nx_graph.edges(data=True)}) -# nx.draw_networkx_edge_labels(nx_graph, pos, edge_labels={(u, v): d for u, v, d in nx_graph.edges(data=True)}, font_size=3, alpha=0.5, rotate=False) -# plt.show() - - -# def draw_matplot_circular() -> None: -# nx_graph = calculate_graph() -# path = nx.bidirectional_shortest_path(nx_graph, "image", "speech") -# path_edges = list(zip(path, path[1:])) -# edge_colors = ["red" if edge in path_edges or tuple(reversed(edge)) in path_edges else "black" for edge in nx_graph.edges()] -# pos = nx.circular_layout(nx_graph) -# nx.draw_networkx_nodes(nx_graph, pos) -# nx.draw_networkx_edges(nx_graph, pos, edge_color=edge_colors) -# nx.draw_networkx_labels(nx_graph, pos) -# nx.draw_circular(nx_graph) -# plt.show() - - -# def draw_matplot_graphviz() -> None: -# nx_graph = calculate_graph() -# path = nx.bidirectional_shortest_path(nx_graph, "image", "speech") -# path_edges = list(zip(path, path[1:])) -# edge_colors = ["red" if edge in path_edges or tuple(reversed(edge)) in path_edges else "black" for edge in nx_graph.edges()] -# pos = nx.nx_agraph.graphviz_layout(nx_graph, prog="twopi", root=0) -# options = {"with_labels": False, "alpha": 0.5, "node_size": 15} -# nx.draw(nx_graph, pos, node_color=edge_colors, **options) -# plt.show() - - -# def draw_matplot_weights() -> None: -# nx_graph = calculate_graph() -# pos = nx.spring_layout(nx_graph, scale=20, k=3 / np.sqrt(nx_graph.order())) -# nx.draw(nx_graph, pos=pos, node_color="lightblue", with_labels=True, node_size=500) -# labels = nx.get_edge_attributes(nx_graph, "weight") -# nx.draw_networkx_edge_labels(nx_graph, pos, edge_labels=labels) -# plt.show() - - -# if __name__ == "__main__": -# # draw_matplot_weights() -# draw_matplot_circular() - - -# # from textual.app import App, ComposeResult -# # from textual.widgets import Static -# # from textual_plot import HiResMode # , PlotWidget -# # from textual_plotext import PlotextPlot -# # from networkx import convert_node_labels_to_integers -# # import numpy as np -# # from typing import Sequence -# # class MinimalApp(App[None]): -# # def compose(self) -> ComposeResult: -# # yield Static() -# # # yield PlotextPlot(id="plotext") -# # # yield PlotWidget(id="plot) - -# # def on_mount(self) -> None: - -# # nx_graph = label_edge_attrib_for(nx_graph) -# # self.draw_matplot(nx_graph) -# # # nx_graph_num = convert_node_labels_to_integers(nx_graph) -# # # self.draw_textual_plotext(nx_graph_num) -# # # self.draw_textual_plot(nx_graph_num) - - -# # def draw_textual_plotext(self, nx_graph_num: nx.Graph) -> None: -# # plot = self.query_one("#plotext") -# # first_column = [item[0] for item in list(nx_graph_num.edges())] -# # second_column = [item[1] for item in list(nx_graph_num.edges())] -# # plot.plot(x=first_column, y=second_column, hires_mode=HiResMode.BRAILLE, line_style="purple") - -# # def draw_textual_plot(self, nx_graph_num: nx.Graph) -> None: -# # plot = self.query_one("#plot") -# # plot.plt.scatter(nx_graph_num.edges()) - - -# # if __name__ == "__main__": -# # MinimalApp().run() - -# # data = dict(list(nx_graph_num.edges())) -# # for each in list(nx_graph_num.edges()): - - -# # # nx -# # # plt.show() -# # # # plot.set_xticks([]) -# # # # plot.set_yticks([]) -# # # # plot.set_xlabel("") -# # # # plot.set_ylabel("") -# # # # plot.set_styles("background: #1f1f1f;") diff --git a/tests/test_14_graph.py b/tests/test_14_graph.py deleted file mode 100644 index 6b806efe..00000000 --- a/tests/test_14_graph.py +++ /dev/null @@ -1,339 +0,0 @@ -# # # # -# # # # - -# # pylint:disable=redefined-outer-name -# # pylint:disable=redefined-builtin - -# import datetime -# from pathlib import PosixPath -# from unittest import mock - -# # import matplotlib.pyplot as plt -# import pytest -# from nnll_14 import calculate_graph -# from nnll_15.constants import VALID_CONVERSIONS - - -# class Model: -# """Mock ollama Model class""" - -# def __init__(self, model=None, modified_at=None, digest=None, size=None, details=None): -# self.model = model -# self.modified_at = modified_at -# self.digest = digest -# self.size = size -# self.details = details - - -# class ModelDetails: -# """Mock ollama ModelDetails class""" - -# def __init__(self, parent_model=None, format=None, family=None, families=None, parameter_size=None, quantization_level=None): -# self.parent_model = parent_model -# self.format = format -# self.family = family -# self.families = families -# self.parameter_size = parameter_size -# self.quantization_level = quantization_level - - -# class ListResponse: -# """Mock ollama ListResponse class""" - -# def __init__(self, models=None): -# self.models = models - - -# @pytest.fixture(scope="session") -# def mock_ollama_data(): -# """Mock ollama response""" -# with mock.patch("ollama.list", new_callable=mock.MagicMock()) as mock_get_registry_data: -# data = ListResponse( -# models=[ -# Model( -# model="hf.co/unsloth/gemma-3-27b-it-GGUF:Q8_0", -# modified_at=datetime.datetime(2025, 3, 19, 12, 21, 19, 112890, tzinfo=None), -# digest="965289b1e3e63c66bfc018051b6a907b2f0b18620d5721dd1cdfad759b679a2c", -# size=29565711760, -# details=ModelDetails(parent_model="", format="gguf", family="gemma3", families=["gemma3"], parameter_size="27B", quantization_level="unknown"), -# ), -# Model( -# model="hf.co/unsloth/gemma-3-27b-it-GGUF:Q5_K_M", -# modified_at=datetime.datetime(2025, 3, 18, 12, 13, 57, 294851, tzinfo=None), -# digest="82c7d241b764d0346f382a9059a7b08056075c7bc2d81ac21dfa20d525556b16", -# size=20129415184, -# details=ModelDetails(parent_model="", format="gguf", family="gemma3", families=["gemma3"], parameter_size="27B", quantization_level="unknown"), -# ), -# Model( -# model="hf.co/bartowski/RekaAI_reka-flash-3-GGUF:Q5_K_M", -# modified_at=datetime.datetime(2025, 3, 13, 18, 28, 57, 859962, tzinfo=None), -# digest="43d35cd4e25e90f9cbb33585f60823450bd1f279c4703a1b2831a9cba73e60e4", -# size=15635474582, -# details=ModelDetails(parent_model="", format="gguf", family="llama", families=["llama"], parameter_size="20.9B", quantization_level="unknown"), -# ), -# ] -# ) -# mock_get_registry_data.return_value = data -# yield mock_get_registry_data - - -# class HFCacheInfo: -# """Mock hub cache""" - -# def __init__(self, size_on_disk, repos): -# self.size_on_disk = size_on_disk -# self.repos = repos - - -# class CachedRepoInfo: -# """Mock hub repo cache""" - -# def __init__(self, repo_id, repo_type, repo_path, size_on_disk, nb_files, revisions, files, last_accessed, last_modified): -# self.repo_id = repo_id -# self.repo_type = repo_type -# self.repo_path = repo_path -# self.size_on_disk = size_on_disk -# self.nb_files = nb_files -# self.revisions = revisions -# self.files = files -# self.last_accessed = last_accessed -# self.last_modified = last_modified - - -# @pytest.fixture(scope="session") -# def mock_hub_data(): -# """Mock hub data""" -# with mock.patch("huggingface_hub.scan_cache_dir", new_callable=mock.MagicMock()) as mock_get_registry_data: -# data = HFCacheInfo( -# size_on_disk=91018285403, -# repos=frozenset( -# { -# CachedRepoInfo( -# repo_id="parler-tts/parler-tts-large-v1", -# repo_type="model", -# repo_path=PosixPath("/Users/unauthorized/.cache/huggingface/hub/models--parler-tts--parler-tts-large-v1"), -# size_on_disk=9335526346, -# nb_files=14, -# revisions=None, -# files=None, -# last_accessed=1741910585.3828554, -# last_modified=1741908821.5103855, -# ), -# CachedRepoInfo( -# repo_id="THUDM/CogView3-Plus-3B", -# repo_type="model", -# repo_path=PosixPath("/Users/unauthorized/.cache/huggingface/hub/models--THUDM--CogView3-Plus-3B"), -# size_on_disk=25560123724, -# nb_files=20, -# revisions=None, -# files=None, -# last_accessed=1741827083.5111423, -# last_modified=1741827083.4126444, -# ), -# } -# ), -# ) -# mock_get_registry_data.return_value = data -# yield mock_get_registry_data - - -# def test_mocked_ollama(mock_ollama_data): -# """Check if mocking ollama correctly""" -# result = mock_ollama_data() - -# assert len(result.models) == 3 -# next_model = next(iter(result.models)) -# assert next_model.model == "hf.co/unsloth/gemma-3-27b-it-GGUF:Q8_0" -# assert next_model.size == 29565711760 - - -# def test_mocked_hub(mock_hub_data): -# """Check if mocking hub correctly. -# `frozenset` is converted to a sorted list -# Otherwise hashed return becomes unordered""" -# result = mock_hub_data() -# new_list = [] -# assert len(result.repos) == 2 -# next_model = [*result.repos] -# for x in next_model: -# new_list.append([x.repo_id, x.size_on_disk]) -# new_list.sort(key=lambda x: x[1]) -# assert new_list[0][0] == "parler-tts/parler-tts-large-v1" -# assert new_list[0][1] == 9335526346 - - -# def test_create_graph(mock_ollama_data, mock_hub_data): -# """Run test of graph creation""" -# nx_graph = calculate_graph() - -# assert list(nx_graph) == VALID_CONVERSIONS -# key_data = nx_graph.edges.data("key") -# for edge in key_data: -# if edge[2] is not None: -# assert isinstance(edge[2], str) -# else: -# assert isinstance(edge[1], str) - -# size_data = nx_graph.edges.data("size") -# for edge in size_data: -# if edge[2] is not None: -# assert isinstance(edge[2], int) -# else: -# assert isinstance(edge[1], str) - - -# # when user presses trigger : -# # run shortest distance, then run operations identified on edges - -# # seen = set() -# # [e[1] for e in nx_graph.edges if e[1] not in seen and not seen.add(e[1])] - -# # nx_graph['speech']['text’] get all paths towards -# # get all size on graph -# # nx_graph.edges.data(“keys”) get all model name on graph -# # nx_graph.edges['text','speech',0]['key'] - -# # nx_graph.out_degree('text') get number of edges/paths pointing away -# # nx_graph.in_degree('text') get number of edges/paths pointing towards -# # nx_graph.edges[‘text’, ‘image’][‘weight'] = 4.2 change attribute - - -# # node_attrib = nx.get_node_attributes(nx_graph, “model”) -# # node_attrib[‘text’] - - -# # nx.draw_networkx -# # adjacent_pairs = [(key, item) for key, value in VALID_CONVERSIONS.input.items() for item in (value.values if isinstance(value.values, tuple) else ())] -# # from typing import Dict, Tuple -# # from pydantic import BaseModel, Field - -# # class ConversionValue(BaseModel): -# # """(output_medium, more_output_medium)""" - -# # values: Field() - - -# # class ConversionMap(BaseModel): -# # """{input_medium: (output_medium, more_output_medium)""" - -# # input: Dict[str, ConversionValue] - - -# # from networkx import convert_node_labels_to_integers - - -# # mllama (vllm), text-to-image, text-generation - -# # 2. Add nodes for each unique data type and model combination (e.g., `['text']`, `['image']`, etc.) and edges representing the transformations between them using models. - - -# # # Define a function to add edges based on input-output pairs -# # def add_model_edges(G, input_type, model_dict, output_type): -# # for model_name, model_path in model_dict.items(): -# # G.add_edge(str(input_type), str(output_type), model_name=model_name, model_path=model_path) - - -# # # Add the specified paths to your graph -# # add_model_edges(G, ["text"], {"model1": "path1"}, ["text"]) -# # add_model_edges(G, ["text", "image"], {"model2": "path2"}, ["text"]) -# # add_model_edges(G, ["image"], {"model3": "path3"}, ["text"]) -# # add_model_edges(G, ["text"], {"model4": "path4"}, ["image"]) -# # add_model_edges(G, ["speech"], {"model5": "path5"}, ["text"]) -# # add_model_edges(G, ["text"], {"model6": "path6"}, ["speech"]) - -# # # Example: Find all paths from ['text'] to ['image'] -# # paths = list(nx.all_simple_paths(G, str(["text"]), str(["image"]))) -# # print(paths) - - -# # node would be format -# # edge would be conversion model -# # 'vllm' -# # 'text-generation' - -# # input #output -# # node #edge #node -# # {['text']} { model name: model path} } { ['text']] } -# # [['text', 'image']: { model name: model path} } { ['text'] } -# # {['image']: { model name: model path} } { ['text'] } -# # {['text']: { model name: model path} } { ['image'] } -# # {['speech']: { model name: model path} } { ['text'] } -# # {['text']: { model name: model path} } { ['speech']} - - -# # bidirectional_shortest_path(G, mode_in, mode_out) - -# # G.add_edges_from[(2, 3, {"weight": 3.1415})] #add edge with attribute -# # G.add_nodes_from([(4, {"color": "red"}), (5, {"color": "green"})]) #add node with attribute -# # H = nx.path_graph(10) -# # G.add_nodes_from(H) # import one graph into another -# # G.add_node(H) # the entire graph as a node -# # G.clear() - -# # class ConversionGraph: -# # def __init__(self, graph): -# # self.graph = graph # Graph where keys are formats, values are dict of {format: (steps, quality)} - -# # def manhattan_distance(self, node1, node2): -# # return abs(node1[0] - node2[0]) + abs(node1[1] - node2[1]) - -# # def find_conversion_path(self, initial_format, target_format): -# # # Check if direct conversion is possible -# # if target_format in self.graph[initial_format]: -# # return [(initial_format, target_format)] - -# # # Initialize variables for pathfinding -# # queue = [[(initial_format, 0, float("inf"))]] # (format, steps, quality) -# # visited = set() - -# # while queue: -# # path = queue.pop(0) -# # current_node = path[-1][0] -# # current_steps = path[-1][1] -# # current_quality = path[-1][2] - -# # if current_node == target_format: -# # return path - -# # for neighbor, (steps, quality) in self.graph[current_node].items(): -# # # Avoid backtracking and only move forward -# # if neighbor not in visited: -# # new_steps = current_steps + steps -# # new_quality = min(current_quality, quality) -# # distance_to_goal = self.manhattan_distance((new_steps, new_quality), (0, float("inf"))) - -# # # Prioritize paths with fewer steps but consider higher quality nodes -# # queue.append(path + [(neighbor, new_steps, new_quality)]) - -# # visited.add(current_node) -# # queue.sort(key=lambda p: (len(p), -p[-1][2])) # Sort by path length and quality - -# # return None - - -# # # (steps, quality) -# # graph = { -# # "FormatA": {"FormatB": (1, 8), "FormatC": (2, 9)}, -# # "FormatB": {"FormatD": (1, 7)}, -# # "FormatC": {"FormatD": (3, 6), "FormatE": (2, 10)}, -# # "FormatD": {"TargetFormat": (1, 5)}, -# # "FormatE": {"TargetFormat": (1, 9)}, -# # } - -# # if __name__ == "__main__": -# # converter = ConversionGraph(graph) -# # path = converter.find_conversion_path("FormatA", "TargetFormat") -# # print("Conversion Path:", path) - - -# # for model, details in ollama_models.items(): -# # nx_graph.add_edges_from(details.available_tasks, key=model, weight=details.size) -# # for model, details in hub_models.items(): -# # nx_graph.add_edges_from(details.available_tasks, key=model, weight=details.size) -# # nx_graph = build_conversion_graph() -# # ollama_models = from_ollama_cache() -# # hub_models = from_hf_hub_cache() -# # for model, details in ollama_models.items(): -# # nx_graph.add_edges_from(details.available_tasks, label=model, weight=details.size) -# # for model, details in hub_models.items(): -# # nx_graph.add_edges_from(details.available_tasks, label=model, weight=details.size) From eef99c67d0cbc6ebddf4f3b755dbce1b599c0ef9 Mon Sep 17 00:00:00 2001 From: exdysa <91800957+exdysa@users.noreply.github.com> Date: Tue, 22 Apr 2025 17:43:22 -0400 Subject: [PATCH 3/3] +dependency adjustments --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 0f568ceb..2bd44cf5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,6 @@ keywords = [ "torch", ] -dependencies = ["nnll_01"] [project.optional-dependencies] nnll_01 = ["structlog>=25.2.0", "viztracer>=1.0.3"] nnll-02 = ["huggingface-hub>=0.29.3", "litellm>=1.65.0"]