Spaces:
Paused
Paused
import { | |
type ApiData, | |
type BlobRef, | |
type Config, | |
type EndpointInfo, | |
type JsApiData, | |
type DataType, | |
Command, | |
type Dependency, | |
type ComponentMeta | |
} from "../types"; | |
import { FileData } from "../upload"; | |
const is_node = | |
typeof process !== "undefined" && process.versions && process.versions.node; | |
export function update_object( | |
object: { [x: string]: any }, | |
newValue: any, | |
stack: (string | number)[] | |
): void { | |
while (stack.length > 1) { | |
const key = stack.shift(); | |
if (typeof key === "string" || typeof key === "number") { | |
object = object[key]; | |
} else { | |
throw new Error("Invalid key type"); | |
} | |
} | |
const key = stack.shift(); | |
if (typeof key === "string" || typeof key === "number") { | |
object[key] = newValue; | |
} else { | |
throw new Error("Invalid key type"); | |
} | |
} | |
export async function walk_and_store_blobs( | |
data: DataType, | |
type: string | undefined = undefined, | |
path: string[] = [], | |
root = false, | |
endpoint_info: EndpointInfo<ApiData | JsApiData> | undefined = undefined | |
): Promise<BlobRef[]> { | |
if (Array.isArray(data)) { | |
let blob_refs: BlobRef[] = []; | |
await Promise.all( | |
data.map(async (_, index) => { | |
let new_path = path.slice(); | |
new_path.push(String(index)); | |
const array_refs = await walk_and_store_blobs( | |
data[index], | |
root | |
? endpoint_info?.parameters[index]?.component || undefined | |
: type, | |
new_path, | |
false, | |
endpoint_info | |
); | |
blob_refs = blob_refs.concat(array_refs); | |
}) | |
); | |
return blob_refs; | |
} else if ( | |
(globalThis.Buffer && data instanceof globalThis.Buffer) || | |
data instanceof Blob | |
) { | |
return [ | |
{ | |
path: path, | |
blob: new Blob([data]), | |
type | |
} | |
]; | |
} else if (typeof data === "object" && data !== null) { | |
let blob_refs: BlobRef[] = []; | |
for (const key of Object.keys(data) as (keyof typeof data)[]) { | |
const new_path = [...path, key]; | |
const value = data[key]; | |
blob_refs = blob_refs.concat( | |
await walk_and_store_blobs( | |
value, | |
undefined, | |
new_path, | |
false, | |
endpoint_info | |
) | |
); | |
} | |
return blob_refs; | |
} | |
return []; | |
} | |
export function skip_queue(id: number, config: Config): boolean { | |
let fn_queue = config?.dependencies?.find((dep) => dep.id == id)?.queue; | |
if (fn_queue != null) { | |
return !fn_queue; | |
} | |
return !config.enable_queue; | |
} | |
// todo: add jsdoc for this function | |
export function post_message<Res = any>( | |
message: any, | |
origin: string | |
): Promise<Res> { | |
return new Promise((res, _rej) => { | |
const channel = new MessageChannel(); | |
channel.port1.onmessage = (({ data }) => { | |
channel.port1.close(); | |
res(data); | |
}) as (ev: MessageEvent<Res>) => void; | |
window.parent.postMessage(message, origin, [channel.port2]); | |
}); | |
} | |
export function handle_file( | |
file_or_url: File | string | Blob | Buffer | |
): FileData | Blob | Command { | |
if (typeof file_or_url === "string") { | |
if ( | |
file_or_url.startsWith("http://") || | |
file_or_url.startsWith("https://") | |
) { | |
return { | |
path: file_or_url, | |
url: file_or_url, | |
orig_name: file_or_url.split("/").pop() ?? "unknown", | |
meta: { _type: "gradio.FileData" } | |
}; | |
} | |
if (is_node) { | |
// Handle local file paths | |
return new Command("upload_file", { | |
path: file_or_url, | |
name: file_or_url, | |
orig_path: file_or_url | |
}); | |
} | |
} else if (typeof File !== "undefined" && file_or_url instanceof File) { | |
return { | |
path: file_or_url instanceof File ? file_or_url.name : "blob", | |
orig_name: file_or_url instanceof File ? file_or_url.name : "unknown", | |
// @ts-ignore | |
blob: file_or_url instanceof File ? file_or_url : new Blob([file_or_url]), | |
size: | |
file_or_url instanceof Blob | |
? file_or_url.size | |
: Buffer.byteLength(file_or_url as Buffer), | |
mime_type: | |
file_or_url instanceof File | |
? file_or_url.type | |
: "application/octet-stream", // Default MIME type for buffers | |
meta: { _type: "gradio.FileData" } | |
}; | |
} else if (file_or_url instanceof Buffer) { | |
return new Blob([file_or_url]); | |
} else if (file_or_url instanceof Blob) { | |
return file_or_url; | |
} | |
throw new Error( | |
"Invalid input: must be a URL, File, Blob, or Buffer object." | |
); | |
} | |
/** | |
* Handles the payload by filtering out state inputs and returning an array of resolved payload values. | |
* We send null values for state inputs to the server, but we don't want to include them in the resolved payload. | |
* | |
* @param resolved_payload - The resolved payload values received from the client or the server | |
* @param dependency - The dependency object. | |
* @param components - The array of component metadata. | |
* @param with_null_state - Optional. Specifies whether to include null values for state inputs. Default is false. | |
* @returns An array of resolved payload values, filtered based on the dependency and component metadata. | |
*/ | |
export function handle_payload( | |
resolved_payload: unknown[], | |
dependency: Dependency, | |
components: ComponentMeta[], | |
type: "input" | "output", | |
with_null_state = false | |
): unknown[] { | |
if (type === "input" && !with_null_state) { | |
throw new Error("Invalid code path. Cannot skip state inputs for input."); | |
} | |
// data comes from the server with null state values so we skip | |
if (type === "output" && with_null_state) { | |
return resolved_payload; | |
} | |
let updated_payload: unknown[] = []; | |
let payload_index = 0; | |
for (let i = 0; i < dependency.inputs.length; i++) { | |
const input_id = dependency.inputs[i]; | |
const component = components.find((c) => c.id === input_id); | |
if (component?.type === "state") { | |
// input + with_null_state needs us to fill state with null values | |
if (with_null_state) { | |
if (resolved_payload.length === dependency.inputs.length) { | |
const value = resolved_payload[payload_index]; | |
updated_payload.push(value); | |
payload_index++; | |
} else { | |
updated_payload.push(null); | |
} | |
} else { | |
// this is output & !with_null_state, we skip state inputs | |
// the server payload always comes with null state values so we move along the payload index | |
payload_index++; | |
continue; | |
} | |
// input & !with_null_state isn't a case we care about, server needs null | |
continue; | |
} else { | |
const value = resolved_payload[payload_index]; | |
updated_payload.push(value); | |
payload_index++; | |
} | |
} | |
return updated_payload; | |
} | |