/** 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();
}
}