Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
98.82% covered (success)
98.82%
84 / 85
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
ImageGen
98.82% covered (success)
98.82%
84 / 85
66.67% covered (warning)
66.67%
2 / 3
15
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 register_abilities
100.00% covered (success)
100.00%
45 / 45
100.00% covered (success)
100.00%
1 / 1
1
 generate
97.44% covered (success)
97.44%
38 / 39
0.00% covered (danger)
0.00%
0 / 1
13
1<?php
2
3declare( strict_types=1 );
4
5namespace BLU\Abilities;
6
7/**
8 * Image generation ability using the AI platform service.
9 */
10class ImageGen {
11
12    /**
13     * Constructor - registers image generation ability.
14     */
15    public function __construct() {
16        $this->register_abilities();
17    }
18
19    /**
20     * Register image generation abilities.
21     */
22    private function register_abilities(): void {
23        blu_register_ability(
24            'blu/generate-image',
25            array(
26                'label'               => 'Generate Image',
27                'description'         => 'Create a brand-new AI-generated image from a text prompt. Use only when no existing image is available to work from. If the user wants to modify or enhance an existing image, use blu/edit-image instead.',
28                'category'            => 'blu-mcp',
29                'input_schema'        => array(
30                    'type'       => 'object',
31                    'properties' => array(
32                        'prompt'      => array(
33                            'type'        => 'string',
34                            'description' => 'Detailed description of the image to create (subject, style, lighting, mood, colours). Max 1000 characters.',
35                            'maxLength'   => 1000,
36                        ),
37                        'orientation' => array(
38                            'type'        => 'string',
39                            'description' => 'Image orientation. Defaults to landscape.',
40                            'enum'        => array( 'landscape', 'portrait', 'square' ),
41                        ),
42                        'width'       => array(
43                            'type'        => 'integer',
44                            'description' => 'Desired width in pixels. Max 1920.',
45                            'maximum'     => 1920,
46                            'minimum'     => 1,
47                        ),
48                        'height'      => array(
49                            'type'        => 'integer',
50                            'description' => 'Desired height in pixels. Max 1080.',
51                            'maximum'     => 1080,
52                            'minimum'     => 1,
53                        ),
54                    ),
55                    'required'   => array( 'prompt' ),
56                ),
57                'execute_callback'    => array( $this, 'generate' ),
58                'permission_callback' => fn() => current_user_can( 'upload_files' ),
59                'meta'                => array(
60                    'annotations' => array(
61                        'readonly'    => false,
62                        'destructive' => false,
63                        'idempotent'  => false,
64                    ),
65                ),
66            )
67        );
68    }
69
70    /**
71     * Generate an image via the AI platform.
72     *
73     * @param array $input Tool input parameters.
74     * @return array Standardized ability response.
75     */
76    public function generate( array $input ): array {
77        // Extend PHP execution time for this long-running request.
78        set_time_limit( 120 );
79
80        $api_url = defined( 'NFD_AI_PLATFORM_URL' ) ? NFD_AI_PLATFORM_URL : 'https://ai-platform.hiive.cloud';
81
82        // Get Hiive auth token — required by the ai-platform middleware.
83        $hiive_token = '';
84        if ( class_exists( '\NewfoldLabs\WP\Module\Data\HiiveConnection' ) ) {
85            $hiive_token = \NewfoldLabs\WP\Module\Data\HiiveConnection::get_auth_token();
86        }
87
88        if ( empty( $hiive_token ) ) {
89            return blu_prepare_ability_response( 401, 'Unable to retrieve Hiive authentication token for image generation.' );
90        }
91
92        $body = array(
93            'prompt' => substr( $input['prompt'], 0, 1000 ),
94        );
95
96        if ( ! empty( $input['orientation'] ) ) {
97            $body['orientation'] = $input['orientation'];
98        }
99        if ( ! empty( $input['width'] ) ) {
100            $body['width'] = min( (int) $input['width'], 1920 );
101        }
102        if ( ! empty( $input['height'] ) ) {
103            $body['height'] = min( (int) $input['height'], 1080 );
104        }
105
106        $response = wp_remote_post(
107            trailingslashit( $api_url ) . 'api/v1/imagegen/image',
108            array(
109                'headers' => array(
110                    'Content-Type'  => 'application/json',
111                    'Authorization' => 'Bearer ' . $hiive_token,
112                ),
113                'body'    => wp_json_encode( $body ),
114                'timeout' => 90,
115            )
116        );
117
118        if ( is_wp_error( $response ) ) {
119            $message = $response->get_error_message();
120            if ( false !== strpos( $message, 'timed out' ) || false !== strpos( $message, 'cURL error 28' ) ) {
121                return blu_prepare_ability_response( 504, 'Image generation timed out' );
122            }
123            return blu_prepare_ability_response( 502, 'Image generation service unavailable: ' . $message );
124        }
125
126        $status_code = wp_remote_retrieve_response_code( $response );
127        if ( $status_code < 200 || $status_code >= 300 ) {
128            return blu_prepare_ability_response( $status_code, 'Image generation failed with status ' . $status_code );
129        }
130
131        $data = json_decode( wp_remote_retrieve_body( $response ), true );
132        if ( empty( $data['url'] ) ) {
133            return blu_prepare_ability_response( 500, 'No image URL in response' );
134        }
135
136        return blu_prepare_ability_response( 200, array( 'url' => $data['url'] ) );
137    }
138}