@memberjunction/ai-engine-base 3.4.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,3 @@
1
- "use strict";
2
1
  var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
2
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
3
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -6,16 +5,16 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
6
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
7
6
  };
8
7
  var AIEngineBase_1;
9
- Object.defineProperty(exports, "__esModule", { value: true });
10
- exports.AIEngineBase = void 0;
11
- const core_1 = require("@memberjunction/core");
12
- const AIAgentPermissionHelper_1 = require("./AIAgentPermissionHelper");
13
- const templates_base_types_1 = require("@memberjunction/templates-base-types");
14
- const core_2 = require("@memberjunction/core");
15
- const DEFAULT_MAX_SIZE_BYTES = 20 * 1024 * 1024;
8
+ import { BaseEngine, LogError, Metadata, RunView } from "@memberjunction/core";
9
+ import { AIAgentPermissionHelper } from "./AIAgentPermissionHelper.js";
10
+ import { TemplateEngineBase } from "@memberjunction/templates-base-types";
11
+ import { RegisterForStartup } from "@memberjunction/core";
12
+ // Default fallback values when no metadata is configured
13
+ const DEFAULT_MAX_SIZE_BYTES = 20 * 1024 * 1024; // 20MB
16
14
  const DEFAULT_MAX_COUNT_PER_MESSAGE = 10;
17
15
  const DEFAULT_MAX_DIMENSION = 4096;
18
- let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine {
16
+ // this class handles execution of AI Actions
17
+ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends BaseEngine {
19
18
  constructor() {
20
19
  super(...arguments);
21
20
  this._models = [];
@@ -51,6 +50,10 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
51
50
  this._modalities = [];
52
51
  this._agentModalities = [];
53
52
  this._modelModalities = [];
53
+ /**
54
+ * Cache for configuration inheritance chains.
55
+ * Key: configurationId, Value: array of AIConfigurationEntity from child to root
56
+ */
54
57
  this._configurationChainCache = new Map();
55
58
  }
56
59
  async Config(forceRefresh, contextUser, provider) {
@@ -221,11 +224,16 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
221
224
  CacheLocal: true
222
225
  }
223
226
  ];
224
- await templates_base_types_1.TemplateEngineBase.Instance.Config(false, contextUser);
227
+ // make sure template engine base is loaded up
228
+ await TemplateEngineBase.Instance.Config(false, contextUser);
225
229
  return await this.Load(params, provider, forceRefresh, contextUser);
226
230
  }
227
231
  async AdditionalLoading(contextUser) {
232
+ // Clear the configuration chain cache when data is reloaded
228
233
  this._configurationChainCache.clear();
234
+ // handle associating prompts with prompt categories
235
+ //here we're using the underlying data (i.e _promptCategories and _prompts)
236
+ //rather than the getter methods because the engine's Loaded property is still false
229
237
  for (const PromptCategory of this._promptCategories) {
230
238
  this._prompts.filter((prompt) => {
231
239
  return prompt.CategoryID === PromptCategory.ID;
@@ -233,6 +241,7 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
233
241
  PromptCategory.Prompts.push(prompt);
234
242
  });
235
243
  }
244
+ // handle association agent actions, models, and notes with agents
236
245
  for (const agent of this._agents) {
237
246
  this._agentActions.filter((action) => {
238
247
  return action.AgentID === agent.ID;
@@ -252,10 +261,18 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
252
261
  });
253
262
  }
254
263
  }
264
+ /**
265
+ * Convenience method to returns the highest power model for a given vendor and model type. Loads the metadata if not already loaded.
266
+ * @param vendorName - if set to null, undefined, or an empty string, then all models of the specified type are considered
267
+ * @param modelType - the type of model to consider
268
+ * @param contextUser required on the server side
269
+ * @returns
270
+ */
255
271
  async GetHighestPowerModel(vendorName, modelType, contextUser) {
256
272
  try {
257
- await AIEngineBase_1.Instance.Config(false, contextUser);
273
+ await AIEngineBase_1.Instance.Config(false, contextUser); // most of the time this is already loaded, but just in case it isn't we will load it here
258
274
  const models = AIEngineBase_1.Instance.Models.filter(m => {
275
+ // Guard against AIModelType/Vendor being non-string (defensive coding for data issues)
259
276
  const mModelType = typeof m.AIModelType === 'string' ? m.AIModelType.trim().toLowerCase() : '';
260
277
  const mVendor = typeof m.Vendor === 'string' ? m.Vendor.trim().toLowerCase() : '';
261
278
  const targetType = modelType.trim().toLowerCase();
@@ -263,17 +280,31 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
263
280
  return mModelType === targetType &&
264
281
  (targetVendor === '' || mVendor === targetVendor);
265
282
  });
266
- models.sort((a, b) => b.PowerRank - a.PowerRank);
283
+ // next, sort the models by the PowerRank field so that the highest power rank model is the first array element
284
+ models.sort((a, b) => b.PowerRank - a.PowerRank); // highest power rank first
267
285
  return models[0];
268
286
  }
269
287
  catch (e) {
270
- (0, core_1.LogError)(e);
288
+ LogError(e); // force logging to help debug scenario here
271
289
  throw e;
272
290
  }
273
291
  }
