ctgadget commited on
Commit
93804be
·
1 Parent(s): e6fdc91

updates to main app and requirement.txts to deploy to HF Spaces

Browse files
Files changed (2) hide show
  1. app.py +328 -120
  2. requirements.txt +9 -1
app.py CHANGED
@@ -1,147 +1,355 @@
1
- import io
2
- import random
3
- from typing import List, Tuple
 
 
 
4
 
5
- import aiohttp
6
  import panel as pn
7
- from PIL import Image
8
- from transformers import CLIPModel, CLIPProcessor
9
 
10
- pn.extension(design="bootstrap", sizing_mode="stretch_width")
 
 
 
 
 
 
 
 
 
 
 
11
 
12
- ICON_URLS = {
13
- "brand-github": "https://github.com/holoviz/panel",
14
- "brand-twitter": "https://twitter.com/Panel_Org",
15
- "brand-linkedin": "https://www.linkedin.com/company/panel-org",
16
- "message-circle": "https://discourse.holoviz.org/",
17
- "brand-discord": "https://discord.gg/AXRHnJU6sP",
18
- }
19
 
 
20
 
21
- async def random_url(_):
22
- pet = random.choice(["cat", "dog"])
23
- api_url = f"https://api.the{pet}api.com/v1/images/search"
24
- async with aiohttp.ClientSession() as session:
25
- async with session.get(api_url) as resp:
26
- return (await resp.json())[0]["url"]
27
 
 
28
 
29
- @pn.cache
30
- def load_processor_model(
31
- processor_name: str, model_name: str
32
- ) -> Tuple[CLIPProcessor, CLIPModel]:
33
- processor = CLIPProcessor.from_pretrained(processor_name)
34
- model = CLIPModel.from_pretrained(model_name)
35
- return processor, model
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
 
 
37
 
38
- async def open_image_url(image_url: str) -> Image:
39
- async with aiohttp.ClientSession() as session:
40
- async with session.get(image_url) as resp:
41
- return Image.open(io.BytesIO(await resp.read()))
42
 
 
 
43
 
44
- def get_similarity_scores(class_items: List[str], image: Image) -> List[float]:
45
- processor, model = load_processor_model(
46
- "openai/clip-vit-base-patch32", "openai/clip-vit-base-patch32"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
  )
48
- inputs = processor(
49
- text=class_items,
50
- images=[image],
51
- return_tensors="pt", # pytorch tensors
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  )
53
- outputs = model(**inputs)
54
- logits_per_image = outputs.logits_per_image
55
- class_likelihoods = logits_per_image.softmax(dim=1).detach().numpy()
56
- return class_likelihoods[0]
57
 
 
 
 
58
 
59
- async def process_inputs(class_names: List[str], image_url: str):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  """
61
- High level function that takes in the user inputs and returns the
62
- classification results as panel objects.
63
  """
64
- try:
65
- main.disabled = True
66
- if not image_url:
67
- yield "##### ⚠️ Provide an image URL"
68
- return
69
-
70
- yield "##### Fetching image and running model..."
71
- try:
72
- pil_img = await open_image_url(image_url)
73
- img = pn.pane.Image(pil_img, height=400, align="center")
74
- except Exception as e:
75
- yield f"##### 😔 Something went wrong, please try a different URL!"
76
- return
77
-
78
- class_items = class_names.split(",")
79
- class_likelihoods = get_similarity_scores(class_items, pil_img)
80
-
81
- # build the results column
82
- results = pn.Column("##### 🎉 Here are the results!", img)
83
-
84
- for class_item, class_likelihood in zip(class_items, class_likelihoods):
85
- row_label = pn.widgets.StaticText(
86
- name=class_item.strip(), value=f"{class_likelihood:.2%}", align="center"
87
- )
88
- row_bar = pn.indicators.Progress(
89
- value=int(class_likelihood * 100),
90
- sizing_mode="stretch_width",
91
- bar_color="secondary",
92
- margin=(0, 10),
93
- design=pn.theme.Material,
94
- )
95
- results.append(pn.Column(row_label, row_bar))
96
- yield results
97
- finally:
98
- main.disabled = False
99
-
100
-
101
- # create widgets
102
- randomize_url = pn.widgets.Button(name="Randomize URL", align="end")
103
-
104
- image_url = pn.widgets.TextInput(
105
- name="Image URL to classify",
106
- value=pn.bind(random_url, randomize_url),
107
  )
