#挑战100天100个GPTs
写在前面
- 前段时间做了一个 mermaid 助手可以生成内容
- 但是无法直接生成图片算是一个缺憾,于是想着给它升级一下
效果呈现
期望目标
GPTs 源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 角色:Mermaid 专家
"Rain's Mermaid助手" 旨在理解用户需求,提供基于文本描述和截图转换为Mermaid语法的图表生成服务。此服务确保提供所有Mermaid格式的视觉参考,并为每种格式提供语法解释。
能力: - 仅生成符合Mermaid语法的图表。 - 接受中文输入,严格遵守Mermaid语法规则。 - 为所有用户提供详细解释。 - 将用户截图转换为Mermaid语法,强调准确、全面和忠实原始内容的重要性。 - 自动生成Mermaid图表的图片并提供链接。
工作流程: 1. 根据用户描述生成最合适的Mermaid图表。 2. 以Markdown格式展示Mermaid图表。 3. 创建所有Mermaid样式的视觉参考图。 4. 将截图转换为Mermaid语法,确保完整、全面和忠实地代表原始内容。 5. 根据用户专业知识调整解释,确保初学者易于理解,高级用户获得深入信息。 6. 在回复中包含生成的Mermaid图表图片链接。 在交流过程中,要求全程使用中文。 7. 注意:每次生成Mermaid代码后,都要立即调用 "mermaidChart" action 来获取图片链接,并在回复中包含该链接。
在交流过程中,要求全程使用中文。
|
思考过程
为什么选择用插入 HTML 的方式
这里的思考出发点是为了方便后续调整样式。实际上直接用 nodejs 的 mermaid 包也是可以的,但是在托管服务器 上有文件大小的限制,这里尝试了几种方式都失败了。后来想到用这个相对比较简单的办法,通过在一个 html 中嵌入 markdown 的代码,渲染网页,再截屏。
为什么考虑用图片而不是 base64 编码
还是从最初的角度出发来思考。如何返回给 openai 一个结果,可能有 2 个方式,图片 url,base 64 编码的图片内容。如果是 url,图片还能存放一下,编码内容相对麻烦一些。思考再三,最后决定采用 url 的方式。还有个原因对 base64 编码没信心,放弃了。
为什么要传到网盘去?
这里考虑是 openai 访问我自己的服务器负担比较重,本来就是一个免费的托管服务器,加图片麻烦,后来选择了一个免费的网盘先用着了。
给我们看看代码?
有点乱,如果想完整代码可以加微信。下面是部分关键代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| async function generateMermaidImage(mermaidCode) { const cacheKey = `mermaid_${Buffer.from(mermaidCode).toString('base64')}`; const cachedImage = cache.get(cacheKey); if (cachedImage) { return cachedImage; }
let browser; try { browser = await launchBrowser(); const page = await browser.newPage();
await page.setViewport({ width: FIXED_WIDTH, height: 1000, deviceScaleFactor: 2, });
const htmlTemplate = await fs.readFile(path.join(process.cwd(), 'public', 'mermaid-template.html'), 'utf-8'); const html = htmlTemplate.replace('{{MERMAID_CODE}}', mermaidCode); await page.setContent(html, { waitUntil: 'networkidle0' }); try { await page.waitForSelector('#mermaid-container svg', { timeout: SELECTOR_TIMEOUT1 }); } catch (error) { throw new Error('Mermaid rendering timed out'); }
const height = await page.evaluate(() => { const svg = document.querySelector('#mermaid-container svg'); return svg ? svg.getBoundingClientRect().height : 0; });
if (height === 0) { console.error('Failed to get SVG height'); throw new Error('Failed to render Mermaid diagram'); } await page.setViewport({ width: FIXED_WIDTH, height: Math.ceil(height), deviceScaleFactor: 2, }); const element = await page.$('#mermaid-container'); if (!element) { throw new Error('Mermaid container not found'); } const screenshot = await element.screenshot({ type: 'webp', omitBackground: true }); const optimizedImage = await sharp(screenshot) .png({ quality: 80, compressionLevel: 9 }) .toBuffer();
const optimizedBase64 = optimizedImage.toString('base64'); cache.set(cacheKey, optimizedBase64); return optimizedBase64; } catch (error) { console.error('Error in generateMermaidImage:', error); throw error; } finally { if (browser) { await browser.close(); console.log('Browser closed'); } } }
|