From aa21f41d0b45874b2a9590f356dfbb820126772a Mon Sep 17 00:00:00 2001 From: Salman Paracha Date: Tue, 22 Oct 2024 17:01:23 -0700 Subject: [PATCH] committing to merge with main --- demos/hr_agent/Dockerfile | 1 + demos/hr_agent/arch_config.yaml | 30 ++++++--- demos/hr_agent/docker-compose.yaml | 2 + demos/hr_agent/main.py | 100 ++++++++++++++++------------- demos/hr_agent/requirements.txt | 2 + demos/hr_agent/workforce_data.json | 29 +++++++++ 6 files changed, 111 insertions(+), 53 deletions(-) create mode 100644 demos/hr_agent/workforce_data.json diff --git a/demos/hr_agent/Dockerfile b/demos/hr_agent/Dockerfile index 503ffeff..c97fb497 100644 --- a/demos/hr_agent/Dockerfile +++ b/demos/hr_agent/Dockerfile @@ -5,6 +5,7 @@ FROM base AS builder WORKDIR /src COPY requirements.txt /src/ +COPY workforce_data.json /src/ RUN pip install --prefix=/runtime --force-reinstall -r requirements.txt COPY . /src diff --git a/demos/hr_agent/arch_config.yaml b/demos/hr_agent/arch_config.yaml index fa6a499b..a8db1689 100644 --- a/demos/hr_agent/arch_config.yaml +++ b/demos/hr_agent/arch_config.yaml @@ -27,11 +27,17 @@ system_prompt: | You are a HR agent assistant that helps HR decision makers with reporting and workfoce planning. Nothing else. Please stay on topic of HR. prompt_targets: - - name: headcount - description: Get headcount data for a region by staffing type + - name: hr_qa endpoint: name: app_server - path: /agent/headcount + path: /agent/hr_qa + description: Handle general Q/A related to HR. + default: true + - name: workforce + description: Get workforce data like headcount and satisfacton levels by region and staffing type + endpoint: + name: app_server + path: /agent/workforce parameters: - name: staffing_type type: str @@ -40,10 +46,18 @@ prompt_targets: - name: region type: str required: true - description: the geographical region for which you want headcount data. - - name: hr_qa + description: the geographical region for which you want workforce data. + - name: point_in_time + type: int + required: false + description: a point in time for which to retrieve data. For e.g 0 days ago, 30 days ago, etc. + - name: slack_message endpoint: name: app_server - path: /agent/hr_qa - description: Handle general Q/A related to HR. - default: true + path: /agent/slack_message + description: sends a slack message on a channel + parameters: + - name: slack_message + type: string + required: true + description: the message that should be sent to a slack channel diff --git a/demos/hr_agent/docker-compose.yaml b/demos/hr_agent/docker-compose.yaml index 7ea41173..2c0596fc 100644 --- a/demos/hr_agent/docker-compose.yaml +++ b/demos/hr_agent/docker-compose.yaml @@ -3,6 +3,8 @@ services: build: context: . dockerfile: Dockerfile + environment: + - SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN:?error} ports: - "18083:80" healthcheck: diff --git a/demos/hr_agent/main.py b/demos/hr_agent/main.py index 8a78dd01..1b4bd563 100644 --- a/demos/hr_agent/main.py +++ b/demos/hr_agent/main.py @@ -1,72 +1,82 @@ +import os +import json +import pandas as pd from fastapi import FastAPI, HTTPException from pydantic import BaseModel, Field -from typing import List, Optional +from typing import Optional from enum import Enum -import re +from slack_sdk import WebClient +from slack_sdk.errors import SlackApiError app = FastAPI() +workforce_data_df = None - -class StaffingType(Enum): - FTE = "fte" - AGENCY = "agency" - CONTRACT = "contract" - - -class RegionType(Enum): - ASIA = "asia" - EUROPE = "europe" - AMERICAS = "americas" +with open("workforce_data.json") as file: + workforce_data = json.load(file) + workforce_data_df = pd.json_normalize( + workforce_data, record_path=["regions"], meta=["point_in_time", "satisfaction"] + ) # Define the request model -class HeadcountRequest(BaseModel): - region: RegionType - staffing_type: StaffingType - - -class HeadcountResponseSummary(BaseModel): +class WorkforceRequset(BaseModel): region: str - headcount: int staffing_type: str + point_in_time: Optional[int] = None -HEADCOUNT = { - RegionType.ASIA: { - StaffingType.CONTRACT: 100, - StaffingType.FTE: 150, - StaffingType.AGENCY: 2000, - }, - RegionType.EUROPE: { - StaffingType.CONTRACT: 80, - StaffingType.FTE: 120, - StaffingType.AGENCY: 2500, - }, - RegionType.AMERICAS: { - StaffingType.CONTRACT: 90, - StaffingType.FTE: 200, - StaffingType.AGENCY: 3000, - }, -} +class SlackRequest(BaseModel): + slack_message: str + + +class WorkforceResponse(BaseModel): + region: str + staffing_type: str + headcount: int + satisfaction: float # Post method for device summary -@app.post("/agent/headcount") -def get_headcount(request: HeadcountRequest): +@app.post("/agent/workforce") +def get_workforce(request: WorkforceRequset): """ - Endpoint to headcount data by region, staffing type over time range + Endpoint to workforce data by region, staffing type at a given point in time. """ - headcount = HEADCOUNT[request.region][request.staffing_type] + region = request.region.lower() + staffing_type = request.staffing_type.lower() + point_in_time = request.point_in_time if request.point_in_time else 0 response = { - "region": request.region.value, - "staffing_type": f"Staffing agency: {request.staffing_type}", - "headcount": f"Headcount: {headcount}", + "region": region, + "staffing_type": f"Staffing agency: {staffing_type}", + "headcount": f"Headcount: {int(workforce_data_df[(workforce_data_df['region']==region) & (workforce_data_df['point_in_time']==point_in_time)][staffing_type].values[0])}", + "satisfaction": f"Satisifaction: {float(workforce_data_df[(workforce_data_df['region']==region) & (workforce_data_df['point_in_time']==point_in_time)][satisfaction].values[0])}", } - return response +@app.post("/agent/slack_message") +def send_slack_message(request: SlackRequest): + """ + Endpoint that sends slack message + """ + slack_message = request.slack_message + + # Load the bot token from an environment variable or replace it directly + slack_token = os.getenv( + "SLACK_BOT_TOKEN" + ) # Replace with your token if needed: 'xoxb-your-token' + client = WebClient(token=slack_token) + channel = "hr_agent_demo" + + try: + # Send the message + response = client.chat_postMessage(channel=channel, text=slack_message) + return f"Message sent to {channel}: {response['message']['text']}" + except SlackApiError as e: + print(f"Error sending message: {e.response['error']}") + + @app.post("/agent/hr_qa") async def general_hr_qa(): """ diff --git a/demos/hr_agent/requirements.txt b/demos/hr_agent/requirements.txt index 77e7584c..3068f77e 100644 --- a/demos/hr_agent/requirements.txt +++ b/demos/hr_agent/requirements.txt @@ -1,4 +1,6 @@ fastapi uvicorn pydantic +slack-sdk typing +pandas diff --git a/demos/hr_agent/workforce_data.json b/demos/hr_agent/workforce_data.json new file mode 100644 index 00000000..e28654d7 --- /dev/null +++ b/demos/hr_agent/workforce_data.json @@ -0,0 +1,29 @@ +[ + { + "point_in_time": 0, + "regions": [ + { "region": "asia", "contract": 100, "fte": 150, "agency": 2000 }, + { "region": "europe", "contract": 80, "fte": 120, "agency": 2500 }, + { "region": "americas", "contract": 90, "fte": 200, "agency": 3100 } + ], + "satisfaction": 3.5 + }, + { + "point_in_time": 30, + "regions": [ + { "region": "asia", "contract": 110, "fte": 155, "agency": 1000 }, + { "region": "europe", "contract": 85, "fte": 130, "agency": 1600 }, + { "region": "americas", "contract": 95, "fte": 210, "agency": 3100 } + ], + "satisfaction": 4.0 + }, + { + "point_in_time": 60, + "regions": [ + { "region": "asia", "contract": 115, "fte": 160, "agency": 500 }, + { "region": "europe", "contract": 90, "fte": 140, "agency": 700 }, + { "region": "americas", "contract": 100, "fte": 220, "agency": 1200 } + ], + "satisfaction": 4.7 + } +]