Compare commits

...

2 Commits

Author SHA1 Message Date
04e67ccb2a Merge pull request 'added parameters to the proposer' (#3) from add-parameters-and-simplify-code into main
Reviewed-on: #3
2025-01-03 14:03:23 +00:00
Khanh Dinh
6f09b9fe0b added parameters to the proposer
started to refactor code to make it cleaner
2025-01-03 15:01:47 +01:00
9 changed files with 187 additions and 29 deletions

16
api.py
View File

@ -5,7 +5,8 @@ from dotenv import load_dotenv
from settings import load_settings
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_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"
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=PROMPT_TEMPLATE, user_input=user_input)
def fetch_okrs(user_input: str, okr_cycle: str = 'JAN 25 to APR 25', num_key_results: int=3):
user_prompt = construct_prompt(
prompt_template=PROMPT_TEMPLATE,
user_input=user_input,
okr_cycle=okr_cycle,
num_key_results=num_key_results)
headers = {"api-key": api_key, "Content-Type": "application/json"}
body = {

View File

@ -82,8 +82,8 @@ The json could be structured like this:
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 maximum 5 key results.
The next OKR cycle is from JAN 2025 till APR 2025.
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}
@ -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.
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
@ -129,4 +127,10 @@ team = [
'Mareen Fox Rogers',
'Michelle Wehrli',
'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
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
def proposer_page():
# Streamlit App Layout
st.title("AO/PM OKR Proposer")
import json
from ui.parameters import section_parameters
def section_user_input():
# section for input parameters
section_parameters()
# 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()),
value=st.session_state.get("user_input", INPUT_EXAMPLE.strip()),
height=300,
)
st.session_state['user_input'] = user_input
generate_okrs_clicked = st.button("Generate OKR Proposal")
if generate_okrs_clicked:
print("session_state:", st.session_state)
if not user_input.strip():
st.warning("Please provide some input before generating OKRs.")
else:
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:
# Extract Objective and Key Results from 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["hint"] = hint
def section_okr_proposals():
# Display Results Only if an OKR Has Been Generated
if "objective" in st.session_state and "key_results" in st.session_state:
# Ensure team members exist in session state
@ -52,7 +65,8 @@ def proposer_page():
"Select Responsibles for Objective:",
options=team_members,
default=[],
key="responsibles_objective"
key="responsibles_objective",
max_selections=1
)
# Display Key Results with Responsibles Below Each One
@ -61,12 +75,18 @@ def proposer_page():
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.multiselect(
f"Select Responsibles for Key Result {i}:",
kr_text = st.text_area(
label=f'KR{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,
default=[],
key=f"responsibles_kr_{i}"
key=f"responsibles_kr_{i}",
selection_mode='multi'
)
key_result_boxes.append(kr_text)
@ -75,7 +95,7 @@ def proposer_page():
# Finalize Button Center-Aligned
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))]
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
responsibles_list_objective = responsible_for_objective
@ -83,6 +103,7 @@ def proposer_page():
initials_str_objective = ", ".join(initials_objective)
finalized_objective = f"{initials_str_objective} {finalized_objective}"
st.session_state['finalized_objective'] = finalized_objective
finalized_key_results_with_initials = []
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_str_kr = ", ".join(initials_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)
st.subheader("Finalized Objective:")
@ -112,3 +134,33 @@ def proposer_page():
language=None,
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 json
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():
st.title("Settings")
@ -100,7 +101,7 @@ def load_settings():
# Default settings
return {
"system_prompt": SYSTEM_PROMPT,
"input_template": INPUT_TEMPLATE,
"input_template": INPUT_EXAMPLE,
"prompt_template": PROMPT_TEMPLATE,
"num_key_results": 4,
"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
# Function to construct the prompt
def construct_prompt(prompt_template: str, user_input: str) -> str:
return prompt_template.format(user_input=user_input)
def construct_prompt(
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):