Skip to content

Commit

Permalink
Merge branch 'staging' of https://github.com/edenartlab/eve into staging
Browse files Browse the repository at this point in the history
  • Loading branch information
genekogan committed Jan 14, 2025
2 parents 388b037 + 534ed36 commit cb977fd
Show file tree
Hide file tree
Showing 20 changed files with 345 additions and 254 deletions.
9 changes: 2 additions & 7 deletions .github/workflows/prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,9 @@ on:
push:
branches:
- main
pull_request:
types: [closed]
branches:
- main

jobs:
deploy:
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true))
deploy-prod:
runs-on: ubuntu-latest
env:
DB: PROD
Expand Down Expand Up @@ -46,4 +41,4 @@ jobs:

- name: Deploy to Modal
working-directory: eve
run: rye run modal deploy ./eve/api/api.py
run: rye run modal deploy ./eve/api/api.py
7 changes: 1 addition & 6 deletions .github/workflows/staging.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,9 @@ on:
push:
branches:
- staging
pull_request:
types: [closed]
branches:
- staging

jobs:
deploy:
if: github.ref == 'refs/heads/staging' && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true))
deploy-stage:
runs-on: ubuntu-latest
env:
DB: STAGE
Expand Down
13 changes: 4 additions & 9 deletions .github/workflows/web3-prod.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
name: Deploy to Modal Web3-Production
name: Deploy to Modal Production

on:
push:
branches:
- main
pull_request:
types: [closed]
branches:
- main
- web3-prod

jobs:
deploy:
if: github.ref == 'refs/heads/main' && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true))
deploy-prod:
runs-on: ubuntu-latest
env:
DB: WEB3-PROD
Expand Down Expand Up @@ -46,4 +41,4 @@ jobs:

- name: Deploy to Modal
working-directory: eve
run: rye run modal deploy ./eve/api/api.py
run: rye run modal deploy ./eve/api/api.py
11 changes: 3 additions & 8 deletions .github/workflows/web3-staging.yaml
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
name: Deploy to Modal Web3-Staging
name: Deploy to Modal Staging

on:
push:
branches:
- staging
pull_request:
types: [closed]
branches:
- staging
- web3-staging

jobs:
deploy:
if: github.ref == 'refs/heads/staging' && (github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.pull_request.merged == true))
deploy-stage:
runs-on: ubuntu-latest
env:
DB: WEB3-STAGE
Expand Down
33 changes: 17 additions & 16 deletions eve/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,24 +17,23 @@
def setup_sentry():
sentry_dsn = os.getenv("SENTRY_DSN")
if not sentry_dsn:
print("No Sentry DSN found, skipping Sentry setup")
return

print(f"Setting up sentry for {db}")
# Determine environment
sentry_env = "production" if db == "PROD" else "staging"
if db == "PROD":
traces_sample_rate = 0.1
profiles_sample_rate = 0.05

else:
traces_sample_rate = 1.0
profiles_sample_rate = 1.0

if sentry_dsn:
sentry_sdk.init(
dsn=sentry_dsn,
traces_sample_rate=traces_sample_rate,
profiles_sample_rate=profiles_sample_rate,
environment=sentry_env,
)

# Set sampling rates
traces_sample_rate = 0.1 if db == "PROD" else 1.0
profiles_sample_rate = 0.05 if db == "PROD" else 1.0

sentry_sdk.init(
dsn=sentry_dsn,
traces_sample_rate=traces_sample_rate,
profiles_sample_rate=profiles_sample_rate,
environment=sentry_env,
)


def load_env(db):
Expand Down Expand Up @@ -92,6 +91,8 @@ def verify_env():
db = os.getenv("DB", "STAGE").upper()

if db not in ["STAGE", "PROD", "WEB3-STAGE", "WEB3-PROD"]:
raise Exception(f"Invalid environment: {db}. Must be STAGE, PROD, WEB3-STAGE, or WEB3-PROD")
raise Exception(
f"Invalid environment: {db}. Must be STAGE, PROD, WEB3-STAGE, or WEB3-PROD"
)

