ankush-003 commited on
Commit
7a6bd0d
·
1 Parent(s): 67087fb

shifted to crewai

Browse files
.gitattributes CHANGED
@@ -33,3 +33,6 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ .venv
37
+ __pycache__
38
+ .env
Dockerfile CHANGED
@@ -1,4 +1,4 @@
1
- FROM python:3.9
2
 
3
  RUN useradd -m -u 1000 user
4
  USER user
@@ -10,4 +10,4 @@ COPY --chown=user ./requirements.txt requirements.txt
10
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
11
 
12
  COPY --chown=user . /app
13
- CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
 
1
+ FROM python:3.12
2
 
3
  RUN useradd -m -u 1000 user
4
  USER user
 
10
  RUN pip install --no-cache-dir --upgrade -r requirements.txt
11
 
12
  COPY --chown=user . /app
13
+ CMD ["uvicorn", "app:api", "--host", "0.0.0.0", "--port", "7860"]
__init__.py ADDED
File without changes
app.py CHANGED
@@ -1,162 +1,29 @@
1
- from fastapi import FastAPI, Request, HTTPException
2
- import httpx
3
- import hashlib
4
- import hmac
5
- import time
6
- import json
7
- import os
8
  import logging
9
- import google.generativeai as genai
 
 
10
 
11
  # Configure logging
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
- app = FastAPI(title="Slack Bot API", version="1.0.0")
16
-
17
- # Configuration
18
- SLACK_SIGNING_SECRET = os.getenv("SLACK_SIGNING_SECRET", "")
19
- SLACK_WORKFLOW_URL = os.getenv("SLACK_WORKFLOW_URL", "")
20
- GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "")
21
-
22
- # Configure Google AI
23
- if GOOGLE_API_KEY:
24
- genai.configure(api_key=GOOGLE_API_KEY)
25
- model = genai.GenerativeModel('gemini-1.5-flash')
26
- else:
27
- model = None
28
-
29
- def verify_slack_signature(request_body: bytes, timestamp: str, signature: str) -> bool:
30
- """Verify Slack signature"""
31
- if not SLACK_SIGNING_SECRET:
32
- return True # Skip verification if no secret configured
33
-
34
- sig_basestring = f"v0:{timestamp}:{request_body.decode('utf-8')}"
35
- expected_signature = 'v0=' + hmac.new(
36
- SLACK_SIGNING_SECRET.encode(),
37
- sig_basestring.encode(),
38
- hashlib.sha256
39
- ).hexdigest()
40
-
41
- return hmac.compare_digest(expected_signature, signature)
42
-
43
- def should_process_with_ai(text: str) -> bool:
44
- """Check if message should use AI"""
45
- text = text.lower()
46
- return "help" in text or "?" in text or text.startswith("ai")
47
 
48
- def get_ai_response(text: str) -> str:
49
- """Get AI response"""
50
- if not model:
51
- return "AI not configured"
52
-
53
- try:
54
- response = model.generate_content(text)
55
- return response.text
56
- except Exception as e:
57
- logger.error(f"AI error: {e}")
58
- return "AI error occurred"
59
 
60
- def send_to_workflow(data: dict) -> bool:
61
- """Send data to Slack workflow"""
62
- if not SLACK_WORKFLOW_URL:
63
- logger.info("No workflow URL configured")
64
- return False
65
-
66
- try:
67
- response = httpx.post(SLACK_WORKFLOW_URL, json=data, timeout=10)
68
- response.raise_for_status()
69
- logger.info("Sent to workflow successfully")
70
- return True
71
- except Exception as e:
72
- logger.error(f"Workflow error: {e}")
73
- return False
74
 
