import random import time from dataclasses import asdict, dataclass from typing import Callable, Literal import mesop.labs as mel import mesop as me import requests import json from dataclasses import dataclass, field from mongo_db_writer_main import log_prompt_and_response Role = Literal["user", "bot"] _INTRO_TEXT = """ # Mesop Markdown Editor Example This example shows how to make a simple markdown editor. """.strip() @dataclass(kw_only=True) class Note: """Content of note.""" content: str = "" _APP_TITLE = "# YuvaRaj's LLM-Playground" _BOT_AVATAR_LETTER = "UV" _EMPTY_CHAT_MESSAGE = "Get started with an example" _EXAMPLE_USER_QUERIES = ( "Examples of SQL Commands?", "What a store proc? Provide an examples", "What is Java Class, provide few examples?", "What is quantum computing, and why is it important?" ) _CHAT_MAX_WIDTH = "800px" _MOBILE_BREAKPOINT = 640 @dataclass(kw_only=True) class ChatMessage: """Chat message metadata.""" role: Role = "user" content: str = "You're ai expert, guided by yuvaraj" edited: bool = False # 1 is positive # -1 is negative # 0 is no rating rating: int = 0 @me.stateclass class State: input: str output: list[ChatMessage] in_progress: bool sidebar_expanded: bool = False # Need to use dict instead of ChatMessage due to serialization bug. # See: https://github.com/google/mesop/issues/659 history: list[list[dict]] notes: list[Note] = field(default_factory=lambda: [Note(content=_INTRO_TEXT)]) selected_note_index: int = 0 selected_note_content: str = _INTRO_TEXT show_preview: bool = True def call_ollama_api(input_text: str, history: list[ChatMessage]) -> str: """ Sends the user input and chat history to the Ollama endpoint and retrieves the response. """ OLLAMA_URL = "http://96.228.37.139:4002/api/generate" MODEL_NAME = "mistral-small" # Format the chat history as part of the prompt formatted_history = "\n".join([f"{msg.role}: {msg.content}" for msg in history]) prompt = f"{formatted_history}\nuser: give me response is well formatted {input_text}" # Prepare the payload payload = { "model": MODEL_NAME, "prompt": prompt, # Include chat history in the prompt "stream": False, } try: response = requests.post(OLLAMA_URL, json=payload) response.raise_for_status() # Parse the JSON response response_data = response.json() log_reponse_data=response_data.get("response") # Access the 'response' field from the parsed JSON data if "response" in response_data: # Call the external logging function log_prompt_and_response(prompt, log_reponse_data) return response_data.get("response") else: return "Error: No response field found in Ollama API response" except requests.exceptions.RequestException as e: return f"Error connecting to Ollama: {e}" except json.JSONDecodeError as e: return f"JSON Parsing Error: {e}, Raw Response: {response.text}" def respond_to_chat(input: str, history: list[ChatMessage]): """Calls the Ollama API to get a response, including chat history.""" return call_ollama_api(input, history) def on_load(e: me.LoadEvent): me.set_theme_mode("system") @me.page( security_policy=me.SecurityPolicy( allowed_iframe_parents=["https://google.github.io", "https://huggingface.co"] ), title="YUVI PlayGround for LLM-FineTuning", path="/", on_load=on_load ) def page(): state = me.state(State) with me.box( style=me.Style( background=me.theme_var("surface-container-lowest"), display="flex", flex_direction="column", height="100%", ) ): with me.box( style=me.Style( display="flex", flex_direction="row", flex_grow=1, overflow="hidden" ) ): with me.box( style=me.Style( background=me.theme_var("surface-container-low"), display="flex", flex_direction="column", flex_shrink=0, position="absolute" if state.sidebar_expanded and _is_mobile() else None, height="100%" if state.sidebar_expanded and _is_mobile() else None, width=300 if state.sidebar_expanded else None, z_index=2000, ) ): sidebar() with me.box( style=me.Style( display="flex", flex_direction="column", flex_grow=1, padding=me.Padding(left=60) if state.sidebar_expanded and _is_mobile() else None, ) ): header() with me.box(style=me.Style(flex_grow=1, overflow_y="scroll")): if state.output: chat_pane() else: examples_pane() chat_input() def sidebar(): state = me.state(State) with me.box( style=me.Style( display="flex", flex_direction="column", flex_grow=1, ) ): with me.box(style=me.Style(display="flex", gap=20)): menu_icon(icon="menu", tooltip="Menu", on_click=on_click_menu_icon) if state.sidebar_expanded: me.text( _APP_TITLE, style=me.Style(margin=me.Margin(bottom=0, top=14)), type="headline-6", ) if state.sidebar_expanded: menu_item(icon="add", label="New chat", on_click=on_click_new_chat) else: menu_icon(icon="add", tooltip="New chat", on_click=on_click_new_chat) if state.sidebar_expanded: history_pane() def history_pane(): state = me.state(State) for index, chat in enumerate(state.history): with me.box( key=f"chat-{index}", on_click=on_click_history, style=me.Style( background=me.theme_var("surface-container"), border=me.Border.all( me.BorderSide( width=1, color=me.theme_var("outline-variant"), style="solid" ) ), border_radius=5, cursor="pointer", margin=me.Margin.symmetric(horizontal=10, vertical=10), padding=me.Padding.all(10), text_overflow="ellipsis", ), ): me.text(_truncate_text(chat[0]["content"])) def header(): state = me.state(State) with me.box( style=me.Style( align_items="center", background=me.theme_var("surface-container-lowest"), display="flex", gap=5, justify_content="space-between", padding=me.Padding.symmetric(horizontal=20, vertical=10), font_family="Roboto" ) ): with me.box(style=me.Style(display="flex", gap=5)): if not state.sidebar_expanded: me.text( _APP_TITLE, style=me.Style(margin=me.Margin(bottom=0)), type="headline-6", ) with me.box(style=me.Style(display="flex", gap=5)): icon_button( key="", icon="dark_mode" if me.theme_brightness() == "light" else "light_mode", tooltip="Dark mode" if me.theme_brightness() == "light" else "Light mode", on_click=on_click_theme_brightness, ) def examples_pane(): with me.box( style=me.Style( margin=me.Margin.symmetric(horizontal="auto"), padding=me.Padding.all(15), width=f"min({_CHAT_MAX_WIDTH}, 100%)", ) ): with me.box(style=me.Style(margin=me.Margin(top=25), font_size=40,font_weight=70)): me.text(_EMPTY_CHAT_MESSAGE) with me.box( style=me.Style( display="flex", flex_direction="column" if _is_mobile() else "row", gap=20, margin=me.Margin(top=25), ) ): for index, query in enumerate(_EXAMPLE_USER_QUERIES): with me.box( key=f"query-{index}", on_click=on_click_example_user_query, style=me.Style( background=me.theme_var("surface-container-highest"), border_radius=30, padding=me.Padding.all(20), cursor="pointer", font_family="Roboto" ), ): me.text(query) def chat_pane(): state = me.state(State) with me.box( style=me.Style( background=me.theme_var("surface-container-lowest"), color=me.theme_var("on-surface"), display="flex", flex_direction="column", margin=me.Margin.symmetric(horizontal="auto"), padding=me.Padding.all(15), width=f"min({_CHAT_MAX_WIDTH}, 100%)", font_family="Roboto" #YR ) ): for index, msg in enumerate(state.output): if msg.role == "user": user_message(message=msg) else: bot_message(message_index=index, message=msg) if state.in_progress: with me.box(key="scroll-to", style=me.Style(height=250)): pass def user_message(*, message: ChatMessage): with me.box( style=me.Style( display="flex", gap=15, justify_content="end", margin=me.Margin.all(20), ) ): with me.box( style=me.Style( background=me.theme_var("surface-container-low"), border_radius=10, color=me.theme_var("on-surface-variant"), padding=me.Padding.symmetric(vertical=0, horizontal=10), width="66%", ) ): me.markdown(message.content) def bot_message(*, message_index: int, message: ChatMessage): with me.box(style=me.Style(display="flex", gap=20, margin=me.Margin.all(1))): text_avatar( background=me.theme_var("primary"), color=me.theme_var("on-primary"), label=_BOT_AVATAR_LETTER, ) # Bot message response with me.box(style=me.Style(display="flex", flex_direction="column")): me.markdown( message.content, style=me.Style(color=me.theme_var("on-surface")), ) # Actions panel with me.box(): icon_button( key=f"thumb_up-{message_index}", icon="thumb_up", is_selected=message.rating == 1, tooltip="Good response", on_click=on_click_thumb_up, ) icon_button( key=f"thumb_down-{message_index}", icon="thumb_down", is_selected=message.rating == -1, tooltip="Bad response", on_click=on_click_thumb_down, ) icon_button( key=f"restart-{message_index}", icon="restart_alt", tooltip="Regenerate answer", on_click=on_click_regenerate, ) def chat_input(): state = me.state(State) with me.box( style=me.Style( background=me.theme_var("surface-container") if _is_mobile() else me.theme_var("surface-container"), border_radius=16, display="flex", margin=me.Margin.symmetric(horizontal="auto", vertical=15), padding=me.Padding.all(8), width=f"min({_CHAT_MAX_WIDTH}, 100%)", ) ): with me.box( style=me.Style( flex_grow=1, font_family="Roboto" ) ): me.native_textarea( autosize=True, key="chat_input", min_rows=4, on_blur=on_chat_input, shortcuts={ me.Shortcut(key="Enter"): on_submit_chat_msg, # Handle Enter key me.Shortcut(shift=True, key="Enter"): lambda e: None, # Allow Shift+Enter for newline }, placeholder="Enter your prompt", style=me.Style( background=me.theme_var("surface-container") if _is_mobile() else me.theme_var("surface-container"), border=me.Border.all( me.BorderSide(style="none"), ), color=me.theme_var("on-surface-variant"), outline="none", overflow_y="auto", padding=me.Padding(top=16, left=16), width="100%", ), value=state.input, ) with me.content_button( disabled=state.in_progress, on_click=on_click_submit_chat_msg, type="icon", ): me.icon("send") @me.component def text_avatar(*, label: str, background: str, color: str): me.text( label, style=me.Style( background=background, border_radius="40%", color=color, font_size=20, height=40, line_height="1", margin=me.Margin(top=16), padding=me.Padding(top=10), text_align="center", width="45px", ), ) @me.component def icon_button( *, icon: str, tooltip: str, key: str = "", is_selected: bool = False, on_click: Callable | None = None, ): selected_style = me.Style( background=me.theme_var("surface-container-low"), color=me.theme_var("on-surface-variant"), ) with me.tooltip(message=tooltip): with me.content_button( type="icon", key=key, on_click=on_click, style=selected_style if is_selected else None, ): me.icon(icon) @me.component def menu_icon( *, icon: str, tooltip: str, key: str = "", on_click: Callable | None = None ): with me.tooltip(message=tooltip): with me.content_button( key=key, on_click=on_click, style=me.Style(margin=me.Margin.all(10)), type="icon", ): me.icon(icon) @me.component def menu_item( *, icon: str, label: str, key: str = "", on_click: Callable | None = None ): with me.box(on_click=on_click): with me.box( style=me.Style( background=me.theme_var("surface-container-high"), border_radius=20, cursor="pointer", display="inline-flex", gap=10, line_height=1, margin=me.Margin.all(10), padding=me.Padding(top=10, left=10, right=20, bottom=10), ), ): me.icon(icon) me.text(label, style=me.Style(height=24, line_height="24px")) # Event Handlers def on_click_example_user_query(e: me.ClickEvent): """Populates the user input with the example query""" state = me.state(State) _, example_index = e.key.split("-") state.input = _EXAMPLE_USER_QUERIES[int(example_index)] me.focus_component(key="chat_input") def on_click_thumb_up(e: me.ClickEvent): """Gives the message a positive rating""" state = me.state(State) _, msg_index = e.key.split("-") msg_index = int(msg_index) state.output[msg_index].rating = 1 def on_click_thumb_down(e: me.ClickEvent): """Gives the message a negative rating""" state = me.state(State) _, msg_index = e.key.split("-") msg_index = int(msg_index) state.output[msg_index].rating = -1 def on_click_new_chat(e: me.ClickEvent): """Resets messages and saves the current chat to history.""" state = me.state(State) if state.output: # Save the current chat to history state.history.insert(0, [asdict(message) for message in state.output]) # Reset the output for a new chat state.output = [] me.focus_component(key="chat_input") def on_click_history(e: me.ClickEvent): """Loads existing chat from history and saves current chat.""" state = me.state(State) _, chat_index = e.key.split("-") chat_index = int(chat_index) # Save the current chat to history if it exists if state.output: state.history.insert(0, [asdict(message) for message in state.output]) # Load the selected chat from history chat_messages = [ChatMessage(**chat) for chat in state.history.pop(chat_index)] state.output = chat_messages me.focus_component(key="chat_input") def on_click_theme_brightness(e: me.ClickEvent): """Toggles dark mode.""" if me.theme_brightness() == "light": me.set_theme_mode("dark") else: me.set_theme_mode("light") def on_click_menu_icon(e: me.ClickEvent): """Expands and collapses sidebar menu.""" state = me.state(State) state.sidebar_expanded = not state.sidebar_expanded def on_chat_input(e: me.InputBlurEvent): """Capture chat text input on blur.""" state = me.state(State) state.input = e.value def on_click_regenerate(e: me.ClickEvent): """Regenerates response from an existing message""" state = me.state(State) _, msg_index = e.key.split("-") msg_index = int(msg_index) # Get the user message which is the previous message user_message = state.output[msg_index - 1] # Get bot message to be regenerated assistant_message = state.output[msg_index] assistant_message.content = "" state.in_progress = True yield start_time = time.time() # Send in the old user input and chat history to get the bot response. # We make sure to only pass in the chat history up to this message. ollama_response = respond_to_chat( user_message.content, state.output[:msg_index] ) assistant_message.content = ollama_response yield state.in_progress = False me.focus_component(key="chat_input") yield def transform(input: str, history: list[mel.ChatMessage]): """ Takes user input, sends it to the Ollama API, and yields the complete response. """ # Call Ollama API with user input ollama_response = respond_to_chat(input, history) yield ollama_response def on_submit_chat_msg(e: me.TextareaShortcutEvent): """Handles submitting a chat message when Enter is pressed.""" state = me.state(State) state.input = e.value yield yield from _submit_chat_msg() def on_click_submit_chat_msg(e: me.ClickEvent): yield from _submit_chat_msg() def _submit_chat_msg(): """Handles submitting a chat message.""" state = me.state(State) if state.in_progress or not state.input: return input = state.input # Clear the text input. state.input = "" yield output = state.output if output is None: output = [] output.append(ChatMessage(role="user", content=input)) state.in_progress = True me.scroll_into_view(key="scroll-to") yield # Send user input and chat history to get the bot response. ollama_response = respond_to_chat(input, state.output) assistant_message = ChatMessage(role="bot", content=ollama_response) output.append(assistant_message) state.output = output state.in_progress = False me.focus_component(key="chat_input") yield # Helpers def _is_mobile(): return me.viewport_size().width < _MOBILE_BREAKPOINT def _truncate_text(text, char_limit=100): """Truncates text that is too long.""" if len(text) <= char_limit: return text truncated_text = text[:char_limit].rsplit(" ", 1)[0] return truncated_text.rstrip(".,!?;:") + "..."