nsarrazin HF Staff commited on
Commit
ba9c926
·
unverified ·
1 Parent(s): dd4a8d9

feat(tools): specify either space namespace or huggingface.co URL (#1435)

Browse files
src/lib/server/tools/index.ts CHANGED
@@ -70,7 +70,14 @@ const toolInputSchema = toolInputBaseSchema.and(
70
  export const editableToolSchema = z
71
  .object({
72
  name: z.string().min(1).max(40),
73
- baseUrl: z.string().min(1).max(100),
 
 
 
 
 
 
 
74
  endpoint: z.string().min(1).max(100),
75
  inputs: z.array(toolInputSchema),
76
  outputComponent: z.string().min(1).max(100),
@@ -85,7 +92,6 @@ export const editableToolSchema = z
85
  outputComponentIdx: parseInt(tool.outputComponent.split(";")[0]),
86
  outputComponent: ToolOutputComponents.parse(tool.outputComponent.split(";")[1]),
87
  }));
88
-
89
  export const configTools = z
90
  .array(
91
  z
 
70
  export const editableToolSchema = z
71
  .object({
72
  name: z.string().min(1).max(40),
73
+ // only allow huggingface spaces either through namespace or direct URLs
74
+ baseUrl: z.union([
75
+ z.string().regex(/^[a-zA-Z0-9-]+\/[a-zA-Z0-9-]+$/),
76
+ z
77
+ .string()
78
+ .regex(/^https:\/\/huggingface\.co\/spaces\/[a-zA-Z0-9-]+\/[a-zA-Z0-9-]+$/)
79
+ .transform((url) => url.split("/").slice(-2).join("/")),
80
+ ]),
81
  endpoint: z.string().min(1).max(100),
82
  inputs: z.array(toolInputSchema),
83
  outputComponent: z.string().min(1).max(100),
 
92
  outputComponentIdx: parseInt(tool.outputComponent.split(";")[0]),
93
  outputComponent: ToolOutputComponents.parse(tool.outputComponent.split(";")[1]),
94
  }));
 
95
  export const configTools = z
96
  .array(
97
  z
src/lib/utils/getGradioApi.ts CHANGED
@@ -4,8 +4,13 @@ import type { Client } from "@gradio/client";
4
  export type ApiReturnType = Awaited<ReturnType<typeof Client.prototype.view_api>>;
5
 
6
  export async function getGradioApi(space: string) {
7
- const api: ApiReturnType = await fetch(`${base}/api/spaces-config?space=${space}`).then((res) =>
8
- res.json()
 
 
 
 
 
9
  );
10
  return api;
11
  }
 
4
  export type ApiReturnType = Awaited<ReturnType<typeof Client.prototype.view_api>>;
5
 
6
  export async function getGradioApi(space: string) {
7
+ const api: ApiReturnType = await fetch(`${base}/api/spaces-config?space=${space}`).then(
8
+ async (res) => {
9
+ if (!res.ok) {
10
+ throw new Error(await res.text());
11
+ }
12
+ return res.json();
13
+ }
14
  );
15
  return api;
16
  }
src/routes/api/spaces-config/+server.ts CHANGED
@@ -11,9 +11,23 @@ export async function GET({ url, locals }) {
11
  if (!space) {
12
  return new Response("Missing space", { status: 400 });
13
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
 
15
  try {
16
- const api = await (await Client.connect(space)).view_api();
17
  return new Response(JSON.stringify(api), {
18
  status: 200,
19
  headers: {
@@ -21,7 +35,7 @@ export async function GET({ url, locals }) {
21
  },
22
  });
23
  } catch (e) {
24
- return new Response(JSON.stringify({ error: true, message: "Failed to get space API" }), {
25
  status: 400,
26
  headers: {
27
  "Content-Type": "application/json",
 
11
  if (!space) {
12
  return new Response("Missing space", { status: 400 });
13
  }
14
+ // Extract namespace from space URL or use as-is if it's already in namespace format
15
+ let namespace = null;
16
+ if (space.startsWith("https://huggingface.co/spaces/")) {
17
+ namespace = space.split("/").slice(-2).join("/");
18
+ } else if (space.match(/^[a-zA-Z0-9-]+\/[a-zA-Z0-9-]+$/)) {
19
+ namespace = space;
20
+ }
21
+
22
+ if (!namespace) {
23
+ return new Response(
24
+ "Invalid space name. Specify a namespace or a full URL on huggingface.co.",
25
+ { status: 400 }
26
+ );
27
+ }
28
 
29
  try {
30
+ const api = await (await Client.connect(namespace)).view_api();
31
  return new Response(JSON.stringify(api), {
32
  status: 200,
33
  headers: {
 
35
  },
36
  });
37
  } catch (e) {
38
+ return new Response("Error fetching space API. Is the name correct?", {
39
  status: 400,
40
  headers: {
41
  "Content-Type": "application/json",