From ad1501ea05a7c1f69f584591010c4e706e065c87 Mon Sep 17 00:00:00 2001 From: Dev Aggarwal Date: Mon, 19 Aug 2024 23:18:59 +0530 Subject: [PATCH] Refactor integration connection and fix usability problems with integrations for new users - Add `AppUser.first_name_possesive` - Set `BotIntegration` streaming default to `True` and don't overwrite streaming=True in integration connect code - Simplify run title generation in `BasePage`. - Create `bot_integration_connect.py` for common integration methods. - Streamline integration logic: use published run only & pass pr_id in the integration redirect url state - Remove integration welcome screen for logged-in users - simply duplicate the published run for them - Cleanup URL generation and fix redirects on the integrations page - Add user nudges for unpublished changes + for non-owners - Fix slack personal channel creation: Avoid setting slack_create_personal_channels=False after connect so re-connect force creates personal channels --- app_users/models.py | 14 ++ ..._alter_botintegration_streaming_enabled.py | 18 ++ bots/models.py | 4 +- daras_ai_v2/base.py | 9 +- daras_ai_v2/bot_integration_connect.py | 44 +++++ daras_ai_v2/bot_integration_widgets.py | 35 ++++ daras_ai_v2/icons.py | 2 + recipes/VideoBots.py | 185 ++++++------------ routers/facebook_api.py | 79 +++----- routers/root.py | 5 +- routers/slack_api.py | 30 +-- .../0017_alter_modelpricing_model_name.py | 18 ++ 12 files changed, 229 insertions(+), 214 deletions(-) create mode 100644 bots/migrations/0080_alter_botintegration_streaming_enabled.py create mode 100644 daras_ai_v2/bot_integration_connect.py create mode 100644 usage_costs/migrations/0017_alter_modelpricing_model_name.py diff --git a/app_users/models.py b/app_users/models.py index 1e1016520..09832cebc 100644 --- a/app_users/models.py +++ b/app_users/models.py @@ -145,6 +145,20 @@ def first_name(self): return str(self.phone_number) return "Anon" + def first_name_possesive(self) -> str: + if self.display_name: + name = self.display_name.split(" ")[0] + elif self.email: + name = self.email.split("@")[0] + elif self.phone_number: + name = str(self.phone_number) + else: + return "My" + if name.endswith("s"): + return name + "'" + else: + return name + "'s" + @db_middleware @transaction.atomic def add_balance( diff --git a/bots/migrations/0080_alter_botintegration_streaming_enabled.py b/bots/migrations/0080_alter_botintegration_streaming_enabled.py new file mode 100644 index 000000000..ad4c4bbe3 --- /dev/null +++ b/bots/migrations/0080_alter_botintegration_streaming_enabled.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2024-08-19 16:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('bots', '0079_remove_botintegration_twilio_asr_language_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='botintegration', + name='streaming_enabled', + field=models.BooleanField(default=True, help_text='If set, the bot will stream messages to the frontend (Slack, Web & Whatsapp only)'), + ), + ] diff --git a/bots/models.py b/bots/models.py index 51eaebc59..3b7258e3b 100644 --- a/bots/models.py +++ b/bots/models.py @@ -698,8 +698,8 @@ class BotIntegration(models.Model): ) streaming_enabled = models.BooleanField( - default=False, - help_text="If set, the bot will stream messages to the frontend (Slack & Web only)", + default=True, + help_text="If set, the bot will stream messages to the frontend", ) created_at = models.DateTimeField(auto_now_add=True) diff --git a/daras_ai_v2/base.py b/daras_ai_v2/base.py index 7ee73dcfc..8cd1c6dee 100644 --- a/daras_ai_v2/base.py +++ b/daras_ai_v2/base.py @@ -374,7 +374,6 @@ def _render_header(self): and published_run.saved_run != current_run and self.request and self.request.user - and published_run.created_by == self.request.user ) if can_user_edit_run and has_unpublished_changes: @@ -589,13 +588,7 @@ def _render_publish_modal( title = published_run.title or self.title else: recipe_title = self.get_root_published_run().title or self.title - if self.request.user.display_name: - username = self.request.user.display_name + "'s" - elif self.request.user.email: - username = self.request.user.email.split("@")[0] + "'s" - else: - username = "My" - title = f"{username} {recipe_title}" + title = f"{self.request.user.first_name_possesive()} {recipe_title}" published_run_title = gui.text_input( "##### Title", key="published_run_title", diff --git a/daras_ai_v2/bot_integration_connect.py b/daras_ai_v2/bot_integration_connect.py new file mode 100644 index 000000000..8222c421d --- /dev/null +++ b/daras_ai_v2/bot_integration_connect.py @@ -0,0 +1,44 @@ +import json + +from fastapi import HTTPException, Request + +from bots.models import ( + BotIntegration, + Platform, + PublishedRun, +) +from daras_ai_v2.base import RecipeTabs + + +def connect_bot_to_published_run( + bi: BotIntegration, published_run: PublishedRun | None +) -> str: + """ + Connect the bot integration to the provided saved and published runs. + Returns the redirect url to the integrations page for that bot integration. + """ + + from daras_ai_v2.slack_bot import send_confirmation_msg + from recipes.VideoBots import VideoBotsPage + + print(f"Connecting {bi} to {published_run}") + + bi.published_run = published_run + bi.save(update_fields=["published_run"]) + + if bi.platform == Platform.SLACK: + send_confirmation_msg(bi) + + return VideoBotsPage.app_url( + tab=RecipeTabs.integrations, + example_id=published_run.published_run_id, + path_params=dict(integration_id=bi.api_integration_id()), + ) + + +def load_published_run_from_state(request: Request) -> PublishedRun: + pr_id = json.loads(request.query_params.get("state") or "{}").get("pr_id") + try: + return PublishedRun.objects.get(id=pr_id) + except PublishedRun.DoesNotExist: + raise HTTPException(status_code=404, detail="Published Run not found") diff --git a/daras_ai_v2/bot_integration_widgets.py b/daras_ai_v2/bot_integration_widgets.py index 2782c2eab..06888a016 100644 --- a/daras_ai_v2/bot_integration_widgets.py +++ b/daras_ai_v2/bot_integration_widgets.py @@ -19,6 +19,41 @@ from routers.root import RecipeTabs, chat_route, chat_lib_route +def integrations_welcome_screen(title: str): + with gui.center(): + gui.markdown(f"#### {title}") + + col1, col2, col3 = gui.columns( + 3, + column_props=dict( + style=dict( + display="flex", + flexDirection="column", + alignItems="center", + textAlign="center", + maxWidth="300px", + ), + ), + style={"justifyContent": "center"}, + ) + with col1: + gui.html("πŸƒβ€β™€οΈ", style={"fontSize": "4rem"}) + gui.markdown( + """ + 1. Fork & Save your Run + """ + ) + gui.caption("Make changes, Submit & Save your perfect workflow") + with col2: + gui.image(icons.integrations_img, alt="Integrations", style={"height": "5rem"}) + gui.markdown("2. Connect to Slack, Whatsapp or your App") + gui.caption("Or Facebook, Instagram and the web. Wherever your users chat.") + with col3: + gui.html("πŸ“ˆ", style={"fontSize": "4rem"}) + gui.markdown("3. Test, Analyze & Iterate") + gui.caption("Analyze your usage. Update your Saved Run to test changes.") + + def general_integration_settings(bi: BotIntegration, current_user: AppUser): if gui.session_state.get(f"_bi_reset_{bi.id}"): gui.session_state[f"_bi_streaming_enabled_{bi.id}"] = ( diff --git a/daras_ai_v2/icons.py b/daras_ai_v2/icons.py index c862da919..6ce628f16 100644 --- a/daras_ai_v2/icons.py +++ b/daras_ai_v2/icons.py @@ -31,3 +31,5 @@ "jcb": '', "diners": '', } + +integrations_img = "https://storage.googleapis.com/dara-c1b52.appspot.com/daras_ai/media/c3ba2392-d6b9-11ee-a67b-6ace8d8c9501/image.png" diff --git a/recipes/VideoBots.py b/recipes/VideoBots.py index 591ef397a..3f4fdac5d 100644 --- a/recipes/VideoBots.py +++ b/recipes/VideoBots.py @@ -8,7 +8,12 @@ from furl import furl from pydantic import BaseModel, Field -from bots.models import BotIntegration, Platform, SavedRun, PublishedRun +from bots.models import ( + BotIntegration, + Platform, + PublishedRun, + PublishedRunVisibility, +) from bots.models import Workflow from celeryapp.tasks import send_integration_attempt_email from daras_ai.image_input import ( @@ -30,6 +35,7 @@ azure_form_recognizer_models, ) from daras_ai_v2.base import BasePage, RecipeTabs +from daras_ai_v2.bot_integration_connect import connect_bot_to_published_run from daras_ai_v2.bot_integration_widgets import ( general_integration_settings, slack_specific_settings, @@ -38,6 +44,7 @@ get_bot_test_link, web_widget_config, get_web_widget_embed_code, + integrations_welcome_screen, ) from daras_ai_v2.doc_search_settings_widgets import ( query_instructions_widget, @@ -104,7 +111,6 @@ from url_shortener.models import ShortenedURL DEFAULT_COPILOT_META_IMG = "https://storage.googleapis.com/dara-c1b52.appspot.com/daras_ai/media/7a3127ec-1f71-11ef-aa2b-02420a00015d/Copilot.jpg" -INTEGRATION_IMG = "https://storage.googleapis.com/dara-c1b52.appspot.com/daras_ai/media/c3ba2392-d6b9-11ee-a67b-6ace8d8c9501/image.png" GRAYCOLOR = "#00000073" SAFETY_BUFFER = 100 @@ -1049,49 +1055,29 @@ def render_integrations_tab(self): # not signed in case if not self.request.user or self.request.user.is_anonymous: - integration_welcome_screen(title="Connect your Copilot") + integrations_welcome_screen(title="Connect your Copilot") gui.newline() with gui.center(): - gui.anchor( - "Get Started", - href=self.get_auth_url(self.app_url()), - type="primary", - ) + gui.anchor("Get Started", href=self.get_auth_url(), type="primary") return - current_run, published_run = self.get_runs_from_query_params( + sr, pr = self.get_runs_from_query_params( *extract_query_params(gui.get_query_params()) - ) # type: ignore - - # signed in but not on a run the user can edit (admins will never see this) - if not self.can_user_edit_run(current_run, published_run): - integration_welcome_screen(title="Create your Saved Copilot") - gui.newline() - with gui.center(): - gui.anchor( - "Run & Save this Copilot", - href=self.get_auth_url(self.app_url()), - type="primary", - ) - return + ) - # signed, has submitted run, but not published (admins will never see this) - # note: this means we no longer allow botintegrations on non-published runs which is a breaking change requested by Sean - if not self.can_user_edit_published_run(published_run): - integration_welcome_screen(title="Save your Published Copilot") - gui.newline() - with gui.center(): - self._render_published_run_buttons( - current_run=current_run, - published_run=published_run, - redirect_to=self.current_app_url(RecipeTabs.integrations), - ) - return + # make user the user knows that they are on a saved run not the published run + if pr and pr.saved_run_id != sr.id: + last_saved_url = self.app_url( + tab=RecipeTabs.integrations, example_id=pr.published_run_id + ) + gui.caption( + f"Note: You seem to have unpublished changes. Integrations use the [last saved version]({last_saved_url}), not the currently visible edits.", + className="text-center text-muted", + ) - # see which integrations are available to the user for the current published run - assert published_run, "At this point, published_run should be available" - integrations_q = Q(published_run=published_run) | Q( - saved_run__example_id=published_run.published_run_id + # see which integrations are available to the user for the published run + integrations_q = Q(published_run=pr) | Q( + saved_run__example_id=pr.published_run_id ) if not self.is_current_user_admin(): integrations_q &= Q(billing_account_uid=self.request.user.uid) @@ -1100,33 +1086,27 @@ def render_integrations_tab(self): integrations_q ).order_by("platform", "-created_at") - run_title = get_title_breadcrumbs( - VideoBotsPage, current_run, published_run - ).h1_title + run_title = get_title_breadcrumbs(VideoBotsPage, sr, pr).h1_title - # signed in, can edit, but no connected botintegrations on this run - if not integrations_qs.exists(): + # no connected integrations on this run + if not (integrations_qs and integrations_qs.exists()): self.render_integrations_add( - label=""" - #### Connect your Copilot - Run Saved βœ… β€’ Connect β€’ Test & Configure - """, + label="#### Connect your Copilot", run_title=run_title, + pr=pr, ) return # this gets triggered on the /add route if gui.session_state.pop("--add-integration", None): - cancel_url = self.current_app_url(RecipeTabs.integrations) self.render_integrations_add( - label=f""" - #### Configure your Copilot: Add a New Integration - Run Saved βœ… β€’ Connected βœ… β€’ [Test & Configure]({cancel_url}) βœ… - """, + label="#### Add a New Integration to your Copilot", run_title=run_title, + pr=pr, ) with gui.center(): if gui.button("Return to Test & Configure"): + cancel_url = self.current_app_url(RecipeTabs.integrations) raise gui.RedirectException(cancel_url) return @@ -1136,11 +1116,14 @@ def render_integrations_tab(self): integrations=list(integrations_qs), run_title=run_title ) - def render_integrations_add(self, label: str, run_title: str): + def render_integrations_add(self, label: str, run_title: str, pr: PublishedRun): from routers.facebook_api import fb_connect_url, wa_connect_url from routers.slack_api import slack_connect_url gui.write(label, unsafe_allow_html=True, className="text-center") + + can_edit = self.is_current_user_admin() or self.can_user_edit_published_run(pr) + gui.newline() pressed_platform = None @@ -1160,14 +1143,22 @@ def render_integrations_add(self, label: str, run_title: str): with gui.tag("td", className="ps-3"): gui.caption(choice.label) - if pressed_platform: - current_run, published_run = ( - self.get_current_sr(), - self.get_current_published_run(), + if not can_edit: + gui.caption( + "P.S. You're not an owner of this saved workflow, so we'll create a copy of it in your Saved Runs.", + className="text-center text-muted", ) - current_run_id, published_run_id = ( - current_run.run_id if current_run else None - ), (published_run.published_run_id if published_run else None) + + if pressed_platform: + if not can_edit: + run_title = f"{self.request.user.first_name_possesive()} {run_title}" + pr = pr.duplicate( + user=self.request.user, + title=run_title, + notes=pr.notes, + visibility=PublishedRunVisibility.UNLISTED, + ) + match pressed_platform: case Platform.WEB: bi = BotIntegration.objects.create( @@ -1175,13 +1166,13 @@ def render_integrations_add(self, label: str, run_title: str): billing_account_uid=self.request.user.uid, platform=Platform.WEB, ) - redirect_url = connect(bi, current_run, published_run) + redirect_url = connect_bot_to_published_run(bi, pr) case Platform.WHATSAPP: - redirect_url = wa_connect_url(current_run_id, published_run_id) + redirect_url = wa_connect_url(pr.id) case Platform.SLACK: - redirect_url = slack_connect_url(current_run_id, published_run_id) + redirect_url = slack_connect_url(pr.id) case Platform.FACEBOOK: - redirect_url = fb_connect_url(current_run_id, published_run_id) + redirect_url = fb_connect_url(pr.id) case _: raise ValueError(f"Unsupported platform: {pressed_platform}") @@ -1382,7 +1373,7 @@ def render_integrations_settings( gui.caption(f"Add another connection for {run_title}.") with col2: gui.anchor( - f'   Add Integration', + f'   Add Integration', str(furl(self.current_app_url(RecipeTabs.integrations)) / "add/"), unsafe_allow_html=True, ) @@ -1416,35 +1407,6 @@ def render_integrations_settings( gui.rerun() -def connect( - bi: BotIntegration, current_run: SavedRun, published_run: PublishedRun | None -) -> RedirectException: - """ - Connect the bot integration to the provided saved and published runs. - Returns a redirect exception to the integrations page for that bot integration. - """ - - from daras_ai_v2.slack_bot import send_confirmation_msg - - print(f"Connecting {bi} to {current_run} and {published_run}") - - bi.streaming_enabled = True - bi.saved_run = current_run - if published_run and published_run.saved_run.id == current_run.id: - bi.published_run = published_run - else: - bi.published_run = None - if bi.platform == Platform.SLACK: - bi.slack_create_personal_channels = False - send_confirmation_msg(bi) - bi.save() - - path_params = dict(integration_id=bi.api_integration_id()) - return RedirectException( - VideoBotsPage.current_app_url(RecipeTabs.integrations, path_params=path_params) - ) - - def messages_as_prompt(query_msgs: list[dict]) -> str: return "\n".join( f'{entry["role"]}: """{get_entry_text(entry)}"""' for entry in query_msgs @@ -1610,41 +1572,6 @@ def convo_window_clipper( return 0 -def integration_welcome_screen(title: str): - with gui.center(): - gui.markdown(f"#### {title}") - - col1, col2, col3 = gui.columns( - 3, - column_props=dict( - style=dict( - display="flex", - flexDirection="column", - alignItems="center", - textAlign="center", - maxWidth="300px", - ), - ), - style={"justifyContent": "center"}, - ) - with col1: - gui.html("πŸƒβ€β™€οΈ", style={"fontSize": "4rem"}) - gui.markdown( - """ - 1. Fork & Save your Run - """ - ) - gui.caption("Make changes, Submit & Save your perfect workflow") - with col2: - gui.image(INTEGRATION_IMG, alt="Integrations", style={"height": "5rem"}) - gui.markdown("2. Connect to Slack, Whatsapp or your App") - gui.caption("Or Facebook, Instagram and the web. Wherever your users chat.") - with col3: - gui.html("πŸ“ˆ", style={"fontSize": "4rem"}) - gui.markdown("3. Test, Analyze & Iterate") - gui.caption("Analyze your usage. Update your Saved Run to test changes.") - - class ConnectChoice(typing.NamedTuple): platform: Platform img: str diff --git a/routers/facebook_api.py b/routers/facebook_api.py index ab760b053..68998839a 100644 --- a/routers/facebook_api.py +++ b/routers/facebook_api.py @@ -1,19 +1,23 @@ +import json + import requests from fastapi.responses import RedirectResponse from furl import furl -import json from starlette.background import BackgroundTasks from starlette.requests import Request from starlette.responses import HTMLResponse, Response -from bots.models import BotIntegration, Platform, SavedRun, PublishedRun +from bots.models import BotIntegration, Platform from daras_ai_v2 import settings, db +from daras_ai_v2.bot_integration_connect import ( + connect_bot_to_published_run, + load_published_run_from_state, +) from daras_ai_v2.bots import msg_handler from daras_ai_v2.exceptions import raise_for_status from daras_ai_v2.facebook_bots import WhatsappBot, FacebookBot from daras_ai_v2.fastapi_tricks import fastapi_request_json from daras_ai_v2.functional import map_parallel -from recipes.VideoBots import connect from routers.custom_api_router import CustomAPIRouter app = CustomAPIRouter() @@ -25,16 +29,8 @@ def fb_connect_whatsapp_redirect(request: Request): redirect_url = furl("/login", query_params={"next": request.url}) return RedirectResponse(str(redirect_url)) - connection_state = json.loads(request.query_params.get("state", "{}")) - current_run_id = connection_state.get("current_run_id", None) - published_run_id = connection_state.get("published_run_id", None) - retry_button = ( - f'Retry' - ) - current_run = SavedRun.objects.get(run_id=current_run_id) - published_run = PublishedRun.objects.filter( - published_run_id=published_run_id - ).first() + pr = load_published_run_from_state(request) + retry_button = f'Retry' code = request.query_params.get("code") if not code: @@ -74,7 +70,7 @@ def fb_connect_whatsapp_redirect(request: Request): # {'data': [{'verified_name': 'XXXX', 'code_verification_status': 'VERIFIED', 'display_phone_number': 'XXXX', 'quality_rating': 'UNKNOWN', 'platform_type': 'NOT_APPLICABLE', 'throughput': {'level': 'NOT_APPLICABLE'}, 'last_onboarded_time': '2024-02-22T20:42:16+0000', 'id': 'XXXX'}], 'paging': {'cursors': {'before': 'XXXX', 'after': 'XXXX'}}} phone_numbers = r.json()["data"] - redirect = None + redirect_url = None for phone_number in phone_numbers: business_name = phone_number["verified_name"] display_phone_number = phone_number["display_phone_number"] @@ -120,13 +116,12 @@ def fb_connect_whatsapp_redirect(request: Request): ) r.raise_for_status() - redirect = connect(bi, current_run, published_run) + redirect_url = connect_bot_to_published_run(bi, pr) - return ( - RedirectResponse(url=redirect.url, status_code=redirect.status_code) - if redirect - else HTMLResponse("No phone numbers found!" + retry_button) - ) + if redirect_url: + return RedirectResponse(redirect_url) + else: + return HTMLResponse("No phone numbers found! " + retry_button, status_code=404) @app.get("/__/fb/connect/") @@ -135,21 +130,8 @@ def fb_connect_redirect(request: Request): redirect_url = furl("/login", query_params={"next": request.url}) return RedirectResponse(str(redirect_url)) - connection_state = json.loads(request.query_params.get("state", "{}")) - current_run_id: str | None = connection_state.get("current_run_id", None) - published_run_id: str | None = connection_state.get("published_run_id", None) - retry_button = ( - f'Retry' - ) - if not current_run_id or not published_run_id: - return HTMLResponse( - f"

Oh No! Something went wrong here. Please go back to the integration page and try again or contact support.

", - status_code=400, - ) - current_run = SavedRun.objects.get(run_id=current_run_id) - published_run = PublishedRun.objects.filter( - published_run_id=published_run_id - ).first() + pr = load_published_run_from_state(request) + retry_button = f'Retry' code = request.query_params.get("code") if not code: @@ -188,15 +170,14 @@ def fb_connect_redirect(request: Request): request.user.uid, fb_pages ) - redirect = None - for integration in integrations: - redirect = connect(integration, current_run, published_run) + redirect_url = None + for bi in integrations: + redirect_url = connect_bot_to_published_run(bi, pr) - return ( - RedirectResponse(url=redirect.url, status_code=redirect.status_code) - if redirect - else HTMLResponse("No pages found!" + retry_button) - ) + if redirect_url: + return RedirectResponse(redirect_url) + else: + return HTMLResponse("No pages found! " + retry_button, status_code=404) def get_currently_connected_fb_pages(user_access_token): @@ -271,7 +252,7 @@ def fb_webhook( ).tostr() -def wa_connect_url(current_run_id: str | None, published_run_id: str | None) -> str: +def wa_connect_url(pr_id: int) -> str: return furl( "https://www.facebook.com/v18.0/dialog/oauth", query_params={ @@ -280,9 +261,7 @@ def wa_connect_url(current_run_id: str | None, published_run_id: str | None) -> "redirect_uri": wa_connect_redirect_url, "response_type": "code", "config_id": settings.FB_WHATSAPP_CONFIG_ID, - "state": json.dumps( - dict(current_run_id=current_run_id, published_run_id=published_run_id) - ), + "state": json.dumps(dict(pr_id=pr_id)), }, ).tostr() @@ -295,7 +274,7 @@ def wa_connect_url(current_run_id: str | None, published_run_id: str | None) -> ).tostr() -def fb_connect_url(current_run_id: str | None, published_run_id: str | None) -> str: +def fb_connect_url(pr_id: int) -> str: return furl( "https://www.facebook.com/dialog/oauth", query_params={ @@ -310,9 +289,7 @@ def fb_connect_url(current_run_id: str | None, published_run_id: str | None) -> "pages_show_list", ] ), - "state": json.dumps( - dict(current_run_id=current_run_id, published_run_id=published_run_id) - ), + "state": json.dumps(dict(pr_id=pr_id)), }, ).tostr() diff --git a/routers/root.py b/routers/root.py index 62c0125ca..d49bbd529 100644 --- a/routers/root.py +++ b/routers/root.py @@ -718,9 +718,6 @@ def page_wrapper(request: Request, className=""): gui.html(templates.get_template("login_scripts.html").render(**context)) -INTEGRATION_IMG = "https://storage.googleapis.com/dara-c1b52.appspot.com/daras_ai/media/c3ba2392-d6b9-11ee-a67b-6ace8d8c9501/image.png" - - class TabData(typing.NamedTuple): title: str label: str @@ -749,7 +746,7 @@ class RecipeTabs(TabData, Enum): route=history_route, ) integrations = TabData( - title=f'Facebook, Whatsapp, Slack, Instagram Icons Integrations', + title=f'Facebook, Whatsapp, Slack, Instagram Icons Integrations', label="Integrations", route=integrations_route, ) diff --git a/routers/slack_api.py b/routers/slack_api.py index f5308d4ea..b5f8f3ed7 100644 --- a/routers/slack_api.py +++ b/routers/slack_api.py @@ -15,11 +15,13 @@ Platform, Conversation, Message, - SavedRun, - PublishedRun, ) from bots.tasks import create_personal_channels_for_all_members from daras_ai_v2 import settings +from daras_ai_v2.bot_integration_connect import ( + connect_bot_to_published_run, + load_published_run_from_state, +) from daras_ai_v2.bots import msg_handler from daras_ai_v2.exceptions import raise_for_status from daras_ai_v2.fastapi_tricks import ( @@ -34,12 +36,11 @@ fetch_user_info, parse_slack_response, ) -from recipes.VideoBots import connect router = APIRouter() -def slack_connect_url(current_run_id: str | None, published_run_id: str | None): +def slack_connect_url(pr_id: int): return furl( "https://slack.com/oauth/v2/authorize", query_params=dict( @@ -72,9 +73,7 @@ def slack_connect_url(current_run_id: str | None, published_run_id: str | None): "groups:write.invites", ] ), - state=json.dumps( - dict(current_run_id=current_run_id, published_run_id=published_run_id) - ), + state=json.dumps(dict(pr_id=pr_id)), ), ) @@ -85,16 +84,8 @@ def slack_connect_redirect(request: Request): redirect_url = furl("/login", query_params={"next": request.url}) return RedirectResponse(str(redirect_url)) - connection_state = json.loads(request.query_params.get("state", "{}")) - current_run_id = connection_state.get("current_run_id", None) - published_run_id = connection_state.get("published_run_id", None) - retry_button = ( - f'Retry' - ) - current_run = SavedRun.objects.get(run_id=current_run_id) - published_run = PublishedRun.objects.filter( - published_run_id=published_run_id - ).first() + pr = load_published_run_from_state(request) + retry_button = f'Retry' code = request.query_params.get("code") if not code: @@ -157,12 +148,11 @@ def slack_connect_redirect(request: Request): BotIntegration.objects.filter(pk=bi.pk).update(**config) bi.refresh_from_db() - redirect = connect(bi, current_run, published_run) - if bi.slack_create_personal_channels: create_personal_channels_for_all_members.delay(bi.id) - return RedirectResponse(url=redirect.url, status_code=redirect.status_code) + redirect_url = connect_bot_to_published_run(bi, pr) + return RedirectResponse(redirect_url) @router.post("/__/slack/interaction/") diff --git a/usage_costs/migrations/0017_alter_modelpricing_model_name.py b/usage_costs/migrations/0017_alter_modelpricing_model_name.py new file mode 100644 index 000000000..2fdea2c4a --- /dev/null +++ b/usage_costs/migrations/0017_alter_modelpricing_model_name.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.7 on 2024-08-19 16:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('usage_costs', '0016_alter_modelpricing_model_name'), + ] + + operations = [ + migrations.AlterField( + model_name='modelpricing', + name='model_name', + field=models.CharField(choices=[('gpt_4_o', 'GPT-4o (openai)'), ('gpt_4_o_mini', 'GPT-4o-mini (openai)'), ('gpt_4_turbo_vision', 'GPT-4 Turbo with Vision (openai)'), ('gpt_4_vision', 'GPT-4 Vision (openai) πŸ”»'), ('gpt_4_turbo', 'GPT-4 Turbo (openai)'), ('gpt_4', 'GPT-4 (openai)'), ('gpt_4_32k', 'GPT-4 32K (openai) πŸ”»'), ('gpt_3_5_turbo', 'ChatGPT (openai)'), ('gpt_3_5_turbo_16k', 'ChatGPT 16k (openai)'), ('gpt_3_5_turbo_instruct', 'GPT-3.5 Instruct (openai) πŸ”»'), ('llama3_70b', 'Llama 3 70b (Meta AI)'), ('llama_3_groq_70b_tool_use', 'Llama 3 Groq 70b Tool Use'), ('llama3_8b', 'Llama 3 8b (Meta AI)'), ('llama_3_groq_8b_tool_use', 'Llama 3 Groq 8b Tool Use'), ('llama2_70b_chat', 'Llama 2 70b Chat [Deprecated] (Meta AI)'), ('mixtral_8x7b_instruct_0_1', 'Mixtral 8x7b Instruct v0.1 (Mistral)'), ('gemma_2_9b_it', 'Gemma 2 9B (Google)'), ('gemma_7b_it', 'Gemma 7B (Google)'), ('gemini_1_5_pro', 'Gemini 1.5 Pro (Google)'), ('gemini_1_pro_vision', 'Gemini 1.0 Pro Vision (Google)'), ('gemini_1_pro', 'Gemini 1.0 Pro (Google)'), ('palm2_chat', 'PaLM 2 Chat (Google)'), ('palm2_text', 'PaLM 2 Text (Google)'), ('claude_3_5_sonnet', 'Claude 3.5 Sonnet (Anthropic)'), ('claude_3_opus', 'Claude 3 Opus [L] (Anthropic)'), ('claude_3_sonnet', 'Claude 3 Sonnet [M] (Anthropic)'), ('claude_3_haiku', 'Claude 3 Haiku [S] (Anthropic)'), ('sea_lion_7b_instruct', 'SEA-LION-7B-Instruct [Deprecated] (aisingapore)'), ('llama3_8b_cpt_sea_lion_v2_instruct', 'Llama3 8B CPT SEA-LIONv2 Instruct (aisingapore)'), ('sarvam_2b', 'Sarvam 2B (sarvamai)'), ('text_davinci_003', 'GPT-3.5 Davinci-3 [Deprecated] (openai)'), ('text_davinci_002', 'GPT-3.5 Davinci-2 [Deprecated] (openai)'), ('code_davinci_002', 'Codex [Deprecated] (openai)'), ('text_curie_001', 'Curie [Deprecated] (openai)'), ('text_babbage_001', 'Babbage [Deprecated] (openai)'), ('text_ada_001', 'Ada [Deprecated] (openai)'), ('protogen_2_2', 'Protogen V2.2 (darkstorm2150)'), ('epicdream', 'epiCDream (epinikion)'), ('dream_shaper', 'DreamShaper (Lykon)'), ('dreamlike_2', 'Dreamlike Photoreal 2.0 (dreamlike.art)'), ('sd_2', 'Stable Diffusion v2.1 (stability.ai)'), ('sd_1_5', 'Stable Diffusion v1.5 (RunwayML)'), ('dall_e', 'DALLΒ·E 2 (OpenAI)'), ('dall_e_3', 'DALLΒ·E 3 (OpenAI)'), ('openjourney_2', 'Open Journey v2 beta (PromptHero)'), ('openjourney', 'Open Journey (PromptHero)'), ('analog_diffusion', 'Analog Diffusion (wavymulder)'), ('protogen_5_3', 'Protogen v5.3 (darkstorm2150)'), ('jack_qiao', 'Stable Diffusion v1.4 [Deprecated] (Jack Qiao)'), ('rodent_diffusion_1_5', 'Rodent Diffusion 1.5 [Deprecated] (NerdyRodent)'), ('deepfloyd_if', 'DeepFloyd IF [Deprecated] (stability.ai)'), ('dream_shaper', 'DreamShaper (Lykon)'), ('dreamlike_2', 'Dreamlike Photoreal 2.0 (dreamlike.art)'), ('sd_2', 'Stable Diffusion v2.1 (stability.ai)'), ('sd_1_5', 'Stable Diffusion v1.5 (RunwayML)'), ('dall_e', 'Dall-E (OpenAI)'), ('instruct_pix2pix', '✨ InstructPix2Pix (Tim Brooks)'), ('openjourney_2', 'Open Journey v2 beta (PromptHero) 🐒'), ('openjourney', 'Open Journey (PromptHero) 🐒'), ('analog_diffusion', 'Analog Diffusion (wavymulder) 🐒'), ('protogen_5_3', 'Protogen v5.3 (darkstorm2150) 🐒'), ('jack_qiao', 'Stable Diffusion v1.4 [Deprecated] (Jack Qiao)'), ('rodent_diffusion_1_5', 'Rodent Diffusion 1.5 [Deprecated] (NerdyRodent)'), ('sd_2', 'Stable Diffusion v2.1 (stability.ai)'), ('runway_ml', 'Stable Diffusion v1.5 (RunwayML)'), ('dall_e', 'Dall-E (OpenAI)'), ('jack_qiao', 'Stable Diffusion v1.4 [Deprecated] (Jack Qiao)'), ('wav2lip', 'LipSync (wav2lip)')], help_text='The name of the model. Only used for Display purposes.', max_length=255), + ), + ]