19
19
use core \exception \coding_exception ;
20
20
use core_ai \aiactions \base ;
21
21
use core_ai \aiactions \responses ;
22
-
22
+ use core \ plugininfo \ aiprovider as aiproviderplugin ;
23
23
/**
24
24
* AI subsystem manager.
25
25
*
@@ -79,7 +79,7 @@ public static function get_supported_actions(string $pluginname): array {
79
79
*/
80
80
public function get_providers_for_actions (array $ actions , bool $ enabledonly = false ): array {
81
81
$ providers = [];
82
- $ instances = $ this ->get_provider_instances ();
82
+ $ instances = $ this ->get_sorted_providers ();
83
83
foreach ($ actions as $ action ) {
84
84
$ providers [$ action ] = [];
85
85
foreach ($ instances as $ instance ) {
@@ -388,6 +388,11 @@ public function create_provider_instance(
388
388
389
389
$ id = $ this ->db ->insert_record ('ai_providers ' , $ provider ->to_record ());
390
390
391
+ // Ensure the provider instance order config gets updated if the provider is enabled.
392
+ if ($ enabled ) {
393
+ $ this ->update_provider_order ($ id , \core \plugininfo \aiprovider::ENABLE );
394
+ }
395
+
391
396
return $ provider ->with (id: $ id );
392
397
}
393
398
@@ -410,7 +415,7 @@ public function get_provider_record(array $filter = [], int $strictness = IGNORE
410
415
* Get the provider records according to the filter.
411
416
*
412
417
* @param array|null $filter The filterable elements to get the records from.
413
- * @return \stdClass[]
418
+ * @return array
414
419
*/
415
420
public function get_provider_records (?array $ filter = null ): array {
416
421
return $ this ->db ->get_records (
@@ -428,7 +433,6 @@ public function get_provider_records(?array $filter = null): array {
428
433
*
429
434
* @param null|array $filter The database filter to apply when fetching provider records.
430
435
* @return array An array of instantiated provider objects.
431
- * @throws \dml_exception If there is a database error during record retrieval.
432
436
*/
433
437
public function get_provider_instances (?array $ filter = null ): array {
434
438
// Filter out any null values from the array (providers that couldn't be instantiated).
@@ -469,7 +473,6 @@ function ($record): ?provider {
469
473
* @param array|null $config the configuration of the provider instance to be updated.
470
474
* @param array|null $actionconfig the action configuration of the provider instance to be updated.
471
475
* @return provider
472
- * @throws \dml_exception
473
476
*/
474
477
public function update_provider_instance (
475
478
provider $ provider ,
@@ -515,6 +518,7 @@ public function enable_provider_instance(provider $provider): provider {
515
518
if (!$ provider ->enabled ) {
516
519
$ provider = $ provider ->with (enabled: true );
517
520
$ this ->db ->update_record ('ai_providers ' , $ provider ->to_record ());
521
+ $ this ->update_provider_order ($ provider ->id , aiproviderplugin::ENABLE );
518
522
}
519
523
520
524
return $ provider ;
@@ -536,8 +540,146 @@ public function disable_provider_instance(provider $provider): provider {
536
540
$ provider = $ provider ->with (enabled: false );
537
541
$ this ->db ->update_record ('ai_providers ' , $ provider ->to_record ());
538
542
}
543
+ $ this ->update_provider_order ($ provider ->id , aiproviderplugin::DISABLE );
539
544
}
540
545
541
546
return $ provider ;
542
547
}
548
+
549
+ /**
550
+ * Sorts provider instances by configured order.
551
+ *
552
+ * @param array $unsorted of provider instance objects
553
+ * @return array of provider instance objects
554
+ */
555
+ public static function sort_providers_by_order (array $ unsorted ): array {
556
+ $ sorted = [];
557
+ $ orderarray = explode (', ' , get_config ('core_ai ' , 'provider_order ' ));
558
+
559
+ foreach ($ orderarray as $ notused => $ providerid ) {
560
+ foreach ($ unsorted as $ key => $ provider ) {
561
+ if ($ provider ->id == $ providerid ) {
562
+ $ sorted [] = $ provider ;
563
+ unset($ unsorted [$ key ]);
564
+ }
565
+ }
566
+ }
567
+
568
+ return array_merge ($ sorted , $ unsorted );
569
+ }
570
+
571
+ /**
572
+ * Get the configured ai providers from the manager.
573
+ *
574
+ * @return array
575
+ */
576
+ public function get_sorted_providers (): array {
577
+ $ unsorted = $ this ->get_provider_instances ();
578
+ $ orders = $ this ->sort_providers_by_order ($ unsorted );
579
+ $ sortedplugins = [];
580
+
581
+ foreach ($ orders as $ order ) {
582
+ $ sortedplugins [$ order ->id ] = $ unsorted [$ order ->id ];
583
+ }
584
+
585
+ return $ sortedplugins ;
586
+ }
587
+
588
+ /**
589
+ * Change the order of the provider instance relative to other provider instances.
590
+ *
591
+ * When possible, the change will be stored into the config_log table, to let admins check when/who has modified it.
592
+ *
593
+ * @param int $providerid The provider ID.
594
+ * @param int $direction The direction to move the provider instance. Negative numbers mean up, Positive mean down.
595
+ * @return bool Whether the provider has been updated or not.
596
+ */
597
+ public function change_provider_order (int $ providerid , int $ direction ): bool {
598
+ $ activefactors = array_keys ($ this ->get_sorted_providers ());
599
+ $ key = array_search ($ providerid , $ activefactors );
600
+
601
+ if ($ key === false ) {
602
+ return false ;
603
+ }
604
+
605
+ $ movedown = ($ direction === aiproviderplugin::MOVE_DOWN && $ key < count ($ activefactors ) - 1 );
606
+ $ moveup = ($ direction === aiproviderplugin::MOVE_UP && $ key >= 1 );
607
+ if ($ movedown || $ moveup ) {
608
+ $ this ->update_provider_order ($ providerid , $ direction );
609
+ return true ;
610
+ }
611
+
612
+ return false ;
613
+ }
614
+
615
+ /**
616
+ * Update the provider instance order configuration.
617
+ *
618
+ * @param int $providerid The provider ID.
619
+ * @param string|int $action
620
+ *
621
+ * @throws dml_exception
622
+ */
623
+ public function update_provider_order (int $ providerid , string |int $ action ): void {
624
+ $ order = explode (', ' , get_config ('core_ai ' , 'provider_order ' ));
625
+ $ key = array_search ($ providerid , $ order );
626
+
627
+ switch ($ action ) {
628
+ case aiproviderplugin::MOVE_UP :
629
+ if ($ key >= 1 ) {
630
+ $ fsave = $ order [$ key ];
631
+ $ order [$ key ] = $ order [$ key - 1 ];
632
+ $ order [$ key - 1 ] = $ fsave ;
633
+ }
634
+ break ;
635
+
636
+ case aiproviderplugin::MOVE_DOWN :
637
+ if ($ key < (count ($ order ) - 1 )) {
638
+ $ fsave = $ order [$ key ];
639
+ $ order [$ key ] = $ order [$ key + 1 ];
640
+ $ order [$ key + 1 ] = $ fsave ;
641
+ }
642
+ break ;
643
+
644
+ case aiproviderplugin::ENABLE :
645
+ if (!$ key ) {
646
+ $ order [] = $ providerid ;
647
+ }
648
+ break ;
649
+
650
+ case aiproviderplugin::DISABLE :
651
+ if ($ key ) {
652
+ unset($ order [$ key ]);
653
+ }
654
+ break ;
655
+ }
656
+
657
+ $ this ->set_provider_config (['provider_order ' => implode (', ' , $ order )], 'core_ai ' );
658
+
659
+ \core \session \manager::gc (); // Remove stale sessions.
660
+ \core_plugin_manager::reset_caches ();
661
+ }
662
+
663
+ /**
664
+ * Sets config variable for given provider instance.
665
+ *
666
+ * @param array $data The data to set.
667
+ * @param string $plugin The plugin name.
668
+ *
669
+ * @return bool true or exception.
670
+ * @throws dml_exception
671
+ */
672
+ public function set_provider_config (array $ data , string $ plugin ): bool |dml_exception {
673
+ $ providerconf = get_config ($ plugin );
674
+ foreach ($ data as $ key => $ newvalue ) {
675
+ if (empty ($ providerconf ->$ key )) {
676
+ add_to_config_log ($ key , null , $ newvalue , $ plugin );
677
+ set_config ($ key , $ newvalue , $ plugin );
678
+ } else if ($ providerconf ->$ key != $ newvalue ) {
679
+ add_to_config_log ($ key , $ providerconf ->$ key , $ newvalue , $ plugin );
680
+ set_config ($ key , $ newvalue , $ plugin );
681
+ }
682
+ }
683
+ return true ;
684
+ }
543
685
}
0 commit comments