HEX
Server: Apache/2.4.65 (Debian)
System: Linux 88f31f35b0b8 6.1.0-38-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.147-1 (2025-08-02) x86_64
User: www-data (33)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: /var/www/html/wp-content/plugins/ai-engine/labs/wpai-gateway-image-model.php
<?php
/**
 * ImageGenerationModel for the AI Engine gateway.
 *
 * Routes AiClient image prompts through `Meow_MWAI_Query_Image` and wraps the
 * resulting image URLs as `File` parts on a `ModelMessage` inside a
 * `GenerativeAiResult`.
 */

use WordPress\AiClient\Common\Exception\RuntimeException;
use WordPress\AiClient\Files\DTO\File;
use WordPress\AiClient\Messages\DTO\Message;
use WordPress\AiClient\Messages\DTO\MessagePart;
use WordPress\AiClient\Messages\DTO\ModelMessage;
use WordPress\AiClient\Providers\DTO\ProviderMetadata;
use WordPress\AiClient\Providers\Models\Contracts\ModelInterface;
use WordPress\AiClient\Providers\Models\DTO\ModelConfig;
use WordPress\AiClient\Providers\Models\DTO\ModelMetadata;
use WordPress\AiClient\Providers\Models\ImageGeneration\Contracts\ImageGenerationModelInterface;
use WordPress\AiClient\Results\DTO\Candidate;
use WordPress\AiClient\Results\DTO\GenerativeAiResult;
use WordPress\AiClient\Results\DTO\TokenUsage;
use WordPress\AiClient\Results\Enums\FinishReasonEnum;

class Meow_MWAI_Labs_WPAI_Gateway_ImageModel implements ModelInterface, ImageGenerationModelInterface {

  private ModelMetadata $modelMetadata;
  private ProviderMetadata $providerMetadata;
  private ModelConfig $config;

  public function __construct( ModelMetadata $modelMetadata, ProviderMetadata $providerMetadata ) {
    $this->modelMetadata    = $modelMetadata;
    $this->providerMetadata = $providerMetadata;
    $this->config           = new ModelConfig();
  }

  public function metadata(): ModelMetadata         { return $this->modelMetadata; }
  public function providerMetadata(): ProviderMetadata { return $this->providerMetadata; }
  public function setConfig( ModelConfig $config ): void { $this->config = $config; }
  public function getConfig(): ModelConfig          { return $this->config; }

  public function generateImageResult( array $prompt ): GenerativeAiResult {
    $core = Meow_MWAI_Labs_WPAI_Gateway::$core;
    if ( ! $core ) {
      throw new RuntimeException( 'AI Engine core unavailable.' );
    }

    $model_id = $this->modelMetadata->getId();
    $env      = $this->resolve_env_for_model( $core, $model_id );
    if ( ! $env ) {
      throw new RuntimeException( sprintf(
        'No AI Engine environment is configured for model "%s".',
        $model_id
      ) );
    }

    // Collapse all text parts across the prompt into a single image prompt.
    $text = '';
    foreach ( $prompt as $msg ) {
      if ( ! $msg instanceof Message ) { continue; }
      foreach ( $msg->getParts() as $part ) {
        if ( $part instanceof MessagePart && $part->getType()->isText() ) {
          $text .= (string) $part->getText();
        }
      }
    }
    if ( $text === '' ) {
      throw new RuntimeException( 'Empty image prompt.' );
    }

    $query = new Meow_MWAI_Query_Image( $text );
    $query->set_scope( 'wp-ai-client' );
    $query->set_env_id( $env['id'] );
    $query->set_model( $model_id );

    $reply = $core->run_query( $query );

    // AI Engine places generated images in $reply->results as a list of URLs.
    // Fall back to $reply->result if results is empty.
    $urls = [];
    if ( ! empty( $reply->results ) && is_array( $reply->results ) ) {
      foreach ( $reply->results as $u ) {
        if ( is_string( $u ) && $u !== '' ) {
          $urls[] = $u;
        }
      }
    }
    if ( empty( $urls ) && ! empty( $reply->result ) ) {
      $urls[] = (string) $reply->result;
    }
    if ( empty( $urls ) ) {
      throw new RuntimeException( 'Image generation returned no URLs.' );
    }

    $candidates = [];
    foreach ( $urls as $url ) {
      $file = new File( $url );
      $part = new MessagePart( $file );
      $message = new ModelMessage( [ $part ] );
      $candidates[] = new Candidate( $message, FinishReasonEnum::stop() );
    }

    $tokens = new TokenUsage( 0, 0, 0 );
    $id     = (string) ( $reply->id ?? uniqid( 'mwai-img-', true ) );
    return new GenerativeAiResult(
      $id,
      $candidates,
      $tokens,
      $this->providerMetadata,
      $this->modelMetadata
    );
  }

  private function resolve_env_for_model( $core, string $model_id ): ?array {
    $options = $core->get_all_options();
    $engines = $options['ai_engines'] ?? [];
    $engine_type_for_model = null;
    foreach ( $engines as $engine ) {
      foreach ( $engine['models'] ?? [] as $m ) {
        if ( ( $m['model'] ?? '' ) === $model_id ) {
          $engine_type_for_model = $engine['type'] ?? null;
          break 2;
        }
      }
    }
    if ( ! $engine_type_for_model ) {
      return null;
    }
    foreach ( $options['ai_envs'] ?? [] as $env ) {
      if ( ( $env['type'] ?? '' ) === $engine_type_for_model ) {
        return $env;
      }
    }
    return null;
  }
}