Skip to content

Commit 6ca43e0

Browse files
committed
♻️ Refactor: set model as optional parameter for chatbot class and modify the _generate_chatbot_content function to handle both Olla-style and standard API chatbots
Also, fix bug in the _format_text function to recognize caption type
1 parent e8df33b commit 6ca43e0

File tree

3 files changed

+107
-132
lines changed

3 files changed

+107
-132
lines changed

src/vuegen/config_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ def _create_chatbot_component(self, component_data: dict) -> r.ChatBot:
599599
title=component_data["title"],
600600
logger=self.logger,
601601
api_url=component_data["api_url"],
602-
model=component_data["model"],
602+
model=component_data.get("model"),
603603
caption=component_data.get("caption"),
604604
headers=component_data.get("headers"),
605605
params=component_data.get("params"),

src/vuegen/report.py

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -586,15 +586,14 @@ def make_api_request(self, dynamic_request_body: Optional[dict] = None) -> Optio
586586
self.logger.info(f"Making {self.method} request to API: {self.api_url}")
587587
self.logger.debug(f"Headers: {self.headers}")
588588
self.logger.debug(f"Params: {self.params}")
589-
self.logger.debug(f"Request Body: {request_body_to_send}")
590589

591590
response = requests.request(
592591
self.method,
593592
self.api_url,
594593
headers=self.headers,
595594
params=self.params,
596595
# Validate the request body based on the method
597-
json=request_body_to_send if self.method in ["POST", "PUT", "PATCH"] and request_body_to_send else None
596+
json=request_body_to_send if self.method in ["POST", "PUT", "PATCH"] and request_body_to_send else None,
598597
)
599598
response.raise_for_status()
600599
self.logger.info(
@@ -603,17 +602,6 @@ def make_api_request(self, dynamic_request_body: Optional[dict] = None) -> Optio
603602
return response.json()
604603
except requests.exceptions.RequestException as e:
605604
self.logger.error(f"API request failed: {e}")
606-
# Attempt to get error details from response body if possible
607-
try:
608-
error_details = e.response.json() if e.response else str(e)
609-
self.logger.error(f"Error details: {error_details}")
610-
except json.JSONDecodeError:
611-
error_details = e.response.text if e.response else str(e)
612-
self.logger.error(f"Error details (non-JSON): {error_details}")
613-
return None
614-
except json.JSONDecodeError as e:
615-
self.logger.error(f"Failed to decode JSON response: {e}")
616-
self.logger.error(f"Response text: {response.text}")
617605
return None
618606

619607

@@ -624,10 +612,10 @@ class ChatBot(Component):
624612
625613
Attributes
626614
----------
627-
model : str
628-
The language model to use for the chatbot.
629615
api_call : APICall
630616
An instance of the APICall class used to interact with the API for fetching chatbot responses.
617+
model : Optional[str]
618+
The language model to use for the chatbot (default is None).
631619
headers : Optional[dict]
632620
Headers to include in the API request (default is None).
633621
params : Optional[dict]
@@ -639,8 +627,8 @@ def __init__(
639627
title: str,
640628
logger: logging.Logger,
641629
api_url: str,
642-
model: str,
643630
caption: str = None,
631+
model: Optional[str] = None,
644632
headers: Optional[dict] = None,
645633
params: Optional[dict] = None,
646634
):
@@ -659,8 +647,6 @@ def __init__(
659647
caption=None,
660648
headers=headers,
661649
params=params,
662-
# Default request_body is empty, it will be set dynamically
663-
request_body=None
664650
)
665651

666652

src/vuegen/streamlit_reportview.py

Lines changed: 102 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ def _format_text(
256256
"""
257257
if type == "header":
258258
tag = f"h{level}"
259-
elif type == "paragraph":
259+
elif type == "paragraph" or type == "caption":
260260
tag = "p"
261261

262262
return f"""st.markdown('''<{tag} style='text-align: {text_align}; color: {color};'>{text}</{tag}>''', unsafe_allow_html=True)"""
@@ -417,6 +417,9 @@ def _generate_subsection(
417417
subsection_content = []
418418
subsection_imports = []
419419

420+
# Track if there's a Chatbot component in this subsection
421+
has_chatbot = False
422+
420423
# Add subsection header and description
421424
subsection_content.append(
422425
self._format_text(
@@ -451,15 +454,17 @@ def _generate_subsection(
451454
elif component.component_type == r.ComponentType.APICALL:
452455
subsection_content.extend(self._generate_apicall_content(component))
453456
elif component.component_type == r.ComponentType.CHATBOT:
457+
has_chatbot = True
454458
subsection_content.extend(self._generate_chatbot_content(component))
455459
else:
456460
self.report.logger.warning(
457461
f"Unsupported component type '{component.component_type}' in subsection: {subsection.title}"
458462
)
459463

460-
# Define the footer variable and add it to the home page content
461-
subsection_content.append("footer = '''" + generate_footer() + "'''\n")
462-
subsection_content.append("st.markdown(footer, unsafe_allow_html=True)\n")
464+
if not has_chatbot:
465+
# Define the footer variable and add it to the home page content
466+
subsection_content.append("footer = '''" + generate_footer() + "'''\n")
467+
subsection_content.append("st.markdown(footer, unsafe_allow_html=True)\n")
463468

464469
self.report.logger.info(
465470
f"Generated content and imports for subsection: '{subsection.title}'"
@@ -854,122 +859,69 @@ def _generate_apicall_content(self, apicall) -> List[str]:
854859
f"Successfully generated content for APICall '{apicall.title}' using method '{apicall.method}'"
855860
)
856861
return apicall_content
857-
862+
858863
def _generate_chatbot_content(self, chatbot) -> List[str]:
859864
"""
860-
Generate content for a ChatBot component with exact API requirements.
865+
Generate content for a ChatBot component, supporting both standard and Ollama-style APIs.
866+
867+
Parameters
868+
----------
869+
chatbot : ChatBot
870+
The ChatBot component to generate content for.
871+
872+
Returns
873+
-------
874+
list : List[str]
875+
The list of content lines for the ChatBot.
861876
"""
862877
chatbot_content = []
863878

864-
# Add title
879+
# Add chatbot title as header
865880
chatbot_content.append(
866881
self._format_text(
867882
text=chatbot.title, type="header", level=4, color="#2b8cbe"
868883
)
869884
)
870885

871-
# Chatbot logic with exact API requirements
872-
chatbot_content.append(
873-
f"""
874-
def generate_query(prompt):
875-
try:
876-
response = requests.post(
877-
"{chatbot.api_call.api_url}",
878-
json={{"prompt": prompt}}, # Only send the prompt as required
879-
headers={chatbot.api_call.headers}
880-
)
881-
response.raise_for_status()
882-
return response.json()
883-
except requests.exceptions.RequestException as e:
884-
st.error(f"API request failed: {{str(e)}}")
885-
if hasattr(e, 'response') and e.response:
886-
try:
887-
error_details = e.response.json()
888-
st.error(f"Error details: {{error_details}}")
889-
except ValueError:
890-
st.error(f"Response text: {{e.response.text}}")
891-
return None
892-
893-
# Chatbot interaction
886+
# --- Shared code blocks (as strings) ---
887+
init_messages_block = """
888+
# Init session state
894889
if 'messages' not in st.session_state:
895890
st.session_state['messages'] = []
891+
"""
896892

893+
render_messages_block = """
897894
# Display chat history
898895
for message in st.session_state['messages']:
899896
with st.chat_message(message['role']):
900-
if isinstance(message['content'], dict):
901-
st.markdown(message['content'].get('text', ''), unsafe_allow_html=True)
902-
if 'links' in message['content']:
897+
content = message['content']
898+
if isinstance(content, dict):
899+
st.markdown(content.get('text', ''), unsafe_allow_html=True)
900+
if 'links' in content:
903901
st.markdown("**Sources:**")
904-
for link in message['content']['links']:
905-
st.markdown(f"- [{{link}}]({{link}})")
906-
if 'subgraph_pyvis' in message['content']:
907-
st.components.v1.html(message['content']['subgraph_pyvis'], height=600)
902+
for link in content['links']:
903+
st.markdown(f"- [{link}]({link})")
904+
if 'subgraph_pyvis' in content:
905+
st.components.v1.html(content['subgraph_pyvis'], height=600)
908906
else:
909-
st.write(message['content'])
907+
st.write(content)
908+
"""
910909

911-
# Handle user input
910+
handle_prompt_block = """
911+
# Capture and append new user prompt
912912
if prompt := st.chat_input("Enter your prompt here:"):
913-
st.session_state.messages.append({{"role": "user", "content": prompt}})
913+
st.session_state.messages.append({"role": "user", "content": prompt})
914914
with st.chat_message("user"):
915915
st.write(prompt)
916-
917-
with st.spinner('Generating answer...'):
918-
response = generate_query(prompt)
919-
920-
if response:
921-
st.session_state.messages.append({{
922-
"role": "assistant",
923-
"content": response
924-
}})
925-
with st.chat_message("assistant"):
926-
st.markdown(response.get('text', ''), unsafe_allow_html=True)
927-
if 'links' in response:
928-
st.markdown("**Sources:**")
929-
for link in response['links']:
930-
st.markdown(f"- [{{link}}]({{link}})")
931-
if 'subgraph_pyvis' in response:
932-
st.components.v1.html(response['subgraph_pyvis'], height=600)
933-
else:
934-
st.error("Failed to get response from API")
935916
"""
936-
)
937917

938-
if chatbot.caption:
918+
if chatbot.model:
919+
# --- Ollama-style streaming chatbot ---
939920
chatbot_content.append(
940-
self._format_text(
941-
text=chatbot.caption, type="caption", text_align="left"
942-
)
943-
)
944-
945-
return chatbot_content
921+
f"""
922+
{init_messages_block}
946923
947-
def _generate_ollama_chatbot_content(self, chatbot) -> List[str]:
948-
"""
949-
Generate content for a ChatBot component.
950-
951-
Parameters
952-
----------
953-
chatbot : ChatBot
954-
The ChatBot component to generate content for.
955-
956-
Returns
957-
-------
958-
list : List[str]
959-
The list of content lines for the ChatBot.
960-
"""
961-
chatbot_content = []
962-
963-
# Add title
964-
chatbot_content.append(
965-
self._format_text(
966-
text=chatbot.title, type="header", level=4, color="#2b8cbe"
967-
)
968-
)
969-
970-
# Chatbot logic for embedding in the web application
971-
chatbot_content.append(
972-
f"""
924+
# Function to send prompt to Ollama API
973925
def generate_query(messages):
974926
response = requests.post(
975927
"{chatbot.api_call.api_url}",
@@ -978,6 +930,7 @@ def generate_query(messages):
978930
response.raise_for_status()
979931
return response
980932
933+
# Parse streaming response from Ollama
981934
def parse_api_response(response):
982935
try:
983936
output = ""
@@ -991,53 +944,89 @@ def parse_api_response(response):
991944
except Exception as e:
992945
return {{"role": "assistant", "content": f"Error while processing API response: {{str(e)}}"}}
993946
947+
# Simulated typing effect for responses
994948
def response_generator(msg_content):
995949
for word in msg_content.split():
996950
yield word + " "
997951
time.sleep(0.1)
998952
yield "\\n"
999953
1000-
# Chatbot interaction in the app
1001-
if 'messages' not in st.session_state:
1002-
st.session_state['messages'] = []
954+
{render_messages_block}
1003955
1004-
# Display chat history
1005-
for message in st.session_state['messages']:
1006-
with st.chat_message(message['role']):
1007-
st.write(message['content'])
956+
{handle_prompt_block}
1008957
1009-
# Handle new input from the user
1010-
if prompt := st.chat_input("Enter your prompt here:"):
1011-
# Add user's question to the session state
1012-
st.session_state.messages.append({{"role": "user", "content": prompt}})
1013-
with st.chat_message("user"):
1014-
st.write(prompt)
1015-
1016958
# Retrieve question and generate answer
1017959
combined = "\\n".join(msg["content"] for msg in st.session_state.messages if msg["role"] == "user")
1018960
messages = [{{"role": "user", "content": combined}}]
1019961
with st.spinner('Generating answer...'):
1020962
response = generate_query(messages)
1021963
parsed_response = parse_api_response(response)
1022-
964+
1023965
# Add the assistant's response to the session state and display it
1024966
st.session_state.messages.append(parsed_response)
1025967
with st.chat_message("assistant"):
1026968
st.write_stream(response_generator(parsed_response["content"]))
1027-
"""
969+
"""
970+
)
971+
else:
972+
# --- Standard (non-streaming) API chatbot ---
973+
chatbot_content.append(
974+
f"""
975+
{init_messages_block}
976+
977+
# Function to send prompt to standard API
978+
def generate_query(prompt):
979+
try:
980+
response = requests.post(
981+
"{chatbot.api_call.api_url}",
982+
json={{"prompt": prompt}},
983+
headers={chatbot.api_call.headers}
1028984
)
985+
response.raise_for_status()
986+
return response.json()
987+
except requests.exceptions.RequestException as e:
988+
st.error(f"API request failed: {{str(e)}}")
989+
if hasattr(e, 'response') and e.response:
990+
try:
991+
error_details = e.response.json()
992+
st.error(f"Error details: {{error_details}}")
993+
except ValueError:
994+
st.error(f"Response text: {{e.response.text}}")
995+
return None
996+
997+
{render_messages_block}
998+
999+
{handle_prompt_block}
1000+
1001+
with st.spinner('Generating answer...'):
1002+
response = generate_query(prompt)
1003+
1004+
if response:
1005+
# Append and display assistant response
1006+
st.session_state.messages.append({{
1007+
"role": "assistant",
1008+
"content": response
1009+
}})
1010+
with st.chat_message("assistant"):
1011+
st.markdown(response.get('text', ''), unsafe_allow_html=True)
1012+
if 'links' in response:
1013+
st.markdown("**Sources:**")
1014+
for link in response['links']:
1015+
st.markdown(f"- [{{link}}]({{link}})")
1016+
if 'subgraph_pyvis' in response:
1017+
st.components.v1.html(response['subgraph_pyvis'], height=600)
1018+
else:
1019+
st.error("Failed to get response from API")
1020+
"""
1021+
)
10291022

1030-
# Add caption if available
10311023
if chatbot.caption:
10321024
chatbot_content.append(
10331025
self._format_text(
10341026
text=chatbot.caption, type="caption", text_align="left"
10351027
)
10361028
)
10371029

1038-
self.report.logger.info(
1039-
f"Successfully generated content for ChatBot: '{chatbot.title}'"
1040-
)
10411030
return chatbot_content
10421031

10431032
def _generate_component_imports(self, component: r.Component) -> List[str]:

0 commit comments

Comments
 (0)