From c06b275c0f58c61066ea50a8ac1292fa1ce996cb Mon Sep 17 00:00:00 2001 From: Alexander Walther Date: Mon, 6 Jan 2025 01:18:32 +0100 Subject: [PATCH 1/2] Neue Felder: OpenAI-Prompt und OpenAI-Spellcheck --- lib/yform/value/openai_prompt.php | 136 ++++++++++++++++++++++++++ lib/yform/value/openai_spellcheck.php | 106 ++++++++++++++++++++ 2 files changed, 242 insertions(+) create mode 100644 lib/yform/value/openai_prompt.php create mode 100644 lib/yform/value/openai_spellcheck.php diff --git a/lib/yform/value/openai_prompt.php b/lib/yform/value/openai_prompt.php new file mode 100644 index 0000000..0c7bd8e --- /dev/null +++ b/lib/yform/value/openai_prompt.php @@ -0,0 +1,136 @@ +needsOutput()) { + $this->params['form_output'][$this->getId()] = $this->parse('value.textarea.tpl.php'); + } + } + + public function postFormAction(): void + { + $apiKey = $this->getElement(3); + $fields = $this->getElement(4); + $targetField = $this->getElement(5); + $overwrite = (bool) $this->getElement(6); + $systemMessage = $this->getElement(7); + + $model = $this->getElement(8) ?? 'gpt-4o-mini'; + $maxTokens = $this->getElement(9) ?? 4000; + $temperature = $this->getElement(10) ?? 0.5; + $topP = $this->getElement(11) ?? 1.0; + $frequencyPenalty = $this->getElement(12) ?? 0.0; + $presencePenalty = $this->getElement(13) ?? 0.0; + + + + if (!isset($this->params['value_pool']['sql'][$targetField])) { + dump('Zielfeld ' .$targetField. ' nicht gefunden'); + return; + } + + + if (!$overwrite && isset($this->params['value_pool']['sql'][$targetField]) && !empty($this->params['value_pool']['sql'][$targetField])) { + dump('Zielfeld ' . $targetField . ' bereits befüllt'); + return; + } + + $fieldValues = []; + foreach (explode(',', $fields) as $field) { + if (isset($this->params['value_pool']['email'][$field])) { + $fieldValues[$field] = $this->params['value_pool']['email'][$field]; + } + } + + $responseText = self::getChatGPTResponse($fieldValues, $apiKey, $systemMessage, $model, $maxTokens, $temperature, $topP, $frequencyPenalty, $presencePenalty); + $this->params['value_pool']['sql'][$targetField] = $responseText; + $this->params['value_pool']['email'][$targetField] = $responseText; + } + + public static function getChatGPTResponse($fieldValues, $apiKey, $systemMessage, $model, $maxTokens, $temperature, $topP, $frequencyPenalty, $presencePenalty) + { + $apiUrl = "https://api.openai.com/v1/chat/completions"; + + $userMessage = ''; + foreach ($fieldValues as $value) { + $userMessage .= "$value\n"; + } + + $data = [ + 'model' => $model, + 'messages' => [ + [ + 'role' => 'system', + 'content' => $systemMessage + ], + [ + 'role' => 'user', + 'content' => $userMessage + ], + ], + 'max_tokens' => (int) $maxTokens, + 'temperature' => (float) $temperature, + 'top_p' => (float) $topP, + 'frequency_penalty' => (float) $frequencyPenalty, + 'presence_penalty' => (float) $presencePenalty + ]; + + $content = json_encode($data, JSON_UNESCAPED_UNICODE); + + try { + $socket = rex_socket::factoryUrl($apiUrl); + $socket->addHeader('Content-Type', 'application/json; charset=UTF-8'); + $socket->addHeader('Authorization', 'Bearer ' . $apiKey); + $socket->addHeader('OpenAI-Organization', ''); + $socket->addHeader('OpenAI-Project', ''); + + $response = $socket->doPost($content); + + if (!$response->isOk()) { + dump($content, $socket, $response->getHeader()); + return false; + } + + $result = $response->getBody(); + $response = json_decode($result, true); + + return $response['choices'][0]['message']['content']; + } catch (rex_socket_exception $e) { + dump("Fehler bei der API-Anfrage: " . $e->getMessage()); + return ''; + } + } + + public function getDescription(): string + { + return 'value|openai_prompt|name|label|api_key|fields|target_field|overwrite|prompt_text|system_message|user_message_template'; + } + + public function getDefinitions(): array + { + + return [ + 'type' => 'value', + 'name' => 'openai_prompt', + 'values' => [ + 'name' => ['type' => 'name', 'label' => rex_i18n::msg('yform_values_defaults_name') ?? ''], + 'label' => ['type' => 'text', 'label' => rex_i18n::msg('yform_values_defaults_label') ?? ''], + 'api_key' => ['type' => 'text', 'label' => 'OpenAI API Key'], + 'fields' => ['type' => 'text', 'label' => 'Datenbank-Tabellenfelder'], + 'target_field' => ['type' => 'text', 'label' => 'Zielfeld'], + 'overwrite' => ['type' => 'checkbox', 'label' => 'Überschreiben, wenn Zielfeld bereits befüllt ist'], + 'system_message' => ['type' => 'textarea', 'label' => 'Systemnachricht'], + 'model' => ['type' => 'text', 'label' => 'Modell'], + 'max_tokens' => ['type' => 'text', 'label' => 'Maximale Tokenanzahl'], + 'temperature' => ['type' => 'text', 'label' => 'Temperatur'], + 'top_p' => ['type' => 'text', 'label' => 'Top P'], + 'frequency_penalty' => ['type' => 'text', 'label' => 'Frequenzstrafe'], + 'presence_penalty' => ['type' => 'text', 'label' => 'Präsenzstrafe'], + ], + 'description' => 'Erweitert den Inhalt eines Feldes mithilfe der OpenAI API.', + 'db_type' => ['text', 'mediumtext'], + ]; + } +} diff --git a/lib/yform/value/openai_spellcheck.php b/lib/yform/value/openai_spellcheck.php new file mode 100644 index 0000000..6c8f01c --- /dev/null +++ b/lib/yform/value/openai_spellcheck.php @@ -0,0 +1,106 @@ +needsOutput()) { + $this->params['form_output'][$this->getId()] = $this->parse('value.textarea.tpl.php'); + } + } + + public function postFormAction(): void + { + $apiKey = $this->getElement(3); + $field = $this->getElement(4); + + if ($this->getParam('send') == 0) { + return; + }; + + + if (!isset($this->params['value_pool']['sql'][$field])) { + return; + } + + $fieldValue = $this->params['value_pool']['sql'][$field]; + + $responseText = self::getChatGPTResponse($fieldValue, $apiKey, $this->systemMessage, $this->apiUrl); + $this->params['value_pool']['sql'][$field] = $responseText; + } + + public static function getChatGPTResponse($fieldValue, $apiKey, $systemMessage, $apiUrl) + { + $userMessage = "Text: \"$fieldValue\""; + + $data = [ + 'model' => 'gpt-4', + 'messages' => [ + [ + 'role' => 'user', + 'content' => $userMessage + ], + [ + 'role' => 'system', + 'content' => $systemMessage + ], + ], + 'max_tokens' => 4444, + 'temperature' => 0.3, + 'top_p' => 0.95, + 'frequency_penalty' => 0.0, + 'presence_penalty' => 0.0 + ]; + + $content = json_encode($data, JSON_UNESCAPED_UNICODE); + + try { + $socket = rex_socket::factoryUrl($apiUrl); + $socket->addHeader('Content-Type', 'application/json; charset=UTF-8'); + $socket->addHeader('Authorization', 'Bearer ' . $apiKey); + + $response = $socket->doPost($content); + + if (!$response->isOk()) { + dump($response, "Fehler bei der API-Anfrage."); + } + + $result = $response->getBody(); + $response = json_decode($result, true); + + if (isset($response['choices'][0]['message']['content'])) { + return $response['choices'][0]['message']['content']; + } else { + dump("Fehler: Keine gültige Antwort von der API erhalten.", $response); + return ''; + } + } catch (rex_socket_exception $e) { + dump("Fehler bei der API-Anfrage: " . $e->getMessage()); + return ''; + } + } + + public function getDescription(): string + { + return 'value|openai_spellcheck|name|label|api_key|field'; + } + + public function getDefinitions(): array + { + return [ + 'type' => 'value', + 'name' => 'openai_spellcheck', + 'values' => [ + 'name' => ['type' => 'name', 'label' => rex_i18n::msg('yform_values_defaults_name') ?? ''], + 'label' => ['type' => 'text', 'label' => rex_i18n::msg('yform_values_defaults_label') ?? ''], + 'api_key' => ['type' => 'text', 'label' => 'OpenAI API Key'], + 'field' => ['type' => 'text', 'label' => 'Datenbank-Tabellenfeld'] + ], + 'description' => 'Automatische Rechtschreib- und Grammatikprüfung mithilfe der OpenAI API.', + 'db_type' => ['text', 'mediumtext'], + ]; + } +} From 91ea4dc09339bfdc0927623e8801b0670139266a Mon Sep 17 00:00:00 2001 From: alxndr-w Date: Mon, 6 Jan 2025 00:22:12 +0000 Subject: [PATCH 2/2] Apply php-cs-fixer changes --- lib/yform/value/number_lat.php | 1 - lib/yform/value/number_lng.php | 1 - lib/yform/value/openai_prompt.php | 16 ++++++---------- lib/yform/value/openai_spellcheck.php | 26 ++++++++++++-------------- lib/yform/value/seo_title.php | 3 +-- lib/yform/value/showvalue_extended.php | 1 - 6 files changed, 19 insertions(+), 29 deletions(-) diff --git a/lib/yform/value/number_lat.php b/lib/yform/value/number_lat.php index b387dbb..8fb9598 100644 --- a/lib/yform/value/number_lat.php +++ b/lib/yform/value/number_lat.php @@ -2,7 +2,6 @@ class rex_yform_value_number_lat extends rex_yform_value_number { - public function getDescription(): string { return 'number_lat|name|label|precision|scale|defaultwert|[no_db]|[unit]|[notice]|[attributes]'; diff --git a/lib/yform/value/number_lng.php b/lib/yform/value/number_lng.php index 16adbdb..f14acd3 100644 --- a/lib/yform/value/number_lng.php +++ b/lib/yform/value/number_lng.php @@ -2,7 +2,6 @@ class rex_yform_value_number_lng extends rex_yform_value_number { - public function getDescription(): string { return 'number_lng|name|label|precision|scale|defaultwert|[no_db]|[unit]|[notice]|[attributes]'; diff --git a/lib/yform/value/openai_prompt.php b/lib/yform/value/openai_prompt.php index 0c7bd8e..88a2f14 100644 --- a/lib/yform/value/openai_prompt.php +++ b/lib/yform/value/openai_prompt.php @@ -24,14 +24,11 @@ public function postFormAction(): void $frequencyPenalty = $this->getElement(12) ?? 0.0; $presencePenalty = $this->getElement(13) ?? 0.0; - - if (!isset($this->params['value_pool']['sql'][$targetField])) { - dump('Zielfeld ' .$targetField. ' nicht gefunden'); + dump('Zielfeld ' . $targetField . ' nicht gefunden'); return; } - if (!$overwrite && isset($this->params['value_pool']['sql'][$targetField]) && !empty($this->params['value_pool']['sql'][$targetField])) { dump('Zielfeld ' . $targetField . ' bereits befüllt'); return; @@ -51,7 +48,7 @@ public function postFormAction(): void public static function getChatGPTResponse($fieldValues, $apiKey, $systemMessage, $model, $maxTokens, $temperature, $topP, $frequencyPenalty, $presencePenalty) { - $apiUrl = "https://api.openai.com/v1/chat/completions"; + $apiUrl = 'https://api.openai.com/v1/chat/completions'; $userMessage = ''; foreach ($fieldValues as $value) { @@ -63,18 +60,18 @@ public static function getChatGPTResponse($fieldValues, $apiKey, $systemMessage, 'messages' => [ [ 'role' => 'system', - 'content' => $systemMessage + 'content' => $systemMessage, ], [ 'role' => 'user', - 'content' => $userMessage + 'content' => $userMessage, ], ], 'max_tokens' => (int) $maxTokens, 'temperature' => (float) $temperature, 'top_p' => (float) $topP, 'frequency_penalty' => (float) $frequencyPenalty, - 'presence_penalty' => (float) $presencePenalty + 'presence_penalty' => (float) $presencePenalty, ]; $content = json_encode($data, JSON_UNESCAPED_UNICODE); @@ -98,7 +95,7 @@ public static function getChatGPTResponse($fieldValues, $apiKey, $systemMessage, return $response['choices'][0]['message']['content']; } catch (rex_socket_exception $e) { - dump("Fehler bei der API-Anfrage: " . $e->getMessage()); + dump('Fehler bei der API-Anfrage: ' . $e->getMessage()); return ''; } } @@ -110,7 +107,6 @@ public function getDescription(): string public function getDefinitions(): array { - return [ 'type' => 'value', 'name' => 'openai_prompt', diff --git a/lib/yform/value/openai_spellcheck.php b/lib/yform/value/openai_spellcheck.php index 6c8f01c..5f2fa3d 100644 --- a/lib/yform/value/openai_spellcheck.php +++ b/lib/yform/value/openai_spellcheck.php @@ -2,8 +2,8 @@ class rex_yform_value_openai_spellcheck extends rex_yform_value_abstract { - private $systemMessage = "Bitte überprüfe den folgenden Text auf Rechtschreibung und Grammatikfehler. Korrigiere den Text. Achte darauf, dass das Format des Textes (HTML, Markdown oder reiner Text) beibehalten wird. Wenn der Text in HTML ist, gib korrekt formatiertes HTML zurück. Wenn der Text in Markdown ist, gib korrekt formatiertes Markdown zurück. Wenn der Text weder HTML noch Markdown ist, gib korrekt formatierten Plaintext zurück. Fehler sind auch falsche Zeilenumbrüche mitten im Satz, falsche Sonderzeichen oder Trennzeichen mitten im Wort und falsche Formatierung von Listen. Formatiere Listen in eigenen Absätzen beziehungsweise Listenpunkten. Auf keinen Fall darf der Text inhaltlich verändert werden. Deine Antwort darf nur den Text enthalten, der korrigiert wurde. Wenn du keine Fehler findest, gib den Text dennoch vollständig und unverändert zurück."; - private $apiUrl = "https://api.openai.com/v1/chat/completions"; + private $systemMessage = 'Bitte überprüfe den folgenden Text auf Rechtschreibung und Grammatikfehler. Korrigiere den Text. Achte darauf, dass das Format des Textes (HTML, Markdown oder reiner Text) beibehalten wird. Wenn der Text in HTML ist, gib korrekt formatiertes HTML zurück. Wenn der Text in Markdown ist, gib korrekt formatiertes Markdown zurück. Wenn der Text weder HTML noch Markdown ist, gib korrekt formatierten Plaintext zurück. Fehler sind auch falsche Zeilenumbrüche mitten im Satz, falsche Sonderzeichen oder Trennzeichen mitten im Wort und falsche Formatierung von Listen. Formatiere Listen in eigenen Absätzen beziehungsweise Listenpunkten. Auf keinen Fall darf der Text inhaltlich verändert werden. Deine Antwort darf nur den Text enthalten, der korrigiert wurde. Wenn du keine Fehler findest, gib den Text dennoch vollständig und unverändert zurück.'; + private $apiUrl = 'https://api.openai.com/v1/chat/completions'; public function enterObject() { @@ -17,10 +17,9 @@ public function postFormAction(): void $apiKey = $this->getElement(3); $field = $this->getElement(4); - if ($this->getParam('send') == 0) { + if (0 == $this->getParam('send')) { return; - }; - + } if (!isset($this->params['value_pool']['sql'][$field])) { return; @@ -41,18 +40,18 @@ public static function getChatGPTResponse($fieldValue, $apiKey, $systemMessage, 'messages' => [ [ 'role' => 'user', - 'content' => $userMessage + 'content' => $userMessage, ], [ 'role' => 'system', - 'content' => $systemMessage + 'content' => $systemMessage, ], ], 'max_tokens' => 4444, 'temperature' => 0.3, 'top_p' => 0.95, 'frequency_penalty' => 0.0, - 'presence_penalty' => 0.0 + 'presence_penalty' => 0.0, ]; $content = json_encode($data, JSON_UNESCAPED_UNICODE); @@ -65,7 +64,7 @@ public static function getChatGPTResponse($fieldValue, $apiKey, $systemMessage, $response = $socket->doPost($content); if (!$response->isOk()) { - dump($response, "Fehler bei der API-Anfrage."); + dump($response, 'Fehler bei der API-Anfrage.'); } $result = $response->getBody(); @@ -73,12 +72,11 @@ public static function getChatGPTResponse($fieldValue, $apiKey, $systemMessage, if (isset($response['choices'][0]['message']['content'])) { return $response['choices'][0]['message']['content']; - } else { - dump("Fehler: Keine gültige Antwort von der API erhalten.", $response); - return ''; } + dump('Fehler: Keine gültige Antwort von der API erhalten.', $response); + return ''; } catch (rex_socket_exception $e) { - dump("Fehler bei der API-Anfrage: " . $e->getMessage()); + dump('Fehler bei der API-Anfrage: ' . $e->getMessage()); return ''; } } @@ -97,7 +95,7 @@ public function getDefinitions(): array 'name' => ['type' => 'name', 'label' => rex_i18n::msg('yform_values_defaults_name') ?? ''], 'label' => ['type' => 'text', 'label' => rex_i18n::msg('yform_values_defaults_label') ?? ''], 'api_key' => ['type' => 'text', 'label' => 'OpenAI API Key'], - 'field' => ['type' => 'text', 'label' => 'Datenbank-Tabellenfeld'] + 'field' => ['type' => 'text', 'label' => 'Datenbank-Tabellenfeld'], ], 'description' => 'Automatische Rechtschreib- und Grammatikprüfung mithilfe der OpenAI API.', 'db_type' => ['text', 'mediumtext'], diff --git a/lib/yform/value/seo_title.php b/lib/yform/value/seo_title.php index b733638..ee3cb4a 100644 --- a/lib/yform/value/seo_title.php +++ b/lib/yform/value/seo_title.php @@ -26,9 +26,8 @@ public function postFormAction(): void if (isset($this->params['value_pool']['sql'][$name])) { $value .= ' ' . $this->params['value_pool']['sql'][$name]; continue; - } else { - $value .= trim($name, '"\''); } + $value .= trim($name, '"\''); $name = explode('.', $name); if (count($name) > 1) { diff --git a/lib/yform/value/showvalue_extended.php b/lib/yform/value/showvalue_extended.php index 43ba4e7..b7fe7b0 100644 --- a/lib/yform/value/showvalue_extended.php +++ b/lib/yform/value/showvalue_extended.php @@ -22,5 +22,4 @@ public function getDefinitions(): array 'db_type' => ['text', 'varchar(191)', 'mediumtext', 'longtext'], ]; } - }