@acedatacloud/skills 2026.719.1 → 2026.719.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acedatacloud/skills",
3
- "version": "2026.719.1",
3
+ "version": "2026.719.3",
4
4
  "description": "Agent Skills for AceDataCloud AI services — music, image, video generation, LLM chat, web search. Compatible with Claude Code, GitHub Copilot, Gemini CLI, OpenAI Codex, and 30+ AI coding agents.",
5
5
  "keywords": [
6
6
  "agent-skills",
@@ -62,7 +62,7 @@ models include:
62
62
  | Gemini | `gemini-3.1-pro`, `gemini-3.1-pro-preview`, `gemini-3.1-flash-image-preview`, `gemini-3.1-flash-lite-preview`, `gemini-3-pro-preview`, `gemini-2.5-flash-lite`, `gemini-2.0-flash-lite` |
63
63
  | Grok | `grok-4`, `grok-4-0709`, `grok-3`, `grok-3-fast` |
64
64
  | DeepSeek | `deepseek-r1`, `deepseek-r1-0528`, `deepseek-v3`, `deepseek-v3-250324`, `deepseek-v3.2-exp`, `deepseek-v4-flash` |
65
- | Kimi | `kimi-k2.5`, `kimi-k2-thinking-turbo`, `kimi-k2-thinking`, `kimi-k2-instruct-0905`, `kimi-k2-0905-preview`, `kimi-k2-turbo-preview`, `kimi-k2-0711-preview` |
65
+ | Kimi | `kimi-k3`, `kimi-k2.6`, `kimi-k2.5`, `kimi-k2-thinking-turbo`, `kimi-k2-thinking`, `kimi-k2-instruct-0905`, `kimi-k2-0905-preview`, `kimi-k2-turbo-preview`, `kimi-k2-0711-preview` |
66
66
  | GLM | `glm-5.2`, `glm-5.1`, `glm-5`, `glm-5-turbo`, `glm-4.7`, `glm-4.6`, `glm-4.5`, `glm-4.5v`, `glm-3-turbo` |
67
67
 
68
68
  `/aichat2/conversations` also accepts `model_group` values
@@ -34,7 +34,7 @@ Pass the final semantic snapshot to `parse-note-snapshot --note-url <canonical-u
34
34
 
35
35
  Open the fresh author link from a result/detail page. Return visible profile text, followers/following/engagement totals, and bounded recent notes. Do not expose unrelated private account data.
36
36
 
37
- Pass the final semantic snapshot to `parse-profile-snapshot --profile-url <canonical-url>` before reporting structured profile data. Report only explicitly labeled profile metrics; never reinterpret unrelated page numbers as followers, following, likes, or favorites.
37
+ Pass the final semantic snapshot to `parse-profile-snapshot --profile-url <canonical-url>` before reporting structured profile data. `visible_metrics` contains explicitly labeled profile metrics. Preserve `visible_counts` separately as unlabeled page counters; never reinterpret them as followers, following, likes, or favorites.
38
38
 
39
39
  ## Content planning
40
40
 
@@ -27,6 +27,7 @@ PROFILE_PATH = re.compile(r"^/user/profile/([A-Za-z0-9]+)$")
27
27
  TITLE_UNIT = re.compile(r"[\u3400-\u9fff]|[A-Za-z0-9]+")
28
28
  COUNT_VALUE = re.compile(r"^(\d+(?:\.\d+)?)([万千]?)$")
29
29
  PROFILE_METRIC = re.compile(r"^(关注|粉丝|获赞与收藏|获赞|收藏)\s*([\d,.]+(?:万|千|w|W|k|K)?)$")
30
+ ENGAGEMENT_METRIC = re.compile(r"^(赞|点赞|收藏|评论|分享)\s*[::]?\s*([\d,.]+(?:万|千|w|W|k|K)?)$")
30
31
 
31
32
 
32
33
  class ContractError(ValueError):
@@ -239,7 +240,9 @@ def _visible_counts(nodes: List[Dict]) -> List[str]:
239
240
  values = []
240
241
  for node in nodes:
241
242
  name = _named_text(node)
242
- if node.get("role") in {"button", "section"} and COUNT_VALUE.fullmatch(name):
243
+ if node.get("role") in {"button", "section"} and (
244
+ COUNT_VALUE.fullmatch(name) or ENGAGEMENT_METRIC.fullmatch(name)
245
+ ):
243
246
  values.append(name)
244
247
  return values
245
248
 
@@ -292,10 +295,13 @@ def parse_profile_snapshot(snapshot: dict, profile_url: str) -> dict:
292
295
  notes = []
293
296
  seen = set()
294
297
  metrics = []
298
+ counts = []
295
299
  for node in nodes:
296
300
  name = _named_text(node)
297
301
  if node.get("role") == "section" and PROFILE_METRIC.fullmatch(name):
298
302
  metrics.append(name)
303
+ elif node.get("role") in {"button", "section"} and COUNT_VALUE.fullmatch(name):
304
+ counts.append(name)
299
305
  note = _path_match(node.get("href"), NOTE_PATH)
300
306
  if note and name and note.group(1) not in seen:
301
307
  seen.add(note.group(1))
@@ -305,6 +311,7 @@ def parse_profile_snapshot(snapshot: dict, profile_url: str) -> dict:
305
311
  "canonical_url": canonical_url,
306
312
  "display_name": _first_named(nodes, {"heading"}),
307
313
  "visible_metrics": metrics,
314
+ "visible_counts": counts,
308
315
  "notes": notes,
309
316
  "truncated": bool(snapshot.get("truncated", False)),
310
317
  }
@@ -4,8 +4,9 @@
4
4
  {"role": "heading", "name": "示例笔记"},
5
5
  {"role": "link", "name": "示例作者", "href": "https://www.xiaohongshu.com/user/profile/user123"},
6
6
  {"role": "article", "name": "这是脱敏后的示例正文"},
7
- {"role": "button", "name": "128"},
7
+ {"role": "button", "name": "赞:128"},
8
8
  {"role": "comment", "name": "示例评论"},
9
+ {"role": "link", "name": "赞过的人", "href": "https://www.xiaohongshu.com/user/profile/liker999"},
9
10
  {"role": "link", "name": "评论者", "href": "https://www.xiaohongshu.com/user/profile/commenter456"}
10
11
  ],
11
12
  "truncated": false
@@ -202,7 +202,7 @@ def test_parse_note_fixture() -> None:
202
202
  assert result["title"] == "示例笔记"
203
203
  assert result["author_state"] == "available"
204
204
  assert result["profile_url"] == "https://www.xiaohongshu.com/user/profile/user123"
205
- assert result["visible_engagement"] == ["128"]
205
+ assert result["visible_engagement"] == ["赞:128"]
206
206
  assert result["comments"] == ["示例评论"]
207
207
 
208
208
 
@@ -228,6 +228,32 @@ def test_parse_note_ignores_generic_list_items_before_author() -> None:
228
228
  assert result["comments"] == []
229
229
 
230
230
 
231
+ def test_parse_note_refuses_to_guess_between_pre_comment_profiles() -> None:
232
+ snapshot = {
233
+ "origin": "https://www.xiaohongshu.com",
234
+ "nodes": [
235
+ {"role": "heading", "name": "示例笔记"},
236
+ {
237
+ "role": "link",
238
+ "name": "赞过的人",
239
+ "href": "https://www.xiaohongshu.com/user/profile/liker999",
240
+ },
241
+ {
242
+ "role": "link",
243
+ "name": "示例作者",
244
+ "href": "https://www.xiaohongshu.com/user/profile/user123",
245
+ },
246
+ ],
247
+ }
248
+
249
+ result = xhs_contract.parse_note_snapshot(
250
+ snapshot, "https://www.xiaohongshu.com/explore/abc123"
251
+ )
252
+
253
+ assert result["author"] is None
254
+ assert result["author_state"] == "ambiguous"
255
+
256
+
231
257
  def test_parse_profile_fixture() -> None:
232
258
  fixture = Path(__file__).parent / "fixtures" / "profile.json"
233
259
  result = xhs_contract.parse_profile_snapshot(
@@ -238,6 +264,7 @@ def test_parse_profile_fixture() -> None:
238
264
  assert result["canonical_url"] == "https://www.xiaohongshu.com/user/profile/user123"
239
265
  assert result["display_name"] == "示例作者"
240
266
  assert result["visible_metrics"] == ["粉丝 128"]
267
+ assert result["visible_counts"] == ["999", "5678"]
241
268
  assert result["notes"] == [
242
269
  {"note_id": "abc123", "title": "最近笔记", "url": "https://www.xiaohongshu.com/explore/abc123"}
243
270
  ]