Skip to content

Commit

Permalink
Merge pull request #44 from wandb/WandbotZD
Browse files Browse the repository at this point in the history
Wandbot zd
  • Loading branch information
parambharat committed Nov 10, 2023
2 parents c50ec35 + c94f0da commit b5956fd
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 91 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Before running the Q&A bot, ensure the following environment variables are set:

```bash
OPENAI_API_KEY
COHERE_API_KEY
SLACK_APP_TOKEN
SLACK_BOT_TOKEN
SLACK_SIGNING_SECRET
Expand Down
132 changes: 41 additions & 91 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ tree-sitter-languages = "^1.7.1"
cohere = "^4.32"
markdownify = "^0.11.6"
uvicorn = "^0.23.2"
zenpy = { version = "^2.0.41", optional = true }

[build-system]
requires = ["poetry-core>=1.0.0"]
Expand Down
115 changes: 115 additions & 0 deletions src/wandbot/apps/ZD/ZDWandbot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import asyncio
from zenpy import Zenpy
from zenpy.lib.api_objects import Comment
from zenpy.lib.api_objects import Ticket
import wandb
from wandbot.chat.config import ChatConfig
from wandbot.chat.schemas import ChatRepsonse, ChatRequest
import os
from datetime import datetime

from functools import partial
from wandbot.api.client import APIClient
from wandbot.api.schemas import APIQueryResponse
from wandbot.utils import get_logger
from wandbot.apps.ZD.config import ZDAppConfig
import pandas as pd

logger = get_logger(__name__)
config = ZDAppConfig()

class ZendeskAIResponseSystem:

def __init__(self):
userCreds = {
'email' : config.ZENDESK_EMAIL,
'password' : config.ZENDESK_PASSWORD,
'subdomain': config.ZENDESK_SUBDOMAIN
}
self.zenpy_client = Zenpy(**userCreds)
self.api_client = APIClient(url=config.WANDBOT_API_URL)

def create_new_ticket(self, questionText):
self.zenpy_client.tickets.create(Ticket(subject="WandbotTest4", description=questionText, status = 'new', priority = 'low', tags=["botTest","forum"]))

def fetch_new_tickets(self):
new_tickets = self.zenpy_client.search(type='ticket', status='new', group_id=config.ZDGROUPID)
# Filtering based on specific requirements
filtered_tickets = [ticket for ticket in new_tickets if 'forum' in ticket.tags]
# filtered_tickets = [ticket for ticket in new_tickets if 'bottest' in ticket.tags] # for testing purposes only
filtered_ticketsNotAnswered = [ticket for ticket in filtered_tickets if 'answered_by_bot' not in ticket.tags] # for testing purposes only

return filtered_ticketsNotAnswered

def extract_question(self, ticket):
description = ticket.description
# Preprocessing
question = description.lower().replace('\n', ' ').replace('\r', '')
question = question.replace('[discourse post]','')
question = question[:4095]

return question

async def generate_response(self, question, ticket_id):
try:
chat_history = []

response = self.api_client.query(question=question, chat_history=[])
if response == None:
raise Exception("Recieved no response")

except Exception as e:
logger.error(f"Error: {e}")
response = 'Something went wrong!'
return response

return response.answer

#TODO: add the necessary format we want to depending on ticket type
def format_response(self, response):
response = str(response)
max_length = 2000
if len(response) > max_length:
response = response[:max_length] + '...'
return response+"\n\n-WandBot 🤖"

def update_ticket(self, ticket, response):
try:
comment = Comment(body=response)
ticket.comment = Comment(body=response, public=False)

ticket.status="open"
ticket.tags.append('answered_by_bot')
self.zenpy_client.tickets.update(ticket)
except Exception as e:
logger.error(f"Error: {e}")

#TODO add feedback gathering
def gather_feedback(self, ticket):
try:
ticket.comment = Comment(body="How did we do?", public=False)
self.zenpy_client.tickets.update(ticket)
except Exception as e:
logger.error(f"Error: {e}")

async def run(self):
# test tickets
# self.create_new_ticket("How Do I start a run?")
self.create_new_ticket("Is there a way to programatically list all projects for a given entity?")
while True:
await asyncio.sleep(120)

new_tickets = self.fetch_new_tickets()
logger.info(f"New unanswered Tickets: {len(new_tickets)}")
for ticket in new_tickets:
question = self.extract_question(ticket)
response = await self.generate_response(question, ticket)

formatted_response = self.format_response(response)
self.update_ticket(ticket, formatted_response)
# self.gather_feedback(ticket)

if __name__ == "__main__":

zd = ZendeskAIResponseSystem()
asyncio.run(zd.run())
19 changes: 19 additions & 0 deletions src/wandbot/apps/ZD/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from pydantic import AnyHttpUrl, Field
from pydantic_settings import BaseSettings

ZDGROUPID = "360016040851"

class ZDAppConfig(BaseSettings):
ZENDESK_EMAIL: str = Field(..., env="ZENDESK_EMAIL"),
ZENDESK_PASSWORD: str = Field(..., env="ZENDESK_PASSWORD"),
ZENDESK_SUBDOMAIN: str = Field(..., env="ZENDESK_SUBDOMAIN"),

WANDB_API_KEY: str = Field(..., env="WANDB_API_KEY")
ZDGROUPID: str = ZDGROUPID
WANDBOT_API_URL: AnyHttpUrl = Field(..., env="WANDBOT_API_URL")
include_sources: bool = True

class Config:
env_file = ".env"
env_file_encoding = "utf-8"
extra = "allow"

0 comments on commit b5956fd

Please sign in to comment.