Squad

AI integration for ProcessWire. Supports Anthropic, OpenAI, Google, xAI, and OpenRouter.

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


  • 14 providers, one API — Anthropic (Claude), OpenAI (GPT), Google (Gemini), xAI (Grok), OpenRouter (400+ models), plus direct Chinese providers: DeepSeek, Qwen (Alibaba), Kimi (Moonshot), GLM (Zhipu), MiniMax, Yi (01.AI), Doubao (ByteDance), Ernie (Baidu), Hunyuan (Tencent).
  • Textchat() / ask() with system prompts, multi-turn history, temperature, token limits.
  • Embeddingsembed() for one string or a batch (OpenAI, Google, Qwen, Zhipu). Powers RAG (see the Atlas module).
  • Imagesimage() for text-to-image (xAI Grok Imagine, OpenAI gpt-image-1 / DALL·E 3).
  • Tool use / agentsrun() drives a multi-step tool-calling loop (OpenAI and Anthropic tool formats).
  • Automatic fallbackaskWithFallback() walks every enabled key, then other providers, until one succeeds.
  • Multiple keys per provider — enable/disable, default-key selection, one-click connection test in admin.
  • Encrypted key storage — keys live in a dedicated table, encrypted with libsodium (secret from config.php), so a database dump never exposes them. env:NAME references are also supported.
  • Prompt caching — Anthropic prompt caching for large system prompts, automatic for prompts ≥ ~3 KB.
  • Adaptive-model aware — automatically omits temperature/sampling params on models that reject them (Claude Opus 4.7/4.8, Fable/Mythos), so calls don't 400.
  • File cache — TTL-based response caching (D/W/M/Y, custom like 2W) with optional page scoping.
  • Field storageaskAndSave() / generate() write AI copy straight to page fields (skip if already filled).
  • Editable model catalogmodels.json + live model refresh (OpenAI/OpenRouter) + per-key custom model IDs.

Requirements


  • ProcessWire 3.0.210+
  • PHP 8.1+ (with the sodium extension for encrypted key storage — bundled in PHP 8)
  • cURL extension

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

ProviderConsole
Anthropicconsole.anthropic.com
OpenAIplatform.openai.com/api-keys
Google (Gemini)aistudio.google.com/apikey
xAIconsole.x.ai
OpenRouteropenrouter.ai/keys
DeepSeek / Qwen / Kimi / GLM / MiniMax / Yi / Doubao / Ernie / Hunyuaneach 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


MethodReturnsDescription
chat($msg, $opts)stringText only, '' on error
ask($msg, $opts)arraysuccess, content, usage, raw, cached
askWithFallback($msg, $opts)arrayTries all keys/providers until success
askMultiple($msg, $providers)arraySame prompt to several providers
embed($input, $opts)arrayembedding (single) / embeddings (batch), model, usage
image($prompt, $opts)arrayurl / b64, model, provider
run($opts)arrayTool-use loop: content, steps, messages, usage
generate($page, $blocks, $opts)arrayMulti-block field generation
askAndSave($page, $fields, $msg)arrayAsk + save to field (skip if filled)
saveTo / loadFrombool / ?stringManual field storage
getProvidersStatus()arrayProviders + key status
getDefaultEmbedProvider() / getDefaultImageProvider()?stringFirst capable provider with a key
clearCache / clearAllCache / cacheStatsCache management

Common $opts: provider, model, systemPrompt, maxTokens, temperature, history, keyIndex, cache, timeout. Embeddings/images also accept provider/model; image() takes aspect, resolution, size, n.


Security


  • Keys are encrypted at rest. They are stored in the squad_keys table as libsodium ciphertext; the encryption key is derived from a secret in config.php ($config->squadSecrettableSaltuserAuthSalt), never from the database — so a DB dump only ever contains ciphertext. Don't change that salt after storing keys, or they'd need re-entering.
  • env: references — store a key as env:OPENAI_API_KEY to read it from an environment variable instead of the database.
  • Admin AJAX actions are CSRF-validated; JSON embedded in admin markup is escaped.

License


MIT. Author: Maxim Semenovsmnv.org · built for the ProcessWire community.

More modules by Maxim Semenov

  • Context

    Export ProcessWire site context for AI development (JSON + TOON formats)
  • Ichiban (SEO control center)

    Comprehensive SEO module: meta/OG/schema, audit, redirects, revisions, email reports.
  • WireWall

    Advanced traffic firewall with VPN/Proxy/Tor detection, rate limiting, and JS challenge
  • Subscribe

    Newsletter subscription handler with lists, double opt-in, honeypot, rate limiting and unsubscribe link.
  • Dimensions

    Stores product dimensions (L×W×H) and weight with selectable units of measurement.
  • Squad

    AI integration for ProcessWire. Supports Anthropic, OpenAI, Google, xAI, and OpenRouter.
  • Plausible Analytics

    Plausible Analytics dashboard using Stats API v2 with page-edit widget, traffic trends chart, and geo/device tabs.
  • PagePDF

    Convert ProcessWire pages to PDF documents with advanced typography support
  • Verk

    Site ops layer for ProcessWire: tasks, sprints, quarter planning, editorial calendar, content audit, and knowledge base.

All modules by Maxim Semenov

Install and use modules at your own risk. Always have a site and database backup before installing new modules.