我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

Klavis 项目 Hugging Face MCP 服务器 Gradio Widget 本地开发 Shim 实战指南

Klavis 项目 Hugging Face MCP 服务器 Gradio Widget 本地开发 Shim 实战指南 Klavis 项目 Hugging Face MCP 服务器 Gradio Widget 本地开发 Shim 实战指南【免费下载链接】klavisKlavis AI: MCP integration platforms that let AI agents use tools reliably at any scale项目地址: https://gitcode.com/GitHub_Trending/kl/klavis本篇技术指南围绕 mcp_servers/hugging_face/packages/app/src/web/components/WIDGET_DEV_README.md 展开深入讲解 Klavis 仓库中 Hugging Face MCP 服务器前端packages/app即官方 Hugging Face MCP Server 的 Web 端里 Gradio Widget 的本地开发测试机制——GradioWidgetDevShim开发 Shim。你不需要 ChatGPT 集成即可在本机浏览器中预览、调试 Widget 的渲染行为与交互逻辑。读完本文你将掌握如何启动dev:widget开发命令、如何通过 iframe 隔离环境 Mock 的window.openaiAPI 模拟宿主注入、如何构造toolOutput/widgetState测试数据、如何理解并扩展这套通用 Skybridge Widget 测试框架以及开发环境与 ChatGPT 生产环境的差异。什么是 Gradio Widget 开发 Shim在 ChatGPT / OpenAI 生态中Widget 通常运行在宿主页面注入的window.openai全局 API 之上数据由宿主通过openai:set_globals自定义事件下发。这种依赖导致在没有 ChatGPT 客户端时难以单独开发调试。Klavis 仓库中的 Hugging Face MCP 服务器 Web 前端通过一个通用 iframe 测试环境Shim解决了这一问题它把 Widget 加载进隔离的 iframe在 iframe 的contentWindow上 Mock 出window.openaiAPI从而让开发者可以在纯浏览器环境下复现生产环境的行为。从源码结构看Shim 核心位于 GradioWidgetDevShim.tsx它的入口是 gradio-widget-dev.tsx以StrictMode挂载GradioWidgetDevShim组件而被测的 Widget 本体则由 gradio-widget.tsx 渲染 GradioWidgetApp.tsx。快速启动一条命令跑起本地预览在仓库根目录安装依赖后该包基于 pnpm workspace 管理进入packages/app目录执行cd mcp_servers/hugging_face/packages/app npm run dev:widgetdev:widget在 package.json 中定义为vite dev即dev:vite的等价脚本由 Vite 开发服务器驱动。启动成功后在浏览器中访问http://localhost:5173/gradio-widget-dev.html此时页面由左右两个面板组成左侧面板约 60%iframe 展示被测 Widget加载gradio-widget.html右侧面板约 40%交互控制区用于注入数据与切换状态。整体设计目标是提供一个与生产环境行为一致production-like的隔离预览Widget 始终运行在 iframe 沙箱中与宿主页面互不干扰。初始化流程与底层事件机制文档给出的初始化流程如下1. Iframe loads gradio-widget.html 2. Shim injects window.openai mock API 3. Shim auto-sends initial toolOutput data 4. Widget React app initializes with data 5. Hooks (useWidgetProps, etc.) read window.openai 6. Widget renders with test data结合源码可以还原更精确的实现细节iframe 加载完成Shim 在 iframe 上注册load事件监听见 GradioWidgetDevShim.tsx。注入 Mock API在handleLoad中构造mockOpenAi对象并在Widget 脚本执行之前写入iframe.contentWindow.openai源码注释明确标注 Inject into iframes window BEFORE the widget loads见第 L151-L153 行随后打印[Shim] window.openai initialized in iframe。自动下发初始数据通过setTimeout(..., 100)延迟 100ms确保 iframe 内的 React 已挂载完成再把解析后的toolOutput与widgetState写入 mock 对象并派发事件。事件驱动渲染Mock API 中setWidgetState、callTool、sendFollowUpMessage、openExternal、requestDisplayMode等方法的实现见第 L111-L149 行它们把 Widget 的回调转发到 Shim 的控制面板并打印[Shim]前缀日志。事件类型与全局类型定义openai:set_globals事件与OpenAiGlobals类型的定义位于 hooks/types.ts其中关键类型包括OpenAiGlobals视觉theme、用户环境userAgent、locale、布局maxHeight、displayMode、safeArea、状态toolInput、toolOutput、toolResponseMetadata、widgetState、setWidgetStateDisplayModepip | inline | fullscreenThemelight | darkSET_GLOBALS_EVENT_TYPE常量openai:set_globals宿主 APIcallTool、sendFollowUpMessage、openExternal、requestDisplayMode。值得注意的是文件注释说明这些类型currently copied from types.ts in chatgpt/web-sandbox即是对 ChatGPT Web 沙箱公共约定的复刻未来将改用公开包。Hooks 的订阅机制Shim 与 Widget 之间的反应式同步建立在useSyncExternalStore之上React 18 提供的订阅外部 store 的标准 API。以 useOpenAiGlobal.ts 为根基订阅阶段监听openai:set_globals事件事件detail.globals中含目标 key 时触发onChange()getSnapshot返回window.openai?.[key]在window未定义SSR时安全降级。围绕它派生的 hooks 有见 hooks/index.ts 的导出useWidgetProps.ts读取toolOutput支持传入默认值或默认值工厂函数useDisplayMode.ts读取displayModeuseMaxHeight.ts读取maxHeightuseTheme.ts读取themeuseWidgetState.ts读取widgetState并返回setWidgetState在调用时会同步到window.openai?.setWidgetState即宿主的持久化通道。当 Shim 更新数据时整个链路是Shim (parent window) ↓ iframe.contentWindow.openai {...} ↓ new CustomEvent(openai:set_globals) ↓ iframeWindow.dispatchEvent(event) ↓ Widget (iframe) ← useSyncExternalStore listens to events ← Hook triggers re-render ← Component updates with new data交互控制台详解右侧控制面板由 GradioWidgetDevShim.tsx 的 JSX 渲染包含以下控件控件说明源码依据Tool Output (JSON)编辑下发给 Widget 的toolOutput数据第 L270-L281 行Widget State (JSON)编辑双向状态widgetState第 L283-L294 行Display Modeinline/fullscreen/pip三态按钮第 L296-L314 行Max Height400–1200px 范围滑块步长 50默认 800第 L316-L330 行Themelight/dark切换第 L332-L350 行Send Update手动把当前面板数据下发到 Widget第 L352-L358 行逻辑见sendUpdate第 L188-L234 行Quick Presets一键注入常见场景数据第 L360-L450 行Send Update 的数据流sendUpdate第 L188-L234 行的执行步骤解析两个 JSON 文本域widgetStateJson为空串时按null处理从iframe.contentWindow取openaiApi未初始化则报错window.openai not initialized把toolOutput、widgetState、displayMode、maxHeight、theme全部写回 mock 对象构造openai:set_globals事件并dispatchEvent到 iframe window控制台打印[Shim] Update sent to widgetJSON 非法时在面板顶部红框显示Invalid JSON: ...。状态持久化Shim 具备localStorage 持久化首次挂载时读取gradio-widget-dev-state恢复上次的toolOutputJson、widgetStateJson、displayMode、maxHeight、theme第 L36-L50 行任何变化都会写回同一 key第 L53-L62 行。这样刷新页面后测试数据不丢失。自动联动当displayMode、maxHeight、theme变化时第 L65-L91 行Shim 会自动更新 mock 对象上的对应字段并派发事件无需手动点击 Send UpdateWidget 会立即响应。这也是 Widget 中useDisplayMode、useMaxHeight、useTheme三个 hook 实时生效的来源。快捷预设Quick Presets源码内置了 5 个预设按钮对应常见测试场景预设注入的 toolOutput用途Audio WAV{ url: https://example.com/audio.wav }音频渲染Image PNG{ url: https://huggingface.co/datasets/huggingface/brand-assets/resolve/main/hf-logo.png }图片渲染Video MP4{ url: https://example.com/video.mp4 }视频类数据Empty (no content){}空状态Show LoadingtoolOutput null加载态Huggy 动画构造测试数据toolOutput 与 widgetState文档给出了 4 组可直接复制的测试 JSONShim 中也有默认值与预设对应音频文件{ url: https://example.com/audio.wav }这也是 Shim 的DEFAULT_TOOL_OUTPUT见第 L4-L6 行。结合 GradioWidgetApp.tsx 的实现isAudioUrl通过url是否以.wav结尾大小写不敏感判定命中后渲染audio controls播放器并展示spaceName对应的 Hugging Face Space 链接。视频文件{ url: https://example.com/video.mp4 }GradioWidgetApp当前只对音频.wav与图片.jpg/.jpeg/.png/.gif/.webp/.svg提供专用预览其他 URL 类型包括视频落入兜底分支显示 Content available but no preview available for this type. 并展示 URL。空状态{}toolOutput非空对象但无url时Widget 显示 No content to display.若把toolOutput设为null对应 Show Loading 预设则进入加载态随机播放 Huggy Face 的 Huggy 动画60% Huggy Pop、30% Vibing、10% Doodle 的加权随机见第 L31-L37 行并显示 Loading...。自定义数据{ url: https://your-file-url.com/file.ext, metadata: { duration: 120, title: My Audio } }GradioToolOutput接口第 L4-L8 行声明了url与spaceName字段并允许任意扩展键[key: string]: unknown因此metadata等附加信息会被透传Widget 可按需读取。调试技巧读懂 [Shim] 日志打开浏览器 DevTools Console可以按以下日志确认链路是否正常日志含义[Shim] window.openai initialized in iframeMock API 注入完成[Shim] Initial data sent to widget初始数据自动下发成功[Shim] Update sent to widget手动 Send Update 已派发[Shim] setWidgetState called: {...}Widget 调用了setWidgetState[Shim] callTool called: name argsWidget 调用了callTool返回固定{ result: Mock tool response }[Shim] sendFollowUpMessage called: {...}Widget 请求发送追问消息[Shim] openExternal called: {...}Widget 请求打开外部链接Shim 用window.open(..., _blank)响应[Shim] Auto-updated displayMode, maxHeight, theme布局/主题自动联动已触发[Shim] Failed to send initial data: ...初始 JSON 解析失败Invalid JSON: ...面板顶部红色提示sendUpdate解析失败此外requestDisplayMode被调用时如 Widget 请求全屏Shim 会同步切换右侧面板的displayMode状态并返回承诺的 mode模拟宿主响应源码注释指出移动端 PiP 会被强制为 fullscreen。扩展这套 Shim 到其他 Widget文档明确指出这是一个通用测试框架This is ageneric shimthat can test any Skybridge widget。扩展步骤更换 iframe 源把 GradioWidgetDevShim.tsx 中iframe.src从/gradio-widget.html改为你的 Widget HTML 入口调整默认数据按目标 Widget 的数据形状修改DEFAULT_TOOL_OUTPUT与DEFAULT_WIDGET_STATE按需增加控件若目标 Widget 有专属参数可在右侧面板追加对应的 JSON 编辑器或按钮复用 hooks目标 Widget 的 React 组件只要使用useWidgetProps、useDisplayMode、useMaxHeight、useTheme、useWidgetState等标准 Skybridge hooks就能零改动接入 Shim自动获得事件订阅能力。从打包脚本看Widget 的生产构建由build:gradio-widgetcross-env VITE_BUILD_TARGETgradio-widget vite build node scripts/html-to-ts.js见 package.json完成产物被内联为 TypeScript 字符串供服务端注入这解释了为何开发时以独立 HTML 页面加载、生产时以单文件形态嵌入。开发环境 vs 生产环境文档提供的对比表是理解这套设计的关键方面开发Shim生产ChatGPT数据来源JSON 文本域ChatGPT tool outputwindow.openai由 Shim Mock由 ChatGPT 提供事件手动触发由 ChatGPT 触发隔离iframe 沙箱iframe 沙箱行为一致一致Widget 在两种环境下行为一致的原因均可在仓库源码中得到印证同一套 hooksuseWidgetProps、useDisplayMode等全部基于window.openai与openai:set_globals事件不感知数据来源同一套事件系统事件类型常量SET_GLOBALS_EVENT_TYPE openai:set_globals是唯一的数据通道同一套隔离生产环境同样以 iframe 承载 Widget注入的 API 对象结构与 Mock 对象结构一致MockOpenAiApi即PartialOpenAiGlobals与宿主 API 的组合同一套 APIwindow.openai上的字段与回调方法名一一对应。因此在 Shim 中验证通过的行为可以直接预期在 ChatGPT 生产宿主上成立反之若 Widget 依赖了 Mock 未提供的字段例如safeArea的动态变化、真实的toolResponseMetadata则需要回到 hooks/types.ts 核对OpenAiGlobals完整契约并在 Mock 中补全对应实现。小结GradioWidgetDevShim用约 450 行 React 代码构建了一个无宿主可调试、有宿主零差异的 Widget 开发闭环iframe 隔离保证环境纯净Mock 的window.openai完整复刻宿主契约openai:set_globals事件 useSyncExternalStore驱动反应式渲染JSON 编辑、预设、持久化与日志让测试场景的构造与排查都变得高效。对于在 Klavis 仓库中开发或扩展 Hugging Face MCP 服务器 Web 端 Widget 的开发者这是最直接的本地调试入口npm run dev:widget后访问http://localhost:5173/gradio-widget-dev.html即可开始验证你的 Widget 在任何数据形态下的表现。【免费下载链接】klavisKlavis AI: MCP integration platforms that let AI agents use tools reliably at any scale项目地址: https://gitcode.com/GitHub_Trending/kl/klavis创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表