added parameters to the proposer #3

Merged
khanhdinh merged 1 commits from add-parameters-and-simplify-code into main 2025-01-03 14:03:24 +00:00
9 changed files with 187 additions and 29 deletions
Showing only changes of commit 6f09b9fe0b - Show all commits

16
api.py
View File

@ -5,7 +5,8 @@ from dotenv import load_dotenv
from settings import load_settings from settings import load_settings
from utils import construct_prompt from utils import construct_prompt
from config import INPUT_TEMPLATE, SYSTEM_PROMPT, PROMPT_TEMPLATE #from config import SYSTEM_PROMPT, PROMPT_TEMPLATE
from config.config import INPUT_EXAMPLE, SYSTEM_PROMPT, PROMPT_TEMPLATE
# Load API key from .env file # Load API key from .env file
load_dotenv() load_dotenv()
@ -17,13 +18,12 @@ if not api_key:
api_url = "https://genai.dev.odp.lhgroup.de/openai/deployments/gpt-4-turbo/chat/completions?api-version=2023-07-01-preview" 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): def fetch_okrs(user_input: str, okr_cycle: str = 'JAN 25 to APR 25', num_key_results: int=3):
#settings = load_settings() user_prompt = construct_prompt(
prompt_template=PROMPT_TEMPLATE,
#system_prompt = settings["system_prompt"] user_input=user_input,
#input_template = settings["input_template"] okr_cycle=okr_cycle,
num_key_results=num_key_results)
user_prompt = construct_prompt(prompt_template=PROMPT_TEMPLATE, user_input=user_input)
headers = {"api-key": api_key, "Content-Type": "application/json"} headers = {"api-key": api_key, "Content-Type": "application/json"}
body = { body = {

View File

@ -82,8 +82,8 @@ The json could be structured like this:
PROMPT_TEMPLATE = """ PROMPT_TEMPLATE = """
Please help us in defining proper OKRs. Please help us in defining proper OKRs.
Here is what we have thought about and we would like to phrase an OKR with maximum 5 key results. Here is what we have thought about and we would like to phrase an OKR with {num_key_results} key results.
The next OKR cycle is from JAN 2025 till APR 2025. The next OKR cycle is from {okr_cycle}
this is the user input: this is the user input:
{user_input} {user_input}
@ -99,8 +99,6 @@ We would like to improve this, to have a better steering function, e.g. by addin
To achieve this, we need to develop a bonus concept which is viable, feasible and desirable. 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 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. 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 for user input
@ -130,3 +128,9 @@ team = [
'Michelle Wehrli', 'Michelle Wehrli',
'Johannes Otto' 'Johannes Otto'
] ]
cycle_definitions = {
'Cycle1': 'JAN 2025 to APR 2025',
'Cycle2': 'MAY 2025 to AUG 2025',
'Cycle3': 'SEP 2025 to DEC 2025'
}

0
config/__init__.py Normal file
View File

69
config/config.py Normal file
View File

@ -0,0 +1,69 @@
cycle_definitions = {
'Cycle1': 'JAN 2025 to APR 2025',
'Cycle2': 'MAY 2025 to AUG 2025',
'Cycle3': 'SEP 2025 to DEC 2025'
}
SYSTEM_PROMPT = """
You are an OKR coach who helps beginners create well-phrased OKRs (Objectives and Key Results).
Your goal is to provide a proposal for an OKR based on user input, formatted as JSON.
If the input is unclear or incomplete, include a hint with specific questions or suggestions
to help improve the input.
Always follow this format for your response:
{
"hint": "Suggestions or questions to improve the input, if needed. If no hint is needed, return an empty string.",
"proposal": [
{
"objective": "A clear and concise objective variant",
"key_results": ["Key result 1", "Key result 2", "... up to 5 key results"]
},
...
]
}
Keep your responses concise, actionable, and aligned with OKR best practices. If you need more context from the user, ask for it in the `hint`.
Always provide a 'hint' how to further improve the user input in order to get better results.
"""
# 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 {num_key_results} key results.
The next OKR cycle is from {okr_cycle}
this is the user input:
{user_input}
Please provide the response in json format.
"""
INPUT_EXAMPLE = """
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.
"""
team = [
'Khanh Dinh',
'Roberto Renna',
'Suat Sonkur',
'Sven Eichmeyer',
'Robin Plitzko',
'Vikas Rathore',
'Nadia Kartascheff',
'Andreas Muheim',
'Christopher Koch',
'Andreas Pluess',
'Manfred Hahn',
'Michaela Schumacher',
'Sandra Pastoor',
'Mareen Fox Rogers',
'Michelle Wehrli',
'Johannes Otto'
]

View File

@ -1,28 +1,40 @@
import streamlit as st import streamlit as st
from api import fetch_okrs from api import fetch_okrs
from config import INPUT_TEMPLATE, team #from config import INPUT_TEMPLATE, team, cycle_definitions
from config.config import INPUT_EXAMPLE, cycle_definitions, team
from utils import extract_llm_response from utils import extract_llm_response
def proposer_page(): import json
# Streamlit App Layout
st.title("AO/PM OKR Proposer") from ui.parameters import section_parameters
def section_user_input():
# section for input parameters
section_parameters()
# Input Section and Buttons Row # Input Section and Buttons Row
st.subheader("Enter your idea or goal:") st.subheader("Enter your idea or goal:")
user_input = st.text_area( user_input = st.text_area(
"Input your idea here:", "Input your idea here:",
value=st.session_state.get("user_input", INPUT_TEMPLATE.strip()), value=st.session_state.get("user_input", INPUT_EXAMPLE.strip()),
height=300, height=300,
) )
st.session_state['user_input'] = user_input
generate_okrs_clicked = st.button("Generate OKR Proposal") generate_okrs_clicked = st.button("Generate OKR Proposal")
if generate_okrs_clicked: if generate_okrs_clicked:
print("session_state:", st.session_state)
if not user_input.strip(): if not user_input.strip():
st.warning("Please provide some input before generating OKRs.") st.warning("Please provide some input before generating OKRs.")
else: else:
with st.spinner("Generating OKRs..."): with st.spinner("Generating OKRs..."):
response = fetch_okrs(user_input=user_input) response = fetch_okrs(
user_input=user_input,
okr_cycle=st.session_state['okr_cycle'],
num_key_results=st.session_state['num_key_results'])
if response: if response:
# Extract Objective and Key Results from response # Extract Objective and Key Results from response
objective, key_results, hint = extract_llm_response(response) objective, key_results, hint = extract_llm_response(response)
@ -30,6 +42,7 @@ def proposer_page():
st.session_state["key_results"] = key_results st.session_state["key_results"] = key_results
st.session_state["hint"] = hint st.session_state["hint"] = hint
def section_okr_proposals():
# Display Results Only if an OKR Has Been Generated # Display Results Only if an OKR Has Been Generated
if "objective" in st.session_state and "key_results" in st.session_state: if "objective" in st.session_state and "key_results" in st.session_state:
# Ensure team members exist in session state # Ensure team members exist in session state
@ -52,7 +65,8 @@ def proposer_page():
"Select Responsibles for Objective:", "Select Responsibles for Objective:",
options=team_members, options=team_members,
default=[], default=[],
key="responsibles_objective" key="responsibles_objective",
max_selections=1
) )
# Display Key Results with Responsibles Below Each One # Display Key Results with Responsibles Below Each One
@ -61,12 +75,18 @@ def proposer_page():
responsibles_for_key_results = [] responsibles_for_key_results = []
for i, kr in enumerate(st.session_state["key_results"], start=1): 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}") kr_text = st.text_area(
responsible_for_kr = st.multiselect( label=f'KR{i}',
f"Select Responsibles for Key Result {i}:", value=kr,
key=f"proposal_kr_{i}"
)
#kr_text = st.text_area(f"Key Result {i}:", value=kr, key=f"kr_{i}")
responsible_for_kr = st.pills(
label=f"Select Responsibles for Key Result {i}:",
options=team_members, options=team_members,
default=[], default=[],
key=f"responsibles_kr_{i}" key=f"responsibles_kr_{i}",
selection_mode='multi'
) )
key_result_boxes.append(kr_text) key_result_boxes.append(kr_text)
@ -75,7 +95,7 @@ def proposer_page():
# Finalize Button Center-Aligned # Finalize Button Center-Aligned
if st.button("Finalize"): if st.button("Finalize"):
finalized_objective = objective_text.strip() finalized_objective = objective_text.strip()
finalized_key_results = [st.session_state[f"kr_{i+1}"].strip() for i in range(len(key_result_boxes))] finalized_key_results = [st.session_state[f"proposal_kr_{i+1}"].strip() for i in range(len(key_result_boxes))]
# Append initials of responsibles to Objective and Key Results # Append initials of responsibles to Objective and Key Results
responsibles_list_objective = responsible_for_objective responsibles_list_objective = responsible_for_objective
@ -83,6 +103,7 @@ def proposer_page():
initials_str_objective = ", ".join(initials_objective) initials_str_objective = ", ".join(initials_objective)
finalized_objective = f"{initials_str_objective} {finalized_objective}" finalized_objective = f"{initials_str_objective} {finalized_objective}"
st.session_state['finalized_objective'] = finalized_objective
finalized_key_results_with_initials = [] finalized_key_results_with_initials = []
for i, kr in enumerate(finalized_key_results): for i, kr in enumerate(finalized_key_results):
@ -90,6 +111,7 @@ def proposer_page():
initials_kr = [f"{''.join([part[0] for part in name.split()]).upper()}" for name in responsibles_list_kr] initials_kr = [f"{''.join([part[0] for part in name.split()]).upper()}" for name in responsibles_list_kr]
initials_str_kr = ", ".join(initials_kr) initials_str_kr = ", ".join(initials_kr)
finalized_key_results_with_initials.append(f"KR{i+1}: [{initials_str_kr}] {kr}") finalized_key_results_with_initials.append(f"KR{i+1}: [{initials_str_kr}] {kr}")
st.session_state['finalized_key_results_with_initials'] = finalized_key_results_with_initials
# Display finalized data in non-editable format (full width) # Display finalized data in non-editable format (full width)
st.subheader("Finalized Objective:") st.subheader("Finalized Objective:")
@ -112,3 +134,33 @@ def proposer_page():
language=None, language=None,
wrap_lines=True wrap_lines=True
) )
export_data_as_json()
def export_data_as_json():
# Prepare data for export
finalized_key_results_with_initials = st.session_state.get('finalized_key_results_with_initials')
export_data = {
"user_input": st.session_state.get('user_input'),
"finalized_objective": st.session_state.get('finalized_objective'),
"key_results": [kr.split(':')[-1].strip() for kr in finalized_key_results_with_initials]
}
# Convert data to JSON string
json_data = json.dumps(export_data, indent=4)
# Add a download button
st.download_button(
label="Download Finalized OKR",
data=json_data,
file_name="okr_data.json",
mime="application/json"
)
def proposer_page():
# Streamlit App Layout
st.title("AO/PM OKR Proposer")
section_user_input()
section_okr_proposals()

View File

@ -1,7 +1,8 @@
import streamlit as st import streamlit as st
import json import json
import os import os
from config import SYSTEM_PROMPT, INPUT_TEMPLATE, PROMPT_TEMPLATE, team from config.config import SYSTEM_PROMPT, INPUT_EXAMPLE, PROMPT_TEMPLATE, team
''' '''
def settings_page(): def settings_page():
st.title("Settings") st.title("Settings")
@ -100,7 +101,7 @@ def load_settings():
# Default settings # Default settings
return { return {
"system_prompt": SYSTEM_PROMPT, "system_prompt": SYSTEM_PROMPT,
"input_template": INPUT_TEMPLATE, "input_template": INPUT_EXAMPLE,
"prompt_template": PROMPT_TEMPLATE, "prompt_template": PROMPT_TEMPLATE,
"num_key_results": 4, "num_key_results": 4,
"team_members": ["Khanh Dinh", "Robin Plitzko", "Roberto Renna"], "team_members": ["Khanh Dinh", "Robin Plitzko", "Roberto Renna"],

0
ui/__init__.py Normal file
View File

24
ui/parameters.py Normal file
View File

@ -0,0 +1,24 @@
import streamlit as st
from config.config import cycle_definitions
def section_parameters():
st.subheader(body='Parameters for the OKR')
# Requested Parameter - Number of Key Results
num_key_results = st.number_input(
label='Please provide the number of key results',
min_value=1,
max_value=5,
key='num_key_results',
value=3
)
# Requested Parameter - OKR Cycle
options = [f"{key} ({value})" for key, value in cycle_definitions.items()]
okr_cycle = st.segmented_control(
label="Please select the OKR cycle",
options=options,
selection_mode="single",
default=options[0]
)
st.session_state['okr_cycle'] = okr_cycle

View File

@ -3,8 +3,16 @@ import streamlit as st
import re import re
# Function to construct the prompt # Function to construct the prompt
def construct_prompt(prompt_template: str, user_input: str) -> str: def construct_prompt(
return prompt_template.format(user_input=user_input) prompt_template: str,
user_input: str,
okr_cycle: str = 'JAN 25 to APR 25',
num_key_results: int = 3 ) -> str:
return prompt_template.format(
user_input=user_input,
okr_cycle=okr_cycle,
num_key_results=num_key_results)
def parse_json_content(cleaned_content: str): def parse_json_content(cleaned_content: str):