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:
2026-07-13 08:55:22 +08:00
2 changed files with 35 additions and 5 deletions
@@ -47,7 +47,7 @@
<div class="aksrt-ai__form-card">
<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">
<label>
<input type="checkbox" v-model="form.enabled"> 启用主题 AI Provider
@@ -67,7 +67,8 @@
</div>
<div class="aksrt-form-group" v-if="form.provider === 'openai'">
<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 class="aksrt-form-group" v-if="form.provider !== 'none'">
<label>模型</label>
@@ -117,7 +118,7 @@
<h3><i class="fas fa-exclamation-triangle"></i> 重要说明</h3>
<ul>
<li>AI 输出仅作为<strong>建议</strong>,不会自动写库;需用户点「更新/提交」才保存</li>
<li>优先使用 WordPress AI Connectors;主题 API Key 为兼容回退</li>
<li>主题 API Key 已配置时优先使用;否则回退 WordPress AI Connectors</li>
<li>生成接口要求登录与编辑/投稿权限;配置接口仍需管理员</li>
</ul>
</div>
+31 -2
View File
@@ -62,7 +62,7 @@ class AKSRT_AI {
$merged['enabled'] = !empty($merged['enabled']);
$merged['provider'] = sanitize_key((string) ($merged['provider'] ?? self::PROVIDER_NONE));
$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['max_tokens'] = max(100, min(4000, (int) ($merged['max_tokens'] ?? 1000)));
$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 {
$config = $this->get_config();
$max_tokens = (int) ($options['max_tokens'] ?? $config['max_tokens']);
$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', [
'headers' => [