Download the PHP package php-loves-ai/multimodal-ai-runner without Composer

On this page you can find all versions of the php package php-loves-ai/multimodal-ai-runner. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package multimodal-ai-runner

🐘 ❀️ πŸ€– PHP LOVES AI: easy create with AI (no python or API integrations needed)

Run AI models locally from PHP. Generate images, write and answer text, describe pictures, transcribe speech, read text aloud, enlarge photos and make short videos β€” on your own machine, from PHP code or the command line, with any matching model from Hugging Face.

Task What it does Example model
🎨 text-to-image Generate an image from a text prompt stabilityai/sd-turbo
πŸ”Ž image-to-image Enlarge a photo, or redraw it following a prompt caidas/swin2SR-classical-sr-x2-64
✍️ text-to-text Answer a prompt, or continue it Qwen/Qwen2.5-0.5B-Instruct
πŸ‘€ image-to-text Describe an image, or answer questions about it HuggingFaceTB/SmolVLM-256M-Instruct
🎧 speech-to-text Transcribe speech in audio or video openai/whisper-tiny
πŸ—£οΈ text-to-speech Read text aloud into an audio file facebook/mms-tts-eng
🎬 text-to-video Generate a video from a text prompt Wan-AI/Wan2.1-T2V-1.3B-Diffusers
🎞️ image-to-video Animate an image into a video stabilityai/stable-video-diffusion-img2vid-xt

πŸš€ Easy to start

🐘 …and the same from PHP:

πŸ’‘ Run vendor/bin/loves-ai on its own to see every task and which ones are ready to use.

πŸ”’ No Python, no API keys, no cloud. Models run on your machine; nothing is sent anywhere. A Hugging Face key is only needed for private or gated models.

Requirements

Supported platforms: macOS arm64, Linux x86_64, Linux arm64, Windows x86_64.

Installation

Commands

Everything runs through one command, vendor/bin/loves-ai, so nothing in vendor/bin clashes with other packages. Running it without arguments lists the tasks and marks the runners that are installed:

vendor/bin/loves-ai --version prints the installed package version.

Hugging Face API key

