@memberjunction/ng-ai-test-harness 2.111.1 → 2.112.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,5 +1,5 @@
1
- import { Component, Input, Output, EventEmitter, ViewChild, SecurityContext } from '@angular/core';
2
- import { Metadata, RunView, CompositeKey } from '@memberjunction/core';
1
+ import { Component, Input, Output, EventEmitter, ViewChild, SecurityContext, } from '@angular/core';
2
+ import { Metadata, RunView, CompositeKey } from '@memberjunction/global';
3
3
  import { MJNotificationService } from '@memberjunction/ng-notifications';
4
4
  import { SharedService } from '@memberjunction/ng-shared';
5
5
  import { Subject } from 'rxjs';
@@ -1277,7 +1277,7 @@ export class AITestHarnessComponent {
1277
1277
  seed: null,
1278
1278
  stopSequences: [],
1279
1279
  includeLogProbs: false,
1280
- topLogProbs: 2
1280
+ topLogProbs: 2,
1281
1281
  };
1282
1282
  /** Raw stop sequences input for textarea */
1283
1283
  this.stopSequencesText = '';
@@ -1291,7 +1291,7 @@ export class AITestHarnessComponent {
1291
1291
  { text: 'Text', value: 'Text' },
1292
1292
  { text: 'Markdown', value: 'Markdown' },
1293
1293
  { text: 'JSON', value: 'JSON' },
1294
- { text: 'Model Specific', value: 'ModelSpecific' }
1294
+ { text: 'Model Specific', value: 'ModelSpecific' },
1295
1295
  ];
1296
1296
  /** Selected response format for prompt execution */
1297
1297
  this.selectedResponseFormat = this.responseFormatOptions[0];
