PluginProbe
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot / 4.9.4
BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot v4.9.4
4.9.4 4.9.3 4.9.2 4.9.1 4.9.0 4.8.2 4.8.1 4.8.0 4.7.0 4.6.2 4.6.1 4.6.0 4.5.6 4.5.5 4.5.4 4.5.3 4.5.2 4.5.1 4.5.0 4.4.1 4.4.0 3.3.4 3.4.0 3.4.1 3.4.2 All 202 releases
betterdocs / includes / AI / Providers / OpenAIProvider.php

OpenAIProvider.php in BetterDocs – AI Documentation, Knowledge Base, MCP Server, Docs, Wikis, FAQ & Chatbot 4.9.4, at includes/AI/Providers/OpenAIProvider.php

125 lines 4.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace WPDeveloper\BetterDocs\AI\Providers;
4
5 use WPDeveloper\BetterDocs\Utils\AIHelper;
6
7 /**
8 * OpenAI Chat Completions provider.
9 *
10 * Adds the GPT-5 family handling on top of the shared OpenAI-compatible base:
11 * GPT-5 models require `max_completion_tokens`, reject a custom temperature, and
12 * bill internal reasoning against the output budget — so we send a low
13 * `reasoning_effort` to avoid empty completions. The original GPT-5 generation
14 * accepts `minimal`; the gpt-5.x point releases (e.g. gpt-5.5) dropped it and
15 * need `none` instead (see default_reasoning_effort()).
16 *
17 * @since 4.4.0
18 */
19 class OpenAIProvider extends OpenAICompatibleProvider {
20
21 public function id() {
22 return 'openai';
23 }
24
25 public function label() {
26 return 'OpenAI';
27 }
28
29 protected function base_url() {
30 return 'https://api.openai.com/v1';
31 }
32
33 /**
34 * {@inheritDoc}
35 */
36 protected function build_payload( $model, $messages, $max_tokens, $temperature = null ) {
37 if ( 0 === strpos( (string) $model, 'gpt-5' ) ) {
38 return array(
39 'model' => $model,
40 'messages' => $messages,
41 'max_completion_tokens' => (int) $max_tokens,
42 'reasoning_effort' => apply_filters( 'betterdocs_openai_gpt5_reasoning_effort', $this->default_reasoning_effort( $model ), $model, $max_tokens ),
43 );
44 }
45
46 return parent::build_payload( $model, $messages, $max_tokens, $temperature );
47 }
48
49 /**
50 * Transcribe audio or video via OpenAI's speech-to-text endpoint.
51 *
52 * The endpoint accepts video containers (mp4, webm, mpeg) as well as audio
53 * and reads the audio track out of them, which is why this feature needs no
54 * ffmpeg on the host — something no WordPress host can be assumed to have.
55 *
56 * `response_format=text` returns the transcript as a bare string rather than
57 * JSON; we ask for `json` instead so a provider error still decodes into the
58 * usual `{ error: { message } }` shape that post_multipart() can report.
59 *
60 * @param array $file `[ 'path', 'filename', 'mime' ]`
61 * @param array $options `[ 'model', 'timeout' ]`
62 * @return string|\WP_Error
63 */
64 public function transcribe( $file, $options = array() ) {
65 if ( empty( $this->api_key ) ) {
66 return new \WP_Error( 'no_api_key', sprintf(
67 /* translators: %s: provider label */
68 __( '%s API key is not configured.', 'betterdocs' ),
69 $this->label()
70 ) );
71 }
72
73 $model = ! empty( $options['model'] ) ? (string) $options['model'] : 'gpt-4o-mini-transcribe';
74 $timeout = isset( $options['timeout'] ) ? (int) $options['timeout'] : 120;
75 $status = null;
76
77 $data = $this->post_multipart(
78 $this->base_url() . '/audio/transcriptions',
79 array( 'Authorization' => 'Bearer ' . $this->api_key ),
80 array(
81 'model' => $model,
82 'response_format' => 'json',
83 ),
84 array(
85 'name' => 'file',
86 'filename' => $file['filename'],
87 'type' => $file['mime'],
88 'path' => $file['path'],
89 ),
90 $timeout,
91 $status
92 );
93
94 if ( is_wp_error( $data ) ) {
95 return $data;
96 }
97
98 if ( isset( $data['error'] ) ) {
99 $message = isset( $data['error']['message'] ) ? $data['error']['message'] : __( 'Unknown error.', 'betterdocs' );
100 return new \WP_Error( 'provider_error', $this->classify_http_error( $status, $message, $model ) );
101 }
102
103 return isset( $data['text'] ) ? (string) $data['text'] : '';
104 }
105
106 /**
107 * Default reasoning_effort for a gpt-5* model.
108 *
109 * The original GPT-5 generation (gpt-5, gpt-5-mini, gpt-5-nano) accepts
110 * 'minimal'. The gpt-5.x point releases (e.g. gpt-5.5) dropped 'minimal'
111 * from the API and only accept none|low|medium|high; sending 'minimal'
112 * returns a 400 "Unsupported value: 'reasoning_effort'". For those we
113 * default to 'none' — no reasoning tokens, the fastest option, leaving the
114 * whole token budget for visible output (closest to gpt-5 'minimal'
115 * behaviour). Override per model via the
116 * betterdocs_openai_gpt5_reasoning_effort filter.
117 *
118 * @param string $model OpenAI model identifier.
119 * @return string reasoning_effort value.
120 */
121 protected function default_reasoning_effort( $model ) {
122 return AIHelper::is_gpt5_point_release( $model ) ? 'none' : 'minimal';
123 }
124 }
125