A key is optional. Public models are pulled without one; private and gated models need it (create one at https://huggingface.co/settings/tokens).

setup asks for it once and saves the answer in .local/huggingface/credentials.json, readable only by its owner. Pressing Enter is remembered too, so setup does not ask again. To save or replace a key later, run vendor/bin/loves-ai setup --token=hf_... or pass --token=hf_... to pull. When there is no terminal to ask in (Docker builds, CI, deploy scripts), setup skips the question; pass --token there if you need a key.

The key is never read from environment variables, so every process running the project uses the same one.

Where models and runners live

Everything is stored inside your project, next to vendor/, in one fixed place:

These paths cannot be changed. They depend only on the project directory, never on the user, HOME, the working directory or environment variables. So the CLI, the web server (php-fpm running as www-data), queue workers and other containers sharing the project directory all find the same models and runners, and no code or config ever needs a path. On a server or in Docker, run setup and pull once in the project and forget about it. The web server's user needs read and execute access to .local.

setup and pull report the path they installed to, and add a .gitignore to .local, so its hundreds of MB and your API key are never committed. Add .local/ to .dockerignore if you build images from the project directory.

setup --force re-downloads, and setup --debug shows the URLs and paths used. After upgrading the package, run vendor/bin/loves-ai setup --force (plus setup text-to-image --force if you use it) to get the matching binaries.

Running a command before its binary is installed fails with The puller is not installed yet. and a hint to run setup.

Pulling models

Public models need no API key. Private and gated models use the key saved in the project (see Hugging Face API key).

From the command line

Only the files the runners can read

Hugging Face repositories usually publish the same weights several times over, for PyTorch, TensorFlow, Flax and ONNX. The runners read PyTorch only, so everything else is downloaded for nothing. pull leaves those files out:

Model Repository Pulled
openai/whisper-tiny 0.61 GB 0.16 GB
SfinOe/stable-diffusion-v1.5 10.96 GB 5.48 GB
facebook/mms-tts-eng 0.29 GB 0.15 GB
nlpconnect/vit-gpt2-image-captioning 0.98 GB 0.98 GB

Skipped are weights for other frameworks (.h5, .msgpack, .onnx, .gguf, .ckpt), single-file copies of a diffusers pipeline, alternative versions such as fp16 and non_ema, leftovers from training (optimizer.pt, trainer_state.json, checkpoint-*) and sample media. A .bin file is skipped only when the same repository also publishes it as .safetensors, so models that ship .bin alone β€” the last row above β€” are pulled untouched.

Pass --all to download the repository as it is, if a model ever needs a file these rules leave out.

The opening message is picked at random from a few cozy variants (see PullCommand::INTROS).

When Hugging Face refuses a model, pull explains why and what to do:

--token saves the key for next time. A gated model also needs its terms accepted on its Hugging Face page, with the account the key belongs to.

Pulls one model at a time into .local/models/<model id>. The puller's own output is hidden unless --debug is given. To keep that output, set a log file β€” each run is appended to it with a timestamp, the puller's output and the outcome. Output is colored on terminals; set NO_COLOR=1 to disable colors. Exit codes: 0 success, 1 pull failed, 2 invalid usage. Run vendor/bin/loves-ai pull --help for details.

Defaults come from config/pull.php:

Key Default Overridden by
revision main --revision
log_file null (output discarded) --log-file

From PHP

Failures throw exceptions implementing PhpLovesAi\Exception\PhpLovesAiException. A model Hugging Face refuses throws ModelAccessDeniedException ($model, $reason: gated or not_found, $apiKeyUsed); other failures throw PullFailedException. Both list the models pulled before the failure in $pulled. To save a key from PHP, use (new PhpLovesAi\HuggingFace\Credentials())->saveApiKey('hf_...').

The binary can also be used directly:

It writes one JSON line per model to stdout, {"model": "...", "path": "...", "skipped_files": 3, "skipped_bytes": 471859200} when pulled or {"model": "...", "error": "gated|not_found"} when refused, and progress to stderr. Exit codes: 0 success, 1 at least one model failed, 2 invalid arguments.

Generating images

Pull a complete Diffusers text-to-image model first, such as stabilityai/sd-turbo. Its repository has a model_index.json. Add-ons like embeddings or LoRAs, and single-file checkpoints, cannot be used on their own: the runner rejects them with an explanation before starting.

Then:

From the command line

Option Meaning
--output=PATH Image file to write (default: timestamped .png in output_dir)
--negative-prompt=TEXT What the image should not contain
--steps=N Inference steps (default: the model's own)
--guidance=SCALE Guidance scale; turbo models use 0 (default: the model's own)
--width=PX, --height=PX Image size (default: the model's native size)
--seed=N Random seed, for reproducible images
--device=DEVICE cpu, cuda, mps… (default: the best available)
--log-file=PATH Append the runner's output to this file
--debug Show the runner's output while generating

Values are passed to the model as-is β€” one it cannot handle (a prompt that is too long, an unsupported size…) makes the run fail. Defaults come from config/text-to-image.php (output_dir, log_file).

From PHP

Throws BinaryNotInstalledException when setup text-to-image has not been run, ModelNotFoundException when the model was not pulled yet, UnsupportedModelException when the pulled model is not a complete Diffusers text-to-image model, and RunFailedException (with the runner's error output) when generation fails.

Enlarging and redrawing images

Two kinds of image-to-image models work, and the runner tells them apart by the model's own files:

(For plain resizing to a smaller size, PHP's own GD or Imagick extension is faster and needs no model.)

From the command line

Option Meaning
--output=PATH Image file to write (default: a timestamped .png in output_dir)
--prompt=TEXT What the result should look like; needed by diffusers models, refused by upscaling models
--negative-prompt=TEXT What the result should not contain (diffusers models)
--strength=N How much of the original to keep, 0 to 1; higher changes more (diffusers models)
--steps=N Inference steps (default: the pipeline's own)
--guidance=SCALE Guidance scale; turbo models use 0 (default: the pipeline's own)
--seed=N Random seed, for reproducible images
--device=DEVICE cpu, cuda, mps… (default: the best available)
--log-file=PATH Append the runner's output to this file
--debug Show the runner's output while working

Defaults come from config/image-to-image.php (output_dir, log_file).

From PHP

Throws ImageNotFoundException when the image does not exist, BinaryNotInstalledException when setup image-to-image has not been run, ModelNotFoundException when the model was not pulled yet, UnsupportedModelException when the model does not produce images, and RunFailedException (with the runner's error output) when the run fails, e.g. when a prompt is missing or given to a model that takes none.

Upscaling works on the whole image at once, so memory use grows with the picture: a large photo can need several GB. Enlarging in a queue job, and shrinking very large photos first, keeps web requests safe.

Generating text

Pull a transformers text generation model first. Both kinds of text models work:

The runner rejects models it cannot load before starting, with an explanation: GGUF files (made for llama.cpp and Ollama), ONNX-only repositories, LoRA adapters, image models, and models that need their own Python code (trust_remote_code), which it never runs.

From the command line

Option Meaning
--system=TEXT Instructions for chat models, e.g. "You are a helpful assistant."
--max-new-tokens=N Maximum length of the answer in tokens (default: 256)
--temperature=T Randomness: 0 always picks the likeliest words (default: the model's own)
--top-p=P Nucleus sampling probability, e.g. 0.9 (default: the model's own)
--seed=N Random seed, for reproducible text
--device=DEVICE cpu, cuda, mps… (default: the best available)
--log-file=PATH Append the runner's output to this file
--debug Show the runner's output, and the text as it is written

Defaults come from config/text-to-text.php (log_file).

From PHP

Throws BinaryNotInstalledException when setup text-to-text has not been run, ModelNotFoundException when the model was not pulled yet, UnsupportedModelException when the model is not a transformers text model, and RunFailedException (with the runner's error output) when generation fails.

Small models run on CPU, but larger ones get slow quickly: a 0.5B model writes a few words per second on a laptop CPU, and each run loads the model from disk again. Run generation in a queue job rather than in a web request.

Describing images

Pull a transformers image-to-text model first. Both kinds work:

As with text models, the runner rejects models it cannot load before starting, with an explanation: text-only models, GGUF files, ONNX-only repositories and models that need their own Python code (such as Florence-2 or Moondream).

From the command line

Option Meaning
--max-new-tokens=N Maximum length of the text in tokens (default: 256)
--temperature=T Randomness: 0 always picks the likeliest words (default: the model's own)
--seed=N Random seed, for reproducible text
--device=DEVICE cpu, cuda, mps… (default: the best available)
--log-file=PATH Append the runner's output to this file
--debug Show the runner's output, and the text as it is written

Defaults come from config/image-to-text.php (log_file).

From PHP

Throws ImageNotFoundException when the image file does not exist, BinaryNotInstalledException when setup image-to-text has not been run, ModelNotFoundException when the model was not pulled yet, UnsupportedModelException when the model cannot read images, and RunFailedException (with the runner's error output) when generation fails, e.g. because the file is not an image.

Transcribing speech

Pull a transformers speech recognition model first. Both kinds work:

The runner decodes audio itself, with no FFmpeg installation needed: WAV, MP3, M4A/AAC, FLAC, OGG/Opus and the audio track of video files work, at any length. It rejects models it cannot load before starting, with an explanation, including whisper.cpp (GGML) and faster-whisper (CTranslate2) conversions, which are common on Hugging Face.

From the command line

Option Meaning
--language=LANGUAGE Spoken language for Whisper-style models, e.g. en or french (default: detected)
--translate Translate the speech into English (Whisper-style models)
--timestamps Print when each segment is spoken: phrases for Whisper-style models, words for CTC models
--device=DEVICE cpu, cuda, mps… (default: the best available)
--log-file=PATH Append the runner's output to this file
--debug Show the runner's output while transcribing

Defaults come from config/speech-to-text.php (log_file).

From PHP

Throws AudioNotFoundException when the file does not exist, BinaryNotInstalledException when setup speech-to-text has not been run, ModelNotFoundException when the model was not pulled yet, UnsupportedModelException when the model cannot transcribe speech (or cannot be told a language or translate), and RunFailedException (with the runner's error output) when transcription fails, e.g. because the file has no audio.

Small models (whisper-tiny, whisper-base) are quick; larger models and long recordings take a while, especially on CPU, and each run loads the model from disk again, so run transcription in a queue job.

Reading text aloud

Pull a transformers text-to-speech model first, one that needs nothing but text:

Models that need extra files or their own Python code are rejected before starting, with an explanation: SpeechT5 (which needs a speaker embedding file), Kokoro and Parler-TTS (which ship their own code), and speech recognition models given to the wrong runner.

From the command line

Option Meaning
--output=PATH Audio file to write; its extension picks the format: .wav, .mp3, .m4a, .flac, .ogg (default: a timestamped .wav in output_dir)
--voice=VOICE Voice of models that have several, e.g. a Bark preset like v2/en_speaker_6, or a speaker number
--speed=RATE Speaking rate of VITS-style models, e.g. 0.8 slower, 1.2 faster (default: the model's own)
--seed=N Random seed, for reproducible audio
--device=DEVICE cpu, cuda, mps… (default: the best available)
--log-file=PATH Append the runner's output to this file
--debug Show the runner's output while speaking

Defaults come from config/text-to-speech.php (output_dir, log_file).

From PHP

Throws BinaryNotInstalledException when setup text-to-speech has not been run, ModelNotFoundException when the model was not pulled yet, UnsupportedModelException when the model cannot speak, and RunFailedException (with the runner's error output) when generation fails, e.g. for an output format it cannot write.

Small voice models speak a sentence in a second or two on a laptop CPU, but each run loads the model again, so run longer texts in a queue job.

Generating videos

Pull a diffusers video model first, e.g. Wan-AI/Wan2.1-T2V-1.3B-Diffusers, zai-org/CogVideoX-2b or an AnimateDiff pipeline.

Video models are the heaviest thing here. They are several gigabytes to pull, want a lot of memory, and a few seconds of video takes minutes on a GPU and up to hours on a CPU. Start with the smallest model, few frames and a small frame size, and always generate in a queue job.

From the command line

Option Meaning
--output=PATH Video file to write; its extension picks the format: .mp4, .webm, .mkv, .gif (default: a timestamped .mp4 in output_dir)
--negative-prompt=TEXT What the video should not contain
--frames=N Number of frames to generate (default: the pipeline's own)
--fps=N Frames per second of the written file (default: 8)
--steps=N Inference steps (default: the pipeline's own)
--guidance=SCALE Guidance scale (default: the pipeline's own)
--width=PX, --height=PX Frame size (default: the pipeline's own)
--seed=N Random seed, for reproducible videos
--device=DEVICE cpu, cuda, mps… (default: the best available)
--log-file=PATH Append the runner's output to this file
--debug Show the runner's output while filming

Defaults come from config/text-to-video.php (output_dir, log_file).

From PHP

Throws BinaryNotInstalledException when setup text-to-video has not been run, ModelNotFoundException when the model was not pulled yet, UnsupportedModelException when the model is not a diffusers video pipeline (a still-image pipeline says so and points to text-to-image), and RunFailedException (with the runner's error output) when generation fails, e.g. for an output format it cannot write or when memory runs out.

Animating images

Pull a diffusers image-to-video model first. They differ in what they take:

A model whose repository only ships its text-to-video pipeline is converted to the image-to-video pipeline of the same family automatically, when diffusers has one.

The same warning as for text-to-video applies: these models are large and slow, so generate in a queue job.

From the command line

The options are the same as text-to-video, plus --prompt=TEXT for models that accept one. Defaults come from config/image-to-video.php (output_dir, log_file).

From PHP

Throws ImageNotFoundException when the image does not exist, BinaryNotInstalledException when setup image-to-video has not been run, ModelNotFoundException when the model was not pulled yet, UnsupportedModelException when the model is not a diffusers video pipeline, and RunFailedException when the run fails, e.g. when the model takes no prompt but one was given.

How it works

The Composer package is tiny and holds only PHP code; the heavy parts live outside of it:

  1. Puller binary β€” a Python program that downloads models from Hugging Face and saves them in the project.
  2. Runner binaries β€” one per task, each holding its own copy of PyTorch and the libraries that task needs. Within a task, one runner serves many models: diffusers and transformers pick the right architecture from the model's own files, so new models work without a new release.
  3. Both are compiled with PyInstaller into standalone programs, built per platform by GitHub Actions and attached to each GitHub release. setup downloads the ones matching the current OS, and the other commands find them automatically. Nothing needs Python installed.
  4. You choose which models to pull; weights never travel through Composer.
  5. The PHP classes run those programs through Symfony Process and give you a plain, typed API.

Releasing binaries

Publishing a GitHub release runs .github/workflows/release-binaries.yml, which builds every binary on each supported platform and attaches <tool>-<os>-<arch>.tar.gz plus a .sha256 checksum to the release. setup downloads from the release matching the installed package version (development installs use the latest release). The workflow can also be started by hand for an existing tag.

To build and pack locally (PyInstaller does not cross-compile, so this covers the current platform only):

Linux binaries can be built from any Docker host (including a Mac): python/build-in-docker.sh linux/amd64 or python/build-in-docker.sh linux/arm64 builds and packs them on an old glibc base, so they run on Debian 11+, Ubuntu 20.04+ and RHEL 9+. The release workflow uses the same script.

The runners bundle torch (plus diffusers or transformers, torchvision for image-to-text and PyAV with FFmpeg's libraries for the audio runners), so they are built as directories (~600–700 MB, ~210–220 MB packed) rather than single files; Linux builds use CPU-only torch to stay within GitHub's release asset size limit. To test setup against local assets, serve python/release over HTTP and set PHP_LOVES_AI_DOWNLOAD_URL to its URL.

Structure

License

MIT


All versions of multimodal-ai-runner with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
ext-json Version *
symfony/process Version ^6.4 || ^7.0 || ^8.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package php-loves-ai/multimodal-ai-runner contains the following files

Loading the files please wait ...