load_env(db)
54 changes: 50 additions & 4 deletions eve/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@
import os
import threading
import json
from fastapi.responses import JSONResponse
import modal
from fastapi import FastAPI, Depends, BackgroundTasks, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.security import APIKeyHeader, HTTPBearer
from apscheduler.schedulers.background import BackgroundScheduler
from pathlib import Path
from contextlib import asynccontextmanager
from starlette.middleware.base import BaseHTTPMiddleware
import sentry_sdk

from eve import auth, db
from eve.postprocessing import (
Expand Down Expand Up @@ -67,7 +70,34 @@ async def lifespan(app: FastAPI):
app.state.scheduler.shutdown(wait=True)


class SentryContextMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
with sentry_sdk.configure_scope() as scope:
scope.set_tag("package", "eve-api")

# Extract client context from headers
client_platform = request.headers.get("X-Client-Platform")
client_agent = request.headers.get("X-Client-Agent")

if client_platform:
scope.set_tag("client_platform", client_platform)
if client_agent:
scope.set_tag("client_agent", client_agent)

scope.set_context(
"api",
{
"endpoint": request.url.path,
"modal_serve": os.getenv("MODAL_SERVE"),
"client_platform": client_platform,
"client_agent": client_agent,
},
)
return await call_next(request)


web_app = FastAPI(lifespan=lifespan)
web_app.add_middleware(SentryContextMiddleware)
web_app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
Expand Down Expand Up @@ -167,6 +197,15 @@ async def trigger_delete(
return await handle_trigger_delete(request, scheduler)


@web_app.exception_handler(Exception)
async def catch_all_exception_handler(request, exc):
sentry_sdk.capture_exception(exc)
return JSONResponse(
status_code=500,
content={"message": str(exc)},
)


# Modal app setup
app = modal.App(
name=app_name,
Expand Down Expand Up @@ -222,17 +261,24 @@ def fastapi_app():
image=image, concurrency_limit=1, schedule=modal.Period(minutes=15), timeout=3600
)
async def postprocessing():
with sentry_sdk.configure_scope() as scope:
scope.set_tag("component", "postprocessing")
scope.set_context("function", {"name": "postprocessing"})

try:
await cancel_stuck_tasks()
except Exception as e:
print(f"Error cancelling stuck tasks: {e}")
sentry_sdk.capture_exception(e)

try:
await run_nsfw_detection()
except Exception as e:
print(f"Error running nsfw detection: {e}")
# try:
# await run_nsfw_detection()
# except Exception as e:
# print(f"Error running nsfw detection: {e}")
# sentry_sdk.capture_exception(e)

try:
await generate_lora_thumbnails()
except Exception as e:
print(f"Error generating lora thumbnails: {e}")
sentry_sdk.capture_exception(e)
22 changes: 17 additions & 5 deletions eve/api/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,8 @@
from ably import AblyRest
from apscheduler.schedulers.background import BackgroundScheduler
import traceback
import asyncio
from contextlib import asynccontextmanager

from eve.api.errors import APIError
from eve.tool import Tool
from eve.user import User
from eve.agent import Agent
Expand All @@ -31,12 +30,25 @@ async def get_update_channel(
async def setup_chat(
request: ChatRequest, background_tasks: BackgroundTasks
) -> tuple[User, Agent, Thread, list[Tool]]:
user = User.from_mongo(request.user_id)
agent = Agent.from_mongo(request.agent_id, cache=True)
try:
user = User.from_mongo(request.user_id)
except Exception as e:
raise APIError(f"Invalid user_id: {request.user_id}", status_code=400) from e

try:
agent = Agent.from_mongo(request.agent_id, cache=True)
except Exception as e:
raise APIError(f"Invalid agent_id: {request.agent_id}", status_code=400) from e

tools = agent.get_tools(cache=True)

if request.thread_id:
thread = Thread.from_mongo(request.thread_id)
try:
thread = Thread.from_mongo(request.thread_id)
except Exception as e:
raise APIError(
f"Invalid thread_id: {request.thread_id}", status_code=400
) from e
else:
thread = agent.request_thread(user=user.id)
background_tasks.add_task(async_title_thread, thread, request.user_message)
Expand Down
9 changes: 7 additions & 2 deletions eve/auth.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os
from typing import Optional
from fastapi.security import APIKeyHeader, HTTPBearer, HTTPAuthorizationCredentials
from fastapi import WebSocket, HTTPException, Depends, status
from clerk_backend_api import Clerk
Expand Down Expand Up @@ -152,16 +153,20 @@ async def authenticate_ws(websocket: WebSocket):


def authenticate_admin(
token: HTTPAuthorizationCredentials = Depends(bearer_scheme),
token: Optional[HTTPAuthorizationCredentials] = Depends(bearer_scheme),
):
if not token:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized"
)
if token.credentials != EDEN_ADMIN_KEY:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Unauthorized"
)


def authenticate_admin_api_key(
api_key: str = Depends(api_key_header),
api_key: Optional[str] = Depends(api_key_header),
):
"""Authenticate admin users by checking their API key's admin status"""
if not api_key:
Expand Down
21 changes: 21 additions & 0 deletions eve/clients/common.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import os
import time
import asyncio

from eve.models import ClientType
from .. import sentry_sdk

db = os.getenv("DB", "STAGE")

Expand Down Expand Up @@ -53,6 +55,25 @@
day_timestamps = {}


def client_context(client_platform: str):
"""Decorator to add client context to Sentry for all async methods"""

def decorator(cls):
for name, method in cls.__dict__.items():
if asyncio.iscoroutinefunction(method):

async def wrapped_method(self, *args, __method=method, **kwargs):
with sentry_sdk.configure_scope() as scope:
scope.set_tag("client_platform", client_platform)
scope.set_tag("client_agent", self.agent.username)
return await __method(self, *args, **kwargs)

setattr(cls, name, wrapped_method)
return cls

return decorator


def user_over_rate_limits(user):
user_id = str(user.id)

Expand Down
24 changes: 17 additions & 7 deletions eve/clients/discord/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def replace_mentions_with_usernames(
return message_content.strip()


@common.client_context("discord")
class Eden2Cog(commands.Cog):
def __init__(
self,
Expand Down Expand Up @@ -285,7 +286,11 @@ async def on_message(self, message: discord.Message) -> None:
async with session.post(
f"{self.api_url}/chat",
json=request_data,
headers={"Authorization": f"Bearer {os.getenv('EDEN_ADMIN_KEY')}"},
headers={
"Authorization": f"Bearer {os.getenv('EDEN_ADMIN_KEY')}",
"X-Client-Platform": "discord",
"X-Client-Agent": self.agent.username,
},
) as response:
if response.status != 200:
error_msg = await response.text()
Expand Down Expand Up @@ -394,12 +399,17 @@ def start(

agent_name = os.getenv("EDEN_AGENT_USERNAME")
agent = Agent.load(agent_name)
logger.info(f"Launching Discord bot {agent.username}...")

bot_token = os.getenv("CLIENT_DISCORD_TOKEN")
bot = DiscordBot()
bot.add_cog(Eden2Cog(bot, agent, local=local))
bot.run(bot_token)
with sentry_sdk.configure_scope() as scope:
scope.set_tag("package", "eve-clients")
scope.set_tag("client_platform", "discord")
scope.set_tag("client_agent", agent_name)
scope.set_context("discord", {"agent": agent_name, "local": local})
logger.info(f"Launching Discord bot {agent.username}...")

bot_token = os.getenv("CLIENT_DISCORD_TOKEN")
bot = DiscordBot()
bot.add_cog(Eden2Cog(bot, agent, local=local))
bot.run(bot_token)
except Exception as e:
logger.error("Failed to start Discord bot", exc_info=True)
sentry_sdk.capture_exception(e)
Expand Down
6 changes: 5 additions & 1 deletion eve/clients/farcaster/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,11 @@ async def process_webhook(
async with session.post(
f"{api_url}/chat",
json=request_data,
headers={"Authorization": f"Bearer {os.getenv('EDEN_ADMIN_KEY')}"},
headers={
"Authorization": f"Bearer {os.getenv('EDEN_ADMIN_KEY')}",
"X-Client-Platform": "farcaster",
"X-Client-Agent": agent.username,
},
) as response:
if response.status != 200:
raise Exception("Failed to process request")
Expand Down
Loading

0 comments on commit cb977fd

Please sign in to comment.