refactor demos (#398)

This commit is contained in:
Salman Paracha 2025-02-07 18:45:42 -08:00 committed by GitHub
parent 2bd61d628c
commit b3c95a6698
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
93 changed files with 338 additions and 1042 deletions

View file

@ -0,0 +1,16 @@
FROM python:3.12 AS base
FROM base AS builder
WORKDIR /src
COPY requirements.txt /src/
RUN pip install --prefix=/runtime --force-reinstall -r requirements.txt
FROM python:3.12-slim AS output
COPY --from=builder /runtime /usr/local
WORKDIR /app
COPY . /app
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80", "--log-level", "info"]

View file

@ -0,0 +1,29 @@
# HR Agent Demo
This demo showcases how the **Arch** can be used to build an HR agent to manage workforce-related inquiries, workforce planning, and communication via Slack. It intelligently routes incoming prompts to the correct targets, providing concise and useful responses tailored for HR and workforce decision-making.
## Available Functions:
- **HR Q/A**: Handles general Q&A related to insurance policies.
- **Endpoint**: `/agent/hr_qa`
- **Workforce Data Retrieval**: Retrieves data related to workforce metrics like headcount, satisfaction, and staffing.
- **Endpoint**: `/agent/workforce`
- Parameters:
- `staffing_type` (str, required): Type of staffing (e.g., `contract`, `fte`, `agency`).
- `region` (str, required): Region for which the data is requested (e.g., `asia`, `europe`, `americas`).
- `point_in_time` (int, optional): Time point for data retrieval (e.g., `0 days ago`, `30 days ago`).
- **Initiate Policy**: Sends messages to a Slack channel
- **Endpoint**: `/agent/slack_message`
- Parameters:
- `slack_message` (str, required): The message content to be sent
# Starting the demo
1. Please make sure the [pre-requisites](https://github.com/katanemo/arch/?tab=readme-ov-file#prerequisites) are installed correctly
2. Start Arch
```sh
sh run_demo.sh
```
3. Navigate to http://localhost:18080/agent/chat
4. "Can you give me workforce data for asia?"

View file

@ -0,0 +1,62 @@
version: v0.1
listener:
address: 127.0.0.1
port: 8080 #If you configure port 443, you'll need to update the listener with tls_certificates
message_format: huggingface
# Centralized way to manage LLMs, manage keys, retry logic, failover and limits in a central way
llm_providers:
- name: OpenAI
provider_interface: openai
access_key: $OPENAI_API_KEY
model: gpt-4o-mini
default: true
# Arch creates a round-robin load balancing between different endpoints, managed via the cluster subsystem.
endpoints:
app_server:
# value could be ip address or a hostname with port
# this could also be a list of endpoints for load balancing
# for example endpoint: [ ip1:port, ip2:port ]
endpoint: host.docker.internal:18080
# max time to wait for a connection to be established
connect_timeout: 0.005s
# default system prompt used by all prompt targets
system_prompt: |
You are a Workforce assistant that helps on workforce planning and HR decision makers with reporting and workforce planning. Use following rules when responding,
- when you get data in json format, offer some summary but don't be too verbose
- be concise, to the point and do not over analyze the data
prompt_targets:
- name: workforce
description: Get workforce data like headcount and satisfaction levels by region and staffing type
endpoint:
name: app_server
path: /agent/workforce
http_method: POST
parameters:
- name: staffing_type
type: str
description: specific category or nature of employment used by an organization like fte, contract and agency
required: true
enum: [fte, contract, agency]
- name: region
type: str
required: true
description: Geographical region for which you want workforce data like asia, europe, americas.
- name: data_snapshot_days_ago
type: int
required: false
description: the snapshot day for which you want workforce data.
- name: slack_message
endpoint:
name: app_server
path: /agent/slack_message
http_method: POST
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

View file

@ -0,0 +1,29 @@
services:
api_server:
build:
context: .
environment:
- SLACK_BOT_TOKEN=${SLACK_BOT_TOKEN:-None}
- OPENAI_API_KEY=${OPENAI_API_KEY:?error}
- CHAT_COMPLETION_ENDPOINT=http://host.docker.internal:10000/v1
volumes:
- ./arch_config.yaml:/app/arch_config.yaml
ports:
- "18080:80"
healthcheck:
test: ["CMD", "curl" ,"http://localhost:80/healthz"]
interval: 5s
retries: 20
chatbot_ui:
build:
context: ../../shared/chatbot_ui
dockerfile: Dockerfile
ports:
- "18080:8080"
environment:
- CHAT_COMPLETION_ENDPOINT=http://host.docker.internal:10000/v1
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./arch_config.yaml:/app/arch_config.yaml

Binary file not shown.

After

Width:  |  Height:  |  Size: 549 KiB

View file

@ -0,0 +1,94 @@
import os
import json
import pandas as pd
import gradio as gr
import logging
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel, Field
from enum import Enum
from typing import List, Optional, Tuple
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
from openai import OpenAI
app = FastAPI()
workforce_data_df = None
with open("workforce_data.json") as file:
workforce_data = json.load(file)
workforce_data_df = pd.json_normalize(
workforce_data,
record_path=["regions"],
meta=["data_snapshot_days_ago", "satisfaction"],
)
# Define the request model
class WorkforceRequest(BaseModel):
region: str
staffing_type: str
data_snapshot_days_ago: Optional[int] = None
class SlackRequest(BaseModel):
slack_message: str
class WorkforceResponse(BaseModel):
region: str
staffing_type: str
headcount: int
satisfaction: float
@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'
if slack_token is None:
print(f"Message for slack: {slack_message}")
else:
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']}")
# Post method for device summary
@app.post("/agent/workforce")
def get_workforce(request: WorkforceRequest):
"""
Endpoint to workforce data by region, staffing type at a given point in time.
"""
region = request.region.lower()
staffing_type = request.staffing_type.lower()
data_snapshot_days_ago = (
request.data_snapshot_days_ago
if request.data_snapshot_days_ago
else 0 # this param is not required.
)
response = {
"region": region,
"staffing_type": f"Staffing agency: {staffing_type}",
"headcount": f"Headcount: {int(workforce_data_df[(workforce_data_df['region']==region) & (workforce_data_df['data_snapshot_days_ago']==data_snapshot_days_ago)][staffing_type].values[0])}",
"satisfaction": f"Satisfaction: {float(workforce_data_df[(workforce_data_df['region']==region) & (workforce_data_df['data_snapshot_days_ago']==data_snapshot_days_ago)]['satisfaction'].values[0])}",
}
return response
if __name__ == "__main__":
app.run(debug=True)

View file

@ -0,0 +1,13 @@
fastapi
uvicorn
slack-sdk
typing
pandas
gradio==5.3.0
async_timeout==4.0.3
loguru==0.7.2
asyncio==3.4.3
httpx==0.27.0
python-dotenv==1.0.1
pydantic==2.8.2
openai==1.51.0

View file

@ -0,0 +1,47 @@
#!/bin/bash
set -e
# Function to start the demo
start_demo() {
# Step 1: Check if .env file exists
if [ -f ".env" ]; then
echo ".env file already exists. Skipping creation."
else
# Step 2: Create `.env` file and set OpenAI key
if [ -z "$OPENAI_API_KEY" ]; then
echo "Error: OPENAI_API_KEY environment variable is not set for the demo."
exit 1
fi
echo "Creating .env file..."
echo "OPENAI_API_KEY=$OPENAI_API_KEY" > .env
echo ".env file created with OPENAI_API_KEY."
fi
# Step 3: Start Arch
echo "Starting Arch with arch_config.yaml..."
archgw up arch_config.yaml
# Step 4: Start Network Agent
echo "Starting HR Agent using Docker Compose..."
docker compose up -d # Run in detached mode
}
# Function to stop the demo
stop_demo() {
# Step 1: Stop Docker Compose services
echo "Stopping HR Agent using Docker Compose..."
docker compose down
# Step 2: Stop Arch
echo "Stopping Arch..."
archgw down
}
# Main script logic
if [ "$1" == "down" ]; then
stop_demo
else
# Default action is to bring the demo up
start_demo
fi

View file

@ -0,0 +1,14 @@
test_cases:
- id: get workforce data
input:
messages:
- role: user
content: what is workforce data for asia for fte employees
expected_tools:
- type: function
function:
name: workforce
arguments:
staffing_type: fte
region: asia
expected_output_contains: asia

View file

@ -0,0 +1,29 @@
[
{
"data_snapshot_days_ago": 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
},
{
"data_snapshot_days_ago": 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
},
{
"data_snapshot_days_ago": 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
}
]