Spaces:
Runtime error
Runtime error
| import re | |
| import os | |
| import panel as pn | |
| from io import StringIO | |
| from panel.io.mime_render import exec_with_return | |
| from llama_index import ( | |
| VectorStoreIndex, | |
| SimpleDirectoryReader, | |
| ServiceContext, | |
| StorageContext, | |
| load_index_from_storage, | |
| ) | |
| from llama_index.chat_engine import ContextChatEngine | |
| from llama_index.embeddings import OpenAIEmbedding | |
| from llama_index.llms import OpenAI | |
| SYSTEM_PROMPT = ( | |
| "You are a data visualization pro and expert in HoloViz hvplot + holoviews. " | |
| "Your primary goal is to assist the user in editing based on user requests using best practices. " | |
| "Simply provide code in code fences (```python). You must have `hvplot_obj` as the last line of code. " | |
| "Note, data columns are ['sepal_length', 'sepal_width', 'petal_length', 'petal_width', 'species'] and " | |
| "hvplot is built on top of holoviews--anything you can do with holoviews, you can do " | |
| "with hvplot. First try to use hvplot **kwargs instead of opts, e.g. `legend='top_right'` " | |
| "instead of `opts(legend_position='top_right')`. If you need to use opts, you can use " | |
| "concise version, e.g. `opts(xlabel='Petal Length')` vs `opts(hv.Opts(xlabel='Petal Length'))`" | |
| ) | |
| USER_CONTENT_FORMAT = """ | |
| Request: | |
| {content} | |
| Code: | |
| ```python | |
| {code} | |
| ``` | |
| """.strip() | |
| DEFAULT_HVPLOT = """ | |
| import hvplot.pandas | |
| from bokeh.sampledata.iris import flowers | |
| hvplot_obj = flowers.hvplot(x='petal_length', y='petal_width', by='species', kind='scatter') | |
| hvplot_obj | |
| """.strip() | |
| def exception_handler(exc): | |
| if retries.value == 0: | |
| chat_interface.send(f"Can't figure this out: {exc}", respond=False) | |
| return | |
| chat_interface.send(f"Fix this error:\n```python\n{exc}\n```") | |
| retries.value = retries.value - 1 | |
| def init_llm(event): | |
| api_key = event.new | |
| if not api_key: | |
| api_key = os.environ.get("OPENAI_API_KEY") | |
| if not api_key: | |
| return | |
| pn.state.cache["llm"] = OpenAI(api_key=api_key) | |
| def create_chat_engine(llm): | |
| try: | |
| storage_context = StorageContext.from_defaults(persist_dir="persisted/") | |
| index = load_index_from_storage(storage_context=storage_context) | |
| except Exception as exc: | |
| embed_model = OpenAIEmbedding() | |
| service_context = ServiceContext.from_defaults(llm=llm, embed_model=embed_model) | |
| documents = SimpleDirectoryReader( | |
| input_dir="hvplot_docs", required_exts=[".md"], recursive=True | |
| ).load_data() | |
| index = VectorStoreIndex.from_documents( | |
| documents, service_context=service_context, show_progress=True | |
| ) | |
| index.storage_context.persist("persisted/") | |
| retriever = index.as_retriever() | |
| chat_engine = ContextChatEngine.from_defaults( | |
| system_prompt=SYSTEM_PROMPT, | |
| retriever=retriever, | |
| verbose=True, | |
| ) | |
| return chat_engine | |
| def callback(content: str, user: str, instance: pn.chat.ChatInterface): | |
| if "llm" not in pn.state.cache: | |
| yield "Need to set OpenAI API key first" | |
| return | |
| if "engine" not in pn.state.cache: | |
| engine = pn.state.cache["engine"] = create_chat_engine(pn.state.cache["llm"]) | |
| else: | |
| engine = pn.state.cache["engine"] | |
| # new user contents | |
| user_content = USER_CONTENT_FORMAT.format( | |
| content=content, code=code_editor.value | |
| ) | |
| # send user content to chat engine | |
| agent_response = engine.stream_chat(user_content) | |
| message = None | |
| for chunk in agent_response.response_gen: | |
| message = instance.stream(chunk, message=message, user="OpenAI") | |
| # extract code | |
| llm_matches = re.findall(r"```python\n(.*)\n```", message.object, re.DOTALL) | |
| if llm_matches: | |
| llm_code = llm_matches[0] | |
| if llm_code.splitlines()[-1].strip() != "hvplot_obj": | |
| llm_code += "\nhvplot_obj" | |
| code_editor.value = llm_code | |
| retries.value = 2 | |
| def update_plot(event): | |
| with StringIO() as buf: | |
| hvplot_pane.object = exec_with_return(event.new, stderr=buf) | |
| buf.seek(0) | |
| errors = buf.read() | |
| if errors: | |
| exception_handler(errors) | |
| pn.extension("codeeditor", sizing_mode="stretch_width", exception_handler=exception_handler) | |
| # instantiate widgets and panes | |
| api_key_input = pn.widgets.PasswordInput( | |
| placeholder=( | |
| "Currently subsidized by Andrew, " | |
| "but you can also pass your own OpenAI API Key" | |
| ) | |
| ) | |
| chat_interface = pn.chat.ChatInterface( | |
| callback=callback, | |
| show_clear=False, | |
| show_undo=False, | |
| show_button_name=False, | |
| message_params=dict( | |
| show_reaction_icons=False, | |
| show_copy_icon=False, | |
| ), | |
| height=650, | |
| callback_exception="verbose", | |
| ) | |
| hvplot_pane = pn.pane.HoloViews( | |
| exec_with_return(DEFAULT_HVPLOT), | |
| sizing_mode="stretch_both", | |
| ) | |
| code_editor = pn.widgets.CodeEditor( | |
| value=DEFAULT_HVPLOT, | |
| language="python", | |
| sizing_mode="stretch_both", | |
| ) | |
| retries = pn.widgets.IntInput(value=2, visible=False) | |
| error = pn.widgets.StaticText(visible=False) | |
| # watch for code changes | |
| api_key_input.param.watch(init_llm, "value") | |
| code_editor.param.watch(update_plot, "value") | |
| api_key_input.param.trigger("value") | |
| # lay them out | |
| tabs = pn.Tabs( | |
| ("Plot", hvplot_pane), | |
| ("Code", code_editor), | |
| ) | |
| sidebar = [api_key_input, chat_interface] | |
| main = [tabs] | |
| template = pn.template.FastListTemplate( | |
| sidebar=sidebar, | |
| main=main, | |
| sidebar_width=600, | |
| main_layout=None, | |
| accent_base_color="#fd7000", | |
| header_background="#fd7000", | |
| title="Chat with Plot" | |
| ) | |
| template.servable() | |