feat: add inference.sh integration (infsh tool + skill) (#1682)

Add inference.sh CLI (infsh) as a tool integration, giving agents
access to 150+ AI apps through a single CLI — image gen (FLUX, Reve,
Seedream), video (Veo, Wan, Seedance), LLMs, search (Tavily, Exa),
3D, avatar/lipsync, and more. One API key manages all services.

Tools:
- infsh: run any infsh CLI command (app list, app run, etc.)
- infsh_install: install the CLI if not present

Registered as an 'inference' toolset (opt-in, not in core tools).
Includes comprehensive skill docs with examples for all app categories.

Changes from original PR:
- NOT added to _HERMES_CORE_TOOLS (available via --toolsets inference)
- Added 12 tests covering tool registration, command execution,
  error handling, timeout, JSON parsing, and install flow

Inspired by PR #1021 by @okaris.

Co-authored-by: okaris <okaris@users.noreply.github.com>
This commit is contained in:
Teknium
2026-03-17 02:59:21 -07:00
committed by GitHub
parent d9a7b83ae3
commit 6020db0243
10 changed files with 1264 additions and 0 deletions

View File

@@ -0,0 +1,114 @@
"""Tests for tools/infsh_tool.py — inference.sh CLI integration."""
import json
import subprocess
from unittest.mock import patch, MagicMock
import pytest
from tools.infsh_tool import (
check_infsh_requirements,
infsh_tool,
infsh_install,
)
class TestCheckRequirements:
def test_returns_bool(self):
result = check_infsh_requirements()
assert isinstance(result, bool)
def test_returns_true_when_infsh_on_path(self, monkeypatch):
monkeypatch.setattr("shutil.which", lambda cmd: "/usr/local/bin/infsh" if cmd == "infsh" else None)
assert check_infsh_requirements() is True
def test_returns_false_when_missing(self, monkeypatch):
monkeypatch.setattr("shutil.which", lambda cmd: None)
assert check_infsh_requirements() is False
class TestInfshTool:
def test_not_installed_returns_error(self, monkeypatch):
monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: False)
result = json.loads(infsh_tool("app list"))
assert result["success"] is False
assert "not installed" in result["error"].lower()
def test_successful_command(self, monkeypatch):
monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: True)
mock_result = MagicMock()
mock_result.returncode = 0
mock_result.stdout = '{"apps": ["flux", "veo"]}'
mock_result.stderr = ""
with patch("subprocess.run", return_value=mock_result) as mock_run:
result = json.loads(infsh_tool("app list --search flux"))
assert result["success"] is True
mock_run.assert_called_once()
call_cmd = mock_run.call_args[0][0]
assert "infsh app list --search flux" in call_cmd
def test_failed_command(self, monkeypatch):
monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: True)
mock_result = MagicMock()
mock_result.returncode = 1
mock_result.stdout = ""
mock_result.stderr = "unknown command"
with patch("subprocess.run", return_value=mock_result):
result = json.loads(infsh_tool("badcommand"))
assert result["success"] is False
assert result["exit_code"] == 1
def test_timeout_handled(self, monkeypatch):
monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: True)
with patch("subprocess.run", side_effect=subprocess.TimeoutExpired("infsh", 300)):
result = json.loads(infsh_tool("app run something", timeout=300))
assert result["success"] is False
assert "timed out" in result["error"].lower()
def test_json_output_parsed(self, monkeypatch):
monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: True)
mock_result = MagicMock()
mock_result.returncode = 0
mock_result.stdout = '{"url": "https://example.com/image.png"}'
mock_result.stderr = ""
with patch("subprocess.run", return_value=mock_result):
result = json.loads(infsh_tool("app run flux --json"))
assert result["success"] is True
assert isinstance(result["output"], dict)
assert result["output"]["url"] == "https://example.com/image.png"
class TestInfshInstall:
def test_already_installed(self, monkeypatch):
monkeypatch.setattr("tools.infsh_tool.check_infsh_requirements", lambda: True)
monkeypatch.setattr("tools.infsh_tool._get_infsh_path", lambda: "/usr/local/bin/infsh")
mock_result = MagicMock()
mock_result.returncode = 0
mock_result.stdout = "infsh v1.2.3"
with patch("subprocess.run", return_value=mock_result):
result = json.loads(infsh_install())
assert result["success"] is True
assert result["already_installed"] is True
class TestToolRegistration:
def test_tools_registered(self):
from tools.registry import registry
assert "infsh" in registry._tools
assert "infsh_install" in registry._tools
def test_infsh_in_inference_toolset(self):
from toolsets import TOOLSETS
assert "inference" in TOOLSETS
assert "infsh" in TOOLSETS["inference"]["tools"]
assert "infsh_install" in TOOLSETS["inference"]["tools"]
def test_infsh_not_in_core_tools(self):
from toolsets import _HERMES_CORE_TOOLS
assert "infsh" not in _HERMES_CORE_TOOLS
assert "infsh_install" not in _HERMES_CORE_TOOLS