@@ -1477,7 +1477,7 @@ export class AITestHarnessComponent {
1477
1477
  const serializedAgentRun = message.data.agentRun;
1478
1478
  // Update streaming message content if available
1479
1479
  if (message.data.progress?.message) {
1480
- const streamingMessage = this.conversationMessages.find(m => m.isStreaming);
1480
+ const streamingMessage = this.conversationMessages.find((m) => m.isStreaming);
1481
1481
  if (streamingMessage) {
1482
1482
  // Clear any typing animation interval
1483
1483
  const typingInterval = streamingMessage._typingInterval;
@@ -1499,7 +1499,7 @@ export class AITestHarnessComponent {
1499
1499
  // Load the serialized data into our entity
1500
1500
  await this.currentAgentRun.LoadFromData(serializedAgentRun);
1501
1501
  // Update assistant message with agent run ID if available
1502
- const streamingMessage = this.conversationMessages.find(m => m.isStreaming);
1502
+ const streamingMessage = this.conversationMessages.find((m) => m.isStreaming);
1503
1503
  if (streamingMessage && this.currentAgentRun.ID) {
1504
1504
  streamingMessage.agentRunId = this.currentAgentRun.ID;
1505
1505
  }
@@ -1512,19 +1512,19 @@ export class AITestHarnessComponent {
1512
1512
  status: this.currentAgentRun.Status,
1513
1513
  stepCount: this.currentAgentRun.Steps?.length || 0,
1514
1514
  executionMonitorMode: this.executionMonitorMode,
1515
- steps: this.currentAgentRun.Steps?.map(s => ({
1515
+ steps: this.currentAgentRun.Steps?.map((s) => ({
1516
1516
  id: s.ID,
1517
1517
  type: s.StepType,
1518
1518
  status: s.Status,
1519
1519
  hasSubAgentRun: !!s.SubAgentRun,
1520
- subStepCount: s.SubAgentRun?.Steps?.length || 0
1521
- }))
1520
+ subStepCount: s.SubAgentRun?.Steps?.length || 0,
1521
+ })),
1522
1522
  });
1523
1523
  // Force change detection
1524
1524
  this.cdr.detectChanges();
1525
1525
  }
1526
1526
  else if (message?.type === 'StreamingContent' && message.data?.streaming) {
1527
- const streamingMessage = this.conversationMessages.find(m => m.isStreaming);
1527
+ const streamingMessage = this.conversationMessages.find((m) => m.isStreaming);
1528
1528
  if (streamingMessage) {
1529
1529
  // Append streaming content
1530
1530
  if (!streamingMessage.streamingContent) {
@@ -1555,15 +1555,15 @@ export class AITestHarnessComponent {
1555
1555
  if (this.entity && 'AIModelTypeID' in this.entity) {
1556
1556
  const prompt = this.entity;
1557
1557
  if (prompt.AIModelTypeID) {
1558
- filteredModels = AIEngineBase.Instance.Models.filter(model => model.AIModelTypeID === prompt.AIModelTypeID && model.IsActive);
1558
+ filteredModels = AIEngineBase.Instance.Models.filter((model) => model.AIModelTypeID === prompt.AIModelTypeID && model.IsActive);
1559
1559
  }
1560
1560
  else {
1561
1561
  // No model type restriction, show all active models
1562
- filteredModels = AIEngineBase.Instance.Models.filter(model => model.IsActive);
1562
+ filteredModels = AIEngineBase.Instance.Models.filter((model) => model.IsActive);
1563
1563
  }
1564
1564
  // Set default response format from prompt with slight delay for Kendo dropdown
1565
1565
  setTimeout(() => {
1566
- const format = this.responseFormatOptions.find(f => f.value.trim().toLowerCase() === prompt.ResponseFormat.trim().toLowerCase());
1566
+ const format = this.responseFormatOptions.find((f) => f.value.trim().toLowerCase() === prompt.ResponseFormat.trim().toLowerCase());
1567
1567
  if (format) {
1568
1568
  this.selectedResponseFormat = format;
1569
1569
  }
@@ -1574,7 +1574,7 @@ export class AITestHarnessComponent {
1574
1574
  }
1575
1575
  else {
1576
1576
  // Not a prompt entity, show all active models
1577
- filteredModels = AIEngineBase.Instance.Models.filter(model => model.IsActive);
1577
+ filteredModels = AIEngineBase.Instance.Models.filter((model) => model.IsActive);
1578
1578
  }
1579
1579
  // Sort models by name
1580
1580
  filteredModels.sort((a, b) => a.Name.localeCompare(b.Name));
@@ -1586,7 +1586,7 @@ export class AITestHarnessComponent {
1586
1586
  // Add a blank option at the beginning with the default model name
1587
1587
  this.availableModels = [
1588
1588
  { ID: '', Name: this.defaultModelName ? `-- Default: ${this.defaultModelName} --` : '-- Use Default Model --' },
1589
- ...filteredModels
1589
+ ...filteredModels,
1590
1590
  ];
1591
1591
  // Don't auto-select a model - let the dropdown show the blank option
1592
1592
  this.selectedModelId = '';
@@ -1605,15 +1605,14 @@ export class AITestHarnessComponent {
1605
1605
  async getDefaultModelName(prompt) {
1606
1606
  try {
1607
1607
  // Get prompt-specific model associations
1608
- const promptModels = AIEngineBase.Instance.PromptModels.filter(pm => pm.PromptID === prompt.ID &&
1609
- (pm.Status === 'Active' || pm.Status === 'Preview'));
1608
+ const promptModels = AIEngineBase.Instance.PromptModels.filter((pm) => pm.PromptID === prompt.ID && (pm.Status === 'Active' || pm.Status === 'Preview'));
1610
1609
  let defaultModel = null;
1611
1610
  if (promptModels.length > 0) {
1612
1611
  // Sort by priority (higher priority first)
1613
1612
  promptModels.sort((a, b) => (b.Priority || 0) - (a.Priority || 0));
1614
1613
  // Find the first active model
1615
1614
  for (const pm of promptModels) {
1616
- const model = AIEngineBase.Instance.Models.find(m => m.ID === pm.ModelID && m.IsActive);
1615
+ const model = AIEngineBase.Instance.Models.find((m) => m.ID === pm.ModelID && m.IsActive);
1617
1616
  if (model) {
1618
1617
  defaultModel = model;
1619
1618
  break;
@@ -1622,8 +1621,7 @@ export class AITestHarnessComponent {
1622
1621
  }
1623
1622
  // If no prompt-specific model, use selection strategy
1624
1623
  if (!defaultModel) {
1625
- const candidates = AIEngineBase.Instance.Models.filter(m => m.IsActive &&
1626
- (!prompt.AIModelTypeID || m.AIModelTypeID === prompt.AIModelTypeID));
1624
+ const candidates = AIEngineBase.Instance.Models.filter((m) => m.IsActive && (!prompt.AIModelTypeID || m.AIModelTypeID === prompt.AIModelTypeID));
1627
1625
  if (candidates.length > 0) {
1628
1626
  // Apply selection strategy
1629
1627
  if (prompt.SelectionStrategy === 'ByPower') {
@@ -1660,18 +1658,16 @@ export class AITestHarnessComponent {
1660
1658
  return;
1661
1659
  }
1662
1660
  // Get vendors that offer this model - same logic as loadVendorsForModel
1663
- const modelVendors = AIEngineBase.Instance.ModelVendors.filter(mv => mv.ModelID === this.defaultModel.ID &&
1664
- mv.Status === 'Active' &&
1665
- mv.Type?.trim().toLowerCase() === 'inference provider');
1661
+ const modelVendors = AIEngineBase.Instance.ModelVendors.filter((mv) => mv.ModelID === this.defaultModel.ID && mv.Status === 'Active' && mv.Type?.trim().toLowerCase() === 'inference provider');
1666
1662
  // Map to vendor objects with priority from ModelVendor
1667
1663
  const vendorObjects = [];
1668
1664
  for (const mv of modelVendors) {
1669
- const vendor = AIEngineBase.Instance.Vendors.find(v => v.ID === mv.VendorID);
1665
+ const vendor = AIEngineBase.Instance.Vendors.find((v) => v.ID === mv.VendorID);
1670
1666
  if (vendor) {
1671
1667
  vendorObjects.push({
1672
1668
  ID: vendor.ID,
1673
1669
  Name: vendor.Name,
1674
- Priority: mv.Priority || 999 // Use ModelVendor priority
1670
+ Priority: mv.Priority || 999, // Use ModelVendor priority
1675
1671
  });
1676
1672
  }
1677
1673
  }
@@ -1699,12 +1695,12 @@ export class AITestHarnessComponent {
1699
1695
  EntityName: 'MJ: AI Configurations',
1700
1696
  ExtraFilter: `Status IN ('Active', 'Preview')`,
1701
1697
  OrderBy: 'IsDefault DESC, Name',
1702
- ResultType: 'entity_object'
1698
+ ResultType: 'entity_object',
1703
1699
  });
1704
1700
  if (result.Success && result.Results) {
1705
1701
  this.availableConfigurations = result.Results;
1706
1702
  // Auto-select the default configuration if one exists
1707
- const defaultConfig = this.availableConfigurations.find(c => c.IsDefault);
1703
+ const defaultConfig = this.availableConfigurations.find((c) => c.IsDefault);
1708
1704
  if (defaultConfig) {
1709
1705
  this.selectedConfigurationId = defaultConfig.ID;
1710
1706
  }
@@ -1750,19 +1746,17 @@ export class AITestHarnessComponent {
1750
1746
  return;
1751
1747
  }
1752
1748
  // Get model-specific vendors
1753
- const modelVendors = AIEngineBase.Instance.ModelVendors.filter(mv => mv.ModelID === this.selectedModelId &&
1754
- mv.Status === 'Active' &&
1755
- mv.Type?.trim().toLowerCase() === 'inference provider');
1749
+ const modelVendors = AIEngineBase.Instance.ModelVendors.filter((mv) => mv.ModelID === this.selectedModelId && mv.Status === 'Active' && mv.Type?.trim().toLowerCase() === 'inference provider');
1756
1750
  // Map to vendor objects with priority from ModelVendor
1757
1751
  const vendorObjects = [];
1758
1752
  for (const mv of modelVendors) {
1759
- const vendor = AIEngineBase.Instance.Vendors.find(v => v.ID === mv.VendorID);
1753
+ const vendor = AIEngineBase.Instance.Vendors.find((v) => v.ID === mv.VendorID);
1760
1754
  if (vendor) {
1761
1755
  // For now, include all vendors. TODO: Filter by vendor type when available
1762
1756
  vendorObjects.push({
1763
1757
  ID: vendor.ID,
1764
1758
  Name: vendor.Name,
1765
- Priority: mv.Priority || 999 // Use ModelVendor priority
1759
+ Priority: mv.Priority || 999, // Use ModelVendor priority
1766
1760
  });
1767
1761
  }
1768
1762
  }
@@ -1801,7 +1795,9 @@ export class AITestHarnessComponent {
1801
1795
  this.advancedParams.seed = prompt.Seed;
1802
1796
  if (prompt.StopSequences) {
1803
1797
  this.stopSequencesText = prompt.StopSequences;
1804
- this.advancedParams.stopSequences = prompt.StopSequences.split(',').map((s) => s.trim()).filter((s) => s.length > 0);
1798
+ this.advancedParams.stopSequences = prompt.StopSequences.split(',')
1799
+ .map((s) => s.trim())
1800
+ .filter((s) => s.length > 0);
1805
1801
  }
1806
1802
  if (prompt.IncludeLogProbs != null)
1807
1803
  this.advancedParams.includeLogProbs = prompt.IncludeLogProbs;
@@ -1825,7 +1821,7 @@ export class AITestHarnessComponent {
1825
1821
  EntityName: 'Template Params',
1826
1822
  ExtraFilter: `TemplateID='${prompt.TemplateID}'`,
1827
1823
  OrderBy: 'Name ASC',
1828
- ResultType: 'entity_object'
1824
+ ResultType: 'entity_object',
1829
1825
  });
1830
1826
  if (result.Success && result.Results && result.Results.length > 0) {
1831
1827
  // Clear existing template variables
@@ -1835,7 +1831,7 @@ export class AITestHarnessComponent {
1835
1831
  this.templateVariables.push({
1836
1832
  name: param.Name,
1837
1833
  value: param.DefaultValue || '',
1838
- type: this.getVariableTypeFromParamType(param.Type)
1834
+ type: this.getVariableTypeFromParamType(param.Type),
1839
1835
  });
1840
1836
  }
1841
1837
  // If no parameters found, add one empty variable to start
@@ -1883,14 +1879,14 @@ export class AITestHarnessComponent {
1883
1879
  // Reset vendor - will be loaded by loadDefaultVendor
1884
1880
  this.selectedVendorId = '';
1885
1881
  // Reset response format to prompt's setting
1886
- const format = this.responseFormatOptions.find(f => f.value.trim().toLowerCase() === prompt.ResponseFormat.trim().toLowerCase());
1882
+ const format = this.responseFormatOptions.find((f) => f.value.trim().toLowerCase() === prompt.ResponseFormat.trim().toLowerCase());
1887
1883
  this.selectedResponseFormat = format || this.responseFormatOptions[0];
1888
1884
  // Reset max tokens
1889
1885
  this.maxTokens = null;
1890
1886
  // Reset skip validation
1891
1887
  this.skipValidation = false;
1892
1888
  // Reset configuration to default
1893
- const defaultConfig = this.availableConfigurations.find(c => c.IsDefault);
1889
+ const defaultConfig = this.availableConfigurations.find((c) => c.IsDefault);
1894
1890
  if (defaultConfig) {
1895
1891
  this.selectedConfigurationId = defaultConfig.ID;
1896
1892
  }
@@ -1908,7 +1904,7 @@ export class AITestHarnessComponent {
1908
1904
  seed: null,
1909
1905
  stopSequences: [],
1910
1906
  includeLogProbs: false,
1911
- topLogProbs: 2
1907
+ topLogProbs: 2,
1912
1908
  };
1913
1909
  this.stopSequencesText = '';
1914
1910
  // Reload prompt defaults for advanced params
@@ -1953,14 +1949,14 @@ export class AITestHarnessComponent {
1953
1949
  seed: null,
1954
1950
  stopSequences: [],
1955
1951
  includeLogProbs: false,
1956
- topLogProbs: 2
1952
+ topLogProbs: 2,
1957
1953
  };
1958
1954
  this.stopSequencesText = '';
1959
1955
  this.advancedParamsExpanded = false;
1960
1956
  this.skipValidation = false;
1961
1957
  // Reset agent configuration to default
1962
1958
  if (this.mode === 'agent') {
1963
- const defaultConfig = this.availableConfigurations.find(c => c.IsDefault);
1959
+ const defaultConfig = this.availableConfigurations.find((c) => c.IsDefault);
1964
1960
  this.agentConfigurationId = defaultConfig?.ID || '';
1965
1961
  }
1966
1962
  }
@@ -2006,7 +2002,7 @@ export class AITestHarnessComponent {
2006
2002
  currentAgentRun: !!this.currentAgentRun,
2007
2003
  agentRunStatus: this.currentAgentRun?.Status || 'none',
2008
2004
  conversationMessages: this.conversationMessages.length,
2009
- executionMonitorMode: this.executionMonitorMode
2005
+ executionMonitorMode: this.executionMonitorMode,
2010
2006
  });
2011
2007
  this.activeTab = tab;
2012
2008
  // If switching to execution monitor tab, ensure it has the latest data
@@ -2014,13 +2010,11 @@ export class AITestHarnessComponent {
2014
2010
  console.log('📊 Switching to execution monitor tab');
2015
2011
  // Always ensure we have the latest execution data when switching to monitor
2016
2012
  if (this.conversationMessages.length > 0) {
2017
- const lastAssistantMessage = this.conversationMessages
2018
- .filter(m => m.role === 'assistant' && m.agentRunId)
2019
- .pop();
2013
+ const lastAssistantMessage = this.conversationMessages.filter((m) => m.role === 'assistant' && m.agentRunId).pop();
2020
2014
  console.log('🔍 Last assistant message with agent run:', {
2021
2015
  found: !!lastAssistantMessage,
2022
2016
  hasAgentRunId: !!lastAssistantMessage?.agentRunId,
2023
- agentRunId: lastAssistantMessage?.agentRunId
2017
+ agentRunId: lastAssistantMessage?.agentRunId,
2024
2018
  });
2025
2019
  if (lastAssistantMessage && lastAssistantMessage.agentRunId) {
2026
2020
  // Always update agent run to ensure it's fresh
@@ -2031,7 +2025,7 @@ export class AITestHarnessComponent {
2031
2025
  this.lastProcessedRunId = messageRunId;
2032
2026
  console.log('✅ Loading agent run:', {
2033
2027
  mode: this.executionMonitorMode,
2034
- runId: this.lastProcessedRunId
2028
+ runId: this.lastProcessedRunId,
2035
2029
  });
2036
2030
  // Trigger change detection to ensure the execution monitor updates
2037
2031
  setTimeout(() => {
@@ -2048,7 +2042,7 @@ export class AITestHarnessComponent {
2048
2042
  }
2049
2043
  else {
2050
2044
  console.log('📄 Switching away from execution monitor, preserving data:', {
2051
- currentAgentRunExists: !!this.currentAgentRun
2045
+ currentAgentRunExists: !!this.currentAgentRun,
2052
2046
  });
2053
2047
  // Don't clear currentAgentRun when switching away from execution monitor
2054
2048
  // This preserves the state for when the user switches back
@@ -2094,7 +2088,7 @@ export class AITestHarnessComponent {
2094
2088
  this.executionMonitorMode = 'historical';
2095
2089
  console.log('✅ Loaded agent run:', {
2096
2090
  id: agentRunEntity.ID,
2097
- stepCount: this.currentAgentRun.Steps?.length || 0
2091
+ stepCount: this.currentAgentRun.Steps?.length || 0,
2098
2092
  });
2099
2093
  // Force change detection to update the execution monitor
2100
2094
  this.cdr.detectChanges();
@@ -2151,7 +2145,7 @@ export class AITestHarnessComponent {
2151
2145
  id: this.generateMessageId(),
2152
2146
  role: 'user',
2153
2147
  content: this.currentUserMessage.trim(),
2154
- timestamp: new Date()
2148
+ timestamp: new Date(),
2155
2149
  };
2156
2150
  this.conversationMessages.push(userMessage);
2157
2151
  // Clear input and update change detection
@@ -2203,7 +2197,7 @@ export class AITestHarnessComponent {
2203
2197
  streamingContent: this.mode === 'agent' ? 'Running agent...' : 'Running prompt...',
2204
2198
  agentRunId: '',
2205
2199
  streamingStartTime: Date.now(),
2206
- elapsedTime: 0
2200
+ elapsedTime: 0,
2207
2201
  };
2208
2202
  this.conversationMessages.push(assistantMessage);
2209
2203
  this.scrollNeeded = true;
@@ -2235,7 +2229,7 @@ export class AITestHarnessComponent {
2235
2229
  data: Object.keys(dataContext).length > 0 ? dataContext : undefined,
2236
2230
  lastRunId: this.lastAgentRunId || undefined,
2237
2231
  autoPopulateLastRunPayload: this.lastAgentRunId ? true : false,
2238
- configurationId: this.agentConfigurationId || undefined
2232
+ configurationId: this.agentConfigurationId || undefined,
2239
2233
  });
2240
2234
  // Stop elapsed time counter
2241
2235
  if (this.elapsedTimeInterval) {
@@ -2250,7 +2244,7 @@ export class AITestHarnessComponent {
2250
2244
  const parseOptions = {
2251
2245
  extractInlineJson: true,
2252
2246
  maxDepth: 100,
2253
- debug: false
2247
+ debug: false,
2254
2248
  };
2255
2249
  // Apply recursive JSON parsing to extract any nested JSON strings
2256
2250
  const fullResult = ParseJSONRecursive(executionResult, parseOptions);
@@ -2292,7 +2286,7 @@ export class AITestHarnessComponent {
2292
2286
  assistantMessage.payload = payloadData; // Store the payload if present
2293
2287
  const startedAt = executionResult.agentRun?.StartedAt ? new Date(executionResult.agentRun.StartedAt).getTime() : 0;
2294
2288
  const completedAt = executionResult.agentRun?.CompletedAt ? new Date(executionResult.agentRun.CompletedAt).getTime() : 0;
2295
- const executionTime = (startedAt && completedAt) ? (completedAt - startedAt) : 0;
2289
+ const executionTime = startedAt && completedAt ? completedAt - startedAt : 0;
2296
2290
  assistantMessage.executionTime = executionTime;
2297
2291
  assistantMessage.agentRunId = fullResult.agentRun?.ID || assistantMessage.agentRunId;
2298
2292
  // Update the tracking ID when we set new execution data
@@ -2310,7 +2304,7 @@ export class AITestHarnessComponent {
2310
2304
  console.error('❌ AI Test Harness: Execution failed', {
2311
2305
  success: executionResult?.success,
2312
2306
  errorMessage: executionResult?.agentRun?.ErrorMessage,
2313
- hasPayload: !!executionResult?.payload
2307
+ hasPayload: !!executionResult?.payload,
2314
2308
  });
2315
2309
  assistantMessage.content = 'I encountered an error processing your request.';
2316
2310
  assistantMessage.error = executionResult?.agentRun?.ErrorMessage || 'Unknown error occurred';
@@ -2340,7 +2334,7 @@ export class AITestHarnessComponent {
2340
2334
  error: error,
2341
2335
  message: error?.message,
2342
2336
  stack: error?.stack,
2343
- type: error?.constructor?.name
2337
+ type: error?.constructor?.name,
2344
2338
  });
2345
2339
  // Update assistant message with error
2346
2340
  const lastMessage = this.conversationMessages[this.conversationMessages.length - 1];
@@ -2381,7 +2375,7 @@ export class AITestHarnessComponent {
2381
2375
  streamingContent: this.mode === 'agent' ? 'Running agent...' : 'Running prompt...',
2382
2376
  agentRunId: '',
2383
2377
  streamingStartTime: Date.now(),
2384
- elapsedTime: 0
2378
+ elapsedTime: 0,
2385
2379
  };
2386
2380
  this.conversationMessages.push(assistantMessage);
2387
2381
  this.scrollNeeded = true;
@@ -2395,14 +2389,14 @@ export class AITestHarnessComponent {
2395
2389
  // Prepare data context (template variables + user message)
2396
2390
  const dataContext = {
2397
2391
  ...templateVariables,
2398
- userMessage: userMessage
2392
+ userMessage: userMessage,
2399
2393
  };
2400
2394
  // Build conversation messages
2401
2395
  const messages = this.conversationMessages
2402
- .filter(m => !m.isStreaming && m.content) // Only include non-streaming messages with content
2403
- .map(m => ({
2396
+ .filter((m) => !m.isStreaming && m.content) // Only include non-streaming messages with content
2397
+ .map((m) => ({
2404
2398
  role: m.role,
2405
- content: m.content
2399
+ content: m.content,
2406
2400
  }));
2407
2401
  // Execute the prompt using the new AI client
2408
2402
  const executionResult = await dataProvider.AI.RunAIPrompt({
@@ -2426,7 +2420,7 @@ export class AITestHarnessComponent {
2426
2420
  topLogProbs: this.advancedParams.includeLogProbs ? this.advancedParams.topLogProbs : undefined,
2427
2421
  messages: messages.length > 0 ? messages : undefined,
2428
2422
  rerunFromPromptRunID: this.originalPromptRunId || undefined,
2429
- systemPromptOverride: this.systemPromptOverride || undefined
2423
+ systemPromptOverride: this.systemPromptOverride || undefined,
2430
2424
  });
2431
2425
  // Stop elapsed time counter
2432
2426
  if (this.elapsedTimeInterval) {
@@ -2439,16 +2433,16 @@ export class AITestHarnessComponent {
2439
2433
  const parseOptions = {
2440
2434
  extractInlineJson: true,
2441
2435
  maxDepth: 100,
2442
- debug: false
2436
+ debug: false,
2443
2437
  };
2444
2438
  if (executionResult?.success) {
2445
2439
  // Use parsedResult if available, otherwise fall back to output
2446
2440
  // Handle case where parsedResult is already an object (from GraphQL client)
2447
2441
  const contentToDisplay = executionResult.parsedResult
2448
- ? (typeof executionResult.parsedResult === 'object'
2442
+ ? typeof executionResult.parsedResult === 'object'
2449
2443
  ? JSON.stringify(executionResult.parsedResult, null, 2)
2450
- : executionResult.parsedResult)
2451
- : (executionResult.output || 'No response generated');
2444
+ : executionResult.parsedResult
2445
+ : executionResult.output || 'No response generated';
2452
2446
  assistantMessage.content = contentToDisplay;
2453
2447
  assistantMessage.executionTime = executionResult.executionTimeMs;
2454
2448
  // Store the complete execution result for JSON display
@@ -2522,10 +2516,10 @@ export class AITestHarnessComponent {
2522
2516
  startTypingAnimation(message) {
2523
2517
  const initialMessages = [
2524
2518
  "I'm processing your request...",
2525
- "Let me think about that...",
2526
- "Working on your request...",
2527
- "Analyzing your question...",
2528
- "Processing..."
2519
+ 'Let me think about that...',
2520
+ 'Working on your request...',
2521
+ 'Analyzing your question...',
2522
+ 'Processing...',
2529
2523
  ];
2530
2524
  const selectedMessage = initialMessages[Math.floor(Math.random() * initialMessages.length)];
2531
2525
  let index = 0;
@@ -2592,7 +2586,7 @@ export class AITestHarnessComponent {
2592
2586
  this.agentVariables.push({
2593
2587
  name: '',
2594
2588
  value: '',
2595
- type: 'string'
2589
+ type: 'string',
2596
2590
  });
2597
2591
  }
2598
2592
  /**
@@ -2610,7 +2604,7 @@ export class AITestHarnessComponent {
2610
2604
  this.templateVariables.push({
2611
2605
  name: '',
2612
2606
  value: '',
2613
- type: 'string'
2607
+ type: 'string',
2614
2608
  });
2615
2609
  }
2616
2610
  /**
@@ -2654,10 +2648,10 @@ export class AITestHarnessComponent {
2654
2648
  const parsedData = JSON.parse(saved);
2655
2649
  this.savedConversations = parsedData || [];
2656
2650
  // Convert date strings back to Date objects
2657
- this.savedConversations.forEach(conv => {
2651
+ this.savedConversations.forEach((conv) => {
2658
2652
  conv.createdAt = new Date(conv.createdAt);
2659
2653
  conv.updatedAt = new Date(conv.updatedAt);
2660
- conv.messages.forEach(msg => {
2654
+ conv.messages.forEach((msg) => {
2661
2655
  msg.timestamp = new Date(msg.timestamp);
2662
2656
  });
2663
2657
  });
@@ -2700,7 +2694,7 @@ export class AITestHarnessComponent {
2700
2694
  }
2701
2695
  // If updating existing conversation, pre-fill the name
2702
2696
  if (this.currentConversationId) {
2703
- const currentConv = this.savedConversations.find(c => c.id === this.currentConversationId);
2697
+ const currentConv = this.savedConversations.find((c) => c.id === this.currentConversationId);
2704
2698
  this.tempConversationName = currentConv ? currentConv.name : '';
2705
2699
  }
2706
2700
  else {
@@ -2746,11 +2740,11 @@ export class AITestHarnessComponent {
2746
2740
  skipValidation: this.mode === 'prompt' ? this.skipValidation : undefined,
2747
2741
  agentConfigurationId: this.mode === 'agent' ? this.agentConfigurationId : undefined,
2748
2742
  createdAt: new Date(),
2749
- updatedAt: new Date()
2743
+ updatedAt: new Date(),
2750
2744
  };
2751
2745
  if (this.currentConversationId) {
2752
2746
  // Update existing conversation
2753
- const index = this.savedConversations.findIndex(c => c.id === this.currentConversationId);
2747
+ const index = this.savedConversations.findIndex((c) => c.id === this.currentConversationId);
2754
2748
  if (index >= 0) {
2755
2749
  conversation.id = this.currentConversationId;
2756
2750
  conversation.createdAt = this.savedConversations[index].createdAt;
@@ -2803,7 +2797,7 @@ export class AITestHarnessComponent {
2803
2797
  }
2804
2798
  autoSaveConversation() {
2805
2799
  if (this.currentConversationId && this.conversationMessages.length > 0) {
2806
- const index = this.savedConversations.findIndex(c => c.id === this.currentConversationId);
2800
+ const index = this.savedConversations.findIndex((c) => c.id === this.currentConversationId);
2807
2801
  if (index >= 0) {
2808
2802
  this.savedConversations[index].messages = [...this.conversationMessages];
2809
2803
  this.savedConversations[index].dataContext = this.buildDataContext();
@@ -2868,9 +2862,7 @@ export class AITestHarnessComponent {
2868
2862
  this.agentVariables.push({
2869
2863
  name: key,
2870
2864
  value: typeof value === 'object' ? JSON.stringify(value) : String(value),
2871
- type: typeof value === 'boolean' ? 'boolean' :
2872
- typeof value === 'number' ? 'number' :
2873
- typeof value === 'object' ? 'object' : 'string'
2865
+ type: typeof value === 'boolean' ? 'boolean' : typeof value === 'number' ? 'number' : typeof value === 'object' ? 'object' : 'string',
2874
2866
  });
2875
2867
  }
2876
2868
  // Restore configuration for agent mode
@@ -2886,9 +2878,7 @@ export class AITestHarnessComponent {
2886
2878
  this.templateVariables.push({
2887
2879
  name: key,
2888
2880
  value: typeof value === 'object' ? JSON.stringify(value) : String(value),
2889
- type: typeof value === 'boolean' ? 'boolean' :
2890
- typeof value === 'number' ? 'number' :
2891
- typeof value === 'object' ? 'object' : 'string'
2881
+ type: typeof value === 'boolean' ? 'boolean' : typeof value === 'number' ? 'number' : typeof value === 'object' ? 'object' : 'string',
2892
2882
  });
2893
2883
  }
2894
2884
  // Restore advanced parameters
@@ -2924,9 +2914,7 @@ export class AITestHarnessComponent {
2924
2914
  }
2925
2915
  }
2926
2916
  // Load agent run from the last assistant message if available
2927
- const lastAssistantMessage = this.conversationMessages
2928
- .filter(m => m.role === 'assistant' && m.agentRunId)
2929
- .pop();
2917
+ const lastAssistantMessage = this.conversationMessages.filter((m) => m.role === 'assistant' && m.agentRunId).pop();
2930
2918
  if (lastAssistantMessage && lastAssistantMessage.agentRunId) {
2931
2919
  this.loadAgentRun(lastAssistantMessage.agentRunId);
2932
2920
  this.executionMonitorMode = 'historical';
@@ -2944,7 +2932,7 @@ export class AITestHarnessComponent {
2944
2932
  async deleteConversation(conversation, event) {
2945
2933
  event.stopPropagation();
2946
2934
  if (confirm(`Delete conversation "${conversation.name}"?`)) {
2947
- const index = this.savedConversations.findIndex(c => c.id === conversation.id);
2935
+ const index = this.savedConversations.findIndex((c) => c.id === conversation.id);
2948
2936
  if (index >= 0) {
2949
2937
  this.savedConversations.splice(index, 1);
2950
2938
  // Save to localStorage
@@ -2965,17 +2953,17 @@ export class AITestHarnessComponent {
2965
2953
  entity: {
2966
2954
  id: this.entity?.ID,
2967
2955
  name: this.getEntityName(),
2968
- type: this.mode
2956
+ type: this.mode,
2969
2957
  },
2970
2958
  messages: this.conversationMessages,
2971
2959
  dataContext: this.buildDataContext(),
2972
- exportedAt: new Date().toISOString()
2960
+ exportedAt: new Date().toISOString(),
2973
2961
  };
2974
2962
  // Add mode-specific data
2975
2963
  if (this.mode === 'agent') {
2976
2964
  exportData.templateData = this.buildTemplateData();
2977
2965
  exportData.agentSettings = {
2978
- configurationId: this.agentConfigurationId
2966
+ configurationId: this.agentConfigurationId,
2979
2967
  };
2980
2968
  }
2981
2969
  else {
@@ -2985,7 +2973,7 @@ export class AITestHarnessComponent {
2985
2973
  vendorId: this.selectedVendorId,
2986
2974
  configurationId: this.selectedConfigurationId,
2987
2975
  maxTokens: this.maxTokens,
2988
- skipValidation: this.skipValidation
2976
+ skipValidation: this.skipValidation,
2989
2977
  };
2990
2978
  exportData.advancedParams = this.advancedParams;
2991
2979
  }
@@ -3018,7 +3006,7 @@ export class AITestHarnessComponent {
3018
3006
  }
3019
3007
  this.conversationMessages = data.messages.map((msg) => ({
3020
3008
  ...msg,
3021
- timestamp: new Date(msg.timestamp)
3009
+ timestamp: new Date(msg.timestamp),
3022
3010
  }));
3023
3011
  // Import agent variables (unified from dataContext and templateData)
3024
3012
  this.agentVariables = [];
@@ -3027,9 +3015,13 @@ export class AITestHarnessComponent {
3027
3015
  this.agentVariables.push({
3028
3016
  name: key,
3029
3017
  value: typeof value === 'object' ? JSON.stringify(value) : String(value),
3030
- type: typeof value === 'boolean' ? 'boolean' :
3031
- typeof value === 'number' ? 'number' :
3032
- typeof value === 'object' ? 'object' : 'string'
3018
+ type: typeof value === 'boolean'
3019
+ ? 'boolean'
3020
+ : typeof value === 'number'
3021
+ ? 'number'
3022
+ : typeof value === 'object'
3023
+ ? 'object'
3024
+ : 'string',
3033
3025
  });
3034
3026
  }
3035
3027
  // Template data is already imported into agentVariables above
@@ -3116,7 +3108,7 @@ export class AITestHarnessComponent {
3116
3108
  formatTimestamp(date) {
3117
3109
  return date.toLocaleTimeString('en-US', {
3118
3110
  hour: '2-digit',
3119
- minute: '2-digit'
3111
+ minute: '2-digit',
3120
3112
  });
3121
3113
  }
3122
3114
  /**
@@ -3180,7 +3172,7 @@ export class AITestHarnessComponent {
3180
3172
  // Add agent run ID for reference
3181
3173
  const enhancedParsed = {
3182
3174
  ...parsed,
3183
- _agentRunID: parsed.agentRunID
3175
+ _agentRunID: parsed.agentRunID,
3184
3176
  };
3185
3177
  this.currentJsonContent = this.formatJson(enhancedParsed);
3186
3178
  }
@@ -3211,7 +3203,7 @@ export class AITestHarnessComponent {
3211
3203
  resizable: true,
3212
3204
  draggable: true,
3213
3205
  top: centerTop,
3214
- left: centerLeft
3206
+ left: centerLeft,
3215
3207
  });
3216
3208
  // Pass the JSON content to the component
3217
3209
  const windowContent = this.jsonWindowRef.content.instance;
@@ -3228,9 +3220,7 @@ export class AITestHarnessComponent {
3228
3220
  * Get the last run ID from conversation messages
3229
3221
  */
3230
3222
  getLastRunId() {
3231
- const lastAssistantMessage = this.conversationMessages
3232
- .filter(m => m.role === 'assistant' && m.agentRunId)
3233
- .pop();
3223
+ const lastAssistantMessage = this.conversationMessages.filter((m) => m.role === 'assistant' && m.agentRunId).pop();
3234
3224
  return lastAssistantMessage?.agentRunId || null;
3235
3225
  }
3236
3226
  /**
@@ -3264,9 +3254,7 @@ export class AITestHarnessComponent {
3264
3254
  async copyMessage(message) {
3265
3255
  try {
3266
3256
  // Convert content to string if needed
3267
- const content = typeof message.content === 'string'
3268
- ? message.content
3269
- : JSON.stringify(message.content);
3257
+ const content = typeof message.content === 'string' ? message.content : JSON.stringify(message.content);
3270
3258
  await navigator.clipboard.writeText(content);
3271
3259
  MJNotificationService.Instance.CreateSimpleNotification('Message copied to clipboard', 'success', 2000);
3272
3260
  }
@@ -3310,7 +3298,7 @@ export class AITestHarnessComponent {
3310
3298
  const parseOptions = {
3311
3299
  extractInlineJson: true,
3312
3300
  maxDepth: 100,
3313
- debug: false
3301
+ debug: false,
3314
3302
  };
3315
3303
  try {
3316
3304
  let parsed;
@@ -3392,9 +3380,9 @@ export class AITestHarnessComponent {
3392
3380
  /```[\s\S]*?```/, // Code blocks
3393
3381
  /`[^`]+`/, // Inline code
3394
3382
  /^[-*+]\s/m, // Lists
3395
- /^\d+\.\s/m // Numbered lists
3383
+ /^\d+\.\s/m, // Numbered lists
3396
3384
  ];
3397
- if (markdownPatterns.some(pattern => pattern.test(content))) {
3385
+ if (markdownPatterns.some((pattern) => pattern.test(content))) {
3398
3386
  return 'markdown';
3399
3387
  }
3400
3388
  return 'text';
@@ -3403,9 +3391,7 @@ export class AITestHarnessComponent {
3403
3391
  // Basic markdown to HTML conversion with improved formatting
3404
3392
  let html = content;
3405
3393
  // Escape HTML first
3406
- html = html.replace(/&/g, '&')
3407
- .replace(/</g, '&lt;')
3408
- .replace(/>/g, '&gt;');
3394
+ html = html.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
3409
3395
  // Code blocks with language support
3410
3396
  html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (match, lang, code) => {
3411
3397
  const language = lang || '';
@@ -3495,12 +3481,7 @@ export class AITestHarnessComponent {
3495
3481
  * Escapes HTML content for use in attributes
3496
3482
  */
3497
3483
  escapeHtmlAttribute(text) {
3498
- return text
3499
- .replace(/&/g, '&amp;')
3500
- .replace(/"/g, '&quot;')
3501
- .replace(/'/g, '&#39;')
3502
- .replace(/</g, '&lt;')
3503
- .replace(/>/g, '&gt;');
3484
+ return text.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
3504
3485
  }
3505
3486
  /**
3506
3487
  * Renders JSON content with human-readable content prominently displayed
@@ -3541,9 +3522,12 @@ export class AITestHarnessComponent {
3541
3522
  */
3542
3523
  copyJsonContent() {
3543
3524
  if (this.currentJsonContent) {
3544
- navigator.clipboard.writeText(this.currentJsonContent).then(() => {
3525
+ navigator.clipboard
3526
+ .writeText(this.currentJsonContent)
3527
+ .then(() => {
3545
3528
  // Success - JSON copied
3546
- }).catch((err) => {
3529
+ })
3530
+ .catch((err) => {
3547
3531
  // Error copying
3548
3532
  });
3549
3533
  }
@@ -3562,15 +3546,14 @@ export class AITestHarnessComponent {
3562
3546
  return parsed.userMessage;
3563
3547
  }
3564
3548
  // Priority 2: Check for nested userMessage in nextStep or other objects
3565
- if (parsed.nextStep && parsed.nextStep.userMessage &&
3566
- typeof parsed.nextStep.userMessage === 'string' && parsed.nextStep.userMessage.trim()) {
3549
+ if (parsed.nextStep &&
3550
+ parsed.nextStep.userMessage &&
3551
+ typeof parsed.nextStep.userMessage === 'string' &&
3552
+ parsed.nextStep.userMessage.trim()) {
3567
3553
  return parsed.nextStep.userMessage;
3568
3554
  }
3569
3555
  // Priority 3: Other common human-readable fields
3570
- const contentFields = [
3571
- 'message', 'content', 'response', 'text', 'output',
3572
- 'result', 'answer', 'reply', 'description', 'summary'
3573
- ];
3556
+ const contentFields = ['message', 'content', 'response', 'text', 'output', 'result', 'answer', 'reply', 'description', 'summary'];
3574
3557
  for (const field of contentFields) {
3575
3558
  if (parsed[field] && typeof parsed[field] === 'string' && parsed[field].trim()) {
3576
3559
  return parsed[field];
@@ -3665,18 +3648,22 @@ export class AITestHarnessComponent {
3665
3648
  if (Array.isArray(obj)) {
3666
3649
  if (obj.length === 0)
3667
3650
  return '<span class="json-bracket">[]</span>';
3668
- const items = obj.map(item => `<div class="json-array-item">${nextIndent}${this.createJsonDisplayHtml(item, depth + 1)}</div>`).join(',\n');
3651
+ const items = obj
3652
+ .map((item) => `<div class="json-array-item">${nextIndent}${this.createJsonDisplayHtml(item, depth + 1)}</div>`)
3653
+ .join(',\n');
3669
3654
  return `<span class="json-bracket">[</span>\n${items}\n<div class="json-indent">${indent}</div><span class="json-bracket">]</span>`;
3670
3655
  }
3671
3656
  if (typeof obj === 'object') {
3672
3657
  const keys = Object.keys(obj);
3673
3658
  if (keys.length === 0)
3674
3659
  return '<span class="json-bracket">{}</span>';
3675
- const properties = keys.map(key => {
3660
+ const properties = keys
3661
+ .map((key) => {
3676
3662
  const escapedKey = this.escapeHtml(key);
3677
3663
  const value = this.createJsonDisplayHtml(obj[key], depth + 1);
3678
3664
  return `<div class="json-property">${nextIndent}<span class="json-key">"${escapedKey}"</span><span class="json-colon">:</span> ${value}</div>`;
3679
- }).join(',\n');
3665
+ })
3666
+ .join(',\n');
3680
3667
  return `<span class="json-bracket">{</span>\n${properties}\n<div class="json-indent">${indent}</div><span class="json-bracket">}</span>`;
3681
3668
  }
3682
3669
  return String(obj);
@@ -3747,8 +3734,8 @@ export class AITestHarnessComponent {
3747
3734
  // Split by comma and trim each sequence
3748
3735
  this.advancedParams.stopSequences = this.stopSequencesText
3749
3736
  .split(',')
3750
- .map(s => s.trim())
3751
- .filter(s => s.length > 0);
3737
+ .map((s) => s.trim())
3738
+ .filter((s) => s.length > 0);
3752
3739
  }
3753
3740
  }
3754
3741
  /**
@@ -3821,12 +3808,16 @@ export class AITestHarnessComponent {
3821
3808
  const convertedMessages = chatMessages.map((msg, index) => ({
3822
3809
  id: `msg-${Date.now()}-${index}`,
3823
3810
  role: msg.role,
3824
- content: typeof msg.content === 'string' ? msg.content :
3825
- Array.isArray(msg.content) ?
3826
- msg.content.filter(block => block.type === 'text').map(block => block.content).join('\n') :
3827
- '',
3811
+ content: typeof msg.content === 'string'
3812
+ ? msg.content
3813
+ : Array.isArray(msg.content)
3814
+ ? msg.content
3815
+ .filter((block) => block.type === 'text')
3816
+ .map((block) => block.content)
3817
+ .join('\n')
3818
+ : '',
3828
3819
  timestamp: new Date(),
3829
- isStreaming: false
3820
+ isStreaming: false,
3830
3821
  }));
3831
3822
  console.log('🎯 Converted messages for test harness:', convertedMessages);
3832
3823
  this.conversationMessages = convertedMessages;
@@ -3912,5 +3903,5 @@ export class AITestHarnessComponent {
3912
3903
  type: ViewChild,
3913
3904
  args: ['saveDialogInput']
3914
3905
  }] }); })();
3915
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AITestHarnessComponent, { className: "AITestHarnessComponent", filePath: "lib/ai-test-harness.component.ts", lineNumber: 155 }); })();
3906
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AITestHarnessComponent, { className: "AITestHarnessComponent", filePath: "lib/ai-test-harness.component.ts", lineNumber: 178 }); })();
3916
3907
  //# sourceMappingURL=ai-test-harness.component.js.map