Merge pull request 'fix(ai): normalize OpenAI-compatible API base to /v1' (#104) from worktree-frontend-single-zibll-1to1 into main
Reviewed-on: CYL/wordpress-theme-aksrt#104
This commit is contained in:
@@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
<div class="aksrt-ai__form-card">
|
<div class="aksrt-ai__form-card">
|
||||||
<h3>主题 Provider 配置(回退链路)</h3>
|
<h3>主题 Provider 配置(回退链路)</h3>
|
||||||
<p class="aksrt-form-hint">当站点已配置 WordPress AI Connectors 时,生成会优先走核心客户端;以下配置作为兼容回退。</p>
|
<p class="aksrt-form-hint">已配置主题 Provider 时优先走下方接口;未配置时才尝试 WordPress AI Connectors。NewAPI/兼容网关请填到 <code>/v1</code>(也可只填域名,保存时会自动补全)。</p>
|
||||||
<div class="aksrt-form-group">
|
<div class="aksrt-form-group">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" v-model="form.enabled"> 启用主题 AI Provider
|
<input type="checkbox" v-model="form.enabled"> 启用主题 AI Provider
|
||||||
@@ -67,7 +67,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="aksrt-form-group" v-if="form.provider === 'openai'">
|
<div class="aksrt-form-group" v-if="form.provider === 'openai'">
|
||||||
<label>API Base URL</label>
|
<label>API Base URL</label>
|
||||||
<input v-model="form.api_base" type="text" class="aksrt-input" placeholder="https://api.openai.com/v1">
|
<input v-model="form.api_base" type="text" class="aksrt-input" placeholder="https://llm.example.com/v1">
|
||||||
|
<p class="aksrt-form-hint">示例:https://api.openai.com/v1 或 https://你的网关/v1(不要填到 /chat/completions)</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="aksrt-form-group" v-if="form.provider !== 'none'">
|
<div class="aksrt-form-group" v-if="form.provider !== 'none'">
|
||||||
<label>模型</label>
|
<label>模型</label>
|
||||||
@@ -117,7 +118,7 @@
|
|||||||
<h3><i class="fas fa-exclamation-triangle"></i> 重要说明</h3>
|
<h3><i class="fas fa-exclamation-triangle"></i> 重要说明</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li>AI 输出仅作为<strong>建议</strong>,不会自动写库;需用户点「更新/提交」才保存</li>
|
<li>AI 输出仅作为<strong>建议</strong>,不会自动写库;需用户点「更新/提交」才保存</li>
|
||||||
<li>优先使用 WordPress AI Connectors;主题 API Key 为兼容回退</li>
|
<li>主题 API Key 已配置时优先使用;否则回退 WordPress AI Connectors</li>
|
||||||
<li>生成接口要求登录与编辑/投稿权限;配置接口仍需管理员</li>
|
<li>生成接口要求登录与编辑/投稿权限;配置接口仍需管理员</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class AKSRT_AI {
|
|||||||
$merged['enabled'] = !empty($merged['enabled']);
|
$merged['enabled'] = !empty($merged['enabled']);
|
||||||
$merged['provider'] = sanitize_key((string) ($merged['provider'] ?? self::PROVIDER_NONE));
|
$merged['provider'] = sanitize_key((string) ($merged['provider'] ?? self::PROVIDER_NONE));
|
||||||
$merged['api_key'] = (string) ($merged['api_key'] ?? '');
|
$merged['api_key'] = (string) ($merged['api_key'] ?? '');
|
||||||
$merged['api_base'] = esc_url_raw((string) ($merged['api_base'] ?? 'https://api.openai.com/v1'));
|
$merged['api_base'] = $this->normalize_api_base((string) ($merged['api_base'] ?? 'https://api.openai.com/v1'));
|
||||||
$merged['model'] = sanitize_text_field((string) ($merged['model'] ?? 'gpt-3.5-turbo'));
|
$merged['model'] = sanitize_text_field((string) ($merged['model'] ?? 'gpt-3.5-turbo'));
|
||||||
$merged['max_tokens'] = max(100, min(4000, (int) ($merged['max_tokens'] ?? 1000)));
|
$merged['max_tokens'] = max(100, min(4000, (int) ($merged['max_tokens'] ?? 1000)));
|
||||||
$merged['rate_limit'] = max(1, min(100, (int) ($merged['rate_limit'] ?? 10)));
|
$merged['rate_limit'] = max(1, min(100, (int) ($merged['rate_limit'] ?? 10)));
|
||||||
@@ -294,11 +294,40 @@ class AKSRT_AI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize OpenAI-compatible base URL.
|
||||||
|
*
|
||||||
|
* Accepts gateway root (https://host), /v1, trailing slash, or full
|
||||||
|
* .../chat/completions and returns a base that can append /chat/completions.
|
||||||
|
*/
|
||||||
|
public function normalize_api_base(string $api_base): string {
|
||||||
|
$api_base = trim($api_base);
|
||||||
|
if ($api_base === '') {
|
||||||
|
return 'https://api.openai.com/v1';
|
||||||
|
}
|
||||||
|
|
||||||
|
$api_base = esc_url_raw($api_base);
|
||||||
|
$api_base = rtrim($api_base, '/');
|
||||||
|
|
||||||
|
// If user pasted the full chat endpoint, strip the leaf.
|
||||||
|
if (preg_match('#/chat/completions$#i', $api_base)) {
|
||||||
|
$api_base = (string) preg_replace('#/chat/completions$#i', '', $api_base);
|
||||||
|
$api_base = rtrim($api_base, '/');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Common gateways (NewAPI etc.): root host needs /v1.
|
||||||
|
if (!preg_match('#/(v1|openai/v1)$#i', $api_base)) {
|
||||||
|
$api_base .= '/v1';
|
||||||
|
}
|
||||||
|
|
||||||
|
return rtrim($api_base, '/');
|
||||||
|
}
|
||||||
|
|
||||||
private function generate_via_theme(string $prompt, string $system, array $options): array {
|
private function generate_via_theme(string $prompt, string $system, array $options): array {
|
||||||
$config = $this->get_config();
|
$config = $this->get_config();
|
||||||
$max_tokens = (int) ($options['max_tokens'] ?? $config['max_tokens']);
|
$max_tokens = (int) ($options['max_tokens'] ?? $config['max_tokens']);
|
||||||
$temperature = (float) ($options['temperature'] ?? 0.7);
|
$temperature = (float) ($options['temperature'] ?? 0.7);
|
||||||
$api_base = rtrim((string) $config['api_base'], '/');
|
$api_base = $this->normalize_api_base((string) $config['api_base']);
|
||||||
|
|
||||||
$response = wp_remote_post($api_base . '/chat/completions', [
|
$response = wp_remote_post($api_base . '/chat/completions', [
|
||||||
'headers' => [
|
'headers' => [
|
||||||
|
|||||||
Reference in New Issue
Block a user