Download frontend/src/features/docs/api-docs-page.tsx from li2895/li: direct link, hf CLI and curl.
- Browser
- Download file 20.3 kB
-
https://huggingface.co/spaces/li2895/li/resolve/main/frontend/src/features/docs/api-docs-page.tsx
- Command line
-
hf download hf://spaces/li2895/li/frontend/src/features/docs/api-docs-page.tsx
-
curl -L -o api-docs-page.tsx https://huggingface.co/spaces/li2895/li/resolve/main/frontend/src/features/docs/api-docs-page.tsx
20.3 kB
| import { useQuery } from "@tanstack/react-query"; | |
| import { Braces, Code2, Info, Link2 } from "lucide-react"; | |
| import { useState, type ReactNode } from "react"; | |
| import { Navigate, useParams } from "react-router-dom"; | |
| import { useTranslation } from "react-i18next"; | |
| import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; | |
| import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; | |
| import { listModels } from "@/entities/model/model-api"; | |
| import type { ModelRouteDTO } from "@/entities/model/types"; | |
| import { getSystemInfo } from "@/entities/system/system-api"; | |
| import { runtimeConfig } from "@/shared/config/runtime-config"; | |
| import { CopyButton } from "@/shared/components/copy-button"; | |
| import { cn } from "@/shared/lib/cn"; | |
| type ExampleLanguage = "curl" | "python" | "javascript"; | |
| type ExampleView = "request" | "response"; | |
| type Method = "GET" | "POST"; | |
| type Capability = ModelRouteDTO["capability"]; | |
| type FieldDefinition = { | |
| name: string; | |
| required?: boolean; | |
| descriptionKey: string; | |
| }; | |
| type EndpointDefinition = { | |
| key: string; | |
| category: string; | |
| title: string; | |
| method: Method; | |
| path: string; | |
| descriptionKey: string; | |
| capabilities: Capability[]; | |
| fields: FieldDefinition[]; | |
| noteKeys: string[]; | |
| request: (model: string) => Record<string, unknown> | undefined; | |
| response: Record<string, unknown>; | |
| }; | |
| const exampleLanguages: ExampleLanguage[] = ["curl", "python", "javascript"]; | |
| const endpoints: Record<string, EndpointDefinition> = { | |
| "chat/completions": { | |
| key: "chat/completions", category: "Chat", title: "Chat completions", method: "POST", path: "/chat/completions", | |
| descriptionKey: "docs.endpointChat", capabilities: ["chat", "responses"], | |
| fields: [ | |
| { name: "model", required: true, descriptionKey: "docs.reference.fieldModel" }, | |
| { name: "messages", required: true, descriptionKey: "docs.reference.fieldChatMessages" }, | |
| { name: "stream", descriptionKey: "docs.reference.fieldStream" }, | |
| { name: "max_completion_tokens", descriptionKey: "docs.reference.fieldMaxCompletionTokens" }, | |
| { name: "tools", descriptionKey: "docs.reference.fieldTools" }, | |
| { name: "tool_choice", descriptionKey: "docs.reference.fieldToolChoice" }, | |
| ], | |
| noteKeys: ["docs.reference.noteChatImages", "docs.reference.noteChatTools"], | |
| request: (model) => ({ model, messages: [{ role: "system", content: "You are a concise assistant." }, { role: "user", content: "Explain HTTP streaming." }], stream: false }), | |
| response: { id: "chatcmpl_example", object: "chat.completion", model: "grok-chat-fast", choices: [{ index: 0, message: { role: "assistant", content: "HTTP streaming sends response data incrementally." }, finish_reason: "stop" }], usage: { prompt_tokens: 24, completion_tokens: 12, total_tokens: 36 } }, | |
| }, | |
| "chat/responses": { | |
| key: "chat/responses", category: "Chat", title: "Responses", method: "POST", path: "/responses", | |
| descriptionKey: "docs.endpointResponses", capabilities: ["chat", "responses"], | |
| fields: [ | |
| { name: "model", required: true, descriptionKey: "docs.reference.fieldModel" }, | |
| { name: "input", required: true, descriptionKey: "docs.reference.fieldInput" }, | |
| { name: "instructions", descriptionKey: "docs.reference.fieldInstructions" }, | |
| { name: "stream", descriptionKey: "docs.reference.fieldStream" }, | |
| { name: "store", descriptionKey: "docs.reference.fieldStore" }, | |
| { name: "previous_response_id", descriptionKey: "docs.reference.fieldPreviousResponse" }, | |
| { name: "tools", descriptionKey: "docs.reference.fieldTools" }, | |
| ], | |
| noteKeys: ["docs.reference.noteResponsesState", "docs.reference.noteResponsesCompact"], | |
| request: (model) => ({ model, input: "Explain HTTP streaming.", store: false, stream: false }), | |
| response: { id: "resp_example", object: "response", status: "completed", model: "grok-chat-fast", output: [{ type: "message", role: "assistant", status: "completed", content: [{ type: "output_text", text: "HTTP streaming sends response data incrementally." }] }], usage: { input_tokens: 18, output_tokens: 12, total_tokens: 30 } }, | |
| }, | |
| "chat/messages": { | |
| key: "chat/messages", category: "Chat", title: "Messages", method: "POST", path: "/messages", | |
| descriptionKey: "docs.endpointMessages", capabilities: ["chat", "responses"], | |
| fields: [ | |
| { name: "model", required: true, descriptionKey: "docs.reference.fieldModel" }, | |
| { name: "max_tokens", required: true, descriptionKey: "docs.reference.fieldMaxTokens" }, | |
| { name: "messages", required: true, descriptionKey: "docs.reference.fieldMessages" }, | |
| { name: "system", descriptionKey: "docs.reference.fieldSystem" }, | |
| { name: "stream", descriptionKey: "docs.reference.fieldStream" }, | |
| { name: "tools", descriptionKey: "docs.reference.fieldAnthropicTools" }, | |
| { name: "tool_choice", descriptionKey: "docs.reference.fieldToolChoice" }, | |
| ], | |
| noteKeys: ["docs.reference.noteMessagesEvents", "docs.reference.noteMessagesLimits"], | |
| request: (model) => ({ model, max_tokens: 1024, system: "You are a concise assistant.", messages: [{ role: "user", content: "Explain HTTP streaming." }], stream: false }), | |
| response: { id: "msg_example", type: "message", role: "assistant", model: "grok-chat-fast", content: [{ type: "text", text: "HTTP streaming sends response data incrementally." }], stop_reason: "end_turn", stop_sequence: null, usage: { input_tokens: 18, output_tokens: 12 } }, | |
| }, | |
| "image/generations": { | |
| key: "image/generations", category: "Image", title: "Image generations", method: "POST", path: "/images/generations", | |
| descriptionKey: "docs.endpointImageGeneration", capabilities: ["image"], | |
| fields: [ | |
| { name: "model", required: true, descriptionKey: "docs.reference.fieldModel" }, | |
| { name: "prompt", required: true, descriptionKey: "docs.reference.fieldPrompt" }, | |
| { name: "n", descriptionKey: "docs.reference.fieldImageCount" }, | |
| { name: "aspect_ratio", descriptionKey: "docs.reference.fieldAspectRatio" }, | |
| { name: "resolution", descriptionKey: "docs.reference.fieldResolution" }, | |
| { name: "response_format", descriptionKey: "docs.reference.fieldResponseFormat" }, | |
| { name: "stream", descriptionKey: "docs.reference.fieldImageStream" }, | |
| ], | |
| noteKeys: ["docs.reference.noteImageCount", "docs.reference.noteImageStorage"], | |
| request: (model) => ({ model, prompt: "A minimal red chair in a bright studio", n: 1, response_format: "url" }), | |
| response: { created: 1783860000, data: [{ url: "http://127.0.0.1:8000/v1/media/images/example" }] }, | |
| }, | |
| "image/edits": { | |
| key: "image/edits", category: "Image", title: "Image edits", method: "POST", path: "/images/edits", | |
| descriptionKey: "docs.endpointImageEdit", capabilities: ["image_edit"], | |
| fields: [ | |
| { name: "model", required: true, descriptionKey: "docs.reference.fieldModel" }, | |
| { name: "prompt", required: true, descriptionKey: "docs.reference.fieldPrompt" }, | |
| { name: "image / images", required: true, descriptionKey: "docs.reference.fieldEditImages" }, | |
| { name: "n", descriptionKey: "docs.reference.fieldImageCount" }, | |
| { name: "response_format", descriptionKey: "docs.reference.fieldResponseFormat" }, | |
| ], | |
| noteKeys: ["docs.reference.noteEditJSON", "docs.reference.noteEditSources", "docs.reference.noteImageStorage"], | |
| request: (model) => ({ model, prompt: "Change the chair to black", image: { url: "https://example.com/chair.png" }, n: 1, response_format: "url" }), | |
| response: { created: 1783860000, data: [{ url: "http://127.0.0.1:8000/v1/media/images/example" }] }, | |
| }, | |
| "video/generations": { | |
| key: "video/generations", category: "Video", title: "Video generations", method: "POST", path: "/videos/generations", | |
| descriptionKey: "docs.endpointVideoCreate", capabilities: ["video"], | |
| fields: [ | |
| { name: "model", required: true, descriptionKey: "docs.reference.fieldModel" }, | |
| { name: "prompt", descriptionKey: "docs.reference.fieldVideoPrompt" }, | |
| { name: "duration", descriptionKey: "docs.reference.fieldDuration" }, | |
| { name: "aspect_ratio", descriptionKey: "docs.reference.fieldAspectRatio" }, | |
| { name: "resolution", descriptionKey: "docs.reference.fieldResolution" }, | |
| { name: "image", descriptionKey: "docs.reference.fieldVideoImage" }, | |
| { name: "reference_images", descriptionKey: "docs.reference.fieldReferenceImages" }, | |
| ], | |
| noteKeys: ["docs.reference.noteVideoAsync", "docs.reference.noteVideoStrict"], | |
| request: (model) => ({ model, prompt: "A paper airplane flying over a city", duration: 8, aspect_ratio: "16:9", resolution: "720p" }), | |
| response: { request_id: "video_example" }, | |
| }, | |
| "video/get": { | |
| key: "video/get", category: "Video", title: "Get video", method: "GET", path: "/videos/{request_id}", | |
| descriptionKey: "docs.endpointVideoGet", capabilities: ["video"], | |
| fields: [{ name: "request_id", required: true, descriptionKey: "docs.reference.fieldRequestId" }], | |
| noteKeys: ["docs.reference.noteVideoPolling", "docs.reference.noteVideoStatus"], | |
| request: () => undefined, | |
| response: { status: "done", model: "grok-imagine-video", progress: 100, video: { url: "https://example.com/generated.mp4", duration: 8, respect_moderation: true } }, | |
| }, | |
| }; | |
| export function ApiDocsPage() { | |
| const { t } = useTranslation(); | |
| const { category, endpoint } = useParams(); | |
| const definition = endpoints[`${category ?? ""}/${endpoint ?? ""}`]; | |
| const [language, setLanguage] = useState<ExampleLanguage>("curl"); | |
| const [exampleView, setExampleView] = useState<ExampleView>("request"); | |
| const [selectedModel, setSelectedModel] = useState(""); | |
| const systemQuery = useQuery({ queryKey: ["system-info"], queryFn: getSystemInfo, staleTime: Number.POSITIVE_INFINITY, retry: 1 }); | |
| const modelsQuery = useQuery({ queryKey: ["docs", "available-models"], queryFn: () => listModels({ page: 1, pageSize: 100 }), staleTime: 30_000 }); | |
| if (!definition) return <Navigate to="/docs/chat/completions" replace />; | |
| const publicApiBaseUrl = systemQuery.data?.publicApiBaseURL || runtimeConfig.publicApiBaseUrl; | |
| const baseUrl = `${publicApiBaseUrl.replace(/\/$/, "")}/v1`; | |
| const availableModels = (modelsQuery.data?.items ?? []).filter((model) => model.enabled && model.available && definition.capabilities.includes(model.capability)); | |
| const selectedModelAvailable = availableModels.some((model) => model.publicId === selectedModel); | |
| const exampleModel = (selectedModelAvailable ? selectedModel : availableModels[0]?.publicId) || fallbackModel(definition.key); | |
| const examples = createExamples(definition, baseUrl, exampleModel); | |
| const responseExample = JSON.stringify(withExampleModel(definition.response, exampleModel), null, 2); | |
| return ( | |
| <div className="w-full space-y-10"> | |
| <header className="space-y-4"> | |
| <div className="min-w-0"> | |
| <h1 className="text-2xl font-medium text-foreground">{definition.title}</h1> | |
| <p className="mt-2 max-w-2xl text-sm leading-6 text-muted-foreground">{t(definition.descriptionKey)}</p> | |
| </div> | |
| <EndpointSignature method={definition.method} path={`/v1${definition.path}`} /> | |
| </header> | |
| <div className="space-y-10"> | |
| <DocsSection icon={<Link2 />} title={t("docs.reference.connection")}> | |
| <div className="grid gap-4 sm:grid-cols-2"> | |
| <ConnectionItem label={t("docs.baseUrl")} value={baseUrl} /> | |
| <ConnectionItem label={t("docs.authentication")} value={definition.key === "chat/messages" ? "x-api-key: g2a_..." : "Authorization: Bearer g2a_..."} /> | |
| {definition.key === "chat/messages" ? <ConnectionItem label="anthropic-version" value="2023-06-01" /> : null} | |
| </div> | |
| </DocsSection> | |
| <DocsSection icon={<Braces />} title={definition.method === "GET" ? t("docs.reference.pathParameters") : t("docs.reference.requestBody")}> | |
| <div className="overflow-hidden rounded-md bg-card"> | |
| <div className="hidden grid-cols-[minmax(120px,180px)_minmax(0,1fr)] gap-5 bg-secondary/35 px-4 py-2 text-xs text-muted-foreground sm:grid"> | |
| <span>{t("docs.reference.parameter")}</span> | |
| <span>{t("docs.reference.description")}</span> | |
| </div> | |
| <div>{definition.fields.map((field, index) => <ParameterItem key={field.name} field={field} muted={index % 2 === 1} />)}</div> | |
| </div> | |
| </DocsSection> | |
| <DocsSection icon={<Code2 />} title={t("docs.reference.example")}> | |
| <ExamplePanel | |
| view={exampleView} | |
| onViewChange={setExampleView} | |
| language={language} | |
| onLanguageChange={setLanguage} | |
| code={exampleView === "request" ? examples[language] : responseExample} | |
| models={availableModels} | |
| selectedModel={exampleModel} | |
| onModelChange={setSelectedModel} | |
| /> | |
| </DocsSection> | |
| {definition.noteKeys.length > 0 ? ( | |
| <DocsSection icon={<Info />} title={t("docs.reference.notes")}> | |
| <ul className="space-y-2 rounded-md bg-secondary/35 px-4 py-3 text-xs leading-5 text-muted-foreground"> | |
| {definition.noteKeys.map((key) => <li key={key} className="relative pl-3 before:absolute before:left-0 before:top-[0.55rem] before:size-1 before:rounded-full before:bg-muted-foreground/55">{t(key)}</li>)} | |
| </ul> | |
| </DocsSection> | |
| ) : null} | |
| </div> | |
| </div> | |
| ); | |
| } | |
| function withExampleModel(response: Record<string, unknown>, model: string): Record<string, unknown> { | |
| return "model" in response ? { ...response, model } : response; | |
| } | |
| function fallbackModel(key: string): string { | |
| if (key.startsWith("image/")) return key === "image/edits" ? "grok-imagine-image-edit" : "grok-imagine-image"; | |
| if (key.startsWith("video/")) return "grok-imagine-video"; | |
| return "your-enabled-model"; | |
| } | |
| function createExamples(definition: EndpointDefinition, baseUrl: string, model: string): Record<ExampleLanguage, string> { | |
| const request = definition.request(model); | |
| const url = `${baseUrl}${definition.path.replace("{request_id}", "video_example")}`; | |
| const messageHeaders = definition.key === "chat/messages"; | |
| const curlHeaders = messageHeaders | |
| ? [ | |
| ' -H "x-api-key: $GROK2API_API_KEY"', | |
| ' -H "anthropic-version: 2023-06-01"', | |
| ' -H "Content-Type: application/json"', | |
| ].join(" \\\n") | |
| : [ | |
| ' -H "Authorization: Bearer $GROK2API_API_KEY"', | |
| ' -H "Content-Type: application/json"', | |
| ].join(" \\\n"); | |
| const curlBody = request ? ` \\\n -d '${JSON.stringify(request, null, 2)}'` : ""; | |
| const headers = messageHeaders | |
| ? { "x-api-key": "g2a_your_api_key", "anthropic-version": "2023-06-01", "Content-Type": "application/json" } | |
| : { Authorization: "Bearer g2a_your_api_key", "Content-Type": "application/json" }; | |
| const pythonImports = request ? "import json\nimport requests" : "import requests"; | |
| const pythonPayload = request ? `\n\npayload = json.loads(r'''${JSON.stringify(request, null, 2)}''')` : ""; | |
| const pythonBody = request ? ",\n json=payload" : ""; | |
| const javascriptBody = request ? `,\n body: JSON.stringify(${JSON.stringify(request, null, 2)})` : ""; | |
| return { | |
| curl: `export GROK2API_API_KEY="g2a_your_api_key"\n\ncurl -X ${definition.method} "${url}" \\\n${curlHeaders}${curlBody}`, | |
| python: `${pythonImports}${pythonPayload}\n\nresponse = requests.${definition.method.toLowerCase()}(\n "${url}",\n headers=${JSON.stringify(headers, null, 2)}${pythonBody}\n)\nresponse.raise_for_status()\nprint(response.json())`, | |
| javascript: `const response = await fetch("${url}", {\n method: "${definition.method}",\n headers: ${JSON.stringify(headers, null, 2)}${javascriptBody}\n});\n\nif (!response.ok) throw new Error(await response.text());\nconsole.log(await response.json());`, | |
| }; | |
| } | |
| function MethodLabel({ method }: { method: Method }) { | |
| return <span className={cn("font-mono text-xs font-semibold", method === "GET" ? "text-emerald-600 dark:text-emerald-400" : "text-sky-600 dark:text-sky-400")}>{method}</span>; | |
| } | |
| function EndpointSignature({ method, path }: { method: Method; path: string }) { | |
| return ( | |
| <div className="flex h-8 w-fit max-w-full items-center gap-2 rounded-md bg-card px-3"> | |
| <MethodLabel method={method} /> | |
| <code className="min-w-0 truncate text-xs" title={path}>{path}</code> | |
| <CopyButton value={path} /> | |
| </div> | |
| ); | |
| } | |
| function DocsSection({ icon, title, children }: { icon: ReactNode; title: string; children: ReactNode }) { | |
| return ( | |
| <section className="space-y-3.5"> | |
| <div className="flex items-center gap-2 text-sm font-medium [&_svg]:size-4 [&_svg]:text-muted-foreground">{icon}{title}</div> | |
| <div className="min-w-0">{children}</div> | |
| </section> | |
| ); | |
| } | |
| function ConnectionItem({ label, value }: { label: string; value: string }) { | |
| return ( | |
| <div className="min-w-0"> | |
| <div className="mb-1.5 text-xs text-muted-foreground">{label}</div> | |
| <div className="flex h-8 min-w-0 items-center rounded-md bg-secondary/55 pl-3 pr-0.5"> | |
| <code className="min-w-0 flex-1 truncate text-xs text-muted-foreground" title={value}>{value}</code> | |
| <CopyButton value={value} /> | |
| </div> | |
| </div> | |
| ); | |
| } | |
| function ParameterItem({ field, muted }: { field: FieldDefinition; muted: boolean }) { | |
| const { t } = useTranslation(); | |
| return ( | |
| <div className={cn("grid grid-cols-1 gap-1.5 px-4 py-3 sm:grid-cols-[minmax(120px,180px)_minmax(0,1fr)] sm:gap-5", muted && "bg-secondary/20")}> | |
| <div className="min-w-0"> | |
| <code className="break-all text-xs font-medium text-foreground"> | |
| {field.name} | |
| {field.required ? <span className="ml-1 text-destructive" title={t("docs.reference.required")}>*</span> : null} | |
| </code> | |
| </div> | |
| <div className="min-w-0 text-xs leading-5 text-muted-foreground">{t(field.descriptionKey)}</div> | |
| </div> | |
| ); | |
| } | |
| function ExamplePanel({ | |
| view, | |
| onViewChange, | |
| language, | |
| onLanguageChange, | |
| code, | |
| models, | |
| selectedModel, | |
| onModelChange, | |
| }: { | |
| view: ExampleView; | |
| onViewChange: (view: ExampleView) => void; | |
| language: ExampleLanguage; | |
| onLanguageChange: (language: ExampleLanguage) => void; | |
| code: string; | |
| models: ModelRouteDTO[]; | |
| selectedModel: string; | |
| onModelChange: (model: string) => void; | |
| }) { | |
| const { t } = useTranslation(); | |
| return ( | |
| <div className="overflow-hidden rounded-lg bg-card"> | |
| <div className="flex min-h-12 flex-wrap items-center gap-2 px-3 py-2"> | |
| <Tabs value={view} onValueChange={(value) => onViewChange(value as ExampleView)}> | |
| <TabsList> | |
| <TabsTrigger value="request">{t("docs.reference.request")}</TabsTrigger> | |
| <TabsTrigger value="response">{t("docs.reference.response")}</TabsTrigger> | |
| </TabsList> | |
| </Tabs> | |
| <div className="flex min-w-0 flex-1 flex-wrap items-center justify-end gap-2"> | |
| {view === "request" ? ( | |
| <Select value={language} onValueChange={(value) => onLanguageChange(value as ExampleLanguage)}> | |
| <SelectTrigger className="h-8 w-28 bg-background text-xs" aria-label={t("docs.exampleLanguage")}><SelectValue /></SelectTrigger> | |
| <SelectContent> | |
| {exampleLanguages.map((item) => <SelectItem key={item} value={item}>{item === "javascript" ? "JavaScript" : item === "python" ? "Python" : "cURL"}</SelectItem>)} | |
| </SelectContent> | |
| </Select> | |
| ) : null} | |
| {models.length > 0 ? ( | |
| <Select value={selectedModel} onValueChange={onModelChange}> | |
| <SelectTrigger className="h-8 w-[190px] max-w-full bg-background text-xs" aria-label={t("docs.reference.exampleModel")}><SelectValue /></SelectTrigger> | |
| <SelectContent>{models.map((model) => <SelectItem key={model.id} value={model.publicId}>{model.publicId}</SelectItem>)}</SelectContent> | |
| </Select> | |
| ) : null} | |
| <CopyButton value={code} /> | |
| </div> | |
| </div> | |
| <pre className="max-h-[480px] overflow-auto bg-secondary/45 p-4 text-xs leading-5 text-foreground"><code>{code}</code></pre> | |
| </div> | |
| ); | |
| } | |