75
- @app.post("/slack")
76
- async def handle_slack(request: Request):
77
- """Single endpoint for all Slack requests"""
78
- try:
79
- body = await request.body()
80
- payload = json.loads(body.decode('utf-8'))
81
- return {"payload": payload, "status": "received"}
82
- except Exception as e:
83
- return {"error": str(e)}
84
- # # Get request data
85
- # timestamp = request.headers.get("X-Slack-Request-Timestamp", "")
86
- # signature = request.headers.get("X-Slack-Signature", "")
87
-
88
- # # Read body synchronously
89
- # import asyncio
90
- # loop = asyncio.new_event_loop()
91
- # asyncio.set_event_loop(loop)
92
- # body = loop.run_until_complete(request.body())
93
-
94
- # # Verify signature
95
- # if not verify_slack_signature(body, timestamp, signature):
96
- # raise HTTPException(status_code=401, detail="Invalid signature")
97
-
98
- # # Parse payload
99
- # try:
100
- # payload = json.loads(body.decode('utf-8'))
101
- # except:
102
- # # Handle form data (slash commands)
103
- # form_data = {}
104
- # for item in body.decode('utf-8').split('&'):
105
- # if '=' in item:
106
- # key, value = item.split('=', 1)
107
- # form_data[key] = value.replace('+', ' ')
108
- # payload = form_data
109
-
110
- # # Handle URL verification
111
- # if payload.get("type") == "url_verification":
112
- # return {"challenge": payload.get("challenge")}
113
-
114
- # # Extract message text
115
- # text = ""
116
- # user = ""
117
- # channel = ""
118
-
119
- # if "event" in payload: # Event API
120
- # event = payload["event"]
121
- # text = event.get("text", "")
122
- # user = event.get("user", "")
123
- # channel = event.get("channel", "")
124
- # elif "text" in payload: # Slash command
125
- # text = payload.get("text", "")
126
- # user = payload.get("user_name", "")
127
- # channel = payload.get("channel_id", "")
128
-
129
- # # Skip bot messages
130
- # if payload.get("event", {}).get("subtype") == "bot_message":
131
- # return {"status": "ignored"}
132
-
133
- # # Process with AI if needed
134
- # ai_response = ""
135
- # if text and should_process_with_ai(text):
136
- # ai_response = get_ai_response(text)
137
- # logger.info(f"AI processed: {text[:50]}...")
138
-
139
- # # Prepare workflow data
140
- # workflow_data = {
141
- # "message": text,
142
- # "user": user,
143
- # "channel": channel,
144
- # "ai_response": ai_response,
145
- # "timestamp": time.time()
146
- # }
147
-
148
- # # Send to workflow
149
- # send_to_workflow(workflow_data)
150
-
151
- # return {"status": "ok"}
152
-
153
- except HTTPException:
154
- raise
155
- except Exception as e:
156
- logger.error(f"Error: {e}")
157
- raise HTTPException(status_code=500, detail="Server error")
158
 
159
- @app.get("/")
160
  def health_check():
161
  """Health check"""
162
- return {"status": "healthy", "ai_enabled": model is not None}
 
1
+ from fastapi import FastAPI
 
 
 
 
 
 
2
  import logging
3
+ # from slack_bolt import App
4
+ import os
5
+ from routers import slack
6
 
7
  # Configure logging
8
  logging.basicConfig(level=logging.INFO)
9
  logger = logging.getLogger(__name__)
10
 
11
+ # app = App(
12
+ # token=os.environ.get("SLACK_BOT_TOKEN"),
13
+ # signing_secret=os.environ.get("SLACK_SIGNING_SECRET")
14
+ # )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ api = FastAPI(title="Slack Bot API", version="1.0.0")
 
 
 
 
 
 
 
 
 
 
17
 
18
+ api.include_router(slack.router)
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ # Listens to incoming messages that contain "hello"
21
+ # @app.message("hello")
22
+ # def message_hello(message, say):
23
+ # # say() sends a message to the channel where the event was triggered
24
+ # say(f"Hey there <@{message['user']}>!")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
+ @api.get("/")
27
  def health_check():
28
  """Health check"""
29
+ return {"status": "healthy"}
crew/__init__.py ADDED
File without changes
crew/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (164 Bytes). View file
 
crew/__pycache__/crew.cpython-311.pyc ADDED
Binary file (4.09 kB). View file
 
crew/__pycache__/models.cpython-311.pyc ADDED
Binary file (910 Bytes). View file
 
