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 / GeminiProvider.php

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

140 lines 4.7 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 * Google Gemini provider (Generative Language API).
11 *
12 * Auth is a query-string API key (not a bearer header). Chat uses
13 * functionDeclarations for tool calling; images use an Imagen `:predict` call.
14 */
15 class GeminiProvider extends AbstractProvider {
16
17 const BASE = 'https://generativelanguage.googleapis.com/v1beta/models/';
18
19 public function get_id(): string {
20 return 'gemini';
21 }
22
23 public function get_label(): string {
24 return 'Google Gemini';
25 }
26
27 public function get_models(): array {
28 return [ 'gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.0-flash' ];
29 }
30
31 public function supports_images(): bool {
32 return true;
33 }
34
35 public function chat( array $messages, array $options = [] ): array {
36 list( $system, $turns ) = self::split_system( $messages, (string) ( $options['system'] ?? '' ) );
37
38 $contents = [];
39 foreach ( $turns as $turn ) {
40 $contents[] = [
41 'role' => 'assistant' === $turn['role'] ? 'model' : 'user',
42 'parts' => [ [ 'text' => $turn['content'] ] ],
43 ];
44 }
45
46 $body = [
47 'contents' => $contents,
48 'generationConfig' => [
49 'temperature' => $this->temperature( $options ),
50 'maxOutputTokens' => $this->max_tokens( $options ),
51 ],
52 ];
53 if ( '' !== $system ) {
54 $body['systemInstruction'] = [ 'parts' => [ [ 'text' => $system ] ] ];
55 }
56 if ( ! empty( $options['tools'] ) ) {
57 $body['tools'] = [ [ 'functionDeclarations' => $this->format_tools( $options['tools'] ) ] ];
58 if ( 'required' === ( $options['tool_choice'] ?? '' ) ) {
59 $body['toolConfig'] = [ 'functionCallingConfig' => [ 'mode' => 'ANY' ] ];
60 }
61 }
62
63 $url = self::BASE . rawurlencode( $this->resolve_model( $options ) ) . ':generateContent?key=' . rawurlencode( sanitize_text_field( (string) $this->config['api_key'] ) );
64 $result = $this->post_json( $url, [], $body );
65 if ( ! $result['ok'] ) {
66 return [ 'text' => '', 'tool_calls' => [], 'usage' => [], 'error' => $result['error'], 'raw' => $result['data'] ];
67 }
68
69 return $this->normalize_chat( $result['data'] );
70 }
71
72 /**
73 * @param array $tools
74 * @return array
75 */
76 protected function format_tools( array $tools ): array {
77 $out = [];
78 foreach ( $tools as $tool ) {
79 $out[] = [
80 'name' => $tool['name'],
81 'description' => $tool['description'] ?? '',
82 'parameters' => self::params_to_json_schema( $tool['params'] ?? [] ),
83 ];
84 }
85 return $out;
86 }
87
88 /**
89 * @param mixed $data
90 * @return array
91 */
92 protected function normalize_chat( $data ): array {
93 $text = '';
94 $tool_calls = [];
95
96 $parts = $data['candidates'][0]['content']['parts'] ?? [];
97 foreach ( (array) $parts as $part ) {
98 if ( isset( $part['text'] ) ) {
99 $text .= (string) $part['text'];
100 }
101 if ( isset( $part['functionCall']['name'] ) ) {
102 $tool_calls[] = [
103 'name' => (string) $part['functionCall']['name'],
104 'arguments' => self::decode_arguments( $part['functionCall']['args'] ?? [] ),
105 ];
106 }
107 }
108
109 return [
110 'text' => $text,
111 'tool_calls' => $tool_calls,
112 'usage' => $data['usageMetadata'] ?? [],
113 'error' => null,
114 'raw' => $data,
115 ];
116 }
117
118 public function generate_image( string $prompt, array $options = [] ): array {
119 $model = ! empty( $options['model'] ) ? (string) $options['model']
120 : ( ! empty( $this->config['image_model'] ) ? (string) $this->config['image_model'] : 'imagen-3.0-generate-002' );
121
122 $url = self::BASE . rawurlencode( $model ) . ':predict?key=' . rawurlencode( sanitize_text_field( (string) $this->config['api_key'] ) );
123 $body = [
124 'instances' => [ [ 'prompt' => $prompt ] ],
125 'parameters' => [ 'sampleCount' => 1 ],
126 ];
127
128 $result = $this->post_json( $url, [], $body, 120 );
129 if ( ! $result['ok'] ) {
130 return [ 'error' => $result['error'] ];
131 }
132
133 $b64 = $result['data']['predictions'][0]['bytesBase64Encoded'] ?? '';
134 if ( '' !== $b64 ) {
135 return [ 'b64' => (string) $b64, 'mime' => 'image/png', 'error' => null ];
136 }
137 return [ 'error' => __( 'Image provider returned no image.', 'better-payment' ) ];
138 }
139 }
140