108
- class_names = pn.widgets.TextInput(
109
- name="Comma separated class names",
110
- placeholder="Enter possible class names, e.g. cat, dog",
111
- value="cat, dog, parrot",
 
 
 
 
 
 
 
112
  )
113
 
114
- input_widgets = pn.Column(
115
- "##### 😊 Click randomize or paste a URL to start classifying!",
116
- pn.Row(image_url, randomize_url),
117
- class_names,
 
118
  )
119
 
120
- # add interactivity
121
- interactive_result = pn.panel(
122
- pn.bind(process_inputs, image_url=image_url, class_names=class_names),
123
- height=600,
124
  )
125
 
126
- # add footer
127
- footer_row = pn.Row(pn.Spacer(), align="center")
128
- for icon, url in ICON_URLS.items():
129
- href_button = pn.widgets.Button(icon=icon, width=35, height=35)
130
- href_button.js_on_click(code=f"window.open('{url}')")
131
- footer_row.append(href_button)
132
- footer_row.append(pn.Spacer())
133
-
134
- # create dashboard
135
- main = pn.WidgetBox(
136
- input_widgets,
137
- interactive_result,
138
- footer_row,
139
  )
140
 
141
- title = "Panel Demo - Image Classification"
142
- pn.template.BootstrapTemplate(
143
- title=title,
144
- main=main,
145
- main_max_width="min(50%, 698px)",
146
- header_background="#F08080",
147
- ).servable(title=title)
 
1
+ import os
2
+ import re
3
+ import sys
4
+ import logging
5
+ import nest_asyncio
6
+ #import time
7
 
 
8
  import panel as pn
9
+ import tiktoken
10
+ import chromadb
11
 
12
+ from llama_index.core import (
13
+ Settings,
14
+ VectorStoreIndex,
15
+ PromptTemplate,
16
+ PromptHelper,
17
+ StorageContext
18
+ )
19
+ from llama_index.core.text_splitter import SentenceSplitter
20
+ from llama_index.llms.openai import OpenAI
21
+ from llama_index.embeddings.huggingface import HuggingFaceEmbedding
22
+ from llama_index.readers.web import SimpleWebPageReader
23
+ from llama_index.vector_stores.chroma import ChromaVectorStore
24
 
25
+ nest_asyncio.apply()
 
 
 
 
 
 
26
 
27
+ FORMAT = "%(asctime)s | %(levelname)s | %(name)s | %(message)s"
28
 
29
+ @pn.cache
30
+ def get_logger(name, format_=FORMAT, level=logging.INFO):
31
+ logger = logging.getLogger(name)
 
 
 
32
 
33
+ logger.handlers.clear()
34
 
35
+ handler = logging.StreamHandler()
36
+ handler.setStream(sys.stdout)
37
+ formatter = logging.Formatter(format_)
38
+ handler.setFormatter(formatter)
39
+ logger.addHandler(handler)
40
+ logger.propagate = False
41
+
42
+ logger.setLevel(level)
43
+ logger.info("Logger successfully configured")
44
+ return logger
45
+
46
+ ####################
47
+ # Global Constants #
48
+ ####################
49
+
50
+ pn.extension("codeeditor", sizing_mode="stretch_width")
51
+
52
+ TTL = 1800 # 30 minutes
53
+ ACCENT = "#2EB872"
54
+ THEME = pn.config.theme
55
+
56
+ CHAT_GPT_LOGO = "https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/ChatGPT_logo.svg/512px-ChatGPT_logo.svg.png"
57
+ CHAT_GPT_URL = "https://chat.openai.com/"
58
+ LLAMA_INDEX_LOGO = "https://asset.brandfetch.io/id6a4s3gXI/idncpUsO_z.jpeg"
59
+ LLAMA_INDEX_URL = "https://www.llamaindex.ai/"
60
+
61
+ LLM_VERSION = "gpt-3.5-turbo-1106"
62
+
63
+ pn.chat.ChatMessage.default_avatars.update(
64
+ {
65
+ "assistant": CHAT_GPT_LOGO,
66
+ "user": "🦙",
67
+ }
68
+ )
69
+ pn.chat.ChatMessage.show_reaction_icons = False
70
+
71
+ EXPLANATION = f"""
72
+ ## ScaleUp - (Level up your Python abilities)
73
+ ---
74
+
75
+ **ScaleUp** is a powerful Python coding assistant app that leverages `OpenAI` and `LlamaIndex` to provide an interactive,
76
+ AI-powered learning experience.
77
+
78
+ It acts as a virtual mentor, offering expert guidance, contextually relevant responses, and an integrated code editor for writing and testing Python code.
79
+
80
+ ### Key Features:
81
+
82
+ - **Expert Python Guidance**: Get insightful and accurate answers to your Python queries.
83
+ - **Interactive Code Editor**: Write and test your code, with suggestions and code snippets from the AI.
84
+ - **Context-Aware Responses**: Responses are tailored based on your provided information and a comprehensive knowledge base.
85
+ - **Streaming Responses**: Receive real-time, up-to-date responses as the AI generates them.
86
+
87
+ ## OpenAI GPT
88
+ ---
89
+ We are using the OpenAI `{LLM_VERSION}` to power the coding assistant.
90
 
91
+ ## Getting Started
92
+ ---
93
 
94
+ Ask your Python-related questions, share your code snippets, or request guidance on specific topics.
 
 
 
95
 
96
+ The AI will respond with detailed explanations, code examples, and insightful suggestions to help you learn and improve your Python skills.
97
+ """
98
 
99
+ SYSTEM_PROMPT = (
100
+ "You are an expert Python developer with years of experience writing Python code and teaching Python to other programmers. "
101
+ "You have vast experience mentoring people who are learning Python. "
102
+ "I want you to be my mentor while I learn Python myself. "
103
+ "Your goal is to provide insightful, accurate, and concise answers to questions in this domain. "
104
+ "When generating code, please explicitly state the sources you reference.\n\n"
105
+ "Here is some context related to the query:\n"
106
+ "-----------------------------------------\n"
107
+ "{context_str}\n"
108
+ "-----------------------------------------\n"
109
+ "Considering the above information, please respond to the following inquiry with detailed references to applicable principles, "
110
+ "libraries, design patterns, or debugging methodology where appropriate:\n\n"
111
+ "Question: {query_str}\n\n"
112
+ "Answer succinctly, and ensure your response is understandable to someone with extreme enthusiasm to learn Python programming."
113
+ )
114
+
115
+ # URL's for context with RAG Based Data
116
+ URLS = [
117
+ "https://thewhitetulip.gitbook.io/py",
118
+ "https://docs.python.org/3/tutorial/",
119
+ "https://awesomepython.org/",
120
+ "https://awesome-python.com/",
121
+ ]
122
+
123
+ ##########################################
124
+ # Data Processing and handling functions #
125
+ ##########################################
126
+
127
+ USER_CONTENT_FORMAT = """
128
+ Request:
129
+ {content}
130
+ Code:
131
+ ```python
132
+ {code}
133
+ ```
134
+ """.strip()
135
+
136
+ DEFAULT_CODE_EXAMPLE = """
137
+ print("Hello World")
138
+ """.strip()
139
+
140
+ # Sample Python programming questions
141
+ EXAMPLE_QUESTIONS = f"""
142
+ ## Python Programming Questions
143
+
144
+ ### Basic
145
+
146
+ - Write a Python function to find the maximum of three numbers.
147
+ - Write a Python program to reverse a string.
148
+ - Write a Python program to check if a given number is prime or not.
149
+ - Write a Python program to find the factorial of a number.
150
+ - Write a Python program to check if a string is a palindrome or not.
151
+ - Write a Python program to find the largest number in a list.
152
+ - Write a Python program to find the sum of all numbers in a list.
153
+ - Write a Python program to find the second largest number in a list.
154
+ - Write a Python program to remove duplicates from a list.
155
+ - Write a Python program to implement a simple calculator.
156
+ - Write a Python program to check if a string is a palindrome.
157
+ - Write a Python program to find the Fibonacci sequence up to a given number.
158
+ - Write a Python program to Solve the Fizbuzz Algorithm in the most simple way you can think of ...
159
+
160
+ ### Advanced
161
+
162
+ - Write a Python program to sort a list of dictionaries by a specific value.
163
+ - Write a Python program to implement a binary search algorithm.
164
+ - Write a Python program to implement a merge sort algorithm.
165
+ - Write a Python program to implement a linked list data structure.
166
+ - Write a Python program to implement a binary tree data structure.
167
+ - Implement an LRU (Least Recently Used) Cache.
168
+ - Write a function to check if a binary tree is balanced.
169
+ - Implement a stack using two queues.
170
+ - Write a function to calculate the factorial of a number recursively.
171
+ - Implement a depth-first search (DFS) algorithm to traverse a graph.
172
+
173
+ """
174
+
175
+ def _powered_by():
176
+ """Returns a component describing the frameworks powering the chat ui."""
177
+ params = {"height": 40, "sizing_mode": "fixed", "margin": (0, 10)}
178
+ return pn.Column(
179
+ pn.pane.Markdown("### AI Powered By", margin=(10, 5, 10, 0)),
180
+ pn.Row(
181
+ pn.pane.Image(LLAMA_INDEX_LOGO, link_url=LLAMA_INDEX_URL, **params),
182
+ pn.pane.Image(CHAT_GPT_LOGO, link_url=CHAT_GPT_URL, **params),
183
+ align="center",
184
+ ),
185
  )
