Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

更新教师查看学生详情页面 #20

Merged
merged 3 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v2.7.3
v2.7.4
1 change: 1 addition & 0 deletions docs/init-dev-mac.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ docker run \
--name=hznuoj \
--restart=always \
-p 8877:80 \
-p 11434:11434 \
-v /var/hznuoj/data:/var/hznuoj/data \
-v "$PROJECT_DIR/hznuoj/web:/var/www/web" \
hznuoj/hznuoj:latest
Expand Down
65 changes: 65 additions & 0 deletions web/OJ/chat.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

// 设置时区为东八区
date_default_timezone_set('PRC');

// 这行代码用于关闭输出缓冲。关闭后,脚本的输出将立即发送到浏览器,而不是等待缓冲区填满或脚本执行完毕。
ini_set('output_buffering', 'off');

// 这行代码禁用了 zlib 压缩。通常情况下,启用 zlib 压缩可以减小发送到浏览器的数据量,但对于服务器发送事件来说,实时性更重要,因此需要禁用压缩。
ini_set('zlib.output_compression', false);

// 这行代码使用循环来清空所有当前激活的输出缓冲区。ob_end_flush() 函数会刷新并关闭最内层的输出缓冲区,@ 符号用于抑制可能出现的错误或警告。
while (@ob_end_flush()) {
}

// 这行代码设置 HTTP 响应的 Content-Type 为 text/event-stream,这是服务器发送事件(SSE)的 MIME 类型。
header('Content-Type: text/event-stream');

// 这行代码设置 HTTP 响应的 Cache-Control 为 no-cache,告诉浏览器不要缓存此响应。
header('Cache-Control: no-cache');

// 这行代码设置 HTTP 响应的自定义头部 X-Accel-Buffering 为 no,用于禁用某些代理或 Web 服务器(如 Nginx)的缓冲。
header('X-Accel-Buffering: no');

require_once './include/static.php';

// 引入敏感词检测类
require './class/Class.DFA.php';

// 引入流处理类
require './class/Class.StreamHandler.php';

// 引入调用 OpenAI 接口类
require './class/Class.ChatGPT.php';

echo 'data: ' . json_encode(['time' => date('Y-m-d H:i:s'), 'content' => '']) . PHP_EOL . PHP_EOL;
flush();

$question = urldecode($_GET['q'] ?? '');
if (empty($question)) {
echo "event: close" . PHP_EOL;
echo "data: Connection closed" . PHP_EOL . PHP_EOL;
flush();
exit();
}
$question = str_ireplace('{[$add$]}', '+', $question);

// api 和 模型选择
$chat = new OllamaChat(
"http://$DB_HOST:11434/api/generate",
"$AI_MODEL"
);

$DOCUMENT_ROOT = $_SERVER['DOCUMENT_ROOT'];
$dfa = new DFA([
'words_file' => "$DOCUMENT_ROOT/OJ/plugins/code-helper/dict.txt",
]);
$chat->set_dfa($dfa);


// 开始提问
$chat->qa([
'system' => '你是HznuOnlineJudge的智能代码助手,只负责和代码相关的问题',
'question' => $question,
]);
104 changes: 104 additions & 0 deletions web/OJ/class/Class.ChatGPT.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

