Files
AI-Chat/app/Controllers/ChatController.php

118 lines
3.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Controllers;
use App\Models\Config;
use App\Services\AIService;
class ChatController
{
public static function completions(): void
{
$input = json_decode(file_get_contents('php://input'), true);
$provider = $input['provider'] ?? '';
$model = $input['model'] ?? '';
$messages = $input['messages'] ?? [];
$stream = $input['stream'] ?? true;
$systemPrompt = $input['systemPrompt'] ?? '';
$thinkingMode = $input['thinkingMode'] ?? false;
if (!$provider || !$model || !$messages) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => '缺少必要参数provider、model、messages']);
return;
}
if (!is_array($messages) || empty($messages)) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => 'messages 必须是非空数组']);
return;
}
// 获取供应商配置
$configRow = Config::getByKey('providers');
if (!$configRow) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => '未找到供应商配置']);
return;
}
$providers = json_decode($configRow['config_value'], true);
if (!is_array($providers)) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => '供应商配置格式错误']);
return;
}
$providerKey = $provider;
if (!isset($providers[$providerKey])) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => '供应商不存在: ' . $providerKey]);
return;
}
$providerConfig = $providers[$providerKey];
if (empty($providerConfig['enabled'])) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => '供应商已禁用: ' . $providerKey]);
return;
}
$models = $providerConfig['models'] ?? [];
if (!empty($models) && !in_array($model, $models)) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => '模型不在供应商支持列表中: ' . $model]);
return;
}
// 非流式模式暂不支持
if (!$stream) {
http_response_code(400);
echo json_encode(['success' => false, 'message' => '当前仅支持流式响应']);
return;
}
// 设置 SSE 响应头
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');
header('X-Accel-Buffering: no');
// 关闭输出缓冲
while (ob_get_level()) {
ob_end_flush();
}
$options = [
'provider' => $providerConfig,
'systemPrompt' => $systemPrompt,
'thinkingMode' => (bool) $thinkingMode
];
try {
AIService::streamChat($providerKey, $model, $messages, $options, function ($chunk, $type = 'content') {
if ($type === 'thinking') {
self::sendSSE(['thinking' => $chunk]);
} else {
self::sendSSE(['content' => $chunk]);
}
});
self::sendSSE('[DONE]');
} catch (\Throwable $e) {
self::sendSSE(['type' => 'error', 'message' => $e->getMessage()]);
}
}
private static function sendSSE($data): void
{
if (is_string($data)) {
echo "data: " . $data . "\n\n";
} else {
echo "data: " . json_encode($data, JSON_UNESCAPED_UNICODE) . "\n\n";
}
flush();
}
}