@@ -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
894889if 'messages' not in st.session_state:
895890 st.session_state['messages'] = []
891+ """
896892
893+ render_messages_block = """
897894# Display chat history
898895for 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
912912if 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
973925def 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
981934def 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
994948def 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