Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
// This file is part of Moodle - http://moodle.org/
3
//
4
// Moodle is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// Moodle is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
16
 
17
namespace aiprovider_openai;
18
 
19
use core\http_client;
20
use core_ai\ai_image;
21
use GuzzleHttp\Psr7\Request;
22
use Psr\Http\Message\RequestInterface;
23
use Psr\Http\Message\ResponseInterface;
24
 
25
/**
26
 * Class process image generation.
27
 *
28
 * @package    aiprovider_openai
29
 * @copyright  2024 Matt Porritt <matt.porritt@moodle.com>
30
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
31
 */
32
class process_generate_image extends abstract_processor {
33
    /** @var int The number of images to generate dall-e-3 only supports 1. */
34
    private int $numberimages = 1;
35
 
36
    /** @var string Response format: url or b64_json. */
37
    private string $responseformat = 'url';
38
 
39
    #[\Override]
40
    protected function query_ai_api(): array {
41
        $response = parent::query_ai_api();
42
 
43
        // If the request was successful, save the URL to a file.
44
        if ($response['success']) {
45
            $fileobj = $this->url_to_file(
46
                $this->action->get_configuration('userid'),
47
                $response['sourceurl']
48
            );
49
            // Add the file to the response, so the calling placement can do whatever they want with it.
50
            $response['draftfile'] = $fileobj;
51
        }
52
 
53
        return $response;
54
    }
55
 
56
    /**
57
     * Convert the given aspect ratio to an image size
58
     * that is compatible with the OpenAI API.
59
     *
60
     * @param string $ratio The aspect ratio of the image.
61
     * @return string The size of the image.
62
     */
63
    private function calculate_size(string $ratio): string {
64
        if ($ratio === 'square') {
65
            $size = '1024x1024';
66
        } else if ($ratio === 'landscape') {
67
            $size = '1792x1024';
68
        } else if ($ratio === 'portrait') {
69
            $size = '1024x1792';
70
        } else {
71
            throw new \coding_exception('Invalid aspect ratio: ' . $ratio);
72
        }
73
        return $size;
74
    }
75
 
76
    #[\Override]
77
    protected function create_request_object(string $userid): RequestInterface {
78
        // Create the request object.
79
        $requestobj = new \stdClass();
80
        $requestobj->model = $this->get_model();
81
        $requestobj->user = $userid;
82
        $requestobj->prompt = $this->action->get_configuration('prompttext');
83
        $requestobj->n = $this->numberimages;
84
        $requestobj->quality = $this->action->get_configuration('quality');
85
        $requestobj->response_format = $this->responseformat;
86
        $requestobj->size = $this->calculate_size($this->action->get_configuration('aspectratio'));
87
        $requestobj->style = $this->action->get_configuration('style');
88
        // Append the extra model settings.
89
        $modelsettings = $this->get_model_settings();
90
        foreach ($modelsettings as $setting => $value) {
91
            $requestobj->$setting = $value;
92
        }
93
        return new Request(
94
            method: 'POST',
95
            uri: '',
96
            headers: [
97
                'Content-Type' => 'application/json',
98
            ],
99
            body: json_encode($requestobj),
100
        );
101
    }
102
 
103
    #[\Override]
104
    protected function handle_api_success(ResponseInterface $response): array {
105
        $responsebody = $response->getBody();
106
        $bodyobj = json_decode($responsebody->getContents());
107
 
108
        return [
109
            'success' => true,
110
            'sourceurl' => $bodyobj->data[0]->url,
111
            'revisedprompt' => $bodyobj->data[0]->revised_prompt,
112
            'model' => $this->get_model(), // There is no model in the response, use config.
113
        ];
114
    }
115
 
116
    /**
117
     * Convert the url for the image to a file.
118
     *
119
     * Placements can't interact with the provider AI directly,
120
     * therefore we need to provide the image file in a format that can
121
     * be used by placements. So we use the file API.
122
     *
123
     * @param int $userid The user id.
124
     * @param string $url The URL to the image.
125
     * @return \stored_file The file object.
126
     */
127
    private function url_to_file(int $userid, string $url): \stored_file {
128
        global $CFG;
129
 
130
        require_once("{$CFG->libdir}/filelib.php");
131
 
132
        $parsedurl = parse_url($url, PHP_URL_PATH); // Parse the URL to get the path.
133
        $filename = basename($parsedurl); // Get the basename of the path.
134
 
135
        $client = \core\di::get(http_client::class);
136
 
137
        // Download the image and add the watermark.
138
        $tempdst = make_request_directory() . DIRECTORY_SEPARATOR . $filename;
139
        $client->get($url, [
140
            'sink' => $tempdst,
141
            'timeout' => $CFG->repositorygetfiletimeout,
142
        ]);
143
 
144
        $image = new ai_image($tempdst);
145
        $image->add_watermark()->save();
146
 
147
        // We put the file in the user draft area initially.
148
        // Placements (on behalf of the user) can then move it to the correct location.
149
        $fileinfo = new \stdClass();
150
        $fileinfo->contextid = \context_user::instance($userid)->id;
151
        $fileinfo->filearea = 'draft';
152
        $fileinfo->component = 'user';
153
        $fileinfo->itemid = file_get_unused_draft_itemid();
154
        $fileinfo->filepath = '/';
155
        $fileinfo->filename = $filename;
156
 
157
        $fs = get_file_storage();
158
        return $fs->create_file_from_string($fileinfo, file_get_contents($tempdst));
159
    }
160
}