186
+
187
+ llm = OpenAI(temperature=0.1, model=LLM_VERSION, max_tokens=512)
188
+ embed_model = HuggingFaceEmbedding(model_name="BAAI/bge-small-en-v1.5")
189
+ text_splitter = SentenceSplitter(chunk_size=1024, chunk_overlap=20)
190
+
191
+ prompt_helper = PromptHelper(
192
+ context_window=4096,
193
+ num_output=256,
194
+ chunk_overlap_ratio=0.1,
195
+ chunk_size_limit=None,
196
+ )
197
+
198
+ # Settings configuration
199
+ Settings.llm = llm
200
+ Settings.embed_model = embed_model
201
+ Settings.tokenizer = tiktoken.encoding_for_model(LLM_VERSION).encode
202
+ Settings.text_splitter = text_splitter
203
+ Settings.prompt_helper = prompt_helper
204
+
205
+ def load_data(data=URLS):
206
+ """
207
+ Initialize the Index
208
+ """
209
+ reader = SimpleWebPageReader(html_to_text=True)
210
+ documents = reader.load_data(data)
211
+
212
+ logging.info("index creating with `%d` documents", len(documents))
213
+ chroma_client = chromadb.EphemeralClient()
214
+ chroma_collection = chroma_client.get_or_create_collection("python-data")
215
+ vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
216
+ storage_context = StorageContext.from_defaults(vector_store=vector_store)
217
+ index = VectorStoreIndex.from_documents(documents, storage_context=storage_context, embed_model=embed_model)
218
+
219
+ return index
220
+
221
+
222
+ def initialize_query_engine(index):
223
+ """
224
+ Initialize Query Engine
225
+ """
226
+ # Custom Prompt Template
227
+ template = SYSTEM_PROMPT
228
+ qa_template = PromptTemplate(template)
229
+
230
+ # build query engine with custom template
231
+ query_engine = index.as_query_engine(text_qa_template=qa_template, similarity_top_k=3)
232
+
233
+ return query_engine
234
+
235
+
236
+ def build_chat_engine(index):
237
+ """
238
+ Initialize Chat Engine
239
+ """
240
+ # Custom Prompt Template
241
+ template = SYSTEM_PROMPT
242
+ qa_template = PromptTemplate(template)
243
+
244
+ chat_engine = index.as_chat_engine(
245
+ chat_mode="context",
246
+ text_qa_template=qa_template,
247
+ verbose=True,
248
+ streaming=True
249
  )
