Agno now exposes advance_code_fence in agno.utils.markdown, the same fence tracking Agno's page chunker uses. A sync_pages transform sees every line of a page, code included, and advance_code_fence lets it change the prose and leave code examples exactly as written.
Feed advance_code_fence one line at a time with the state it returned for the previous line. This transform decodes HTML entities in prose and skips anything inside a fence. Agno calls a transform with the page's text and the page's path as a keyword argument, so the function accepts both.
import html
from agno.utils.markdown import advance_code_fence
def normalize_prose(markdown: str, path: str = "") -> str:
output, fence = [], None
for line in markdown.splitlines(keepends=True):
was_code = fence is not None
fence, delimiter = advance_code_fence(line.rstrip("\r\n"), fence)
output.append(line if was_code or delimiter else html.unescape(line))
return "".join(output)
report = knowledge.sync_pages(url="https://docs.agno.com/llms.txt", transform=normalize_prose)Given Use <Agent> here., a Python block containing x = a < b, and Done & dusted., normalize_prose returns Use <Agent> here. and Done & dusted. and leaves the code line as x = a < b.
In advance_code_fence, a closing fence has to use the same character as the opening fence and be at least as long. A four-backtick block can therefore show a three-backtick example safely. advance_code_fence also counts fences indented inside Markdown or MDX.
See the cookbook, and learn more about transforming published Markdown in the documentation.
Frequently asked questions
Import advance_code_fence from agno.utils.markdown and call it once per line with the state it returned for the previous line. Leave a line unchanged when it sits inside a fence or is a fence delimiter.
Yes. A closing fence has to use the same character as the opening fence and be at least as long, so a four-backtick block can safely show a three-backtick example.
advance_code_fence counts fences indented inside Markdown or MDX.
