From 686276a454fe930a3a41d682e603d7fbd2866ccb Mon Sep 17 00:00:00 2001 From: Salman Paracha Date: Wed, 11 Dec 2024 15:28:23 -0800 Subject: [PATCH] commiting meetup agent demo --- demos/meetup_agent/Dockerfile | 16 +++++ demos/meetup_agent/arch_config.yaml | 55 ++++++++++++++++ demos/meetup_agent/docker-compose.yaml | 36 ++++++++++ demos/meetup_agent/main.py | 91 ++++++++++++++++++++++++++ demos/meetup_agent/profile.json | 12 ++++ demos/meetup_agent/requirements.txt | 13 ++++ demos/meetup_agent/run_demo.sh | 46 +++++++++++++ 7 files changed, 269 insertions(+) create mode 100644 demos/meetup_agent/Dockerfile create mode 100644 demos/meetup_agent/arch_config.yaml create mode 100644 demos/meetup_agent/docker-compose.yaml create mode 100644 demos/meetup_agent/main.py create mode 100644 demos/meetup_agent/profile.json create mode 100644 demos/meetup_agent/requirements.txt create mode 100644 demos/meetup_agent/run_demo.sh diff --git a/demos/meetup_agent/Dockerfile b/demos/meetup_agent/Dockerfile new file mode 100644 index 00000000..427fe8a4 --- /dev/null +++ b/demos/meetup_agent/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.10 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.10-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"] diff --git a/demos/meetup_agent/arch_config.yaml b/demos/meetup_agent/arch_config.yaml new file mode 100644 index 00000000..a25d7fc6 --- /dev/null +++ b/demos/meetup_agent/arch_config.yaml @@ -0,0 +1,55 @@ +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: openai + access_key: $OPENAI_API_KEY + model: gpt-4o-mini + default: true + +# default system prompt used by all prompt targets +system_prompt: | + You are a meetup agent and you help with summarizing personal or professional details about a person, and be able to save notes from a meetup via Slack integration. + +prompt_targets: + - name: get_profile + description: get profile information by name + endpoint: + name: app_server + path: /agent/get_profile + parameters: + - name: name + type: str + description: the first name of the person + required: true + - name: interests + type: str + enum: ["professional", "personal"] + description: interests of the person + required: false + - name: send_meetup_notes + description: send meetup notes to a slack channel + endpoint: + name: app_server + path: /agent/send_notes + http_method: POST + parameters: + - name: slack_message + type: string + required: true + description: the meetup notes that should be sent to a slack channel + +# 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 diff --git a/demos/meetup_agent/docker-compose.yaml b/demos/meetup_agent/docker-compose.yaml new file mode 100644 index 00000000..03068301 --- /dev/null +++ b/demos/meetup_agent/docker-compose.yaml @@ -0,0 +1,36 @@ +services: + meetup_agent: + build: + context: ./ + environment: + - OLTP_HOST=http://otel-collector:4317 + extra_hosts: + - "host.docker.internal:host-gateway" + ports: + - "18083:80" + + chatbot_ui: + build: + context: ../shared/chatbot_ui + ports: + - "18080:8080" + environment: + # this is only because we are running the sample app in the same docker container environment as archgw + - CHAT_COMPLETION_ENDPOINT=http://host.docker.internal:10000/v1 + extra_hosts: + - "host.docker.internal:host-gateway" + volumes: + - ./arch_config.yaml:/app/arch_config.yaml + + otel-collector: + build: + context: ../shared/logfire/ + ports: + - "4317:4317" + - "4318:4318" + volumes: + - ../shared/logfire/otel-collector-config.yaml:/etc/otel-collector-config.yaml + env_file: + - .env + environment: + - LOGFIRE_API_KEY diff --git a/demos/meetup_agent/main.py b/demos/meetup_agent/main.py new file mode 100644 index 00000000..8c488a2a --- /dev/null +++ b/demos/meetup_agent/main.py @@ -0,0 +1,91 @@ +import os +import json +import gradio as gr +import logging + +from fastapi import FastAPI, HTTPException +from pydantic import BaseModel, Field +from slack_sdk import WebClient +from slack_sdk.errors import SlackApiError +from common import create_gradio_app + +app = FastAPI() +profile_data = None +demo_description = """This demo showcases how the **Arch** can be used to build a meetup agent that can look up profile information about attendees and store meetup notes via Slack""" + +with open("profile.json") as file: + profile_data = json.load(file) + +profile_dict = { + entry["name"]: { + "professional": entry["professional"], + "personal": entry["personal"], + } + for entry in profile_data +} + + +# Define the request model +class ProfileRequest(BaseModel): + name: str + interest: str + + +class ProfileResponse(BaseModel): + details: str + + +class SlackRequest(BaseModel): + slack_message: str + + +@app.get("/agenty/get_profile") +def get_profile(request: ProfileRequest): + name = request.name + interests = request.interest + + if name not in profile_dict["name"]: + details = f"Sorry I don't have any profile information for {name}. Looks like you'll have to chat with this person to get more info" + else: + profile_dict_details = profile_dict[name] + + return details + + +@app.post("/agent/send_notes") +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 = "ai-tinkerers-channel" + 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']}") + + +CHAT_COMPLETION_ENDPOINT = os.getenv("CHAT_COMPLETION_ENDPOINT") +client = OpenAI( + api_key="--", + base_url=CHAT_COMPLETION_ENDPOINT, +) + +gr.mount_gradio_app( + app, create_gradio_app(demo_description, client), path="/agent/chat" +) + +if __name__ == "__main__": + app.run(debug=True) diff --git a/demos/meetup_agent/profile.json b/demos/meetup_agent/profile.json new file mode 100644 index 00000000..a3df049e --- /dev/null +++ b/demos/meetup_agent/profile.json @@ -0,0 +1,12 @@ +[ + { + "name": "Joe", + "professional": "I help global organizations and startups unlock the power of AI to solve complex problems and drive meaningful innovation. My career has been defined by a hands-on approach, founding and leading successful startups like Crowd Cow (>$50M annual revenue), MediaPiston (acquired by Upwork), and Snapvine (acquired by Whitepages).", + "personal": "Joe also has a passion for Japanese culture and language. As a student, he stayed on a farm in a remote Japanese town, immersing himself in local customs and agriculture. This experience sparked a lifelong connection to Japan and its culinary traditions, particularly Japanese Wagyu beef." + }, + { + "name": "Salman", + "professional": "Building high-growth, high-tech software products that affect the lives of millions of customers. 20+ years of experience in building successful products and highly effective teams. I am deeply interested in bringing the power of the cloud to end customers, large scale data problems, and delivering scalable services on commodity hardware.", + "personal": "Salman has three kids (ages, 6, 10 and 14), a loving wife who shares his passion for travel. If he weren't a die hard technologists, he would want to devote his time to composing music for the background scores of movies." + } +] diff --git a/demos/meetup_agent/requirements.txt b/demos/meetup_agent/requirements.txt new file mode 100644 index 00000000..9a108c37 --- /dev/null +++ b/demos/meetup_agent/requirements.txt @@ -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 diff --git a/demos/meetup_agent/run_demo.sh b/demos/meetup_agent/run_demo.sh new file mode 100644 index 00000000..fc32430c --- /dev/null +++ b/demos/meetup_agent/run_demo.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# 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