From bc2e200a2b38c1ff39c1763a2496283f1579cf64 Mon Sep 17 00:00:00 2001 From: Khanh Dinh Date: Mon, 30 Dec 2024 23:15:03 +0100 Subject: [PATCH] initial version --- .env | 1 + .idea/.gitignore | 5 + .idea/OKR_Proposer.iml | 10 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 4 + .idea/modules.xml | 8 + .idea/workspace.xml | 69 ++++++ .vscode/launch.json | 7 + api.py | 39 ++++ app.py | 135 ++++++++++++ config.py | 58 +++++ main.py | 42 ++++ proposer.py | 116 ++++++++++ requirements.txt | 74 +++++++ settings.py | 202 ++++++++++++++++++ settings1.json | 11 + styles.py | 12 ++ test.ipynb | 42 ++++ utils.py | 160 ++++++++++++++ 19 files changed, 1001 insertions(+) create mode 100644 .env create mode 100644 .idea/.gitignore create mode 100644 .idea/OKR_Proposer.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/workspace.xml create mode 100644 .vscode/launch.json create mode 100644 api.py create mode 100644 app.py create mode 100644 config.py create mode 100644 main.py create mode 100644 proposer.py create mode 100644 requirements.txt create mode 100644 settings.py create mode 100644 settings1.json create mode 100644 styles.py create mode 100644 test.ipynb create mode 100644 utils.py 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 "", [], ""