psaiops 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of psaiops might be problematic. Click here for more details.
- psaiops-0.0.1/.github/README.md +16 -0
- psaiops-0.0.1/PKG-INFO +36 -0
- psaiops-0.0.1/psaiops/__init__.py +0 -0
- psaiops-0.0.1/psaiops/compose/__init__.py +0 -0
- psaiops-0.0.1/psaiops/elements/__init__.py +0 -0
- psaiops-0.0.1/psaiops/elements/data.py +17 -0
- psaiops-0.0.1/psaiops/elements/dropdown.py +19 -0
- psaiops-0.0.1/psaiops/score/__init__.py +0 -0
- psaiops-0.0.1/psaiops/steer/__init__.py +0 -0
- psaiops-0.0.1/psaiops/steer/app.py +202 -0
- psaiops-0.0.1/pyproject.toml +21 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# psAI ops <img src="images/logo.png" alt="apehex logo" width="32" height="32">
|
|
2
|
+
|
|
3
|
+
[![License][shield-license]][github-license]
|
|
4
|
+
[![Latest][shield-release]][github-release]
|
|
5
|
+
|
|
6
|
+
Collection of web apps to inspect & engineer activations.
|
|
7
|
+
|
|
8
|
+
## License
|
|
9
|
+
|
|
10
|
+
Licensed under the [aGPLv3][github-license].
|
|
11
|
+
|
|
12
|
+
[github-license]: LICENSE.md
|
|
13
|
+
[github-release]: https://github.com/apehex/psai-ops/releases/latest
|
|
14
|
+
|
|
15
|
+
[shield-license]: https://img.shields.io/badge/license-aGPLv3-green?style=flat-square
|
|
16
|
+
[shield-release]: https://img.shields.io/github/release/apehex/psai-ops.svg?style=flat-square
|
psaiops-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: psaiops
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Web apps to inspect & engineer NN activations.
|
|
5
|
+
License: .github/LICENSE.md
|
|
6
|
+
Author: apehex
|
|
7
|
+
Author-email: apehex@protonmail.com
|
|
8
|
+
Requires-Python: >=3.10,<3.14
|
|
9
|
+
Classifier: License :: Other/Proprietary License
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Requires-Dist: deformers (>=0.0)
|
|
16
|
+
Requires-Dist: gradio (>=5.0)
|
|
17
|
+
Requires-Dist: requests (>=2.0)
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# psAI ops <img src="images/logo.png" alt="apehex logo" width="32" height="32">
|
|
21
|
+
|
|
22
|
+
[![License][shield-license]][github-license]
|
|
23
|
+
[![Latest][shield-release]][github-release]
|
|
24
|
+
|
|
25
|
+
Collection of web apps to inspect & engineer activations.
|
|
26
|
+
|
|
27
|
+
## License
|
|
28
|
+
|
|
29
|
+
Licensed under the [aGPLv3][github-license].
|
|
30
|
+
|
|
31
|
+
[github-license]: LICENSE.md
|
|
32
|
+
[github-release]: https://github.com/apehex/psai-ops/releases/latest
|
|
33
|
+
|
|
34
|
+
[shield-license]: https://img.shields.io/badge/license-aGPLv3-green?style=flat-square
|
|
35
|
+
[shield-release]: https://img.shields.io/github/release/apehex/psai-ops.svg?style=flat-square
|
|
36
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
# CONSTANTS ####################################################################
|
|
4
|
+
|
|
5
|
+
HF_URL = 'https://huggingface.co/api/quicksearch?q={target}&type={label}&limit={limit}'
|
|
6
|
+
|
|
7
|
+
# HUGGING FACE #################################################################
|
|
8
|
+
|
|
9
|
+
def query_huggingface(target: str, label: str='model', limit: int=16, endpoint: str=HF_URL) -> list:
|
|
10
|
+
__results = []
|
|
11
|
+
# query HF
|
|
12
|
+
__response = requests.get(endpoint.format(target=target, label=label, limit=limit))
|
|
13
|
+
# no error
|
|
14
|
+
if __response.status_code == 200:
|
|
15
|
+
__results = [__d['id'] for __d in __response.json().get(f'{label}s', [])]
|
|
16
|
+
# list of strings
|
|
17
|
+
return __results
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import gradio
|
|
2
|
+
|
|
3
|
+
import psaiops.elements.data
|
|
4
|
+
|
|
5
|
+
# AUTO-COMPLETE ################################################################
|
|
6
|
+
|
|
7
|
+
def update_dropdown(query, label: str, data: gradio.KeyUpData):
|
|
8
|
+
# model_dropdown.key_up(fn=update_dropdown, inputs=[model_dropdown, gradio.State("model")], outputs=model_dropdown, queue=False, show_progress="hidden")
|
|
9
|
+
datasets = psaiops.elements.data.query_huggingface(target=data.input_value, label=label, limit=16)
|
|
10
|
+
return gradio.update(choices=datasets, visible=True)
|
|
11
|
+
|
|
12
|
+
# with gradio.Blocks() as demo:
|
|
13
|
+
# model_dropdown = gradio.Dropdown(label="Models Auto-Complete", choices=[""], allow_custom_value=True)
|
|
14
|
+
# dataset_dropdown = gradio.Dropdown(label="Datasets Auto-Complete", choices=[""], allow_custom_value=True)
|
|
15
|
+
# spaces_dropdown = gradio.Dropdown(label="Spaces Auto-Complete", choices=[""], allow_custom_value=True)
|
|
16
|
+
# model_dropdown.key_up(fn=update_dropdown, inputs=[model_dropdown, gradio.State("model")], outputs=model_dropdown, queue=False, show_progress="hidden")
|
|
17
|
+
# dataset_dropdown.key_up(fn=update_dropdown, inputs=[dataset_dropdown, gradio.State("dataset")], outputs=dataset_dropdown, queue=False, show_progress="hidden")
|
|
18
|
+
# spaces_dropdown.key_up(fn=update_dropdown, inputs=[spaces_dropdown, gradio.State("space")], outputs=spaces_dropdown, queue=False, show_progress="hidden")
|
|
19
|
+
# demo.launch(share=True, debug=True)
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import gradio
|
|
2
|
+
|
|
3
|
+
# MODEL ########################################################################
|
|
4
|
+
|
|
5
|
+
def create_model_tab():
|
|
6
|
+
pass
|
|
7
|
+
|
|
8
|
+
# PROMPT #######################################################################
|
|
9
|
+
|
|
10
|
+
# SAMPLING #####################################################################
|
|
11
|
+
|
|
12
|
+
class GradioInterface:
|
|
13
|
+
"""Manages the Gradio user interface."""
|
|
14
|
+
|
|
15
|
+
def __init__(self, orchestra: LLMForestOrchestra):
|
|
16
|
+
"""Initialize the interface."""
|
|
17
|
+
self.orchestra = orchestra
|
|
18
|
+
|
|
19
|
+
def create_interface(self) -> gr.Blocks:
|
|
20
|
+
"""Create the Gradio interface."""
|
|
21
|
+
with gr.Blocks(title="LLM Forest Orchestra", theme=gr.themes.Soft()) as demo:
|
|
22
|
+
gr.Markdown(self.DESCRIPTION)
|
|
23
|
+
|
|
24
|
+
with gr.Tabs():
|
|
25
|
+
with gr.TabItem("🎵 Generate Music"):
|
|
26
|
+
self._create_generation_tab()
|
|
27
|
+
|
|
28
|
+
return demo
|
|
29
|
+
|
|
30
|
+
def _create_generation_tab(self):
|
|
31
|
+
"""Create the main generation tab."""
|
|
32
|
+
with gr.Row():
|
|
33
|
+
with gr.Column(scale=1):
|
|
34
|
+
text_input = gr.Textbox(
|
|
35
|
+
value=self.EXAMPLE_TEXT,
|
|
36
|
+
label="Input Text",
|
|
37
|
+
lines=8,
|
|
38
|
+
placeholder="Enter text to sonify..."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
model_name = gr.Textbox(
|
|
42
|
+
value=self.orchestra.DEFAULT_MODEL,
|
|
43
|
+
label="Hugging Face Model",
|
|
44
|
+
info="Model must support output_hidden_states and output_attentions"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
compute_mode = gr.Radio(
|
|
48
|
+
choices=["Full model", "Mock latents"],
|
|
49
|
+
value="Mock latents",
|
|
50
|
+
label="Compute Mode",
|
|
51
|
+
info="Mock latents for quick CPU-only demo"
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
with gr.Row():
|
|
55
|
+
instrument_preset = gr.Dropdown(
|
|
56
|
+
choices=self.orchestra.instrument_manager.list_presets(),
|
|
57
|
+
value="Ensemble (melody+bass+pad etc.)",
|
|
58
|
+
label="Instrument Preset"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
scale_choice = gr.Dropdown(
|
|
62
|
+
choices=self.orchestra.scale_manager.list_scales() + ["Custom"],
|
|
63
|
+
value="C pentatonic",
|
|
64
|
+
label="Musical Scale"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
custom_scale = gr.Textbox(
|
|
68
|
+
value="",
|
|
69
|
+
label="Custom Scale Notes",
|
|
70
|
+
placeholder="60,62,65,67,70",
|
|
71
|
+
visible=False
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
with gr.Row():
|
|
75
|
+
base_tempo = gr.Slider(
|
|
76
|
+
120, 960,
|
|
77
|
+
value=480,
|
|
78
|
+
step=1,
|
|
79
|
+
label="Tempo (ticks per beat)"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
num_layers = gr.Slider(
|
|
83
|
+
1, 6,
|
|
84
|
+
value=6,
|
|
85
|
+
step=1,
|
|
86
|
+
label="Max Layers"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
with gr.Row():
|
|
90
|
+
velocity_low = gr.Slider(
|
|
91
|
+
1, 126,
|
|
92
|
+
value=40,
|
|
93
|
+
step=1,
|
|
94
|
+
label="Min Velocity"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
velocity_high = gr.Slider(
|
|
98
|
+
2, 127,
|
|
99
|
+
value=90,
|
|
100
|
+
step=1,
|
|
101
|
+
label="Max Velocity"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
seed = gr.Number(
|
|
105
|
+
value=42,
|
|
106
|
+
precision=0,
|
|
107
|
+
label="Random Seed"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
generate_btn = gr.Button(
|
|
111
|
+
"🎼 Generate MIDI",
|
|
112
|
+
variant="primary",
|
|
113
|
+
size="lg"
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
with gr.Column(scale=1):
|
|
117
|
+
midi_output = gr.File(
|
|
118
|
+
label="Generated MIDI File",
|
|
119
|
+
file_types=[".mid", ".midi"]
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
stats_display = gr.Markdown(label="Quick Stats")
|
|
123
|
+
|
|
124
|
+
metadata_json = gr.Code(
|
|
125
|
+
label="Metadata (JSON)",
|
|
126
|
+
language="json"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
with gr.Row():
|
|
130
|
+
play_instructions = gr.Markdown(
|
|
131
|
+
"""
|
|
132
|
+
### 🎧 How to Play
|
|
133
|
+
1. Download the MIDI file
|
|
134
|
+
2. Open in any DAW or MIDI player
|
|
135
|
+
3. Adjust instruments and effects as desired
|
|
136
|
+
4. Export to audio format
|
|
137
|
+
"""
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
# Set up interactions
|
|
141
|
+
def update_custom_scale_visibility(choice):
|
|
142
|
+
return gr.update(visible=(choice == "Custom"))
|
|
143
|
+
|
|
144
|
+
scale_choice.change(
|
|
145
|
+
update_custom_scale_visibility,
|
|
146
|
+
inputs=[scale_choice],
|
|
147
|
+
outputs=[custom_scale]
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
def generate_wrapper(
|
|
151
|
+
text, model_name, compute_mode, base_tempo,
|
|
152
|
+
velocity_low, velocity_high, scale_choice,
|
|
153
|
+
custom_scale, num_layers, instrument_preset, seed
|
|
154
|
+
):
|
|
155
|
+
"""Wrapper for generation with error handling."""
|
|
156
|
+
try:
|
|
157
|
+
# Parse custom scale if needed
|
|
158
|
+
custom_notes = None
|
|
159
|
+
if scale_choice == "Custom" and custom_scale:
|
|
160
|
+
custom_notes = [int(x.strip()) for x in custom_scale.split(",")]
|
|
161
|
+
|
|
162
|
+
# Generate
|
|
163
|
+
filename, metadata = self.orchestra.generate(
|
|
164
|
+
text=text,
|
|
165
|
+
model_name=model_name,
|
|
166
|
+
compute_mode=compute_mode,
|
|
167
|
+
base_tempo=int(base_tempo),
|
|
168
|
+
velocity_range=(int(velocity_low), int(velocity_high)),
|
|
169
|
+
scale_name=scale_choice,
|
|
170
|
+
custom_scale_notes=custom_notes,
|
|
171
|
+
num_layers=int(num_layers),
|
|
172
|
+
instrument_preset=instrument_preset,
|
|
173
|
+
seed=int(seed)
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
# Format stats
|
|
177
|
+
stats = metadata.get("stats", {})
|
|
178
|
+
stats_text = f"""
|
|
179
|
+
### Generation Statistics
|
|
180
|
+
- **Layers Used**: {stats.get('num_layers', 'N/A')}
|
|
181
|
+
- **Tokens Processed**: {stats.get('num_tokens', 'N/A')}
|
|
182
|
+
- **Total Notes**: {stats.get('total_notes', 'N/A')}
|
|
183
|
+
- **Notes per Layer**: {stats.get('notes_per_layer', [])}
|
|
184
|
+
- **Scale**: {stats.get('scale', [])}
|
|
185
|
+
- **Tempo**: {stats.get('tempo_ticks_per_beat', 'N/A')} ticks/beat
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
return filename, stats_text, json.dumps(metadata, indent=2)
|
|
189
|
+
|
|
190
|
+
except Exception as e:
|
|
191
|
+
error_msg = f"### ❌ Error\n{str(e)}"
|
|
192
|
+
return None, error_msg, json.dumps({"error": str(e)}, indent=2)
|
|
193
|
+
|
|
194
|
+
generate_btn.click(
|
|
195
|
+
fn=generate_wrapper,
|
|
196
|
+
inputs=[
|
|
197
|
+
text_input, model_name, compute_mode, base_tempo,
|
|
198
|
+
velocity_low, velocity_high, scale_choice,
|
|
199
|
+
custom_scale, num_layers, instrument_preset, seed
|
|
200
|
+
],
|
|
201
|
+
outputs=[midi_output, stats_display, metadata_json]
|
|
202
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "psaiops"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = "Web apps to inspect & engineer NN activations."
|
|
5
|
+
license = ".github/LICENSE.md"
|
|
6
|
+
readme = ".github/README.md"
|
|
7
|
+
authors = ["apehex <apehex@protonmail.com>"]
|
|
8
|
+
packages = [{include = "psaiops"}]
|
|
9
|
+
|
|
10
|
+
[tool.poetry.dependencies]
|
|
11
|
+
python = ">=3.10, <3.14"
|
|
12
|
+
deformers = ">=0.0"
|
|
13
|
+
gradio = ">=5.0"
|
|
14
|
+
requests = ">=2.0"
|
|
15
|
+
|
|
16
|
+
[tool.poetry.group.dev.dependencies]
|
|
17
|
+
pytest = "*"
|
|
18
|
+
|
|
19
|
+
[build-system]
|
|
20
|
+
requires = ["poetry-core"]
|
|
21
|
+
build-backend = "poetry.core.masonry.api"
|