mirror of
https://github.com/gradio-app/gradio.git
synced 2024-11-21 01:01:05 +08:00
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:
parent
012da05287
commit
936c7137a9
7
.changeset/witty-pugs-beg.md
Normal file
7
.changeset/witty-pugs-beg.md
Normal 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
|
@ -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
3
globals.d.ts
vendored
@ -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;
|
||||
}
|
||||
|
@ -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()
|
||||
)
|
||||
|
@ -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]
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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}")
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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 /> <br />Note: making a
|
||||
{#if current_language == "bash"}<br /> <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
|
||||
|
@ -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}, {auth: ["{username}", **password**]}{/if});
|
||||
const result = await client.predict({#if named}<span class="api-name"
|
||||
>"/{dependency.api_name}"</span
|
||||
>{:else}{dependency_index}{/if}, { <!--
|
||||
|
@ -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,12 +153,12 @@ client.<span class="highlight"
|
||||
<pre>import { Client } from "@gradio/client";
|
||||
|
||||
const app = await Client.connect(<span class="token string">"{short_root}"</span
|
||||
>);
|
||||
>{#if username !== null}, {auth: ["{username}", **password**]}{/if});
|
||||
{#each js_zipped as { call, api_name }}<!--
|
||||
-->
|
||||
await client.predict(<span class="api-name">
|
||||
"/{api_name}"</span
|
||||
>{#if call},{/if}{call});
|
||||
>{#if call}, {/if}{call});
|
||||
{/each}</pre>
|
||||
</div>
|
||||
</code>
|
||||
|
Loading…
Reference in New Issue
Block a user