import streamlit as st from streamlit_chat import message import requests import pandas as pd import plotly.express as px st.set_page_config(layout="wide") API_URL = "https://virtual-eng-expert-api-v1.greensea-b20be511.northeurope.azurecontainerapps.io/api" headers = {"Content-Type": "application/json"} def query(payload, retries=2): """Query the API with retry logic""" for attempt in range(retries + 1): try: response = requests.post(API_URL, headers=headers, json=payload, timeout=60) response.raise_for_status() # Raise an exception for bad status codes # Check if response is actually JSON before parsing content_type = response.headers.get('Content-Type', '') if 'application/json' not in content_type: # Response is not JSON - likely HTML error page error_text = response.text[:1000] st.error(f"API returned non-JSON response (Content-Type: {content_type})") st.error(f"Response preview: {error_text[:500]}") with st.expander("Debug: View full response"): st.text(error_text) with st.expander("Debug: View request payload"): st.json(payload) raise ValueError(f"API returned non-JSON response. Status: {response.status_code}") # Try to parse JSON try: return response.json() except ValueError as json_error: # Response claims to be JSON but isn't valid st.error(f"API returned invalid JSON. Response text: {response.text[:500]}") with st.expander("Debug: View request payload"): st.json(payload) raise ValueError(f"Invalid JSON response: {str(json_error)}") except requests.exceptions.HTTPError as e: if attempt < retries: st.warning(f"Attempt {attempt + 1} failed with {e.response.status_code}. Retrying...") continue error_msg = f"API Error {e.response.status_code}" try: # Try to get JSON error if available error_json = e.response.json() if isinstance(error_json, dict): error_msg += f": {error_json.get('error', error_json.get('message', str(error_json)))}" else: error_msg += f": {error_json}" except: # Not JSON, show text response error_text = e.response.text[:500] if error_text: error_msg += f": {error_text}" st.error(error_msg) with st.expander("Debug: View request payload"): st.json(payload) raise except requests.exceptions.Timeout: if attempt < retries: st.warning(f"Attempt {attempt + 1} timed out. Retrying...") continue st.error("Request timed out. The API may be slow or unavailable. Please try again.") raise except requests.exceptions.RequestException as e: if attempt < retries: st.warning(f"Attempt {attempt + 1} failed: {str(e)[:100]}. Retrying...") continue st.error(f"Connection Error: {str(e)}") raise def generate_response(user,prompt): output = query({"namespace":"virtualEngineer","version":1,"messages":[{"role":user, "content": prompt}]}) return output #Creating the chatbot interface st.title("Engineering Expert") st.caption("Problem: A major facilities engineering provider with over 40,000 field-based engineers and a large PDF-based knowledge repository of over 100,000 pages of documents faced significant challenges. The existing system used a metadata-driven search, which was highly manual to maintain and inefficient for engineers needing quick access to relevant information. This inefficiency led to delays in field operations and difficulties in maintaining up-to-date knowledge across the organization.") st.caption("Solution: To address this challenge, the engineering provider implemented a Virtual Engineering Assistant powered by Generative AI (GenAI). The solution involved developing an advanced information science strategy that leveraged a vector database and unique memory architectures to improve information retrieval and decision support.") col1, col2 = st.columns(2) # Storing the chat if 'generated' not in st.session_state: st.session_state['generated'] = [] if 'past' not in st.session_state: st.session_state['past'] = [] with col1: with st.form(key='my_form'): User = st.text_input("Step 1 Set Engineer Name","", key="user") input_text = st.text_input("Step 2 How can I help today?","", key="input") submit_button = st.form_submit_button(label='Submit') if submit_button: output = generate_response(User,input_text) # store the output st.session_state.past.append(input_text) st.session_state.generated.append(output['choices'][0]['message']['content']) df = pd.DataFrame(output["sources"]) fig = px.scatter(df, y="Score",color="Source", hover_data=['Insight']) fig.update_layout(showlegend=False) # Layout the columns st.subheader("Response Data") st.caption(f"Overall Answer Confidence: {output['accuracy']}%") st.caption(f"Time Taken: {round(output['timeTaken'],1)} Seconds") st.caption(f"Cognitive Cache Hit: {output['cacheHit']}") st.caption('The below chart shows the distribution of knowledge in the knowledge store. This is used to supply expert information to answer your specific question. We request the top 100 results to speed up this demo, but analysing the knowledge store is an important part of maintaining high quality knowledge base which is key to high alignment, minimising hallucinations and ensuring safety.') st.plotly_chart(fig, theme=None, use_container_width=True) st.caption('Sources:') st.caption('This table represents the books and their page numbers that were used to create the expert answer') st.table(df.loc[:, ['Score', 'Source', 'startpage']].head(5)) if st.session_state['generated']: for i in range(len(st.session_state['generated'])-1, -1, -1): with col2: message(st.session_state["generated"][i], key=str(i),avatar_style="identicon",seed="Socks") message(st.session_state['past'][i], is_user=True, key=str(i) + '_user',avatar_style="identicon",seed="Mittens")