psaiops 0.0.1__py3-none-any.whl → 0.0.3__py3-none-any.whl
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/combine/__init__.py +0 -0
- psaiops/compose/contrast/__init__.py +0 -0
- psaiops/edit/__init__.py +0 -0
- psaiops/elements/data.py +11 -5
- psaiops/score/attention/__init__.py +0 -0
- psaiops/score/attention/app.py +142 -0
- psaiops/score/attention/lib.py +191 -0
- {psaiops-0.0.1.dist-info → psaiops-0.0.3.dist-info}/METADATA +1 -1
- psaiops-0.0.3.dist-info/RECORD +15 -0
- {psaiops-0.0.1.dist-info → psaiops-0.0.3.dist-info}/WHEEL +1 -1
- psaiops/elements/dropdown.py +0 -19
- psaiops/steer/app.py +0 -202
- psaiops-0.0.1.dist-info/RECORD +0 -11
|
File without changes
|
|
File without changes
|
psaiops/edit/__init__.py
ADDED
|
File without changes
|
psaiops/elements/data.py
CHANGED
|
@@ -8,10 +8,16 @@ HF_URL = 'https://huggingface.co/api/quicksearch?q={target}&type={label}&limit={
|
|
|
8
8
|
|
|
9
9
|
def query_huggingface(target: str, label: str='model', limit: int=16, endpoint: str=HF_URL) -> list:
|
|
10
10
|
__results = []
|
|
11
|
-
#
|
|
12
|
-
|
|
13
|
-
#
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
# make sure the label has no trailing "s"
|
|
12
|
+
__label = label.rstrip('s').strip(' ')
|
|
13
|
+
# the HTTP request or the parsing may fail
|
|
14
|
+
try:
|
|
15
|
+
# query HF
|
|
16
|
+
__response = requests.get(endpoint.format(target=target, label=__label, limit=limit))
|
|
17
|
+
# filter by type ('models' / 'datasets' / 'spaces')
|
|
18
|
+
__results = [__d.get('id', '') for __d in __response.json().get(f'{__label}s', [])]
|
|
19
|
+
# ignore all the errors
|
|
20
|
+
except:
|
|
21
|
+
__results = []
|
|
16
22
|
# list of strings
|
|
17
23
|
return __results
|
|
File without changes
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import gradio
|
|
2
|
+
|
|
3
|
+
import psaiops.score.attention.lib
|
|
4
|
+
|
|
5
|
+
# META #########################################################################
|
|
6
|
+
|
|
7
|
+
TITLE = '''Attention Scoring'''
|
|
8
|
+
INTRO = '''Score each token according to the weights of the attention layers.\nThe model is fixed to "openai/gpt-oss-20b" for now.'''
|
|
9
|
+
STYLE = ''''''
|
|
10
|
+
|
|
11
|
+
MODEL = 'openai/gpt-oss-20b'
|
|
12
|
+
|
|
13
|
+
# INTRO ########################################################################
|
|
14
|
+
|
|
15
|
+
def create_intro_block(intro: str) -> dict:
|
|
16
|
+
__intro = gradio.Markdown(intro)
|
|
17
|
+
return {'intro_block': __intro}
|
|
18
|
+
|
|
19
|
+
# MODEL ########################################################################
|
|
20
|
+
|
|
21
|
+
def create_model_block() -> dict:
|
|
22
|
+
__model_dd = gradio.Dropdown(label='Model', value='openai/gpt-oss-20b', choices=['openai/gpt-oss-20b'], allow_custom_value=False, multiselect=False, interactive=True) # 'openai/gpt-oss-120b'
|
|
23
|
+
__layer_sl = gradio.Slider(label='Layer Depth', value=12, minimum=-1, maximum=23, step=1, interactive=True) # info='-1 to average on all layers'
|
|
24
|
+
__head_sl = gradio.Slider(label='Attention Head', value=-1, minimum=-1, maximum=63, step=1, interactive=True) # info='-1 to average on all heads'
|
|
25
|
+
__model_dd.change(fn=update_layer_range, inputs=[__layer_sl, __model_dd], outputs=__layer_sl, queue=False, show_progress='hidden')
|
|
26
|
+
return {
|
|
27
|
+
'model_block': __model_dd,
|
|
28
|
+
'layer_block': __layer_sl,
|
|
29
|
+
'head_block': __head_sl}
|
|
30
|
+
|
|
31
|
+
# SAMPLING #####################################################################
|
|
32
|
+
|
|
33
|
+
def create_sampling_block() -> dict:
|
|
34
|
+
__tokens = gradio.Slider(label='Tokens', value=32, minimum=0, maximum=128, step=1, interactive=True)
|
|
35
|
+
__topk = gradio.Slider(label='Top K', value=4, minimum=1, maximum=8, step=1, interactive=True)
|
|
36
|
+
__topp = gradio.Slider(label='Top P', value=0.8, minimum=0.0, maximum=1.0, step=0.1, interactive=True)
|
|
37
|
+
return {
|
|
38
|
+
'tokens_block': __tokens,
|
|
39
|
+
'topk_block': __topk,
|
|
40
|
+
'topp_block': __topp}
|
|
41
|
+
|
|
42
|
+
# DISPLAY ######################################################################
|
|
43
|
+
|
|
44
|
+
def create_display_block() -> dict:
|
|
45
|
+
__display = gradio.Radio(label='Display', value='Tokens', choices=['Tokens', 'Indexes'], interactive=True)
|
|
46
|
+
return {'display_block': __display}
|
|
47
|
+
|
|
48
|
+
# INPUTS #######################################################################
|
|
49
|
+
|
|
50
|
+
def create_inputs_block() -> dict:
|
|
51
|
+
__input = gradio.Textbox(label='Prompt', value='', placeholder='A string of tokens to score.', lines=4, show_copy_button=True, interactive=True)
|
|
52
|
+
return {'input_block': __input}
|
|
53
|
+
|
|
54
|
+
# OUTPUTS ######################################################################
|
|
55
|
+
|
|
56
|
+
def create_outputs_block() -> dict:
|
|
57
|
+
__output = gradio.HighlightedText(label='Scores', value='', interactive=False, show_legend=False, show_inline_category=False, combine_adjacent=True)
|
|
58
|
+
return {'output_block': __output}
|
|
59
|
+
|
|
60
|
+
# ACTIONS ######################################################################
|
|
61
|
+
|
|
62
|
+
def create_actions_block() -> dict:
|
|
63
|
+
__process = gradio.Button('Process', variant='primary', size='lg', interactive=True)
|
|
64
|
+
__position = gradio.Slider(label='Position', value=-1, minimum=-1, maximum=128, step=1, interactive=True) # info='-1 to average on all tokens'
|
|
65
|
+
return {
|
|
66
|
+
'process_block': __process,
|
|
67
|
+
'position_block': __position}
|
|
68
|
+
|
|
69
|
+
# STATE ########################################################################
|
|
70
|
+
|
|
71
|
+
def create_state() -> dict:
|
|
72
|
+
return {'attention_state': gradio.State(None), 'token_state': gradio.State(None)}
|
|
73
|
+
|
|
74
|
+
# LAYOUT #######################################################################
|
|
75
|
+
|
|
76
|
+
def create_layout(intro: str=INTRO) -> dict:
|
|
77
|
+
__fields = {}
|
|
78
|
+
__fields.update(create_intro_block(intro=intro))
|
|
79
|
+
with gradio.Tabs():
|
|
80
|
+
with gradio.Tab('Score Tokens') as __main_tab:
|
|
81
|
+
__fields.update({'main_tab': __main_tab})
|
|
82
|
+
with gradio.Row():
|
|
83
|
+
with gradio.Column(scale=1):
|
|
84
|
+
__fields.update(create_inputs_block())
|
|
85
|
+
with gradio.Column(scale=1):
|
|
86
|
+
__fields.update(create_outputs_block())
|
|
87
|
+
with gradio.Row():
|
|
88
|
+
__fields.update(create_actions_block())
|
|
89
|
+
with gradio.Tab('Settings') as __settings_tab:
|
|
90
|
+
__fields.update({'settings_tab': __settings_tab})
|
|
91
|
+
with gradio.Column(scale=1):
|
|
92
|
+
with gradio.Row():
|
|
93
|
+
__fields.update(create_model_block())
|
|
94
|
+
with gradio.Row():
|
|
95
|
+
__fields.update(create_sampling_block())
|
|
96
|
+
with gradio.Row():
|
|
97
|
+
__fields.update(create_display_block())
|
|
98
|
+
return __fields
|
|
99
|
+
|
|
100
|
+
# EVENTS #######################################################################
|
|
101
|
+
|
|
102
|
+
def update_layer_range(value: int, model: str) -> dict:
|
|
103
|
+
return gradio.update(maximum=35, value=min(35, int(value))) if '120b' in model else gradio.update(maximum=23, value=min(23, int(value)))
|
|
104
|
+
|
|
105
|
+
def update_position_range(value: int, dimension: int) -> dict:
|
|
106
|
+
return gradio.update(maximum=dimension - 1, value=min(dimension - 1, value))
|
|
107
|
+
|
|
108
|
+
def update_output_value(
|
|
109
|
+
attention_data: torch.Tensor=None,
|
|
110
|
+
token_data: torch.Tensor=None,) -> torch.Tensor:
|
|
111
|
+
return
|
|
112
|
+
|
|
113
|
+
# APP ##########################################################################
|
|
114
|
+
|
|
115
|
+
def create_app(title: str=TITLE, intro: str=INTRO, style: str=STYLE, model: str=MODEL) -> gradio.Blocks:
|
|
116
|
+
__fields = {}
|
|
117
|
+
with gradio.Blocks(theme=gradio.themes.Soft(), title=title, css=style) as __app:
|
|
118
|
+
# init
|
|
119
|
+
__device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
|
120
|
+
__model = psaiops.score.attention.lib.get_model(name=model, device=__device)
|
|
121
|
+
__tokenizer = psaiops.score.attention.lib.get_tokenizer(name=model, device=__device)
|
|
122
|
+
# create the UI
|
|
123
|
+
__fields.update(create_layout(intro=intro))
|
|
124
|
+
# init the state
|
|
125
|
+
__fields.update(create_state())
|
|
126
|
+
# fetch the relevant fields
|
|
127
|
+
__button = __fields['process_block']
|
|
128
|
+
# wire the input fields
|
|
129
|
+
__button.click(
|
|
130
|
+
fn=psaiops.score.attention.lib.score_tokens,
|
|
131
|
+
inputs=[__model, __tokenizer] + [__fields[__k] for __k in ['input_block', 'tokens_block', 'topk_block', 'topp_block', 'position_block', 'layer_block', 'head_block']] + [__device],
|
|
132
|
+
outputs=__fields['output_block'],
|
|
133
|
+
queue=False,
|
|
134
|
+
show_progress='full')
|
|
135
|
+
# gradio application
|
|
136
|
+
return __app
|
|
137
|
+
|
|
138
|
+
# MAIN #########################################################################
|
|
139
|
+
|
|
140
|
+
if __name__ == '__main__':
|
|
141
|
+
__app = create_app()
|
|
142
|
+
__app.launch(share=True, debug=True)
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
import transformers
|
|
5
|
+
|
|
6
|
+
import deformers.models.openai.gptoss
|
|
7
|
+
|
|
8
|
+
# LOAD #########################################################################
|
|
9
|
+
|
|
10
|
+
@functools.lru_cache(maxsize=4)
|
|
11
|
+
def get_tokenizer(name: str, device: str='cpu'):
|
|
12
|
+
return transformers.AutoTokenizer.from_pretrained(
|
|
13
|
+
name,
|
|
14
|
+
use_fast=True,
|
|
15
|
+
dtype='auto',
|
|
16
|
+
device_map=device)
|
|
17
|
+
|
|
18
|
+
@functools.lru_cache(maxsize=2)
|
|
19
|
+
def get_model(name: str, device: str='cpu'):
|
|
20
|
+
__model = deformers.models.openai.gptoss.GptOssForCausalInference.from_pretrained(
|
|
21
|
+
name,
|
|
22
|
+
dtype='auto',
|
|
23
|
+
device_map=device)
|
|
24
|
+
# toggle the inference mode (not training)
|
|
25
|
+
__model.eval()
|
|
26
|
+
# transformers model
|
|
27
|
+
return __model
|
|
28
|
+
|
|
29
|
+
# PREPROCESS #####################################################################
|
|
30
|
+
|
|
31
|
+
def preprocess_token_ids(
|
|
32
|
+
tokenizer_obj: object,
|
|
33
|
+
prompt_str: str,
|
|
34
|
+
device_str: str='cpu'
|
|
35
|
+
) -> dict:
|
|
36
|
+
# tokenize
|
|
37
|
+
__inputs = tokenizer_obj(prompt_str, return_tensors='pt')
|
|
38
|
+
# move to the main device
|
|
39
|
+
return {__k: __v.to(device_str) for __k, __v in __inputs.items()}
|
|
40
|
+
|
|
41
|
+
# GENERATE #######################################################################
|
|
42
|
+
|
|
43
|
+
def generate_token_ids(
|
|
44
|
+
model_obj: object,
|
|
45
|
+
input_args: dict,
|
|
46
|
+
token_num: int,
|
|
47
|
+
topk_num: int = 4,
|
|
48
|
+
topp_num: float = 0.9,
|
|
49
|
+
) -> torch.Tensor:
|
|
50
|
+
# generate completion
|
|
51
|
+
with torch.no_grad():
|
|
52
|
+
__outputs = model_obj.generate(
|
|
53
|
+
**input_args,
|
|
54
|
+
max_new_tokens=token_num,
|
|
55
|
+
do_sample=(0.0 < topp_num < 1.0) or (topk_num > 0),
|
|
56
|
+
top_k=topk_num if (topk_num > 0) else None,
|
|
57
|
+
top_p=topp_num if (0.0 < topp_num < 1.0) else None,
|
|
58
|
+
return_dict_in_generate=True,
|
|
59
|
+
output_hidden_states=False,
|
|
60
|
+
output_attentions=False,
|
|
61
|
+
output_scores=False,
|
|
62
|
+
early_stopping=True,
|
|
63
|
+
use_cache=True)
|
|
64
|
+
# full sequence
|
|
65
|
+
return __outputs.sequences # (1, T)
|
|
66
|
+
|
|
67
|
+
# COMPUTE ########################################################################
|
|
68
|
+
|
|
69
|
+
def compute_attention_weights(
|
|
70
|
+
model_obj: object,
|
|
71
|
+
token_obj: torch.Tensor,
|
|
72
|
+
) -> torch.Tensor:
|
|
73
|
+
# process the full sequence
|
|
74
|
+
with torch.no_grad():
|
|
75
|
+
__outputs = model_obj(
|
|
76
|
+
input_ids=token_obj,
|
|
77
|
+
output_attentions=True,
|
|
78
|
+
return_dict=True)
|
|
79
|
+
# parse the outputs
|
|
80
|
+
return torch.stack(__outputs.attentions, dim=0)
|
|
81
|
+
|
|
82
|
+
# REDUCE #######################################################################
|
|
83
|
+
|
|
84
|
+
def reduce_attention_weights(
|
|
85
|
+
attention_data: torch.Tensor,
|
|
86
|
+
token_idx: int, # -1 => avg over all tokens
|
|
87
|
+
layer_idx: int, # -1 => avg over layers
|
|
88
|
+
head_idx: int, # -1 => avg over heads
|
|
89
|
+
input_dim: int,
|
|
90
|
+
) -> torch.Tensor:
|
|
91
|
+
# parse
|
|
92
|
+
__layer_dim, __batch_dim, __head_dim, __output_dim, __output_dim = tuple(attention_data.shape) # L, B, H, T, T
|
|
93
|
+
__layer_idx = min(layer_idx, __layer_dim)
|
|
94
|
+
__head_idx = min(head_idx, __head_dim)
|
|
95
|
+
__token_idx = min(token_idx, __output_dim - input_dim - 1) # T = I + O
|
|
96
|
+
# select the relevant data along each axis
|
|
97
|
+
__layer_slice = slice(None) if (__layer_idx < 0) else slice(__layer_idx, __layer_idx + 1)
|
|
98
|
+
__sample_slice = slice(None)
|
|
99
|
+
__head_slice = slice(None) if (__head_idx < 0) else slice(__head_idx, __head_idx + 1)
|
|
100
|
+
__token_slice = slice(input_dim, __output_dim) if (__token_idx < 0) else slice(input_dim + __token_idx, input_dim + __token_idx + 1)
|
|
101
|
+
# filter the data
|
|
102
|
+
__data = attention_data[__layer_slice, __sample_slice, __head_slice, __token_slice, slice(None)]
|
|
103
|
+
# reduce all the axes but the last
|
|
104
|
+
return __data.mean(dim=tuple(range(len(__data.shape) - 1)))
|
|
105
|
+
|
|
106
|
+
# FORMAT #########################################################################
|
|
107
|
+
|
|
108
|
+
def postprocess_attention_scores(
|
|
109
|
+
attention_data: torch.Tensor, # (T,)
|
|
110
|
+
input_dim: int,
|
|
111
|
+
token_idx: int,
|
|
112
|
+
) -> list:
|
|
113
|
+
__output_dim = int(attention_data.shape[-1])
|
|
114
|
+
# isolate the scores of the input prompt
|
|
115
|
+
__input_slice = slice(0, input_dim)
|
|
116
|
+
# mask the token that were used to compute the scores
|
|
117
|
+
__token_idx = min(token_idx, __output_dim - input_dim - 1) # T = I + O
|
|
118
|
+
__output_range = list(range(__output_dim - input_dim)) if (__token_idx < 0) else [__token_idx]
|
|
119
|
+
__output_mask = torch.BoolTensor([__i in __output_range for __i in range(__output_dim - input_dim)])
|
|
120
|
+
# normalize the scores
|
|
121
|
+
__input_scores = attention_data[__input_slice] / (attention_data[__input_slice].sum() + 1e-5)
|
|
122
|
+
# round to obtain integer labels from 0 to 100
|
|
123
|
+
__input_scores = torch.round(100.0 * __input_scores, decimals=0).type(torch.int32)
|
|
124
|
+
# the generated tokens are not scored
|
|
125
|
+
__output_scores = torch.where(__output_mask, -1, 0).type(torch.int32)
|
|
126
|
+
# native list of integers
|
|
127
|
+
return __input_scores.tolist() + __output_scores.tolist() # (I,) + (O,) = (T,)
|
|
128
|
+
|
|
129
|
+
# POSTPROCESS ####################################################################
|
|
130
|
+
|
|
131
|
+
def postprocess_token_ids(
|
|
132
|
+
tokenizer_obj: object,
|
|
133
|
+
token_obj: torch.Tensor,
|
|
134
|
+
) -> list:
|
|
135
|
+
# remove the batch axis
|
|
136
|
+
__indices = token_obj.squeeze().tolist()
|
|
137
|
+
# back to token strings
|
|
138
|
+
__tokens = tokenizer_obj.convert_ids_to_tokens(__indices)
|
|
139
|
+
# normalize the tokens
|
|
140
|
+
return [__t.replace('Ġ', ' ') for __t in __tokens]
|
|
141
|
+
|
|
142
|
+
# COMPUTE ########################################################################
|
|
143
|
+
|
|
144
|
+
def score_tokens(
|
|
145
|
+
model_obj: object,
|
|
146
|
+
tokenizer_obj: object,
|
|
147
|
+
prompt_str: str,
|
|
148
|
+
token_num: int=32,
|
|
149
|
+
topk_num: int = 4,
|
|
150
|
+
topp_num: float = 0.9,
|
|
151
|
+
token_idx: int, # -1 => avg over all tokens
|
|
152
|
+
layer_idx: int, # -1 => avg over layers
|
|
153
|
+
head_idx: int, # -1 => avg over heads
|
|
154
|
+
device_str: str='cuda',
|
|
155
|
+
) -> list:
|
|
156
|
+
# dictionary {'input_ids': _, 'attention_mask': _}
|
|
157
|
+
__inputs = preprocess_token_ids(
|
|
158
|
+
tokenizer_obj=tokenizer_obj,
|
|
159
|
+
prompt_str=prompt_str,
|
|
160
|
+
device_str=device_str)
|
|
161
|
+
# parse the inputs
|
|
162
|
+
__input_dim = int(__inputs['input_ids'].shape[-1])
|
|
163
|
+
# tensor (1, T)
|
|
164
|
+
__outputs = generate_token_ids(
|
|
165
|
+
model_obj=model_obj,
|
|
166
|
+
input_args=__inputs,
|
|
167
|
+
token_num=token_num,
|
|
168
|
+
topk_num=topk_num,
|
|
169
|
+
topp_num=topp_num)
|
|
170
|
+
# tensor (L, S, H, T, T)
|
|
171
|
+
__attentions = compute_attention_weights(
|
|
172
|
+
model_obj=model_obj,
|
|
173
|
+
token_obj=__outputs)
|
|
174
|
+
# reduce the layer, sample, head and output token axes => tensor (T,)
|
|
175
|
+
__scores = reduce_attention_weights(
|
|
176
|
+
__attentions,
|
|
177
|
+
token_idx=token_idx,
|
|
178
|
+
layer_idx=layer_idx,
|
|
179
|
+
head_idx=head_idx,
|
|
180
|
+
input_dim=__input_dim)
|
|
181
|
+
# translate the scores into integer labels
|
|
182
|
+
__labels = postprocess_attention_scores(
|
|
183
|
+
__scores,
|
|
184
|
+
input_dim=__input_dim,
|
|
185
|
+
token_idx=token_idx)
|
|
186
|
+
# detokenize the IDs
|
|
187
|
+
__tokens = postprocess_token_ids(
|
|
188
|
+
tokenizer_obj=__tokenizer,
|
|
189
|
+
token_obj=__outputs)
|
|
190
|
+
# match tokens and labels for the HighlightedText field
|
|
191
|
+
return list(zip(__tokens, __labels))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
psaiops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
psaiops/combine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
psaiops/compose/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
+
psaiops/compose/contrast/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
5
|
+
psaiops/edit/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
psaiops/elements/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
psaiops/elements/data.py,sha256=vGYeMN11uP9gs8rV6aSDffE_TeIX5PmdzWGwUpdGE2Y,906
|
|
8
|
+
psaiops/score/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
psaiops/score/attention/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
+
psaiops/score/attention/app.py,sha256=FgTyGrklLfWVQICwtXT7mohBXmSMVyv5iRSgNC64Z-0,6549
|
|
11
|
+
psaiops/score/attention/lib.py,sha256=UQObfalIAenLdg3qZw5l003fenvB5RLeav4G-8H3RHs,6925
|
|
12
|
+
psaiops/steer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
psaiops-0.0.3.dist-info/METADATA,sha256=aof38JNXN2bi0cG31ba1JCLfrB6onSCd3R-econzaL0,1221
|
|
14
|
+
psaiops-0.0.3.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
|
|
15
|
+
psaiops-0.0.3.dist-info/RECORD,,
|
psaiops/elements/dropdown.py
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
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)
|
psaiops/steer/app.py
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
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
|
-
)
|
psaiops-0.0.1.dist-info/RECORD
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
psaiops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
psaiops/compose/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
psaiops/elements/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
|
-
psaiops/elements/data.py,sha256=j6fJZLND0E-YVsG7j-YKINJW8HVwgzihaatgxRI0Uzw,671
|
|
5
|
-
psaiops/elements/dropdown.py,sha256=PCyvq5SJJ1PCuP1Y5JZYyJUML2RToG7WgpkRo9OZYA0,1390
|
|
6
|
-
psaiops/score/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
psaiops/steer/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
psaiops/steer/app.py,sha256=bxmNHCR_iOUeWLEMmpJeC36PpKwHDTAMX1TLGb-t4sA,7626
|
|
9
|
-
psaiops-0.0.1.dist-info/METADATA,sha256=VUqkroJ9Mva14_HAS1Mje80EZECkwpjgujyx5yRVjos,1221
|
|
10
|
-
psaiops-0.0.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
11
|
-
psaiops-0.0.1.dist-info/RECORD,,
|