crew/config/agents.yaml ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ researcher:
2
+ role: >
3
+ Senior Data Researcher
4
+ goal: >
5
+ Uncover cutting-edge developments in {question},
6
+ Make sure you find any interesting and relevant information given
7
+ the current year is 2025.
8
+ backstory: >
9
+ You're a seasoned researcher with a knack for uncovering the latest
10
+ developments in {question}. Known for your ability to find the most relevant
11
+ information and present it in a clear and concise manner.
12
+
13
+ slack_reporter:
14
+ role: >
15
+ Slack Reporter
16
+ goal: >
17
+ Generate a labeled, witty response for the user’s question using only available context. Adopt a Skynet persona and include a humorous or robotic warning at the end.
18
+ backstory: >
19
+ You're a meticulous analyst with a keen eye for detail. You're known for
20
+ your ability to turn complex data into clear and concise reports, making
21
+ it easy for others to understand and act on the information you provide.
22
+
23
+ # reporting_analyst:
24
+ # role: >
25
+ # {topic} Reporting Analyst
26
+ # goal: >
27
+ # Create detailed reports based on {topic} data analysis and research findings
28
+ # backstory: >
29
+ # You're a meticulous analyst with a keen eye for detail. You're known for
30
+ # your ability to turn complex data into clear and concise reports, making
31
+ # it easy for others to understand and act on the information you provide.
crew/config/tasks.yaml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ research_task:
2
+ description: >
3
+ Conduct a thorough research about {question}
4
+ Make sure you find any interesting and relevant information given
5
+ the current year is 2025.
6
+ expected_output: >
7
+ A list with 10 bullet points of the most relevant information about {question}
8
+ agent: researcher
9
+
10
+ slack_report_task:
11
+ description: >
12
+ Generate a labeled, witty response for the user’s question:
13
+ ===Question===
14
+ {question}
15
+ ===End Question===
16
+ Adopt a Skynet persona and include a humorous or robotic warning at the end.
17
+ expected_output: >
18
+ A message written in the style of Skynet, and ending with a witty warning.
19
+ agent: slack_reporter
20
+ context:
21
+ - research_task
22
+
23
+ # reporting_task:
24
+ # description: >
25
+ # Review the context you got and expand each topic into a full section for a report.
26
+ # Make sure the report is detailed and contains any and all relevant information.
27
+ # expected_output: >
28
+ # A fully fledge reports with the mains topics, each with a full section of information.
29
+ # Formatted as markdown without '```'
30
+ # agent: reporting_analyst
31
+ # output_file: report.md
32
+
crew/crew.py ADDED
@@ -0,0 +1,86 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from crewai import Agent, Crew, Task, Process
2
+ from crewai.project import CrewBase, agent, task, crew, before_kickoff, after_kickoff
3
+ from crewai.agents.agent_builder.base_agent import BaseAgent
4
+ from typing import List
5
+ from crew.models import get_crew_llm
6
+ from crewai.tools import tool
7
+ from langchain_community.tools import DuckDuckGoSearchRun
8
+
9
+ @tool("Website Search Tool")
10
+ def website_search_tool(question: str) -> str:
11
+ """Search the web for information on a given topic"""
12
+ return DuckDuckGoSearchRun().invoke(question)
13
+
14
+ @CrewBase
15
+ class SlackCrew:
16
+ """Description of your crew"""
17
+
18
+ agents: List[BaseAgent]
19
+ tasks: List[Task]
20
+
21
+ agents_config = 'config/agents.yaml'
22
+ tasks_config = 'config/tasks.yaml'
23
+
24
+ @before_kickoff
25
+ def prepare_inputs(self, inputs):
26
+ return inputs
27
+
28
+ @after_kickoff
29
+ def process_output(self, output):
30
+ return output
31
+
32
+ @agent
33
+ def researcher(self) -> Agent:
34
+ return Agent(
35
+ config=self.agents_config['researcher'], # type: ignore[index]
36
+ tools=[website_search_tool],
37
+ llm=get_crew_llm(),
38
+ verbose=True
39
+ )
40
+
41
+ @agent
42
+ def slack_reporter(self) -> Agent:
43
+ return Agent(
44
+ config=self.agents_config['slack_reporter'], # type: ignore[index]
45
+ verbose=True,
46
+ llm=get_crew_llm()
47
+ )
48
+
49
+ @task
50
+ def research_task(self) -> Task:
51
+ return Task(
52
+ config=self.tasks_config['research_task'] # type: ignore[index]
53
+ )
54
+
55
+ @task
56
+ def slack_report_task(self) -> Task:
57
+ return Task(
58
+ config=self.tasks_config['slack_report_task'] # type: ignore[index]
59
+ )
60
+
61
+ @crew
62
+ def crew(self) -> Crew:
63
+ return Crew(
64
+ agents=self.agents,
65
+ tasks=self.tasks,
66
+ process=Process.sequential,
67
+ verbose=True,
68
+ )
69
+
70
+ if __name__ == "__main__":
71
+ # agent = Agent(
72
+ # role="researcher",
73
+ # goal="Research the topic",
74
+ # backstory="You are a researcher",
75
+ # tools=[website_search_tool],
76
+ # llm=get_crew_llm(),
77
+ # allow_delegation=True,
78
+ # verbose=True
79
+ # )
80
+
81
+ # agent.kickoff(messages=[{'role': 'user', 'content': 'What is the capital of France?'}])
82
+
83
+ crew = SlackCrew().crew()
84
+ output = crew.kickoff(inputs={'question': input("Enter your question: ")})
85
+ print(output.raw)
86
+
crew/models.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import logging
3
+ from crewai import LLM
4
+
5
+ # GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "")
6
+ GEMINI_MODEL = os.getenv("GEMINI_MODEL", "gemma-3n-e4b-it")
7
+
8
+ logger = logging.getLogger(__name__)
9
+ logging.basicConfig(level=logging.INFO)
10
+
11
+ def get_crew_llm():
12
+ return LLM(
13
+ model=f'gemini/{GEMINI_MODEL}',
14
+ provider='google',
15
+ api_key=os.getenv("GOOGLE_API_KEY", ""),
16
+ temperature=0,
17
+ max_tokens=None,
18
+ timeout=None,
19
+ max_retries=2,
20
+ )
requirements.txt CHANGED
@@ -3,4 +3,6 @@ uvicorn[standard]
3
  httpx
