Skip to content

fix(weixin): normalize markdown image syntax#5297

Merged
wenshao merged 1 commit into
QwenLM:mainfrom
tt-a1i:fix/weixin-markdown-image-plain-text
Jun 18, 2026
Merged

fix(weixin): normalize markdown image syntax#5297
wenshao merged 1 commit into
QwenLM:mainfrom
tt-a1i:fix/weixin-markdown-image-plain-text

Conversation

@tt-a1i

@tt-a1i tt-a1i commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

What this PR does

In the WeChat (Weixin) channel, outbound markdown is flattened to plain text by markdownToPlainText because WeChat does not render markdown. This PR reorders the regex pass so that the image syntax ![alt](url) is normalized before the ordinary link syntax [text](url).

Previously the link regex ran first and matched the [alt](url) portion inside ![alt](url), leaving the leading ! outside the match, so an image rendered as !alt (url). With the image rule running first, the whole image token is consumed and rendered as [alt]; since the result no longer contains a trailing (url), the link rule correctly leaves it alone. Ordinary links [text](url) (with no leading !) are still handled by the link rule exactly as before. The regression test that asserted the old buggy !alt (url) output is updated to assert the corrected [alt] output.

Why it's needed

The previous regex ordering produced malformed plain text for any markdown image sent through the WeChat channel: an image such as ![alt](https://img.png) came out as !alt (https://img.png) instead of the intended [alt] alt-text placeholder. This is a user-facing correctness bug in WeChat message output. The fix makes image flattening behave consistently with the existing alt-text intent while leaving normal-link behavior untouched.

Reviewer Test Plan

How to verify

Reproduce the original bug and confirm the fix from packages/channels/weixin:

  • Repro: markdownToPlainText('![alt](https://img.png)')
  • Expected on main (before): !alt (https://img.png) (buggy)
  • Expected with this PR (after): [alt]
  • Normal links are unchanged: markdownToPlainText('[click here](https://example.com)') stays click here (https://example.com) on both versions.

Verification commands (run from packages/channels/weixin unless noted):

  • npx vitest run --config vitest.config.ts src/send.test.ts
  • npx vitest run --config vitest.config.ts
  • npm run build --workspace=@qwen-code/channel-weixin
  • npx eslint packages/channels/weixin/src/send.ts packages/channels/weixin/src/send.test.ts
  • npx prettier --check packages/channels/weixin/src/send.ts packages/channels/weixin/src/send.test.ts
  • git diff --check

Evidence (Before & After)

N/A — non-visible logic fix (plain-text conversion); covered by the unit tests above.

Tested on

OS Status
🍏 macOS ✅ CI
🪟 Windows ✅ CI
🐧 Linux ✅ CI

✅ tested · ⚠️ not tested · N/A

Environment (optional)

Unit tests only (npm workspaces); scoped to the @qwen-code/channel-weixin package.

Risk & Scope

  • Main risk or tradeoff: Low; scoped to markdownToPlainText in the WeChat channel. The behavior change is limited to image tokens ![alt](url) (now [alt]); normal links are byte-identical before and after.
  • Not validated / out of scope: No unrelated refactors, no public API changes, no UI redesigns. Nested clickable images [![alt](img)](link) remain a pre-existing limitation of the naive regex approach and are not addressed here.
  • Breaking changes / migration notes: None.

Linked Issues

None.

中文说明

这个 PR 做了什么

在微信(Weixin)通道里,由于微信不渲染 markdown,发出的 markdown 会被 markdownToPlainText 压平为纯文本。本 PR 调整了正则处理的顺序,让图片语法 ![alt](url) 在普通链接语法 [text](url) 之前被规整。

此前链接正则先执行,匹配到 ![alt](url) 内部的 [alt](url) 片段,把前导的 ! 留在匹配之外,于是图片被渲染成 !alt (url)。让图片规则先执行后,整个图片 token 被一次性消费并渲染为 [alt];由于结果中不再含有结尾的 (url),链接规则便不会误伤它。普通链接 [text](url)(无前导 !)仍由链接规则按原样处理。原本断言旧的错误输出 !alt (url) 的回归测试,已更新为断言修复后的 [alt] 输出。

为什么需要

之前的正则顺序会让任何经微信通道发出的 markdown 图片产生格式错误的纯文本:例如 ![alt](https://img.png) 会输出成 !alt (https://img.png),而非预期的 [alt] 替代文本占位。这是微信消息输出中一个面向用户的正确性 bug。本修复使图片压平行为与既有的替代文本意图保持一致,同时不影响普通链接的行为。

评审者测试计划

如何验证

packages/channels/weixin 下复现原 bug 并确认修复:

  • 复现:markdownToPlainText('![alt](https://img.png)')
  • main 上(改动前)期望:!alt (https://img.png)(错误)
  • 本 PR(改动后)期望:[alt]
  • 普通链接不变:markdownToPlainText('[click here](https://example.com)') 在两个版本都保持 click here (https://example.com)

验证命令(除特别说明外,均在 packages/channels/weixin 下运行):

  • npx vitest run --config vitest.config.ts src/send.test.ts
  • npx vitest run --config vitest.config.ts
  • npm run build --workspace=@qwen-code/channel-weixin
  • npx eslint packages/channels/weixin/src/send.ts packages/channels/weixin/src/send.test.ts
  • npx prettier --check packages/channels/weixin/src/send.ts packages/channels/weixin/src/send.test.ts
  • git diff --check

证据(前后对比)

N/A —— 非可视化逻辑修复(纯文本转换);已由上述单元测试覆盖。

测试平台

操作系统 状态
🍏 macOS ✅ CI
🪟 Windows ✅ CI
🐧 Linux ✅ CI

✅ 已测试 · ⚠️ 未测试 · N/A

环境(可选)

仅单元测试(npm workspaces);范围限于 @qwen-code/channel-weixin 包。

风险与范围

  • 主要风险或取舍:低;范围限于微信通道的 markdownToPlainText。行为变化仅限于图片 token ![alt](url)(现为 [alt]);普通链接在改动前后字节级一致。
  • 未验证 / 范围之外:无无关重构、无公共 API 变更、无 UI 重设计。嵌套的可点击图片 [![alt](img)](link) 仍是朴素正则方案的既有局限,本 PR 不予处理。
  • 破坏性变更 / 迁移说明:无。

关联 Issue

无。

AI Assistance Disclosure

I used Codex to review the changes, sanity-check the implementation against existing patterns, and help spot potential edge cases.

.replace(/^#{1,6}\s+/gm, '')
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1 ($2)')
.replace(/!\[([^\]]*)\]\([^)]+\)/g, '[$1]')
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1 ($2)')

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] Add a comment explaining why the image regex must run before the link regex.

The image pattern ![alt](url) contains the link pattern [alt](url) as a substring, so if the link regex runs first it matches the inner portion and produces !alt (url) instead of [alt] — exactly the bug this PR fixes. The ordering dependency is the load-bearing logic here, but it's completely undocumented. The old test even had a comment describing the wrong behavior ("link regex fires before image regex"); this PR removed that comment without replacing it with one explaining the constraint.

Without a comment, a future refactor (alphabetizing, consolidating into a config-driven loop, etc.) could silently reintroduce the regression.

Suggested change
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1 ($2)')
// Image regex MUST run before the link regex: ![alt](url) contains
// [alt](url) as a substring, so the link regex would match it first.
.replace(/!\[([^\]]*)\]\([^)]+\)/g, '[$1]')
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1 ($2)')

— glm-5.2 via Qwen Code /review

it('converts image syntax (link regex fires before image regex)', () => {
// In the current implementation, the link regex fires before the image regex,
// so `![alt](url)` becomes `!alt (url)` rather than `[alt]`
it('converts image syntax to alt text', () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Suggestion] Consider adding a test that covers mixed images and links in the same input string.

The entire motivation for this reorder is correct coexistence of image and link syntax — yet the tests only exercise each pattern in isolation. A future developer who swaps the two .replace() lines back would see all current tests pass. Adding a mixed-content test would guard against that regression:

Suggested change
it('converts image syntax to alt text', () => {
it('converts image syntax to alt text', () => {
const result = markdownToPlainText('![alt](https://img.png)');
expect(result).toBe('[alt]');
});
it('handles mixed images and links without cross-contamination', () => {
expect(
markdownToPlainText(
'![logo](https://img.png) and [docs](https://docs.com)',
),
).toBe('[logo] and docs (https://docs.com)');
});

— glm-5.2 via Qwen Code /review

@wenshao

wenshao commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@wenshao

wenshao commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

✅ Local verification report — PR #5297 fix(weixin): normalize markdown image syntax

Verdict: LGTM — correct, in-scope, and safe to merge. The fix does exactly what it claims, fixes a real bug, and introduces no regression to normal-link rendering. All tests, build, lint and formatting pass.

What the PR changes

It swaps the order of two replace() calls in markdownToPlainText (packages/channels/weixin/src/send.ts) so the image regex runs before the link regex, and updates the corresponding test expectation.

Root cause on main: for ![alt](url), the link regex \[([^\]]+)\]\(([^)]+)\) fires first and matches the [alt](url) substring, leaving the leading ! outside the match → output !alt (url). The image regex then has nothing left to match. After the reorder, the image regex !\[...\]\(...\) consumes the whole token first → [alt], and since the result has no trailing (url), the link regex correctly leaves it alone.

How it was verified

Real tests run under tmux in two isolated detached worktrees (pr5297-head = be81974, origin/main = 49ff31b), with the repo's hoisted node_modules symlinked in. Env: Node v22.22.2, npm 10.9.7, vitest 3.2.4.

Check Result
Merges cleanly into main (git merge-tree) ✅ no conflicts; weixin package byte-identical between merge-base and current main
Full weixin suite on PR (vitest run) 44/44 (media.test.ts 11 + send.test.ts 33)
BASE suite as-is ✅ 33/33 (asserting the old buggy !alt (url) output)
Genuine regression test — PR's new test against BASE source FAILS as expected: expected '!alt (https://img.png)' to be '[alt]' → proves the test catches the real bug
npm run build --workspace=@qwen-code/channel-weixin (tsc --build) ✅ exit 0
eslint send.ts + send.test.ts ✅ exit 0
prettier --check send.ts + send.test.ts ✅ "All matched files use Prettier code style!"
git diff --check ✅ exit 0

Differential behavior A/B (exact regex chains from both versions)

Input BEFORE (main) AFTER (PR)
![alt](https://img.png) !alt (https://img.png) [alt] the fix
![a cat](https://img.png) !a cat (https://img.png) [a cat] fixed
[click here](https://example.com) click here (https://example.com) click here (https://example.com) unchanged (no regression)
hello! [link](https://x.com) hello! link (https://x.com) hello! link (https://x.com) unchanged (lone ! ≠ image)
[site](a) and ![pic](b) site (a) and !pic (b) site (a) and [pic] link part unchanged, image part fixed
![](https://img.png) (empty alt) [] [] identical on both
![alt][1] (reference-style) ![alt][1] ![alt][1] unchanged (no inline URL)

Across 15 cases: normal links are byte-identical before/after; every difference is an image token that is now rendered correctly.

Minor observation (not a blocker, out of scope)

The nested clickable image [![alt](img)](link) is rendered imperfectly by both versions — [alt (https://img.png)] before vs [[alt]](https://link.com) after. This is a pre-existing limitation of the naive regex approach, not introduced by this PR, and is outside the stated scope. Could be a future follow-up if clickable images matter for WeChat output.

Reproduce

git worktree add --detach /tmp/wt-after  $(gh pr view 5297 --json headRefOid -q .headRefOid)
git worktree add --detach /tmp/wt-before origin/main
ln -s "$PWD/node_modules" /tmp/wt-after/node_modules
ln -s "$PWD/node_modules" /tmp/wt-before/node_modules
# PR (green):
(cd /tmp/wt-after/packages/channels/weixin && ../../../../node_modules/.bin/vitest run --config vitest.config.ts)
# Prove the test catches the bug: copy PR's test onto BASE source -> image test FAILS
cp /tmp/wt-after/packages/channels/weixin/src/send.test.ts /tmp/wt-before/packages/channels/weixin/src/send.test.ts
(cd /tmp/wt-before/packages/channels/weixin && ../../../../node_modules/.bin/vitest run --config vitest.config.ts)
🇨🇳 中文版(点击展开)

✅ 本地验证报告 — PR #5297 fix(weixin): normalize markdown image syntax

结论:可以合并。 修复准确、范围聚焦,修正了一个真实存在的 bug,且不会影响普通链接的渲染。测试、构建、lint、格式化全部通过。

PR 改动内容

markdownToPlainTextpackages/channels/weixin/src/send.ts)中交换了两个 replace() 调用的顺序,让图片正则先于链接正则执行,并同步更新了测试断言。

main 上的根因: 对于 ![alt](url),链接正则 \[([^\]]+)\]\(([^)]+)\) 先命中,匹配了 [alt](url) 这一段,把前面的 ! 留在匹配之外 → 输出 !alt (url),图片正则之后就无内容可匹配。调整顺序后,图片正则 !\[...\]\(...\) 先吃掉整个 token → [alt];由于结果不再带有 (url),链接正则不会误伤它。普通链接 [text](url)(无前导 !)依旧由链接正则按原样处理。

验证方式

tmux 中、两个隔离的 detached worktree(pr5297-head = be81974origin/main = 49ff31b)里运行真实测试,并软链接复用仓库 hoist 的 node_modules。环境:Node v22.22.2、npm 10.9.7、vitest 3.2.4。

检查项 结果
干净合入 maingit merge-tree ✅ 无冲突;weixin 包在 merge-base 与当前 main 之间字节一致
PR 上 weixin 全量测试(vitest run 44/44media.test.ts 11 + send.test.ts 33)
BASE 原样测试 ✅ 33/33(断言旧的错误输出 !alt (url)
回归测试有效性 — 用 PR 的新测试跑 BASE 源码 如期失败expected '!alt (https://img.png)' to be '[alt]' → 证明该测试确实能捕获此 bug
npm run buildtsc --build ✅ exit 0
eslint send.ts + send.test.ts ✅ exit 0
prettier --check ✅ 全部符合 Prettier 风格
git diff --check ✅ exit 0

行为差异 A/B(直接取两个版本的真实正则链对比)

输入 改动前(main 改动后(PR) 说明
![alt](https://img.png) !alt (https://img.png) [alt] 本次修复
[click here](https://example.com) click here (https://example.com) click here (https://example.com) 不变(无回归)
[site](a) and ![pic](b) site (a) and !pic (b) site (a) and [pic] 链接部分不变,图片部分修复
hello! [link](https://x.com) hello! link (https://x.com) hello! link (https://x.com) 不变(单独的 ! 不构成图片)
![](https://img.png)(空 alt) [] [] 两版本一致

15 个用例中:普通链接在改动前后字节级一致;所有差异都是图片 token 现在被正确渲染。

一点小观察(非阻塞、超出本次范围)

可点击图片 [![alt](img)](link)两个版本里都渲染得不理想——改动前 [alt (https://img.png)],改动后 [[alt]](https://link.com)。这是朴素正则方案的既有局限,并非本 PR 引入,且不在本次修复范围内。若 WeChat 输出需要支持可点击图片,可作为后续跟进。


Verified locally with real vitest runs under tmux on isolated git worktrees of the PR head and origin/main. Differential harness used the verbatim regex chains from both versions.

@tt-a1i
tt-a1i marked this pull request as ready for review June 18, 2026 17:01
@wenshao

wenshao commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @tt-a1i, thanks for the fix! The change itself looks correct and well-scoped — swapping the regex order so image syntax is handled before link syntax is the right approach.

However, the PR body doesn't follow the PR template. A few required sections are missing:

  • What this PR does / Why it's needed — the "Summary" section covers the what but not the why (e.g. what user-facing problem does this fix?)
  • Reviewer Test Plan — including Evidence (Before/After) and Tested-on table
  • Risk & Scope — even a one-liner like "no risk, image-only change, normal links unaffected" helps reviewers triage quickly
  • Linked Issues — if there's a related issue
  • 中文说明 — bilingual description block

Could you update the PR body to match the template? The fix itself is solid — just the description needs the standard format so reviewers (and future git log readers) can quickly understand the context.

中文说明

你好 @tt-a1i,感谢修复!代码改动本身正确且范围合理——调换正则顺序让图片语法先于链接语法处理,是正确的做法。

不过 PR 描述没有按照 PR 模板 填写,缺少以下几个必填部分:

  • What this PR does / Why it's needed — "Summary" 只说了 改了什么,没有说 为什么改(比如修了什么用户可见的问题?)
  • Reviewer Test Plan — 包括 Before/After 对比和 Tested-on 表格
  • Risk & Scope — 哪怕只写一句"无风险,仅影响图片,普通链接不受影响"也好
  • Linked Issues — 如果有相关 issue 的话
  • 中文说明 — 双语描述

请按照模板更新 PR 描述。代码本身没问题,只是需要标准格式方便审查和后续查阅。

Qwen Code · qwen3.7-max

@wenshao

wenshao commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓ — the earlier template concern from the CI bot has been addressed.

On direction: this is a real user-facing bug. WeChat doesn't render markdown, so markdownToPlainText flattens it — but the link regex was eating the [alt](url) portion of ![alt](url) before the image regex got a chance, producing !alt (url) instead of [alt]. Users sending images through the WeChat channel see malformed output. Clearly within scope.

On approach: the fix is exactly two lines swapped — reorder the image regex to run before the link regex. This is the minimal correct solution. The image token ![alt](url) contains [alt](url) as a substring, so the more-specific regex must consume it first. No simpler path exists. The diff is clean: no drive-by refactors, no scope creep, no unrelated changes. +3/-5 across two files, one of which is the test update.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板已完善 ✓ — 之前 CI 机器人提出的模板问题已修复。

方向:这是一个真实的用户可见 bug。微信不渲染 markdown,markdownToPlainText 负责压平——但链接正则在图片正则之前执行,会吃掉 ![alt](url) 中的 [alt](url) 部分,产生 !alt (url) 而非 [alt]。通过微信通道发送图片的用户会看到格式错误的输出。明确在项目范围内。

方案:修复就是交换两行正则顺序——让图片正则先于链接正则执行。这是最小正确解。图片 token ![alt](url) 包含 [alt](url) 作为子串,所以更具体的正则必须先消费它。不存在更简单的路径。diff 干净:无顺手重构、无范围蔓延、无无关改动。两个文件 +3/-5,其中一个是测试更新。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal (before reading diff): The bug is that ![alt](url) gets partially consumed by the link regex [text](url) → text (url) before the image regex ![alt](url) → [alt] can fire. The fix is to reorder: image first, link second. The more-specific regex (with the ! prefix) should consume its tokens before the less-specific one.

Diff comparison: the PR does exactly this — a clean two-line swap. My proposal and the PR are identical in approach.

No correctness bugs, no security concerns, no regressions. The existing link test (click here (https://example.com)) still passes, confirming normal links are unaffected. The image regex !\[([^\]]*)\]\([^)]+\)[$1] correctly consumes the full image token so the link regex never sees it.

Edge cases considered:

  • [text](url) with no ! prefix → link regex fires as before ✓
  • ![alt](url) → image regex fires first, produces [alt], link regex doesn't match [alt] (no (url)) ✓
  • Nested [![alt](img)](link) → acknowledged in PR as a pre-existing limitation, not in scope ✓

Testing

This is a pure string transformation (markdown → plain text) with no TUI surface. Verification is via unit tests, which are the correct test vehicle here.

Before (main, buggy behavior):

✓ src/send.test.ts (34 tests) 16ms

  ✓ markdownToPlainText > converts image syntax (link regex fires before image regex)
    // test asserts: !alt (https://img.png)  ← buggy output

 Test Files  1 passed (1)
      Tests  34 passed (34)

After (this PR, fixed behavior):

✓ src/send.test.ts (33 tests) 16ms

  ✓ markdownToPlainText > converts image syntax to alt text
    // test asserts: [alt]  ← correct output

 Test Files  1 passed (1)
      Tests  33 passed (33)

All tests pass in both configurations. The link conversion test continues to pass unchanged.

中文说明

代码审查

独立方案(读 diff 之前):bug 是 ![alt](url) 被链接正则 [text](url) → text (url) 部分消费,导致图片正则 ![alt](url) → [alt] 无法匹配。修复方法是调换顺序:图片先,链接后。更具体的正则(带 ! 前缀)应先消费其 token。

Diff 对比:PR 正是这样做的——干净的两行交换。我的方案与 PR 完全一致。

无正确性 bug、无安全问题、无回归。现有链接测试(click here (https://example.com))仍然通过,确认普通链接不受影响。图片正则 !\[([^\]]*)\]\([^)]+\)[$1] 正确消费完整图片 token,链接正则永远不会看到它。

考虑的边界情况:

  • 无前导 ![text](url) → 链接正则照常执行 ✓
  • ![alt](url) → 图片正则先执行,产生 [alt],链接正则不匹配 [alt](无 (url))✓
  • 嵌套 [![alt](img)](link) → PR 中已说明为既有局限,不在范围内 ✓

测试

这是一个纯字符串转换(markdown → 纯文本),无 TUI 界面。验证通过单元测试完成,这是正确的测试方式。

改动前(main,bug 行为): 34 个测试全部通过,图片测试断言 !alt (https://img.png) ← 错误输出

改动后(本 PR,修复行为): 33 个测试全部通过,图片测试断言 [alt] ← 正确输出

两种配置下所有测试均通过。链接转换测试不受影响。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Clean fix, ships exactly what it promises.

Going back to my independent proposal from Stage 2: the PR and I arrived at the same solution — reorder the two regex lines. There's no simpler path, and the PR doesn't add anything unnecessary.

This solves a real problem: WeChat users receiving malformed image output (!alt (url) instead of [alt]). The code change is two lines, the test is updated to assert the correct behavior, and nothing else is touched. Every edit in the diff is necessary for the stated goal.

The unit tests are the right verification for a pure string-transformation function — they confirm both the fix and that normal links remain unaffected. The PR author also correctly identified and disclosed the pre-existing nested-image limitation as out of scope.

If I had to maintain this in six months, I'd thank the author — the fix is self-explanatory and the test documents the expected behavior clearly.

Approving. ✅

中文说明

干净的修复,完全实现了承诺的效果。

回顾我在第二阶段的独立方案:PR 和我得出了相同的解决方案——调换两行正则顺序。没有更简单的路径,PR 也没有添加任何不必要的东西。

这解决了一个真实的问题:微信用户收到格式错误的图片输出(!alt (url) 而非 [alt])。代码改动两行,测试更新为断言正确行为,没有触及任何其他内容。diff 中的每一处编辑都是实现目标所必需的。

单元测试是纯字符串转换函数的正确验证方式——同时确认了修复有效且普通链接不受影响。PR 作者也正确识别并说明了嵌套图片的既有局限不在范围内。

如果六个月后需要维护这段代码,我会感谢作者——修复不言自明,测试清晰记录了预期行为。

批准 ✅

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, looks ready to ship. ✅

@wenshao
wenshao merged commit a45b14f into QwenLM:main Jun 18, 2026
53 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants