PluginProbe
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More / 2.3.1
Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More v2.3.1
2.3.4 2.3.3 2.3.2 2.3.1 2.3.0 2.2.2 2.2.1 2.2.0 2.1.2 2.1.1 trunk 0.0.1 0.0.2 0.0.3 0.0.4 0.0.5 0.0.6 0.0.7 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 All 66 releases
better-payment / includes / AI / Providers / OpenAIProvider.php

OpenAIProvider.php in Better Payment – Instant Payments, Donations, Fundraising with Subscriptions & More 2.3.1, at includes/AI/Providers/OpenAIProvider.php

154 lines 4.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Better_Payment\Lite\AI\Providers;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 /**
10 * OpenAI provider (Chat Completions + Images).
11 *
12 * Also the base for any OpenAI-compatible API (see {@see OpenRouterProvider}),
13 * so the endpoint and auth headers are overridable.
14 */
15 class OpenAIProvider extends AbstractProvider {
16
17 public function get_id(): string {
18 return 'openai';
19 }
20
21 public function get_label(): string {
22 return 'OpenAI';
23 }
24
25 public function get_models(): array {
26 return [ 'gpt-4o', 'gpt-4o-mini', 'gpt-4.1', 'gpt-4.1-mini' ];
27 }
28
29 public function supports_images(): bool {
30 return true;
31 }
32
33 protected function chat_endpoint(): string {
34 return 'https://api.openai.com/v1/chat/completions';
35 }
36
37 protected function images_endpoint(): string {
38 return 'https://api.openai.com/v1/images/generations';
39 }
40
41 /**
42 * @return array<string, string>
43 */
44 protected function auth_headers(): array {
45 return [ 'Authorization' => 'Bearer ' . sanitize_text_field( (string) $this->config['api_key'] ) ];
46 }
47
48 public function chat( array $messages, array $options = [] ): array {
49 list( $system, $turns ) = self::split_system( $messages, (string) ( $options['system'] ?? '' ) );
50
51 $payload_messages = [];
52 if ( '' !== $system ) {
53 $payload_messages[] = [ 'role' => 'system', 'content' => $system ];
54 }
55 foreach ( $turns as $turn ) {
56 $payload_messages[] = $turn;
57 }
58
59 $body = [
60 'model' => $this->resolve_model( $options ),
61 'messages' => $payload_messages,
62 'temperature' => $this->temperature( $options ),
63 'max_tokens' => $this->max_tokens( $options ),
64 ];
65
66 if ( ! empty( $options['tools'] ) ) {
67 $body['tools'] = $this->format_tools( $options['tools'] );
68 $body['tool_choice'] = 'required' === ( $options['tool_choice'] ?? '' ) ? 'required' : 'auto';
69 }
70
71 $result = $this->post_json( $this->chat_endpoint(), $this->auth_headers(), $body );
72 if ( ! $result['ok'] ) {
73 return [ 'text' => '', 'tool_calls' => [], 'usage' => [], 'error' => $result['error'], 'raw' => $result['data'] ];
74 }
75
76 return $this->normalize_chat( $result['data'] );
77 }
78
79 /**
80 * Map provider-agnostic tool descriptors to OpenAI function tools.
81 *
82 * @param array $tools
83 * @return array
84 */
85 protected function format_tools( array $tools ): array {
86 $out = [];
87 foreach ( $tools as $tool ) {
88 $out[] = [
89 'type' => 'function',
90 'function' => [
91 'name' => $tool['name'],
92 'description' => $tool['description'] ?? '',
93 'parameters' => self::params_to_json_schema( $tool['params'] ?? [] ),
94 ],
95 ];
96 }
97 return $out;
98 }
99
100 /**
101 * @param mixed $data
102 * @return array
103 */
104 protected function normalize_chat( $data ): array {
105 $message = $data['choices'][0]['message'] ?? [];
106 $text = (string) ( $message['content'] ?? '' );
107 $tool_calls = [];
108
109 foreach ( (array) ( $message['tool_calls'] ?? [] ) as $call ) {
110 if ( ! isset( $call['function']['name'] ) ) {
111 continue;
112 }
113 $tool_calls[] = [
114 'name' => (string) $call['function']['name'],
115 'arguments' => self::decode_arguments( $call['function']['arguments'] ?? [] ),
116 ];
117 }
118
119 return [
120 'text' => $text,
121 'tool_calls' => $tool_calls,
122 'usage' => $data['usage'] ?? [],
123 'error' => null,
124 'raw' => $data,
125 ];
126 }
127
128 public function generate_image( string $prompt, array $options = [] ): array {
129 $model = ! empty( $options['model'] ) ? (string) $options['model']
130 : ( ! empty( $this->config['image_model'] ) ? (string) $this->config['image_model'] : 'gpt-image-1' );
131
132 $body = [
133 'model' => $model,
134 'prompt' => $prompt,
135 'size' => (string) ( $options['size'] ?? '1024x1024' ),
136 'n' => 1,
137 ];
138
139 $result = $this->post_json( $this->images_endpoint(), $this->auth_headers(), $body, 120 );
140 if ( ! $result['ok'] ) {
141 return [ 'error' => $result['error'] ];
142 }
143
144 $item = $result['data']['data'][0] ?? [];
145 if ( ! empty( $item['b64_json'] ) ) {
146 return [ 'b64' => (string) $item['b64_json'], 'mime' => 'image/png', 'error' => null ];
147 }
148 if ( ! empty( $item['url'] ) ) {
149 return [ 'url' => (string) $item['url'], 'error' => null ];
150 }
151 return [ 'error' => __( 'Image provider returned no image.', 'better-payment' ) ];
152 }
153 }
154