From ba3ec1300e81e64be7389d759b89284c66473158 Mon Sep 17 00:00:00 2001 From: aliabid94 Date: Wed, 21 Feb 2024 17:27:58 -0600 Subject: [PATCH 1/4] Tab select fix (#7470) * changes * add changeset * changes * changes * changes * Update js/tabs/shared/Tabs.svelte Co-authored-by: Aarni Koskela * changes * changes * changes --------- Co-authored-by: Ali Abid Co-authored-by: gradio-pr-bot Co-authored-by: Aarni Koskela --- .changeset/wicked-cars-sing.md | 6 ++++ demo/tabs/run.ipynb | 1 + demo/tabs/run.py | 46 +++++++++++++++++++++++++ js/app/test/tabs.spec.ts | 63 ++++++++++++++++++++++++++++++++++ js/tabs/shared/Tabs.svelte | 8 +++-- 5 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 .changeset/wicked-cars-sing.md create mode 100644 demo/tabs/run.ipynb create mode 100644 demo/tabs/run.py create mode 100644 js/app/test/tabs.spec.ts diff --git a/.changeset/wicked-cars-sing.md b/.changeset/wicked-cars-sing.md new file mode 100644 index 0000000000000..522fb527fb67b --- /dev/null +++ b/.changeset/wicked-cars-sing.md @@ -0,0 +1,6 @@ +--- +"@gradio/tabs": patch +"gradio": patch +--- + +fix:Tab select fix diff --git a/demo/tabs/run.ipynb b/demo/tabs/run.ipynb new file mode 100644 index 0000000000000..45de81987121a --- /dev/null +++ b/demo/tabs/run.ipynb @@ -0,0 +1 @@ +{"cells": [{"cell_type": "markdown", "id": "302934307671667531413257853548643485645", "metadata": {}, "source": ["# Gradio Demo: tabs"]}, {"cell_type": "code", "execution_count": null, "id": "272996653310673477252411125948039410165", "metadata": {}, "outputs": [], "source": ["!pip install -q gradio "]}, {"cell_type": "code", "execution_count": null, "id": "288918539441861185822528903084949547379", "metadata": {}, "outputs": [], "source": ["import gradio as gr\n", "\n", "with gr.Blocks() as demo:\n", " with gr.Tabs():\n", " with gr.Tab(\"Set 1\"):\n", " with gr.Tabs() as tabs_1:\n", " tabset_1 = []\n", " textset_1 = []\n", " for i in range(10):\n", " with gr.Tab(f\"Tab {i+1}\") as tab:\n", " gr.Markdown(f\"Text {i+1}!\")\n", " textbox = gr.Textbox(label=f\"Input {i+1}\")\n", " tabset_1.append(tab)\n", " textset_1.append(textbox)\n", " with gr.Tab(\"Set 2\"):\n", " tabset_2 = []\n", " textset_2 = []\n", " for i in range(10):\n", " with gr.Tab(f\"Tab {i+11}\") as tab:\n", " gr.Markdown(f\"Text {i+11}!\")\n", " textbox = gr.Textbox(label=f\"Input {i+11}\")\n", " tabset_2.append(tab)\n", " textset_2.append(textbox)\n", "\n", " for text1, text2 in zip(textset_1, textset_2):\n", " text1.submit(lambda x: x, text1, text2)\n", "\n", " selected = gr.Textbox(label=\"Selected Tab\")\n", " with gr.Row():\n", " hide_odd_btn = gr.Button(\"Hide Odd Tabs\")\n", " show_all_btn = gr.Button(\"Show All Tabs\")\n", " make_even_uninteractive_btn = gr.Button(\"Make Even Tabs Uninteractive\")\n", " make_all_interactive_btn = gr.Button(\"Make All Tabs Interactive\")\n", "\n", " hide_odd_btn.click(lambda: [gr.Tab(visible=i % 2 == 1) for i, _ in enumerate(tabset_1 + tabset_2)], outputs=(tabset_1 + tabset_2))\n", " show_all_btn.click(lambda: [gr.Tab(visible=True) for tab in tabset_1 + tabset_2], outputs=(tabset_1 + tabset_2))\n", " make_even_uninteractive_btn.click(lambda: [gr.Tab(interactive=i % 2 == 0) for i, _ in enumerate(tabset_1 + tabset_2)], outputs=(tabset_1 + tabset_2))\n", " make_all_interactive_btn.click(lambda: [gr.Tab(interactive=True) for tab in tabset_1 + tabset_2], outputs=(tabset_1 + tabset_2))\n", "\n", " def get_selected_index(evt: gr.SelectData):\n", " return evt.value\n", " gr.on([tab.select for tab in tabset_1 + tabset_2], get_selected_index, outputs=selected)\n", "\n", "\n", "if __name__ == \"__main__\":\n", " demo.launch()"]}], "metadata": {}, "nbformat": 4, "nbformat_minor": 5} \ No newline at end of file diff --git a/demo/tabs/run.py b/demo/tabs/run.py new file mode 100644 index 0000000000000..6696399d68278 --- /dev/null +++ b/demo/tabs/run.py @@ -0,0 +1,46 @@ +import gradio as gr + +with gr.Blocks() as demo: + with gr.Tabs(): + with gr.Tab("Set 1"): + with gr.Tabs() as tabs_1: + tabset_1 = [] + textset_1 = [] + for i in range(10): + with gr.Tab(f"Tab {i+1}") as tab: + gr.Markdown(f"Text {i+1}!") + textbox = gr.Textbox(label=f"Input {i+1}") + tabset_1.append(tab) + textset_1.append(textbox) + with gr.Tab("Set 2"): + tabset_2 = [] + textset_2 = [] + for i in range(10): + with gr.Tab(f"Tab {i+11}") as tab: + gr.Markdown(f"Text {i+11}!") + textbox = gr.Textbox(label=f"Input {i+11}") + tabset_2.append(tab) + textset_2.append(textbox) + + for text1, text2 in zip(textset_1, textset_2): + text1.submit(lambda x: x, text1, text2) + + selected = gr.Textbox(label="Selected Tab") + with gr.Row(): + hide_odd_btn = gr.Button("Hide Odd Tabs") + show_all_btn = gr.Button("Show All Tabs") + make_even_uninteractive_btn = gr.Button("Make Even Tabs Uninteractive") + make_all_interactive_btn = gr.Button("Make All Tabs Interactive") + + hide_odd_btn.click(lambda: [gr.Tab(visible=i % 2 == 1) for i, _ in enumerate(tabset_1 + tabset_2)], outputs=(tabset_1 + tabset_2)) + show_all_btn.click(lambda: [gr.Tab(visible=True) for tab in tabset_1 + tabset_2], outputs=(tabset_1 + tabset_2)) + make_even_uninteractive_btn.click(lambda: [gr.Tab(interactive=i % 2 == 0) for i, _ in enumerate(tabset_1 + tabset_2)], outputs=(tabset_1 + tabset_2)) + make_all_interactive_btn.click(lambda: [gr.Tab(interactive=True) for tab in tabset_1 + tabset_2], outputs=(tabset_1 + tabset_2)) + + def get_selected_index(evt: gr.SelectData): + return evt.value + gr.on([tab.select for tab in tabset_1 + tabset_2], get_selected_index, outputs=selected) + + +if __name__ == "__main__": + demo.launch() \ No newline at end of file diff --git a/js/app/test/tabs.spec.ts b/js/app/test/tabs.spec.ts new file mode 100644 index 0000000000000..2fd9d7c6756bf --- /dev/null +++ b/js/app/test/tabs.spec.ts @@ -0,0 +1,63 @@ +import { test, expect } from "@gradio/tootils"; + +test("clicking through tabs shows correct content", async ({ page }) => { + await page.getByRole("tab", { name: "Tab 2" }).click(); + await expect(page.getByText("Text 1!")).toBeHidden(); + await expect(page.getByText("Text 2!")).toBeVisible(); + + await page.getByRole("tab", { name: "Tab 4" }).click(); + await expect(page.getByText("Text 2!")).toBeHidden(); + await expect(page.getByText("Text 4!")).toBeVisible(); + + await page.getByRole("tab", { name: "Set 2" }).click(); + await page.getByRole("tab", { name: "Tab 12" }).click(); + await expect(page.getByText("Text 2!")).toBeHidden(); + await expect(page.getByText("Text 12!")).toBeVisible(); +}); + +test("correct selected tab shown", async ({ page }) => { + await page.getByRole("tab", { name: "Tab 2" }).click(); + await expect(page.getByLabel("Selected Tab")).toHaveValue("Tab 2"); + + await page.getByRole("tab", { name: "Tab 5" }).click(); + await expect(page.getByLabel("Selected Tab")).toHaveValue("Tab 5"); + + await page + .getByRole("button", { name: "Make Even Tabs Uninteractive" }) + .click(); + await page.waitForTimeout(1000); + + await expect(page.getByRole("tab", { name: "Tab 2" })).toBeDisabled(); + + await page.getByRole("button", { name: "Make All Tabs Interactive" }).click(); + await page.waitForTimeout(1000); + + await page.getByRole("tab", { name: "Tab 2" }).click(); + await expect(page.getByLabel("Selected Tab")).toHaveValue("Tab 2"); + + await page.getByRole("button", { name: "Hide Odd Tabs" }).click(); + await page.waitForTimeout(1000); + + await page.getByRole("tab", { name: "Tab 4" }).click(); + await page.getByRole("button", { name: "Show All Tabs" }).click(); + await page.waitForTimeout(1000); + + await page.getByRole("tab", { name: "Tab 9" }).click(); + await expect(page.getByLabel("Selected Tab")).toHaveValue("Tab 9"); +}); + +test("output from one tab to another works", async ({ page }) => { + await page.getByRole("tab", { name: "Tab 4" }).click(); + await page.getByLabel("Input 4").fill("hi"); + await page.getByLabel("Input 4").press("Enter"); + + await page.getByRole("tab", { name: "Set 2" }).click(); + await page.getByRole("tab", { name: "Tab 13" }).click(); + await expect(page.getByLabel("Input 13")).toHaveValue(""); + await expect(page.getByLabel("Input 13")).toBeVisible(); + await expect(page.getByLabel("Input 14")).toBeHidden(); + + await page.getByRole("tab", { name: "Tab 14" }).click(); + await expect(page.getByLabel("Input 14")).toBeVisible(); + await expect(page.getByLabel("Input 14")).toHaveValue("hi"); +}); diff --git a/js/tabs/shared/Tabs.svelte b/js/tabs/shared/Tabs.svelte index db73eb20eae4f..ad2cef3b782d5 100644 --- a/js/tabs/shared/Tabs.svelte +++ b/js/tabs/shared/Tabs.svelte @@ -31,11 +31,12 @@ setContext(TABS, { register_tab: (tab: Tab) => { + let index: number; let existingTab = tabs.find((t) => t.id === tab.id); if (existingTab) { // update existing tab with newer values - let i = tabs.findIndex((t) => t.id === tab.id); - tabs[i] = { ...tabs[i], ...tab }; + index = tabs.findIndex((t) => t.id === tab.id); + tabs[index] = { ...tabs[index], ...tab }; } else { tabs.push({ name: tab.name, @@ -44,6 +45,7 @@ visible: tab.visible, interactive: tab.interactive }); + index = tabs.length - 1; } selected_tab.update((current) => { if (current === false && tab.visible && tab.interactive) { @@ -54,7 +56,7 @@ return nextTab ? nextTab.id : current; }); tabs = tabs; - return tabs.length - 1; + return index; }, unregister_tab: (tab: Tab) => { const i = tabs.findIndex((t) => t.id === tab.id); From 16fbe9cd0cffa9f2a824a0165beb43446114eec7 Mon Sep 17 00:00:00 2001 From: aliabid94 Date: Wed, 21 Feb 2024 20:21:20 -0600 Subject: [PATCH 2/4] Prevent components from working with non-uploaded files (#7465) * changes * chanegs * add changeset * changes * changes * add changeset * changes * changes * changes --------- Co-authored-by: Ali Abid Co-authored-by: gradio-pr-bot --- .changeset/moody-impalas-rule.md | 5 ++++ gradio/blocks.py | 42 +++++++++++++++++++++++--------- gradio/processing_utils.py | 13 +++++++++- gradio/routes.py | 9 ++----- gradio/utils.py | 7 ++++++ 5 files changed, 56 insertions(+), 20 deletions(-) create mode 100644 .changeset/moody-impalas-rule.md diff --git a/.changeset/moody-impalas-rule.md b/.changeset/moody-impalas-rule.md new file mode 100644 index 0000000000000..5ec57a97ee41a --- /dev/null +++ b/.changeset/moody-impalas-rule.md @@ -0,0 +1,5 @@ +--- +"gradio": patch +--- + +feat:Prevent components from working with non-uploaded files diff --git a/gradio/blocks.py b/gradio/blocks.py index 616d6af389064..1206adff2aafa 100644 --- a/gradio/blocks.py +++ b/gradio/blocks.py @@ -9,7 +9,6 @@ import secrets import string import sys -import tempfile import threading import time import warnings @@ -70,6 +69,7 @@ get_cancel_function, get_continuous_fn, get_package_version, + get_upload_folder, ) try: @@ -119,12 +119,7 @@ def __init__( self._constructor_args: list[dict] self.state_session_capacity = 10000 self.temp_files: set[str] = set() - self.GRADIO_CACHE = str( - Path( - os.environ.get("GRADIO_TEMP_DIR") - or str(Path(tempfile.gettempdir()) / "gradio") - ).resolve() - ) + self.GRADIO_CACHE = get_upload_folder() if render: self.render() @@ -1110,6 +1105,7 @@ def __call__(self, *inputs, fn_index: int = 0, api_name: str | None = None): inputs=processed_inputs, request=None, state={}, + explicit_call=True, ) outputs = outputs["data"] @@ -1299,7 +1295,11 @@ def validate_inputs(self, fn_index: int, inputs: list[Any]): ) def preprocess_data( - self, fn_index: int, inputs: list[Any], state: SessionState | None + self, + fn_index: int, + inputs: list[Any], + state: SessionState | None, + explicit_call: bool = False, ): state = state or SessionState(self) block_fn = self.fns[fn_index] @@ -1326,7 +1326,10 @@ def preprocess_data( if input_id in state: block = state[input_id] inputs_cached = processing_utils.move_files_to_cache( - inputs[i], block, add_urls=True + inputs[i], + block, + add_urls=True, + check_in_upload_folder=not explicit_call, ) if getattr(block, "data_model", None) and inputs_cached is not None: if issubclass(block.data_model, GradioModel): # type: ignore @@ -1522,8 +1525,14 @@ def handle_streaming_diffs( return data - def run_fn_batch(self, fn, batch, fn_index, state): - return [fn(fn_index, list(i), state) for i in zip(*batch)] + def run_fn_batch(self, fn, batch, fn_index, state, explicit_call=None): + output = [] + for i in zip(*batch): + args = [fn_index, list(i), state] + if explicit_call is not None: + args.append(explicit_call) + output.append(fn(*args)) + return output async def process_api( self, @@ -1536,6 +1545,7 @@ async def process_api( event_id: str | None = None, event_data: EventData | None = None, in_event_listener: bool = True, + explicit_call: bool = False, ) -> dict[str, Any]: """ Processes API calls from the frontend. First preprocesses the data, @@ -1548,6 +1558,8 @@ async def process_api( iterators: the in-progress iterators for each generator function (key is function index) event_id: id of event that triggered this API call event_data: data associated with the event trigger itself + in_event_listener: whether this API call is being made in response to an event listener + explicit_call: whether this call is being made directly by calling the Blocks function, instead of through an event listener or API route Returns: None """ block_fn = self.fns[fn_index] @@ -1575,6 +1587,7 @@ async def process_api( inputs, fn_index, state, + explicit_call, limiter=self.limiter, ) result = await self.call_function( @@ -1603,7 +1616,12 @@ async def process_api( inputs = [] else: inputs = await anyio.to_thread.run_sync( - self.preprocess_data, fn_index, inputs, state, limiter=self.limiter + self.preprocess_data, + fn_index, + inputs, + state, + explicit_call, + limiter=self.limiter, ) was_generating = old_iterator is not None result = await self.call_function( diff --git a/gradio/processing_utils.py b/gradio/processing_utils.py index 5f82f25217426..52e9e59f2fffb 100644 --- a/gradio/processing_utils.py +++ b/gradio/processing_utils.py @@ -20,7 +20,7 @@ from gradio import wasm_utils from gradio.data_classes import FileData, GradioModel, GradioRootModel -from gradio.utils import abspath +from gradio.utils import abspath, get_upload_folder, is_in_or_equal with warnings.catch_warnings(): warnings.simplefilter("ignore") # Ignore pydub warning if ffmpeg is not installed @@ -241,6 +241,7 @@ def move_files_to_cache( block: Component, postprocess: bool = False, add_urls=False, + check_in_upload_folder=False, ) -> dict: """Move any files in `data` to cache and (optionally), adds URL prefixes (/file=...) needed to access the cached file. Also handles the case where the file is on an external Gradio app (/proxy=...). @@ -252,6 +253,8 @@ def move_files_to_cache( block: The component whose data is being processed postprocess: Whether its running from postprocessing root_url: The root URL of the local server, if applicable + add_urls: Whether to add URLs to the payload + check_in_upload_folder: If True, instead of moving the file to cache, checks if the file is in already in cache (exception if not). """ def _move_to_cache(d: dict): @@ -264,6 +267,14 @@ def _move_to_cache(d: dict): payload.path = payload.url elif not block.proxy_url: # If the file is on a remote server, do not move it to cache. + if check_in_upload_folder and not client_utils.is_http_url_like( + payload.path + ): + path = os.path.abspath(payload.path) + if not is_in_or_equal(path, get_upload_folder()): + raise ValueError( + f"File {path} is not in the upload folder and cannot be accessed." + ) temp_file_path = block.move_resource_to_block_cache(payload.path) if temp_file_path is None: raise ValueError("Did not determine a file path for the resource.") diff --git a/gradio/routes.py b/gradio/routes.py index 8766360578009..29386e59b95a4 100644 --- a/gradio/routes.py +++ b/gradio/routes.py @@ -17,7 +17,6 @@ import os import posixpath import secrets -import tempfile import threading import time import traceback @@ -67,9 +66,7 @@ move_uploaded_files_to_cache, ) from gradio.state_holder import StateHolder -from gradio.utils import ( - get_package_version, -) +from gradio.utils import get_package_version, get_upload_folder if TYPE_CHECKING: from gradio.blocks import Block @@ -136,9 +133,7 @@ def __init__(self, **kwargs): self.cookie_id = secrets.token_urlsafe(32) self.queue_token = secrets.token_urlsafe(32) self.startup_events_triggered = False - self.uploaded_file_dir = os.environ.get("GRADIO_TEMP_DIR") or str( - (Path(tempfile.gettempdir()) / "gradio").resolve() - ) + self.uploaded_file_dir = get_upload_folder() self.change_event: None | threading.Event = None self._asyncio_tasks: list[asyncio.Task] = [] # Allow user to manually set `docs_url` and `redoc_url` diff --git a/gradio/utils.py b/gradio/utils.py index b3f366b70ae42..78a1819f6dfe8 100644 --- a/gradio/utils.py +++ b/gradio/utils.py @@ -13,6 +13,7 @@ import os import pkgutil import re +import tempfile import threading import time import traceback @@ -1082,3 +1083,9 @@ def compare_objects(obj1, obj2, path=None): return edits return compare_objects(old, new) + + +def get_upload_folder() -> str: + return os.environ.get("GRADIO_TEMP_DIR") or str( + (Path(tempfile.gettempdir()) / "gradio").resolve() + ) From 33f68cb6c22897f7996b6c84b0e528c47fae00b5 Mon Sep 17 00:00:00 2001 From: "Yuichiro Tachibana (Tsuchiya)" Date: Thu, 22 Feb 2024 22:28:56 +0900 Subject: [PATCH 3/4] Fix Canvas3D/Canvas3DGS async imports (#7511) * Fix Model3D and Model3DUpload to handle async-imports and access the child component's exported method * Refactoring: reorder the lines to put the related snippets together * add changeset * Refactoring * Disable the undo buttons for Canvas3DGS * add changeset * Refactoring to use optional chaining --------- Co-authored-by: gradio-pr-bot --- .changeset/yummy-states-wink.md | 6 +++ js/model3D/shared/Model3D.svelte | 55 ++++++++++++++------------ js/model3D/shared/Model3DUpload.svelte | 54 +++++++++++++------------ 3 files changed, 64 insertions(+), 51 deletions(-) create mode 100644 .changeset/yummy-states-wink.md diff --git a/.changeset/yummy-states-wink.md b/.changeset/yummy-states-wink.md new file mode 100644 index 0000000000000..0cf6482dd5240 --- /dev/null +++ b/.changeset/yummy-states-wink.md @@ -0,0 +1,6 @@ +--- +"@gradio/model3d": patch +"gradio": patch +--- + +fix:Fix Canvas3D/Canvas3DGS async imports diff --git a/js/model3D/shared/Model3D.svelte b/js/model3D/shared/Model3D.svelte index 5fdacd66fad02..492e2bafdb498 100644 --- a/js/model3D/shared/Model3D.svelte +++ b/js/model3D/shared/Model3D.svelte @@ -4,6 +4,8 @@ import { File, Download, Undo } from "@gradio/icons"; import type { I18nFormatter } from "@gradio/utils"; import { dequal } from "dequal"; + import type Canvas3DGS from "./Canvas3DGS.svelte"; + import type Canvas3D from "./Canvas3D.svelte"; export let value: FileData | null; export let clear_color: [number, number, number, number] = [0, 0, 0, 0]; @@ -21,23 +23,33 @@ let current_settings = { camera_position, zoom_speed, pan_speed }; - let canvas3dgs: any; - let canvas3d: any; let use_3dgs = false; - let resolved_url: string | undefined; - - async function loadCanvas3D(): Promise { + let Canvas3DGSComponent: typeof Canvas3DGS; + let Canvas3DComponent: typeof Canvas3D; + async function loadCanvas3D(): Promise { const module = await import("./Canvas3D.svelte"); return module.default; } - - async function loadCanvas3DGS(): Promise { + async function loadCanvas3DGS(): Promise { const module = await import("./Canvas3DGS.svelte"); return module.default; } + $: if (value) { + use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply"); + if (use_3dgs) { + loadCanvas3DGS().then((component) => { + Canvas3DGSComponent = component; + }); + } else { + loadCanvas3D().then((component) => { + Canvas3DComponent = component; + }); + } + } + let canvas3d: Canvas3D | undefined; function handle_undo(): void { - canvas3d.reset_camera_position(camera_position, zoom_speed, pan_speed); + canvas3d?.reset_camera_position(camera_position, zoom_speed, pan_speed); } $: { @@ -46,25 +58,12 @@ current_settings.zoom_speed !== zoom_speed || current_settings.pan_speed !== pan_speed ) { - canvas3d.reset_camera_position(camera_position, zoom_speed, pan_speed); + canvas3d?.reset_camera_position(camera_position, zoom_speed, pan_speed); current_settings = { camera_position, zoom_speed, pan_speed }; } } - $: { - if (value) { - use_3dgs = value?.path.endsWith(".splat") || value?.path.endsWith(".ply"); - if (use_3dgs) { - loadCanvas3DGS().then((module) => { - canvas3dgs = module; - }); - } else { - loadCanvas3D().then((module) => { - canvas3d = module; - }); - } - } - } + let resolved_url: string | undefined;
- handle_undo()} /> + {#if !use_3dgs} + + handle_undo()} /> + {/if} {:else} { + let Canvas3DGSComponent: typeof Canvas3DGS; + let Canvas3DComponent: typeof Canvas3D; + async function loadCanvas3D(): Promise { const module = await import("./Canvas3D.svelte"); return module.default; } - - async function loadCanvas3DGS(): Promise { + async function loadCanvas3DGS(): Promise { const module = await import("./Canvas3DGS.svelte"); return module.default; } + $: if (value) { + use_3dgs = value.path.endsWith(".splat") || value.path.endsWith(".ply"); + if (use_3dgs) { + loadCanvas3DGS().then((component) => { + Canvas3DGSComponent = component; + }); + } else { + loadCanvas3D().then((component) => { + Canvas3DComponent = component; + }); + } + } + let canvas3d: Canvas3D | undefined; async function handle_undo(): Promise { - canvas3d.reset_camera_position(camera_position, zoom_speed, pan_speed); + canvas3d?.reset_camera_position(camera_position, zoom_speed, pan_speed); } const dispatch = createEventDispatcher<{ @@ -66,21 +79,6 @@ let dragging = false; $: dispatch("drag", dragging); - - $: { - if (value) { - use_3dgs = value?.path.endsWith(".splat") || value?.path.endsWith(".ply"); - if (use_3dgs) { - loadCanvas3DGS().then((module) => { - canvas3dgs = module; - }); - } else { - loadCanvas3D().then((module) => { - canvas3d = module; - }); - } - } - } @@ -97,7 +95,7 @@ {:else}
{#if use_3dgs} - + {:else} Date: Fri, 23 Feb 2024 00:08:29 +0800 Subject: [PATCH 4/4] Streaming example for the updated OpenAI API (#7508) * Changes for updated OpenAI api * Update guides/04_chatbots/01_creating-a-chatbot-fast.md --------- Co-authored-by: Abubakar Abid --- .../04_chatbots/01_creating-a-chatbot-fast.md | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/guides/04_chatbots/01_creating-a-chatbot-fast.md b/guides/04_chatbots/01_creating-a-chatbot-fast.md index 5dd0d15b60f45..b636e52f2f401 100644 --- a/guides/04_chatbots/01_creating-a-chatbot-fast.md +++ b/guides/04_chatbots/01_creating-a-chatbot-fast.md @@ -198,10 +198,11 @@ gr.ChatInterface(predict).launch() Of course, we could also use the `openai` library directy. Here a similar example, but this time with streaming results as well: ```python -import openai +from openai import OpenAI import gradio as gr -openai.api_key = "sk-..." # Replace with your key +api_key = "sk-..." # Replace with your key +client = OpenAI(api_key=api_key) def predict(message, history): history_openai_format = [] @@ -209,19 +210,17 @@ def predict(message, history): history_openai_format.append({"role": "user", "content": human }) history_openai_format.append({"role": "assistant", "content":assistant}) history_openai_format.append({"role": "user", "content": message}) - - response = openai.ChatCompletion.create( - model='gpt-3.5-turbo', - messages= history_openai_format, - temperature=1.0, - stream=True - ) + + response = client.chat.completions.create(model='gpt-3.5-turbo', + messages= history_openai_format, + temperature=1.0, + stream=True) partial_message = "" for chunk in response: - if len(chunk['choices'][0]['delta']) != 0: - partial_message = partial_message + chunk['choices'][0]['delta']['content'] - yield partial_message + if chunk.choices[0].delta.content is not None: + partial_message = partial_message + chunk.choices[0].delta.content + yield partial_message gr.ChatInterface(predict).launch() ```