wxgeorge commited on
Commit
339af39
β€’
1 Parent(s): 1711c03

:tada: initial commit

Browse files
Files changed (5) hide show
  1. README.md +4 -4
  2. app/__main__.py +212 -0
  3. app/klimbr.py +66 -0
  4. logo.svg +3 -0
  5. requirements.txt +1 -0
README.md CHANGED
@@ -1,12 +1,12 @@
1
  ---
2
  title: Klimbr Demo
3
- emoji: πŸ¦€
4
  colorFrom: green
5
  colorTo: red
6
  sdk: gradio
7
- sdk_version: 4.44.0
8
- app_file: app.py
9
  pinned: false
10
  ---
11
 
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: Klimbr Demo
3
+ emoji: πŸ§—πŸΎβ€β™‚οΈ
4
  colorFrom: green
5
  colorTo: red
6
  sdk: gradio
7
+ sdk_version: 4.37.2
8
+ app_file: app/__main__.py
9
  pinned: false
10
  ---
11
 
12
+ A space demoing the [klimbr](https://github.com/av/klmbr) input prompt randomization method.
app/__main__.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from openai import OpenAI
2
+ import gradio as gr
3
+ import os
4
+ import json
5
+ import html
6
+ import random
7
+ import datetime
8
+ from . import klimbr
9
+ klimbrize_string = klimbr.randomize
10
+
11
+ api_key = os.environ.get('FEATHERLESS_API_KEY')
12
+ client = OpenAI(
13
+ base_url="https://api.featherless.ai/v1",
14
+ api_key=api_key
15
+ )
16
+
17
+ klimbr_cache = {}
18
+ def memoized_klimbr(message, percentage, extra):
19
+ key = (message, percentage, extra)
20
+ if key not in klimbr_cache:
21
+ klimbr_cache[key] = klimbrize_string(message, percentage)[0]
22
+
23
+ return klimbr_cache[key]
24
+
25
+ def klimberize_conversation(message, history, percentage):
26
+ # we memoize the klimbrization of strings.
27
+ # this is to work with the gradio chat interface model
28
+ # so that messages are not _re_-randomized at each conversation turn
29
+
30
+ klimbred_history = [
31
+ (memoized_klimbr(human, percentage, index), assistant)
32
+ for index, (human, assistant) in enumerate(history)
33
+ ]
34
+
35
+ klimbred_message = memoized_klimbr(message, percentage, len(history))
36
+
37
+ return (klimbred_message, klimbred_history)
38
+
39
+ def respond(message, history, model, klimbr_percentage):
40
+ history_openai_format = []
41
+
42
+ message, history = klimberize_conversation(message, history, klimbr_percentage)
43
+
44
+ for human, assistant in history:
45
+ history_openai_format.append({"role": "user", "content": human })
46
+ history_openai_format.append({"role": "assistant", "content":assistant})
47
+ history_openai_format.append({"role": "user", "content": message})
48
+
49
+ response = client.chat.completions.create(
50
+ model=model,
51
+ messages= history_openai_format,
52
+ temperature=1.0,
53
+ stream=True,
54
+ max_tokens=2000,
55
+ extra_headers={
56
+ 'HTTP-Referer': 'https://huggingface.co/spaces/featherless-ai/klimbr-demo',
57
+ 'X-Title': "Klimbr demo space"
58
+ }
59
+ )
60
+
61
+ partial_message = ""
62
+ for chunk in response:
63
+ if chunk.choices[0].delta.content is not None:
64
+ content = chunk.choices[0].delta.content
65
+ escaped_content = html.escape(content)
66
+ partial_message += escaped_content
67
+ yield partial_message
68
+
69
+ logo = open('./logo.svg').read()
70
+
71
+ # we chose a few models across the smaller model classes to give a sense of the technique
72
+ MODEL_CHOICES = {
73
+ "llama2-13b-4k": [
74
+ "NousResearch/Nous-Hermes-Llama2-13b",
75
+ ],
76
+ "llama3-8b-8k": [
77
+ "meta-llama/Meta-Llama-3-8B-Instruct",
78
+ "NousResearch/Hermes-2-Theta-Llama-3-8B",
79
+ "aaditya/Llama3-OpenBioLLM-8B",
80
+ "elyza/Llama-3-ELYZA-JP-8B",
81
+ "mlabonne/NeuralDaredevil-8B-abliterated",
82
+ ],
83
+ "llama31-8b-16k": [
84
+ "meta-llama/Meta-Llama-3.1-8B-Instruct",
85
+ "NousResearch/Hermes-3-Llama-3.1-8B",
86
+ "shenzhi-wang/Llama3.1-8B-Chinese-Chat",
87
+ "AXCXEPT/Llama-3.1-8B-EZO-1.1-it",
88
+ "mlabonne/Meta-Llama-3.1-8B-Instruct-abliterated",
89
+ "VAGOsolutions/Llama-3.1-SauerkrautLM-8b-Instruct",
90
+ ],
91
+ "mistral-v02-7b-lc": [
92
+ "HuggingFaceH4/zephyr-7b-beta",
93
+ "mlabonne/NeuralDaredevil-7B",
94
+ "HuggingFaceH4/zephyr-7b-alpha",
95
+ ],
96
+ "mistral-nemo-12b-lc": [
97
+ "mistralai/Mistral-Nemo-Instruct-2407",
98
+ ],
99
+ "rwvk-14b-lc": [
100
+ "m8than/apple-rwkv-1-c-14b",
101
+ ],
102
+ }
103
+
104
+ def build_model_choices():
105
+ all_choices = []
106
+ for model_class_name in MODEL_CHOICES:
107
+ model_class = MODEL_CHOICES[model_class_name]
108
+ all_choices += [ (f"{model_id} ({model_class_name})", model_id) for model_id in model_class ]
109
+
110
+ return all_choices
111
+
112
+ model_choices = build_model_choices()
113
+
114
+ def initial_model(referer=None):
115
+ return "mistralai/Mistral-Nemo-Instruct-2407"
116
+ # let's use a random but different model each day.
117
+ # key=os.environ.get('RANDOM_SEED', 'kcOtfNHA+e')
118
+ # o = random.Random(f"{key}-{datetime.date.today().strftime('%Y-%m-%d')}")
119
+ # return o.choice(model_choices)[1]
120
+
121
+ title_text="Klimbr token input pre-processor demo space"
122
+ klimbr_url="https://github.com/av/klmbr"
123
+ css = """
124
+ .logo-mark { fill: #ffe184; }
125
+
126
+ /* from https://github.com/gradio-app/gradio/issues/4001
127
+ * necessary as putting ChatInterface in gr.Blocks changes behaviour
128
+ */
129
+
130
+ .contain { display: flex; flex-direction: column; }
131
+ .gradio-container { height: 100vh !important; }
132
+ #component-0 { height: 100%; }
133
+ #chatbot { flex-grow: 1; overflow: auto;}
134
+ .lead-text {
135
+ display: flex;
136
+ flex-direction: column;
137
+ align-items: center;
138
+ justify-content: center;
139
+ padding: 20px;
140
+ box-sizing: border-box;
141
+ }
142
+
143
+ .content {
144
+ max-width: 60vh;
145
+ text-align: center;
146
+ font-size: 15pt;
147
+ }
148
+
149
+ .h1 {
150
+ margin-bottom: 20px;
151
+ }
152
+ """
153
+ with gr.Blocks(title_text, css=css) as demo:
154
+ gr.HTML(f"""
155
+ <div class="lead-text">
156
+ <h1 align="center"><a href="{klimbr_url}">Klimbr</a> demo space</h1>
157
+ <div class="content">
158
+ <p>
159
+ Klimbr is a technique to increase entropy in LLM outputs
160
+ by adding entropy to the input prompt prior to inference.
161
+ </p>
162
+ <p>
163
+ For details on the technique see <a href="{klimbr_url}">the klimbr github</a>
164
+ or the source code of this space.
165
+ </p>
166
+ </div>
167
+ """)
168
+
169
+ # hidden_state = gr.State(value=initial_model)
170
+ percentage = gr.Slider(
171
+ minimum=0,
172
+ maximum=1,
173
+ value=0.15,
174
+ label="Percentage of input text to randomize"
175
+ )
176
+
177
+ with gr.Row():
178
+ model_selector = gr.Dropdown(
179
+ label="Select your Model",
180
+ choices=model_choices,
181
+ value=initial_model,
182
+ # value=hidden_state,
183
+ scale=4
184
+ )
185
+ gr.Button(
186
+ value="Visit Model Card ↗️",
187
+ scale=1
188
+ ).click(
189
+ inputs=[model_selector],
190
+ js="(model_selection) => { window.open(`https://huggingface.co/${model_selection}`, '_blank') }",
191
+ fn=None,
192
+ )
193
+
194
+ gr.ChatInterface(
195
+ respond,
196
+ additional_inputs=[model_selector, percentage],
197
+ head=""",
198
+ <script>console.log("Hello from gradio!")</script>
199
+ """,
200
+ concurrency_limit=5
201
+ )
202
+ gr.HTML(f"""
203
+ <p align="center">
204
+ Inference by <a href="https://featherless.ai">{logo}</a>
205
+ </p>
206
+ """)
207
+ def update_initial_model_choice(request: gr.Request):
208
+ return initial_model(request.headers.get('referer'))
209
+
210
+ demo.load(update_initial_model_choice, outputs=model_selector)
211
+
212
+ demo.launch()
app/klimbr.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # from https://github.com/av/klmbr/blob/ca2967123d171fc6d91c329c40e5050a86088446/klmbr/main.py
2
+ import random
3
+
4
+ mods = [
5
+ "capitalize",
6
+ "diacritic",
7
+ 'leetspeak',
8
+ "remove_vowel",
9
+ ]
10
+
11
+ def randomize(text, percentage):
12
+ if not text:
13
+ return "", {} # Return empty string and empty mapping if input is empty
14
+
15
+ if not 0 <= percentage <= 100:
16
+ raise ValueError("Percentage must be between 0 and 100")
17
+
18
+ words = text.split()
19
+ chars = list(text)
20
+ num_chars_to_modify = max(1, int(len(chars) * (percentage / 100)))
21
+ indices_to_modify = random.sample(range(len(chars)), num_chars_to_modify)
22
+ word_mapping = {}
23
+
24
+ for idx in indices_to_modify:
25
+ modification = random.choice(mods)
26
+
27
+ # Find the word that contains the current character
28
+ current_length = 0
29
+ for word_idx, word in enumerate(words):
30
+ if current_length <= idx < current_length + len(word):
31
+ original_word = word
32
+ word_start_idx = current_length
33
+ break
34
+ current_length += len(word) + 1 # +1 for the space
35
+ else:
36
+ # If we're here, we're likely dealing with a space or the last character
37
+ continue
38
+
39
+ if modification == "capitalize":
40
+ chars[idx] = chars[idx].swapcase()
41
+ elif modification == "diacritic":
42
+ if chars[idx].isalpha():
43
+ diacritics = ["Μ€", "́", "Μ‚", "Μƒ", "̈", "Μ„", "Μ†", "Μ‡", "̊", "Μ‹"]
44
+ chars[idx] = chars[idx] + random.choice(diacritics)
45
+ elif modification == "leetspeak":
46
+ leetspeak_map = {
47
+ "a": "4", "e": "3", "i": "1", "o": "0", "s": "5",
48
+ "t": "7", "b": "8", "g": "9", "l": "1",
49
+ }
50
+ chars[idx] = leetspeak_map.get(chars[idx].lower(), chars[idx])
51
+ elif modification == "remove_vowel":
52
+ if chars[idx].lower() in "aeiou":
53
+ chars[idx] = ""
54
+
55
+ modified_word = "".join(
56
+ chars[word_start_idx : word_start_idx + len(original_word)]
57
+ )
58
+
59
+ if modified_word != original_word:
60
+ # Clean up both the modified word and the original word
61
+ cleaned_modified_word = modified_word.rstrip('.,')
62
+ cleaned_original_word = original_word.rstrip('.,')
63
+ word_mapping[cleaned_modified_word] = cleaned_original_word
64
+
65
+ modified_text = "".join(chars)
66
+ return modified_text, word_mapping
logo.svg ADDED
requirements.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ openai