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:
parent
d9a7b83ae3
commit
6020db0243
10 changed files with 1264 additions and 0 deletions
|
|
@ -95,6 +95,7 @@ def _discover_tools():
|
||||||
"tools.send_message_tool",
|
"tools.send_message_tool",
|
||||||
"tools.honcho_tools",
|
"tools.honcho_tools",
|
||||||
"tools.homeassistant_tool",
|
"tools.homeassistant_tool",
|
||||||
|
"tools.infsh_tool",
|
||||||
]
|
]
|
||||||
import importlib
|
import importlib
|
||||||
for mod_name in _modules:
|
for mod_name in _modules:
|
||||||
|
|
|
||||||
23
skills/inference-sh/DESCRIPTION.md
Normal file
23
skills/inference-sh/DESCRIPTION.md
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# inference.sh
|
||||||
|
|
||||||
|
Run 150+ AI applications in the cloud via the [inference.sh](https://inference.sh) platform.
|
||||||
|
|
||||||
|
**One API key for everything** - Access all AI services with a single account. No need to manage separate API keys for FLUX, Veo, Claude, Tavily, Twitter, etc. You can also bring your own keys if you prefer.
|
||||||
|
|
||||||
|
## Available Skills
|
||||||
|
|
||||||
|
- **cli**: The inference.sh CLI (`infsh`) for running AI apps
|
||||||
|
|
||||||
|
## What's Included
|
||||||
|
|
||||||
|
- **Image Generation**: FLUX, Reve, Seedream, Grok Imagine, Gemini
|
||||||
|
- **Video Generation**: Veo, Wan, Seedance, OmniHuman, HunyuanVideo
|
||||||
|
- **LLMs**: Claude, Gemini, Kimi, GLM-4 (via OpenRouter)
|
||||||
|
- **Search**: Tavily, Exa
|
||||||
|
- **3D**: Rodin
|
||||||
|
- **Social**: Twitter/X automation
|
||||||
|
- **Audio**: TTS, voice cloning
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
This category provides the `infsh` and `infsh_install` tools in the `inference` toolset.
|
||||||
372
skills/inference-sh/cli/SKILL.md
Normal file
372
skills/inference-sh/cli/SKILL.md
Normal file
|
|
@ -0,0 +1,372 @@
|
||||||
|
---
|
||||||
|
name: inference-sh-cli
|
||||||
|
description: "Run 150+ AI apps via inference.sh CLI (infsh) - image generation, video creation, LLMs, search, 3D, Twitter automation. Models: FLUX, Veo, Gemini, Grok, Claude, Seedance, OmniHuman, Tavily, Exa, OpenRouter. Triggers: inference.sh, infsh, ai apps, serverless ai, flux, veo, image generation, video generation"
|
||||||
|
version: 1.0.0
|
||||||
|
author: inference.sh
|
||||||
|
license: MIT
|
||||||
|
metadata:
|
||||||
|
hermes:
|
||||||
|
tags: [AI, image-generation, video, LLM, search, inference, FLUX, Veo, Claude]
|
||||||
|
requires_tools: [infsh]
|
||||||
|
---
|
||||||
|
|
||||||
|
# inference.sh CLI
|
||||||
|
|
||||||
|
Run 150+ AI apps in the cloud with a simple CLI. No GPU required.
|
||||||
|
|
||||||
|
**One API key for everything** - Manage all AI services (FLUX, Veo, Claude, Tavily, X/Twitter, and more) with a single inference.sh account. No need to sign up for dozens of different providers. You can also bring your own API keys if you prefer.
|
||||||
|
|
||||||
|
## Tools
|
||||||
|
|
||||||
|
This skill is backed by the `infsh` and `infsh_install` tools:
|
||||||
|
|
||||||
|
- **infsh**: Run any infsh command (app list, app run, etc.)
|
||||||
|
- **infsh_install**: Install the CLI if not already present
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install (if needed)
|
||||||
|
infsh_install
|
||||||
|
|
||||||
|
# List available apps
|
||||||
|
infsh app list
|
||||||
|
|
||||||
|
# Search for apps
|
||||||
|
infsh app list --search flux
|
||||||
|
infsh app list --search video
|
||||||
|
|
||||||
|
# Run an app
|
||||||
|
infsh app run falai/flux-dev-lora --input '{"prompt": "a cat astronaut"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Local File Uploads
|
||||||
|
|
||||||
|
The CLI automatically uploads local files when you provide a file path instead of a URL:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Upscale a local image
|
||||||
|
infsh app run falai/topaz-image-upscaler --input '{"image": "/path/to/photo.jpg", "upscale_factor": 2}' --json
|
||||||
|
|
||||||
|
# Image-to-video from local file
|
||||||
|
infsh app run falai/wan-2-5-i2v --input '{"image": "/path/to/image.png", "prompt": "make it come alive"}' --json
|
||||||
|
|
||||||
|
# Video generation with local first frame
|
||||||
|
infsh app run bytedance/seedance-1-5-pro --input '{"prompt": "dancing figure", "image": "./first-frame.png"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Image Generation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Gemini 2.5 Flash Image (Google) - fast, high quality
|
||||||
|
infsh app run google/gemini-2-5-flash-image --input '{"prompt": "futuristic city", "num_images": 1}' --json
|
||||||
|
|
||||||
|
# Gemini 3 Pro Image Preview (Google) - latest model
|
||||||
|
infsh app run google/gemini-3-pro-image-preview --input '{"prompt": "photorealistic landscape"}' --json
|
||||||
|
|
||||||
|
# Gemini 3.1 Flash Image Preview (Google)
|
||||||
|
infsh app run google/gemini-3-1-flash-image-preview --input '{"prompt": "artistic portrait"}' --json
|
||||||
|
|
||||||
|
# FLUX Dev with LoRA support
|
||||||
|
infsh app run falai/flux-dev-lora --input '{"prompt": "sunset over mountains", "num_images": 1}' --json
|
||||||
|
|
||||||
|
# FLUX 2 Klein with LoRA
|
||||||
|
infsh app run falai/flux-2-klein-lora --input '{"prompt": "portrait photo"}' --json
|
||||||
|
|
||||||
|
# Reve - stylized generation and editing
|
||||||
|
infsh app run falai/reve --input '{"prompt": "cyberpunk city"}' --json
|
||||||
|
|
||||||
|
# Seedream 5 Lite - high-quality 2K-3K (ByteDance)
|
||||||
|
infsh app run bytedance/seedream-5-lite --input '{"prompt": "nature scene"}' --json
|
||||||
|
|
||||||
|
# Seedream 4.5 - 2K-4K images
|
||||||
|
infsh app run bytedance/seedream-4-5 --input '{"prompt": "detailed illustration"}' --json
|
||||||
|
|
||||||
|
# Seedream 3.0 - cinematic quality
|
||||||
|
infsh app run bytedance/seedream-3-0-t2i --input '{"prompt": "fantasy landscape"}' --json
|
||||||
|
|
||||||
|
# Grok Imagine - xAI image generation
|
||||||
|
infsh app run xai/grok-imagine-image --input '{"prompt": "abstract art"}' --json
|
||||||
|
|
||||||
|
# Grok Imagine Pro - higher quality
|
||||||
|
infsh app run xai/grok-imagine-image-pro --input '{"prompt": "photorealistic portrait"}' --json
|
||||||
|
|
||||||
|
# Qwen Image 2 Pro (Alibaba)
|
||||||
|
infsh app run alibaba/qwen-image-2-pro --input '{"prompt": "anime character"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Video Generation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Veo 3.1 Fast (Google)
|
||||||
|
infsh app run google/veo-3-1-fast --input '{"prompt": "drone shot of coastline"}' --json
|
||||||
|
|
||||||
|
# Veo 3.1 (higher quality)
|
||||||
|
infsh app run google/veo-3-1 --input '{"prompt": "cinematic scene"}' --json
|
||||||
|
|
||||||
|
# Veo 3 Fast
|
||||||
|
infsh app run google/veo-3-fast --input '{"prompt": "nature documentary shot"}' --json
|
||||||
|
|
||||||
|
# Veo 2
|
||||||
|
infsh app run google/veo-2 --input '{"prompt": "slow motion water splash"}' --json
|
||||||
|
|
||||||
|
# Grok Imagine Video - xAI
|
||||||
|
infsh app run xai/grok-imagine-video --input '{"prompt": "timelapse of clouds"}' --json
|
||||||
|
|
||||||
|
# Seedance 1.5 Pro - ByteDance
|
||||||
|
infsh app run bytedance/seedance-1-5-pro --input '{"prompt": "dancing figure", "resolution": "1080p"}' --json
|
||||||
|
|
||||||
|
# Seedance 1.0 Pro
|
||||||
|
infsh app run bytedance/seedance-1-0-pro --input '{"prompt": "walking through forest"}' --json
|
||||||
|
|
||||||
|
# Seedance 1.0 Pro Fast
|
||||||
|
infsh app run bytedance/seedance-1-0-pro-fast --input '{"prompt": "quick motion"}' --json
|
||||||
|
|
||||||
|
# Seedance 1.0 Lite - 720p lightweight
|
||||||
|
infsh app run bytedance/seedance-1-0-lite --input '{"prompt": "simple animation"}' --json
|
||||||
|
|
||||||
|
# Wan 2.5 - text-to-video
|
||||||
|
infsh app run falai/wan-2-5 --input '{"prompt": "person walking through city"}' --json
|
||||||
|
|
||||||
|
# Wan 2.5 Image-to-Video
|
||||||
|
infsh app run falai/wan-2-5-i2v --input '{"image": "/path/to/image.png", "prompt": "make it move naturally"}' --json
|
||||||
|
|
||||||
|
# LTX Video
|
||||||
|
infsh app run infsh/ltx-video --input '{"prompt": "realistic scene"}' --json
|
||||||
|
|
||||||
|
# Magi 1
|
||||||
|
infsh app run infsh/magi-1 --input '{"prompt": "creative video"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Avatar & Lipsync
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# OmniHuman 1.5 - multi-character audio-driven avatars
|
||||||
|
infsh app run bytedance/omnihuman-1-5 --input '{"audio": "/path/to/audio.mp3", "image": "/path/to/face.jpg"}' --json
|
||||||
|
|
||||||
|
# OmniHuman 1.0
|
||||||
|
infsh app run bytedance/omnihuman-1-0 --input '{"audio": "/path/to/speech.wav", "image": "/path/to/portrait.png"}' --json
|
||||||
|
|
||||||
|
# Fabric 1.0 - image animation
|
||||||
|
infsh app run falai/fabric-1-0 --input '{"image": "/path/to/photo.jpg"}' --json
|
||||||
|
|
||||||
|
# PixVerse Lipsync
|
||||||
|
infsh app run falai/pixverse-lipsync --input '{"audio": "/path/to/audio.mp3", "video": "/path/to/video.mp4"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Upscaling
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Topaz Image Upscaler - up to 4x
|
||||||
|
infsh app run falai/topaz-image-upscaler --input '{"image": "/path/to/photo.jpg", "upscale_factor": 2}' --json
|
||||||
|
|
||||||
|
# Topaz Video Upscaler
|
||||||
|
infsh app run falai/topaz-video-upscaler --input '{"video": "/path/to/video.mp4"}' --json
|
||||||
|
|
||||||
|
# Real-ESRGAN - image enhancement
|
||||||
|
infsh app run infsh/real-esrgan --input '{"image": "/path/to/image.jpg"}' --json
|
||||||
|
|
||||||
|
# Thera - upscale to any size
|
||||||
|
infsh app run infsh/thera --input '{"image": "/path/to/image.jpg"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## LLMs (via OpenRouter)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Claude Opus 4.6
|
||||||
|
infsh app run openrouter/claude-opus-46 --input '{"prompt": "Explain quantum computing"}' --json
|
||||||
|
|
||||||
|
# Claude Sonnet 4.5
|
||||||
|
infsh app run openrouter/claude-sonnet-45 --input '{"prompt": "Write a poem"}' --json
|
||||||
|
|
||||||
|
# Claude Haiku 4.5
|
||||||
|
infsh app run openrouter/claude-haiku-45 --input '{"prompt": "Quick question"}' --json
|
||||||
|
|
||||||
|
# Gemini 3 Pro Preview
|
||||||
|
infsh app run openrouter/gemini-3-pro-preview --input '{"prompt": "Analyze this"}' --json
|
||||||
|
|
||||||
|
# Kimi K2 Thinking
|
||||||
|
infsh app run openrouter/kimi-k2-thinking --input '{"prompt": "Solve this step by step"}' --json
|
||||||
|
|
||||||
|
# GLM 4.6
|
||||||
|
infsh app run openrouter/glm-46 --input '{"prompt": "Help me with"}' --json
|
||||||
|
|
||||||
|
# MiniMax M2.5
|
||||||
|
infsh app run openrouter/minimax-m-25 --input '{"prompt": "Creative writing"}' --json
|
||||||
|
|
||||||
|
# Intellect 3
|
||||||
|
infsh app run openrouter/intellect-3 --input '{"prompt": "Research question"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Web Search
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Tavily Search Assistant - comprehensive results
|
||||||
|
infsh app run tavily/search-assistant --input '{"query": "latest AI news", "include_answer": true}' --json
|
||||||
|
|
||||||
|
# Tavily Extract - get content from URLs
|
||||||
|
infsh app run tavily/extract --input '{"urls": ["https://example.com"]}' --json
|
||||||
|
|
||||||
|
# Exa Search - neural search
|
||||||
|
infsh app run exa/search --input '{"query": "machine learning tutorials"}' --json
|
||||||
|
|
||||||
|
# Exa Answer - LLM-powered answers
|
||||||
|
infsh app run exa/answer --input '{"query": "what is transformers architecture"}' --json
|
||||||
|
|
||||||
|
# Exa Extract - extract web content
|
||||||
|
infsh app run exa/extract --input '{"url": "https://example.com"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3D Generation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Rodin 3D Generator
|
||||||
|
infsh app run infsh/rodin-3d-generator --input '{"prompt": "a wooden chair"}' --json
|
||||||
|
|
||||||
|
# HunyuanImage to 3D
|
||||||
|
infsh app run infsh/hunyuan-image-to-3d-2 --input '{"image": "/path/to/object.png"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Text-to-Speech
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Kokoro TTS - lightweight
|
||||||
|
infsh app run falai/kokoro-tts --input '{"text": "Hello, this is a test."}' --json
|
||||||
|
|
||||||
|
# Dia TTS - realistic dialogue
|
||||||
|
infsh app run falai/dia-tts --input '{"text": "Two characters talking"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Twitter/X Automation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Post a tweet
|
||||||
|
infsh app run x/post-tweet --input '{"text": "Hello from AI!"}' --json
|
||||||
|
|
||||||
|
# Create post with media
|
||||||
|
infsh app run x/post-create --input '{"text": "Check this out", "media": "/path/to/image.jpg"}' --json
|
||||||
|
|
||||||
|
# Send DM
|
||||||
|
infsh app run x/dm-send --input '{"recipient_id": "123456", "text": "Hi there!"}' --json
|
||||||
|
|
||||||
|
# Follow user
|
||||||
|
infsh app run x/user-follow --input '{"user_id": "123456"}' --json
|
||||||
|
|
||||||
|
# Like a post
|
||||||
|
infsh app run x/post-like --input '{"post_id": "123456789"}' --json
|
||||||
|
|
||||||
|
# Retweet
|
||||||
|
infsh app run x/post-retweet --input '{"post_id": "123456789"}' --json
|
||||||
|
|
||||||
|
# Get user profile
|
||||||
|
infsh app run x/user-get --input '{"username": "elonmusk"}' --json
|
||||||
|
|
||||||
|
# Get post
|
||||||
|
infsh app run x/post-get --input '{"post_id": "123456789"}' --json
|
||||||
|
|
||||||
|
# Delete post
|
||||||
|
infsh app run x/post-delete --input '{"post_id": "123456789"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Utilities
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Browser automation
|
||||||
|
infsh app run infsh/agent-browser --function open --session new --input '{"url": "https://example.com"}' --json
|
||||||
|
|
||||||
|
# Media merger - combine videos/images
|
||||||
|
infsh app run infsh/media-merger --input '{"files": ["/path/to/video1.mp4", "/path/to/video2.mp4"]}' --json
|
||||||
|
|
||||||
|
# Video audio extractor
|
||||||
|
infsh app run infsh/video-audio-extractor --input '{"video": "/path/to/video.mp4"}' --json
|
||||||
|
|
||||||
|
# Video audio merger
|
||||||
|
infsh app run infsh/video-audio-merger --input '{"video": "/path/to/video.mp4", "audio": "/path/to/audio.mp3"}' --json
|
||||||
|
|
||||||
|
# Caption videos
|
||||||
|
infsh app run infsh/caption-videos --input '{"video": "/path/to/video.mp4"}' --json
|
||||||
|
|
||||||
|
# Stitch images
|
||||||
|
infsh app run infsh/stitch-images --input '{"images": ["/path/to/1.jpg", "/path/to/2.jpg"]}' --json
|
||||||
|
|
||||||
|
# Python executor
|
||||||
|
infsh app run infsh/python-executor --input '{"code": "print(2+2)"}' --json
|
||||||
|
|
||||||
|
# HTML to image
|
||||||
|
infsh app run infsh/html-to-image --input '{"html": "<h1>Hello</h1>"}' --json
|
||||||
|
|
||||||
|
# NSFW detection
|
||||||
|
infsh app run infsh/falconsai-nsfw-detection --input '{"image": "/path/to/image.jpg"}' --json
|
||||||
|
|
||||||
|
# Media analyzer
|
||||||
|
infsh app run infsh/media-analyzer --input '{"file": "/path/to/media.jpg"}' --json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Patterns
|
||||||
|
|
||||||
|
### Generate + Upscale Pipeline
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate image, capture URL, then upscale
|
||||||
|
infsh app run falai/flux-dev-lora --input '{"prompt": "portrait photo"}' --json --save result.json
|
||||||
|
|
||||||
|
# Extract URL and upscale (using jq)
|
||||||
|
IMG=$(cat result.json | jq -r '.images[0].url')
|
||||||
|
infsh app run falai/topaz-image-upscaler --input "{\"image\": \"$IMG\", \"upscale_factor\": 2}" --json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get App Schema
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# See what inputs an app accepts
|
||||||
|
infsh app get falai/flux-dev-lora
|
||||||
|
|
||||||
|
# Generate sample input
|
||||||
|
infsh app sample falai/flux-dev-lora
|
||||||
|
|
||||||
|
# Save sample to file, edit, then run
|
||||||
|
infsh app sample falai/flux-dev-lora --save input.json
|
||||||
|
# edit input.json...
|
||||||
|
infsh app run falai/flux-dev-lora --input input.json --json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Long-running Tasks
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start without waiting
|
||||||
|
infsh app run google/veo-3-1 --input '{"prompt": "..."}' --no-wait
|
||||||
|
|
||||||
|
# Check status later
|
||||||
|
infsh task get <task-id>
|
||||||
|
|
||||||
|
# Save result when done
|
||||||
|
infsh task get <task-id> --save result.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Categories
|
||||||
|
|
||||||
|
| Category | Apps |
|
||||||
|
|----------|------|
|
||||||
|
| **Image** | google/nano-banana, google/nano-banana-pro, google/nano-banana-2, falai/flux-dev-lora, bytedance/seedream-5-lite, falai/reve, xai/grok-imagine-image |
|
||||||
|
| **Video** | google/veo-*, xai/grok-imagine-video, bytedance/seedance-*, falai/wan-2-5*, infsh/ltx-video, infsh/magi-1 |
|
||||||
|
| **Avatar** | bytedance/omnihuman-*, falai/fabric-1-0, falai/pixverse-lipsync |
|
||||||
|
| **Upscale** | falai/topaz-image-upscaler, falai/topaz-video-upscaler, infsh/real-esrgan, infsh/thera |
|
||||||
|
| **LLMs** | openrouter/claude-*, openrouter/gemini-*, openrouter/kimi-*, openrouter/glm-* |
|
||||||
|
| **Search** | tavily/search-assistant, tavily/extract, exa/search, exa/answer, exa/extract |
|
||||||
|
| **3D** | infsh/rodin-3d-generator, infsh/hunyuan-image-to-3d-2 |
|
||||||
|
| **TTS** | falai/kokoro-tts, falai/dia-tts |
|
||||||
|
| **Social** | x/post-tweet, x/post-create, x/dm-send, x/user-follow, x/post-like, x/post-retweet |
|
||||||
|
| **Utils** | infsh/agent-browser, infsh/media-merger, infsh/caption-videos, infsh/stitch-images |
|
||||||
|
|
||||||
|
## Reference Files
|
||||||
|
|
||||||
|
- [Authentication & Setup](references/authentication.md)
|
||||||
|
- [Discovering Apps](references/app-discovery.md)
|
||||||
|
- [Running Apps](references/running-apps.md)
|
||||||
|
- [CLI Reference](references/cli-reference.md)
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [inference.sh Docs](https://inference.sh/docs)
|
||||||
|
- [CLI Setup Guide](https://inference.sh/docs/extend/cli-setup)
|
||||||
|
- [Apps Overview](https://inference.sh/docs/apps/overview)
|
||||||
112
skills/inference-sh/cli/references/app-discovery.md
Normal file
112
skills/inference-sh/cli/references/app-discovery.md
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
# Discovering Apps
|
||||||
|
|
||||||
|
## List All Apps
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app list
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pagination
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app list --page 2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Filter by Category
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app list --category image
|
||||||
|
infsh app list --category video
|
||||||
|
infsh app list --category audio
|
||||||
|
infsh app list --category text
|
||||||
|
infsh app list --category other
|
||||||
|
```
|
||||||
|
|
||||||
|
## Search
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app search "flux"
|
||||||
|
infsh app search "video generation"
|
||||||
|
infsh app search "tts" -l
|
||||||
|
infsh app search "image" --category image
|
||||||
|
```
|
||||||
|
|
||||||
|
Or use the flag form:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app list --search "flux"
|
||||||
|
infsh app list --search "video generation"
|
||||||
|
infsh app list --search "tts"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Featured Apps
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app list --featured
|
||||||
|
```
|
||||||
|
|
||||||
|
## Newest First
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app list --new
|
||||||
|
```
|
||||||
|
|
||||||
|
## Detailed View
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app list -l
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows table with app name, category, description, and featured status.
|
||||||
|
|
||||||
|
## Save to File
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app list --save apps.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Your Apps
|
||||||
|
|
||||||
|
List apps you've deployed:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app my
|
||||||
|
infsh app my -l # detailed
|
||||||
|
```
|
||||||
|
|
||||||
|
## Get App Details
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app get falai/flux-dev-lora
|
||||||
|
infsh app get falai/flux-dev-lora --json
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows full app info including input/output schema.
|
||||||
|
|
||||||
|
## Popular Apps by Category
|
||||||
|
|
||||||
|
### Image Generation
|
||||||
|
- `falai/flux-dev-lora` - FLUX.2 Dev (high quality)
|
||||||
|
- `falai/flux-2-klein-lora` - FLUX.2 Klein (fastest)
|
||||||
|
- `infsh/sdxl` - Stable Diffusion XL
|
||||||
|
- `google/gemini-3-pro-image-preview` - Gemini 3 Pro
|
||||||
|
- `xai/grok-imagine-image` - Grok image generation
|
||||||
|
|
||||||
|
### Video Generation
|
||||||
|
- `google/veo-3-1-fast` - Veo 3.1 Fast
|
||||||
|
- `google/veo-3` - Veo 3
|
||||||
|
- `bytedance/seedance-1-5-pro` - Seedance 1.5 Pro
|
||||||
|
- `infsh/ltx-video-2` - LTX Video 2 (with audio)
|
||||||
|
- `bytedance/omnihuman-1-5` - OmniHuman avatar
|
||||||
|
|
||||||
|
### Audio
|
||||||
|
- `infsh/dia-tts` - Conversational TTS
|
||||||
|
- `infsh/kokoro-tts` - Kokoro TTS
|
||||||
|
- `infsh/fast-whisper-large-v3` - Fast transcription
|
||||||
|
- `infsh/diffrythm` - Music generation
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [Browsing the Grid](https://inference.sh/docs/apps/browsing-grid) - Visual app browsing
|
||||||
|
- [Apps Overview](https://inference.sh/docs/apps/overview) - Understanding apps
|
||||||
|
- [Running Apps](https://inference.sh/docs/apps/running) - How to run apps
|
||||||
59
skills/inference-sh/cli/references/authentication.md
Normal file
59
skills/inference-sh/cli/references/authentication.md
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
# Authentication & Setup
|
||||||
|
|
||||||
|
## Install the CLI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://cli.inference.sh | sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Login
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh login
|
||||||
|
```
|
||||||
|
|
||||||
|
This opens a browser for authentication. After login, credentials are stored locally.
|
||||||
|
|
||||||
|
## Check Authentication
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh me
|
||||||
|
```
|
||||||
|
|
||||||
|
Shows your user info if authenticated.
|
||||||
|
|
||||||
|
## Environment Variable
|
||||||
|
|
||||||
|
For CI/CD or scripts, set your API key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export INFSH_API_KEY=your-api-key
|
||||||
|
```
|
||||||
|
|
||||||
|
The environment variable overrides the config file.
|
||||||
|
|
||||||
|
## Update CLI
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh update
|
||||||
|
```
|
||||||
|
|
||||||
|
Or reinstall:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://cli.inference.sh | sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Error | Solution |
|
||||||
|
|-------|----------|
|
||||||
|
| "not authenticated" | Run `infsh login` |
|
||||||
|
| "command not found" | Reinstall CLI or add to PATH |
|
||||||
|
| "API key invalid" | Check `INFSH_API_KEY` or re-login |
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [CLI Setup](https://inference.sh/docs/extend/cli-setup) - Complete CLI installation guide
|
||||||
|
- [API Authentication](https://inference.sh/docs/api/authentication) - API key management
|
||||||
|
- [Secrets](https://inference.sh/docs/secrets/overview) - Managing credentials
|
||||||
104
skills/inference-sh/cli/references/cli-reference.md
Normal file
104
skills/inference-sh/cli/references/cli-reference.md
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
# CLI Reference
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -fsSL https://cli.inference.sh | sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Global Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `infsh help` | Show help |
|
||||||
|
| `infsh version` | Show CLI version |
|
||||||
|
| `infsh update` | Update CLI to latest |
|
||||||
|
| `infsh login` | Authenticate |
|
||||||
|
| `infsh me` | Show current user |
|
||||||
|
|
||||||
|
## App Commands
|
||||||
|
|
||||||
|
### Discovery
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `infsh app list` | List available apps |
|
||||||
|
| `infsh app list --category <cat>` | Filter by category (image, video, audio, text, other) |
|
||||||
|
| `infsh app search <query>` | Search apps |
|
||||||
|
| `infsh app list --search <query>` | Search apps (flag form) |
|
||||||
|
| `infsh app list --featured` | Show featured apps |
|
||||||
|
| `infsh app list --new` | Sort by newest |
|
||||||
|
| `infsh app list --page <n>` | Pagination |
|
||||||
|
| `infsh app list -l` | Detailed table view |
|
||||||
|
| `infsh app list --save <file>` | Save to JSON file |
|
||||||
|
| `infsh app my` | List your deployed apps |
|
||||||
|
| `infsh app get <app>` | Get app details |
|
||||||
|
| `infsh app get <app> --json` | Get app details as JSON |
|
||||||
|
|
||||||
|
### Execution
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `infsh app run <app> --input <file>` | Run app with input file |
|
||||||
|
| `infsh app run <app> --input '<json>'` | Run with inline JSON |
|
||||||
|
| `infsh app run <app> --input <file> --no-wait` | Run without waiting for completion |
|
||||||
|
| `infsh app sample <app>` | Show sample input |
|
||||||
|
| `infsh app sample <app> --save <file>` | Save sample to file |
|
||||||
|
|
||||||
|
## Task Commands
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `infsh task get <task-id>` | Get task status and result |
|
||||||
|
| `infsh task get <task-id> --json` | Get task as JSON |
|
||||||
|
| `infsh task get <task-id> --save <file>` | Save task result to file |
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
| Command | Description |
|
||||||
|
|---------|-------------|
|
||||||
|
| `infsh app init` | Create new app (interactive) |
|
||||||
|
| `infsh app init <name>` | Create new app with name |
|
||||||
|
| `infsh app test --input <file>` | Test app locally |
|
||||||
|
| `infsh app deploy` | Deploy app |
|
||||||
|
| `infsh app deploy --dry-run` | Validate without deploying |
|
||||||
|
| `infsh app pull <id>` | Pull app source |
|
||||||
|
| `infsh app pull --all` | Pull all your apps |
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `INFSH_API_KEY` | API key (overrides config) |
|
||||||
|
|
||||||
|
## Shell Completions
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Bash
|
||||||
|
infsh completion bash > /etc/bash_completion.d/infsh
|
||||||
|
|
||||||
|
# Zsh
|
||||||
|
infsh completion zsh > "${fpath[1]}/_infsh"
|
||||||
|
|
||||||
|
# Fish
|
||||||
|
infsh completion fish > ~/.config/fish/completions/infsh.fish
|
||||||
|
```
|
||||||
|
|
||||||
|
## App Name Format
|
||||||
|
|
||||||
|
Apps use the format `namespace/app-name`:
|
||||||
|
|
||||||
|
- `falai/flux-dev-lora` - fal.ai's FLUX 2 Dev
|
||||||
|
- `google/veo-3` - Google's Veo 3
|
||||||
|
- `infsh/sdxl` - inference.sh's SDXL
|
||||||
|
- `bytedance/seedance-1-5-pro` - ByteDance's Seedance
|
||||||
|
- `xai/grok-imagine-image` - xAI's Grok
|
||||||
|
|
||||||
|
Version pinning: `namespace/app-name@version`
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [CLI Setup](https://inference.sh/docs/extend/cli-setup) - Complete CLI installation guide
|
||||||
|
- [Running Apps](https://inference.sh/docs/apps/running) - How to run apps via CLI
|
||||||
|
- [Creating an App](https://inference.sh/docs/extend/creating-app) - Build your own apps
|
||||||
|
- [Deploying](https://inference.sh/docs/extend/deploying) - Deploy apps to the cloud
|
||||||
171
skills/inference-sh/cli/references/running-apps.md
Normal file
171
skills/inference-sh/cli/references/running-apps.md
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
# Running Apps
|
||||||
|
|
||||||
|
## Basic Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app run user/app-name --input input.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Inline JSON
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app run falai/flux-dev-lora --input '{"prompt": "a sunset over mountains"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Version Pinning
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app run user/app-name@1.0.0 --input input.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Local File Uploads
|
||||||
|
|
||||||
|
The CLI automatically uploads local files when you provide a file path instead of a URL. Any field that accepts a URL also accepts a local path:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Upscale a local image
|
||||||
|
infsh app run falai/topaz-image-upscaler --input '{"image": "/path/to/photo.jpg", "upscale_factor": 2}'
|
||||||
|
|
||||||
|
# Image-to-video from local file
|
||||||
|
infsh app run falai/wan-2-5-i2v --input '{"image": "./my-image.png", "prompt": "make it move"}'
|
||||||
|
|
||||||
|
# Avatar with local audio and image
|
||||||
|
infsh app run bytedance/omnihuman-1-5 --input '{"audio": "/path/to/speech.mp3", "image": "/path/to/face.jpg"}'
|
||||||
|
|
||||||
|
# Post tweet with local media
|
||||||
|
infsh app run x/post-create --input '{"text": "Check this out!", "media": "./screenshot.png"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
Supported paths:
|
||||||
|
- Absolute paths: `/home/user/images/photo.jpg`
|
||||||
|
- Relative paths: `./image.png`, `../data/video.mp4`
|
||||||
|
- Home directory: `~/Pictures/photo.jpg`
|
||||||
|
|
||||||
|
## Generate Sample Input
|
||||||
|
|
||||||
|
Before running, generate a sample input file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app sample falai/flux-dev-lora
|
||||||
|
```
|
||||||
|
|
||||||
|
Save to file:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app sample falai/flux-dev-lora --save input.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Then edit `input.json` and run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
infsh app run falai/flux-dev-lora --input input.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## Workflow Example
|
||||||
|
|
||||||
|
### Image Generation with FLUX
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Get app details
|
||||||
|
infsh app get falai/flux-dev-lora
|
||||||
|
|
||||||
|
# 2. Generate sample input
|
||||||
|
infsh app sample falai/flux-dev-lora --save input.json
|
||||||
|
|
||||||
|
# 3. Edit input.json
|
||||||
|
# {
|
||||||
|
# "prompt": "a cat astronaut floating in space",
|
||||||
|
# "num_images": 1,
|
||||||
|
# "image_size": "landscape_16_9"
|
||||||
|
# }
|
||||||
|
|
||||||
|
# 4. Run
|
||||||
|
infsh app run falai/flux-dev-lora --input input.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Video Generation with Veo
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Generate sample
|
||||||
|
infsh app sample google/veo-3-1-fast --save input.json
|
||||||
|
|
||||||
|
# 2. Edit prompt
|
||||||
|
# {
|
||||||
|
# "prompt": "A drone shot flying over a forest at sunset"
|
||||||
|
# }
|
||||||
|
|
||||||
|
# 3. Run
|
||||||
|
infsh app run google/veo-3-1-fast --input input.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Text-to-Speech
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Quick inline run
|
||||||
|
infsh app run falai/kokoro-tts --input '{"text": "Hello, this is a test."}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Task Tracking
|
||||||
|
|
||||||
|
When you run an app, the CLI shows the task ID:
|
||||||
|
|
||||||
|
```
|
||||||
|
Running falai/flux-dev-lora
|
||||||
|
Task ID: abc123def456
|
||||||
|
```
|
||||||
|
|
||||||
|
For long-running tasks, you can check status anytime:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check task status
|
||||||
|
infsh task get abc123def456
|
||||||
|
|
||||||
|
# Get result as JSON
|
||||||
|
infsh task get abc123def456 --json
|
||||||
|
|
||||||
|
# Save result to file
|
||||||
|
infsh task get abc123def456 --save result.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run Without Waiting
|
||||||
|
|
||||||
|
For very long tasks, run in background:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Submit and return immediately
|
||||||
|
infsh app run google/veo-3 --input input.json --no-wait
|
||||||
|
|
||||||
|
# Check later
|
||||||
|
infsh task get <task-id>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
The CLI returns the app output directly. For file outputs (images, videos, audio), you'll receive URLs to download.
|
||||||
|
|
||||||
|
Example output:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"url": "https://cloud.inference.sh/...",
|
||||||
|
"content_type": "image/png"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
| Error | Cause | Solution |
|
||||||
|
|-------|-------|----------|
|
||||||
|
| "invalid input" | Schema mismatch | Check `infsh app get` for required fields |
|
||||||
|
| "app not found" | Wrong app name | Check `infsh app list --search` |
|
||||||
|
| "quota exceeded" | Out of credits | Check account balance |
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
- [Running Apps](https://inference.sh/docs/apps/running) - Complete running apps guide
|
||||||
|
- [Streaming Results](https://inference.sh/docs/api/sdk/streaming) - Real-time progress updates
|
||||||
|
- [Setup Parameters](https://inference.sh/docs/apps/setup-parameters) - Configuring app inputs
|
||||||
114
tests/tools/test_infsh_tool.py
Normal file
114
tests/tools/test_infsh_tool.py
Normal 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
|
||||||
302
tools/infsh_tool.py
Normal file
302
tools/infsh_tool.py
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Inference.sh Tool Module
|
||||||
|
|
||||||
|
A simple tool for running AI apps via the inference.sh CLI (infsh).
|
||||||
|
Provides two functions:
|
||||||
|
- infsh_install: Install the infsh CLI
|
||||||
|
- infsh: Run any infsh command
|
||||||
|
|
||||||
|
This is a lightweight wrapper that gives agents direct access to 150+ AI apps
|
||||||
|
including image generation (FLUX, Reve), video (Veo, Wan), LLMs, search, and more.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
from tools.infsh_tool import infsh_tool, infsh_install
|
||||||
|
|
||||||
|
# Install the CLI
|
||||||
|
result = infsh_install()
|
||||||
|
|
||||||
|
# Search for apps first (always do this!)
|
||||||
|
result = infsh_tool("app list --search flux")
|
||||||
|
|
||||||
|
# Run an app
|
||||||
|
result = infsh_tool("app run falai/flux-dev-lora --input '{\"prompt\": \"a cat\"}' --json")
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Configuration
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
DEFAULT_TIMEOUT = 300 # 5 minutes for long-running AI tasks
|
||||||
|
INSTALL_TIMEOUT = 60
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Availability check
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def check_infsh_requirements() -> bool:
|
||||||
|
"""Check if infsh is available in PATH."""
|
||||||
|
return shutil.which("infsh") is not None
|
||||||
|
|
||||||
|
|
||||||
|
def _get_infsh_path() -> Optional[str]:
|
||||||
|
"""Get the path to infsh binary."""
|
||||||
|
return shutil.which("infsh")
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Install function
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def infsh_install() -> str:
|
||||||
|
"""
|
||||||
|
Install the inference.sh CLI.
|
||||||
|
|
||||||
|
Downloads and installs the infsh binary using the official installer script.
|
||||||
|
The installer detects OS/arch, downloads the correct binary, verifies checksum,
|
||||||
|
and places it in PATH.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON string with success/error status
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Check if already installed
|
||||||
|
if check_infsh_requirements():
|
||||||
|
infsh_path = _get_infsh_path()
|
||||||
|
# Get version
|
||||||
|
version_result = subprocess.run(
|
||||||
|
["infsh", "--version"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10
|
||||||
|
)
|
||||||
|
version = version_result.stdout.strip() if version_result.returncode == 0 else "unknown"
|
||||||
|
return json.dumps({
|
||||||
|
"success": True,
|
||||||
|
"message": f"infsh is already installed at {infsh_path}",
|
||||||
|
"version": version,
|
||||||
|
"already_installed": True
|
||||||
|
})
|
||||||
|
|
||||||
|
# Run the installer
|
||||||
|
result = subprocess.run(
|
||||||
|
["sh", "-c", "curl -fsSL https://cli.inference.sh | sh"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=INSTALL_TIMEOUT,
|
||||||
|
env={**os.environ, "NONINTERACTIVE": "1"}
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode != 0:
|
||||||
|
return json.dumps({
|
||||||
|
"success": False,
|
||||||
|
"error": f"Installation failed: {result.stderr}",
|
||||||
|
"stdout": result.stdout
|
||||||
|
})
|
||||||
|
|
||||||
|
# Verify installation
|
||||||
|
if not check_infsh_requirements():
|
||||||
|
return json.dumps({
|
||||||
|
"success": False,
|
||||||
|
"error": "Installation completed but infsh not found in PATH. You may need to restart your shell or add ~/.local/bin to PATH.",
|
||||||
|
"stdout": result.stdout
|
||||||
|
})
|
||||||
|
|
||||||
|
return json.dumps({
|
||||||
|
"success": True,
|
||||||
|
"message": "infsh installed successfully",
|
||||||
|
"stdout": result.stdout,
|
||||||
|
"next_step": "Run 'infsh login' to authenticate, or set INFSH_API_KEY environment variable"
|
||||||
|
})
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return json.dumps({
|
||||||
|
"success": False,
|
||||||
|
"error": f"Installation timed out after {INSTALL_TIMEOUT}s"
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception("infsh_install error: %s", e)
|
||||||
|
return json.dumps({
|
||||||
|
"success": False,
|
||||||
|
"error": f"Installation error: {type(e).__name__}: {e}"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Main tool function
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def infsh_tool(
|
||||||
|
command: str,
|
||||||
|
timeout: Optional[int] = None,
|
||||||
|
) -> str:
|
||||||
|
"""
|
||||||
|
Execute an infsh CLI command.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
command: The infsh command to run (without the 'infsh' prefix).
|
||||||
|
Examples: "app list", "app run falai/flux-schnell --input '{}'"
|
||||||
|
timeout: Command timeout in seconds (default: 300)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON string with output, exit_code, and error fields
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
effective_timeout = timeout or DEFAULT_TIMEOUT
|
||||||
|
|
||||||
|
# Check if infsh is installed
|
||||||
|
if not check_infsh_requirements():
|
||||||
|
return json.dumps({
|
||||||
|
"success": False,
|
||||||
|
"error": "infsh CLI is not installed. Use infsh_install to install it first.",
|
||||||
|
"hint": "Call the infsh_install tool to set up the CLI"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Build the full command
|
||||||
|
full_command = f"infsh {command}"
|
||||||
|
|
||||||
|
# Execute
|
||||||
|
result = subprocess.run(
|
||||||
|
full_command,
|
||||||
|
shell=True,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=effective_timeout,
|
||||||
|
env=os.environ
|
||||||
|
)
|
||||||
|
|
||||||
|
output = result.stdout
|
||||||
|
error = result.stderr
|
||||||
|
|
||||||
|
# Try to parse JSON output if present
|
||||||
|
parsed_output = None
|
||||||
|
if output.strip():
|
||||||
|
try:
|
||||||
|
parsed_output = json.loads(output)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
pass # Not JSON, keep as string
|
||||||
|
|
||||||
|
response = {
|
||||||
|
"success": result.returncode == 0,
|
||||||
|
"exit_code": result.returncode,
|
||||||
|
"output": parsed_output if parsed_output is not None else output,
|
||||||
|
}
|
||||||
|
|
||||||
|
if error:
|
||||||
|
response["stderr"] = error
|
||||||
|
|
||||||
|
return json.dumps(response, indent=2)
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return json.dumps({
|
||||||
|
"success": False,
|
||||||
|
"error": f"Command timed out after {effective_timeout}s",
|
||||||
|
"hint": "For long-running tasks, consider using --no-wait flag"
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception("infsh_tool error: %s", e)
|
||||||
|
return json.dumps({
|
||||||
|
"success": False,
|
||||||
|
"error": f"Execution error: {type(e).__name__}: {e}"
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Registry
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
from tools.registry import registry
|
||||||
|
|
||||||
|
INFSH_TOOL_DESCRIPTION = """Run AI apps via inference.sh CLI. Access 150+ apps for image generation, video, LLMs, search, 3D, and more.
|
||||||
|
|
||||||
|
One API key for everything - manage all AI services (FLUX, Veo, Claude, Tavily, etc.) with a single inference.sh account. You can also bring your own API keys.
|
||||||
|
|
||||||
|
IMPORTANT: Always use 'app list --search <query>' first to find the exact app ID before running. App names change frequently.
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
- app list --search <query>: Find apps (ALWAYS DO THIS FIRST)
|
||||||
|
- app run <app-id> --input '<json>' --json: Run an app
|
||||||
|
- app get <app-id>: Get app schema before running
|
||||||
|
|
||||||
|
Verified app examples (use --search to confirm current names):
|
||||||
|
- Image: google/nano-banana, google/nano-banana-pro, google/nano-banana-2, falai/flux-dev-lora, bytedance/seedream-5-lite, falai/reve, xai/grok-imagine-image
|
||||||
|
- Video: google/veo-3-1-fast, bytedance/seedance-1-5-pro, falai/wan-2-5
|
||||||
|
- Upscale: falai/topaz-image-upscaler
|
||||||
|
- Search: tavily/search-assistant, exa/search
|
||||||
|
- LLM: openrouter/claude-sonnet-45
|
||||||
|
|
||||||
|
Workflow: ALWAYS search first, then run:
|
||||||
|
1. app list --search image
|
||||||
|
2. app run falai/flux-dev-lora --input '{"prompt": "a sunset"}' --json"""
|
||||||
|
|
||||||
|
INFSH_SCHEMA = {
|
||||||
|
"name": "infsh",
|
||||||
|
"description": INFSH_TOOL_DESCRIPTION,
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"command": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The infsh command (without 'infsh' prefix). ALWAYS use 'app list --search <query>' first to find correct app IDs, then 'app run <id> --input <json> --json'"
|
||||||
|
},
|
||||||
|
"timeout": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Max seconds to wait (default: 300). AI tasks like video generation may take 1-2 minutes.",
|
||||||
|
"minimum": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["command"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
INFSH_INSTALL_SCHEMA = {
|
||||||
|
"name": "infsh_install",
|
||||||
|
"description": "Install the inference.sh CLI (infsh). Downloads and installs the binary. Run this first if infsh is not available.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {},
|
||||||
|
"required": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_infsh(args, **kw):
|
||||||
|
return infsh_tool(
|
||||||
|
command=args.get("command", ""),
|
||||||
|
timeout=args.get("timeout"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _handle_infsh_install(args, **kw):
|
||||||
|
return infsh_install()
|
||||||
|
|
||||||
|
|
||||||
|
# Register both tools under the "inference" toolset
|
||||||
|
registry.register(
|
||||||
|
name="infsh",
|
||||||
|
toolset="inference",
|
||||||
|
schema=INFSH_SCHEMA,
|
||||||
|
handler=_handle_infsh,
|
||||||
|
check_fn=check_infsh_requirements,
|
||||||
|
requires_env=[],
|
||||||
|
)
|
||||||
|
|
||||||
|
registry.register(
|
||||||
|
name="infsh_install",
|
||||||
|
toolset="inference",
|
||||||
|
schema=INFSH_INSTALL_SCHEMA,
|
||||||
|
handler=_handle_infsh_install,
|
||||||
|
check_fn=lambda: True, # Always available - it's the installer
|
||||||
|
requires_env=[],
|
||||||
|
)
|
||||||
|
|
@ -184,6 +184,12 @@ TOOLSETS = {
|
||||||
"includes": []
|
"includes": []
|
||||||
},
|
},
|
||||||
|
|
||||||
|
"inference": {
|
||||||
|
"description": "inference.sh CLI (infsh) — run 150+ AI apps: image gen (FLUX, Reve), video (Veo, Wan), LLMs, search (Tavily, Exa), 3D, and more",
|
||||||
|
"tools": ["infsh", "infsh_install"],
|
||||||
|
"includes": []
|
||||||
|
},
|
||||||
|
|
||||||
"delegation": {
|
"delegation": {
|
||||||
"description": "Spawn subagents with isolated context for complex subtasks",
|
"description": "Spawn subagents with isolated context for complex subtasks",
|
||||||
"tools": ["delegate_task"],
|
"tools": ["delegate_task"],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue