Skip to content
Changelog

Toolkit fixes for CSV, Shell, Workspace, Gemini, GitHub, Local File System and PubMed

October 1, 20262 min read

Agno's CsvTools, ShellTools, Workspace, GeminiTools, GithubTools, LocalFileSystemTools and PubmedTools each got a fix for a value they dropped, ignored or mangled. The outputs below come from running the previous release and this one, with the Gemini, GitHub and PubMed APIs stubbed.

CsvTools honors row_limit=0

CsvTools.read_csv_file picked its limit with row_limit or self.row_limit, so an explicit 0 fell back to the toolkit's limit, or to every row when none was set. A 0 now returns no rows, and an omitted row_limit still uses the toolkit's setting:

CsvTools(csvs=[path]).read_csv_file("people", row_limit=0)
# Before: '[{"name": "Alice", "city": "Paris"}, {"name": "Bob", "city": "Rome"}]'
# Now:    '[]'

ShellTools and Workspace honor tail=0

ShellTools.run_shell_command, agno.utils.shell.run_shell_command, and Workspace.run_command and arun_command cut output with [-tail:]. For tail=0 that slice is [-0:], the whole list. All four now return an empty string, on success and on the Workspace error path:

ShellTools().run_shell_command(["printf", "one\\ntwo\\nthree"], tail=0)
# Before: 'one\ntwo\nthree'
# Now:    ''

GeminiTools.generate_video returns raw video bytes

generate_video built its Video artifact from the base64 text of the video. Video.content holds raw bytes, so anything that saved or uploaded the artifact got base64 text. It now stores the bytes from Gemini, like generate_image and the other video toolkits do, and the cookbook writes video.content straight to a file. The first bytes of video.content for an MP4:

Before: b'AAAAGGZ0eXBtcDQy'
Now:    b'\x00\x00\x00\x18ftypmp42'

GithubTools.create_branch built its link by rewriting the Git ref API URL, which only works on github.com. It now builds the link from the repository's web URL and escapes the branch name. get_file_content counted every non-ASCII character as a binary signal, so a file with more than 200 of them in its first 1000 characters came back as a binary file. It now counts only NUL and control characters. For a GitHub Enterprise repository and a Chinese README:

Before: url     https://github.example.com/api/v3/repos/acme/app/tree/feature/agent
        content 'Binary file (content not displayed)'
Now:    url     https://github.example.com/acme/app/tree/feature/agent
        content '# 智能体平台\n\n这是一个用于构建、运行和管理智...'

LocalFileSystemTools reads and writes UTF-8

LocalFileSystemTools.write_file and read_file used the locale's encoding. On a host set to cp1252, GBK or Latin-1, writing non-ASCII text failed and reading a UTF-8 file returned garbled text. Both now use UTF-8. Under a Latin-1 locale:

Before: write 'Grüße aus Zürich, 東京' -> Error: Failed to write file: 'latin-1' codec can't encode characters in position 18-19
        read  'café 東京'              -> 'café æ\x9d±äº¬'
Now:    write 'Grüße aus Zürich, 東京' -> Successfully wrote file to: .../note.txt
        read  'café 東京'              -> 'café 東京'

PubmedTools keeps metadata for short abstracts

PubmedTools.search_pubmed applied its 200-character check to the whole result string, so an abstract of 200 characters or fewer, or no abstract at all, came back without Title and Published. The check now only decides whether to cut the summary:

Before: Summary: We test tool calling in 1B to 8B models.
Now:    Title: Tool use in small language models
        Published: 2026-03-02
        Summary: We test tool calling in 1B to 8B models.

See the cookbook, and learn more about the CSV, Shell, Workspace, Gemini, GitHub, Local File System and PubMed toolkits in the documentation.

Frequently asked questions

get_file_content counted every non-ASCII character as a sign of binary data, so a file with more than 200 of them in its first 1000 characters came back as "Binary file (content not displayed)". It now counts only NUL and control characters, so UTF-8 text in any language comes through.

The tools sliced the output with [-tail:], and [-0:] is the whole list. ShellTools.run_shell_command, agno.utils.shell.run_shell_command, and Workspace.run_command and arun_command now return an empty string for tail=0.

The returned Video artifact held the base64 text of the video, so saving video.content wrote text to disk. generate_video now stores the raw bytes, so video.content can be written straight to an .mp4 file.

write_file and read_file used the locale encoding, such as cp1252 or GBK. Both now use UTF-8 on every platform.

Shipped around the same time