commit bc2e200a2b38c1ff39c1763a2496283f1579cf64 Author: Khanh Dinh Date: Mon Dec 30 23:15:03 2024 +0100 initial version diff --git a/.env b/.env new file mode 100644 index 0000000..357d210 --- /dev/null +++ b/.env @@ -0,0 +1 @@ +OPENAI_API_KEY=GTpNepFFXK6Gy3nHC9LiRteAJzkL4g67dn5CKD3a \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..dc4e1fc --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,5 @@ +__pycache__/ +*.pyc +.vscode/ +.env +venv/ diff --git a/.idea/OKR_Proposer.iml b/.idea/OKR_Proposer.iml new file mode 100644 index 0000000..74d515a --- /dev/null +++ b/.idea/OKR_Proposer.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..aac26a5 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..f8abe40 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..9054b53 --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1735579636156 + + + + \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..5c7247b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,7 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [] +} \ No newline at end of file diff --git a/api.py b/api.py new file mode 100644 index 0000000..7c1d82b --- /dev/null +++ b/api.py @@ -0,0 +1,39 @@ +import requests +import os +import streamlit as st +from dotenv import load_dotenv +from settings import load_settings +from utils import construct_prompt + +# Load API key from .env file +load_dotenv() +api_key = os.getenv("OPENAI_API_KEY") + +if not api_key: + st.error("API key not found. Please set OPENAI_API_KEY in your .env file.") + st.stop() + +api_url = "https://genai.dev.odp.lhgroup.de/openai/deployments/gpt-4-turbo/chat/completions?api-version=2023-07-01-preview" + +def fetch_okrs(user_input: str): + settings = load_settings() + + system_prompt = settings["system_prompt"] + input_template = settings["input_template"] + + user_prompt = construct_prompt(prompt_template=input_template, user_input=user_input) + + headers = {"api-key": api_key, "Content-Type": "application/json"} + body = { + "messages": [ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_prompt} + ] + } + try: + response = requests.post(url=api_url, headers=headers, json=body) + response.raise_for_status() + return response.json() + except Exception as e: + st.error(f"Error fetching data from API: {e}") + return None \ No newline at end of file diff --git a/app.py b/app.py new file mode 100644 index 0000000..48f5885 --- /dev/null +++ b/app.py @@ -0,0 +1,135 @@ +import streamlit as st +import os +import requests +import json + + +from styles import apply_styles + +from streamlit_option_menu import option_menu + +# Import other pages or modules +from settings import settings_page, load_settings +from proposer import proposer_page + +apply_styles() +#proposer_page() + + +# Navigation Menu +selected = option_menu( + menu_title="Main Menu", + options=["Home", "Settings"], + icons=["house", "gear"], + menu_icon="cast", + default_index=0, + orientation="horizontal", +) + +# Render pages based on selection +if selected == "Home": + # Home page logic goes here (e.g., OKR generator) + #from utils import construct_prompt # Example import; replace with actual logic. + + proposer_page() + +elif selected == "Settings": + settings_page() + + + ''' + +# Streamlit App Layout +st.title("OKR Generator") + +# Input Section and Buttons Row +st.subheader("Enter your idea or goal:") +user_input = st.text_area( + "Input your idea here:", + value=st.session_state.get("user_input", INPUT_TEMPLATE.strip()), + height=200, +) + +col1, col2 = st.columns([1, 1]) +with col1: + if st.button("Reset All"): + st.session_state.clear() +with col2: + generate_okrs_clicked = st.button("Generate OKRs") + +if generate_okrs_clicked: + if not user_input.strip(): + st.warning("Please provide some input before generating OKRs.") + else: + with st.spinner("Generating OKRs..."): + # Construct prompt and call API + prompt = construct_prompt(prompt_template=PROMPT_TEMPLATE, user_input=user_input) + response = fetch_okrs(user_prompt=prompt, system_prompt=SYSTEM_PROMPT) + if response: + # Extract Objective and Key Results from response + objective, key_results = extract_llm_response(response) + st.session_state["objective"] = objective + st.session_state["key_results"] = key_results + +# Display Results Only if an OKR Has Been Generated +if "objective" in st.session_state and "key_results" in st.session_state: + # Display Objective Field with Responsibles Input Below It + st.subheader("Proposal Objective:") + objective_text = st.text_area( + "Proposal Objective:", + value=st.session_state.get("objective", ""), + height=100, + ) + + responsible_for_objective = st.text_input( + "Responsibles for Objective (comma-separated):", + value="", + placeholder="e.g., Khanh Dinh, John Doe", + key="responsibles_objective" + ) + + # Display Key Results with Responsibles Below Each One + st.subheader("Proposal Key Results:") + key_result_boxes = [] + responsibles_for_key_results = [] + + for i, kr in enumerate(st.session_state["key_results"], start=1): + kr_text = st.text_area(f"Key Result {i}:", value=kr, key=f"kr_{i}") + responsible_for_kr = st.text_input( + f"Responsibles for Key Result {i} (comma-separated):", + value="", + placeholder="e.g., Khanh Dinh", + key=f"responsibles_kr_{i}" + ) + + key_result_boxes.append(kr_text) + responsibles_for_key_results.append(responsible_for_kr) + + # Finalize Button Center-Aligned + #finalize_col = st.columns([3, 2, 3])[1] + #with finalize_col: + if st.button("Finalize"): + finalized_objective = objective_text.strip() + finalized_key_results = [st.session_state[f"kr_{i+1}"].strip() for i in range(len(key_result_boxes))] + + # Append initials of responsibles to Objective and Key Results + responsibles_list_objective = [name.strip() for name in responsible_for_objective.split(",") if name.strip()] + initials_objective = [f"[{''.join([part[0] for part in name.split()]).upper()}]" for name in responsibles_list_objective] + initials_str_objective = ", ".join(initials_objective) + + finalized_objective += f" [{initials_str_objective}]" + + finalized_key_results_with_initials = [] + for i, kr in enumerate(finalized_key_results): + responsibles_list_kr = [name.strip() for name in responsibles_for_key_results[i].split(",") if name.strip()] + initials_kr = [f"{''.join([part[0] for part in name.split()]).upper()}" for name in responsibles_list_kr] + initials_str_kr = ", ".join(initials_kr) + finalized_key_results_with_initials.append(f"KR{i+1}: [{initials_str_kr}] {kr}") + + # Display finalized data in non-editable format (full width) + st.subheader("Finalized Objective:") + st.write(finalized_objective) + + st.subheader("Finalized Key Results:") + for kr in finalized_key_results_with_initials: + st.write(kr)''' \ No newline at end of file diff --git a/config.py b/config.py new file mode 100644 index 0000000..cbdb656 --- /dev/null +++ b/config.py @@ -0,0 +1,58 @@ +SYSTEM_PROMPT = """ +You are an OKR coach and support us, as beginners in OKRs, with the correct phrasing of OKRs. +Always one proposal and provide the proposal in the requested format (json). +If you think the input is not clear enough and the OKR could be improved, then provide a hint, +what could be added to the user input in order to provide improved OKRs. The hint should also +always provided within the json. + +Format could look like this: +{ + "hint": "any additional questions, which can be added to the user input to rerun the prompting", + "proposals": [ + { + "objective":"objective variant 1", + "key_results": ["key result 1"] + }, + ... + ] +} +""" + +INPUT_TEMPLATE = """ +We want to improve our SLA framework by finalizing the current SLA template, which is used for the negotiations with our ground service partners. +The resulting SLA will be used in order to measure the quality of the service partner and issue penalties if targets are not met. +We would like to improve this, to have a better steering function, e.g. by adding a bonus component to the SLA framework. + +To achieve this, we need to develop a bonus concept which is viable, feasible and desirable. +We need to simulate the concept, so that we don't risk issuing too much bonus which we cannot cover. +We need to define organizational processes and responsibilities so that we are able to pay a bonus. + +How should the OKR look like for the next cycle which starts in Jan 2025 and ends in Apr 2025? +""" + +# Prompt template for user input +PROMPT_TEMPLATE = """ +Please help us in defining proper OKRs. +Here is what we have thought about and we would like to phrase an OKR with up to 5 key results. + +this is the user input: +{user_input} + +Please provide the response in json format. +the three proposals and potentially further questions to the user to improve the OKR, should be structured in a json as response. +Objective (key: objective) +key results (list with the key results) +The json could be structured like this: + +{ + "hint": "any additional questions, which can be added to the user input to rerun the prompting", + "proposals": [ + { + "objective":"objective variant 1", + "key_result_1":"key result 1", + ... + }, + ... + ] +} +""" \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..314bb3c --- /dev/null +++ b/main.py @@ -0,0 +1,42 @@ +import streamlit as st +from dotenv import load_dotenv +from api import fetch_okrs +from utils import construct_prompt, extract_llm_response +from styles import apply_styles +from config import SYSTEM_PROMPT, INPUT_TEMPLATE + +# Load environment variables +load_dotenv() + +# Apply custom styles +apply_styles() + +# Streamlit App Layout +st.title("OKR Generator") +st.subheader("Enter your idea or goal:") + +user_input = st.text_area( + "Input your idea here:", + value=st.session_state.get("user_input", INPUT_TEMPLATE.strip()), + height=200, +) + +if st.button("Generate OKRs"): + if not user_input.strip(): + st.warning("Please provide some input before generating OKRs.") + else: + with st.spinner("Generating OKRs..."): + prompt = construct_prompt(user_input) + response = fetch_okrs(prompt=prompt, system_prompt=SYSTEM_PROMPT) + if response: + objective, key_results = extract_llm_response(response) + st.session_state["objective"] = objective + st.session_state["key_results"] = key_results + +if "objective" in st.session_state: + st.subheader("Proposal Objective:") + st.write(st.session_state["objective"]) + + st.subheader("Proposal Key Results:") + for kr in st.session_state["key_results"]: + st.write(kr) diff --git a/proposer.py b/proposer.py new file mode 100644 index 0000000..931974f --- /dev/null +++ b/proposer.py @@ -0,0 +1,116 @@ +import streamlit as st + +from api import fetch_okrs +from config import SYSTEM_PROMPT, INPUT_TEMPLATE, PROMPT_TEMPLATE +from utils import construct_prompt, extract_llm_response + +def proposer_page(): + # Streamlit App Layout + st.title("OKR Generator") + + # Input Section and Buttons Row + st.subheader("Enter your idea or goal:") + user_input = st.text_area( + "Input your idea here:", + value=st.session_state.get("user_input", INPUT_TEMPLATE.strip()), + height=200, + ) + + col1, col2 = st.columns([1, 1]) + with col1: + if st.button("Reset All"): + st.session_state.clear() + with col2: + generate_okrs_clicked = st.button("Generate OKRs") + + if generate_okrs_clicked: + if not user_input.strip(): + st.warning("Please provide some input before generating OKRs.") + else: + with st.spinner("Generating OKRs..."): + # Construct prompt and call API + #prompt = construct_prompt(prompt_template=PROMPT_TEMPLATE, user_input=user_input) + response = fetch_okrs(user_input=user_input) + if response: + # Extract Objective and Key Results from response + print(response) + objective, key_results, hint = extract_llm_response(response) + + st.session_state["objective"] = objective + st.session_state["key_results"] = key_results + st.session_state["hint"] = hint + + #st.subheader("Hint to improve the OKR proposal") + #st.text(hint) + + # Display Results Only if an OKR Has Been Generated + if "objective" in st.session_state and "key_results" in st.session_state: + + # Display Objective Field with Responsibles Input Below It + st.subheader("Proposal Objective:") + objective_text = st.text_area( + "Proposal Objective:", + value=st.session_state.get("objective", ""), + height=100, + ) + + responsible_for_objective = st.text_input( + "Responsibles for Objective (comma-separated):", + value="", + placeholder="e.g., Khanh Dinh, John Doe", + key="responsibles_objective" + ) + + # Display Key Results with Responsibles Below Each One + st.subheader("Proposal Key Results:") + key_result_boxes = [] + responsibles_for_key_results = [] + + for i, kr in enumerate(st.session_state["key_results"], start=1): + kr_text = st.text_area(f"Key Result {i}:", value=kr, key=f"kr_{i}") + responsible_for_kr = st.text_input( + f"Responsibles for Key Result {i} (comma-separated):", + value="", + placeholder="e.g., Khanh Dinh", + key=f"responsibles_kr_{i}" + ) + + key_result_boxes.append(kr_text) + responsibles_for_key_results.append(responsible_for_kr) + + # Finalize Button Center-Aligned + #finalize_col = st.columns([3, 2, 3])[1] + #with finalize_col: + if st.button("Finalize"): + finalized_objective = objective_text.strip() + finalized_key_results = [st.session_state[f"kr_{i+1}"].strip() for i in range(len(key_result_boxes))] + + # Append initials of responsibles to Objective and Key Results + responsibles_list_objective = [name.strip() for name in responsible_for_objective.split(",") if name.strip()] + initials_objective = [f"[{''.join([part[0] for part in name.split()]).upper()}]" for name in responsibles_list_objective] + initials_str_objective = ", ".join(initials_objective) + + finalized_objective = f"{initials_str_objective} {finalized_objective}" + + finalized_key_results_with_initials = [] + for i, kr in enumerate(finalized_key_results): + responsibles_list_kr = [name.strip() for name in responsibles_for_key_results[i].split(",") if name.strip()] + initials_kr = [f"{''.join([part[0] for part in name.split()]).upper()}" for name in responsibles_list_kr] + initials_str_kr = ", ".join(initials_kr) + finalized_key_results_with_initials.append(f"KR{i+1}: [{initials_str_kr}] {kr}") + + # Display finalized data in non-editable format (full width) + st.subheader("Finalized Objective:") + st.code( + body=finalized_objective, + language=None, + wrap_lines=True + ) + + st.subheader("Finalized Key Results:") + for kr in finalized_key_results_with_initials: + st.code( + body=kr, + language=None, + wrap_lines=True + ) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..e6d3df2 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,74 @@ +altair==5.5.0 +appnope==0.1.4 +asttokens==3.0.0 +attrs==24.3.0 +blinker==1.9.0 +cachetools==5.5.0 +certifi==2024.12.14 +charset-normalizer==3.4.1 +click==8.1.8 +comm==0.2.2 +cramjam==2.9.1 +debugpy==1.8.11 +decorator==5.1.1 +et-xmlfile==2.0.0 +exceptiongroup==1.2.2 +executing==2.1.0 +fastparquet==2024.11.0 +fsspec==2024.12.0 +gitdb==4.0.11 +GitPython==3.1.43 +idna==3.10 +importlib-metadata==8.5.0 +ipykernel==6.29.5 +ipython==8.18.1 +jedi==0.19.2 +jinja2==3.1.5 +jsonschema==4.23.0 +jsonschema-specifications==2024.10.1 +jupyter-client==8.6.3 +jupyter-core==5.7.2 +markdown-it-py==3.0.0 +MarkupSafe==3.0.2 +matplotlib-inline==0.1.7 +mdurl==0.1.2 +narwhals==1.20.1 +nest-asyncio==1.6.0 +numpy==2.0.2 +openpyxl==3.1.5 +packaging==24.2 +pandas==2.2.3 +parso==0.8.4 +pexpect==4.9.0 +pillow==11.0.0 +platformdirs==4.3.6 +prompt-toolkit==3.0.48 +protobuf==5.29.2 +psutil==6.1.1 +ptyprocess==0.7.0 +pure-eval==0.2.3 +pyarrow==18.1.0 +pydeck==0.9.1 +pygments==2.18.0 +pypdf2==3.0.1 +python-dateutil==2.9.0.post0 +python-dotenv==1.0.1 +pytz==2024.2 +pyzmq==26.2.0 +referencing==0.35.1 +requests==2.32.3 +rich==13.9.4 +rpds-py==0.22.3 +six==1.17.0 +smmap==5.0.1 +stack-data==0.6.3 +streamlit==1.41.1 +tenacity==9.0.0 +toml==0.10.2 +tornado==6.4.2 +traitlets==5.14.3 +typing-extensions==4.12.2 +tzdata==2024.2 +urllib3==2.3.0 +wcwidth==0.2.13 +zipp==3.21.0 diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..97aeaaa --- /dev/null +++ b/settings.py @@ -0,0 +1,202 @@ +import streamlit as st +import json +import os +from config import SYSTEM_PROMPT, INPUT_TEMPLATE, PROMPT_TEMPLATE + +def settings_page(): + st.title("Settings") + + # Section for System Prompt + st.subheader("System Prompt") + if "system_prompt" not in st.session_state: + st.session_state["system_prompt"] = SYSTEM_PROMPT.strip() + system_prompt = st.text_area( + "Define the system prompt:", + value=st.session_state["system_prompt"] + ) + + # Section for Input Template + st.subheader("Input Template") + if "input_template" not in st.session_state: + st.session_state["input_template"] = INPUT_TEMPLATE.strip() + input_template = st.text_area( + "Define the input template:", + value=st.session_state["input_template"], + height=400 + ) + + # Section for Prompt Template + st.subheader("Prompt Template") + if "prompt_template" not in st.session_state: + st.session_state["prompt_template"] = PROMPT_TEMPLATE.strip() + prompt_template = st.text_area( + "Define the prompt template:", + value=st.session_state["prompt_template"] + ) + + # Section for Number of Key Results + st.subheader("Number of Key Results") + if "num_key_results" not in st.session_state: + st.session_state["num_key_results"] = 4 + num_key_results = st.number_input( + "Set the maximum number of key results:", + min_value=1, + max_value=10, + value=st.session_state["num_key_results"] + ) + + # Save Button for Settings + if st.button("Save Settings"): + st.session_state["system_prompt"] = system_prompt + st.session_state["input_template"] = input_template + st.session_state["prompt_template"] = prompt_template + st.session_state["num_key_results"] = num_key_results + st.success("Settings saved successfully!") + + # Section for Team Member Management + team_member_management() + +def team_member_management(): + st.subheader("Team Member Management") + + # Initialize team members in session state + if "team_members" not in st.session_state: + st.session_state["team_members"] = ["John Doe", "Jane Smith"] + + # Display current team members + st.write("### Current Team Members") + for member in st.session_state["team_members"]: + col1, col2 = st.columns([4, 1]) + col1.write(member) + if col2.button("Remove", key=f"remove_{member}"): + st.session_state["team_members"].remove(member) + st.experimental_rerun() + + # Add new team member + st.write("### Add New Team Member") + new_member = st.text_input("Enter new team member name:", key="new_member") + if st.button("Add Team Member"): + if new_member.strip(): + if new_member.strip() not in st.session_state["team_members"]: + st.session_state["team_members"].append(new_member.strip()) + st.success(f"Added {new_member.strip()} to the team.") + st.experimental_rerun() + else: + st.warning(f"{new_member.strip()} is already in the team.") + else: + st.warning("Please enter a valid name.") + + + + +SETTINGS_FILE = "settings.json" + +# Load settings from JSON file +def load_settings(): + if os.path.exists(SETTINGS_FILE) and 1 == 2: + with open(SETTINGS_FILE, "r") as f: + return json.load(f) + else: + # Default settings + return { + "system_prompt": SYSTEM_PROMPT, + "input_template": INPUT_TEMPLATE, + "prompt_template": PROMPT_TEMPLATE, + "num_key_results": 4, + "team_members": ["Khanh Dinh", "Robin Plitzko", "Roberto Renna"], + "team_member_template": "{name} - Team Member" + } + +# Save settings to JSON file +def save_settings(settings): + with open(SETTINGS_FILE, "w") as f: + json.dump(settings, f, indent=4) + +# Settings Page +def settings_page(): + st.title("Settings") + + # Load settings + settings = load_settings() + + # Section for System Prompt + st.subheader("System Prompt") + system_prompt = st.text_area( + "Define the system prompt:", + value=settings["system_prompt"] + ) + + # Section for Input Template + st.subheader("Input Template") + input_template = st.text_area( + "Define the input template:", + value=settings["input_template"] + ) + + # Section for Prompt Template + st.subheader("Prompt Template") + prompt_template = st.text_area( + "Define the prompt template (use {num_key_results} for dynamic insertion):", + value=settings["prompt_template"] + ) + + # Section for Number of Key Results + st.subheader("Number of Key Results") + num_key_results = st.number_input( + "Set the maximum number of key results:", + min_value=1, + max_value=10, + value=settings["num_key_results"] + ) + + # Section for Team Member Template + st.subheader("Team Member Template") + team_member_template = st.text_input( + "Define a template for team members (use {name} for dynamic insertion):", + value=settings["team_member_template"] + ) + + # Section for Team Member Management + team_member_management(settings) + + # Save Button for Settings + if st.button("Save Settings"): + # Update settings dictionary + settings["system_prompt"] = system_prompt + settings["input_template"] = input_template + settings["prompt_template"] = prompt_template + settings["num_key_results"] = num_key_results + settings["team_member_template"] = team_member_template + + # Save to file + save_settings(settings) + st.success("Settings saved successfully!") + +# Team Member Management Section +def team_member_management(settings): + st.subheader("Team Member Management") + + # Display current team members + st.write("### Current Team Members") + for member in settings["team_members"]: + col1, col2 = st.columns([4, 1]) + col1.write(member) + if col2.button(f"Remove {member}", key=f"remove_{member}"): + settings["team_members"].remove(member) + save_settings(settings) + st.experimental_rerun() + + # Add new team member + st.write("### Add New Team Member") + new_member = st.text_input("Enter new team member name:", key="new_member") + if st.button("Add Team Member"): + if new_member.strip(): + if new_member.strip() not in settings["team_members"]: + settings["team_members"].append(new_member.strip()) + save_settings(settings) + st.success(f"Added {new_member.strip()} to the team.") + st.experimental_rerun() + else: + st.warning(f"{new_member.strip()} is already in the team.") + else: + st.warning("Please enter a valid name.") diff --git a/settings1.json b/settings1.json new file mode 100644 index 0000000..09b678b --- /dev/null +++ b/settings1.json @@ -0,0 +1,11 @@ +{ + "system_prompt": "You are an OKR coach and support us, as beginners in OKRs, with the correct phrasing of OKRs.", + "input_template": "We want to improve our SLA framework by finalizing the current SLA template...", + "prompt_template": "Please help us in defining proper OKRs. Here is what we have thought about and we would like to phrase an OKR with max. 4 key results. this is the user input: {user_input} Please provide the response in json format. Objective (key: objective) key results (list with the key results)", + "num_key_results": 4, + "team_members": [ + "John Doe", + "Jane Smith" + ], + "team_member_template": "{name} - Team Member" +} diff --git a/styles.py b/styles.py new file mode 100644 index 0000000..07332c7 --- /dev/null +++ b/styles.py @@ -0,0 +1,12 @@ +import streamlit as st + +def apply_styles(): + page_bg_color = """ + + """ + st.markdown(page_bg_color, unsafe_allow_html=True) diff --git a/test.ipynb b/test.ipynb new file mode 100644 index 0000000..d01c8a4 --- /dev/null +++ b/test.ipynb @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "hello\n" + ] + } + ], + "source": [ + "print('hello')" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "venv", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.6" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..8cea80a --- /dev/null +++ b/utils.py @@ -0,0 +1,160 @@ +import json +import streamlit as st + +# Function to construct the prompt +def construct_prompt(prompt_template: str, user_input: str) -> str: + return prompt_template.format(user_input=user_input) + +'''# Function to extract and parse JSON response +def extract_llm_response(response): + print("response:", response) + try: + raw_message_content = response["choices"][0]["message"]["content"] + print("raw_message_content:", raw_message_content) + # Clean and parse the JSON content + cleaned_content = raw_message_content.replace("`", "").split("json")[-1] + + # for debugging + #if debug: + # print("cleaned:", '-'*50) + # print(cleaned_content.strip()) + + def parse_json_content(cleaned_content: str): + """ + Parses the cleaned content to extract valid JSON data. + + Args: + cleaned_content (str): The raw content containing JSON data. + + Returns: + dict or list: The parsed JSON object. + """ + import re + + # Step 1: Strip unwanted characters and clean the content + cleaned_content = cleaned_content.strip() + + # Step 2: Use regex to extract only the valid JSON block (e.g., starts with [ or {) + json_match = re.search(r"(\{.*\}|\[.*\])", cleaned_content, re.DOTALL) + + if not json_match: + raise ValueError("No valid JSON found in the content.") + + # Step 3: Extract and parse the valid JSON + valid_json = json_match.group(0) # Extract matched JSON block + try: + extracted_data = json.loads(valid_json) + except json.JSONDecodeError as e: + raise ValueError(f"Failed to decode JSON. Error: {e}\nContent:\n{valid_json}") + + return extracted_data + + parsed_data = parse_json_content(cleaned_content=cleaned_content) + print("parsed_data:",parsed_data) + print("debug:", parsed_data.get("objective", "")) + + #parsed_data = json.loads(cleaned_content) + return parsed_data.get("objective", ""), parsed_data.get("key_results", []) + except Exception as e: + st.error(f"Error parsing API response: {e}") + return "", []''' + +import json +import re + +def parse_json_content(cleaned_content: str): + """ + Parses the cleaned content to extract valid JSON data. + + Args: + cleaned_content (str): The raw content containing JSON data. + + Returns: + dict or list: The parsed JSON object. + """ + import re + + # Step 1: Strip unwanted characters and clean the content + cleaned_content = cleaned_content.strip() + + # Step 2: Use regex to extract only the valid JSON block (e.g., starts with [ or {) + json_match = re.search(r"(\{.*\}|\[.*\])", cleaned_content, re.DOTALL) + + if not json_match: + raise ValueError("No valid JSON found in the content.") + + # Step 3: Extract and parse the valid JSON + valid_json = json_match.group(0) # Extract matched JSON block + try: + extracted_data = json.loads(valid_json) + except json.JSONDecodeError as e: + raise ValueError(f"Failed to decode JSON. Error: {e}\nContent:\n{valid_json}") + + return extracted_data + +# Function to extract and parse JSON response +def extract_llm_response(response): + """ + Extracts and parses the JSON response from the API. + + Args: + response (dict): The API response containing a hint and proposals. + + Returns: + tuple: A tuple containing the objective (str), key results (list), and hint (str). + """ + print("RESPONSE:",response) + + raw_message_content = response["choices"][0]["message"]["content"] + print("raw_message_content:", raw_message_content) + # Clean and parse the JSON content + cleaned_content = raw_message_content.replace("`", "").split("json")[-1] + print("cleaned content", cleaned_content) + + parsed_data = parse_json_content(cleaned_content=cleaned_content) + print("parsed_data:",parsed_data) + + hint = parsed_data.get("hint", "") + + proposals = parsed_data.get("proposals", []) + + if proposals: + # Extract the first proposal's objective and key results + first_proposal = proposals[0] # Get the first proposal (assuming it's a list) + objective = first_proposal.get("objective", "") + key_results = first_proposal.get("key_results", []) + else: + objective = "" + key_results = [] + + #print("debug:", parsed_data.get("objective", "")) + + return objective, key_results, hint + + #try: + # Extract hint from the response + hint = response.get("hint", "") + + # Extract proposals from the response + proposals = response.get("proposals", []) + print("hint:", hint) + print("proposals:", proposals) + + # Check if proposals are available + if proposals: + # Extract the first proposal's objective and key results + first_proposal = proposals[0] # Get the first proposal (assuming it's a list) + objective = first_proposal.get("objective", "") + key_results = first_proposal.get("key_results", []) + else: + objective = "" + key_results = [] + + # Log parsed data for debugging + print("parsed_data:", {"objective": objective, "key_results": key_results, "hint": hint}) + + return objective, key_results, hint + + #except Exception as e: + # print(f"Error parsing API response: {e}") + # return "", [], ""