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

Neue Felder: OpenAI-Prompt und OpenAI-Spellcheck #94

Merged
merged 2 commits into from
Jan 6, 2025
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
1 change: 0 additions & 1 deletion lib/yform/value/number_lat.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]';
Expand Down
1 change: 0 additions & 1 deletion lib/yform/value/number_lng.php
Original file line number Diff line number Diff line change
Expand Up @@ -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]';
Expand Down
132 changes: 132 additions & 0 deletions lib/yform/value/openai_prompt.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<?php

class rex_yform_value_openai_prompt extends rex_yform_value_abstract
{
public function enterObject()
{
if ($this->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'],
];
}
}
104 changes: 104 additions & 0 deletions lib/yform/value/openai_spellcheck.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

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';

public function enterObject()
{
if ($this->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 (0 == $this->getParam('send')) {
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'];
}
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'],
];
}
}
3 changes: 1 addition & 2 deletions lib/yform/value/seo_title.php
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
1 change: 0 additions & 1 deletion lib/yform/value/showvalue_extended.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,4 @@ public function getDefinitions(): array
'db_type' => ['text', 'varchar(191)', 'mediumtext', 'longtext'],
];
}

}
Loading