From 9818fe42e7b9315b51101ff477ba317e613f737a Mon Sep 17 00:00:00 2001 From: Dev Aggarwal Date: Thu, 22 Feb 2024 14:39:16 +0300 Subject: [PATCH 01/23] fix wav2lip model id pricing --- scripts/init_self_hosted_pricing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/init_self_hosted_pricing.py b/scripts/init_self_hosted_pricing.py index 6ea45c7d3..d1df62a58 100644 --- a/scripts/init_self_hosted_pricing.py +++ b/scripts/init_self_hosted_pricing.py @@ -31,7 +31,7 @@ def run(): add_model(model_ids[m], m.name) except KeyError: pass - add_model("gooey-gpu/wav2lip_gan.pth", "wav2lip") + add_model("wav2lip_gan.pth", "wav2lip") def add_model(model_id, model_name): From a9941ed85fcb3c1f3eb0e3651e7293e0a635edbd Mon Sep 17 00:00:00 2001 From: Dev Aggarwal Date: Tue, 27 Feb 2024 17:15:28 +0530 Subject: [PATCH 02/23] make InvalidQRCode as UserError --- recipes/QRCodeGenerator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipes/QRCodeGenerator.py b/recipes/QRCodeGenerator.py index b83ab4f2c..a81606c52 100644 --- a/recipes/QRCodeGenerator.py +++ b/recipes/QRCodeGenerator.py @@ -735,7 +735,7 @@ def extract_qr_code_data(img: np.ndarray) -> str: return info -class InvalidQRCode(AssertionError): +class InvalidQRCode(UserError): pass From 6ab6ff8f037358706ebfdc9bfb17a010b297f955 Mon Sep 17 00:00:00 2001 From: Dev Aggarwal Date: Tue, 27 Feb 2024 17:15:47 +0530 Subject: [PATCH 03/23] faster admin view for savedrun --- bots/admin.py | 17 +++++++++++++---- daras_ai_v2/settings.py | 4 +++- gooeysite/urls.py | 3 ++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/bots/admin.py b/bots/admin.py index 524a1c708..23d0a9dd5 100644 --- a/bots/admin.py +++ b/bots/admin.py @@ -294,16 +294,25 @@ class SavedRunAdmin(admin.ModelAdmin): django.db.models.JSONField: {"widget": JSONEditorWidget}, } + def get_queryset(self, request): + return ( + super() + .get_queryset(request) + .prefetch_related( + "parent_version", + "parent_version__published_run", + "parent_version__published_run__saved_run", + ) + ) + def lookup_allowed(self, key, value): if key in ["parent_version__published_run__id__exact"]: return True return super().lookup_allowed(key, value) def view_user(self, saved_run: SavedRun): - return change_obj_url( - AppUser.objects.get(uid=saved_run.uid), - label=f"{saved_run.uid}", - ) + user = AppUser.objects.get(uid=saved_run.uid) + return change_obj_url(user) view_user.short_description = "View User" diff --git a/daras_ai_v2/settings.py b/daras_ai_v2/settings.py index d5d2741ae..706946934 100644 --- a/daras_ai_v2/settings.py +++ b/daras_ai_v2/settings.py @@ -35,7 +35,7 @@ HASHIDS_SALT = config("HASHIDS_SALT", default="") ALLOWED_HOSTS = ["*"] -INTERNAL_IPS = ["127.0.0.1"] +INTERNAL_IPS = ["127.0.0.1", "localhost"] SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") # Application definition @@ -48,6 +48,7 @@ "django.contrib.staticfiles", "bots", "django_extensions", + # "debug_toolbar", # the order matters, since we want to override the admin templates "django.forms", # needed to override admin forms "django.contrib.admin", @@ -67,6 +68,7 @@ "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", + # "debug_toolbar.middleware.DebugToolbarMiddleware", ] ROOT_URLCONF = "gooeysite.urls" diff --git a/gooeysite/urls.py b/gooeysite/urls.py index 6c3809436..fba775500 100644 --- a/gooeysite/urls.py +++ b/gooeysite/urls.py @@ -16,8 +16,9 @@ """ from django.contrib import admin -from django.urls import path +from django.urls import path, include urlpatterns = [ + # path("__debug__/", include("debug_toolbar.urls")), path("", admin.site.urls), ] From ca5d033857027603dc535e6a33fbdda92adb0454 Mon Sep 17 00:00:00 2001 From: clr-li <111320104+clr-li@users.noreply.github.com> Date: Mon, 5 Feb 2024 23:03:31 -0800 Subject: [PATCH 04/23] Added link to support email --- templates/report_email.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/report_email.html b/templates/report_email.html index 098aca9bb..9e3afbf6a 100644 --- a/templates/report_email.html +++ b/templates/report_email.html @@ -19,6 +19,8 @@
Reason for Report: {{ reason_for_report }}

+ In the meantime, you can go through our docs or our video tutorials. +

{% if run_uid != user.uid %} Creator User ID: {{ run_uid }}
From 7cc34933acb9382774e6340d87a02028a72d7ec0 Mon Sep 17 00:00:00 2001 From: Kaustubh Maske Patil <37668193+nikochiko@users.noreply.github.com> Date: Tue, 20 Feb 2024 18:53:09 +0530 Subject: [PATCH 05/23] Copy Link button: add current tab to the URL --- daras_ai_v2/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/daras_ai_v2/base.py b/daras_ai_v2/base.py index 0fc63ea33..62bba29d9 100644 --- a/daras_ai_v2/base.py +++ b/daras_ai_v2/base.py @@ -320,7 +320,7 @@ def _render_social_buttons(self, show_button_text: bool = False): copy_to_clipboard_button( f'{button_text}', - value=self._get_current_app_url(), + value=self.get_tab_url(self.tab), type="secondary", className="mb-0 ms-lg-2", ) From 400cc1add9a98f7610cc8dba086d87eef636c9fa Mon Sep 17 00:00:00 2001 From: clr-li <111320104+clr-li@users.noreply.github.com> Date: Mon, 19 Feb 2024 09:59:32 -0800 Subject: [PATCH 06/23] Fixed title, added desctiption --- templates/login_options.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/login_options.html b/templates/login_options.html index c287f13f0..5e3a5e072 100644 --- a/templates/login_options.html +++ b/templates/login_options.html @@ -1,7 +1,8 @@ {% extends 'base.html' %} {% block head %} - Login - Gooey.AI + Login to Gooey.AI + {% endblock %} From 6088901b35e0ccea8e8f2997eb5df68bb5f3f950 Mon Sep 17 00:00:00 2001 From: clr-li <111320104+clr-li@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:15:02 -0800 Subject: [PATCH 07/23] Fixed title --- templates/base.html | 2 +- templates/login_options.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/base.html b/templates/base.html index e2c180de7..4dfd9d157 100644 --- a/templates/base.html +++ b/templates/base.html @@ -7,7 +7,7 @@ - {{ title }} + {% block title %}{{ title }}{% endblock title %} {% block head %}{% endblock head %} diff --git a/templates/login_options.html b/templates/login_options.html index 5e3a5e072..91cfd9254 100644 --- a/templates/login_options.html +++ b/templates/login_options.html @@ -1,7 +1,7 @@ {% extends 'base.html' %} {% block head %} - Login to Gooey.AI + {% block title %}Login to Gooey.AI{% endblock title %} {% endblock %} From 475d9a7b80748bc49308391f57652c0f6aa874ef Mon Sep 17 00:00:00 2001 From: clr-li <111320104+clr-li@users.noreply.github.com> Date: Mon, 19 Feb 2024 10:16:24 -0800 Subject: [PATCH 08/23] Added title tag --- templates/login_options.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/login_options.html b/templates/login_options.html index 91cfd9254..abb32220c 100644 --- a/templates/login_options.html +++ b/templates/login_options.html @@ -1,7 +1,7 @@ {% extends 'base.html' %} {% block head %} - {% block title %}Login to Gooey.AI{% endblock title %} + {% block title %}Login to Gooey.AI{% endblock title %} {% endblock %} From c4dfc0c034f51d08e832847c4c1de01a4bbdf546 Mon Sep 17 00:00:00 2001 From: Dev Aggarwal Date: Tue, 27 Feb 2024 19:23:16 +0530 Subject: [PATCH 09/23] fix meta title for base.html --- templates/base.html | 1 + templates/login_options.html | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/templates/base.html b/templates/base.html index 4dfd9d157..5af87990a 100644 --- a/templates/base.html +++ b/templates/base.html @@ -8,6 +8,7 @@ {% block title %}{{ title }}{% endblock title %} + {% block head %}{% endblock head %} diff --git a/templates/login_options.html b/templates/login_options.html index abb32220c..4825e7451 100644 --- a/templates/login_options.html +++ b/templates/login_options.html @@ -1,8 +1,9 @@ {% extends 'base.html' %} +{% block title %}Login to Gooey.AI{% endblock title %} + {% block head %} - {% block title %}Login to Gooey.AI{% endblock title %} - + {% endblock %} From 10200a0d60ad45d4214afb2bac6e8743a4834566 Mon Sep 17 00:00:00 2001 From: Alexander Metzger Date: Thu, 15 Feb 2024 15:52:57 -0800 Subject: [PATCH 10/23] allow different analysis json format --- recipes/VideoBotsStats.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/recipes/VideoBotsStats.py b/recipes/VideoBotsStats.py index a8fd4e541..10e5ad527 100644 --- a/recipes/VideoBotsStats.py +++ b/recipes/VideoBotsStats.py @@ -543,6 +543,7 @@ def calculate_stats_binned_by_time( df["Msgs_per_user"] = df["Messages_Sent"] / df["Senders"] df.fillna(0, inplace=True) df = df.round(0).astype("int32", errors="ignore") + df = df.sort_values(by=["date"], ascending=True).reset_index() return df def plot_graphs(self, view, df): @@ -806,8 +807,6 @@ def get_tabular_data( neg_feedbacks: FeedbackQuerySet = Feedback.objects.filter( message__conversation__bot_integration=bi, rating=Feedback.Rating.RATING_THUMBS_DOWN, - created_at__date__gte=start_date, - created_at__date__lte=end_date, ) # type: ignore if start_date and end_date: neg_feedbacks = neg_feedbacks.filter( @@ -820,9 +819,11 @@ def get_tabular_data( successful_messages: MessageQuerySet = Message.objects.filter( conversation__bot_integration=bi, analysis_result__contains={"Answered": True}, - created_at__date__gte=start_date, - created_at__date__lte=end_date, ) # type: ignore + successful_messages |= Message.objects.filter( + conversation__bot_integration=bi, + analysis_result__contains={"assistant": {"answer": "Found"}}, + ) if start_date and end_date: successful_messages = successful_messages.filter( created_at__date__gte=start_date, created_at__date__lte=end_date @@ -834,9 +835,11 @@ def get_tabular_data( unsuccessful_messages: MessageQuerySet = Message.objects.filter( conversation__bot_integration=bi, analysis_result__contains={"Answered": False}, - created_at__date__gte=start_date, - created_at__date__lte=end_date, ) # type: ignore + unsuccessful_messages |= Message.objects.filter( + conversation__bot_integration=bi, + analysis_result__contains={"assistant": {"answer": "Missing"}}, + ) if start_date and end_date: unsuccessful_messages = unsuccessful_messages.filter( created_at__date__gte=start_date, created_at__date__lte=end_date From fe4e2fc4d3854d829a618aac61e843b61c9f4f2d Mon Sep 17 00:00:00 2001 From: Dev Aggarwal Date: Tue, 27 Feb 2024 19:35:12 +0530 Subject: [PATCH 11/23] use | on Q instead of | on queryset --- recipes/VideoBotsStats.py | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/recipes/VideoBotsStats.py b/recipes/VideoBotsStats.py index 10e5ad527..4781d4b40 100644 --- a/recipes/VideoBotsStats.py +++ b/recipes/VideoBotsStats.py @@ -33,7 +33,7 @@ TruncYear, Concat, ) -from django.db.models import Count, Avg +from django.db.models import Count, Avg, Q ID_COLUMNS = [ "conversation__fb_page_id", @@ -817,13 +817,10 @@ def get_tabular_data( df["Bot"] = bi.name elif details == "Answered Successfully": successful_messages: MessageQuerySet = Message.objects.filter( + Q(analysis_result__contains={"Answered": True}) + | Q(analysis_result__contains={"assistant": {"answer": "Found"}}), conversation__bot_integration=bi, - analysis_result__contains={"Answered": True}, ) # type: ignore - successful_messages |= Message.objects.filter( - conversation__bot_integration=bi, - analysis_result__contains={"assistant": {"answer": "Found"}}, - ) if start_date and end_date: successful_messages = successful_messages.filter( created_at__date__gte=start_date, created_at__date__lte=end_date @@ -833,13 +830,10 @@ def get_tabular_data( df["Bot"] = bi.name elif details == "Answered Unsuccessfully": unsuccessful_messages: MessageQuerySet = Message.objects.filter( + Q(analysis_result__contains={"Answered": False}) + | Q(analysis_result__contains={"assistant": {"answer": "Missing"}}), conversation__bot_integration=bi, - analysis_result__contains={"Answered": False}, ) # type: ignore - unsuccessful_messages |= Message.objects.filter( - conversation__bot_integration=bi, - analysis_result__contains={"assistant": {"answer": "Missing"}}, - ) if start_date and end_date: unsuccessful_messages = unsuccessful_messages.filter( created_at__date__gte=start_date, created_at__date__lte=end_date From bad21da67a6ba5088ca241ddd80306e007ea0425 Mon Sep 17 00:00:00 2001 From: Alexander Metzger Date: Thu, 15 Feb 2024 16:04:27 -0800 Subject: [PATCH 12/23] remove landbot, admins see all integrations on published run --- recipes/VideoBots.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/recipes/VideoBots.py b/recipes/VideoBots.py index 410416342..822869f9f 100644 --- a/recipes/VideoBots.py +++ b/recipes/VideoBots.py @@ -975,11 +975,11 @@ def render_selected_tab(self, selected_tab): unsafe_allow_html=True, ) - st.write("---") - st.text_input( - "###### 🤖 [Landbot](https://landbot.io/) URL", key="landbot_url" - ) - show_landbot_widget() + # st.write("---") + # st.text_input( + # "###### 🤖 [Landbot](https://landbot.io/) URL", key="landbot_url" + # ) + # show_landbot_widget() def messenger_bot_integration(self): from routers.facebook_api import ig_connect_url, fb_connect_url @@ -1028,15 +1028,21 @@ def messenger_bot_integration(self): st.button("🔄 Refresh") + current_run, published_run = self.get_runs_from_query_params( + *extract_query_params(gooey_get_query_params()) + ) # type: ignore + integrations: QuerySet[BotIntegration] = BotIntegration.objects.filter( billing_account_uid=self.request.user.uid ).order_by("platform", "-created_at") + + if self.is_current_user_admin(): + integrations |= BotIntegration.objects.filter( + published_run=published_run + ).order_by("platform", "-created_at") if not integrations: return - current_run, published_run = self.get_runs_from_query_params( - *extract_query_params(gooey_get_query_params()) - ) for bi in integrations: is_connected = (bi.saved_run == current_run) or ( ( From d4e2631f7751bc379365e7560dc231afd094a87d Mon Sep 17 00:00:00 2001 From: Dev Aggarwal Date: Tue, 27 Feb 2024 20:15:55 +0530 Subject: [PATCH 13/23] fix the admin query for integrations --- recipes/VideoBots.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/recipes/VideoBots.py b/recipes/VideoBots.py index 822869f9f..ecf5a99e5 100644 --- a/recipes/VideoBots.py +++ b/recipes/VideoBots.py @@ -4,7 +4,7 @@ import os.path import typing -from django.db.models import QuerySet +from django.db.models import QuerySet, Q from furl import furl from pydantic import BaseModel, Field @@ -1032,14 +1032,21 @@ def messenger_bot_integration(self): *extract_query_params(gooey_get_query_params()) ) # type: ignore + integrations_q = Q(billing_account_uid=self.request.user.uid) + + # show admins all the bots connected to the current run + if self.is_current_user_admin(): + integrations_q |= Q(saved_run=current_run) + if published_run: + integrations_q |= Q( + saved_run__example_id=published_run.published_run_id + ) + integrations_q |= Q(published_run=published_run) + integrations: QuerySet[BotIntegration] = BotIntegration.objects.filter( - billing_account_uid=self.request.user.uid + integrations_q ).order_by("platform", "-created_at") - if self.is_current_user_admin(): - integrations |= BotIntegration.objects.filter( - published_run=published_run - ).order_by("platform", "-created_at") if not integrations: return From 35e021ad42507e3e21c17cbe1880fb6a929c4a00 Mon Sep 17 00:00:00 2001 From: Kaustubh Maske Patil <37668193+nikochiko@users.noreply.github.com> Date: Mon, 12 Feb 2024 22:16:04 +0530 Subject: [PATCH 14/23] Show preferred fields alongside required fields in API tab --- daras_ai_v2/base.py | 50 ++++++++++++++++++++++++++++---------- recipes/QRCodeGenerator.py | 9 +++++++ routers/root.py | 9 ++----- tests/test_apis.py | 11 +++------ 4 files changed, 52 insertions(+), 27 deletions(-) diff --git a/daras_ai_v2/base.py b/daras_ai_v2/base.py index 62bba29d9..576c060bf 100644 --- a/daras_ai_v2/base.py +++ b/daras_ai_v2/base.py @@ -1791,8 +1791,8 @@ def run_as_api_tab(self): as_async = st.checkbox("##### Run Async") as_form_data = st.checkbox("##### Upload Files via Form Data") - request_body = get_example_request_body( - self.RequestModel, st.session_state, include_all=include_all + request_body = self.get_example_request_body( + st.session_state, include_all=include_all ) response_body = self.get_example_response_body( st.session_state, as_async=as_async, include_all=include_all @@ -1840,6 +1840,28 @@ def get_price_roundoff(self, state: dict) -> int: def get_raw_price(self, state: dict) -> float: return self.price + @classmethod + def get_example_preferred_fields(cls, state: dict) -> list[str]: + """ + Fields that are not required, but are preferred to be shown in the example. + """ + return [] + + @classmethod + def get_example_request_body( + cls, + state: dict, + include_all: bool = False, + ) -> dict: + preferred_fields = cls.get_example_preferred_fields(state) + return extract_model_fields( + cls.RequestModel, + state, + condition=lambda field_name, field: include_all + or field.required + or field_name in preferred_fields, + ) + def get_example_response_body( self, state: dict, @@ -1854,6 +1876,11 @@ def get_example_response_body( run_id=run_id, uid=self.request.user and self.request.user.uid, ) + output = extract_model_fields( + self.ResponseModel, + state, + condition=lambda field_name, field: include_all or field.required, + ) if as_async: return dict( run_id=run_id, @@ -1861,18 +1888,14 @@ def get_example_response_body( created_at=created_at, run_time_sec=st.session_state.get(StateKeys.run_time, 0), status="completed", - output=get_example_request_body( - self.ResponseModel, state, include_all=include_all - ), + output=output, ) else: return dict( id=run_id, url=web_url, created_at=created_at, - output=get_example_request_body( - self.ResponseModel, state, include_all=include_all - ), + output=output, ) def additional_notes(self) -> str | None: @@ -1920,15 +1943,16 @@ def render_output_caption(): st.caption(caption, unsafe_allow_html=True) -def get_example_request_body( - request_model: typing.Type[BaseModel], +def extract_model_fields( + model: typing.Type[BaseModel], state: dict, - include_all: bool = False, + condition: typing.Callable[[str, "pydantic.ModelField"], bool], ) -> dict: + """Only returns required fields unless include_all is set to True.""" return { field_name: state.get(field_name) - for field_name, field in request_model.__fields__.items() - if include_all or field.required + for field_name, field in model.__fields__.items() + if condition(field_name, field) } diff --git a/recipes/QRCodeGenerator.py b/recipes/QRCodeGenerator.py index a81606c52..2e3a30002 100644 --- a/recipes/QRCodeGenerator.py +++ b/recipes/QRCodeGenerator.py @@ -141,6 +141,15 @@ def related_workflows(self) -> list: EmailFaceInpaintingPage, ] + @classmethod + def get_example_preferred_fields(cls, state: dict) -> list[str]: + if state.get("qr_code_file"): + return ["qr_code_file"] + elif state.get("qr_code_input_image"): + return ["qr_code_input_image"] + else: + return ["qr_code_data"] + def render_form_v2(self): st.text_area( """ diff --git a/routers/root.py b/routers/root.py index 0e852a8d9..27cb62934 100644 --- a/routers/root.py +++ b/routers/root.py @@ -26,10 +26,7 @@ from daras_ai_v2.all_pages import all_api_pages, normalize_slug, page_slug_map from daras_ai_v2.api_examples_widget import api_example_generator from daras_ai_v2.asr import FFMPEG_WAV_ARGS, check_wav_audio_format -from daras_ai_v2.base import ( - RedirectException, - get_example_request_body, -) +from daras_ai_v2.base import RedirectException from daras_ai_v2.bots import request_json from daras_ai_v2.copy_to_clipboard_button_widget import copy_to_clipboard_scripts from daras_ai_v2.db import FIREBASE_SESSION_COOKIE @@ -299,9 +296,7 @@ def _api_docs_page(request): page = workflow.page_cls(request=request) state = page.get_root_published_run().saved_run.to_dict() - request_body = get_example_request_body( - page.RequestModel, state, include_all=include_all - ) + request_body = page.get_example_request_body(state, include_all=include_all) response_body = page.get_example_response_body( state, as_async=as_async, include_all=include_all ) diff --git a/tests/test_apis.py b/tests/test_apis.py index 96c27c7ef..ddd6e0e48 100644 --- a/tests/test_apis.py +++ b/tests/test_apis.py @@ -6,10 +6,7 @@ from auth.auth_backend import force_authentication from bots.models import SavedRun, Workflow from daras_ai_v2.all_pages import all_test_pages -from daras_ai_v2.base import ( - BasePage, - get_example_request_body, -) +from daras_ai_v2.base import BasePage from server import app MAX_WORKERS = 20 @@ -27,7 +24,7 @@ def _test_api_sync(page_cls: typing.Type[BasePage]): state = page_cls.recipe_doc_sr().state r = client.post( f"/v2/{page_cls.slug_versions[0]}/", - json=get_example_request_body(page_cls.RequestModel, state), + json=page_cls.get_example_request_body(state), headers={"Authorization": f"Token None"}, allow_redirects=False, ) @@ -45,7 +42,7 @@ def _test_api_async(page_cls: typing.Type[BasePage]): r = client.post( f"/v3/{page_cls.slug_versions[0]}/async/", - json=get_example_request_body(page_cls.RequestModel, state), + json=page_cls.get_example_request_body(state), headers={"Authorization": f"Token None"}, allow_redirects=False, ) @@ -81,7 +78,7 @@ def _test_apis_examples(sr: SavedRun): page_cls = Workflow(sr.workflow).page_cls r = client.post( f"/v2/{page_cls.slug_versions[0]}/?example_id={sr.example_id}", - json=get_example_request_body(page_cls.RequestModel, state), + json=page_cls.get_example_request_body(state), headers={"Authorization": f"Token None"}, allow_redirects=False, ) From 061d2c2c9d28851a070063d96dc7c931cd2f523d Mon Sep 17 00:00:00 2001 From: Kaustubh Maske Patil <37668193+nikochiko@users.noreply.github.com> Date: Tue, 13 Feb 2024 18:07:48 +0530 Subject: [PATCH 15/23] Add preferred example fields for 8 more recipes --- recipes/CompareLLM.py | 4 ++++ recipes/CompareText2Img.py | 4 ++++ recipes/DocSearch.py | 4 ++++ recipes/DocSummary.py | 4 ++++ recipes/EmailFaceInpainting.py | 4 ++++ recipes/Img2Img.py | 4 ++++ recipes/TextToSpeech.py | 4 ++++ recipes/asr.py | 4 ++++ 8 files changed, 32 insertions(+) diff --git a/recipes/CompareLLM.py b/recipes/CompareLLM.py index 2101aece5..7e709a18d 100644 --- a/recipes/CompareLLM.py +++ b/recipes/CompareLLM.py @@ -59,6 +59,10 @@ def preview_image(self, state: dict) -> str | None: def preview_description(self, state: dict) -> str: return "Which language model works best your prompt? Compare your text generations across multiple large language models (LLMs) like OpenAI's evolving and latest ChatGPT engines and others like Curie, Ada, Babbage." + @classmethod + def get_example_preferred_fields(cls, state: dict) -> list[str]: + return ["input_prompt", "selected_models"] + def render_form_v2(self): st.text_area( """ diff --git a/recipes/CompareText2Img.py b/recipes/CompareText2Img.py index dc5ea1ae2..f1249c01b 100644 --- a/recipes/CompareText2Img.py +++ b/recipes/CompareText2Img.py @@ -77,6 +77,10 @@ class ResponseModel(BaseModel): typing.Literal[tuple(e.name for e in Text2ImgModels)], list[str] ] + @classmethod + def get_example_preferred_fields(cls, state: dict) -> list[str]: + return ["selected_models"] + def preview_image(self, state: dict) -> str | None: return DEFAULT_COMPARE_TEXT2IMG_META_IMG diff --git a/recipes/DocSearch.py b/recipes/DocSearch.py index 6d7977c25..f3174da32 100644 --- a/recipes/DocSearch.py +++ b/recipes/DocSearch.py @@ -81,6 +81,10 @@ class ResponseModel(BaseModel): final_prompt: str final_search_query: str | None + @classmethod + def get_example_preferred_fields(self, state: dict) -> list[str]: + return ["documents"] + def render_form_v2(self): st.text_area("#### Search Query", key="search_query") document_uploader("#### Documents") diff --git a/recipes/DocSummary.py b/recipes/DocSummary.py index 476bd5da6..8955d5e59 100644 --- a/recipes/DocSummary.py +++ b/recipes/DocSummary.py @@ -80,6 +80,10 @@ class ResponseModel(BaseModel): prompt_tree: PromptTree | None final_prompt: str + @classmethod + def get_example_preferred_fields(cls, state: dict) -> list[str]: + return ["task_instructions", "merge_instructions"] + def preview_image(self, state: dict) -> str | None: return DEFAULT_DOC_SUMMARY_META_IMG diff --git a/recipes/EmailFaceInpainting.py b/recipes/EmailFaceInpainting.py index 4b8b50d1e..385596f6d 100644 --- a/recipes/EmailFaceInpainting.py +++ b/recipes/EmailFaceInpainting.py @@ -88,6 +88,10 @@ class ResponseModel(BaseModel): output_images: list[str] email_sent: bool = False + @classmethod + def get_example_preferred_fields(self, state: dict) -> list[str]: + return ["email_address"] + def preview_image(self, state: dict) -> str | None: return DEFAULT_EMAIL_FACE_INPAINTING_META_IMG diff --git a/recipes/Img2Img.py b/recipes/Img2Img.py index e2713aaa2..2e5796c7f 100644 --- a/recipes/Img2Img.py +++ b/recipes/Img2Img.py @@ -72,6 +72,10 @@ class RequestModel(BaseModel): class ResponseModel(BaseModel): output_images: list[str] + @classmethod + def get_example_preferred_fields(self, state: dict) -> list[str]: + return ["text_prompt"] + def preview_image(self, state: dict) -> str | None: return DEFAULT_IMG2IMG_META_IMG diff --git a/recipes/TextToSpeech.py b/recipes/TextToSpeech.py index f56c3957d..7d8f301fe 100644 --- a/recipes/TextToSpeech.py +++ b/recipes/TextToSpeech.py @@ -81,6 +81,10 @@ class ResponseModel(BaseModel): def fallback_preivew_image(self) -> str | None: return DEFAULT_TTS_META_IMG + @classmethod + def get_example_preferred_fields(cls, state: dict) -> list[str]: + return ["tts_provider"] + def preview_description(self, state: dict) -> str: return "Input your text, pick a voice & a Text-to-Speech AI engine to create audio. Compare the best voice generators from Google, UberDuck.ai & more to add automated voices to your podcast, YouTube videos, website, or app." diff --git a/recipes/asr.py b/recipes/asr.py index 7dcea4249..3542614c8 100644 --- a/recipes/asr.py +++ b/recipes/asr.py @@ -48,6 +48,10 @@ class ResponseModel(BaseModel): raw_output_text: list[str] | None output_text: list[str | AsrOutputJson] + @classmethod + def get_example_preferred_fields(cls, state: dict) -> list[str]: + return ["selected_model", "language", "google_translate_target"] + def preview_image(self, state: dict) -> str | None: return DEFAULT_ASR_META_IMG From 6103ee4a23cab053f8ef879638f028cc26c93bf1 Mon Sep 17 00:00:00 2001 From: Dev Aggarwal Date: Tue, 27 Feb 2024 20:35:44 +0530 Subject: [PATCH 16/23] avoid un-necessary lambda --- daras_ai_v2/base.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/daras_ai_v2/base.py b/daras_ai_v2/base.py index 576c060bf..4053d45c5 100644 --- a/daras_ai_v2/base.py +++ b/daras_ai_v2/base.py @@ -1853,13 +1853,11 @@ def get_example_request_body( state: dict, include_all: bool = False, ) -> dict: - preferred_fields = cls.get_example_preferred_fields(state) return extract_model_fields( cls.RequestModel, state, - condition=lambda field_name, field: include_all - or field.required - or field_name in preferred_fields, + include_all=include_all, + preferred_fields=cls.get_example_preferred_fields(state), ) def get_example_response_body( @@ -1879,7 +1877,7 @@ def get_example_response_body( output = extract_model_fields( self.ResponseModel, state, - condition=lambda field_name, field: include_all or field.required, + include_all=include_all, ) if as_async: return dict( @@ -1946,13 +1944,18 @@ def render_output_caption(): def extract_model_fields( model: typing.Type[BaseModel], state: dict, - condition: typing.Callable[[str, "pydantic.ModelField"], bool], + include_all: bool = False, + preferred_fields: list[str] = None, ) -> dict: """Only returns required fields unless include_all is set to True.""" return { field_name: state.get(field_name) for field_name, field in model.__fields__.items() - if condition(field_name, field) + if ( + include_all + or field.required + or (preferred_fields and field_name in preferred_fields) + ) } From c84afedf96b41df0e2ef744f92a8e9ba0ba54cee Mon Sep 17 00:00:00 2001 From: Dev Aggarwal Date: Tue, 27 Feb 2024 20:37:07 +0530 Subject: [PATCH 17/23] show all response fields by default because its confusing --- daras_ai_v2/base.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/daras_ai_v2/base.py b/daras_ai_v2/base.py index 4053d45c5..d40f9358d 100644 --- a/daras_ai_v2/base.py +++ b/daras_ai_v2/base.py @@ -1874,11 +1874,7 @@ def get_example_response_body( run_id=run_id, uid=self.request.user and self.request.user.uid, ) - output = extract_model_fields( - self.ResponseModel, - state, - include_all=include_all, - ) + output = extract_model_fields(self.ResponseModel, state, include_all=True) if as_async: return dict( run_id=run_id, From 7cd707c054aaf5661893e3c61a5557129952748a Mon Sep 17 00:00:00 2001 From: Kaustubh Maske Patil <37668193+nikochiko@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:14:11 +0530 Subject: [PATCH 18/23] Fix pricing for multiple outputs --- recipes/CompareText2Img.py | 2 +- recipes/GoogleGPT.py | 3 +++ recipes/VideoBots.py | 6 ++++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/recipes/CompareText2Img.py b/recipes/CompareText2Img.py index f1249c01b..7b1f421de 100644 --- a/recipes/CompareText2Img.py +++ b/recipes/CompareText2Img.py @@ -268,4 +268,4 @@ def get_raw_price(self, state: dict) -> int: total += 15 case _: total += 2 - return total + return total * state.get("num_outputs", 1) diff --git a/recipes/GoogleGPT.py b/recipes/GoogleGPT.py index 35d650acb..48c512560 100644 --- a/recipes/GoogleGPT.py +++ b/recipes/GoogleGPT.py @@ -279,3 +279,6 @@ def run_v2( max_tokens=request.max_tokens, avoid_repetition=request.avoid_repetition, ) + + def get_raw_price(self, state: dict) -> float: + return self.price * state.get("num_outputs", 1) diff --git a/recipes/VideoBots.py b/recipes/VideoBots.py index ecf5a99e5..e368e8c21 100644 --- a/recipes/VideoBots.py +++ b/recipes/VideoBots.py @@ -601,11 +601,13 @@ def get_raw_price(self, state: dict): "raw_tts_text", state.get("raw_output_text", []) ) tts_state = {"text_prompt": "".join(output_text_list)} - return super().get_raw_price(state) + TextToSpeechPage().get_raw_price( + total = super().get_raw_price(state) + TextToSpeechPage().get_raw_price( tts_state ) case _: - return super().get_raw_price(state) + total = super().get_raw_price(state) + + return total * state.get("num_outputs", 1) def additional_notes(self): tts_provider = st.session_state.get("tts_provider") From 306a685dd4e18cfa94a3e8846cb5d7ba196d7816 Mon Sep 17 00:00:00 2001 From: Kaustubh Maske Patil <37668193+nikochiko@users.noreply.github.com> Date: Tue, 20 Feb 2024 19:34:14 +0530 Subject: [PATCH 19/23] Fix pricing for more recipes --- recipes/DocSearch.py | 5 +++-- recipes/DocSummary.py | 3 +++ recipes/FaceInpainting.py | 6 ++++-- recipes/GoogleImageGen.py | 3 +++ recipes/Img2Img.py | 6 ++++-- recipes/ObjectInpainting.py | 6 ++++-- recipes/SmartGPT.py | 3 +++ recipes/Text2Audio.py | 3 +++ 8 files changed, 27 insertions(+), 8 deletions(-) diff --git a/recipes/DocSearch.py b/recipes/DocSearch.py index f3174da32..322cafbd9 100644 --- a/recipes/DocSearch.py +++ b/recipes/DocSearch.py @@ -209,9 +209,10 @@ def run_v2( def get_raw_price(self, state: dict) -> float: name = state.get("selected_model") try: - return llm_price[LargeLanguageModels[name]] * 2 + unit_price = llm_price[LargeLanguageModels[name]] * 2 except KeyError: - return 10 + unit_price = 10 + return unit_price * state.get("num_outputs", 1) def render_documents(state, label="**Documents**", *, key="documents"): diff --git a/recipes/DocSummary.py b/recipes/DocSummary.py index 8955d5e59..8dd2c0042 100644 --- a/recipes/DocSummary.py +++ b/recipes/DocSummary.py @@ -185,6 +185,9 @@ def run(self, state: dict) -> typing.Iterator[str | None]: case _: raise NotImplementedError(f"{chain_type} not implemented") + def get_raw_price(self, state: dict) -> float: + return self.price * state.get("num_outputs", 1) + MAP_REDUCE_PROMPT = """ {documents} diff --git a/recipes/FaceInpainting.py b/recipes/FaceInpainting.py index 6d787bba6..2f1b6a0b0 100644 --- a/recipes/FaceInpainting.py +++ b/recipes/FaceInpainting.py @@ -336,6 +336,8 @@ def get_raw_price(self, state: dict) -> int: selected_model = state.get("selected_model") match selected_model: case InpaintingModels.dall_e.name: - return 20 + unit_price = 20 case _: - return 5 + unit_price = 5 + + return unit_price * state.get("num_outputs", 1) diff --git a/recipes/GoogleImageGen.py b/recipes/GoogleImageGen.py index 278128a37..a935e54cb 100644 --- a/recipes/GoogleImageGen.py +++ b/recipes/GoogleImageGen.py @@ -238,3 +238,6 @@ def render_example(self, state: dict): def preview_description(self, state: dict) -> str: return "Enter a Google Image Search query + your Img2Img text prompt describing how to alter the result to create a unique, relevant ai generated images for any search query." + + def get_raw_price(self, state: dict) -> float: + return super().get_raw_price(state) * state.get("num_outputs", 1) diff --git a/recipes/Img2Img.py b/recipes/Img2Img.py index 2e5796c7f..41a1139e4 100644 --- a/recipes/Img2Img.py +++ b/recipes/Img2Img.py @@ -206,6 +206,8 @@ def get_raw_price(self, state: dict) -> int: selected_model = state.get("selected_model") match selected_model: case Img2ImgModels.dall_e.name: - return 20 + unit_price = 20 case _: - return 5 + unit_price = 5 + + return unit_price * state.get("num_outputs", 1) diff --git a/recipes/ObjectInpainting.py b/recipes/ObjectInpainting.py index a1e0c2449..db12760bf 100644 --- a/recipes/ObjectInpainting.py +++ b/recipes/ObjectInpainting.py @@ -314,6 +314,8 @@ def get_raw_price(self, state: dict) -> int: selected_model = state.get("selected_model") match selected_model: case InpaintingModels.dall_e.name: - return 20 + unit_price = 20 case _: - return 5 + unit_price = 5 + + return unit_price * state.get("num_outputs", 1) diff --git a/recipes/SmartGPT.py b/recipes/SmartGPT.py index 0aabcfefb..10c4a3fe7 100644 --- a/recipes/SmartGPT.py +++ b/recipes/SmartGPT.py @@ -204,6 +204,9 @@ def render_steps(self): def preview_description(self, state: dict) -> str: return "SmartGPT is a cutting-edge AI technology that can be used to generate natural language responses to any given input. We have combined the power of [CoT](https://arxiv.org/abs/2305.02897), [Reflexion](https://arxiv.org/abs/2303.11366) & [DERA](https://arxiv.org/abs/2303.17071) into one pipeline so that you can use ChatGPT to its full potential! Input your prompt + a reflection/research prompt + a resolver prompt to use SmartGPT for enhanced text generation, natural language and incredible question-answer results." + def get_raw_price(self, state: dict) -> float: + return self.price * state.get("num_outputs", 1) + def answers_as_prompt(texts: list[str], sep="\n\n") -> str: return sep.join( diff --git a/recipes/Text2Audio.py b/recipes/Text2Audio.py index f7ddf5aab..5140fd2c2 100644 --- a/recipes/Text2Audio.py +++ b/recipes/Text2Audio.py @@ -140,6 +140,9 @@ def render_example(self, state: dict): def preview_description(self, state: dict) -> str: return "Generate AI Music with text instruction prompts. AudiLDM is capable of generating realistic audio samples by process any text input. Learn more [here](https://huggingface.co/cvssp/audioldm-m-full)." + def get_raw_price(self, state: dict) -> float: + return super().get_raw_price(state) * state.get("num_outputs", 1) + def _render_output(state): selected_models = state.get("selected_models", []) From df4d1a92ad920d7c6722f06b0ffdaf8566f00e74 Mon Sep 17 00:00:00 2001 From: Kaustubh Maske Patil <37668193+nikochiko@users.noreply.github.com> Date: Tue, 20 Feb 2024 22:00:28 +0530 Subject: [PATCH 20/23] Move num_outputs pricing logic to base.py --- daras_ai_v2/base.py | 2 +- recipes/DocSummary.py | 3 --- recipes/GoogleGPT.py | 3 --- recipes/GoogleImageGen.py | 3 --- recipes/SmartGPT.py | 3 --- recipes/Text2Audio.py | 3 --- 6 files changed, 1 insertion(+), 16 deletions(-) diff --git a/daras_ai_v2/base.py b/daras_ai_v2/base.py index d40f9358d..26825a087 100644 --- a/daras_ai_v2/base.py +++ b/daras_ai_v2/base.py @@ -1838,7 +1838,7 @@ def get_price_roundoff(self, state: dict) -> int: return max(1, math.ceil(self.get_raw_price(state))) def get_raw_price(self, state: dict) -> float: - return self.price + return self.price * state.get("num_outputs", 1) @classmethod def get_example_preferred_fields(cls, state: dict) -> list[str]: diff --git a/recipes/DocSummary.py b/recipes/DocSummary.py index 8dd2c0042..8955d5e59 100644 --- a/recipes/DocSummary.py +++ b/recipes/DocSummary.py @@ -185,9 +185,6 @@ def run(self, state: dict) -> typing.Iterator[str | None]: case _: raise NotImplementedError(f"{chain_type} not implemented") - def get_raw_price(self, state: dict) -> float: - return self.price * state.get("num_outputs", 1) - MAP_REDUCE_PROMPT = """ {documents} diff --git a/recipes/GoogleGPT.py b/recipes/GoogleGPT.py index 48c512560..35d650acb 100644 --- a/recipes/GoogleGPT.py +++ b/recipes/GoogleGPT.py @@ -279,6 +279,3 @@ def run_v2( max_tokens=request.max_tokens, avoid_repetition=request.avoid_repetition, ) - - def get_raw_price(self, state: dict) -> float: - return self.price * state.get("num_outputs", 1) diff --git a/recipes/GoogleImageGen.py b/recipes/GoogleImageGen.py index a935e54cb..278128a37 100644 --- a/recipes/GoogleImageGen.py +++ b/recipes/GoogleImageGen.py @@ -238,6 +238,3 @@ def render_example(self, state: dict): def preview_description(self, state: dict) -> str: return "Enter a Google Image Search query + your Img2Img text prompt describing how to alter the result to create a unique, relevant ai generated images for any search query." - - def get_raw_price(self, state: dict) -> float: - return super().get_raw_price(state) * state.get("num_outputs", 1) diff --git a/recipes/SmartGPT.py b/recipes/SmartGPT.py index 10c4a3fe7..0aabcfefb 100644 --- a/recipes/SmartGPT.py +++ b/recipes/SmartGPT.py @@ -204,9 +204,6 @@ def render_steps(self): def preview_description(self, state: dict) -> str: return "SmartGPT is a cutting-edge AI technology that can be used to generate natural language responses to any given input. We have combined the power of [CoT](https://arxiv.org/abs/2305.02897), [Reflexion](https://arxiv.org/abs/2303.11366) & [DERA](https://arxiv.org/abs/2303.17071) into one pipeline so that you can use ChatGPT to its full potential! Input your prompt + a reflection/research prompt + a resolver prompt to use SmartGPT for enhanced text generation, natural language and incredible question-answer results." - def get_raw_price(self, state: dict) -> float: - return self.price * state.get("num_outputs", 1) - def answers_as_prompt(texts: list[str], sep="\n\n") -> str: return sep.join( diff --git a/recipes/Text2Audio.py b/recipes/Text2Audio.py index 5140fd2c2..f7ddf5aab 100644 --- a/recipes/Text2Audio.py +++ b/recipes/Text2Audio.py @@ -140,9 +140,6 @@ def render_example(self, state: dict): def preview_description(self, state: dict) -> str: return "Generate AI Music with text instruction prompts. AudiLDM is capable of generating realistic audio samples by process any text input. Learn more [here](https://huggingface.co/cvssp/audioldm-m-full)." - def get_raw_price(self, state: dict) -> float: - return super().get_raw_price(state) * state.get("num_outputs", 1) - def _render_output(state): selected_models = state.get("selected_models", []) From 37df128175d6bf0d9404b853a8f8b6cfc2056d1f Mon Sep 17 00:00:00 2001 From: Kaustubh Maske Patil <37668193+nikochiko@users.noreply.github.com> Date: Thu, 25 Jan 2024 19:23:41 +0530 Subject: [PATCH 21/23] Add SEO meta tags for /account/ page --- routers/billing.py | 1 + templates/account.html | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/routers/billing.py b/routers/billing.py index 5f2f2cb06..2f4db598c 100644 --- a/routers/billing.py +++ b/routers/billing.py @@ -114,6 +114,7 @@ def account(request: Request): is_admin = request.user.email in settings.ADMIN_EMAILS context = { + "title": "Account • Gooey.AI", "request": request, "settings": settings, "available_subscriptions": available_subscriptions, diff --git a/templates/account.html b/templates/account.html index 2755facc1..6a803490b 100644 --- a/templates/account.html +++ b/templates/account.html @@ -1,5 +1,11 @@ {% extends 'base.html' %} +{% block head %} + + + +{% endblock %} + {% block content %} -{% endblock content %} \ No newline at end of file +{% endblock content %} From 8f05504793cfaaa578bf2c7c6cfb442772c4eec5 Mon Sep 17 00:00:00 2001 From: Kaustubh Maske Patil <37668193+nikochiko@users.noreply.github.com> Date: Thu, 1 Feb 2024 17:33:21 +0530 Subject: [PATCH 22/23] Use furl for canonical URL on account page --- routers/billing.py | 1 + templates/account.html | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/routers/billing.py b/routers/billing.py index 2f4db598c..2d17aa23e 100644 --- a/routers/billing.py +++ b/routers/billing.py @@ -121,6 +121,7 @@ def account(request: Request): "user_credits": request.user.balance, "subscription": get_user_subscription(request.user), "is_admin": is_admin, + "canonical_url": str(furl(settings.APP_BASE_URL) / "account" / "/"), } return templates.TemplateResponse("account.html", context) diff --git a/templates/account.html b/templates/account.html index 6a803490b..6c14bc501 100644 --- a/templates/account.html +++ b/templates/account.html @@ -3,7 +3,7 @@ {% block head %} - + {% endblock %} {% block content %} From 8ac0e71d6e9a360e71400d43a5b17e5ba49a8d48 Mon Sep 17 00:00:00 2001 From: Dev Aggarwal Date: Tue, 27 Feb 2024 22:04:08 +0530 Subject: [PATCH 23/23] avoid hardcoding account url --- routers/billing.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/routers/billing.py b/routers/billing.py index 2d17aa23e..bf660325b 100644 --- a/routers/billing.py +++ b/routers/billing.py @@ -121,7 +121,9 @@ def account(request: Request): "user_credits": request.user.balance, "subscription": get_user_subscription(request.user), "is_admin": is_admin, - "canonical_url": str(furl(settings.APP_BASE_URL) / "account" / "/"), + "canonical_url": str( + furl(settings.APP_BASE_URL) / router.url_path_for(account.__name__) + ), } return templates.TemplateResponse("account.html", context)