Skip to content

Commit

Permalink
Feature: Add Ruff GitHub Workflow (#12)
Browse files Browse the repository at this point in the history
* feat: added ruff linter with auto fix

Signed-off-by: error9098x <provantablack@gmail.com>

* fix: added ruff.toml and replaced with pyproject.toml

Signed-off-by: error9098x <provantablack@gmail.com>

* fix: rename the workflow

Signed-off-by: error9098x <provantablack@gmail.com>

* fix ruff ci

Signed-off-by: Jack Luar <39641663+luarss@users.noreply.github.com>

* fix ruff

Signed-off-by: Jack Luar <39641663+luarss@users.noreply.github.com>

* cleanup workflow

Signed-off-by: Jack Luar <39641663+luarss@users.noreply.github.com>

* update docs

Signed-off-by: Jack Luar <39641663+luarss@users.noreply.github.com>

* Update ruff_linter.yml

Signed-off-by: Jack Luar <39641663+luarss@users.noreply.github.com>

* Update ruff_linter.yml

Signed-off-by: Song Luar <luar.shuisong@gmail.com>

* Update api.py

Signed-off-by: Song Luar <luar.shuisong@gmail.com>

---------

Signed-off-by: error9098x <provantablack@gmail.com>
Signed-off-by: Jack Luar <39641663+luarss@users.noreply.github.com>
Signed-off-by: Song Luar <luar.shuisong@gmail.com>
Co-authored-by: Jack Luar <39641663+luarss@users.noreply.github.com>
Co-authored-by: Song Luar <luar.shuisong@gmail.com>
  • Loading branch information
3 people authored Jul 9, 2024
1 parent 8ec4b28 commit f64c791
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 55 deletions.
26 changes: 26 additions & 0 deletions .github/workflows/ruff_linter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: Ruff

on: [push, pull_request]

jobs:
lint:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.12

- name: Install Ruff
run: |
python -m pip install --upgrade pip
pip install ruff==0.5.1
- name: Run Ruff Check
run: |
ruff check
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,12 @@ Open [http://0.0.0.0:8000/docs](http://0.0.0.0:8000/docs) for the API docs.

## Tests

1) Ruff (TODO)
1) Ruff: Auto-formatter and checker for python

```
pip install ruff
ruff format && ruff check
```

2) Mypy: A static type checker for python

Expand Down
1 change: 1 addition & 0 deletions evaluation/human_evaluation/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
read_question_and_description,
)


def main() -> None:
load_dotenv()

Expand Down
45 changes: 36 additions & 9 deletions evaluation/human_evaluation/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,31 +108,58 @@ def update_env_file(updates: dict[str, str]) -> None:
with open(env_file, "w") as file:
file.writelines(new_lines)


def main() -> None:
parser = argparse.ArgumentParser(description="Create Google Form and/or Google Sheet, and update .env file.")
parser.add_argument('--create-form', action='store_true', help='Create a Google Form')
parser.add_argument('--create-sheet', action='store_true', help='Create a Google Sheet')
parser.add_argument('--user-email', type=str, required=True, help="Email address to share the created resources with")
parser.add_argument('--form-title', type=str, default='OR Assistant Feedback Form', help='Title for the Google Form')
parser.add_argument('--sheet-title', type=str, default='OR Assistant Evaluation Sheet', help='Title for the Google Sheet')
parser = argparse.ArgumentParser(
description="Create Google Form and/or Google Sheet, and update .env file."
)
parser.add_argument(
"--create-form", action="store_true", help="Create a Google Form"
)
parser.add_argument(
"--create-sheet", action="store_true", help="Create a Google Sheet"
)
parser.add_argument(
"--user-email",
type=str,
required=True,
help="Email address to share the created resources with",
)
parser.add_argument(
"--form-title",
type=str,
default="OR Assistant Feedback Form",
help="Title for the Google Form",
)
parser.add_argument(
"--sheet-title",
type=str,
default="OR Assistant Evaluation Sheet",
help="Title for the Google Sheet",
)

args = parser.parse_args()

updates = {}

if args.create_form:
form_id = create_google_form(args.form_title, args.user_email)
print(f"Form created successfully. View form at: https://docs.google.com/forms/d/{form_id}/edit")
print(
f"Form created successfully. View form at: https://docs.google.com/forms/d/{form_id}/edit"
)
updates["GOOGLE_FORM_ID"] = form_id

if args.create_sheet:
sheet_id = create_google_sheet(args.sheet_title, args.user_email)
print(f"Google Sheet created successfully. View sheet at: https://docs.google.com/spreadsheets/d/{sheet_id}/edit")
print(
f"Google Sheet created successfully. View sheet at: https://docs.google.com/spreadsheets/d/{sheet_id}/edit"
)
updates["GOOGLE_SHEET_ID"] = sheet_id

