Loading...
Loading...
Compare original and translation side by side
lemondhttp://localhost:PORT/api/v1base_urlapi_keyvendor/lemonade/lemondhttp://localhost:PORT/api/v1base_urlapi_keyvendor/lemonade/https://lemonade-server.ai/install_options.htmlhttp://localhost:13305/api/v1https://lemonade-server.ai/install_options.htmlhttp://localhost:13305/api/v1[ ] 1. Survey the app's current AI integration
[ ] 2. Pick a model + backend profile
[ ] 3. Place Embeddable Lemonade in the app's tree (full package, not just the binary)
[ ] 4. Add a `lemond` launcher (subprocess + API key + port + per-stage logging)
[ ] 5. Re-point the existing client at lemond (base_url, api_key, 120s timeout — all three required)
[ ] 6. Wait for /api/v1/health, install backend, then PULL the model before first use
[ ] 7. Wire shutdown and error recoveryLog every stage. A local integration has many silent failure points — spawn, health, backend install, model download, first inference. Without a log line at each transition, "nothing happened" is indistinguishable from "broke at stage 3." Emit one clear line per stage as you build (see Step 4); the most common dead-end in this integration — a blank result with no error — is invisible without them.
[ ] 1. 调研应用当前的AI集成情况
[ ] 2. 选择模型+后端配置
[ ] 3. 将Embeddable Lemonade放置到应用目录树中(完整包,而非仅二进制文件)
[ ] 4. 添加`lemond`启动器(子进程+API密钥+端口+分阶段日志)
[ ] 5. 将现有客户端指向lemond(base_url、api_key、120秒超时——三者均为必填)
[ ] 6. 等待/api/v1/health接口返回成功,安装后端,然后在首次使用前拉取模型
[ ] 7. 配置关闭流程与错误恢复机制记录每个阶段的日志。 本地集成存在许多无提示的故障点——进程启动、健康检查、后端安装、模型下载、首次推理。如果每个阶段没有日志记录,“无响应”无法区分是“在第3阶段崩溃”还是其他问题。构建时为每个阶段输出清晰的日志行(参见步骤4);集成中最常见的死胡同——无错误提示的空白结果——没有日志就无法排查。
openaiOpenAI(chat.completionsresponses.createanthropicAnthropic(messages.createapi.openai.comapi.anthropic.comlocalhost:11434OPENAI_API_KEYANTHROPIC_API_KEYopenai-pythonopenai-node@anthropic-ai/sdkgo-openaifetchopenaiOpenAI(chat.completionsresponses.createanthropicAnthropic(messages.createapi.openai.comapi.anthropic.comlocalhost:11434OPENAI_API_KEYANTHROPIC_API_KEYopenai-pythonopenai-node@anthropic-ai/sdkgo-openaifetch| App's primary need | Default model | Recipe | Why |
|---|---|---|---|
| General chat / assistant | | | Small, fast, good tool calling, fits 8GB systems |
| Coding assistant | | | Strong code, runs on iGPU |
| Vision / multimodal chat | | | Small multimodal default |
| NPU-first on Ryzen AI | | | XDNA2 NPU on Windows |
| Speech-to-text (Windows) | | | One model; probe picks NPU → iGPU/dGPU → CPU automatically |
| Speech-to-text (Linux NPU) | | | Linux NPU path; falls back to |
| Text-to-speech | | | CPU-only, low latency |
| Image generation | | | Single-step generation |
llamacpplemondrocmvulkancpullamacpp_backendvulkanvulkanNote: having an NPU does not mean every recipe supports NPU. Confirm the recipe/backend pair isorinstalledviainstallablebefore committing to it. See reference.md for per-recipe decision rules.GET /api/v1/system-info
| 应用主要需求 | 默认模型 | 方案 | 原因 |
|---|---|---|---|
| 通用聊天/助手 | | | 体积小、速度快、工具调用能力强,适配8GB内存的系统 |
| 编码助手 | | | 代码能力强,可在iGPU上运行 |
| 视觉/多模态聊天 | | | 轻量多模态默认选项 |
| 优先使用Ryzen AI的NPU | | | 适配Windows系统的XDNA2 NPU |
| 语音转文本(Windows) | | | 单模型;自动优先选择NPU → iGPU/dGPU → CPU |
| 语音转文本(Linux NPU) | | | Linux NPU路径;非NPU环境下 fallback 到 |
| 文本转语音 | | | 仅CPU运行,低延迟 |
| 图像生成 | | | 单步生成 |
llamacppllamacpp_backendlemondrocmvulkancpuvulkanvulkan注意: 拥有NPU不代表所有方案都支持NPU。在确定方案前,需通过确认方案/后端组合是已安装或可安装的。请查看reference.md了解各方案的决策规则。GET /api/v1/system-info
https://github.com/lemonade-sdk/lemonade/releases/latestlemonade-embeddable-{VERSION}-windows-x64.ziplemonade-embeddable-{VERSION}-ubuntu-x64.tar.gzDon't hand-build the download URL from the tag. The git tag carries a leading(e.g.v) but the asset filename strips it (v10.8.0), so using the tag verbatim 404s. Ask the GitHub API for the asset by its stable name pattern and use the URL it returns, as below — this stays correct across version and naming changes.lemonade-embeddable-10.8.0-...
undefinedhttps://github.com/lemonade-sdk/lemonade/releases/latestlemonade-embeddable-{VERSION}-windows-x64.ziplemonade-embeddable-{VERSION}-ubuntu-x64.tar.gz不要通过标签手动构建下载URL。 Git标签带有前缀(例如v),但资产文件名会去掉该前缀(v10.8.0),直接使用标签会导致404错误。请通过GitHub API按稳定名称模式获取资产URL,如下所示——这样可确保在版本和命名变更时依然正确。lemonade-embeddable-10.8.0-...
undefined
```bash
```bash
Then download and unpack on Windows (PowerShell):
```powershell
$rel = Invoke-RestMethod https://api.github.com/repos/lemonade-sdk/lemonade/releases/latest
$asset = $rel.assets | Where-Object { $_.name -like "lemonade-embeddable-*-windows-x64.zip" } | Select-Object -First 1
Invoke-WebRequest $asset.browser_download_url -OutFile lemond.zip
Expand-Archive lemond.zip -DestinationPath "$env:TEMP\lemond-unpack"
$folder = $asset.name -replace '\.zip$','' # unpacked dir = asset name without .zip
Copy-Item -Recurse "$env:TEMP\lemond-unpack\$folder\*" vendor\lemonade\
然后在Windows(PowerShell)中下载并解压:
```powershell
$rel = Invoke-RestMethod https://api.github.com/repos/lemonade-sdk/lemonade/releases/latest
$asset = $rel.assets | Where-Object { $_.name -like "lemonade-embeddable-*-windows-x64.zip" } | Select-Object -First 1
Invoke-WebRequest $asset.browser_download_url -OutFile lemond.zip
Expand-Archive lemond.zip -DestinationPath "$env:TEMP\lemond-unpack"
$folder = $asset.name -replace '\.zip$','' # 解压后的目录 = 资产名称去掉.zip
Copy-Item -Recurse "$env:TEMP\lemond-unpack\$folder\*" vendor\lemonade\
On Linux (bash):
```bash
URL=$(curl -s https://api.github.com/repos/lemonade-sdk/lemonade/releases/latest \
| grep browser_download_url | grep ubuntu-x64.tar.gz | cut -d'"' -f4)
curl -L "$URL" | tar -xz --strip-components=1 -C vendor/lemonadeCopy the full package, not just the binary. The archive contains,lemond[.exe],lemonade[.exe], andLICENSE. Theresources/directory is required — without it lemond starts and passes the health check but fails on every model and backend request. Copying only the binary produces a server that looks healthy but cannot function.resources/
vslemondCLI:lemonadeis the embedded server binary that ships with the app. ThelemondCLI is a separate packaging tool used only during development/build time to install backends. The same embeddable archive unpacked above already contains a matchinglemonadenext tolemonade[.exe], so its version aligns with the bundledlemond[.exe]. Do notlemondto get it: the PyPI package is a separate, older release line whose ports, model names, and install API do not match thepip install lemonade-sdkbundled here, and mixing the two is a known source of silent version mismatches. Keep thelemondCLI,lemonade, and the backends all from the one release downloaded in this step so their versions stay aligned.lemond
lemond[.exe]lemonade[.exe]LICENSEresources/vendor/lemonade/
lemond[.exe] # the only binary the app ships
LICENSE
config.json # generated on first run; commit a seed copy
resources/
server_models.json # do not edit; use GET /api/v1/models at runtime
backend_versions.json
bin/ # backends bundled at packaging time
llamacpp/vulkan/llama-server[.exe]
models/ # pre-bundled model weights (optional)
models--unsloth--Qwen3-4B-GGUF/: Do not edit or rely on this file. It can be stale. The only authoritative model list isserver_models.jsonon a runningGET /api/v1/modelsinstance with the backend already installed.lemond
llamacpp:vulkanllamacpp:rocmPOST /api/v1/installGET /api/v1/system-infomodels/POST /api/v1/pullmodels_dir./modelsconfig.jsonautoPackaging time (developer machine, before bundling). Use the lemonade CLI that shipped insideso it matches the bundledvendor/lemonade/version (prefix withlemondor the full path):./vendor/lemonade/lemonade backends install llamacpp:vulkan vendor/lemonade/lemonade backends install flm:npu # Windows NPU path onlyThis bakes the backend binaries intobefore the app ships.vendor/lemonade/bin/does not need to be running. Use a modernlemondCLI whose version matches the bundledlemonade(the copy in the archive you unpacked works); do notlemondfor it.pip install lemonade-sdkFirst-run / runtime (user's machine, afteris running):lemondhttpPOST /api/v1/install {"recipe": "llamacpp", "backend": "rocm"}Use this for hardware-specific backends (e.g.) that cannot be bundled universally.llamacpp:rocmmust already be running (Step 4 complete).lemond
在Linux(bash)中:
```bash
URL=$(curl -s https://api.github.com/repos/lemonade-sdk/lemonade/releases/latest \
| grep browser_download_url | grep ubuntu-x64.tar.gz | cut -d'"' -f4)
curl -L "$URL" | tar -xz --strip-components=1 -C vendor/lemonade复制完整包,而非仅二进制文件。 压缩包包含、lemond[.exe]、lemonade[.exe]和LICENSE。resources/目录是必需的——没有它,lemond可以启动并通过健康检查,但所有模型和后端请求都会失败。仅复制二进制文件会导致服务器看似正常但无法运行。resources/
vslemondCLI:lemonade是随应用打包的嵌入式服务器二进制文件。lemondCLI是仅在开发/构建时使用的独立打包工具,用于安装后端。上述解压的嵌入式包中已包含与lemonade版本匹配的lemond,因此版本保持一致。请勿通过lemonade[.exe]获取该工具:PyPI包是独立的旧版本分支,其端口、模型名称和安装API与此处打包的pip install lemonade-sdk不匹配,混用会导致无提示的版本不兼容问题。确保lemondCLI、lemonade和后端均来自本步骤下载的同一版本,以保持版本一致。lemond
lemond[.exe]lemonade[.exe]LICENSEresources/vendor/lemonade/
lemond[.exe] # 应用仅需打包该二进制文件
LICENSE
config.json # 首次运行时生成;提交一个初始副本
resources/
server_models.json # 请勿编辑;运行时使用GET /api/v1/models获取
backend_versions.json
bin/ # 打包时内置的后端
llamacpp/vulkan/llama-server[.exe]
models/ # 预打包的模型权重(可选)
models--unsloth--Qwen3-4B-GGUF/: 请勿编辑或依赖该文件,它可能已过期。唯一权威的模型列表是运行中的server_models.json实例上的lemond接口(需已安装后端)。GET /api/v1/models
llamacpp:vulkanGET /api/v1/system-infoPOST /api/v1/installllamacpp:rocmmodels/POST /api/v1/pullmodels_dirconfig.json./modelsauto打包时(开发者机器,打包前)。使用目录下的lemonade CLI,确保其与打包的vendor/lemonade/版本匹配(前缀加lemond或完整路径):./vendor/lemonade/lemonade backends install llamacpp:vulkan vendor/lemonade/lemonade backends install flm:npu # 仅适用于Windows NPU路径这会在应用打包前将后端二进制文件嵌入。无需运行vendor/lemonade/bin/。使用与打包的lemond版本匹配的最新lemonade CLI(解压包中的副本即可);请勿通过lemond获取。pip install lemonade-sdk首次运行/运行时(用户机器,启动后):lemondhttpPOST /api/v1/install {"recipe": "llamacpp", "backend": "rocm"}适用于无法通用打包的硬件特定后端(例如)。llamacpp:rocm必须已启动(步骤4完成)。lemond
lemondlemondlemond_launcher.pylemond_launcher.<ext>key = secrets.token_urlsafe(32)socketsubprocesssubprocess.Popen([LEMOND_BIN, LEMOND_DIR, "--port", str(port)], env={**os.environ, "LEMONADE_API_KEY": key})GET /api/v1/healthAuthorization: Bearer {key}portkeyLog one line per lifecycle stage. Build the logging in from the start — not as an afterthought when something breaks. Each silent transition needs a visible marker so a failure points at the exact stage. Aim for:[lemond] Starting on port <port> [lemond] Healthy on port <port> [lemond] <recipe>:<backend> installed (or: already installed / install failed) [lemond] Pulling model <name>... then: Model <name> ready (or: pull returned <status>) [local] <modality> result: <value> (first inference output — empty string here = unpulled model)Logging the first inference result verbatim is what turns the silent-empty failure (Step 6) from a multi-hour mystery into a one-line diagnosis. Route these through the app's normal logging so they can be quieted for release.
Dev-mode file watchers: If the app runs with a file watcher (Tauri, Electron, Next.js, Vite, etc.) that watches the source tree, ensureis excluded from the watched paths. Lemond writes config and cache files at runtime; a watcher that picks these up will restart the app, kill the lemond subprocess, and spawn a new one on a new port — silently breaking any in-flight transcription. Addvendor/lemonade/(or the equivalent) to the watcher's ignore list before testing.vendor/
LEMOND_DIRsecretssocketsubprocessGET http://127.0.0.1:<port>/api/v1/healthlemond/api/v1/healthlemond_launcher.pylemond_launcher.<ext>key = secrets.token_urlsafe(32)socketsubprocess.Popen([LEMOND_BIN, LEMOND_DIR, "--port", str(port)], env={**os.environ, "LEMONADE_API_KEY": key})GET /api/v1/healthAuthorization: Bearer {key}portkey记录每个生命周期阶段的日志。 从一开始就加入日志功能——不要等到出现问题才添加。每个无提示的阶段转换都需要可见标记,以便故障能定位到具体阶段。目标日志如下:[lemond] 在端口<port>启动 [lemond] 端口<port>健康检查通过 [lemond] <recipe>:<backend>已安装 (或:已安装/安装失败) [lemond] 拉取模型<name>... 然后:模型<name>就绪 (或:拉取返回<status>) [local] <modality>结果:<value> (首次推理输出——此处为空字符串表示模型未拉取)记录首次推理结果原文可将无提示的空白结果(步骤6)从数小时的排查难题转化为一行诊断信息。将这些日志接入应用的常规日志系统,以便在发布版本中关闭日志输出。
开发模式文件监视器: 如果应用使用文件监视器(Tauri、Electron、Next.js、Vite等)监视源码目录,请确保被排除在监视路径之外。Lemond运行时会写入配置和缓存文件;监视器检测到这些文件变更会重启应用,杀死lemond子进程,并在新端口启动新进程——这会无声地中断正在进行的转录。测试前将vendor/lemonade/(或等效目录)添加到监视器的忽略列表。vendor/
LEMOND_DIRsecretssocketsubprocessGET http://127.0.0.1:<port>/api/v1/healthlemond/api/v1/healthlemondlemondbase_urlhttp://127.0.0.1:{port}/api/v1api_keyimport httpx
from openai import OpenAI
proc, key, port = start_lemond()
client = OpenAI(
base_url=f"http://127.0.0.1:{port}/api/v1",
api_key=key,
http_client=httpx.Client(timeout=120), # required: 120s for first-run model load
)| Existing client | New | New auth | Timeout |
|---|---|---|---|
| | | |
| | | |
| | | |
Raw | same | | set per-request |
| Ollama-compatible code | | pass key anyway | 120s |
Qwen3-4B-GGUFlemond"local"base_urlhttp://127.0.0.1:{port}/api/v1api_keyimport httpx
from openai import OpenAI
proc, key, port = start_lemond()
client = OpenAI(
base_url=f"http://127.0.0.1:{port}/api/v1",
api_key=key,
http_client=httpx.Client(timeout=120), # 必填:首次运行模型加载需120秒
)| 现有客户端 | 新 | 新认证方式 | 超时设置 |
|---|---|---|---|
| | | |
| | | |
| | | |
原生 | 同上 | | 按请求设置 |
| 兼容Ollama的代码 | | 仍需传入密钥 | 120秒 |
Qwen3-4B-GGUFlemond"local"GET /api/v1/healthPOST /api/v1/load/loadGET /api/v1/healthPOST /api/v1/load/loadPOST /api/v1/pull
{"model": "Whisper-Large-v3-Turbo"}GET /api/v1/modelsSilent-empty is almost always an unpulled model. If inference returns an empty string / blank output with no HTTP error, the model was not downloaded. Check your pull step before debugging anything else — this is the failure mode that wastes the most time. Log the pull result and the first inference result (see Step 4) so this is diagnosable from the console, not by guesswork.
POST /api/v1/pull
{"model": "Whisper-Large-v3-Turbo"}GET /api/v1/models无提示空白结果几乎总是因为模型未拉取。 如果推理返回空字符串/空白输出且无HTTP错误,说明模型未下载。在调试其他问题前先检查拉取步骤——这是最浪费时间的故障模式。记录拉取结果和首次推理结果(参见步骤4),以便通过控制台排查问题,而非猜测。
server spawn → health 200 → backend install → model download → model load → first result服务器启动 → 健康检查200 → 后端安装 → 模型下载 → 模型加载 → 首次结果返回| Symptom | Cause | Recovery |
|---|---|---|
| Inference returns empty / blank with HTTP 200, no error | Model never pulled: backend is installed but weights are absent, so lazy-load has nothing to load | |
| Model not pulled yet (same root cause as the empty-result row above) | |
| Backend not installed for this hardware | |
| Subprocess exits immediately | Port race: another process grabbed the port between | The reference launcher retries with a fresh port automatically (3 attempts) |
| First-run backend extraction is slow on cold disk | Extend timeout to 90s on first launch, 30s after |
| HTTP 401 on every request | Forgot the | Audit the client config because Lemonade rejects unauth'd calls when |
proc.terminate()proc.kill()lemondlemond/api/v1/health| 症状 | 原因 | 恢复方式 |
|---|---|---|
| 推理返回空/空白结果,HTTP 200,无错误 | 模型从未拉取:后端已安装但权重缺失,因此懒加载无内容可加载 | 调用 |
| 模型未拉取(与上述空白结果的根本原因相同) | 调用 |
| 当前硬件未安装对应后端 | 调用 |
| 子进程立即退出 | 端口冲突:在 | 参考启动器会自动使用新端口重试(最多3次) |
| 首次运行时后端在冷磁盘上提取速度慢 | 首次启动时将超时延长至90秒,后续启动为30秒 |
| 所有请求返回HTTP 401 | 忘记添加 | 检查客户端配置,因为当设置 |
proc.terminate()proc.kill()lemondlemond/api/v1/healthvendor/lemonade/lemond[.exe]lemonade[.exe]LICENSEresources/lemondGET /api/v1/healthGET /api/v1/modelsvendor/lemonade/lemondbin/vendor/lemonade/lemond[.exe]lemonade[.exe]LICENSEresources/lemondGET /api/v1/healthGET /api/v1/modelsvendor/lemonade/lemondbin/recipe_options.jsonrecipe_options.json