Squad

Squad AI

Provider-independent AI gateway for ProcessWire β€” chat, content, embeddings, images and tool-use behind one clean API. (Formerly AiWire.)

Squad AI

Squad connects your ProcessWire site to every major AI provider through a single, uniform PHP API. Write code once and switch providers (or fall back between them) without changing a line. Keys are stored encrypted, never in plaintext config.

πŸ“– Full API reference + 25 worked examples β†’ DOCUMENTATION.md Β· changes β†’ CHANGELOG.md

$ai = $modules->get('Squad');
echo $ai->chat('Write a one-line tagline for a dentist in Boston.');

Features


Requirements


Installation

  1. Copy into site/modules/Squad/.
  2. Admin β†’ Modules β†’ Refresh β†’ Install β€œSquad”.
  3. Open the module config, add an API key under any provider, click Test, Save All Keys.
  4. Use $modules->get('Squad') in your templates/modules.
site/modules/Squad/
β”œβ”€β”€ Squad.module.php     # main module + provider catalogue + admin UI
β”œβ”€β”€ SquadProvider.php    # HTTP client for all providers (chat / embed / image / tools)
β”œβ”€β”€ SquadCache.php       # file-based response cache
β”œβ”€β”€ SquadKeys.php        # encrypted key storage (libsodium)
β”œβ”€β”€ models.json          # editable provider/model catalogue
β”œβ”€β”€ README.md Β· DOCUMENTATION.md Β· CHANGELOG.md Β· LICENSE

Where to get keys

Provider Console
Anthropic console.anthropic.com
OpenAI platform.openai.com/api-keys
Google (Gemini) aistudio.google.com/apikey
xAI console.x.ai
OpenRouter openrouter.ai/keys
DeepSeek / Qwen / Kimi / GLM / MiniMax / Yi / Doubao / Ernie / Hunyuan each provider’s own console (see models.json for endpoints)

Quick start

$ai = $modules->get('Squad');

// 1) Simple text
echo $ai->chat('What is ProcessWire?');

// 2) Full response with metadata
$res = $ai->ask('Explain embeddings in one sentence.', ['maxTokens' => 500]);
if ($res['success']) echo $res['content'];

// 3) Fallback across providers
$res = $ai->askWithFallback('Summarise this…', [
    'provider' => 'anthropic', 'fallbackProviders' => ['openai', 'google'],
]);

// 4) Embeddings (one string or an array β†’ vectors)
$vec  = $ai->embed('hello world')['embedding'];          // [float, …]
$vecs = $ai->embed(['a', 'b', 'c'])['embeddings'];       // [[…], […], […]]

// 5) Image generation
$img = $ai->image('a calm coastal sunrise, photographic', ['aspect' => '16:9']);
echo $img['url'];   // or $img['b64']

// 6) Tool use (agent loop)
$res = $ai->run([
    'message' => 'What is 19 * 23?',
    'tools'   => [['name' => 'multiply', 'description' => 'Multiply two numbers',
        'parameters' => ['type'=>'object','properties'=>['a'=>['type'=>'number'],'b'=>['type'=>'number']],'required'=>['a','b']]]],
    'onTool'  => fn($name, $in) => (string) ($in['a'] * $in['b']),
]);
echo $res['content'];

// 7) Write AI copy into page fields
$ai->generate($page, [
    ['field' => 'summary',  'prompt' => "One-sentence summary of {$page->title}"],
    ['field' => 'body',     'prompt' => "Two paragraphs about {$page->title}"],
], ['cache' => 'M']);

API methods

Method Returns Description
chat($msg, $opts) string Text only, '' on error
ask($msg, $opts) array success, content, usage, raw, cached
stream($msg, $onDelta, $opts) array Calls $onDelta for each text delta and returns the final response
askWithFallback($msg, $opts) array Tries all keys/providers until success
askMultiple($msg, $providers) array Same prompt to several providers
embed($input, $opts) array embedding (single) / embeddings (batch), model, usage
image($prompt, $opts) array url / b64, model, provider
run($opts) array Tool-use loop: content, steps, messages, usage
generate($page, $blocks, $opts) array Multi-block field generation
askAndSave($page, $fields, $msg) array Ask + save to field (skip if filled)
saveTo / loadFrom bool / ?string Manual field storage
getProvidersStatus() array Providers + key status
getDefaultEmbedProvider() / getDefaultImageProvider() ?string First capable provider with a key
clearCache / clearAllCache / cacheStats β€” Cache management

Common $opts: provider, model, systemPrompt, maxTokens, temperature, history, keyIndex, cache, timeout, webSearch and webSearchMaxResults (1–10, default 5). Search may add provider charges and latency. OpenRouter supports search for every routed model; direct search is supported for Anthropic, OpenAI, Google and xAI. Other direct adapters return a clear unsupported error instead of silently answering without search.

$result = $modules->get('Squad')->ask(
    'What changed in Australian herbal liqueurs this year?',
    [
        'provider' => 'openrouter',
        'webSearch' => true,
        'webSearchMaxResults' => 5,
    ]
);

foreach($result['sources'] ?? [] as $source) {
    echo $source['title'] . ': ' . $source['url'];
}

Embeddings/images also accept provider/model; image() takes aspect, resolution, size, n. vision($prompt, $images, $opts) accepts bounded local image paths or image data URLs.


Security


License

MIT. Author: Maxim Semenov β€” smnv.org Β· built for the ProcessWire community.