292
+ /**
293
+ * Convenience method to return the highest power LLM model for a given vendor. Loads the metadata if not already loaded.
294
+ * @param vendorName - if provided, filters to only consider models from the specified vendor, otherwise considers all models
295
+ * @param contextUser
296
+ * @returns
297
+ */
274
298
  async GetHighestPowerLLM(vendorName, contextUser) {
275
299
  return await this.GetHighestPowerModel(vendorName, 'LLM', contextUser);
276
300
  }
301
+ /**
302
+ * Gets the active cost configuration for a specific model and vendor combination
303
+ * @param modelID - The ID of the AI model
304
+ * @param vendorID - The ID of the vendor
305
+ * @param processingType - 'Realtime' or 'Batch' (defaults to 'Realtime')
306
+ * @returns The active AIModelCostEntity or null if none found
307
+ */
277
308
  GetActiveModelCost(modelID, vendorID, processingType = 'Realtime') {
278
309
  const now = new Date();
279
310
  const activeCosts = this._modelCosts.filter(cost => cost.ModelID === modelID &&
@@ -282,6 +313,7 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
282
313
  cost.Status === 'Active' &&
283
314
  (!cost.StartedAt || new Date(cost.StartedAt) <= now) &&
284
315
  (!cost.EndedAt || new Date(cost.EndedAt) > now));
316
+ // If multiple active costs exist, return the most recently started one
285
317
  if (activeCosts.length > 0) {
286
318
  return activeCosts.sort((a, b) => {
287
319
  const aStart = a.StartedAt ? new Date(a.StartedAt).getTime() : 0;
@@ -297,15 +329,29 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
297
329
  get AgentRelationships() {
298
330
  return this._agentRelationships;
299
331
  }
332
+ /**
333
+ * Returns the sub-agents for a given agent ID, optionally filtering by status.
334
+ * Includes both child agents (ParentID relationship) and related agents (AgentRelationships).
335
+ *
336
+ * @param agentID - The ID of the parent agent to get sub-agents for
337
+ * @param status - Optional status to filter sub-agents by (e.g., 'Active', 'Inactive'). If not provided, all sub-agents are returned.
338
+ * @param relationshipStatus - Optional status to filter agent relationships by. Defaults to 'Active' if not provided.
339
+ * @returns AIAgentEntityExtended[] - Array of sub-agent entities matching the criteria (deduplicated by ID).
340
+ * @memberof
341
+ */
300
342
  GetSubAgents(agentID, status, relationshipStatus) {
343
+ // Get child agents (ParentID relationship)
301
344
  const childAgents = this._agents.filter(a => a.ParentID === agentID &&
302
345
  (!status || a.Status === status));
303
- const relStatus = relationshipStatus ?? 'Active';
346
+ // Get related agents (AgentRelationships)
347
+ const relStatus = relationshipStatus ?? 'Active'; // Default to Active for relationships
304
348
  const activeRelationships = this._agentRelationships.filter(ar => ar.AgentID === agentID &&
305
349
  ar.Status === relStatus);
350
+ // Get the actual agent entities for related agents
306
351
  const relatedAgents = activeRelationships
307
352
  .map(ar => this._agents.find(a => a.ID === ar.SubAgentID))
308
353
  .filter(a => a != null && (!status || a.Status === status));
354
+ // Combine and deduplicate by ID
309
355
  const uniqueAgentIDs = new Set();
310
356
  const allSubAgents = [];
311
357
  for (const agent of [...childAgents, ...relatedAgents]) {
@@ -328,9 +374,19 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
328
374
  get AgentPrompts() {
329
375
  return this._agentPrompts;
330
376
  }
377
+ /**
378
+ * Cached array of AI Agent Configurations loaded from the database.
379
+ * These define semantic presets for agents (e.g., "Fast", "High Quality").
380
+ */
331
381
  get AgentConfigurations() {
332
382
  return this._agentConfigurations;
333
383
  }
384
+ /**
385
+ * Gets all configuration presets for a specific agent
386
+ * @param agentId The agent ID
387
+ * @param activeOnly If true, only returns Active status presets (default: true)
388
+ * @returns Array of configuration presets sorted by Priority
389
+ */
334
390
  GetAgentConfigurationPresets(agentId, activeOnly = true) {
335
391
  let presets = this._agentConfigurations.filter(ac => ac.AgentID === agentId);
336
392
  if (activeOnly) {
@@ -338,10 +394,21 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
338
394
  }
339
395
  return presets.sort((a, b) => a.Priority - b.Priority);
340
396
  }
397
+ /**
398
+ * Gets the default configuration preset for an agent
399
+ * @param agentId The agent ID
400
+ * @returns The default preset, or undefined if none exists
401
+ */
341
402
  GetDefaultAgentConfigurationPreset(agentId) {
342
403
  const presets = this.GetAgentConfigurationPresets(agentId, true);
343
404
  return presets.find(ac => ac.IsDefault);
344
405
  }
406
+ /**
407
+ * Gets a specific configuration preset by agent ID and preset name
408
+ * @param agentId The agent ID
409
+ * @param presetName The preset name (e.g., "Fast", "HighQuality")
410
+ * @returns The configuration preset, or undefined if not found
411
+ */
345
412
  GetAgentConfigurationPresetByName(agentId, presetName) {
346
413
  return this._agentConfigurations.find(ac => ac.AgentID === agentId &&
347
414
  ac.Name === presetName &&
@@ -374,6 +441,13 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
374
441
  get CredentialBindings() {
375
442
  return this._credentialBindings;
376
443
  }
444
+ /**
445
+ * Gets credential bindings for a specific target, filtered by binding type and sorted by priority.
446
+ * Only returns active bindings.
447
+ * @param bindingType - The type of binding: 'Vendor', 'ModelVendor', or 'PromptModel'
448
+ * @param targetId - The ID of the target entity (AIVendorID, AIModelVendorID, or AIPromptModelID)
449
+ * @returns Array of active credential bindings sorted by Priority (lower = higher priority)
450
+ */
377
451
  GetCredentialBindingsForTarget(bindingType, targetId) {
378
452
  return this._credentialBindings
379
453
  .filter(b => {
@@ -394,6 +468,12 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
394
468
  })
395
469
  .sort((a, b) => a.Priority - b.Priority);
396
470
  }
471
+ /**
472
+ * Checks if any credential bindings exist for a specific target.
473
+ * @param bindingType - The type of binding: 'Vendor', 'ModelVendor', or 'PromptModel'
474
+ * @param targetId - The ID of the target entity
475
+ * @returns True if at least one active binding exists
476
+ */
397
477
  HasCredentialBindings(bindingType, targetId) {
398
478
  return this.GetCredentialBindingsForTarget(bindingType, targetId).length > 0;
399
479
  }
@@ -418,6 +498,9 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
418
498
  get ArtifactTypes() {
419
499
  return this._artifactTypes;
420
500
  }
501
+ /**
502
+ * Convenience method to return only the Language Models. Loads the metadata if not already loaded.
503
+ */
421
504
  get LanguageModels() {
422
505
  return this._models.filter(m => m.AIModelType.trim().toLowerCase() === 'llm');
423
506
  }
@@ -439,14 +522,60 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
439
522
  get ConfigurationParams() {
440
523
  return this._configurationParams;
441
524
  }
525
+ /**
526
+ * Gets configuration parameters for a specific configuration
527
+ * @param configurationId - The ID of the configuration
528
+ * @returns Array of configuration parameters for the specified configuration
529
+ */
442
530
  GetConfigurationParams(configurationId) {
443
531
  return this._configurationParams.filter(p => p.ConfigurationID === configurationId);
444
532
  }
533
+ /**
534
+ * Gets a specific configuration parameter value
535
+ * @param configurationId - The ID of the configuration
536
+ * @param paramName - The name of the parameter
537
+ * @returns The parameter entity or null if not found
538
+ */
445
539
  GetConfigurationParam(configurationId, paramName) {
446
540
  return this._configurationParams.find(p => p.ConfigurationID === configurationId &&
447
541
  p.Name.toLowerCase() === paramName.toLowerCase()) || null;
448
542
  }
543
+ /**
544
+ * Returns the inheritance chain for a configuration, starting with the specified
545
+ * configuration and walking up through parent configurations to the root.
546
+ *
547
+ * The chain is ordered from most-specific (the requested configuration) to
548
+ * least-specific (the root parent with no ParentID).
549
+ *
550
+ * Results are cached for performance. Cache is invalidated when configurations
551
+ * are reloaded via Config().
552
+ *
553
+ * @param configurationId - The ID of the configuration to get the chain for
554
+ * @returns Array of AIConfigurationEntity objects representing the inheritance chain,
555
+ * or empty array if the configuration is not found
556
+ * @throws Error if a circular reference is detected in the configuration hierarchy
557
+ *
558
+ * @example
559
+ * // Single configuration with no parent
560
+ * const chain = AIEngine.Instance.GetConfigurationChain('config-a');
561
+ * // Returns: [ConfigA]
562
+ *
563
+ * @example
564
+ * // Child -> Parent -> Grandparent chain
565
+ * const chain = AIEngine.Instance.GetConfigurationChain('child-config');
566
+ * // Returns: [ChildConfig, ParentConfig, GrandparentConfig]
567
+ *
568
+ * @example
569
+ * // Usage in model selection - first config in chain with a match wins
570
+ * const chain = AIEngine.Instance.GetConfigurationChain(configId);
571
+ * for (const config of chain) {
572
+ * const models = promptModels.filter(pm => pm.ConfigurationID === config.ID);
573
+ * if (models.length > 0) return models;
574
+ * }
575
+ * // Fall back to null-config models if no match in chain
576
+ */
449
577
  GetConfigurationChain(configurationId) {
578
+ // Check cache first
450
579
  if (this._configurationChainCache.has(configurationId)) {
451
580
  return this._configurationChainCache.get(configurationId);
452
581
  }
@@ -454,6 +583,7 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
454
583
  const visitedIds = new Set();
455
584
  let currentId = configurationId;
456
585
  while (currentId) {
586
+ // Cycle detection
457
587
  if (visitedIds.has(currentId)) {
458
588
  const chainNames = chain.map(c => c.Name).join(' -> ');
459
589
  throw new Error(`Circular reference detected in AI Configuration hierarchy. ` +
@@ -465,16 +595,37 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
465
595
  break;
466
596
  visitedIds.add(currentId);
467
597
  chain.push(config);
468
- currentId = config.ParentID;
598
+ currentId = config.ParentID; // Will be null for root configs
469
599
  }
600
+ // Cache the result
470
601
  this._configurationChainCache.set(configurationId, chain);
471
602
  return chain;
472
603
  }
604
+ /**
605
+ * Returns all configuration parameters for a configuration, including inherited
606
+ * parameters from parent configurations. Child parameters override parent parameters
607
+ * with the same name (case-insensitive match).
608
+ *
609
+ * The inheritance chain is walked from root to child, so child values take precedence
610
+ * over parent values for parameters with the same name.
611
+ *
612
+ * @param configurationId - The ID of the configuration to get parameters for
613
+ * @returns Array of AIConfigurationParamEntity objects, with child overrides applied.
614
+ * Returns empty array if configuration is not found.
615
+ *
616
+ * @example
617
+ * // Parent has: temperature=0.7, maxTokens=4000
618
+ * // Child has: temperature=0.9
619
+ * // Result: temperature=0.9 (child), maxTokens=4000 (inherited from parent)
620
+ * const params = AIEngine.Instance.GetConfigurationParamsWithInheritance('child-config-id');
621
+ */
473
622
  GetConfigurationParamsWithInheritance(configurationId) {
474
623
  const chain = this.GetConfigurationChain(configurationId);
475
624
  if (chain.length === 0) {
476
625
  return [];
477
626
  }
627
+ // Use a map to track params by name (lowercase for case-insensitive matching)
628
+ // Walk chain in reverse (root first, child last) so child overwrites parent
478
629
  const paramMap = new Map();
479
630
  for (let i = chain.length - 1; i >= 0; i--) {
480
631
  const configParams = this._configurationParams.filter(p => p.ConfigurationID === chain[i].ID);
@@ -493,58 +644,133 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
493
644
  get AgentStepPaths() {
494
645
  return this._agentStepPaths;
495
646
  }
647
+ // ==========================================
648
+ // Modality Accessors and Helper Methods
649
+ // ==========================================
650
+ /**
651
+ * Gets all AI modalities (Text, Image, Audio, Video, File, Embedding, etc.)
652
+ */
496
653
  get Modalities() {
497
654
  return this._modalities;
498
655
  }
656
+ /**
657
+ * Gets all agent-modality mappings
658
+ */
499
659
  get AgentModalities() {
500
660
  return this._agentModalities;
501
661
  }
662
+ /**
663
+ * Gets all model-modality mappings
664
+ */
502
665
  get ModelModalities() {
503
666
  return this._modelModalities;
504
667
  }
668
+ /**
669
+ * Gets a modality by name (case-insensitive)
670
+ * @param name - The modality name (e.g., 'Text', 'Image', 'Audio', 'Video', 'File')
671
+ * @returns The modality entity or undefined if not found
672
+ */
505
673
  GetModalityByName(name) {
506
674
  return this._modalities.find(m => m.Name.toLowerCase() === name.toLowerCase());
507
675
  }
676
+ /**
677
+ * Gets all modalities supported by an agent for a given direction
678
+ * @param agentId - The agent ID
679
+ * @param direction - 'Input' or 'Output'
680
+ * @returns Array of modality entities the agent supports
681
+ */
508
682
  GetAgentModalities(agentId, direction) {
509
683
  const agentModalityRecords = this._agentModalities.filter(am => am.AgentID === agentId && am.Direction === direction);
510
684
  return agentModalityRecords
511
685
  .map(am => this._modalities.find(m => m.ID === am.ModalityID))
512
686
  .filter((m) => m !== undefined);
513
687
  }
688
+ /**
689
+ * Gets all modalities supported by a model for a given direction
690
+ * @param modelId - The model ID
691
+ * @param direction - 'Input' or 'Output'
692
+ * @returns Array of modality entities the model supports
693
+ */
514
694
  GetModelModalities(modelId, direction) {
515
695
  const modelModalityRecords = this._modelModalities.filter(mm => mm.ModelID === modelId && mm.Direction === direction);
516
696
  return modelModalityRecords
517
697
  .map(mm => this._modalities.find(m => m.ID === mm.ModalityID))
518
698
  .filter((m) => m !== undefined);
519
699
  }
700
+ /**
701
+ * Checks if an agent supports a specific modality for a given direction.
702
+ * If no agent modalities are configured, defaults to text-only.
703
+ * @param agentId - The agent ID
704
+ * @param modalityName - The modality name (e.g., 'Image', 'Audio')
705
+ * @param direction - 'Input' or 'Output'
706
+ * @returns True if the agent supports this modality
707
+ */
520
708
  AgentSupportsModality(agentId, modalityName, direction) {
709
+ // Check if agent has explicit modality records
521
710
  const agentModalities = this.GetAgentModalities(agentId, direction);
522
711
  if (agentModalities.length > 0) {
712
+ // Agent has explicit modality configuration - check it
523
713
  return agentModalities.some(m => m.Name.toLowerCase() === modalityName.toLowerCase());
524
714
  }
715
+ // No explicit agent modalities configured - default to text-only
525
716
  return modalityName.toLowerCase() === 'text';
526
717
  }
718
+ /**
719
+ * Checks if a model supports a specific modality for a given direction
720
+ * @param modelId - The model ID
721
+ * @param modalityName - The modality name (e.g., 'Image', 'Audio')
722
+ * @param direction - 'Input' or 'Output'
723
+ * @returns True if the model supports this modality
724
+ */
527
725
  ModelSupportsModality(modelId, modalityName, direction) {
528
726
  const modelModalities = this.GetModelModalities(modelId, direction);
529
727
  if (modelModalities.length > 0) {
530
728
  return modelModalities.some(m => m.Name.toLowerCase() === modalityName.toLowerCase());
531
729
  }
730
+ // No explicit model modalities - assume text-only (default for LLMs)
532
731
  return modalityName.toLowerCase() === 'text';
533
732
  }
733
+ /**
734
+ * Checks if an agent supports any non-text input modalities (images, audio, video, files).
735
+ * This is used to determine if attachment upload should be enabled in the UI.
736
+ * @param agentId - The agent ID
737
+ * @returns True if the agent supports at least one non-text input modality
738
+ */
534
739
  AgentSupportsAttachments(agentId) {
535
740
  const nonTextModalities = ['image', 'audio', 'video', 'file'];
536
741
  return nonTextModalities.some(modalityName => this.AgentSupportsModality(agentId, modalityName, 'Input'));
537
742
  }
743
+ /**
744
+ * Gets all input modality names supported by an agent (for UI display/filtering)
745
+ * @param agentId - The agent ID
746
+ * @returns Array of modality names the agent accepts as input
747
+ */
538
748
  GetAgentSupportedInputModalities(agentId) {
749
+ // Check explicit agent modalities
539
750
  const agentModalities = this.GetAgentModalities(agentId, 'Input');
540
751
  if (agentModalities.length > 0) {
541
752
  return agentModalities.map(m => m.Name);
542
753
  }
754
+ // No explicit modalities configured - default to text-only
543
755
  return ['Text'];
544
756
  }
757
+ // ==========================================
758
+ // Modality Limit Resolution Methods
759
+ // ==========================================
760
+ /**
761
+ * Resolves the effective limits for a specific modality for an agent.
762
+ * Uses precedence chain: Agent → Model → System → Defaults.
763
+ *
764
+ * @param agentId - The ID of the agent
765
+ * @param modalityName - The modality name (e.g., 'Image', 'Audio', 'Video', 'File')
766
+ * @param modelId - Optional model ID to check model-specific limits (falls back to system defaults if not provided)
767
+ * @returns The resolved modality limits with source information
768
+ */
545
769
  GetAgentModalityLimits(agentId, modalityName, modelId) {
770
+ // Get the base modality record
546
771
  const modality = this.GetModalityByName(modalityName);
547
772
  if (!modality) {
773
+ // Modality doesn't exist - return defaults indicating not allowed
548
774
  return {
549
775
  maxSizeBytes: null,
550
776
  maxCountPerMessage: null,
@@ -554,33 +780,46 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
554
780
  source: 'Default'
555
781
  };
556
782
  }
783
+ // Check agent-specific modality settings first (highest priority)
557
784
  const agentModality = this._agentModalities.find(am => am.AgentID === agentId && am.ModalityID === modality.ID && am.Direction === 'Input');
558
785
  if (agentModality) {
786
+ // Agent has explicit modality configuration
559
787
  return {
560
788
  maxSizeBytes: agentModality.MaxSizeBytes,
561
789
  maxCountPerMessage: agentModality.MaxCountPerMessage,
562
- maxDimension: null,
563
- supportedFormats: null,
790
+ maxDimension: null, // Agent modality doesn't have MaxDimension
791
+ supportedFormats: null, // Agent modality doesn't have SupportedFormats
564
792
  isAllowed: agentModality.IsAllowed,
565
793
  source: 'Agent'
566
794
  };
567
795
  }
796
+ // Check model-specific modality settings (second priority)
568
797
  if (modelId) {
569
798
  const modelLimits = this.GetModelModalityLimits(modelId, modalityName);
570
799
  if (modelLimits.source === 'Model') {
571
800
  return modelLimits;
572
801
  }
573
802
  }
803
+ // Fall back to system-wide modality defaults
574
804
  return {
575
805
  maxSizeBytes: modality.DefaultMaxSizeBytes,
576
806
  maxCountPerMessage: modality.DefaultMaxCountPerMessage,
577
- maxDimension: null,
578
- supportedFormats: null,
579
- isAllowed: true,
807
+ maxDimension: null, // System modality doesn't have MaxDimension
808
+ supportedFormats: null, // System modality doesn't have SupportedFormats by default
809
+ isAllowed: true, // If modality exists at system level, it's generally allowed
580
810
  source: 'System'
581
811
  };
582
812
  }
813
+ /**
814
+ * Resolves the effective limits for a specific modality for a model.
815
+ * Uses precedence chain: Model → System → Defaults.
816
+ *
817
+ * @param modelId - The ID of the model
818
+ * @param modalityName - The modality name (e.g., 'Image', 'Audio', 'Video', 'File')
819
+ * @returns The resolved modality limits with source information
820
+ */
583
821
  GetModelModalityLimits(modelId, modalityName) {
822
+ // Get the base modality record
584
823
  const modality = this.GetModalityByName(modalityName);
585
824
  if (!modality) {
586
825
  return {
@@ -592,6 +831,7 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
592
831
  source: 'Default'
593
832
  };
594
833
  }
834
+ // Check model-specific modality settings
595
835
  const modelModality = this._modelModalities.find(mm => mm.ModelID === modelId && mm.ModalityID === modality.ID && mm.Direction === 'Input');
596
836
  if (modelModality && modelModality.IsSupported) {
597
837
  return {
@@ -603,6 +843,7 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
603
843
  source: 'Model'
604
844
  };
605
845
  }
846
+ // Fall back to system-wide modality defaults
606
847
  return {
607
848
  maxSizeBytes: modality.DefaultMaxSizeBytes,
608
849
  maxCountPerMessage: modality.DefaultMaxCountPerMessage,
@@ -612,6 +853,15 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
612
853
  source: 'System'
613
854
  };
614
855
  }
856
+ /**
857
+ * Gets aggregated attachment limits for an agent, suitable for passing to UI components.
858
+ * Combines limits from all supported input modalities (Image, Audio, Video, File).
859
+ * Uses the most restrictive limits across all modalities for the aggregate values.
860
+ *
861
+ * @param agentId - The ID of the agent
862
+ * @param modelId - Optional model ID to include model-specific limits in the resolution
863
+ * @returns Aggregated attachment limits ready for UI component configuration
864
+ */
615
865
  GetAgentAttachmentLimits(agentId, modelId) {
616
866
  const attachmentModalityNames = ['Image', 'Audio', 'Video', 'File'];
617
867
  const modalityLimitsMap = new Map();
@@ -624,12 +874,15 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
624
874
  modalityLimitsMap.set(modalityName, limits);
625
875
  if (limits.isAllowed) {
626
876
  hasAnyAllowed = true;
877
+ // Track the most restrictive size limit
627
878
  if (limits.maxSizeBytes != null && limits.maxSizeBytes < minMaxSize) {
628
879
  minMaxSize = limits.maxSizeBytes;
629
880
  }
881
+ // Track the most restrictive count limit
630
882
  if (limits.maxCountPerMessage != null && limits.maxCountPerMessage < minMaxCount) {
631
883
  minMaxCount = limits.maxCountPerMessage;
632
884
  }
885
+ // Build accepted file types based on allowed modalities
633
886
  const mimePattern = this.getModalityMimePattern(modalityName, limits.supportedFormats);
634
887
  if (mimePattern) {
635
888
  acceptedTypes.push(mimePattern);
@@ -644,10 +897,15 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
644
897
  modalities: modalityLimitsMap
645
898
  };
646
899
  }
900
+ /**
901
+ * Helper to get MIME type pattern for a modality
902
+ */
647
903
  getModalityMimePattern(modalityName, supportedFormats) {
904
+ // If specific formats are provided, use them
648
905
  if (supportedFormats) {
649
906
  return supportedFormats;
650
907
  }
908
+ // Default MIME patterns by modality
651
909
  switch (modalityName.toLowerCase()) {
652
910
  case 'image':
653
911
  return 'image/*';
@@ -656,27 +914,52 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
656
914
  case 'video':
657
915
  return 'video/*';
658
916
  case 'file':
659
- return '*/*';
917
+ return '*/*'; // Accept all file types
660
918
  default:
661
919
  return null;
662
920
  }
663
921
  }
922
+ /**
923
+ * Gets agent steps for a specific agent, optionally filtered by status
924
+ * @param agentId - The ID of the agent
925
+ * @param status - Optional status filter ('Active', 'Pending', 'Disabled')
926
+ * @returns Array of agent steps
927
+ */
664
928
  GetAgentSteps(agentId, status) {
665
929
  return this._agentSteps.filter(step => step.AgentID === agentId &&
666
930
  (!status || step.Status === status));
667
931
  }
932
+ /**
933
+ * Gets a specific agent step by ID
934
+ * @param stepId - The ID of the step
935
+ * @returns The step or null if not found
936
+ */
668
937
  GetAgentStepByID(stepId) {
669
938
  return this._agentSteps.find(step => step.ID === stepId) || null;
670
939
  }
940
+ /**
941
+ * Gets paths originating from a specific step
942
+ * @param stepId - The ID of the origin step
943
+ * @returns Array of paths from the step
944
+ */
671
945
  GetPathsFromStep(stepId) {
672
946
  return this._agentStepPaths.filter(path => path.OriginStepID === stepId);
673
947
  }
948
+ /**
949
+ * @deprecated AI Model Actions are deprecated. Returns an empty array.
950
+ */
674
951
  get ModelActions() {
675
952
  return [];
676
953
  }
954
+ /**
955
+ * @deprecated AI Actions are deprecated. Returns an empty array.
956
+ */
677
957
  get Actions() {
678
958
  return [];
679
959
  }
960
+ /**
961
+ * @deprecated Entity AI Actions are deprecated. Returns an empty array.
962
+ */
680
963
  get EntityAIActions() {
681
964
  return [];
682
965
  }
@@ -687,15 +970,19 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
687
970
  if (!AIEngineBase_1.Instance.Loaded)
688
971
  throw new Error("AI Metadata not loaded, call AIEngineBase.Config() first.");
689
972
  }
973
+ /**
974
+ * This method will check the result cache for the given params and return the result if it exists, otherwise it will return null if the request is not cached.
975
+ * @param prompt - the fully populated prompt to check the cache for
976
+ */
690
977
  async CheckResultCache(prompt) {
691
978
  try {
692
- const rv = new core_1.RunView();
979
+ const rv = new RunView();
693
980
  const escapedPrompt = prompt.replace(/'/g, "''");
694
981
  const result = await rv.RunView({
695
982
  EntityName: 'AI Result Cache',
696
983
  ExtraFilter: `PromptText = '${escapedPrompt}' AND Status='Active'`,
697
984
  OrderBy: 'RunAt DESC',
698
- MaxRows: 1,
985
+ MaxRows: 1, // get only the latest one
699
986
  ResultType: 'entity_object'
700
987
  }, this.ContextUser);
701
988
  if (result && result.Success && result.Results && result.Results.length > 0) {
@@ -705,12 +992,15 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
705
992
  return null;
706
993
  }
707
994
  catch (err) {
708
- (0, core_1.LogError)(err);
995
+ LogError(err);
709
996
  return null;
710
997
  }
711
998
  }
999
+ /**
1000
+ * Utility method that will cache the result of a prompt in the AI Result Cache entity
1001
+ */
712
1002
  async CacheResult(model, prompt, promptText, resultText) {
713
- const md = new core_1.Metadata();
1003
+ const md = new Metadata();
714
1004
  const cacheItem = await md.GetEntityObject('AI Result Cache', this.ContextUser);
715
1005
  cacheItem.AIModelID = model.ID;
716
1006
  cacheItem.AIPromptID = prompt.ID;
@@ -720,33 +1010,80 @@ let AIEngineBase = AIEngineBase_1 = class AIEngineBase extends core_1.BaseEngine
720
1010
  cacheItem.RunAt = new Date();
721
1011
  return await cacheItem.Save();
722
1012
  }
1013
+ // ==========================================
1014
+ // AI Agent Permission Helper Methods
1015
+ // ==========================================
1016
+ /**
1017
+ * Checks if a user has permission to view an agent.
1018
+ * @param agentId - The ID of the agent to check
1019
+ * @param user - The user to check permissions for
1020
+ * @returns True if the user can view the agent
1021
+ */
723
1022
  async CanUserViewAgent(agentId, user) {
724
- return await AIAgentPermissionHelper_1.AIAgentPermissionHelper.HasPermission(agentId, user, 'view');
725
- }
1023
+ return await AIAgentPermissionHelper.HasPermission(agentId, user, 'view');
1024
+ }
1025
+ /**
1026
+ * Checks if a user has permission to run an agent.
1027
+ * @param agentId - The ID of the agent to check
1028
+ * @param user - The user to check permissions for
1029
+ * @returns True if the user can run the agent
1030
+ */
726
1031
  async CanUserRunAgent(agentId, user) {
727
- return await AIAgentPermissionHelper_1.AIAgentPermissionHelper.HasPermission(agentId, user, 'run');
728
- }
1032
+ return await AIAgentPermissionHelper.HasPermission(agentId, user, 'run');
1033
+ }
1034
+ /**
1035
+ * Checks if a user has permission to edit an agent.
1036
+ * @param agentId - The ID of the agent to check
1037
+ * @param user - The user to check permissions for
1038
+ * @returns True if the user can edit the agent
1039
+ */
729
1040
  async CanUserEditAgent(agentId, user) {
730
- return await AIAgentPermissionHelper_1.AIAgentPermissionHelper.HasPermission(agentId, user, 'edit');
731
- }
1041
+ return await AIAgentPermissionHelper.HasPermission(agentId, user, 'edit');
1042
+ }
1043
+ /**
1044
+ * Checks if a user has permission to delete an agent.
1045
+ * @param agentId - The ID of the agent to check
1046
+ * @param user - The user to check permissions for
1047
+ * @returns True if the user can delete the agent
1048
+ */
732
1049
  async CanUserDeleteAgent(agentId, user) {
733
- return await AIAgentPermissionHelper_1.AIAgentPermissionHelper.HasPermission(agentId, user, 'delete');
734
- }
1050
+ return await AIAgentPermissionHelper.HasPermission(agentId, user, 'delete');
1051
+ }
1052
+ /**
1053
+ * Gets all effective permissions a user has for a specific agent.
1054
+ * @param agentId - The ID of the agent
1055
+ * @param user - The user to check permissions for
1056
+ * @returns Object containing all permission flags and ownership status
1057
+ */
735
1058
  async GetUserAgentPermissions(agentId, user) {
736
- return await AIAgentPermissionHelper_1.AIAgentPermissionHelper.GetEffectivePermissions(agentId, user);
737
- }
1059
+ return await AIAgentPermissionHelper.GetEffectivePermissions(agentId, user);
1060
+ }
1061
+ /**
1062
+ * Gets all agents a user has access to with a specific permission level.
1063
+ * @param user - The user to check permissions for
1064
+ * @param permission - The minimum permission level required ('view', 'run', 'edit', or 'delete')
1065
+ * @returns Array of agents the user can access
1066
+ */
738
1067
  async GetAccessibleAgents(user, permission) {
739
- return await AIAgentPermissionHelper_1.AIAgentPermissionHelper.GetAccessibleAgents(user, permission);
1068
+ return await AIAgentPermissionHelper.GetAccessibleAgents(user, permission);
740
1069
  }
1070
+ /**
1071
+ * Clears the agent permissions cache. Call this after modifying permissions.
1072
+ */
741
1073
  ClearAgentPermissionsCache() {
742
- AIAgentPermissionHelper_1.AIAgentPermissionHelper.ClearCache();
1074
+ AIAgentPermissionHelper.ClearCache();
743
1075
  }
1076
+ /**
1077
+ * Refreshes the permissions cache for a specific agent.
1078
+ * @param agentId - The ID of the agent to refresh
1079
+ * @param user - The user context for server-side operations
1080
+ */
744
1081
  async RefreshAgentPermissionsCache(agentId, user) {
745
- await AIAgentPermissionHelper_1.AIAgentPermissionHelper.RefreshCache(user);
1082
+ await AIAgentPermissionHelper.RefreshCache(user);
746
1083
  }
747
1084
  };
748
- exports.AIEngineBase = AIEngineBase;
749
- exports.AIEngineBase = AIEngineBase = AIEngineBase_1 = __decorate([
750
- (0, core_2.RegisterForStartup)()
1085
+ AIEngineBase = AIEngineBase_1 = __decorate([
1086
+ RegisterForStartup()
751
1087
  ], AIEngineBase);
1088
+ export { AIEngineBase };
752
1089
  //# sourceMappingURL=BaseAIEngine.js.map