瀏覽代碼

Merge branch 'master' of https://github.com/unacms/UNA

AntonLV 7 月之前
父節點
當前提交
e82d6bea3f

+ 33 - 32
composer.lock

@@ -207,16 +207,16 @@
         },
         {
             "name": "aws/aws-sdk-php",
-            "version": "3.371.5",
+            "version": "3.373.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/aws/aws-sdk-php.git",
-                "reference": "d40b962154a146901f71561519f9d9c5a3863a6e"
+                "reference": "483fba51c28b3a0c0647bf5100e0edca82090b18"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d40b962154a146901f71561519f9d9c5a3863a6e",
-                "reference": "d40b962154a146901f71561519f9d9c5a3863a6e",
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/483fba51c28b3a0c0647bf5100e0edca82090b18",
+                "reference": "483fba51c28b3a0c0647bf5100e0edca82090b18",
                 "shasum": ""
             },
             "require": {
@@ -237,12 +237,12 @@
                 "aws/aws-php-sns-message-validator": "~1.0",
                 "behat/behat": "~3.0",
                 "composer/composer": "^2.7.8",
-                "dms/phpunit-arraysubset-asserts": "^0.4.0",
+                "dms/phpunit-arraysubset-asserts": "^v0.5.0",
                 "doctrine/cache": "~1.4",
                 "ext-dom": "*",
                 "ext-openssl": "*",
                 "ext-sockets": "*",
-                "phpunit/phpunit": "^9.6",
+                "phpunit/phpunit": "^10.0",
                 "psr/cache": "^2.0 || ^3.0",
                 "psr/simple-cache": "^2.0 || ^3.0",
                 "sebastian/comparator": "^1.2.3 || ^4.0 || ^5.0",
@@ -298,9 +298,9 @@
             "support": {
                 "forum": "https://github.com/aws/aws-sdk-php/discussions",
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.371.5"
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.373.2"
             },
-            "time": "2026-03-04T19:06:59+00:00"
+            "time": "2026-03-13T18:08:30+00:00"
         },
         {
             "name": "bshaffer/oauth2-server-php",
@@ -1098,16 +1098,16 @@
         },
         {
             "name": "guzzlehttp/psr7",
-            "version": "2.8.0",
+            "version": "2.9.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/guzzle/psr7.git",
-                "reference": "21dc724a0583619cd1652f673303492272778051"
+                "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/guzzle/psr7/zipball/21dc724a0583619cd1652f673303492272778051",
-                "reference": "21dc724a0583619cd1652f673303492272778051",
+                "url": "https://api.github.com/repos/guzzle/psr7/zipball/7d0ed42f28e42d61352a7a79de682e5e67fec884",
+                "reference": "7d0ed42f28e42d61352a7a79de682e5e67fec884",
                 "shasum": ""
             },
             "require": {
@@ -1123,6 +1123,7 @@
             "require-dev": {
                 "bamarni/composer-bin-plugin": "^1.8.2",
                 "http-interop/http-factory-tests": "0.9.0",
+                "jshttp/mime-db": "1.54.0.1",
                 "phpunit/phpunit": "^8.5.44 || ^9.6.25"
             },
             "suggest": {
@@ -1194,7 +1195,7 @@
             ],
             "support": {
                 "issues": "https://github.com/guzzle/psr7/issues",
-                "source": "https://github.com/guzzle/psr7/tree/2.8.0"
+                "source": "https://github.com/guzzle/psr7/tree/2.9.0"
             },
             "funding": [
                 {
@@ -1210,7 +1211,7 @@
                     "type": "tidelift"
                 }
             ],
-            "time": "2025-08-23T21:21:41+00:00"
+            "time": "2026-03-10T16:41:02+00:00"
         },
         {
             "name": "html2text/html2text",
@@ -1832,16 +1833,16 @@
         },
         {
             "name": "neuron-core/neuron-ai",
-            "version": "3.0.10",
+            "version": "3.2.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/neuron-core/neuron-ai.git",
-                "reference": "4ce74d0a0f0b9e9f95a3875519fb600275f3561a"
+                "reference": "9343ca51a1d0b3ce3b6d5ed3904e807bdcebb941"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/neuron-core/neuron-ai/zipball/4ce74d0a0f0b9e9f95a3875519fb600275f3561a",
-                "reference": "4ce74d0a0f0b9e9f95a3875519fb600275f3561a",
+                "url": "https://api.github.com/repos/neuron-core/neuron-ai/zipball/9343ca51a1d0b3ce3b6d5ed3904e807bdcebb941",
+                "reference": "9343ca51a1d0b3ce3b6d5ed3904e807bdcebb941",
                 "shasum": ""
             },
             "require": {
@@ -1892,7 +1893,7 @@
             "description": "The PHP Agentic Framework.",
             "support": {
                 "issues": "https://github.com/neuron-core/neuron-ai/issues",
-                "source": "https://github.com/neuron-core/neuron-ai/tree/3.0.10"
+                "source": "https://github.com/neuron-core/neuron-ai/tree/3.2.2"
             },
             "funding": [
                 {
@@ -1900,7 +1901,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2026-03-03T17:34:35+00:00"
+            "time": "2026-03-14T21:53:48+00:00"
         },
         {
             "name": "nyholm/psr7",
@@ -3361,16 +3362,16 @@
         },
         {
             "name": "stripe/stripe-php",
-            "version": "v19.4.0",
+            "version": "v19.4.1",
             "source": {
                 "type": "git",
                 "url": "https://github.com/stripe/stripe-php.git",
-                "reference": "a8c9db819f86ec47e603b92f9be99661e6a62cf4"
+                "reference": "095384404587d07de2ad1154c389c4051c5ed92f"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/stripe/stripe-php/zipball/a8c9db819f86ec47e603b92f9be99661e6a62cf4",
-                "reference": "a8c9db819f86ec47e603b92f9be99661e6a62cf4",
+                "url": "https://api.github.com/repos/stripe/stripe-php/zipball/095384404587d07de2ad1154c389c4051c5ed92f",
+                "reference": "095384404587d07de2ad1154c389c4051c5ed92f",
                 "shasum": ""
             },
             "require": {
@@ -3380,7 +3381,7 @@
                 "php": ">=5.6.0"
             },
             "require-dev": {
-                "friendsofphp/php-cs-fixer": "3.72.0",
+                "friendsofphp/php-cs-fixer": "3.94.0",
                 "phpstan/phpstan": "^1.2",
                 "phpunit/phpunit": "^5.7 || ^9.0"
             },
@@ -3414,9 +3415,9 @@
             ],
             "support": {
                 "issues": "https://github.com/stripe/stripe-php/issues",
-                "source": "https://github.com/stripe/stripe-php/tree/v19.4.0"
+                "source": "https://github.com/stripe/stripe-php/tree/v19.4.1"
             },
-            "time": "2026-02-25T17:46:20+00:00"
+            "time": "2026-03-06T22:53:13+00:00"
         },
         {
             "name": "symfony/deprecation-contracts",
@@ -4076,16 +4077,16 @@
         },
         {
             "name": "twilio/sdk",
-            "version": "8.11.1",
+            "version": "8.11.2",
             "source": {
                 "type": "git",
                 "url": "https://github.com/twilio/twilio-php",
-                "reference": "7061dc4f080787660ae2a442db620e6afad99c5a"
+                "reference": "f4536d588823f1b1e9f4f26cbf7164cb23e07c10"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/twilio/twilio-php/zipball/7061dc4f080787660ae2a442db620e6afad99c5a",
-                "reference": "7061dc4f080787660ae2a442db620e6afad99c5a",
+                "url": "https://api.github.com/repos/twilio/twilio-php/zipball/f4536d588823f1b1e9f4f26cbf7164cb23e07c10",
+                "reference": "f4536d588823f1b1e9f4f26cbf7164cb23e07c10",
                 "shasum": ""
             },
             "require": {
@@ -4122,7 +4123,7 @@
                 "sms",
                 "twilio"
             ],
-            "time": "2026-02-18T08:25:57+00:00"
+            "time": "2026-03-10T08:37:18+00:00"
         },
         {
             "name": "typesense/typesense-php",

+ 56 - 302
inc/classes/BxDolAI.php

@@ -64,33 +64,35 @@ class BxDolAI extends BxDolFactory implements iBxDolSingleton
     public static function getInstance()
     {
         if (!isset($GLOBALS['bxDolClasses'][__CLASS__])) {
-            $GLOBALS['bxDolClasses'][__CLASS__] = BxDolDb::getInstance()->isTableExists('sys_agents_automators') ? new BxDolAI() : null;
+            $GLOBALS['bxDolClasses'][__CLASS__] = BxDolDb::getInstance()->isTableExists('sys_agents_agents') ? new BxDolAI() : null;
         }
 
         return $GLOBALS['bxDolClasses'][__CLASS__];
     }
 
-    public static function getAiProviderInstance(int $iId):NeuronAI\Providers\AIProviderInterface
-    {
-        return self::getInstance()->getAiProviderInstance($iId);
-    }   
-
-    public static function getAiEmbeddingsProviderInstance(int $iId):NeuronAI\RAG\Embeddings\EmbeddingsProviderInterface
-    {
-        return self::getInstance()->getAiProviderInstance($iId);
-    }   
-
-    public static function getAgentInstance(int $iId): NeuronAI\Agent\Agent
+    public static function getAgentInstance(int $iId, array $aParams = []): NeuronAI\Agent\Agent
     {
         if (isset($GLOBALS['bxDolClasses'][__CLASS__ . '_Agent_' . $iId]))
             return $GLOBALS['bxDolClasses'][__CLASS__ . '_Agent_' . $iId];
 
         $a = BxDolAIQuery::getAgentObject($iId);
-        if (!$a || !$a['active'] || !$a['prompt_system'] || !$a['model_id']) {
-            return null;
+        if (!$a) {
+            $s = "Agent with id {$iId} not found";
+            bx_log('sys_agents', $s, BX_LOG_ERR);
+            throw new Exception($s);
+        }
+        if (!$a['active']) {
+            $s = "Agent with id {$iId} isn't active";
+            bx_log('sys_agents', $s, BX_LOG_ERR);
+            throw new Exception($s);
+        }
+        if (!$a['prompt_system'] || !$a['model_id']) {
+            $s = "Agent with id {$iId} can't be used because it haven't prompt or AI model";
+            bx_log('sys_agents', $s, BX_LOG_ERR);
+            throw new Exception($s);
         }
 
-        $o = BxDolAiAgent::make($a);
+        $o = BxDolAiAgent::make($a, $aParams);
 
         if ($a['vector_store_id']) {
             $aVectorStore = BxDolAIQuery::getVectorStoreObject($a['vector_store_id']);
@@ -105,290 +107,14 @@ class BxDolAI extends BxDolFactory implements iBxDolSingleton
         return $o;
     }
 
-    public static function getModelInstance(int $iId): NeuronAI\Providers\AIProviderInterface | NeuronAI\RAG\Embeddings\EmbeddingsProviderInterface
+    public static function getAiProviderInstance(int $iId):NeuronAI\Providers\AIProviderInterface
     {
-        if (isset($GLOBALS['bxDolClasses'][__CLASS__ . '_Model_' . $iId]))
-            return $GLOBALS['bxDolClasses'][__CLASS__ . '_Model_' . $iId];
-
-        $aProvidersWithKey = ['anthropic'];
-        $a = BxDolAIQuery::getModelObject($iId);
-        if (!$a) {
-            bx_log('sys_agents', "Agent AI Model with id {$iId} not found", BX_LOG_ERR);
-            throw new Exception("Agent AI Model with id {$iId} not found");
-        }
-        if (in_array($a['type'], $aProvidersWithKey) && empty($a['key'])) {
-            bx_log('sys_agents', "Model with id {$iId} has empty key, can't be used", BX_LOG_ERR);
-            throw new Exception("Model with id {$iId} has empty key, can't be used");
-        }
-
-        if (!$a['active']) {
-            bx_log('sys_agents', "Model with id {$iId} is not active, can't be used", BX_LOG_ERR);
-            throw new Exception("Model with id {$iId} is not active, can't be used");
-        }
-
-        $aParameters = !empty($a['params']) ? json_decode($a['params'], true) : [];
-
-        // replace markers {key} {model} in $aParameters recoursively
-        $aParameters = bx_replace_markers($aParameters, [
-            'key' => $a['key'],
-            'model' => $a['model']
-        ]);
-
-        switch($a['type']) {
-            // regular AI providers ------------------------
-            case 'anthropic':
-                $o = new NeuronAI\Providers\Anthropic\Anthropic(
-                    key: $a['key'],
-                    model: $a['model'],
-                    version: $aParameters['version'] ?? '2023-06-01',
-                    max_tokens: $aParameters['max_tokens'] ?? 8192,
-                    parameters: $aParameters['parameters'] ?? [],
-                );
-                break;
-            case 'openai-responses':
-                $o = new NeuronAI\Providers\OpenAI\Responses\OpenAIResponses(
-                    key: $a['key'],
-                    model: $a['model'],
-                    parameters: $aParameters['parameters'] ?? [],
-                    strict_response: $aParameters['strict_response'] ?? false,
-                );
-                break;
-            case 'azure-openai':
-                $o = new NeuronAI\Providers\OpenAI\AzureOpenAI(
-                    key: $a['key'],
-                    model: $a['model'],
-                    endpoint: $aParameters['endpoint'],
-                    version: $aParameters['version'],
-                    parameters: $aParameters['parameters'] ?? [],
-                    strict_response: $aParameters['strict_response'] ?? false,
-                );
-                break;
-            case 'openai-like':
-                $o = new NeuronAI\Providers\OpenAILike(
-                    baseUri: $aParameters['baseUri'],
-                    key: $a['key'],
-                    model: $a['model'],                    
-                    parameters: $aParameters['parameters'] ?? [],
-                    strict_response: $aParameters['strict_response'] ?? false,
-                );
-                break;
-            case 'ollama':
-                $o = new NeuronAI\Providers\Ollama\Ollama(
-                    url: $aParameters['url'] ?? 'http://localhost:11434/api',
-                    model: $a['model'],                    
-                    parameters: $aParameters['parameters'] ?? [],
-                );
-                break;
-            case 'gemini':
-                $o = new NeuronAI\Providers\Gemini\Gemini(
-                    key: $a['key'],
-                    model: $a['model'],
-                    parameters: $aParameters['parameters'] ?? [],
-                );
-                break;
-            case 'mistral':
-                $o = new NeuronAI\Providers\Mistral\Mistral(
-                    key: $a['key'],
-                    model: $a['model'],
-                    parameters: $aParameters['parameters'] ?? [],
-                    strict_response: $aParameters['strict_response'] ?? false,
-                );
-                break;
-            case 'huggingface':
-                $o = new NeuronAI\Providers\HuggingFace\HuggingFace(
-                    key: $a['key'],
-                    model: $a['model'],
-                    inferenceProvider: $aParameters['inferenceProvider'] ?? 'hf-inference/models', // cohere, groq, etc - https://github.com/neuron-core/neuron-ai/blob/3.x/src/Providers/HuggingFace/InferenceProvider.php
-                    parameters: $aParameters['parameters'] ?? [],
-                    strict_response: $aParameters['strict_response'] ?? false,
-                );
-                break;
-            case 'deepseek':
-                $o = new NeuronAI\Providers\DeepSeek\DeepSeek(
-                    key: $a['key'],
-                    model: $a['model'],
-                    parameters: $aParameters['parameters'] ?? [],
-                    strict_response: $aParameters['strict_response'] ?? false,
-                );
-                break;
-            case 'grok':
-                $o = new NeuronAI\Providers\XAI\Grok(
-                    key: $a['key'],
-                    model: $a['model'],
-                    parameters: $aParameters['parameters'] ?? [],
-                    strict_response: $aParameters['strict_response'] ?? false,
-                );
-                break;
-            case 'aws-bedrock':
-                $oClient = new Aws\BedrockRuntime\BedrockRuntimeClient($aParameters['client_params']);
-
-                $o = new NeuronAI\Providers\AWS\BedrockRuntime(
-                    client: $oClient,
-                    model: $a['model'],
-                    inferenceConfig: $aParameters['inferenceConfig'] ?? [],
-                );
-                break;
-            case 'cohere':
-                $o = new NeuronAI\Providers\Cohere\Cohere(
-                    key: $a['key'],
-                    model: $a['model'],
-                    baseUri: $aParameters['baseUri'] ?? 'https://api.cohere.ai/v2',
-                    parameters: $aParameters['parameters'] ?? [],
-                    strict_response: $aParameters['strict_response'] ?? false,
-                );
-                break;
-
-            // embeddings models ------------------------
-            case 'ollama-embeddings':
-                $o = new NeuronAI\RAG\Embeddings\OllamaEmbeddingsProvider(
-                    model: $a['model'],
-                    url: $aParameters['url'] ?? 'http://localhost:11434/api',
-                    parameters: $aParameters['parameters'] ?? [],
-                );
-                break;
-            case 'voyageai-embeddings':
-                $o = new NeuronAI\RAG\Embeddings\VoyageEmbeddingsProvider(
-                    key: $a['key'],
-                    model: $a['model'],
-                    dimensions: !empty($aParameters['dimensions']) ? (int)$aParameters['dimensions'] : null
-                );
-                break;
-            case 'openai-embeddings':
-                $o = new NeuronAI\RAG\Embeddings\OpenAIEmbeddingsProvider(
-                    key: $a['key'],
-                    model: $a['model'],
-                    dimensions: !empty($aParameters['dimensions']) ? (int)$aParameters['dimensions'] : 1024
-                );
-                break;
-            case 'openai-like-embeddings':
-                $o = new NeuronAI\RAG\Embeddings\OpenAILikeEmbeddings(
-                    baseUri: $aParameters['baseUri'],
-                    key: $a['key'],
-                    model: $a['model'],                    
-                    dimensions: !empty($aParameters['dimensions']) ? (int)$aParameters['dimensions'] : 1024
-                );
-                break;
-            case 'aws-bedrock-embeddings':
-                $oClient = new Aws\BedrockRuntime\BedrockRuntimeClient($aParameters['client_params']);
-                $o = new NeuronAI\RAG\Embeddings\AwsBedrockEmbeddingsProvider(
-                    client: $oClient,
-                    model: $a['model']
-                );
-                break;
-            default:
-                bx_log('sys_agents', "Model type {$a['type']} is not supported", BX_LOG_ERR);
-                throw new Exception("Model type {$a['type']} is not supported");
-        }
-
-        $GLOBALS['bxDolClasses'][__CLASS__ . '_Model_' . $iId] = $o;
-
-        return $o;
-    }
+        return BxDolAIModelFactory::getModelInstance($iId);
+    }   
 
-    public static function getVectorStoreInstance(int $iId): NeuronAI\RAG\VectorStore\VectorStoreInterface 
+    public static function getAiEmbeddingsProviderInstance(int $iId):NeuronAI\RAG\Embeddings\EmbeddingsProviderInterface
     {
-        if (isset($GLOBALS['bxDolClasses'][__CLASS__ . '_VectorStore_' . $iId]))
-            return $GLOBALS['bxDolClasses'][__CLASS__ . '_VectorStore_' . $iId];
-
-        $a = BxDolAIQuery::getVectorStoreObject($iId);
-        if (!$a) {
-            bx_log('sys_agents', "Vector store with id {$iId} not found", BX_LOG_ERR);
-            throw new Exception("Vector store with id {$iId} not found");
-        }
-
-        $aParameters = !empty($a['params']) ? json_decode($a['params'], true) : [];
-
-        // replace marker {topk} in $aParameters recoursively
-        $aParameters = bx_replace_markers($aParameters, [
-            'topk' => $a['topk'],
-        ]);
-        
-        // TODO: fix params
-        switch($a['type']) {
-            case 'file':
-                $o = new NeuronAI\RAG\VectorStore\FileVectorStore(
-                    directory: BX_DIRECTORY_STORAGE . 'vector_stores/' . $iId,
-                    topK: $a['topk'] ?? 4
-                );
-                break;
-            case 'pinecon':
-                $o = new NeuronAI\RAG\VectorStore\PineconeVectorStore(
-                    key: $a['key'],
-                    indexUrl: $a['indexUrl'],
-                    topK: $a['topk'] ?? 4,
-                    version: $a['version'] ?? '2025-04',
-                    namespace: $a['namespace'] ?? '__default__'
-                );
-                break;
-            case 'elasticsearch':
-                $oClient = Elastic\Elasticsearch\ClientBuilder::create()
-                    ->setHosts([$a['client_params']['endpoint']])
-                    ->setApiKey($a['client_params']['key'])
-                    ->build();
-                
-                $o = new NeuronAI\RAG\VectorStore\ElasticsearchVectorStore(
-                    client: $oClient,
-                    index: $a['index'],
-                    topK: $a['topk'] ?? 4
-                );
-                break;
-            case 'opensearch':
-                $oClient = (new OpenSearch\GuzzleClientFactory())->create([
-                    'base_uri' => $a['client_params']['endpoint'] ?? 'http://localhost:9200',
-                ]);
-        
-                $o = NeuronAI\RAG\VectorStore\OpenSearchVectorStore(
-                    client: $oClient,
-                    index: $a['index'],
-                    topK: $a['topk'] ?? 4,
-                );
-                break;
-            case 'typesense':
-                $oClient = new \Typesense\Client($a['client_params']);
-
-                $o = new NeuronAI\RAG\VectorStore\TypesenseVectorStore(
-                    client: $oClient,
-                    collection: $a['collection'],
-                    vectorDimension: $a['vectorDimension'] ?? 1024,
-                    topK: $a['topk'] ?? 4,
-                );
-                break;
-            case 'qdrant':
-                $o = new NeuronAI\RAG\VectorStore\QdrantVectorStore(
-                    collectionUrl: $a['collectionUrl'],
-                    key: $a['key'],
-                    topK: $a['topk'] ?? 4,
-                    dimension: $a['dimension'] ?? 1024
-                );
-                break;
-            case 'chromadb':
-                $o = new NeuronAI\RAG\VectorStore\ChromaVectorStore(
-                    collection: $a['collection'],
-                    host: $a['host'] ?? 'http://localhost:8000',
-                    tenant: $a['tenant'] ?? 'default_tenant',
-                    database: $a['database'] ?? 'default_database',
-                    key: $a['key'] ?? null,
-                    topK: $a['topk'] ?? 4
-                );
-                break;
-            case 'meilisearch':
-                $o = new NeuronAI\RAG\VectorStore\MeilisearchVectorStore(
-                    indexUid: $a['indexUid'],
-                    host: $a['host'] ?? 'http://localhost:8000',
-                    key: $a['key'] ?? null,
-                    embedder: $a['embedder'] ?? 'default',
-                    topK: $a['topk'] ?? 4,
-                    dimension: $a['dimension'] ?? 1024
-                );
-                break;
-            default:
-                bx_log('sys_agents', "Vector store type {$a['type']} is not supported", BX_LOG_ERR);
-                throw new Exception("Vector store type {$a['type']} is not supported");
-        }
-
-        $GLOBALS['bxDolClasses'][__CLASS__ . '_VectorStore_' . $iId] = $o;
-
-        return $o;
+        return BxDolAIModelFactory::getModelInstance($iId);
     }
 
     public static function callHelper($mixedHelper, $sMessage)
@@ -648,23 +374,46 @@ class BxDolAI extends BxDolFactory implements iBxDolSingleton
         return $aAutomators;
     }
 
-    public function callAgent($sType, $aAgent, $aParams = [])
+    public function callAgent($sType, $aAgent, $mixedParams = [])
     {
+        $sParams = is_string($mixedParams) ? $mixedParams : json_encode($mixedParams);
+
         // update sample data
         $a = ['alert' => 'alert_sample', 'webhook' => 'webhook_sample'];
         if (isset($a[$sType]) && empty($aAgent[$a[$sType]])) {
-            $this->updateAgentField($aAgent['id'], $a[$sType], json_encode($aParams));
+            $this->updateAgentField($aAgent['id'], $a[$sType], $sParams);
         }
 
-        $o = self::getAgentInstance($aAgent['id']);
+        // set additional params
+        $aParams = [];
+        if ('message' == $sType) {
+            $aParams = ['chat_history_subindex' => $mixedParams['sender_profile_id']];
+        }
+
+        // call agent
+        $o = self::getAgentInstance($aAgent['id'], $aParams);
         if (!$o)
             return false;
 
-        $o->chat(new UserMessage($aParams));
+        $oMessage = $o->chat(new NeuronAI\Chat\Messages\UserMessage($sParams))->getMessage();
+
+        return $oMessage->getContent();
+    }
+
+    public function sendMessengerMessage ($iSender, $iRecipient, $sMsg) 
+    {        
+        $oMessengerModule = BxDolModule::getInstance('bx_messenger');
 
-        $o->run();
+        $aAutoReplyData = [
+            'message' => $sMsg,
+            'participants' => [$iSender, $iRecipient],
+        ];
+
+        $iSaveProfileId = $oMessengerModule->setProfileId($iSender);
+        $a = $oMessengerModule->sendMessage($aAutoReplyData, $iRecipient, $iSender);
+        $oMessengerModule->setProfileId($iSaveProfileId);
 
-        return $o->getMessage();
+        return $a;
     }
 
     public function getAgentsByAlertUnitAndAction($sUnit, $sAction)
@@ -672,6 +421,11 @@ class BxDolAI extends BxDolFactory implements iBxDolSingleton
         return $this->_oDb->getAgentsByAlertUnitAndAction($sUnit, $sAction);
     }
 
+    public function getAgentsByProfileId($iProfileId)
+    {
+        return $this->_oDb->getAgentsByProfileId($iProfileId);
+    }
+
     public function callAutomator($sType, $aParams = [])
     {
         $sMethod = '_callAutomator' . bx_gen_method_name($sType);

+ 193 - 0
inc/classes/BxDolAIModelFactory.php

@@ -0,0 +1,193 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+class BxDolAIModelFactory extends BxDolFactory
+{
+    public static function getModelInstance(int $iId): NeuronAI\Providers\AIProviderInterface | NeuronAI\RAG\Embeddings\EmbeddingsProviderInterface
+    {
+        if (isset($GLOBALS['bxDolClasses'][__CLASS__ . '_Model_' . $iId]))
+            return $GLOBALS['bxDolClasses'][__CLASS__ . '_Model_' . $iId];
+
+        $aProvidersWithKey = ['anthropic'];
+        $a = BxDolAIQuery::getModelObject($iId);
+        if (!$a) {
+            bx_log('sys_agents', "Agent AI Model with id {$iId} not found", BX_LOG_ERR);
+            throw new Exception("Agent AI Model with id {$iId} not found");
+        }
+        if (in_array($a['type'], $aProvidersWithKey) && empty($a['key'])) {
+            bx_log('sys_agents', "Model with id {$iId} has empty key, can't be used", BX_LOG_ERR);
+            throw new Exception("Model with id {$iId} has empty key, can't be used");
+        }
+
+        if (!$a['active']) {
+            bx_log('sys_agents', "Model with id {$iId} is not active, can't be used", BX_LOG_ERR);
+            throw new Exception("Model with id {$iId} is not active, can't be used");
+        }
+
+        $aParametersSystem = !empty($a['params']) ? json_decode($a['params'], true) : [];
+        $aParametersUser = !empty($a['params_user']) ? json_decode($a['params_user'], true) : [];
+        $aParameters = array_merge($aParametersSystem, $aParametersUser);
+
+        // replace markers {key} {model} in $aParameters recoursively
+        $aParameters = bx_replace_markers($aParameters, [
+            'key' => $a['key'],
+            'model' => $a['model']
+        ]);
+
+        switch($a['type']) {
+            // regular AI providers ------------------------
+            case 'anthropic':
+                $o = new NeuronAI\Providers\Anthropic\Anthropic(
+                    key: $a['key'],
+                    model: $a['model'],
+                    version: $aParameters['version'] ?? '2023-06-01',
+                    max_tokens: $aParameters['max_tokens'] ?? 8192,
+                    parameters: $aParameters['parameters'] ?? [],
+                );
+                break;
+            case 'openai-responses':
+                $o = new NeuronAI\Providers\OpenAI\Responses\OpenAIResponses(
+                    key: $a['key'],
+                    model: $a['model'],
+                    parameters: $aParameters['parameters'] ?? [],
+                    strict_response: $aParameters['strict_response'] ?? false,
+                );
+                break;
+            case 'azure-openai':
+                $o = new NeuronAI\Providers\OpenAI\AzureOpenAI(
+                    key: $a['key'],
+                    model: $a['model'],
+                    endpoint: $aParameters['endpoint'],
+                    version: $aParameters['version'],
+                    parameters: $aParameters['parameters'] ?? [],
+                    strict_response: $aParameters['strict_response'] ?? false,
+                );
+                break;
+            case 'openai-like':
+                $o = new NeuronAI\Providers\OpenAILike(
+                    baseUri: $aParameters['baseUri'],
+                    key: $a['key'],
+                    model: $a['model'],                    
+                    parameters: $aParameters['parameters'] ?? [],
+                    strict_response: $aParameters['strict_response'] ?? false,
+                );
+                break;
+            case 'ollama':
+                $o = new NeuronAI\Providers\Ollama\Ollama(
+                    url: $aParameters['url'] ?? 'http://localhost:11434/api',
+                    model: $a['model'],                    
+                    parameters: $aParameters['parameters'] ?? [],
+                );
+                break;
+            case 'gemini':
+                $o = new NeuronAI\Providers\Gemini\Gemini(
+                    key: $a['key'],
+                    model: $a['model'],
+                    parameters: $aParameters['parameters'] ?? [],
+                );
+                break;
+            case 'mistral':
+                $o = new NeuronAI\Providers\Mistral\Mistral(
+                    key: $a['key'],
+                    model: $a['model'],
+                    parameters: $aParameters['parameters'] ?? [],
+                    strict_response: $aParameters['strict_response'] ?? false,
+                );
+                break;
+            case 'huggingface':
+                $o = new NeuronAI\Providers\HuggingFace\HuggingFace(
+                    key: $a['key'],
+                    model: $a['model'],
+                    inferenceProvider: $aParameters['inferenceProvider'] ?? 'hf-inference/models', // cohere, groq, etc - https://github.com/neuron-core/neuron-ai/blob/3.x/src/Providers/HuggingFace/InferenceProvider.php
+                    parameters: $aParameters['parameters'] ?? [],
+                    strict_response: $aParameters['strict_response'] ?? false,
+                );
+                break;
+            case 'deepseek':
+                $o = new NeuronAI\Providers\Deepseek\Deepseek(
+                    key: $a['key'],
+                    model: $a['model'],
+                    parameters: $aParameters['parameters'] ?? [],
+                    strict_response: $aParameters['strict_response'] ?? false,
+                );
+                break;
+            case 'grok':
+                $o = new NeuronAI\Providers\XAI\Grok(
+                    key: $a['key'],
+                    model: $a['model'],
+                    parameters: $aParameters['parameters'] ?? [],
+                    strict_response: $aParameters['strict_response'] ?? false,
+                );
+                break;
+            case 'aws-bedrock':
+                $oClient = new Aws\BedrockRuntime\BedrockRuntimeClient($aParameters['client_params']);
+
+                $o = new NeuronAI\Providers\AWS\BedrockRuntime(
+                    client: $oClient,
+                    model: $a['model'],
+                    inferenceConfig: $aParameters['inferenceConfig'] ?? [],
+                );
+                break;
+            case 'cohere':
+                $o = new NeuronAI\Providers\Cohere\Cohere(
+                    key: $a['key'],
+                    model: $a['model'],
+                    baseUri: $aParameters['baseUri'] ?? 'https://api.cohere.ai/v2',
+                    parameters: $aParameters['parameters'] ?? [],
+                    strict_response: $aParameters['strict_response'] ?? false,
+                );
+                break;
+
+            // embeddings models ------------------------
+            case 'ollama-embeddings':
+                $o = new NeuronAI\RAG\Embeddings\OllamaEmbeddingsProvider(
+                    model: $a['model'],
+                    url: $aParameters['url'] ?? 'http://localhost:11434/api',
+                    parameters: $aParameters['parameters'] ?? [],
+                );
+                break;
+            case 'voyageai-embeddings':
+                $o = new NeuronAI\RAG\Embeddings\VoyageEmbeddingsProvider(
+                    key: $a['key'],
+                    model: $a['model'],
+                    dimensions: !empty($aParameters['dimensions']) ? (int)$aParameters['dimensions'] : null
+                );
+                break;
+            case 'openai-embeddings':
+                $o = new NeuronAI\RAG\Embeddings\OpenAIEmbeddingsProvider(
+                    key: $a['key'],
+                    model: $a['model'],
+                    dimensions: !empty($aParameters['dimensions']) ? (int)$aParameters['dimensions'] : 1024
+                );
+                break;
+            case 'openai-like-embeddings':
+                $o = new NeuronAI\RAG\Embeddings\OpenAILikeEmbeddings(
+                    baseUri: $aParameters['baseUri'],
+                    key: $a['key'],
+                    model: $a['model'],                    
+                    dimensions: !empty($aParameters['dimensions']) ? (int)$aParameters['dimensions'] : 1024
+                );
+                break;
+            case 'aws-bedrock-embeddings':
+                $oClient = new Aws\BedrockRuntime\BedrockRuntimeClient($aParameters['client_params']);
+                $o = new NeuronAI\RAG\Embeddings\AwsBedrockEmbeddingsProvider(
+                    client: $oClient,
+                    model: $a['model']
+                );
+                break;
+            default:
+                bx_log('sys_agents', "Model type {$a['type']} is not supported", BX_LOG_ERR);
+                throw new Exception("Model type {$a['type']} is not supported");
+        }
+
+        $GLOBALS['bxDolClasses'][__CLASS__ . '_Model_' . $iId] = $o;
+
+        return $o;
+    }
+}

+ 7 - 1
inc/classes/BxDolAIQuery.php

@@ -896,6 +896,11 @@ class BxDolAIQuery extends BxDolDb
         return $this->getAll("SELECT * FROM `sys_agents_agents` WHERE `alert_unit` = :unit AND `alert_action` = :action", ['unit' => $sUnit, 'action' => $sAction]);
     }
 
+    public function getAgentsByProfileId($iProfileId)
+    {
+        return $this->getAll("SELECT * FROM `sys_agents_agents` WHERE `profile_id` = :profile", ['profile' => $iProfileId]);
+    }
+
     public function updateAgentField($iId, $sField, $sValue)
     {
         return $this->query("UPDATE `sys_agents_agents` SET `$sField` = :value WHERE `id` = :id", ['value' => $sValue, 'id' => $iId]);
@@ -917,7 +922,8 @@ class BxDolAIQuery extends BxDolDb
 
     public function getTools()
     {
-        return [];
+        $sQuery = "SELECT `id`, `title` FROM `sys_agents_tools` WHERE `active` = 1 ORDER BY `title` ASC";
+        return $this->getPairs($sQuery, 'id', 'title'); 
     }
 }
 

+ 53 - 0
inc/classes/BxDolAIToolFactory.php

@@ -0,0 +1,53 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+class BxDolAIToolFactory extends BxDolFactory
+{
+    public static function getToolInstance(int $iId):  NeuronAI\Tools\ToolInterface
+    {
+        if (isset($GLOBALS['bxDolClasses'][__CLASS__ . '_AiAgentTool_' . $iId]))
+            return $GLOBALS['bxDolClasses'][__CLASS__ . '_AiAgentTool_' . $iId];
+
+        $a = BxDolAIQuery::getToolObject($iId);
+        if (!$a) {
+            bx_log('sys_agents', "Tool with id {$iId} not found", BX_LOG_ERR);
+            throw new Exception("Tool with id {$iId} not found");
+        }
+
+        $aParametersSystem = !empty($a['params']) ? json_decode($a['params'], true) : [];
+        $aParametersUser = !empty($a['params_user']) ? json_decode($a['params_user'], true) : [];
+        $aParameters = array_merge($aParametersSystem, $aParametersUser);
+        
+        switch($a['type']) {
+            case 'mysql_schema':
+                $o = NeuronAI\Tools\Toolkits\MySQL\MySQLSchemaTool::make(
+                    pdo: BxDolDb::getInstance()->getLink(),
+                    tables: $aParameters['tables'] ?? null
+                );
+                break;
+            case 'mysql_select':
+                $o = NeuronAI\Tools\Toolkits\MySQL\MySQLSelectTool::make(
+                    pdo: BxDolDb::getInstance()->getLink(),
+                );
+                break;
+            case 'mysql_write':
+                $o = NeuronAI\Tools\Toolkits\MySQL\MySQLWriteTool::make(
+                    pdo: BxDolDb::getInstance()->getLink(),
+                );
+                break;
+            default:
+                bx_log('sys_agents', "Tool type {$a['type']} is not supported", BX_LOG_ERR);
+                throw new Exception("Tool type {$a['type']} is not supported");
+        }
+
+        $GLOBALS['bxDolClasses'][__CLASS__ . '_AiAgentTool_' . $iId] = $o;
+
+        return $o;
+    }
+}

+ 120 - 0
inc/classes/BxDolAIVectorStoreFactory.php

@@ -0,0 +1,120 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    UnaCore UNA Core
+ * @{
+ */
+
+
+class BxDolAIVectorStoreFactory extends BxDolFactory 
+{
+    public static function getVectorStoreInstance(int $iId): NeuronAI\RAG\VectorStore\VectorStoreInterface 
+    {
+        if (isset($GLOBALS['bxDolClasses'][__CLASS__ . '_VectorStore_' . $iId]))
+            return $GLOBALS['bxDolClasses'][__CLASS__ . '_VectorStore_' . $iId];
+
+        $a = BxDolAIQuery::getVectorStoreObject($iId);
+        if (!$a) {
+            bx_log('sys_agents', "Vector store with id {$iId} not found", BX_LOG_ERR);
+            throw new Exception("Vector store with id {$iId} not found");
+        }
+
+        $aParametersSystem = !empty($a['params']) ? json_decode($a['params'], true) : [];
+        $aParametersUser = !empty($a['params_user']) ? json_decode($a['params_user'], true) : [];
+        $aParameters = array_merge($aParametersSystem, $aParametersUser);
+
+        // replace marker {topk} in $aParameters recoursively
+        $aParameters = bx_replace_markers($aParameters, [
+            'topk' => $a['topk'],
+        ]);
+        
+        // TODO: fix params
+        switch($a['type']) {
+            case 'file':
+                $o = new NeuronAI\RAG\VectorStore\FileVectorStore(
+                    directory: BX_DIRECTORY_STORAGE . 'vector_stores/' . $iId,
+                    topK: $a['topk'] ?? 4
+                );
+                break;
+            case 'pinecon':
+                $o = new NeuronAI\RAG\VectorStore\PineconeVectorStore(
+                    key: $a['key'],
+                    indexUrl: $aParameters['indexUrl'],
+                    topK: $a['topk'] ?? 4,
+                    version: $aParameters['version'] ?? '2025-04',
+                    namespace: $aParameters['namespace'] ?? '__default__'
+                );
+                break;
+            case 'elasticsearch':
+                $oClient = Elastic\Elasticsearch\ClientBuilder::create()
+                    ->setHosts([$aParameters['client_params']['endpoint']])
+                    ->setApiKey($aParameters['client_params']['key'])
+                    ->build();
+                
+                $o = new NeuronAI\RAG\VectorStore\ElasticsearchVectorStore(
+                    client: $oClient,
+                    index: $aParameters['index'],
+                    topK: $a['topk'] ?? 4
+                );
+                break;
+            case 'opensearch':
+                $oClient = (new OpenSearch\GuzzleClientFactory())->create([
+                    'base_uri' => $aParameters['client_params']['endpoint'] ?? 'http://localhost:9200',
+                ]);
+        
+                $o = NeuronAI\RAG\VectorStore\OpenSearchVectorStore(
+                    client: $oClient,
+                    index: $aParameters['index'],
+                    topK: $a['topk'] ?? 4,
+                );
+                break;
+            case 'typesense':
+                $oClient = new \Typesense\Client($aParameters['client_params']);
+
+                $o = new NeuronAI\RAG\VectorStore\TypesenseVectorStore(
+                    client: $oClient,
+                    collection: $aParameters['collection'],
+                    vectorDimension: $aParameters['vectorDimension'] ?? 1024,
+                    topK: $a['topk'] ?? 4,
+                );
+                break;
+            case 'qdrant':
+                $o = new NeuronAI\RAG\VectorStore\QdrantVectorStore(
+                    collectionUrl: $aParameters['collectionUrl'],
+                    key: $a['key'],
+                    topK: $a['topk'] ?? 4,
+                    dimension: $aParameters['dimension'] ?? 1024
+                );
+                break;
+            case 'chromadb':
+                $o = new NeuronAI\RAG\VectorStore\ChromaVectorStore(
+                    collection: $aParameters['collection'],
+                    host: $aParameters['host'] ?? 'http://localhost:8000',
+                    tenant: $aParameters['tenant'] ?? 'default_tenant',
+                    database: $aParameters['database'] ?? 'default_database',
+                    key: $a['key'] ?? null,
+                    topK: $a['topk'] ?? 4
+                );
+                break;
+            case 'meilisearch':
+                $o = new NeuronAI\RAG\VectorStore\MeilisearchVectorStore(
+                    indexUid: $aParameters['indexUid'],
+                    host: $aParameters['host'] ?? 'http://localhost:8000',
+                    key: $a['key'] ?? null,
+                    embedder: $aParameters['embedder'] ?? 'default',
+                    topK: $a['topk'] ?? 4,
+                    dimension: $aParameters['dimension'] ?? 1024
+                );
+                break;
+            default:
+                bx_log('sys_agents', "Vector store type {$a['type']} is not supported", BX_LOG_ERR);
+                throw new Exception("Vector store type {$a['type']} is not supported");
+        }
+
+        $GLOBALS['bxDolClasses'][__CLASS__ . '_VectorStore_' . $iId] = $o;
+
+        return $o;
+    }
+}

+ 50 - 12
inc/classes/BxDolAiAgent.php

@@ -11,21 +11,23 @@ use NeuronAI\RAG\RAG;
 
 class BxDolAiAgent extends RAG
 {
-    public function __construct(protected array $aAgent)
-    {}
+    public function __construct(protected array $aAgent, protected array $aParams = [])
+    {
+        parent::__construct();
+    }
 
-    protected function provider(): AIProviderInterface
+    protected function provider(): NeuronAI\Providers\AIProviderInterface
     {
-        return BxDolAi::getModelInstance($this->aAgent['model_id']);
+        return BxDolAIModelFactory::getModelInstance($this->aAgent['model_id']);
     }
 
     protected function instructions(): string
     {
         $oPrompt = new NeuronAI\Agent\SystemPrompt(
-            background: [$aAgent['prompt_system']],
-            steps: !empty($aAgent['prompt_steps']) ? [$aAgent['prompt_steps']] : [],
-            output: !empty($aAgent['prompt_output']) ? [$aAgent['prompt_output']] : [],
-            toolsUsage: !empty($aAgent['prompt_tools']) ? [$aAgent['prompt_tools']] : []
+            background: [$this->aAgent['prompt_system']],
+            steps: !empty($this->aAgent['prompt_steps']) ? [$this->aAgent['prompt_steps']] : [],
+            output: !empty($this->aAgent['prompt_output']) ? [$this->aAgent['prompt_output']] : [],
+            toolsUsage: !empty($this->aAgent['prompt_tools']) ? [$this->aAgent['prompt_tools']] : []
         );
         return (string) $oPrompt;
     }
@@ -35,15 +37,51 @@ class BxDolAiAgent extends RAG
      */
     protected function tools(): array
     {
-        // TODO:
-        return [];
+        if ($this->aAgent['tools']) {
+            $aTools = explode(',', $this->aAgent['tools']);
+            $aToolInstances = [];
+            foreach ($aTools as $iToolId) {
+                $oTool = BxDolAIToolFactory::getToolInstance($iToolId);
+                $aToolInstances[] = $oTool;                
+            }
+            return $aToolInstances;
+        }
+        else {
+            return [];
+        }
     }
     
-    protected function vectorStore(): VectorStoreInterface
+    protected function vectorStore(): NeuronAI\RAG\VectorStore\VectorStoreInterface
     {
         if ($this->aAgent['vector_store_id'])
-            return BxDolAi::getVectorStoreInstance($this->aAgent['vector_store_id']);
+            return BxDolAIVectorStoreFactory::getVectorStoreInstance($this->aAgent['vector_store_id']);
         else
             return new NeuronAI\RAG\VectorStore\MemoryVectorStore();
     }
+
+    protected function chatHistory(): NeuronAI\Chat\History\ChatHistoryInterface
+    {
+        if ($this->aAgent['chat_history_context']) {
+            return new NeuronAI\Chat\History\SQLChatHistory(
+                thread_id: $this->getСhatHistoryThreadId(),
+                pdo: BxDolDb::getInstance()->getLink(),
+                table: 'sys_agents_chat_history',
+                contextWindow: $this->aAgent['chat_history_context']
+            );
+        }
+        else {
+            return new NeuronAI\Chat\History\InMemoryChatHistory(
+                contextWindow: 50000
+            );
+        }
+    }
+
+    protected function getСhatHistoryThreadId(): string
+    {
+        echoDbgLog($this->aParams);
+        $s = $this->aAgent['trigger'] . ':' . $this->aAgent['id'];
+        if (isset($this->aParams['chat_history_subindex']))
+            $s .=  ':' . $this->aParams['chat_history_subindex'];
+        return $s;
+    }
 }

+ 64 - 0
inc/classes/BxDolAiAlertResponse.php

@@ -0,0 +1,64 @@
+<?php defined('BX_DOL') or die('hack attempt');
+/**
+ * Copyright (c) UNA, Inc - https://una.io
+ * MIT License - https://opensource.org/licenses/MIT
+ *
+ * @defgroup    BaseGeneral Base classes for modules
+ * @ingroup     UnaModules
+ *
+ * @{
+ */
+
+class BxDolAiAlertResponse extends BxDolAlertsResponse
+{
+    public function response($oAlert)
+    {        
+        if ('bx_messenger' == $oAlert->sUnit && 'got_jot' == $oAlert->sAction) {
+            $iProfileIdSender = $oAlert->iSender;
+            $this->processMesssage ($oAlert->iSender, $oAlert->aExtras['recipient_id'], $oAlert->iObject, $oAlert->aExtras['subobject_id'], $oAlert->aExtras['subobject_info']);
+        }
+    }
+
+    protected function processMesssage ($iSender, $iRecipient, $iLotId, $iJotId, $aJotInfo) 
+    {
+        if ($iSender == $iRecipient)
+            return;
+
+        // call agents
+        $oAi = BxDolAI::getInstance();
+        if($aAgents = $oAi->getAgentsByProfileId($iRecipient)) {
+            $GLOBALS['glAgentsCallQueue'] = [];
+            foreach($aAgents as $a) {
+                if (!$a['message_profile_id'] || $iSender == $a['message_profile_id']) {
+
+                    $GLOBALS['glAgentsCallQueue'][] = [
+                        'type' => 'message',
+                        'agent' => $a,
+                        'params' => [
+                            'trigger' => 'message',
+                            'sender_profile_id' => $iSender,
+                            'recipient_profile_id' => $iRecipient,
+                            'message_lot_id' => $iLotId, 
+                            'message_id' => $iJotId,
+                            'message_text' => $aJotInfo['message'],
+                            'message_info' => $aJotInfo,
+                        ],
+                    ];
+                }
+            }
+            if (!empty($GLOBALS['glAgentsCallQueue'])) {
+                ignore_user_abort(true); 
+                set_time_limit(0);
+
+                // TODO: need a way to force messenger to show new message from the server side
+                // register_shutdown_function(function () { 
+                //     bx_ai_process_agents_call_queue();
+                // });
+                bx_ai_process_agents_call_queue(false, false); // TODO: for a while do synchronically
+            }
+
+        }
+    }
+}
+
+/** @} */

+ 3 - 2
inc/classes/BxDolAlerts.php

@@ -104,8 +104,9 @@ class BxDolAlerts extends BxDol
     public function alert()
     {
         if (isset($this->_aAlerts[$this->sUnit]) && isset($this->_aAlerts[$this->sUnit][$this->sAction]))
-            foreach($this->_aAlerts[$this->sUnit][$this->sAction] as $iHandlerId) {
-                $aHandler = $this->_aHandlers[$iHandlerId];
+
+            foreach($this->_aAlerts[$this->sUnit][$this->sAction] as $iHandlerId) {                
+                $aHandler = $this->_aHandlers[$iHandlerId];                
                 if(!$aHandler['active'])
                     continue;
 

+ 7 - 7
inc/classes/BxDolDb.php

@@ -681,9 +681,9 @@ class BxDolDb extends BxDolFactory implements iBxDolSingleton
         }
 
         if((defined('BX_DB_FULL_VISUAL_PROCESSING') && BX_DB_FULL_VISUAL_PROCESSING) || defined('BX_DOL_INSTALL')) {
-            $sOutput = '<div style="border:2px solid red;padding:4px;width:600px;margin:0px auto;">';
-            $sOutput .= '<div style="text-align:center;background-color:red;color:white;font-weight:bold;">Error</div>';
-            $sOutput .= '<div style="text-align:center;">' . $sErrorType . '</div>';
+            $sOutput = '<div style="border:2px solid red;padding:4px;width:600px;margin:0px auto;">'."\n";
+            $sOutput .= '<div style="text-align:center;background-color:red;color:white;font-weight:bold;">Error</div>'."\n";
+            $sOutput .= '<div style="text-align:center;">' . $sErrorType . '</div>'."\n";
             if((defined('BX_DB_FULL_DEBUG_MODE') && BX_DB_FULL_DEBUG_MODE) || defined('BX_DOL_INSTALL'))
 				$sOutput .= $this->errorOutput($aError);
             $sOutput .= '</div>';
@@ -1370,15 +1370,15 @@ class BxDolDb extends BxDolFactory implements iBxDolSingleton
         $sOutput = '';
         
         if(!empty($aError['query']))
-            $sOutput .= '<p><b>Query:</b><br />' . bx_process_output($aError['query']) . '</p>';
+            $sOutput .= '<p><b>Query:</b><br />' . bx_process_output($aError['query']) . '</p>'."\n";
 
         if(!empty($aError['message']))
-            $sOutput .= '<p><b>Mysql error:</b><br />' . $aError['message'] . '</p>';
+            $sOutput .= '<p><b>Mysql error:</b><br />' . $aError['message'] . '</p>'."\n";
 
 		if(!empty($aErrorLocation))
-			$sOutput .= '<p><b>Location:</b><br />The error was found in <b>' . $aErrorLocation['function'] . '</b> function in the file <b>' . $aErrorLocation['file'] . '</b> at line <b>' . $aErrorLocation['line'] . '</b>.</p>';
+			$sOutput .= '<p><b>Location:</b><br />The error was found in <b>' . $aErrorLocation['function'] . '</b> function in the file <b>' . $aErrorLocation['file'] . '</b> at line <b>' . $aErrorLocation['line'] . '</b>.</p>'."\n";
 
-        $sOutput .= '<p><b>collation_connection:</b><br />' . $this->getOne("SELECT @@collation_connection") . '</p>';
+        $sOutput .= '<p><b>collation_connection:</b><br />' . $this->getOne("SELECT @@collation_connection") . '</p>'."\n";
         
 		if(!empty($aError['trace'])) {
             $sBackTrace = print_r($aError['trace'], true);

+ 29 - 0
inc/utils.inc.php

@@ -2907,5 +2907,34 @@ function bx_mem_cache_set(string $sKey, mixed $mixedData): mixed
     return $GLOBALS['glMemCache'][$sKey] = $mixedData;
 }
 
+function bx_ai_process_agents_call_queue($bFinishRequest = true, $bExit = true)
+{
+    if ($bFinishRequest) {
+        if (function_exists('fastcgi_finish_request')) {
+            fastcgi_finish_request();
+        } else {
+            @ob_end_flush();
+            @flush();
+        }
+    }
+
+    if (!empty($GLOBALS['glAgentsCallQueue']))
+    {
+        $oAi = BxDolAI::getInstance();
+        foreach ($GLOBALS['glAgentsCallQueue'] as $r) 
+        {
+            $sMessage = $oAi->callAgent($r['type'], $r['agent'], $r['params']);
+
+            $oParsedown = new Parsedown();
+            $oParsedown->setSafeMode(true);
+            $sMessageHtml = $oParsedown->text($sMessage);
+            $oAi->sendMessengerMessage($r['agent']['profile_id'], $r['params']['sender_profile_id'], str_replace('\n', '', $sMessageHtml));
+        }
+    }
+    
+    if ($bExit) {
+        exit(0);
+    }
+}
 
 /** @} */

文件差異過大導致無法顯示
+ 0 - 0
install/sql/system.sql


+ 8 - 1
modules/base/general/classes/BxBaseModGeneralModule.php

@@ -4153,9 +4153,16 @@ class BxBaseModGeneralModule extends BxDolModule
         return $oActions->{bx_is_api() ? 'getCodeAPI' : 'getCode'}();
     }
 
+    public function setProfileId($i)
+    {
+        $iOldProfileId = $this->_iProfileId;
+        $this->_iProfileId = $i;
+        return $iOldProfileId;
+    }
+    
     public function getProfileId()
     {
-    	return bx_get_logged_profile_id();
+        return $this->_iProfileId;
     }
 
     public function getProfileInfo($iUserId = false)

+ 10 - 1
modules/boonex/english/data/langs/system/en.xml

@@ -2295,7 +2295,7 @@
     <string name="_sys_agents_field_webhook_key_info"><![CDATA[Unique key for webhook endpoint, used as Bearer Authentication header.]]></string>
     <string name="_sys_agents_field_webhook_sample"><![CDATA[Webhook: sample payload]]></string>
     <string name="_sys_agents_field_message_profile"><![CDATA[Message: profile for communication with agent]]></string>
-    <string name="_sys_agents_field_message_profile_info"><![CDATA[Profile to use for sending messages to the agent, messages form other users are ignored.]]></string>
+    <string name="_sys_agents_field_message_profile_info"><![CDATA[Profile to use for sending messages to the agent, messages from other users are ignored.]]></string>
     <string name="_sys_agents_field_vector_store_id"><![CDATA[Knowledge]]></string>
     <string name="_sys_agents_field_vector_store_id_info"><![CDATA[Vector store for knowledge retrieval.]]></string>
     <string name="_sys_agents_field_tools"><![CDATA[Tools]]></string>
@@ -2308,6 +2308,12 @@
     <string name="_sys_agents_field_prompt_output_info"><![CDATA[Instructions for formatting the output.]]></string>
     <string name="_sys_agents_field_prompt_tools"><![CDATA[Tools Prompt]]></string>
     <string name="_sys_agents_field_prompt_tools_info"><![CDATA[Instructions for using available tools.]]></string>
+    <string name="_sys_agents_tools_popup_edit"><![CDATA[Edit tool]]></string>
+    <string name="_sys_agents_field_title"><![CDATA[Title]]></string>
+    <string name="_sys_agents_tools_field_params"><![CDATA[Parameters]]></string>
+    <string name="_sys_agents_tools_field_params_info"><![CDATA[Parameters for configuring the tool, specific to each tool]]></string>
+    <string name="_sys_agents_field_chat_history_context"><![CDATA[Chat history context window]]></string>
+    <string name="_sys_agents_field_chat_history_context_info"><![CDATA[Automatically cut the conversation history based on the size of the context window, set to '0' to disable chat history]]></string>
     <string name="_sys_agents_automators_field_providers"><![CDATA[Providers]]></string>
     <string name="_sys_agents_automators_field_providers_add"><![CDATA[Add]]></string>
     <string name="_sys_agents_automators_field_helpers"><![CDATA[Helpers]]></string>
@@ -2580,6 +2586,9 @@ If it is not enabled then please consider implement this optimization, since it
 	<string name="_sys_audit_userside_caching"><![CDATA[User-side caching for static content]]></string>
 	<string name="_sys_audit_version"><![CDATA[version]]></string>
 	<string name="_sys_audit_version_script"><![CDATA[UNA version]]></string>
+
+	<string name="_sys_agents_agents_popup_edit"><![CDATA[Edit "{0}" agent]]></string>
+
         <string name="_sys_block_type_custom"><![CDATA[custom]]></string>
 	<string name="_sys_block_type_html"><![CDATA[html]]></string>
 	<string name="_sys_block_type_image"><![CDATA[image]]></string>

+ 9 - 0
modules/boonex/russian/data/langs/system/ru.xml

@@ -2283,6 +2283,12 @@
         <string name="_sys_agents_field_prompt_output_info"><![CDATA[Инструкции по форматированию результата.]]></string>
         <string name="_sys_agents_field_prompt_tools"><![CDATA[Инструкция по инструментам]]></string>
         <string name="_sys_agents_field_prompt_tools_info"><![CDATA[Инструкции по использованию доступных инструментов.]]></string>
+        <string name="_sys_agents_tools_popup_edit"><![CDATA[Редактирование инструмента]]></string>
+        <string name="_sys_agents_field_title"><![CDATA[Название]]></string>
+        <string name="_sys_agents_tools_field_params"><![CDATA[Параметры]]></string>
+        <string name="_sys_agents_tools_field_params_info"><![CDATA[Параметры для конфигурации этого инструмента, свои для каждого инструмента]]></string>
+        <string name="_sys_agents_field_chat_history_context"><![CDATA[Размер контекста истории чата]]></string>
+        <string name="_sys_agents_field_chat_history_context_info"><![CDATA[Автоматически обрезать историю диалога на основе размера контекста, 0 - отключить историю чата]]></string>
         <string name="_sys_agents_automators_field_providers"><![CDATA[Поставщики]]></string>
         <string name="_sys_agents_automators_field_providers_add"><![CDATA[Добавить]]></string>
         <string name="_sys_agents_automators_field_helpers"><![CDATA[Помощники]]></string>
@@ -2555,6 +2561,9 @@
 	<string name="_sys_audit_userside_caching"><![CDATA[Кэширование статичного содержимого на стороне пользователя]]></string>
 	<string name="_sys_audit_version"><![CDATA[версия]]></string>
 	<string name="_sys_audit_version_script"><![CDATA[Версия UNA]]></string>
+
+	<string name="_sys_agents_agents_popup_edit"><![CDATA[Редактирование агента "{0}"]]></string>
+
         <string name="_sys_block_type_custom"><![CDATA[индивидуальный]]></string>
 	<string name="_sys_block_type_html"><![CDATA[html]]></string>
 	<string name="_sys_block_type_image"><![CDATA[изображение]]></string>

+ 113 - 161
studio/template/scripts/BxBaseStudioAgentsAgents.php

@@ -93,11 +93,9 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
         $oAI = BxDolAI::getInstance();
 
         $iId = $this->_getId();
-        $aAutomator = $oAI->getAutomator($iId);
-        $aAutomator['providers'] = $this->_oDb->getAutomatorsBy(['sample' => 'providers_by_id_pairs', 'id' => $iId]);
-        $aAutomator['helpers'] = $this->_oDb->getAutomatorsBy(['sample' => 'helpers_by_id_pairs', 'id' => $iId]);
+        $aAgent = BxDolAiQuery::getAgentObject($iId);
 
-        $aForm = $this->_getFormEdit($sAction, $aAutomator);
+        $aForm = $this->_getFormEdit($sAction, $aAgent);
         $oForm = new BxTemplFormView($aForm);
         $oForm->initChecker();
 
@@ -105,97 +103,8 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
             $aValsToAdd = [];
 
             $sName = $oForm->getCleanValue($this->_sFieldName);
-            if($aAutomator[$this->_sFieldName] != $sName) {
-                $sName = $this->_getUniqName($sName);
-                BxDolForm::setSubmittedValue($this->_sFieldName, $sName, $oForm->aFormAttrs['method']);
-            }
-
-            /**
-             * Process Providers
-             */
-            $aProvidersIds = $oForm->getCleanValue('providers_ids');
-            $bProvidersIds = !empty($aProvidersIds) && is_array($aProvidersIds);
-            $aProvidersValues = $oForm->getCleanValue('providers');
-            $bProvidersValues = !empty($aProvidersValues) && is_array($aProvidersValues);
-
-            //--- Providers: Remove deleted
-            if(!empty($aAutomator['providers']) && is_array($aAutomator['providers']))
-                $this->_oDb->deleteAutomatorProvidersById(array_diff(array_keys($aAutomator['providers']), $bProvidersIds ? $aProvidersIds : []));
-
-            //--- Providers: Update existed
-            if($bProvidersIds)
-                foreach($aProvidersIds as $iIndex => $iApId)
-                    $this->_oDb->updateAutomatorProvider(['provider_id' => (int)$aProvidersValues[$iIndex]], ['id' => (int)$iApId]);
-
-            //--- Providers: Add new
-            $iProvidersIds = $bProvidersIds ? count($aProvidersIds) : 0;
-            $iProvidersValues = $bProvidersValues ? count($aProvidersValues) : 0;
-            if($iProvidersValues > $iProvidersIds) {
-                $aProvidersValues = array_slice($aProvidersValues, $iProvidersIds);
-                foreach($aProvidersValues as $iProvidersValue)
-                    $this->_oDb->insertAutomatorProvider([
-                        'automator_id' => $iId,
-                        'provider_id' => (int)$iProvidersValue,
-                    ]);
-            }
-
-            /**
-             * Process Helpers
-             */
-            $aHelpersIds = $oForm->getCleanValue('helpers_ids');
-            $bHelpersIds = !empty($aHelpersIds) && is_array($aHelpersIds);
-            $aHelpersValues = $oForm->getCleanValue('helpers');
-            $bHelpersValues = !empty($aHelpersValues) && is_array($aHelpersValues);
-
-            //--- Helpers: Remove deleted
-            if(!empty($aAutomator['helpers']) && is_array($aAutomator['helpers']))
-                $this->_oDb->deleteAutomatorHelpersById(array_diff(array_keys($aAutomator['helpers']), $bHelpersIds ? $aHelpersIds : []));
-
-            //--- Helpers: Update existed
-            if($bHelpersIds)
-                foreach($aHelpersIds as $iIndex => $iAhId)
-                    $this->_oDb->updateAutomatorHelper(['helper_id' => (int)$aHelpersValues[$iIndex]], ['id' => (int)$iAhId]);
-
-            //--- Helpers: Add new
-            $iHelpersIds = $bHelpersIds ? count($aHelpersIds) : 0;
-            $iHelpersValues = $bHelpersValues ? count($aHelpersValues) : 0;
-            if($iHelpersValues > $iHelpersIds) {
-                $aHelpersValues = array_slice($aHelpersValues, $iHelpersIds);
-                foreach($aHelpersValues as $iHelpersValue)
-                    $this->_oDb->insertAutomatorHelper([
-                        'automator_id' => $iId,
-                        'helper_id' => (int)$iHelpersValue,
-                    ]);
-            }
-
-            /**
-             * Process Assistants
-             */
-            $aAssistantsIds = $oForm->getCleanValue('assistants_ids');
-            $bAssistantsIds = !empty($aAssistantsIds) && is_array($aAssistantsIds);
-            $aAssistantsValues = $oForm->getCleanValue('assistants');
-            $bAssistantsValues = !empty($aAssistantsValues) && is_array($aAssistantsValues);
-
-            //--- Assistants: Remove deleted
-            if(!empty($aAutomator['assistants']) && is_array($aAutomator['assistants']))
-                $this->_oDb->deleteAutomatorAssistantsById(array_diff(array_keys($aAutomator['assistants']), $bAssistantsIds ? $aAssistantsIds : []));
-
-            //--- Assistants: Update existed
-            if($bAssistantsIds)
-                foreach($aAssistantsIds as $iIndex => $iAhId)
-                    $this->_oDb->updateAutomatorAssistant(['assistant_id' => (int)$aAssistantsValues[$iIndex]], ['id' => (int)$iAhId]);
-
-            //--- Assistants: Add new
-            $iAssistantsIds = $bAssistantsIds ? count($aAssistantsIds) : 0;
-            $iAssistantsValues = $bAssistantsValues ? count($aAssistantsValues) : 0;
-            if($iAssistantsValues > $iAssistantsIds) {
-                $aAssistantsValues = array_slice($aAssistantsValues, $iAssistantsIds);
-                foreach($aAssistantsValues as $iAssistantsValue)
-                    $this->_oDb->insertAutomatorAssistant([
-                        'automator_id' => $iId,
-                        'assistant_id' => (int)$iAssistantsValue,
-                    ]);
-            }
+            $sName = $this->_getUniqName($sName);
+            BxDolForm::setSubmittedValue($this->_sFieldName, $sName, $oForm->aFormAttrs['method']);
 
             $iProfileId = $oForm->getCleanValue('profile_id');
             if(empty($iProfileId)) {
@@ -205,53 +114,27 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
 
                 $aValsToAdd['profile_id'] = $iProfileId;
             }
+  
+            $iModel = $oForm->getCleanValue('model_id');
+            $sTrigger = $oForm->getCleanValue('trigger');
 
-            $sSchedulerTime = $oForm->getCleanValue('scheduler_time');
-            if(!empty($sSchedulerTime))
-                $aValsToAdd['params'] = json_encode(['scheduler_time' => $sSchedulerTime]);
-
-            if($oForm->update($iId, $aValsToAdd) !== false) {
-                if(($oCmts = BxDolAI::getInstance()->getAutomatorCmtsObject($iId, $oTemplate)) !== null) {
-                    $sInstructions = $oAI->getAutomatorInstruction('profile', $iProfileId);
-
-                    $aProviders = $this->_oDb->getAutomatorsBy(['sample' => 'providers_by_id_pairs', 'id' => $iId]);
-                    if(!empty($aProviders) && is_array($aProviders))
-                        $sInstructions .= $oAI->getAutomatorInstruction('providers', array_values($aProviders));
-
-                    $aHelpers = $this->_oDb->getAutomatorsBy(['sample' => 'helpers_by_id_pairs', 'id' => $iId]);
-                    if(!empty($aHelpers) && is_array($aHelpers))
-                        $sInstructions .= $oAI->getAutomatorInstruction('helpers', array_values($aHelpers));
-
-                    $aAssistants = $this->_oDb->getAutomatorsBy(['sample' => 'assistants_by_id_pairs', 'id' => $iId]);
-                    if(!empty($aAssistants) && is_array($aAssistants))
-                        $sInstructions .= $oAI->getAutomatorInstruction('assistants', array_values($aAssistants));
-
-                    $oCmts->addAuto([
-                        'cmt_author_id' => $iProfileId,
-                        'cmt_parent_id' => 0,
-                        'cmt_text' => $sInstructions
-                    ]);
-
-                    if(($oMessage = new BxDolAIMessageString('hb', $sInstructions)) && ($sResponse = $oAI->getModelObject($aAutomator['model_id'])->getResponse($aAutomator['type'], $oMessage, $aAutomator['params'])) !== false) {
-                        sleep(1);
-                        $oCmts->addAuto([
-                            'cmt_author_id' => $this->_iProfileIdAi,
-                            'cmt_parent_id' => 0,
-                            'cmt_text' => $sResponse
-                        ]);
-                    }
+            $sTools = is_array($oForm->getCleanValue('tools')) ? implode(',', $oForm->getCleanValue('tools')) : '';
+            $aValsToAdd['tools'] = $sTools;
+
+            $bIsValid = true;
+            if($bIsValid) {
+                if(($iId = $oForm->update($iId, $aValsToAdd)) !== false) {
+                    $aRes = ['grid' => $this->getCode(false), 'blink' => $iId];
                 }
+                else
+                    $aRes = ['msg' => _t('_sys_txt_error_occured')];
 
-                $aRes = ['grid' => $this->getCode(false), 'blink' => $iId];
+                return echoJson($aRes);
             }
-            else
-                $aRes = ['msg' => _t('_sys_txt_error_occured')];
-
-            return echoJson($aRes);
         } 
 
         $sFormId = $oForm->getId();
-        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($sFormId . '_popup', _t('_sys_agents_popup_edit'), $this->_oTemplate->parseHtmlByName('agents_automator_form.html', [
+        $sContent = BxTemplStudioFunctions::getInstance()->popupBox($sFormId . '_popup', _t('_sys_agents_agents_popup_edit', $aAgent['name']), $this->_oTemplate->parseHtmlByName('agents_automator_form.html', [
             'form_id' => $sFormId,
             'form' => $oForm->getCode(true),
             'object' => $this->_sObject,
@@ -294,8 +177,7 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
         $aForm = $this->_getForm($sAction, $aAgent);
         $aForm['form_attrs']['action'] .= '&id=' . $aAgent['id'];
 
-        unset($aForm['inputs']['type']);
-        unset($aForm['inputs']['message']);
+        unset($aForm['inputs']['name']);
 
         return $aForm;
     }
@@ -325,7 +207,6 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
                     'required' => '1',
                     'checker' => [
                         'func' => 'Avail',
-                        'params' => [],
                         'error' => _t('_sys_agents_form_field_err_enter'),
                     ],
                     'db' => [
@@ -342,7 +223,6 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
                     'required' => '1',
                     'checker' => [
                         'func' => 'Avail',
-                        'params' => [],
                         'error' => _t('_sys_agents_form_field_err_select'),
                     ],
                     'db' => [
@@ -356,7 +236,12 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
                     'info' => _t('_sys_agents_field_profile_id_inf'),
                     'value' => isset($aAgent['profile_id']) ? $aAgent['profile_id'] : 0,
                     'values' => bx_srv('system', 'get_options_agents_profile', [], 'TemplServices'),
-                    'required' => '0',
+                    'required' => '1',
+                    'required' => '1',
+                    'checker' => [
+                        'func' => 'Avail',
+                        'error' => _t('_sys_agents_form_field_err_select'),
+                    ],
                     'db' => [
                         'pass' => 'Int',
                     ]
@@ -383,14 +268,19 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
                         'pass' => 'Xss',
                     ]
                 ],
+
+                'alert_section' => array(
+                    'type' => 'block_header',
+                    'caption' => 'Trigger - alert',
+                    'collapsable' => true,
+                    'collapsed' => true,
+                ),
+
                 'alert_sample' => [
                     'type' => 'custom',
                     'name' => 'alert_sample',
                     'caption' => _t('_sys_agents_field_alert_sample'),
                     'content' => isset($aAgent['alert_sample']) ? $aAgent['alert_sample'] : _t('_sys_agents_waiting_for_sample_data'),
-                    // 'tr_attrs' => [
-                    //     'style' => $sType != 'alert' ? 'display:none' : ''
-                    // ],
                 ],
                 'alert_unit' => [
                     'type' => 'text',
@@ -398,9 +288,6 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
                     'caption' => _t('_sys_agents_field_alert_unit'),
                     'info' => _t('_sys_agents_field_alert_unit_info'),
                     'value' => isset($aAgent['alert_unit']) ? $aAgent['alert_unit'] : '',
-                    // 'tr_attrs' => [
-                    //     'style' => $sType != 'alert' ? 'display:none' : ''
-                    // ],
                     'db' => [
                         'pass' => 'Xss',
                     ],
@@ -410,31 +297,41 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
                     'name' => 'alert_action',
                     'caption' => _t('_sys_agents_field_alert_action'),
                     'value' => isset($aAgent['alert_action']) ? $aAgent['alert_action'] : '',
-                    // 'tr_attrs' => [
-                    //    'style' => $sType != 'alert' ? 'display:none' : ''
-                    // ],
                     'db' => [
                         'pass' => 'Xss',
                     ],
                 ],
+
+                'scheduler_section' => array(
+                    'type' => 'block_header',
+                    'caption' => 'Trigger - scheduler',
+                    'collapsable' => true,
+                    'collapsed' => true,
+                ),
+
                 'scheduler_cron' => [
                     'type' => 'text',
                     'name' => 'scheduler_cron',
                     'caption' => _t('_sys_agents_field_scheduler_cron'),
                     'info' => _t('_sys_agents_field_scheduler_cron_info'),
                     'value' => isset($aAgent['scheduler_cron']) ? $aAgent['scheduler_cron'] : '',
-                    // 'tr_attrs' => [
-                    //     'style' => $sType != 'scheduler' ? 'display:none' : ''
-                    // ],
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
                 ],
+
+                'webhook_section' => array(
+                    'type' => 'block_header',
+                    'caption' => 'Trigger - webhook',
+                    'collapsable' => true,
+                    'collapsed' => true,
+                ),
+
                 'webhook_sample' => [
                     'type' => 'custom',
                     'name' => 'webhook_sample',
                     'caption' => _t('_sys_agents_field_webhook_sample'),
                     'content' => isset($aAgent['webhook_sample']) ? $aAgent['webhook_sample'] : _t('_sys_agents_waiting_for_sample_data'),
-                    // 'tr_attrs' => [
-                    //     'style' => $sType != 'webhook' ? 'display:none' : ''
-                    // ],
                 ],
                 'webhook_key' => [
                     'type' => 'text',
@@ -442,20 +339,34 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
                     'caption' => _t('_sys_agents_field_webhook_key'),
                     'info' => _t('_sys_agents_field_webhook_key_info'),
                     'value' => isset($aAgent['webhook_key']) ? $aAgent['webhook_key'] : '',
-                    // 'tr_attrs' => [
-                    //     'style' => $sType != 'webhook' ? 'display:none' : ''
-                    // ],
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
                 ],
+
+                'message_section' => array(
+                    'type' => 'block_header',
+                    'caption' => 'Trigger - message',
+                    'collapsable' => true,
+                    'collapsed' => true,
+                ),
+
                 'message_profile_id' => [
                     'type' => 'select',
                     'name' => 'message_profile_id',
                     'caption' => _t('_sys_agents_field_message_profile'),
                     'info' => _t('_sys_agents_field_message_profile_info'),
                     'value' => isset($aAgent['message_profile_id']) ? $aAgent['message_profile_id'] : '',
-                    // 'tr_attrs' => [
-                    //     'style' => $sType != 'message' ? 'display:none' : ''
-                    // ],
+                    'values' => bx_srv('system', 'get_options_agents_profile', [true, '_adm_nav_txt_items_visible_for_all'], 'TemplServices'),
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
                 ],
+
+                'message_section_end' => array(
+                    'type' => 'block_end',
+                ),
+
                 'vector_store_id' => [
                     'type' => 'select',
                     'name' => 'vector_store_id',
@@ -463,6 +374,24 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
                     'info' => _t('_sys_agents_field_vector_store_id_info'),
                     'value' => isset($aAgent['vector_store_id']) ? $aAgent['vector_store_id'] : 0,
                     'values' => $this->_oDb->getVectorStores(),
+                    'required' => '1',
+                    'checker' => [
+                        'func' => 'Avail',
+                        'error' => _t('_sys_agents_form_field_err_select'),
+                    ],
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
+                ],
+                'chat_history_context' => [
+                    'type' => 'text',
+                    'name' => 'chat_history_context',
+                    'caption' => _t('_sys_agents_field_chat_history_context'),
+                    'info' => _t('_sys_agents_field_chat_history_context_info'),
+                    'value' => isset($aAgent['chat_history_context']) ? $aAgent['chat_history_context'] : 50000,
+                    'db' => [
+                        'pass' => 'Int',
+                    ],
                 ],
                 'tools' => [
                     'type' => 'custom',
@@ -477,13 +406,27 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
                     'caption' => _t('_sys_agents_field_prompt_system'),
                     'info' => _t('_sys_agents_field_prompt_system_info'),
                     'value' => isset($aAgent['prompt_system']) ? $aAgent['prompt_system'] : '',
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
                 ],
+
+                'prompts_section' => array(
+                    'type' => 'block_header',
+                    'caption' => 'Other prompts',
+                    'collapsable' => true,
+                    'collapsed' => true,
+                ),
+
                 'prompt_steps' => [
                     'type' => 'textarea',
                     'name' => 'prompt_steps',
                     'caption' => _t('_sys_agents_field_prompt_steps'),
                     'info' => _t('_sys_agents_field_prompt_steps_info'),
                     'value' => isset($aAgent['prompt_steps']) ? $aAgent['prompt_steps'] : '',
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
                 ],
                 'prompt_output' => [
                     'type' => 'textarea',
@@ -491,6 +434,9 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
                     'caption' => _t('_sys_agents_field_prompt_output'),
                     'info' => _t('_sys_agents_field_prompt_output_info'),
                     'value' => isset($aAgent['prompt_output']) ? $aAgent['prompt_output'] : '',
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
                 ],
                 'prompt_tools' => [
                     'type' => 'textarea',
@@ -498,7 +444,13 @@ class BxBaseStudioAgentsAgents extends BxDolStudioAgentsAgents
                     'caption' => _t('_sys_agents_field_prompt_tools'),
                     'info' => _t('_sys_agents_field_prompt_tools_info'),
                     'value' => isset($aAgent['prompt_tools']) ? $aAgent['prompt_tools'] : '',
+                    'db' => [
+                        'pass' => 'Xss',
+                    ],
                 ],
+                'prompts_section_end' => array(
+                    'type' => 'block_end',
+                ),
                 'submit' => $this->_getFormControls(),
 
             ),

+ 7 - 3
studio/template/scripts/BxBaseStudioAgentsVectorStoreData.php

@@ -19,7 +19,7 @@ class BxBaseStudioAgentsVectorStoreData extends BxDolStudioAgentsVectorStoreData
     protected function _delete ($mixedId)
     {
         $r = $this->_oDb->getVectorStoreDataById($mixedId);
-        $oVectorStore = BxDolAI::getVectorStoreInstance($r['vector_store_id']);
+        $oVectorStore = BxDolAIVectorStoreFactory::getVectorStoreInstance($r['vector_store_id']);
         if ($oVectorStore) {
             $oVectorStore->deleteBySource($r['vector_store_id'], $r['id']);
         }
@@ -53,6 +53,10 @@ class BxBaseStudioAgentsVectorStoreData extends BxDolStudioAgentsVectorStoreData
         return parent::_getCellDefault ($mixedValue, $sKey, $aField, $aRow);
     } 
 
+    /**
+     * Process files which were added to the vector store. 
+     * It takes files converts it to the vector and store in the appropriate vector store.
+     */
     static public function processPendingData()
     { 
         $iLimit = 5; // TODO: add to settings
@@ -64,8 +68,8 @@ class BxBaseStudioAgentsVectorStoreData extends BxDolStudioAgentsVectorStoreData
                 BxDolAiQuery::updateVectorStoreDataStatus($r['id'], 'processing');
 
                 $aVectorStore = BxDolAiQuery::getVectorStoreObject($r['vector_store_id']);
-                $oVectorStore = BxDolAI::getVectorStoreInstance($aVectorStore['id']);
-                $oEmbedder = BxDolAI::getModelInstance($aVectorStore['embedding_provider_id']);
+                $oVectorStore = BxDolAIVectorStoreFactory::getVectorStoreInstance($aVectorStore['id']);
+                $oEmbedder = BxDolAIModelFactory::getModelInstance($aVectorStore['embedding_provider_id']);
                 $sError = '';
 
                 if ($oEmbedder && $oVectorStore) {

+ 2 - 2
template/scripts/BxBaseServices.php

@@ -1527,11 +1527,11 @@ class BxBaseServices extends BxDol implements iBxDolProfileService
         return ['' => _t('_Select_one')] + BxDolAI::getInstance()->getModels(['active' => true, 'hidden' => false]);
     }
 
-    public function serviceGetOptionsAgentsProfile($bSelectOne = true)
+    public function serviceGetOptionsAgentsProfile($bSelectOne = true, $sSelectOneLangKey = '_Select_one')
     {
         $aResult = [];
         if($bSelectOne)
-            $aResult[] = ['key' => '', 'value' => _t('_Select_one')];
+            $aResult[] = ['key' => '', 'value' => _t($sSelectOneLangKey)];
 
         $aAccountsIds = BxDolAccountQuery::getInstance()->getOperators();
         foreach($aAccountsIds as $iAccountId) {

部分文件因文件數量過多而無法顯示