if updates:
update_env_file(updates)
print("The .env file has been updated with the new IDs.")


if __name__ == "__main__":
main()
main()
2 changes: 1 addition & 1 deletion evaluation/human_evaluation/utils/sheets.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,4 @@ def write_responses(responses: list[str], row_numbers: list[int]) -> int:
except HttpError as error:
st.error("Failed to write responses to the Google Sheet.")
st.error(f"An error occurred: {error}")
return 0
return 0
4 changes: 2 additions & 2 deletions evaluation/human_evaluation/utils/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def update_gform(questions_descriptions: list[dict[str, str]]) -> None:
},
}
requests.append(update_request)
else: #If update is not required, create a new question and description
else: # If update is not required, create a new question and description
create_request = {
"createItem": {
"item": {
Expand Down Expand Up @@ -177,4 +177,4 @@ def update_gform(questions_descriptions: list[dict[str, str]]) -> None:
except HttpError as error:
st.error(f"An error occurred while updating the form: {error}")
except Exception as e:
st.error(f"An unexpected error occurred: {e}")
st.error(f"An unexpected error occurred: {e}")
58 changes: 39 additions & 19 deletions frontend/streamlit_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@
from dotenv import load_dotenv
from typing import Callable, Any


def measure_response_time(func: Callable[..., Any]) -> Callable[..., tuple[Any, float]]:
def wrapper(*args: Any, **kwargs: Any) -> tuple[Any, float]:
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
response_time = (end_time - start_time) * 1000 # In milliseconds
return result, response_time

return wrapper


@measure_response_time
def response_generator(user_input: str) -> tuple[tuple[str, str]]:
"""
Expand Down Expand Up @@ -61,6 +64,7 @@ def response_generator(user_input: str) -> tuple[tuple[str, str]]:
st.error(f"Request failed: {e}")
return None, None


def fetch_endpoints() -> tuple[str, list]:
base_url = os.getenv("CHAT_ENDPOINT", "http://localhost:8000")
url = f"{base_url}/chains/listAll"
Expand All @@ -73,6 +77,7 @@ def fetch_endpoints() -> tuple[str, list]:
st.error(f"Failed to fetch endpoints: {e}")
return base_url, []


def main() -> None:
load_dotenv()

Expand All @@ -85,20 +90,20 @@ def main() -> None:
st.title("OR Assistant")

base_url, endpoints = fetch_endpoints()

selected_endpoint = st.selectbox(
"Select preferred architecture",
options=endpoints,
index=0,
format_func=lambda x: x.split('/')[-1].capitalize()
format_func=lambda x: x.split("/")[-1].capitalize(),
)

if 'selected_endpoint' not in st.session_state:
if "selected_endpoint" not in st.session_state:
st.session_state.selected_endpoint = selected_endpoint
else:
st.session_state.selected_endpoint = selected_endpoint
if 'base_url' not in st.session_state:

if "base_url" not in st.session_state:
st.session_state.base_url = base_url

if not os.getenv("CHAT_ENDPOINT"):
Expand Down Expand Up @@ -130,9 +135,13 @@ def main() -> None:
st.markdown(user_input)

response_tuple, response_time = response_generator(user_input)

# Validate the response tuple
if response_tuple and isinstance(response_tuple, tuple) and len(response_tuple) == 2:
if (
response_tuple
and isinstance(response_tuple, tuple)
and len(response_tuple) == 2
):
response, sources = response_tuple
if response is not None:
response_buffer = ""
Expand All @@ -141,26 +150,32 @@ def main() -> None:
message_placeholder = st.empty()

response_buffer = ""
for chunk in response.split(' '):
response_buffer += chunk + ' '
if chunk.endswith('\n'):
response_buffer += ' '
for chunk in response.split(" "):
response_buffer += chunk + " "
if chunk.endswith("\n"):
response_buffer += " "
message_placeholder.markdown(response_buffer)
time.sleep(0.05)

message_placeholder.markdown(response_buffer)

response_time_text = f"Response Time: {response_time / 1000:.2f} seconds"
response_time_colored = f":{'green' if response_time < 5000 else 'orange' if response_time < 10000 else 'red'}[{response_time_text}]"

response_time_text = (
f"Response Time: {response_time / 1000:.2f} seconds"
)
response_time_colored = f":{"green" if response_time < 5000 else "orange" if response_time < 10000 else "red"}[{response_time_text}]"
st.markdown(response_time_colored)
st.session_state.chat_history.append(
{"content": response_buffer, "role": "ai"})
st.session_state.chat_history.append({
"content": response_buffer,
"role": "ai",
})

if sources:
with st.expander("Sources:"):
try:
if isinstance(sources, str):
cleaned_sources = sources.replace("{", "[").replace("}", "]")
cleaned_sources = sources.replace("{", "[").replace(
"}", "]"
)
parsed_sources = ast.literal_eval(cleaned_sources)
else:
parsed_sources = sources
Expand Down Expand Up @@ -193,10 +208,15 @@ def update_state() -> None:
"""
st.session_state.feedback_button = True

if st.button("Feedback", on_click=update_state) or st.session_state.feedback_button:
if (
st.button("Feedback", on_click=update_state)
or st.session_state.feedback_button
):
try:
show_feedback_form(
question_dict, st.session_state.metadata, st.session_state.chat_history
question_dict,
st.session_state.metadata,
st.session_state.chat_history,
)
except Exception as e:
st.error(f"Failed to load feedback form: {e}")
Expand Down
44 changes: 29 additions & 15 deletions frontend/utils/feedback.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

load_dotenv()


def get_sheet_title_by_gid(spreadsheet_metadata: dict, gid: int) -> Optional[str]:
"""
Get the sheet title by Sheet GID
Expand All @@ -25,6 +26,7 @@ def get_sheet_title_by_gid(spreadsheet_metadata: dict, gid: int) -> Optional[str
return sheet["properties"]["title"]
return None


def format_sources(sources: list[str]) -> str:
"""
Format the sources into a string suitable for Google Sheets.
Expand All @@ -39,6 +41,7 @@ def format_sources(sources: list[str]) -> str:
return "\n".join(sources)
return str(sources)


def format_context(context: list[str]) -> str:
"""
Format the context into a string suitable for Google Sheets.
Expand All @@ -53,13 +56,9 @@ def format_context(context: list[str]) -> str:
return "\n".join(context)
return str(context)


def submit_feedback_to_google_sheet(
question: str,
answer: str,
sources: str,
context: str,
issue: str,
version: str
question: str, answer: str, sources: str, context: str, issue: str, version: str
) -> None:
"""
Submit feedback to a specific Google Sheet.
Expand All @@ -81,18 +80,20 @@ def submit_feedback_to_google_sheet(
)

if not os.getenv("FEEDBACK_SHEET_ID"):
raise ValueError("The FEEDBACK_SHEET_ID environment variable is not set or is empty.")
raise ValueError(
"The FEEDBACK_SHEET_ID environment variable is not set or is empty."
)

if not os.getenv("RAG_VERSION"):
raise ValueError("The RAG_VERSION environment variable is not set or is empty.")

SERVICE_ACCOUNT_FILE = os.getenv("GOOGLE_CREDENTIALS_JSON")
SCOPE = [
service_account_file = os.getenv("GOOGLE_CREDENTIALS_JSON")
scope = [
"https://spreadsheets.google.com/feeds",
"https://www.googleapis.com/auth/drive",
]

creds = Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPE)
creds = Credentials.from_service_account_file(service_account_file, scopes=scope)
client = gspread.authorize(creds)

sheet_id = os.getenv("FEEDBACK_SHEET_ID")
Expand All @@ -107,9 +108,17 @@ def submit_feedback_to_google_sheet(
if sheet_title:
sheet = spreadsheet.worksheet(sheet_title)
timestamp = datetime.now(timezone.utc).isoformat()
formatted_sources = format_sources(sources)
formatted_context = format_context(context)
data_to_append = [question, answer, formatted_sources, formatted_context, issue, timestamp, version]
formatted_sources = format_sources(sources)
formatted_context = format_context(context)
data_to_append = [
question,
answer,
formatted_sources,
formatted_context,
issue,
timestamp,
version,
]

if not sheet.row_values(1):
sheet.format("A1:G1", {"textFormat": {"bold": True}})
Expand All @@ -131,7 +140,12 @@ def submit_feedback_to_google_sheet(
else:
st.sidebar.error(f"Sheet with GID {target_gid} not found.")

def show_feedback_form(questions: dict[str, int], metadata: dict[str, dict[str, str]], interactions: list[dict[str, str]]) -> None:

def show_feedback_form(
questions: dict[str, int],
metadata: dict[str, dict[str, str]],
interactions: list[dict[str, str]],
) -> None:
"""
Display feedback form in the sidebar.
Expand Down Expand Up @@ -174,4 +188,4 @@ def show_feedback_form(questions: dict[str, int], metadata: dict[str, dict[str,
st.session_state.submitted = True

if st.session_state.submitted:
st.sidebar.success("Thank you for your feedback!")
st.sidebar.success("Thank you for your feedback!")
Loading

0 comments on commit f64c791

Please sign in to comment.