mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-04-25 11:18:22 +00:00
fix(skills): support parsing multiline YAML strings in SKILL.md frontmatter (#1703)
* fix(skills): support parsing multiline YAML strings in SKILL.md frontmatter * test(skills): add tests for multiline YAML descriptions
This commit is contained in:
parent
68d44f6755
commit
e97c8c9943
@ -33,15 +33,72 @@ def parse_skill_file(skill_file: Path, category: str, relative_path: Path | None
|
||||
|
||||
front_matter = front_matter_match.group(1)
|
||||
|
||||
# Parse YAML front matter (simple key-value parsing)
|
||||
# Parse YAML front matter with basic multiline string support
|
||||
metadata = {}
|
||||
for line in front_matter.split("\n"):
|
||||
line = line.strip()
|
||||
if not line:
|
||||
lines = front_matter.split("\n")
|
||||
current_key = None
|
||||
current_value = []
|
||||
is_multiline = False
|
||||
multiline_style = None
|
||||
indent_level = None
|
||||
|
||||
for line in lines:
|
||||
if is_multiline:
|
||||
if not line.strip():
|
||||
current_value.append("")
|
||||
continue
|
||||
|
||||
current_indent = len(line) - len(line.lstrip())
|
||||
|
||||
if indent_level is None:
|
||||
if current_indent > 0:
|
||||
indent_level = current_indent
|
||||
current_value.append(line[indent_level:])
|
||||
continue
|
||||
elif current_indent >= indent_level:
|
||||
current_value.append(line[indent_level:])
|
||||
continue
|
||||
|
||||
# If we reach here, it's either a new key or the end of multiline
|
||||
if current_key and is_multiline:
|
||||
if multiline_style == "|":
|
||||
metadata[current_key] = "\n".join(current_value).rstrip()
|
||||
else:
|
||||
text = "\n".join(current_value).rstrip()
|
||||
# Replace single newlines with spaces for folded blocks
|
||||
metadata[current_key] = re.sub(r"(?<!\n)\n(?!\n)", " ", text)
|
||||
|
||||
current_key = None
|
||||
current_value = []
|
||||
is_multiline = False
|
||||
multiline_style = None
|
||||
indent_level = None
|
||||
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
if ":" in line:
|
||||
# Handle nested dicts simply by ignoring indentation for now,
|
||||
# or just extracting top-level keys
|
||||
key, value = line.split(":", 1)
|
||||
metadata[key.strip()] = value.strip()
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
|
||||
if value in (">", "|"):
|
||||
current_key = key
|
||||
is_multiline = True
|
||||
multiline_style = value
|
||||
current_value = []
|
||||
indent_level = None
|
||||
else:
|
||||
metadata[key] = value
|
||||
|
||||
if current_key and is_multiline:
|
||||
if multiline_style == "|":
|
||||
metadata[current_key] = "\n".join(current_value).rstrip()
|
||||
else:
|
||||
text = "\n".join(current_value).rstrip()
|
||||
metadata[current_key] = re.sub(r"(?<!\n)\n(?!\n)", " ", text)
|
||||
|
||||
# Extract required fields
|
||||
name = metadata.get("name")
|
||||
|
||||
@ -93,6 +93,27 @@ class TestParseSkillFile:
|
||||
assert result is not None
|
||||
assert result.description == "A skill: does things"
|
||||
|
||||
def test_multiline_yaml_folded_description(self, tmp_path):
|
||||
skill_file = _write_skill(
|
||||
tmp_path,
|
||||
"---\nname: multiline-skill\ndescription: >\n This is a multiline\n description for a skill.\n\n It spans multiple lines.\nlicense: MIT\n---\n\nBody\n",
|
||||
)
|
||||
result = parse_skill_file(skill_file, "public")
|
||||
assert result is not None
|
||||
assert result.name == "multiline-skill"
|
||||
assert result.description == "This is a multiline description for a skill.\n\nIt spans multiple lines."
|
||||
assert result.license == "MIT"
|
||||
|
||||
def test_multiline_yaml_literal_description(self, tmp_path):
|
||||
skill_file = _write_skill(
|
||||
tmp_path,
|
||||
"---\nname: pipe-skill\ndescription: |\n First line.\n Second line.\n---\n\nBody\n",
|
||||
)
|
||||
result = parse_skill_file(skill_file, "public")
|
||||
assert result is not None
|
||||
assert result.name == "pipe-skill"
|
||||
assert result.description == "First line.\nSecond line."
|
||||
|
||||
def test_empty_front_matter_returns_none(self, tmp_path):
|
||||
skill_file = _write_skill(tmp_path, "---\n\n---\n\nBody\n")
|
||||
assert parse_skill_file(skill_file, "public") is None
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user