250
+ return chat_engine
 
 
 
251
 
252
+ ############
253
+ # Main App #
254
+ ############
255
 
256
+ logger = get_logger(name="code_asst_example")
257
+
258
+ index = load_data()
259
+
260
+ # Custom Prompt Template
261
+ template = SYSTEM_PROMPT
262
+ qa_template = PromptTemplate(template)
263
+
264
+ chat_engine = index.as_chat_engine(
265
+ chat_mode="context",
266
+ text_qa_template=qa_template,
267
+ verbose = True,
268
+ streaming=True
269
+ )
270
+
271
+ # Getting the API Key
272
+ os.getenv('OPENAI_API_KEY')
273
+
274
+ async def generate_response(
275
+ contents: str,
276
+ user: str,
277
+ instance: pn.chat.ChatInterface
278
+ ):
279
  """
280
+ Docstring placeholder
 
281
  """
282
+ response = await chat_engine.astream_chat(contents)
283
+ text = ""
284
+ async for token in response.async_response_gen():
285
+ text += token
286
+ yield text
287
+
288
+ # extract code from LLM response
289
+ llm_code = re.findall(r"```python\n(.*)\n```", text, re.DOTALL)[0]
290
+ code_editor.value = llm_code
291
+
292
+
293
+ #######################
294
+ # Panel UI Components #
295
+ #######################
296
+
297
+ chat_interface = pn.chat.ChatInterface(
298
+ callback=generate_response,
299
+ show_send=True,
300
+ show_rerun=False,
301
+ show_undo=True,
302
+ show_clear=True,
303
+ show_button_name=True,
304
+ sizing_mode="stretch_both",
305
+ callback_exception="verbose"
306
+ )
307
+
308
+ chat_interface.send(
309
+ SYSTEM_PROMPT,
310
+ user="System",
311
+ respond=False
 
 
 
 
 
 
 
 
 
 
 
 
 
312
  )
313
+
314
+ code_editor = pn.widgets.CodeEditor(
315
+ value=DEFAULT_CODE_EXAMPLE,
316
+ language="python",
317
+ sizing_mode="stretch_both",
318
+ )
319
+
320
+ # Create a layout for the widgets
321
+ question_layout = pn.Column(
322
+ EXAMPLE_QUESTIONS,
323
+ sizing_mode="stretch_width"
324
  )
325
 
326
+ # lay them out in tabs
327
+ tabs_layout = pn.Tabs(
328
+ ("Code", code_editor),
329
+ ("Example Questions", question_layout),
330
+ sizing_mode = "stretch_both",
331
  )
332
 
333
+ component = pn.Row(
334
+ chat_interface,
335
+ tabs_layout,
336
+ sizing_mode="stretch_both"
337
  )
338
 
339
+ # Serve UI Template
340
+ template = pn.template.FastListTemplate(
341
+ title="ScaleUp Code Assistant 🐍",
342
+ sidebar=[
343
+ EXPLANATION,
344
+ _powered_by(),
345
+ ],
346
+ main=[component],
347
+ main_layout=None,
348
+ accent=ACCENT,
 
 
 
349
  )
350
 
351
+ template.servable()
352
+
353
+ ##################
354
+ # End of the App #
355
+ ##################
 
 
requirements.txt CHANGED
@@ -3,4 +3,12 @@ jupyter
3
  transformers
4
  numpy
5
  torch
6
- aiohttp
 
 
 
 
 
 
 
 
 
3
  transformers
4
  numpy
5
  torch
6
+ aiohttp
7
+ sentence-transformers
8
+ tiktoken
9
+ chromadb
10
+ llama-index
11
+ llama-index-llms-openai
12
+ llama-index-readers-web
13
+ llama-index-embeddings-instructor
14
+ llama-index-vector-stores-chroma