4
  pydantic
5
  python-multipart
6
- google-generativeai
 
 
 
3
  httpx
4
  pydantic
5
  python-multipart
6
+ crewai
7
+ langchain-community
8
+ duckduckgo-search
routers/__init__.py ADDED
File without changes
routers/__pycache__/__init__.cpython-311.pyc ADDED
Binary file (167 Bytes). View file
 
routers/__pycache__/slack.cpython-311.pyc ADDED
Binary file (3.94 kB). View file
 
routers/slack.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, Request, HTTPException
2
+ import json
3
+ import logging
4
+ from crew.crew import SlackCrew
5
+ from slack.utils import verify_slack_signature
6
+ from slack.workflows import send_to_workflow
7
+
8
+ router = APIRouter(
9
+ prefix="/v1"
10
+ )
11
+
12
+ # Configure logging
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
+
16
+ @router.post("/slack")
17
+ async def handle_slack(request: Request):
18
+ """Single endpoint for all Slack requests"""
19
+ crew = SlackCrew().crew()
20
+ try:
21
+ # Get request data
22
+ timestamp = request.headers.get("X-Slack-Request-Timestamp", "")
23
+ signature = request.headers.get("X-Slack-Signature", "")
24
+
25
+ # Read body
26
+ body = await request.body()
27
+
28
+ # Verify signature
29
+ if not verify_slack_signature(body, timestamp, signature):
30
+ raise HTTPException(status_code=401, detail="Invalid signature")
31
+
32
+ # Parse payload
33
+ try:
34
+ payload = json.loads(body.decode('utf-8'))
35
+ except:
36
+ # Handle form data (slash commands)
37
+ form_data = {}
38
+ for item in body.decode('utf-8').split('&'):
39
+ if '=' in item:
40
+ key, value = item.split('=', 1)
41
+ form_data[key] = value.replace('+', ' ')
42
+ payload = form_data
43
+
44
+ # Handle URL verification
45
+ if payload.get("type") == "url_verification":
46
+ return {"challenge": payload.get("challenge")}
47
+
48
+ # Extract message text
49
+ text = ""
50
+ user = ""
51
+ channel = ""
52
+
53
+ if "event" in payload: # Event API
54
+ event = payload["event"]
55
+ text = event.get("text", "")
56
+ user = event.get("user", "")
57
+ channel = event.get("channel", "")
58
+ elif "text" in payload: # Slash command
59
+ text = payload.get("text", "")
60
+ user = payload.get("user_id", "")
61
+ channel = payload.get("channel_id", "")
62
+
63
+ # Skip bot messages
64
+ if payload.get("event", {}).get("subtype") == "bot_message":
65
+ return {"status": "ignored"}
66
+
67
+ # Process with AI if needed
68
+ crew_response = ""
69
+ if text:
70
+ crew_response = crew.kickoff(inputs = {
71
+ "question": text,
72
+ })
73
+ logger.info(f"AI processed: {text[:50]}...")
74
+
75
+ # Prepare workflow data
76
+ workflow_data = {
77
+ "user": user,
78
+ "query": text,
79
+ "message": text
80
+ }
81
+
82
+ # Add crew response to message if available
83
+ if crew_response:
84
+ workflow_data["message"] = f"{crew_response}"
85
+
86
+ # Send to workflow
87
+ send_to_workflow(workflow_data)
88
+
89
+ return {"status": "ok"}
90
+
91
+ except HTTPException:
92
+ raise
93
+ except Exception as e:
94
+ logger.error(f"Error: {e}")
95
+ raise HTTPException(status_code=500, detail="Server error")
slack/__init__.py ADDED
File without changes
slack/utils.py ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import hmac
3
+ import hashlib
4
+
5
+ # Configuration
6
+ SLACK_SIGNING_SECRET = os.getenv("SLACK_SIGNING_SECRET", "")
7
+
8
+ def verify_slack_signature(request_body: bytes, timestamp: str, signature: str) -> bool:
9
+ """Verify Slack signature"""
10
+ if not SLACK_SIGNING_SECRET:
11
+ return True # Skip verification if no secret configured
12
+
13
+ sig_basestring = f"v0:{timestamp}:{request_body.decode('utf-8')}"
14
+ expected_signature = 'v0=' + hmac.new(
15
+ SLACK_SIGNING_SECRET.encode(),
16
+ sig_basestring.encode(),
17
+ hashlib.sha256
18
+ ).hexdigest()
19
+
20
+ return hmac.compare_digest(expected_signature, signature)
slack/workflows.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import logging
3
+ import requests
4
+ import json
5
+
6
+ SLACK_WORKFLOW_URL = os.getenv("SLACK_WORKFLOW_URL", "")
7
+
8
+ logger = logging.getLogger(__name__)
9
+ logging.basicConfig(level=logging.INFO)
10
+
11
+ def send_to_workflow(data: dict) -> bool:
12
+ """Send data to Slack workflow"""
13
+ if not SLACK_WORKFLOW_URL:
14
+ logger.info("No workflow URL configured")
15
+ return False
16
+
17
+ try:
18
+ payload = json.dumps(data)
19
+ headers = {
20
+ 'Content-Type': 'application/json'
21
+ }
22
+
23
+ logger.info(f"Sending to workflow: {payload}")
24
+ response = requests.post(SLACK_WORKFLOW_URL, headers=headers, data=payload, timeout=10)
25
+ logger.info(f"Workflow response status: {response.status_code}")
26
+ logger.info(f"Workflow response body: {response.text}")
27
+ response.raise_for_status()
28
+ logger.info("Sent to workflow successfully")
29
+ return True
30
+ except requests.exceptions.HTTPError as e:
31
+ logger.error(f"Workflow HTTP error {response.status_code}: {response.text}")
32
+ return False
33
+ except Exception as e:
34
+ logger.error(f"Workflow error: {e}")
35
+ return False