class OllamaChat
{
private $api_url = '';
private $streamHandler;
private $question;
private $dfa = NULL;
private $check_sensitive = TRUE;
private $model = '';

public function __construct($url, $model)
{
$this->api_url = $url;
$this->model = $model;
}

public function set_dfa(&$dfa)
{
$this->dfa = $dfa;
if (!empty($this->dfa) && $this->dfa->is_available()) {
$this->check_sensitive = TRUE;
}
}

public function qa($params)
{

$this->question = $params['question'];
$this->streamHandler = new StreamHandler([
'qmd5' => md5($this->question . '' . time())
]);
if ($this->check_sensitive) {
$this->streamHandler->set_dfa($this->dfa);
}

// 开启检测且提问包含敏感词
if ($this->check_sensitive && $this->dfa->containsSensitiveWords($this->question)) {
$this->streamHandler->end('您的问题不合适,AI暂时无法回答');
return;
}

// 根据Ollama API的要求构建请求正文
$json = json_encode([
'prompt' => $this->question,
'model' => $this->model,
]);

$headers = array(
"Content-Type: application/json",
);

$this->ollamaApiCall($json, $headers);
}

private function buildCurlCommand($json, $headers)
{
$command = "curl";

// 添加 URL
$command .= " '" . $this->api_url . "'";

// 添加请求头
foreach ($headers as $header) {
$command .= " -H '" . str_replace("'", "\'", $header) . "'";
}

// 添加 POST 数据
if ($json) {
$command .= " -d '" . str_replace("'", "\'", $json) . "'";
}

// 你可以继续添加其他 cURL 选项,如需要

return $command;
}

private function ollamaApiCall($json, $headers)
{ // 修改后的方法名
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->api_url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
// curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 如果不是HTTPS请求,可以注释或删除此行
// curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 如果不是HTTPS请求,可以注释或删除此行
curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $json);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

curl_setopt($ch, CURLOPT_WRITEFUNCTION, [$this->streamHandler, 'callback']);

// $curlCommand = $this->buildCurlCommand($json, $headers);
// echo $curlCommand . PHP_EOL;

$response = curl_exec($ch);

if (curl_errno($ch)) {
file_put_contents('./log/curl.error.log', curl_error($ch) . PHP_EOL . PHP_EOL, FILE_APPEND);
}

curl_close($ch);
}
}
127 changes: 127 additions & 0 deletions web/OJ/class/Class.DFA.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@

<?php

class DFA
{
private $root;
private $words_file;
private $words_count = 0;

public function __construct($params)
{
$this->words_file = $params['words_file'] ?? '';

$this->root = new DFA_Node();

$this->load_words_file();
}

private function load_words_file(){
if(!file_exists($this->words_file)){
echo "words file not found: $this->words_file\n";
return;
}
$lines = file($this->words_file);
foreach ($lines as $line) {
$words = preg_split('/\s+/', trim($line));
foreach ($words as $word) {
$word = trim($word);
if (empty($word)) {
continue;
}
$this->words_count += 1;
$this->addWord($word);
}
}
}

public function is_available(){
return $this->words_count>0;
}

public function addWord($word)
{
$node = $this->root;
for ($i = 0; $i < strlen($word); $i++) {
$char = $word[$i];
if (!isset($node->children[$char])) {
$node->children[$char] = new DFA_Node();
}
$node = $node->children[$char];
}
$node->isEndOfWord = true;
}

public function replaceWords($text)
{
$result = '';
$length = strlen($text);
for ($i = 0; $i < $length;) {
$node = $this->root;
$j = $i;
$lastMatched = -1;
while ($j < $length && isset($node->children[$text[$j]])) {
$node = $node->children[$text[$j]];
if ($node->isEndOfWord) {
$lastMatched = $j;
}
$j++;
}

if ($lastMatched >= 0) {
$result .= '\*\*\*';
$i = $lastMatched + 1;
} else {
$result .= $text[$i];
$i++;
}
}
return $result;
}

public function containsSensitiveWords($text)
{
$length = strlen($text);
for ($i = 0; $i < $length;) {
$node = $this->root;
$j = $i;
while ($j < $length && isset($node->children[$text[$j]])) {
$node = $node->children[$text[$j]];
if ($node->isEndOfWord) {
return true;
}
$j++;
}
$i++;
}
return false;
}
}

class DFA_Node
{
public $isEndOfWord;
public $children;

public function __construct()
{
$this->isEndOfWord = false;
$this->children = [];
}
}



/*
$inputText = "需要检测的句子";
$isContain = $dfa->containsSensitiveWords($inputText);

echo "Original Text: \n" . $inputText . "\n";
echo "isContain: " . json_encode($isContain) . "\n";


$outputText = $dfa->replaceWords($inputText);

echo "Original Text: \n" . $inputText . "\n";
echo "Replaced Text: \n" . $outputText . "\n";
*/
Loading
Loading