/** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Model.php **/ openai_assistant_id ?? FALSE ); if( SYSTEM->domain ) { $domain = "https://" . SYSTEM->domain . "/api/v1/agents/{$agent->id}/tools"; } $tools = $agent->getTools( TRUE ) ; $tools = array_map( fn( $item ) => [ "type" => "function", "function" => [ "name" => "applyTool-{$item->id}", "description" => $item->description, // "strict" => TRUE, "parameters" => (object)json_decode( $item->parameters ?? "{}" ), ], ], $tools ); $prompt = array_filter([ "#Über Dich" => $agent->prompt_personality, "#Allgemeine Informationen" => $agent->prompt_tasks, "#Verhalten bei Unklarheiten" => $agent->prompt_uncertainties, "#Sensible Themen" => $agent->prompt_sensitives, "#Gesprächsablauf" => $agent->prompt_conversation_flow, "#Häufige Fragen" => $agent->prompt_faq, "#Wissensdatenbank" => $agent->prompt_knowledge_base, "# [!important] Wenn Du im Chat-Modus bist\n" => $agent->prompt_chat, "# [!important] Wenn Du im Telefon-Modus bist\n" => $agent->prompt_phone, ]); $prompt = array_map( "trim", $prompt ); $prompt = array_map( fn( $title, $prompt ) => "{$title}\n{$prompt}", array_keys( $prompt ), array_values( $prompt ) ); $prompt = implode( "\n\n---\n\n", $prompt ); $config = [ "model" => "gpt-4o", "name" => $agent->name, "description" => $domain, "instructions" => $prompt, "tools" => $tools, ]; if( $create ) $response = OpenAI\Assistants::create( $config ); else $response = OpenAI\Assistants::modify( $agent->openai_assistant_id, $config ); if( $response ) parent::save([ "id" => $id, "openai_assistant_id" => $response->id ]); else show( OpenAI::getError() ); // show( $create, $response, OpenAI::getError(), $agent, "X" ); } return $id; } public static function delete( array | int | string $value ) : int { $agent = parent::get( $value ); if( parent::delete( $value ) ) { if( class_exists( OpenAI::class ) ) { if( $agent->openai_assistant_id ) { OpenAI\Assistants::delete( $agent->openai_assistant_id ); } } return TRUE; } return FALSE; } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Calls/Model.php **/ Agents\Model::getValue( $agentId, "id" ), "chat_hashid" => $chatHashId, ])); } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Calls/API/v1/Routes.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\Agents\Calls\API\v1; use ARUNOX\Core\Hooks; use ARUNOX\Core\Router; use ARUNOX\Plugins\REST; class Routes { public static function register() : void { /* ### MAIN ROUTES ############################## # */ // Router::add([ Controller::class, "getAll" ]); // Router::add([ Controller::class, "get" ]); // Router::add([ Controller::class, "getTypeAsOptions" ]); // // Router::add([ Controller::class, "create" ]); // Router::add([ Controller::class, "save" ]); // Router::add([ Controller::class, "delete" ]); // Router::add([ Controller::class, "toggleStatus" ]); // // /* ### REST ROUTES ############################## # */ // Router::add([ Controller::class, "restConfigDetails" ]); // Router::add([ Controller::class, "restConfigCreate" ]); // Router::add([ Controller::class, "restConfigDelete" ]); // // Router::add([ Controller::class, "restResponseCreate" ]); // Router::add([ Controller::class, "restResponseSave" ]); // Router::add([ Controller::class, "restResponseDelete" ]); // Router::add([ Controller::class, "restResponseToggleStatus" ]); } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Calls/API/v1/Controller.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\Agents\Calls\API\v1; use ARUNOX\HTTP; use ARUNOX\Plugins\REST; use ARUNOX\Plugins\Agents; use ARUNOX\API\BaseController; class Controller extends BaseController { /* ### VARIABLES ################################################ # */ protected static array $NOTIFY = [ "notfound" => "Call nicht gefunden", "saved" => "Call wurde erfolgreich gespeichert", "deleted" => "Call wurde erfolgreich gelöscht", "created" => "Call wurde erfolgreich erstellt", "statuschanged.0" => "Call wurde erfolgreich deaktiviert", "statuschanged.1" => "Call wurde erfolgreich aktiviert", "notcreated" => "Call konnte nicht erstellt werden", "notsaved" => "Call konnte nicht gespeichert werden", ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /** * @api * @method GET * * @route.path /agents/{id}/calls/{id} * @route.description Listet alle Calls eines Chats auf */ public static function getAllOwn( string $agentHashId, string $chatHashId, array $where = [] ) : array { return Agents\Calls\Model::getByAgent( $agentHashId, $chatHashId, $where ); } /* ### PUBLIC SAVE FUNCTIONS #################################### # */ /** * @api * @method PUT * * @route.path /agents/{id}/tools/{id} * @route.description Aktualisiert die Eigenschaften eines Tools */ public static function save( string $agentHashId, string $chatHashId, array $data ) : HTTP\Response { if( $result = Calls\Tools\Model::get([ "id" => $id, "origin_id" => $origin ]) ) { if( $result->save( $data ) ) { return REST\Response::jsonResponse( 200, static::getNotify("saved") ); } return REST\Response::jsonResponse( 500, static::getNotify("notsaved") ); } return REST\Response::jsonResponse( 404, static::getNotify("notfound") ); } /** * @api * @method POST * * @route.path /agents/{id}/calls/{id} * @route.description Erstellt einen neuen Call für einen Chat */ public static function create( string $agentHashId, string $chatHashId, array $data ) : HTTP\Response { $data[ "origin_id" ] = Agents\Model::getValue( $agentHashId ); $data[ "chat_hashid" ] = $chatHashId; if( $hashid = Agents\Calls\Model::save( $data ) ) { $result = Agents\Calls\Model::get( $hashid ); return REST\Response::jsonResponse( 200, static::getNotify("created"), [ "data" => $result ] ); } return REST\Response::jsonResponse( 500, static::getNotify("notfound") ); } /** * @api * @method DELETE * * @route.path /agents/{id}/calls/{id} * @route.description Löscht ein Tool eines Agenten */ public static function delete( string $agentHashId, string $callHashId ) : HTTP\Response { $agent = Agents\Model::getValue( $agentHashId ); if( Agents\Calls\Model::delete([ "hashid" => $callHashId, "origin_id" => $agent ]) ) { return REST\Response::jsonResponse( 200, static::getNotify("deleted") ); } return REST\Response::jsonResponse( 500, static::getNotify("notfound") ); } /* ### PUBLIC VALIDATE FUNCTIONS ################################ # */ /* ### PUBLIC REST FUNCTIONS #################################### # */ /* ### PUBLIC REST RESPONSE FUNCTIONS ########################### # */ /* ### PUBLIC REST CONFIG FUNCTIONS ############################# # */ /* ### PUBLIC REST TYPE FUNCTIONS ############################### # */ } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Calls/Source.php **/ [ "color" => REST\Generator\Config\Color::Info, "label" => "API", "icon" => "bi bi-code" ], self::Cron => [ "color" => REST\Generator\Config\Color::Info, "label" => "Cron", "icon" => "bi bi-clock" ], self::Phone => [ "color" => REST\Generator\Config\Color::Info, "label" => "Phone", "icon" => "bi bi-telephone" ], self::Admin => [ "color" => REST\Generator\Config\Color::Info, "label" => "Admin", "icon" => "bi bi-shield-lock" ], self::Webchat => [ "color" => REST\Generator\Config\Color::Info, "label" => "Webchat", "icon" => "bi bi-globe" ], self::Backend => [ "color" => REST\Generator\Config\Color::Info, "label" => "Backend", "icon" => "bi bi-cpu" ], ]; /* ### PUBLIC FUNCTIONS ######################################### # */ } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Calls/Type.php **/ [ "color" => REST\Generator\Config\Color::Info, "label" => "Text", "icon" => "bi bi-chat-dots" ], self::Image => [ "color" => REST\Generator\Config\Color::Info, "label" => "Image", "icon" => "bi bi-image" ], self::AudioIn => [ "color" => REST\Generator\Config\Color::Info, "label" => "Audio in", "icon" => "bi bi-mic" ], self::AudioOut => [ "color" => REST\Generator\Config\Color::Info, "label" => "Audio out", "icon" => "bi bi-volume-up" ], self::ToolCall => [ "color" => REST\Generator\Config\Color::Info, "label" => "Tool Call", "icon" => "bi bi-plug" ], self::ToolResult => [ "color" => REST\Generator\Config\Color::Info, "label" => "Tool Result", "icon" => "bi bi-check-circle" ], self::System => [ "color" => REST\Generator\Config\Color::Info, "label" => "System", "icon" => "bi bi-terminal" ], ]; /* ### PUBLIC FUNCTIONS ######################################### # */ } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Calls/Entity.php **/ Status::class, "type" => Type::class, "role" => Role::class, "source" => Source::class, "date_create" => Attribute\DateTime::class, "date_edit" => Attribute\DateTime::class, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function toggleStatus( bool $save = FALSE ) { $this->status->toggle(); if( $save ) $this->save(); } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Calls/Status.php **/ [ "color" => REST\Generator\Config\Color::Default, "label" => "Draft", "icon" => "bi bi-dash-circle" ], self::Waiting => [ "color" => REST\Generator\Config\Color::Info, "label" => "Waiting", "icon" => "bi bi-hourglass-split" ], self::InProgress => [ "color" => REST\Generator\Config\Color::Warning, "label" => "InProgress", "icon" => "bi bi-cpu" ], self::Completed => [ "color" => REST\Generator\Config\Color::Success, "label" => "Completed", "icon" => "bi bi-check-circle" ], self::Incomplete => [ "color" => REST\Generator\Config\Color::Danger, "label" => "Incomplete", "icon" => "bi bi-exclamation-triangle" ], self::Failed => [ "color" => REST\Generator\Config\Color::Danger, "label" => "Error", "icon" => "bi bi-exclamation-triangle" ], self::Skipped => [ "color" => REST\Generator\Config\Color::Info, "label" => "Skipped", "icon" => "bi bi-slash-circle" ], ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /* ### MAGIC FUNCTIONS ########################################## # */ } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/API/v1/Routes.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\Agents\API\v1; use ARUNOX\Core\Hooks; use ARUNOX\Core\Router; use ARUNOX\Plugins\REST; class Routes { public static function register() : void { /* ### MAIN ROUTES ############################## # */ Router::addAll( Controller::class ); /* ### MENU ROUTES ############################## # */ Hooks::add('router::get->api.v1.rest.routes', function( $routes ) { $routes[] = [ "path" => "/agents", "label" => "Assistenten", "icon" => "bi bi-person-gear", "menu" => "content", ]; $routes[] = [ "path" => "/agents/{id}", "label" => "Bearbeiten", ]; return $routes; }); } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/API/v1/Controller.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\Agents\API\v1; use ARUNOX\HTTP; use ARUNOX\Core\Hooks; use ARUNOX\API\OpenAI; use ARUNOX\Plugins\REST; use ARUNOX\Plugins\Users; use ARUNOX\Plugins\Agents; use ARUNOX\API\BaseController; use ARUNOX\Database\BaseEntity; class Controller extends BaseController { /* ### VARIABLES ################################################ # */ protected static array $NOTIFY = [ "notfound" => "Assistent nicht gefunden", "saved" => "Assistent wurde erfolgreich gespeichert", "deleted" => "Assistent wurde erfolgreich gelöscht", "created" => "Assistent wurde erfolgreich erstellt", "statuschanged.0" => "Assistent wurde erfolgreich deaktiviert", "statuschanged.1" => "Assistent wurde erfolgreich aktiviert", "notcreated" => "Assistent konnte nicht erstellt werden", "notsaved" => "Assistent konnte nicht gespeichert werden", ]; /* ### PUBLIC GET FUNCTIONS ##################################### # */ /** * @api * @method GET * * @route.path /agents * @route.description Listet alle Agents */ public static function getAll() : array { return Agents\Model::getAll(); } /** * @api * @method GET * * @route.path /agents/{hashid} * @route.description Gibt einen einzelnen Agent anhand der ID zurück */ public static function get( int|string $hashid ) : BaseEntity | HTTP\Response { if( $result = Agents\Model::get( $hashid ) ) return $result; return REST\Response::autoResponse( 404, static::$NOTIFY["notfound"] ); } /* ### PUBLIC SAVE FUNCTIONS #################################### # */ /** * @api * @method PUT * * @route.path /agents/{hashid} * @route.description Speichert Änderungen an einem bestehenden Agent */ public static function save( int|string $hashid, array $data ) : HTTP\Response { if( $result = Agents\Model::get( $hashid ) ) { if( $result->save( $data ) ) { return REST\Response::autoResponse( 200, static::$NOTIFY["saved"] ); } return REST\Response::autoResponse( 500, static::$NOTIFY["notsaved"] ); } return REST\Response::autoResponse( 404, static::$NOTIFY["notfound"] ); } /** * @api * @method POST * * @route.path /agents * @route.description Erstellt einen neuen Agent */ public static function create( array $data ) : HTTP\Response { if( $hashid = Agents\Model::save( $data ) ) { $result = Agents\Model::get( $hashid ); return REST\Response::autoResponse( 200, static::$NOTIFY["created"], [ "data" => $result ] ); } return REST\Response::autoResponse( 500, static::$NOTIFY["notfound"] ); } /** * @api * @method DELETE * * @route.path /agents/{hashid} * @route.description Löscht einen Agent anhand der ID */ public static function delete( int|string $hashid ) : HTTP\Response { if( Agents\Model::delete( $hashid ) ) { return REST\Response::autoResponse( 200, static::$NOTIFY["deleted"] ); } return REST\Response::autoResponse( 500, static::$NOTIFY["notfound"] ); } /** * @api * @method PATCH * * @route.path /agents/{hashid}/status * @route.description Wechselt den Status (aktiv/inaktiv) eines Agenten */ public static function toggleStatus( int|string $hashid ) : HTTP\Response { if( $result = Agents\Model::get( $hashid ) ) { $result->toggleStatus( TRUE ); return REST\Response::autoResponse( 200, static::$NOTIFY["statuschanged.{$result->status->getValue()}"], [ "data" => $result ] ); } return REST\Response::autoResponse( 404, static::$NOTIFY["notfound"] ); } /* ### PUBLIC VALIDATE FUNCTIONS ################################ # */ /* ### PUBLIC REST FUNCTIONS #################################### # */ /* ### PUBLIC REST RESPONSE FUNCTIONS ########################### # */ /** * @internal rest * @method PUT * * @route.path /agents/{hashid} * @route.description REST-Antwort nach dem Speichern eines Agenten */ public static function restResponseSave( int|string $hashid, array $data ) : HTTP\Response { $result = static::save( $hashid, $data ); if( $result->isStatus( 200 ) ) { $data = Agents\Model::get( $hashid ); REST\Response::closeModals(); REST\Response::addUpdater( [ "data/table/row", $hashid ], $data ); } return static::restResponse( $result ); } /** * @internal rest * @method POST * * @route.path /agents * @route.description REST-Antwort nach dem Erstellen eines Agenten (inkl. Notifikation, Redirect etc.) */ public static function restResponseCreate( array $data ) : HTTP\Response { $result = static::create( $data ); if( $result->isStatus( 200 ) ) { REST\Response::closeModals(); REST\Response::redirect( "/agents/{$result->getBody("data", "hashid")}" ); } return static::restResponse( $result ); } /** * @internal rest * @method DELETE * * @route.path /agents/{hashid} * @route.description REST-Antwort nach dem Löschen eines Agenten */ public static function restResponseDelete( int|string $hashid ) : HTTP\Response { REST\Response::closeModals(); $result = static::delete( $hashid ); if( $result->isStatus( 200 ) ) { REST\Response::redirect( "/agents" ); } return static::restResponse( $result ); } /** * @internal rest * @method PATCH * * @route.path /agents/{hashid}/status * @route.description REST-Antwort zur Status-Umschaltung eines Agenten (z. B. für Notifications) */ public static function restResponseToggleStatus( int|string $hashid ) : HTTP\Response { $result = static::toggleStatus( $hashid ); if( $result->isStatus( 200 ) ) { $data = $result->getBody("data"); REST\Response::addUpdater( [ "data/table/row", $hashid ], $data ); } return static::restResponse( $result ); } /* ### PUBLIC REST CONFIG FUNCTIONS ############################# # */ /** * @internal rest * @method GET * * @route.path /agents * @route.description REST-Konfiguration für Agenten-Tabelle */ public static function restConfigOverview() : REST\Generator\Base\BaseGenerator { return REST\Response::addWrapper( "overview", "Assistenten" ) ->get() ->setApi( "/agents" ) ->setAction( "/agents/{hashid}" ) ->setOrder( "name", "asc" ) ->setSearch( TRUE ) ->addButton(...Hooks::filter( "rest.generator::button.name->create", [], "agents" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->text", [], "name", TRUE, TRUE, TRUE, "agents" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->text", [], "voice", TRUE, TRUE, TRUE, "agents" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->status", [], "status", [], "agents", "hashid" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->text", [], "phone", TRUE, FALSE, TRUE, "agents" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->datetime", [], "date_edit", FALSE, FALSE, TRUE, "agents" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->actions", [], [], FALSE, "agents" ))->get() ->addButton(...Hooks::filter( "rest.generator::action.name->edit", [], "agents", "hashid" )) ->addButton(...Hooks::filter( "rest.generator::action.name->delete", [], "agents", "hashid" )); } /** * @internal rest * @method GET * * @route.path /agents/{hashid} * @route.alternative.0 /agents/{hashid}#kontext * @route.alternative.1 /agents/{hashid}#chat * @route.alternative.2 /agents/{hashid}#telefon * @route.description REST-Konfiguration für Detailansicht eines Agenten */ public static function restConfigDetails( int|string $hashid ) : REST\Generator\Base\BaseGenerator { $data = Agents\Model::get( $hashid ); return REST\Response::addWrapper( "details", "Assistent bearbeiten" ) ->setBacklink( "/agents" ) ->get() ->setApi( "/agents/{$hashid}" ) ->setAction( "/agents/{$hashid}", "PUT" ) ->addTab( "Assistent" )->get() ->addHeadline( "Assistent", 2 ) ->addText( "Mit diesen Informationen wird das Verhalten des Assistenten gesteuert." ) ->addField(...Hooks::filter( "rest.generator::control.type->text", [], "name", TRUE, "agents" )) ->addField(...Hooks::filter( "rest.generator::control.type->text", [ "readonly" => TRUE, ], "uuid", FALSE, "agents" )) ->parent() ->addTab( "Kontext" )->get() ->addHeadline( "Kontext", 2 ) ->addText( "Kontext der sowohl für Chat als auch Telefon gültig ist. Zur besseren Struktur kann Markdown genutzt werden." ) ->addHeadline( "Persönlichkeit", 3 ) ->addText( "Informationen über den Assistenten sowie welche Persönlichkeit der Assistent haben soll." ) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Persönlichkeit" ], "prompt_personality", FALSE, "agents" )) ->addHeadline( "Allgemeine Informationen", 3 ) ->addText( "Allgemeine Informationen für den Assistenten, z.B. was er machen soll." ) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Allgemeine Informationen" ], "prompt_tasks", FALSE, "agents" )) ->addHeadline( "Verhalten bei Unklarheiten", 3 ) ->addText( "Falls du das Anliegen des Anrufers nicht beantworten kannst:" ) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Verhalten bei Unklarheiten" ], "prompt_uncertainties", FALSE, "agents" )) ->addHeadline( "Sensible Themen", 3 ) ->addText( "Falls der Kunde über folgende Themen spricht, sage, dass du nicht weiterhelfen kannst: " ) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Sensible Themen" ], "prompt_sensitives", FALSE, "agents" )) ->addHeadline( "Gesprächsablauf", 3 ) ->addText( "Ein grober Ablauf eines Gespräches, damit der Assistent weiß wie er das Gespräch führen soll." ) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Gesprächsablauf" ], "prompt_conversation_flow", FALSE, "agents" )) ->addHeadline( "Häufige Fragen", 3 ) ->addText( "Hier eine Übersicht über häufig gestellte Fragen und deren Antworten, die als Orientierungshilfe dienen soll:" ) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Häufige Fragen" ], "prompt_faq", FALSE, "agents" )) ->addHeadline( "Wissensdatenbank", 3 ) ->addText( "Hier eine generelle Auflistung an Informationen, die bei der Gesprächsführung helfen sollen:" ) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Wissensdatenbank" ], "prompt_knowledge_base", FALSE, "agents" )) ->parent() ->addTab( "Chat" )->get() ->addHeadline( "Einstellung für Chat-KI", 2 ) ->addField(...Hooks::filter( "rest.generator::control.type->code", [ "label" => "Einbettungscode", "value" => implode( "\n", [ "", "domain . "/api/v1/agents/chat/{$data->uuid}/styles\">", ]), "readonly" => TRUE, ], "embed_code", FALSE, "agents" )) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [], "start_message_chat", FALSE, "agents" )) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Zusätzlicher Kontext", ], "prompt_chat", FALSE, "agents" )) ->parent() ->addTab( "Telefon" )->get() ->addHeadline( "Einstellung für Telefon-KI", 2 ) ->addGroup() ->addField(...Hooks::filter( "rest.generator::control.type->select", [], "phone_number", FALSE, FALSE, "agents" )) ->addField(...Hooks::filter( "rest.generator::control.type->select", [], "phone_voice", FALSE, FALSE, "agents" )) ->parent() ->addGroup() ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [], "start_message_phone", FALSE, "agents" )) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Zusätzlicher Kontext", ], "prompt_phone", FALSE, "agents" )) ->parent( 2 ) ->addTab( "Tools" )->get() ->addText( "Tools sind Werkzeuge die der Assistent während des Anrufs verwenden kann." ) ->addContent( Agents\Tools\API\v1\Controller::restConfigTable( $hashid ) ) ->parent() ->addTab( "Nachverarbeitung" )->get() ->addText( "Nachverarbeitungen des Assistenten werden nach dem Anruf parallel ausgeführt." ) ->addContent( Agents\Processing\API\v1\Controller::restConfigTable() ) ->parent() ->addTab( "Speichern" )->get() ->setColor( REST\Generator\Config\Color::Primary ) ->setIcon( "bi bi-save" ) ->setType( "submit" ); } /** * @internal rest * @method GET * * @route.path /agents/create * @route.description REST-Konfiguration für das Erstellen eines Agenten (Formular) */ public static function restConfigCreate() : REST\Generator\Base\BaseGenerator { return REST\Response::addModal( "details", "Assistent erstellen" ) ->get() ->setAction( "/agents", "POST" ) ->addButton(...Hooks::filter( "rest.generator::button.type->submit", [], "Erstellen", "agents" )) ->addField(...Hooks::filter( "rest.generator::control.type->text", [], "name", TRUE, "agents" )); } /** * @internal rest * @method GET * * @route.path /agents/{hashid}/delete * @route.description REST-Konfiguration für die Lösch-Bestätigung eines Agenten */ public static function restConfigDelete( int|string $hashid ) : REST\Generator\Base\BaseGenerator { return REST\Response::addModal( "details", "Assistent Löschen" ) ->setSize( REST\Wrapper\Modal\Size::sm ) ->get() ->setAction( "/agents/{$hashid}", "DELETE" ) ->addText( "Möchtest du wirklich den Datensatz (#{$hashid}) löschen? " ) ->addButton(...Hooks::filter( "rest.generator::button.type->cancel", [], NULL, "agents" )) ->addButton(...Hooks::filter( "rest.generator::button.type->delete", [], NULL, "agents" )); } /* ### PUBLIC CHAT FUNCTIONS #################################### # */ /** * @api * @method GET * * @route.path /agents/chat/{uuid}/reset * @route.description Resetten den Chat des Users zum aktuellen Agenten */ public static function resetChat( string $uuid ) : HTTP\Response { $agent = Agents\Model::getByColumn( "uuid", $uuid ); $user = Users\Model::get(); $hashid = hashid( "chat", $agent->hashid, $user->id ); \ARUNOX\API::setAuthToken( ARUNOX_API_KEY ); \ARUNOX\API::setApiUrl( "https://" . SYSTEM->domain ."/api/v1/agents/{$agent->hashid}" ); $prompt = \ARUNOX\API::get( "?columns=prompt" ); $prompt = $prompt->prompt; $tools = \ARUNOX\API::get( "/tools?status=1&columns=id,type,description,url,defaults,header,method,parameters" ); $tools = array_map( fn( $item ) => (object)array_merge( (array)$item, [ "name" => "applyTool-{$item->id}", "openai" => [ "type" => "function", "function" => (object)[ "name" => "applyTool-{$item->id}", "description" => $item->description, "parameters" => (object)json_decode( $item->parameters ?? "{}" ), ], ], ]), $tools ); $tools = array_column( $tools, NULL, "name" ); // show( $tools ); $config = [ "model" => "gpt-4o-mini", // "instructions" => $prompt, "tools" => array_values( array_column( $tools, "openai" ) ), "tool_choice" => "auto", "user" => $hashid, "store" => FALSE, ]; // show( $config ); $calls = [ [ "role" => OpenAI\Messages\Role::Developer, "content" => $prompt, ], [ "role" => OpenAI\Messages\Role::User, "content" => "Ich möchte ein Tool für Agent sam erstellen", ], ]; function runOpenAiResponseLoop( array $config, array &$calls, array $tools, array &$logging ) { $response = OpenAI\Responses::create(array_merge( $config, [ "input" => $calls ])); $toolCall = FALSE; if( OpenAI::hasError() ) show( OpenAI::getError(), "STOP" ); // show( $response, FALSE ); $logging[] = $response; // show( $response ); foreach( $response->output as $output ) { if( $output->type == Agents\Calls\Type::ToolCall ) { $output->role = OpenAI\Messages\Role::Tool; $calls[] = [ "call_id" => $output->call_id, "arguments" => $output->arguments, "name" => $output->name, "type" => $output->type, "id" => $output->id, "status" => $output->status, ]; if( ! $tool = $tools[ $output->name ] ?? NULL ) continue; $toolCall = TRUE; if( $tool->type == 3 ) { $defaults = json_decode( $tool->defaults ?? "[]", TRUE ); $arguments = array_replace( $defaults, json_decode( $output->arguments ?? "[]", TRUE ) ); $toolUrl = preg_replace_callback('/\{(\w+)\}/', function( $m ) use( $arguments ) { return trim( $arguments[ $m[1] ] ?? $m[0], "/" ); }, $tool->url ); $result = \ARUNOX\API::{$tool->method}( $toolUrl, $arguments , $tool->header ); if( !is_scalar( $result ) ) { $result = json_encode( $result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); } $calls[] = [ "call_id" => $output->call_id, "output" => $result, "type" => Agents\Calls\Type::ToolResult, "id" => $output->id, "status" => "completed", ]; } // show( $output, $calls ); } if( $output->type == Agents\Calls\Type::Message ) { $calls[] = [ "content" => $output->content[0]->text, "id" => $output->id, "role" => $output->role, "status" => $output->status, "type" => $output->type, ]; } } if( $toolCall ) return runOpenAiResponseLoop( $config, $calls, $tools, $logging ); return $response; } function runOpenAiChatLoop( array $config, array &$calls, array $tools, array &$logging ) { $response = OpenAI\Chat\Completions::create(array_merge( $config, [ "messages" => $calls ])); if( OpenAI::hasError() ) show( OpenAI::getError(), "STOP" ); $logging[] = $response; $choice = $response->choices[0]; $message = $choice->message; $calls[] = (array)$message; // show( $response ); if( $choice->finish_reason == "tool_calls" ) { foreach( $message->tool_calls as $tool_call ) { if( ! $tool = $tools[ $tool_call->function->name ] ?? NULL ) continue; if( $tool->type == 3 ) { $defaults = json_decode( $tool->defaults ?? "[]", TRUE ); $arguments = array_replace( $defaults, json_decode( $tool_call->function->arguments ?? "[]", TRUE ) ); $toolUrl = preg_replace_callback('/\{(\w+)\}/', function( $m ) use( $arguments ) { return trim( $arguments[ $m[1] ] ?? $m[0], "/" ); }, $tool->url ); $result = \ARUNOX\API::{$tool->method}( $toolUrl, $arguments , $tool->header ); if( !is_scalar( $result ) ) { $result = json_encode( $result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); } $calls[] = [ "tool_call_id" => $tool_call->id, "content" => $result, "role" => OpenAI\Messages\Role::Tool, ]; } } return runOpenAiChatLoop( $config, $calls, $tools, $logging ); } return $response; } $logging = []; // $response = runOpenAiResponseLoop( $config, $calls, $tools, $logging ); $response = runOpenAiChatLoop( $config, $calls, $tools, $logging ); show( $response->choices[0]->message->content, $calls, $logging, "STOP" ); $data = [ "origin_id" => $agent->hashid, "chat_hashid" => $response->user, "status" => Agents\Calls\Status::getByConstant( $response->output[0]->status ), "role" => $response->output[0]->role, "type" => $response->output[0]->type, "msg_id" => $response->output[0]->id, "model" => $response->model, "content" => $response->output[0]->content[0]->text, "tool_name" => $response->output[0]->name, "tool_args" => $response->output[0]->arguments, "tool_call_id" => $response->output[0]->call_id, "usage_prompt" => $response->usage->prompt_tokens, "usage_completion" => $response->usage->completion_tokens, "usage_total" => $response->usage->total_tokens, // "cost_usd" => $response->user, "source" => Agents\Calls\Source::Webchat, // "hashid" => $response->user, ]; show( $response, $data, "STOP" ); $call_id = \ARUNOX\API::post( "/calls/{$hashid}", [ "content" => $agent->start_message_chat, "role" => OpenAI\Messages\Role::Assistant, "type" => Agents\Calls\Type::Message, "status" => Agents\Calls\Status::Complete, ]); show( $call_id, "X"); } /** * @api * @method GET * * @route.path /agents/chat/{uuid}/data * @route.description Lädt den aktuellen Chat-Verlauf für einen Agenten */ public static function getChatData( string $uuid, array $data ) : BaseEntity | bool | array { return Agents\Model::getByColumn( "uuid", $uuid ); } /** * @api * @method GET * * @route.path /agents/chat/{uuid}/script * @route.description Liefert das benötigte JavaScript für den Chat */ public static function includeChatScript( string $uuid, array $data ) : never { $agent = Agents\Model::getByColumn( "uuid", $uuid ); $user = Users\Model::get(); $icons = glob( PLUGINS_DIR . "/agents/assets/icons/*.svg" ); $icons = array_map( fn( $icon ) => [ "file" => file_get_contents( $icon ), "name" => preg_replace_callback('/-([a-z])/', fn( $m ) => strtoupper( $m[1] ), basename( $icon, ".svg" ) ), ], $icons ); $icons = array_column( $icons, "file", "name" ); $openai = json_decode( $user->openai_chats ?? "{}", TRUE ); $hashid = hashid( "chat", $agent->hashid, $user->id ); if( !in_array( $hashid, $openai ) ) { $openai[] = $hashid; $user->save([ "openai_chats" => $openai ]); } // show( $icons ); header("Content-Type: text/javascript "); // echo "const chatAI_MD = '" . base64_encode(file_get_contents( PLUGINS_DIR . "/agents/assets/demo.md" )) . "';"; echo "const chatAI_NAME = '{$agent->name}';"; echo "const chatAI_API = 'https://" . SYSTEM->domain ."/api/v1/agents/{$agent->hashid}';\n"; echo "const chatAI_WS_URL = 'wss://ws.arunox.dev';"; echo "const chatAI_WS_Port = '2347';"; // echo "const chatAI_WS_Token = 'ws_" . session_id(). "';"; echo "const chatAI_minWidth = '350px';"; echo "const chatAI_minHeight = '250px';"; echo "const chatAI_maxWidth = '33vw';"; echo "const chatAI_maxHeight = '66vh';"; echo "const chatAI_icons = " . json_encode( $icons, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE ) .";"; echo "const chatAI_CHAT_ID = '{$hashid}';"; echo file_get_contents( PLUGINS_DIR . "/agents/assets/js/marked.min.js" ); echo file_get_contents( PLUGINS_DIR . "/agents/assets/js/chatai.js" ); echo file_get_contents( PLUGINS_DIR . "/agents/assets/js/chatai_ws.js" ); exit; } /** * @api * @method GET * * @route.path /agents/chat/{id}/styles * @route.description Liefert die benötigten Stylesheets für den Chat */ public static function includeChatStyles( string $uuid, array $data ) : never { header("Content-Type: text/css"); include PLUGINS_DIR . "/agents/assets/css/chatai.css"; exit; } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Tools/Model.php **/ Agents\Model::getValue( $agentId, "id" ), ])); } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Tools/API/v1/Routes.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\Agents\Tools\API\v1; use ARUNOX\Core\Hooks; use ARUNOX\Core\Router; use ARUNOX\Plugins\REST; class Routes { public static function register() : void { Router::addAll( Controller::class ); Hooks::add('router::get->api.v1.rest.routes', function( $routes ) { $routes[] = [ "path" => "/agents/tools/{id}", "label" => "Bearbeiten", ]; return $routes; }); } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Tools/API/v1/Controller.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\Agents\Tools\API\v1; use ARUNOX\API; use ARUNOX\HTTP; use ARUNOX\Core\Hooks; use ARUNOX\Plugins\REST; use ARUNOX\Plugins\Agents; use ARUNOX\Database\Attribute; use ARUNOX\API\BaseController; use ARUNOX\Database\BaseEntity; class Controller extends BaseController { /* ### VARIABLES ################################################ # */ protected static array $NOTIFY = [ "notfound" => "Tool nicht gefunden", "saved" => "Tool wurde erfolgreich gespeichert", "deleted" => "Tool wurde erfolgreich gelöscht", "created" => "Tool wurde erfolgreich erstellt", "statuschanged.0" => "Tool wurde erfolgreich deaktiviert", "statuschanged.1" => "Tool wurde erfolgreich aktiviert", "notcreated" => "Tool konnte nicht erstellt werden", "notsaved" => "Tool konnte nicht gespeichert werden", ]; /* ### PUBLIC FUNCTIONS ######################################### # */ /** * @api * @method GET * * @route.path /agents/{hashid}/tools * @route.link /agents/{hashid}#tools * @route.query.status integer|options=/agents/tools/options/status * @route.description Listet alle Tools eines Agenten */ public static function getAllOwn( string $hashid, array $where = [] ) : array { return Agents\Tools\Model::getByAgent( $hashid, $where ); } /** * @api * @method GET * * @route.path /agents/{hashid}/tools/{id} * @route.description Gibt ein einzelnes Tool eines Agenten zurück */ public static function get( int $origin, int $id ) : BaseEntity | HTTP\Response { if( $result = Agents\Tools\Model::get([ "id" => $id, "origin_id" => $origin ]) ) { return $result; } return REST\Response::jsonResponse( 404, static::$NOTIFY["notfound"] ); } /** * @api * @method GET * * @route.path /agents/tools/options/type * @route.description Gibt ein alle Optionen für type zurück */ public static function getTypeAsOptions() : array { return Agents\Tools\Type::getAll(); } /** * @api * @method GET * * @route.path /agents/tools/options/status * @route.description Gibt ein alle Optionen für status zurück */ public static function getStatusAsOptions() : array { return Attribute\Status::getAll(); } /* ### PUBLIC SAVE FUNCTIONS #################################### # */ /** * @api * @method PUT * * @route.path /agents/{hashid}/tools/{id} * @route.description Aktualisiert die Eigenschaften eines Tools */ public static function save( string $origin, int $id, array $data ) : HTTP\Response { $agent = Agents\Model::getValue( $origin ); if( $result = Agents\Tools\Model::get([ "id" => $id, "origin_id" => $agent ]) ) { if( $result->save( $data ) ) { return REST\Response::jsonResponse( 200, static::$NOTIFY["saved"] ); } return REST\Response::jsonResponse( 500, static::$NOTIFY["notsaved"] ); } return REST\Response::jsonResponse( 404, static::$NOTIFY["notfound"] ); } /** * @api * @method POST * * @route.path /agents/{hashid}/tools * @route.description Erstellt ein neues Tool für einen Agenten */ public static function create( string $origin, array $data ) : HTTP\Response { $data[ "origin_id" ] = Agents\Model::getValue( $origin ); if( $id = Agents\Tools\Model::save( $data ) ) { $result = Agents\Tools\Model::get( $id ); return REST\Response::jsonResponse( 200, static::$NOTIFY["created"], [ "data" => $result ] ); } return REST\Response::jsonResponse( 500, static::$NOTIFY["notfound"] ); } /** * @api * @method DELETE * * @route.path /agents/{hashid}/tools/{id} * @route.description Löscht ein Tool eines Agenten */ public static function delete( string $origin, int $id ) : HTTP\Response { $agent = Agents\Model::getValue( $origin ); if( Agents\Tools\Model::delete([ "id" => $id, "origin_id" => $agent ]) ) { return REST\Response::jsonResponse( 200, static::$NOTIFY["deleted"] ); } return REST\Response::jsonResponse( 500, static::$NOTIFY["notfound"] ); } /** * @api * @method PATCH * * @route.path /agents/{hashid}/tools/{id}/status * @route.description Umschalten des Status eines Tools */ public static function toggleStatus( string $origin, int $id ) : HTTP\Response { $agent = Agents\Model::getValue( $origin ); if( $result = Agents\Tools\Model::get([ "id" => $id, "origin_id" => $agent ]) ) { $result->toggleStatus( TRUE ); return REST\Response::jsonResponse( 200, static::$NOTIFY["statuschanged.{$result->status->getValue()}"], [ "data" => $result ] ); } return REST\Response::jsonResponse( 404, static::$NOTIFY["notfound"] ); } /* ### PUBLIC VALIDATE FUNCTIONS ################################ # */ /* ### PUBLIC REST FUNCTIONS #################################### # */ /* ### PUBLIC REST RESPONSE FUNCTIONS ########################### # */ /** * @internal rest * @method PUT * * @route.path /agents/{hashid}/tools/{id} * @route.description REST-Antwort nach dem Speichern eines Tools */ public static function restResponseSave( string $origin, int $id, array $data ) : HTTP\Response { $agent = Agents\Model::getValue( $origin ); $result = static::save( $agent, $id, $data ); if( $result->isStatus( 200 ) ) { $data = Agents\Tools\Model::get([ "id" => $id, "origin_id" => $agent ]); REST\Response::closeModals(); REST\Response::addUpdater( [ "data/table/row", "/api/v1/agents/{$origin}/tools", $id ], $data ); } return static::restResponse( $result ); } /** * @internal rest * @method POST * * @route.path /agents/{hashid}/tools * @route.description REST-Antwort nach dem Erstellen eines Tools */ public static function restResponseCreate( string $origin, array $data ) : HTTP\Response { $agent = Agents\Model::getValue( $origin ); $result = static::create( $agent, $data ); if( $result->isStatus( 200 ) ) { REST\Response::closeModals(); REST\Response::redirect( "/agents/{$origin}#tools" ); } return static::restResponse( $result ); } /** * @internal rest * @method DELETE * * @route.path /agents/{hashid}/tools/{id} * @route.description REST-Antwort nach dem Löschen eines Tools */ public static function restResponseDelete( string $origin, int $id ) : HTTP\Response { REST\Response::closeModals(); $agent = Agents\Model::getValue( $origin ); $result = static::delete( $agent, $id ); if( $result->isStatus( 200 ) ) { REST\Response::redirect( "/agents/{$origin}#tools" ); } return static::restResponse( $result ); } /** * @internal rest * @method PATCH * * @route.path /agents/{hashid}/tools/{id}/status * @route.description REST-Antwort zur Statusänderung eines Tools */ public static function restResponseToggleStatus( string $origin, int $id ) : HTTP\Response { $agent = Agents\Model::getValue( $origin ); $result = static::toggleStatus( $agent, $id ); if( $result->isStatus( 200 ) ) { $data = $result->getBody("data"); REST\Response::addUpdater( [ "data/table/row", "/api/v1/agents/{$origin}/tools", $id ], $data ); } return static::restResponse( $result ); } /* ### PUBLIC REST CONFIG FUNCTIONS ############################# # */ /** * @internal rest * @method GET * * @route.path /agents/{hashid}/tools/{id} * @route.description REST-Detailansicht für ein Tool */ public static function restConfigDetails( string $origin, int $id ) : REST\Generator\Base\BaseGenerator { $agent = Agents\Model::getValue( $origin ); $tool = Agents\Tools\Model::get([ "id" => $id, "origin_id" => $agent ]); $form = REST\Response::addModal( "details", "Tool bearbeiten" ) ->setData( $tool ) ->setTitle( $tool->type->getLabel() ) ->setIcon( $tool->type->getIcon() ) ->setColor( $tool->type->getColor() ) ->get() ->setAction( "/agents/{$origin}/tools/{id}", "PUT" ) ->addButton(...Hooks::filter( "rest.generator::button.type->submit", [], "Speichern", "agents/{origin_id}/tools" )) ->addText( "Geben Sie diesem Tool eine für Sie passende Beschreibung, zur besseren Wiedererkennung." ) ->addField(...Hooks::filter( "rest.generator::control.type->text", [], "name", TRUE, "agents/tools" )) ->addField(...Hooks::filter( "rest.generator::control.type->text", [], "description", TRUE, "agents/{origin_id}/tools" )) ->addField(...Hooks::filter( "rest.generator::control.type->text", [], "context", FALSE, "agents/tools" )) ->addHeadline( "Einstellungen", "3" ); switch( $tool->type->getValue() ) { case Agents\Tools\Type::SystemTool : static::restConfigFormTypeSystemTool( $form ); break; case Agents\Tools\Type::CallForward : static::restConfigFormTypeCallForward( $form ); break; case Agents\Tools\Type::Calendar : static::restConfigFormTypeCalendar( $form ); break; case Agents\Tools\Type::APIRequest : static::restConfigFormTypeAPIRequest( $form ); break; case Agents\Tools\Type::ConversionFlow : static::restConfigFormTypeConversionFlow( $form ); break; } return $form; } /** * @internal rest * @method GET * * @route.path /agents/{hashid}/tools/create * @route.query.type integer|required|options=/agents/tools/options/type * @route.description REST-Formularkonfiguration zum Erstellen eines Tools */ public static function restConfigCreate( string $origin, $args ) : REST\Generator\Base\BaseGenerator { $agent = Agents\Model::getValue( $origin ); $option = Agents\Tools\Type::get( $args["type"] ?? 3 ); $form = REST\Response::addModal( "details", "Tool erstellen" ) ->setTitle( $option["label"] ) ->setIcon( $option["icon"] ) ->setColor( $option["color"] ) ->get() ->setAction( "/agents/{$origin}/tools", "POST" ) ->addButton(...Hooks::filter( "rest.generator::button.type->submit", [], "Erstellen", "agents/tools" )) ->addText( "Geben Sie diesem Tool eine für Sie passende Beschreibung, zur besseren Wiedererkennung." ) ->addField(...Hooks::filter( "rest.generator::control.type->text", [], "name", TRUE, "agents/tools" )) ->addField(...Hooks::filter( "rest.generator::control.type->text", [], "description", TRUE, "agents/tools" )) ->addField(...Hooks::filter( "rest.generator::control.type->text", [], "context", FALSE, "agents/tools" )) ->addField(...Hooks::filter( "rest.generator::control.type->hidden", [], "type", $args["type"], "agents/tools" )) ->addHeadline( "Einstellungen", "3" ); switch( $args["type"] ) { case Agents\Tools\Type::SystemTool : static::restConfigFormTypeSystemTool( $form ); break; case Agents\Tools\Type::CallForward : static::restConfigFormTypeCallForward( $form ); break; case Agents\Tools\Type::Calendar : static::restConfigFormTypeCalendar( $form ); break; case Agents\Tools\Type::APIRequest : static::restConfigFormTypeAPIRequest( $form ); break; case Agents\Tools\Type::ConversionFlow : static::restConfigFormTypeConversionFlow( $form ); break; } return $form; } /** * @internal rest * @method GET * * @route.path /agents/{hashid}/tools/{id}/delete * @route.description REST-Bestätigungsdialog zum Löschen eines Tools */ public static function restConfigDeleteOwn( string $origin, int $id ) : REST\Generator\Base\BaseGenerator { $agent = Agents\Model::getValue( $origin ); return REST\Response::addModal( "details", "Tool Löschen" ) ->setSize( REST\Wrapper\Modal\Size::sm ) ->get() ->setAction( "/agents/{$origin}/tools/{$id}", "DELETE" ) ->addText( "Möchtest du wirklich den Datensatz (#{$id}) löschen? " ) ->addButton(...Hooks::filter( "rest.generator::button.type->cancel", [], NULL, "agents/{origin_id}/tools" )) ->addButton(...Hooks::filter( "rest.generator::button.type->delete", [], NULL, "agents/{origin_id}/tools" )); } /* ### PUBLIC REST EXTENSION FUNCTIONS ########################## # */ public static function restConfigTable( string $hashid ) : REST\Generator\Base\BaseGenerator { $table = REST\Generator\Data\Table::create() ->setApi( "/agents/{$hashid}/tools" ) ->setAction( "/agents/{$hashid}/tools/{id}" ) ->setOrder( "name", "asc" ) ->setSearch( TRUE ) ->addButton(...Hooks::filter( "rest.generator::button.name->create", [], "agents/{$hashid}/tools" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->icon", [ "icon" => "bi-ui-checks-grid", "options" => Agents\Tools\Type::getAll(), ], "type", [], "agents/{origin_id}/tools" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->status", [], "status", [], "agents/{$hashid}/tools" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->text", [ "label" => "Description", ], "output", TRUE, TRUE, TRUE, "agents/{origin_id}/tools" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->text", [ "label" => "Kontext", ], "context", TRUE, TRUE, TRUE, "agents/{origin_id}/tools" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->datetime", [], "date_edit", FALSE, FALSE, TRUE, "agents/{id}/tools" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->actions", [], [], FALSE, "agents/{id}/tools" ))->get() ->addButton(...Hooks::filter( "rest.generator::action.name->edit", [], "agents/{$hashid}/tools" )) ->addButton(...Hooks::filter( "rest.generator::action.name->delete", [], "agents/{$hashid}/tools" )) ; $buttons = $table->top()->getButtons(); foreach( Agents\Tools\Type::getAll() as $type ) { $type["name"] = "type"; $buttons->addDropdown( ...Hooks::filter( "rest.generator::button.name->create", $type, "agents/{$hashid}/tools" ) ); } return $table; } /* ### PUBLIC REST TYPE FUNCTIONS ############################### # */ public static function restConfigFormTypeSystemTool( REST\Generator\Base\BaseGenerator $form ) { $form->top()->get() ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Dynamische Parameter", "value" => "[]", ], "parameters", FALSE, "agents/tools" )) ; } public static function restConfigFormTypeCallForward( REST\Generator\Base\BaseGenerator $form ) { $form->top()->get() ->addField(...Hooks::filter( "rest.generator::control.type->tel", [], "phone", TRUE, "agents/{origin_id}/tools" )) ; } public static function restConfigFormTypeCalendar( REST\Generator\Base\BaseGenerator $form ) { $form ->get()->setTitle( "Aktuell nur Platzhalter / Muster") ->top(); } public static function restConfigFormTypeAPIRequest( REST\Generator\Base\BaseGenerator $form ) { $form->top()->get() ->addField(...Hooks::filter( "rest.generator::control.type->text", [ "label" => "Ausführungstext", "hint" => "Text der während Ausführung gesprochen wird. Leer lassen für keine Antwort.", ], "response", FALSE, "agents/tools" )) ->addField(...Hooks::filter( "rest.generator::control.type->url", [], "url", TRUE, "agents/tools" )) ->addField(...Hooks::filter( "rest.generator::control.type->select", [ "choose_id" => API\Method::GET, "options" => API\Method::getAll(), ], "method", TRUE, FALSE, "agents/tools" )) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "value" => "[]", ], "header", FALSE, "agents/tools" )) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Dynamische Parameter", "value" => "[]", ], "parameters", FALSE, "agents/tools" )) ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Default / Fixe Parameter", "value" => "[]", ], "defaults", FALSE, "agents/tools" )) ; } public static function restConfigFormTypeConversionFlow( REST\Generator\Base\BaseGenerator $form ) { $form->top()->get() ->addField(...Hooks::filter( "rest.generator::control.type->textarea", [ "label" => "Step", "value" => "[]", ], "steps", FALSE, "agents/tools" )) ; } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Tools/Type.php **/ [ "color" => REST\Generator\Config\Color::Default, "icon" => "bi bi-tools", "label" => "Systemtool" ], self::CallForward => [ "color" => REST\Generator\Config\Color::Primary, "icon" => "bi bi-telephone-forward", "label" => "Anrufweiterleitung" ], self::Calendar => [ "color" => REST\Generator\Config\Color::Secondary, "icon" => "bi bi-calendar-plus", "label" => "Terminbuchung" ], self::APIRequest => [ "color" => REST\Generator\Config\Color::Danger, "icon" => "bi bi-plugin", "label" => "API Request" ], self::ConversionFlow => [ "color" => REST\Generator\Config\Color::Info, "icon" => "bi bi-chat-dots", "label" => "Gesprächsablauf" ], ]; /* ### PUBLIC FUNCTIONS ######################################### # */ } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Tools/Entity.php **/ Attribute\Status::class, "date_create" => Attribute\DateTime::class, "date_edit" => Attribute\DateTime::class, "type" => Type::class, "output" => "output", ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function getOutput() { return match( $this->type->getValue() ) { Type::CallForward => "{$this->phone} » {$this->description}", default => $this->description, }; } public function toggleStatus( bool $save = FALSE ) { $this->status->toggle(); if( $save ) $this->save(); } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Entity.php **/ Attribute\Status::class, "date_create" => Attribute\DateTime::class, "date_edit" => Attribute\DateTime::class, "prompt" => "prompt", ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function toggleStatus( bool $save = FALSE ) { $this->status->toggle(); if( $save ) $this->save(); } public function getTools( bool $onlyActive = FALSE ) { if( $onlyActive ) $where = [ "status" => Attribute\Status::Active ]; else $where = []; return Tools\Model::getAll( array_merge( $where, [ "origin_id" => $this->id, ])); } public function getPrompt() { $prompt = array_filter([ "#Über Dich" => $this->prompt_personality, "#Allgemeine Informationen" => $this->prompt_tasks, "#Verhalten bei Unklarheiten" => $this->prompt_uncertainties, "#Sensible Themen" => $this->prompt_sensitives, "#Gesprächsablauf" => $this->prompt_conversation_flow, "#Häufige Fragen" => $this->prompt_faq, "#Wissensdatenbank" => $this->prompt_knowledge_base, "# Chat-Modus (aktiv)\n" => $this->prompt_chat, // "# Telefon-Modus (aktiv))\n" => $this->prompt_phone, ]); $prompt = array_map( "trim", $prompt ); $prompt = array_map( fn( $title, $prompt ) => "{$title}\n{$prompt}", array_keys( $prompt ), array_values( $prompt ) ); $prompt = implode( "\n\n---\n\n", $prompt ); return $prompt; } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Processing/Model.php **/ Agents\Model::getValue( $agentId, "id" ), ])); } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Processing/API/v1/Routes.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\Agents\Processing\API\v1; use ARUNOX\Core\Hooks; use ARUNOX\Core\Router; class Routes { public static function register() : void { Router::get('/api/v1/agents/{id}/processing', [ Controller::class, 'getByAgent' ]); Router::get('/api/v1/agents/{id}/processing/create', [ Controller::class, 'restConfigCreate' ]); Router::post('/api/v1/agents/{id}/processing', [ Controller::class, 'create' ]); Router::get('/api/v1/agents/{id}/processing/{id}', [ Controller::class, 'get' ]); Router::put('/api/v1/agents/{id}/processing/{id}', [ Controller::class, 'save' ]); Router::patch('/api/v1/agents/{id}/processing/{id}/status', [ Controller::class, 'toggleStatus' ]); Router::get('/api/v1/agents/{id}/processing/{id}/delete', [ Controller::class, 'restConfigDelete' ]); Router::delete('/api/v1/agents/{id}/processing/{id}', [ Controller::class, 'delete' ]); Hooks::add('router::get->api.v1.rest.routes', function( $routes ) { $routes[] = [ "path" => "/agents/processing/{id}", "label" => "Bearbeiten", ]; return $routes; }); } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Processing/API/v1/Controller.php **/ * @copyright 2025 ARUNOX * @version 1.1.0 * * @since 1.0.0 File is created */ namespace ARUNOX\Plugins\Agents\Processing\API\v1; use ARUNOX\HTTP; use ARUNOX\Core\Hooks; use ARUNOX\Plugins\REST; use ARUNOX\Plugins\Agents; use ARUNOX\API\BaseController; class Controller extends BaseController { /* ### VARIABLES ################################################ # */ protected static array $NOTIFY = [ "notfound" => "Tool nicht gefunden", "saved" => "Tool wurde erfolgreich gespeichert", "deleted" => "Tool wurde erfolgreich gelöscht", "created" => "Tool wurde erfolgreich erstellt", "notcreated" => "Tool konnte nicht erstellt werden", ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function getByAgent( int $id ) : HTTP\Response { $results = Agents\Processing\Model::getByAgent( $id ); return $this->jsonResponse( $results ); } public function get( int $origin, int $id ) : HTTP\Response { if( $result = Agents\Processing\Model::get([ "id" => $id, "origin_id" => $origin ]) ) { $this->restConfigDetails( $result ); } else REST\Response::addNotify( "danger", static::$NOTIFY["notfound"] ); return REST\Response::jsonResponse(); } /* ### PUBLIC SAVE FUNCTIONS #################################### # */ public function save( int $origin, int $id, array $data ) : HTTP\Response { if( $result = Agents\Processing\Model::get([ "id" => $id, "origin_id" => $origin ]) ) { $result->save( $data ); REST\Response::addNotify( "success", static::$NOTIFY["saved"] ); REST\Response::closeModals(); REST\Response::addUpdater( [ "data/table/row", "/api/v1/agents/{$result->origin_id}/processing", $id ], $result ); } else REST\Response::addNotify( "danger", static::$NOTIFY["notfound"] ); return REST\Response::jsonResponse(); } public function create( int $origin, array $data ) : HTTP\Response { $data[ "origin_id" ] = $origin; if( $id = Agents\Model::save( $data ) ) { $result = Agents\Processing\Model::get( $id ); REST\Response::addNotify( "success", static::$NOTIFY["created"] ); REST\Response::closeModals(); REST\Response::redirect( "/agents/agents/{$result->origin_id}#processing" ); } else REST\Response::addNotify( "danger", static::$NOTIFY["notcreated"] ); return REST\Response::jsonResponse(); } public function delete( int $origin, int $id ) : HTTP\Response { $result = Agents\Processing\Model::get([ "id" => $id, "origin_id" => $origin ]); REST\Response::closeModals(); if( $result && Agents\Model::delete( $id ) ) { REST\Response::addNotify( "success", static::$NOTIFY["deleted"] ); REST\Response::redirect( "/agents/agents/{$result->origin_id}#processing" ); } else REST\Response::addNotify( "danger", static::$NOTIFY["notfound"] ); return REST\Response::jsonResponse(); } public function toggleStatus( int $origin, int $id ) : HTTP\Response { if( $result = Agents\Processing\Model::get([ "id" => $id, "origin_id" => $origin ]) ) { $result->toggleStatus( TRUE ); REST\Response::addUpdater( [ "data/table/row", "/api/v1/agents/{$result->origin_id}/processing", $id ], $result ); } else REST\Response::addNotify( "danger", static::$NOTIFY["notfound"] ); return REST\Response::jsonResponse(); } /* ### PUBLIC VALIDATE FUNCTIONS ################################ # */ /* ### PUBLIC REST FUNCTIONS #################################### # */ public function restConfigDetails( Agents\Processing\Entity $data ) : HTTP\Response { $form = REST\Response::addModal( "details", "Tool bearbeiten" ) ->setData( $data ) ->setTitle( $data->type->getLabel() ) ->setIcon( $data->type->getIcon() ) ->setColor( $data->type->getColor() ) ->get() ->setAction( "/api/v1/agents/{origin_id}/processing/{id}", "PUT" ) ->addButton(...Hooks::filter( "rest.generator::button.type->submit", [], "Speichern", "agents/{origin_id}/processing" )) ->addText( "Geben Sie diesem Tool eine für Sie passende Beschreibung, zur besseren Wiedererkennung." ) ->addField(...Hooks::filter( "rest.generator::control.type->text", [], "description", TRUE, "agents/{origin_id}/processing" )) ->addHeadline( "Einstellungen", "3" ); switch( $data->type->getValue() ) { case Agents\Processing\Type::EmailDelivery : static::restConfigFormTypeEmailDelivery( $form ); break; case Agents\Processing\Type::SMSDelivery : static::restConfigFormTypeSMSDelivery( $form ); break; case Agents\Processing\Type::APIRequest : static::restConfigFormTypeApi( $form ); break; } return REST\Response::jsonResponse(); } public function restConfigCreate( $args ) : HTTP\Response { $option = Agents\Processing\Type::get( $args["type"] ); $form = REST\Response::addModal( "details", "Tool erstellen" ) ->setTitle( $option["label"] ) ->setIcon( $option["icon"] ) ->setColor( $option["color"] ) ->get() ->setAction( "/api/v1/agents/{id}/processing", "POST" ) ->addButton(...Hooks::filter( "rest.generator::button.type->submit", [], "Erstellen", "agents/{origin_id}/processing" )) ->addText( "Geben Sie diesem Tool eine für Sie passende Beschreibung, zur besseren Wiedererkennung." ) ->addField(...Hooks::filter( "rest.generator::control.type->text", [], "description", TRUE, "agents/{origin_id}/processing" )) ->addHeadline( "Einstellungen", "3" ); switch( $_GET["type"] ) { case Agents\Processing\Type::EmailDelivery : static::restConfigFormTypeEmailDelivery( $form ); break; case Agents\Processing\Type::SMSDelivery : static::restConfigFormTypeSMSDelivery( $form ); break; case Agents\Processing\Type::APIRequest : static::restConfigFormTypeApi( $form ); break; } return REST\Response::jsonResponse(); } public function restConfigDeleteOwn( int $origin, int $id ) : HTTP\Response { REST\Response::addModal( "details", "Tool Löschen" ) ->setData([ "id" => $id ]) ->setSize( REST\Wrapper\Modal\Size::sm ) ->get() ->setAction( "/api/v1/agents/{origin_id}/processing/{id}", "DELETE" ) ->addText( "Möchtest du wirklich den Datensatz (#{$id}) löschen? " ) ->addButton(...Hooks::filter( "rest.generator::button.type->cancel", [], NULL, "agents/{origin_id}/processing" )) ->addButton(...Hooks::filter( "rest.generator::button.type->delete", [], NULL, "agents/{origin_id}/processing" )); return REST\Response::jsonResponse(); } /* ### PUBLIC REST STATIC FUNCTIONS ############################# # */ public static function restConfigTable() : REST\Generator\Base\BaseGenerator { $table = REST\Generator\Data\Table::create() ->setApi( "/api/v1/agents/{id}/processing" ) ->setAction( "/api/v1/agents/{origin_id}/processing/{id}" ) ->setOrder( "name", "asc" ) ->setSearch( TRUE ) ->addButton(...Hooks::filter( "rest.generator::button.name->create", [], "agents/{id}/processing" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->text", [], "type", TRUE, TRUE, TRUE, "agents/{origin_id}/processing" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->status", [], "status", [], "agents/{origin_id}/processing" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->text", [], "description", TRUE, FALSE, TRUE, "agents/{origin_id}/processing" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->datetime", [], "date_edit", FALSE, FALSE, TRUE, "agents/{origin_id}/processing" )) ->addColumn(...Hooks::filter( "rest.generator::column.type->actions", [], [], FALSE, "agents/{origin_id}/processing" ))->get() ->addButton(...Hooks::filter( "rest.generator::action.name->edit", [], "agents/{origin_id}/processing" )) ->addButton(...Hooks::filter( "rest.generator::action.name->delete", [], "agents/{origin_id}/processing" )) ->top(); $buttons = $table->getButtons(); foreach( Agents\Processing\Type::getAll() as $type ) { $type["name"] = "type"; $buttons->addDropdown( ...Hooks::filter( "rest.generator::button.name->create", $type, "agents/{id}/processing" ) ); } return $table; } public static function restConfigFormTypeEmailDelivery( REST\Generator\Base\BaseGenerator $form ) : void { $form ->addField(...Hooks::filter( "rest.generator::control.type->tel", [], "phone", TRUE, "agents/{origin_id}/processing" )) ->top(); } public static function restConfigFormTypeSMSDelivery( REST\Generator\Base\BaseGenerator $form ) : void { $form ->get() ->addField(...Hooks::filter( "rest.generator::control.type->tel", [], "phone", TRUE, "agents/{origin_id}/processing" )) ->setTitle( "Aktuell nur Platzhalter / Muster") ->top(); } public static function restConfigFormTypeApi( REST\Generator\Base\BaseGenerator $form ) : void { $form ->get() ->setTitle( "Aktuell nur Platzhalter / Muster") ->top(); } } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Processing/Type.php **/ [ "color" => REST\Generator\Config\Color::Primary, "icon" => "bi bi-envelope-at", "label" => "E-Mail-Versand" ], 2 => [ "color" => REST\Generator\Config\Color::Secondary, "icon" => "bi bi-phone-vibrate", "label" => "SMS-Versand" ], 3 => [ "color" => REST\Generator\Config\Color::Danger, "icon" => "bi bi-plugin", "label" => "API Request" ], ]; /* ### PUBLIC FUNCTIONS ######################################### # */ } /** Datei: plugins/agents/library/ARUNOX/Plugins/Agents/Processing/Entity.php **/ Attribute\Status::class, "date_create" => Attribute\DateTime::class, "date_edit" => Attribute\DateTime::class, "type" => Type::class, ]; /* ### PUBLIC FUNCTIONS ######################################### # */ public function toggleStatus( bool $save = FALSE ) { $this->status->toggle(); if( $save ) $this->save(); } }