Documents auth in the guides, in the view API page, and also types the Blocks.config object (#8720)

* auth docs

* changes

* add changeset

* add changeset

* add changeset

* type

* changes

* snippets

* import

* add changeset

* changes

* fix typing

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
This commit is contained in:
Abubakar Abid 2024-07-08 18:58:07 -07:00 committed by GitHub
parent 012da05287
commit 936c7137a9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 127 additions and 24 deletions

View File

@ -0,0 +1,7 @@
---
"@gradio/app": patch
"@gradio/client": patch
"gradio": patch
---
fix:Documents auth in the guides, in the view API page, and also types the Blocks.config object

View File

@ -153,7 +153,7 @@ export type SpaceStatusCallback = (a: SpaceStatus) => void;
// Configuration and Response Types
// --------------------------------
export interface Config {
auth_required: boolean;
auth_required?: true;
analytics_enabled: boolean;
connect_heartbeat: boolean;
auth_message: string;
@ -181,6 +181,7 @@ export interface Config {
protocol: "sse_v3" | "sse_v2.1" | "sse_v2" | "sse_v1" | "sse" | "ws";
max_file_size?: number;
theme_hash?: number;
username: string | null;
}
// todo: DRY up types

3
globals.d.ts vendored
View File

@ -16,7 +16,7 @@ declare global {
}
export interface Config {
auth_required: boolean;
auth_required?: true;
auth_message: string;
components: any[];
css: string | null;
@ -41,4 +41,5 @@ export interface Config {
is_space: boolean;
protocol: "ws" | "sse" | "sse_v1" | "sse_v2" | "sse_v2.1" | "sse_v3";
theme_hash?: number;
username: string | null;
}

View File

@ -47,7 +47,7 @@ from gradio.context import (
get_render_context,
set_render_context,
)
from gradio.data_classes import FileData, GradioModel, GradioRootModel
from gradio.data_classes import BlocksConfigDict, FileData, GradioModel, GradioRootModel
from gradio.events import (
EventData,
EventListener,
@ -1024,7 +1024,7 @@ class Blocks(BlockContext, BlocksEvents, metaclass=BlocksMeta):
self.predict = None
self.input_components = None
self.output_components = None
self.__name__ = None
self.__name__ = None # type: ignore
self.api_mode = None
self.progress_tracking = None
@ -1082,7 +1082,7 @@ class Blocks(BlockContext, BlocksEvents, metaclass=BlocksMeta):
@classmethod
def from_config(
cls,
config: dict,
config: BlocksConfigDict,
fns: list[Callable],
proxy_url: str,
) -> Blocks:
@ -1978,8 +1978,8 @@ Received outputs:
def get_config(self):
return {"type": "column"}
def get_config_file(self):
config = {
def get_config_file(self) -> BlocksConfigDict:
config: BlocksConfigDict = {
"version": routes.VERSION,
"mode": self.mode,
"app_id": self.app_id,
@ -2015,7 +2015,7 @@ Received outputs:
"fill_height": self.fill_height,
"theme_hash": self.theme_hash,
}
config.update(self.default_config.get_config())
config.update(self.default_config.get_config()) # type: ignore
config["connect_heartbeat"] = utils.connect_heartbeat(
config, self.blocks.values()
)

View File

@ -8,10 +8,11 @@ import secrets
import shutil
from abc import ABC, abstractmethod
from enum import Enum, auto
from typing import TYPE_CHECKING, Any, List, Optional, Tuple, TypedDict, Union
from typing import TYPE_CHECKING, Any, List, Literal, Optional, Tuple, TypedDict, Union
from fastapi import Request
from gradio_client.utils import traverse
from typing_extensions import NotRequired
from . import wasm_utils
@ -288,3 +289,45 @@ class _StaticFiles:
@classmethod
def clear(cls):
cls.all_paths = []
class BodyCSS(TypedDict):
body_background_fill: str
body_text_color: str
body_background_fill_dark: str
body_text_color_dark: str
class Layout(TypedDict):
id: int
children: list[int | Layout]
class BlocksConfigDict(TypedDict):
version: str
mode: str
app_id: int
dev_mode: bool
analytics_enabled: bool
components: list[dict[str, Any]]
css: str | None
connect_heartbeat: bool
js: str | None
head: str | None
title: str
space_id: str | None
enable_queue: bool
show_error: bool
show_api: bool
is_colab: bool
max_file_size: int | None
stylesheets: list[str]
theme: str | None
protocol: Literal["ws", "sse", "sse_v1", "sse_v2", "sse_v2.1", "sse_v3"]
body_css: BodyCSS
fill_height: bool
theme_hash: str
layout: NotRequired[Layout]
dependencies: NotRequired[list[dict[str, Any]]]
root: NotRequired[str | None]
username: NotRequired[str | None]

View File

@ -464,7 +464,7 @@ def from_spaces_blocks(space: str, hf_token: str | None) -> Blocks:
predict_fns.append(endpoint.make_end_to_end_fn(helper))
else:
predict_fns.append(None)
return gradio.Blocks.from_config(client.config, predict_fns, client.src)
return gradio.Blocks.from_config(client.config, predict_fns, client.src) # type: ignore
def from_spaces_interface(

View File

@ -41,7 +41,7 @@ from starlette.responses import PlainTextResponse, Response
from starlette.types import ASGIApp, Message, Receive, Scope, Send
from gradio import processing_utils, utils
from gradio.data_classes import PredictBody
from gradio.data_classes import BlocksConfigDict, PredictBody
from gradio.exceptions import Error
from gradio.helpers import EventData
from gradio.state_holder import SessionState
@ -640,7 +640,7 @@ def move_uploaded_files_to_cache(files: list[str], destinations: list[str]) -> N
shutil.move(file, dest)
def update_root_in_config(config: dict, root: str) -> dict:
def update_root_in_config(config: BlocksConfigDict, root: str) -> BlocksConfigDict:
"""
Updates the root "key" in the config dictionary to the new root url. If the
root url has changed, all of the urls in the config that correspond to component
@ -649,7 +649,7 @@ def update_root_in_config(config: dict, root: str) -> dict:
previous_root = config.get("root")
if previous_root is None or previous_root != root:
config["root"] = root
config = processing_utils.add_root_url(config, root, previous_root)
config = processing_utils.add_root_url(config, root, previous_root) # type: ignore
return config

View File

@ -384,6 +384,7 @@ class App(FastAPI):
if (app.auth is None and app.auth_dependency is None) or user is not None:
config = blocks.config
config = route_utils.update_root_in_config(config, root)
config["username"] = user
elif app.auth_dependency:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated"
@ -440,6 +441,7 @@ class App(FastAPI):
request=request, route_path="/config", root_path=app.root_path
)
config = route_utils.update_root_in_config(config, root)
config["username"] = get_current_user(request)
return ORJSONResponse(content=config)
@app.get("/static/{path:path}")

View File

@ -52,7 +52,7 @@ from typing_extensions import ParamSpec
import gradio
from gradio.context import get_blocks_context
from gradio.data_classes import FileData
from gradio.data_classes import BlocksConfigDict, FileData
from gradio.exceptions import Error
from gradio.strings import en
@ -1363,11 +1363,21 @@ def _parse_file_size(size: str | int | None) -> int | None:
return multiple * size_int
def connect_heartbeat(config: dict[str, Any], blocks) -> bool:
def connect_heartbeat(config: BlocksConfigDict, blocks) -> bool:
"""
Determines whether a heartbeat is required for a given config.
"""
from gradio.components import State
any_state = any(isinstance(block, State) for block in blocks)
any_unload = False
if "dependencies" not in config:
raise ValueError(
"Dependencies not found in config. Cannot determine whether"
"heartbeat is required."
)
for dep in config["dependencies"]:
for target in dep["targets"]:
if isinstance(target, (list, tuple)) and len(target) == 2:

View File

@ -88,6 +88,20 @@ from gradio_client import Client
client = Client("https://bec81a83-5b5c-471e.gradio.live")
```
## Connecting to a Gradio app with auth
If the Gradio application you are connecting to [requires a username and password](/guides/sharing-your-app#authentication), then provide them as a tuple to the `auth` argument of the `Client` class:
```python
from gradio_client import Client
Client(
space_name,
auth=[username, password]
)
```
## Inspecting the API endpoints
Once you have connected to a Gradio app, you can view the APIs that are available to you by calling the `Client.view_api()` method. For the Whisper Space, we see the following:

View File

@ -111,6 +111,20 @@ import { Client } from "@gradio/client";
const app = Client.connect("https://bec81a83-5b5c-471e.gradio.live");
```
## Connecting to a Gradio app with auth
If the Gradio application you are connecting to [requires a username and password](/guides/sharing-your-app#authentication), then provide them as a tuple to the `auth` argument of the `Client` class:
```js
import { Client } from "@gradio/client";
Client.connect(
space_name,
{ auth: [username, password] }
)
```
## Inspecting the API endpoints
Once you have connected to a Gradio app, you can view the APIs that are available to you by calling the `Client`'s `view_api` method.

View File

@ -44,6 +44,7 @@
export let js: string | null;
export let fill_height = false;
export let ready: boolean;
export let username: string | null;
const {
layout: _layout,
@ -678,6 +679,7 @@
{app}
{space_id}
{api_calls}
{username}
/>
</div>
</div>

View File

@ -7,7 +7,7 @@
declare let BUILD_MODE: string;
interface Config {
auth_required: boolean | undefined;
auth_required?: true;
auth_message: string;
components: ComponentMeta[];
css: string | null;
@ -30,6 +30,7 @@
app_id?: string;
fill_height?: boolean;
theme_hash?: number;
username: string | null;
}
let id = -1;

View File

@ -23,6 +23,8 @@
export let app: Awaited<ReturnType<typeof Client.connect>>;
export let space_id: string | null;
export let root_node: ComponentMeta;
export let username: string | null;
const js_docs =
"https://www.gradio.app/guides/getting-started-with-the-js-client";
const py_docs =
@ -142,6 +144,7 @@
{dependencies}
{root}
short_root={space_id || root}
{username}
/>
<p>
Note: Some API calls only affect the UI, so when using the
@ -195,7 +198,7 @@
<p class="self-baseline">API Recorder</p>
</Button>
to automatically generate your API requests.
{#if current_language == "bash"}<br />&nbsp;<br />Note: making a
{#if current_language == "bash"}<br />&nbsp;<br />Making a
prediction and getting a result requires
<strong>2 requests</strong>: a
<code>POST</code>
@ -204,7 +207,10 @@
<code>GET</code> request to fetch the results. In these snippets,
we've used <code>awk</code> and <code>read</code> to parse the
results, combining these two requests into one command for ease of
use. See <a href={bash_docs} target="_blank">curl docs</a>.
use. {#if username !== null}
Note: connecting to an authenticated app requires an additional
request.{/if} See
<a href={bash_docs} target="_blank">curl docs</a>.
{/if}
<!-- <span
@ -228,6 +234,7 @@
{current_language}
{root}
{space_id}
{username}
/>
<ParametersSnippet

View File

@ -20,7 +20,7 @@
export let space_id: string | null;
export let endpoint_parameters: any;
export let named: boolean;
export let username: string | null;
export let current_language: "python" | "javascript" | "bash";
let python_code: HTMLElement;
@ -54,7 +54,7 @@
class="highlight">import</span
> Client{#if has_file_path}, handle_file{/if}
client = Client(<span class="token string">"{space_id || root}"</span>)
client = Client(<span class="token string">"{space_id || root}"</span>{#if username !== null}, auth=("{username}", **password**){/if})
result = client.<span class="highlight">predict</span
>(<!--
-->{#each endpoint_parameters as { python_type, example_input, parameter_name, parameter_has_default, parameter_default }, i}<!--
@ -93,7 +93,7 @@ const example{component} = await response_{i}.blob();
-->
const client = await Client.connect(<span class="token string"
>"{space_id || root}"</span
>);
>{#if username !== null}, &lbrace;auth: ["{username}", **password**]&rbrace;{/if});
const result = await client.predict({#if named}<span class="api-name"
>"/{dependency.api_name}"</span
>{:else}{dependency_index}{/if}, &lbrace; <!--

View File

@ -9,6 +9,7 @@
export let short_root: string;
export let root: string;
export let current_language: "python" | "javascript" | "bash";
export let username: string | null;
let python_code: HTMLElement;
let python_code_text: string;
@ -133,7 +134,7 @@
class="highlight">import</span
> Client, file
client = Client(<span class="token string">"{short_root}"</span>)
client = Client(<span class="token string">"{short_root}"</span>{#if username !== null}, auth=("{username}", **password**){/if})
{#each py_zipped as { call, api_name }}<!--
-->
client.<span class="highlight"
@ -152,7 +153,7 @@ client.<span class="highlight"
<pre>import &lbrace; Client &rbrace; from "@gradio/client";
const app = await Client.connect(<span class="token string">"{short_root}"</span
>);
>{#if username !== null}, &lbrace;auth: ["{username}", **password**]&rbrace;{/if});
{#each js_zipped as { call, api_name }}<!--
-->
await client.predict(<span class="api-name">