@stack-spot/portal-network 1.0.0-dev.1768944806600 → 1.0.0-dev.1769537511491

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.
Files changed (47) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/api/accountAssetManager.d.ts +14 -10
  3. package/dist/api/accountAssetManager.d.ts.map +1 -1
  4. package/dist/api/accountAssetManager.js +5 -2
  5. package/dist/api/accountAssetManager.js.map +1 -1
  6. package/dist/api/agent-tools.d.ts +752 -164
  7. package/dist/api/agent-tools.d.ts.map +1 -1
  8. package/dist/api/agent-tools.js +203 -43
  9. package/dist/api/agent-tools.js.map +1 -1
  10. package/dist/api/ai.d.ts +3 -2
  11. package/dist/api/ai.d.ts.map +1 -1
  12. package/dist/api/ai.js +1 -2
  13. package/dist/api/ai.js.map +1 -1
  14. package/dist/api/workspace-ai.d.ts +41 -0
  15. package/dist/api/workspace-ai.d.ts.map +1 -1
  16. package/dist/api/workspace-ai.js +34 -0
  17. package/dist/api/workspace-ai.js.map +1 -1
  18. package/dist/client/account-asset-manager.d.ts +3 -0
  19. package/dist/client/account-asset-manager.d.ts.map +1 -1
  20. package/dist/client/agent-tools.d.ts +130 -3
  21. package/dist/client/agent-tools.d.ts.map +1 -1
  22. package/dist/client/agent-tools.js +105 -2
  23. package/dist/client/agent-tools.js.map +1 -1
  24. package/dist/client/ai.d.ts +98 -6
  25. package/dist/client/ai.d.ts.map +1 -1
  26. package/dist/client/ai.js +382 -8
  27. package/dist/client/ai.js.map +1 -1
  28. package/dist/client/types.d.ts +24 -1
  29. package/dist/client/types.d.ts.map +1 -1
  30. package/dist/client/workspace-ai.d.ts +15 -4
  31. package/dist/client/workspace-ai.d.ts.map +1 -1
  32. package/dist/client/workspace-ai.js +17 -3
  33. package/dist/client/workspace-ai.js.map +1 -1
  34. package/dist/utils/string.d.ts +6 -0
  35. package/dist/utils/string.d.ts.map +1 -1
  36. package/dist/utils/string.js +10 -0
  37. package/dist/utils/string.js.map +1 -1
  38. package/package.json +1 -1
  39. package/src/api/accountAssetManager.ts +18 -11
  40. package/src/api/agent-tools.ts +1259 -472
  41. package/src/api/ai.ts +3 -3
  42. package/src/api/workspace-ai.ts +83 -0
  43. package/src/client/agent-tools.ts +55 -2
  44. package/src/client/ai.ts +404 -8
  45. package/src/client/types.ts +30 -1
  46. package/src/client/workspace-ai.ts +23 -7
  47. package/src/utils/string.ts +12 -0
package/src/client/ai.ts CHANGED
@@ -1,14 +1,16 @@
1
1
  import { HttpError } from '@oazapfts/runtime'
2
- import { findLast, isArray, last } from 'lodash'
2
+ import { findLast, isArray, isNil, last } from 'lodash'
3
3
  import { getApiAddresses } from '../api-addresses'
4
4
  import {
5
5
  addFavoriteV1AiStacksStackIdFavoritePost,
6
6
  addFavoriteV1QuickCommandsSlugFavoritePost,
7
7
  calculateNextStepV1QuickCommandsSlugStepsStepSlugCalculateNextStepPost,
8
8
  callbackV1QuickCommandsCallbackExecutionIdGet,
9
+ checkQuickCommandExistsV1QuickCommandsSlugExistsGet,
9
10
  conversationHistoryV1ConversationsConversationIdGet,
10
11
  createAnswerForReviewV1ResourcesResourceTypeSlugResourceSlugReviewsReviewIdAnswersPost,
11
12
  createExecutionV1QuickCommandsCreateExecutionSlugPost,
13
+ createQuickCommandV1QuickCommandsPost,
12
14
  createResourceReviewV1ResourcesResourceTypeSlugResourceSlugReviewsPost,
13
15
  currentV1TokensUsageCurrentGet,
14
16
  defaults,
@@ -16,11 +18,15 @@ import {
16
18
  deleteFavoriteV1AiStacksStackIdFavoriteDelete,
17
19
  deleteFavoriteV1QuickCommandsSlugFavoriteDelete,
18
20
  deleteKnowledgeObjectByCustomIdV1KnowledgeSourcesSlugObjectsCustomIdDelete,
21
+ deleteKnowledgeSourceV1KnowledgeSourcesSlugDelete,
22
+ deleteQuickCommandV1QuickCommandsSlugDelete,
19
23
  deleteResourceReviewV1ResourcesResourceTypeSlugResourceSlugReviewsReviewIdDelete,
20
24
  deleteReviewCommentV1ResourcesResourceTypeSlugResourceSlugReviewsReviewIdAnswersAnswerIdDelete,
21
25
  downloadConversationV1ConversationsConversationIdDownloadGet,
22
26
  findKnowledgeObjectByCustomIdV1KnowledgeSourcesSlugObjectsCustomIdGet,
27
+ findKnowledgeSourceDependenciesV1KnowledgeSourcesSlugDependenciesGet,
23
28
  findKnowledgeSourceV1KnowledgeSourcesSlugGet,
29
+ forkV1QuickCommandsSlugForkPost,
24
30
  formatFetchStepV1QuickCommandsSlugStepsStepSlugFetchFormatPost,
25
31
  formatResultV1QuickCommandsSlugResultFormatPost,
26
32
  getContentDependenciesV1ContentContentTypeContentIdDependenciesGet,
@@ -38,10 +44,15 @@ import {
38
44
  listConversationsV1ConversationsGet,
39
45
  listKnowledgeSourcesV1KnowledgeSourcesGet,
40
46
  postEventV1EventsPost,
47
+ publishV1QuickCommandsSlugPublishPost,
41
48
  quickActionsV1QuickActionsPost,
49
+ QuickCommandPromptResponse,
42
50
  QuickCommandPromptResponse2,
51
+ QuickCommandScriptExecutionResponse,
43
52
  QuickCommandsExecutionRequest,
44
53
  quickCommandsRunV2V2QuickCommandsSlugStepsStepSlugRunPost,
54
+ QuickCommandStepFetchResponse,
55
+ QuickCommandStepLlmResponse,
45
56
  resetKnowledgeObjectsV1KnowledgeSourcesSlugObjectsDelete,
46
57
  runFetchStepV1QuickCommandsSlugStepsStepSlugFetchRunPost,
47
58
  searchKnowledgeSourcesV1KnowledgeSourcesSearchPost,
@@ -56,12 +67,11 @@ import {
56
67
  vectorizeCustomKnowledgeSourceV1KnowledgeSourcesSlugCustomPost,
57
68
  } from '../api/ai'
58
69
 
59
-
60
70
  import { StackspotAPIError } from '../error/StackspotAPIError'
61
71
  import { ReactQueryNetworkClient } from '../network/ReactQueryNetworkClient'
62
72
  import { removeAuthorizationParam } from '../utils/remove-authorization-param'
63
73
  import { StreamedJson } from '../utils/StreamedJson'
64
- import { formatJson } from '../utils/string'
74
+ import { formatJson, getSizeOfString } from '../utils/string'
65
75
  import { agentToolsClient } from './agent-tools'
66
76
  import {
67
77
  AgentInfo,
@@ -71,6 +81,9 @@ import {
71
81
  FixedChatResponse,
72
82
  FixedConversationResponse,
73
83
  FixedDependencyResponse,
84
+ QCContext,
85
+ QCContextExecution,
86
+ QCProgressProps,
74
87
  ReplaceResult,
75
88
  StepChatStep,
76
89
  } from './types'
@@ -117,6 +130,10 @@ class AIClient extends ReactQueryNetworkClient {
117
130
  */
118
131
  vectorizeCustomKS = this.mutation(
119
132
  removeAuthorizationParam(vectorizeCustomKnowledgeSourceV1KnowledgeSourcesSlugCustomPost))
133
+ /**
134
+ * Deletes a KS
135
+ */
136
+ deleteKS = this.mutation(removeAuthorizationParam(deleteKnowledgeSourceV1KnowledgeSourcesSlugDelete))
120
137
  /**
121
138
  * Lists the AI Stacks according to their visibilities.
122
139
  */
@@ -158,6 +175,26 @@ class AIClient extends ReactQueryNetworkClient {
158
175
  * Gets the execution status and result of a quick command by its execution ID.
159
176
  */
160
177
  quickCommandCallbackExecutionId = this.query(removeAuthorizationParam(callbackV1QuickCommandsCallbackExecutionIdGet))
178
+ /**
179
+ * Creates a QC
180
+ */
181
+ createQuickCommand = this.mutation(removeAuthorizationParam(createQuickCommandV1QuickCommandsPost))
182
+ /**
183
+ * Deletes a QC
184
+ */
185
+ deleteQuickCommand = this.mutation(removeAuthorizationParam(deleteQuickCommandV1QuickCommandsSlugDelete))
186
+ /**
187
+ * Forks a QC
188
+ */
189
+ forkQuickCommand = this.mutation(removeAuthorizationParam(forkV1QuickCommandsSlugForkPost))
190
+ /**
191
+ * Publishes a QC
192
+ */
193
+ publishQuickCommand = this.mutation(removeAuthorizationParam(publishV1QuickCommandsSlugPublishPost))
194
+ /**
195
+ * Check Quick Command Available slug
196
+ */
197
+ checkAvailableQuickCommandSlug = this.query(removeAuthorizationParam(checkQuickCommandExistsV1QuickCommandsSlugExistsGet))
161
198
  /**
162
199
  * Lists the knowledge sources according to filters passed as parameter.
163
200
  */
@@ -174,6 +211,10 @@ class AIClient extends ReactQueryNetworkClient {
174
211
  * Lists knowledge sources matching the provided IDs.
175
212
  */
176
213
  searchKnowledgeSources = this.query(removeAuthorizationParam(searchKnowledgeSourcesV1KnowledgeSourcesSearchPost))
214
+ /**
215
+ * Finds Knowledge Source Dependencies
216
+ */
217
+ dependenciesKnowledgeSources = this.query(removeAuthorizationParam(findKnowledgeSourceDependenciesV1KnowledgeSourcesSlugDependenciesGet))
177
218
  /**
178
219
  * Gets the chat history. This is a paginated resource.
179
220
  */
@@ -340,15 +381,16 @@ class AIClient extends ReactQueryNetworkClient {
340
381
  deleteReviewComment = this.mutation(
341
382
  removeAuthorizationParam(deleteReviewCommentV1ResourcesResourceTypeSlugResourceSlugReviewsReviewIdAnswersAnswerIdDelete))
342
383
 
343
- private static async toolsOfAgent(agentId?: string) {
384
+ private static async toolsOfAgent(agentId?: string, agent_version_number?: number) {
344
385
  try {
345
- const agent = agentId ? await agentToolsClient.agent.query({ agentId }) : undefined
386
+ const agent = agentId ? await agentToolsClient.agentV2.query({
387
+ agentCoreId: agentId, versionNumber: agent_version_number }) : undefined
346
388
  if (!agent) return []
347
389
  const tools: (Omit<ChatAgentTool, 'duration' | 'prompt' | 'output'>)[] = []
348
- agent.toolkits?.builtin_toolkits?.forEach(kit => kit.tools?.forEach(({ id, name, description }) => {
390
+ agent.version?.toolkits?.builtin_toolkits?.forEach(kit => kit.tools?.forEach(({ id, name, description }) => {
349
391
  if (id) tools.push({ image: kit.image_url, id, name: name || id, description })
350
392
  }))
351
- agent.toolkits?.custom_toolkits?.forEach(kit => kit.tools?.forEach(({ id, name, description }) => {
393
+ agent.version?.toolkits?.custom_toolkits?.forEach(kit => kit.tools?.forEach(({ id, name, description }) => {
352
394
  if (id) tools.push({ image: kit.avatar ?? undefined, id, name: name || id, description })
353
395
  }))
354
396
  return tools
@@ -357,6 +399,359 @@ class AIClient extends ReactQueryNetworkClient {
357
399
  }
358
400
  }
359
401
 
402
+ /**
403
+ * Runs an Router step of a quick command.
404
+ */
405
+ async runRouterStep(
406
+ ctx: QCContextExecution,
407
+ stepIndex: number, iteration: Record<string, number>,
408
+ progress?:QCProgressProps,
409
+ ) {
410
+ const { qc: { slug, steps }, code, resultMap, customInputs } = ctx
411
+ const step = steps![stepIndex]
412
+ const inputData = Object.keys(customInputs).length > 0 && code ? { ...customInputs, [code]: code } : code ?? customInputs
413
+ try {
414
+ progress?.onStepChange?.({ step: step.slug, ...resultMap, ...{ statusResult: 'START' } })
415
+ if (step.slug in iteration) {
416
+ iteration[step.slug] = iteration[step.slug] + 1
417
+ } else {
418
+ iteration[step.slug] = 1
419
+ }
420
+
421
+ const { next_step_slug } = await aiClient.calculateNextStep.mutate({
422
+ stepSlug: step.slug,
423
+ slug: slug,
424
+ quickCommandEvaluateStepRouterRequest: {
425
+ executions_count: iteration[step.slug],
426
+ input_data: inputData,
427
+ slugs_executions: resultMap,
428
+ },
429
+ })
430
+
431
+ progress?.onStepChange?.({ step: step.slug, ...resultMap, ...{ statusResult: 'END' } })
432
+
433
+ if (next_step_slug === step.slug) {
434
+ return aiClient.runStepsRecursively(stepIndex, ctx, iteration, progress)
435
+ }
436
+ const nextStepIndex = steps?.findIndex((step) => step.slug === next_step_slug)
437
+
438
+ if (isNil(nextStepIndex) || nextStepIndex === -1) return
439
+
440
+ return aiClient.runStepsRecursively(nextStepIndex, ctx, iteration, progress)
441
+ }
442
+ catch (error: any) {
443
+ progress?.onStepChange?.({
444
+ step: step.slug, error: error, answer: JSON.stringify(error.message), statusResult: 'ERROR', ...resultMap,
445
+ })
446
+ // eslint-disable-next-line no-console
447
+ console.error('Error executing QC step', error)
448
+ }
449
+ }
450
+
451
+ async getScriptStepStatus(
452
+ scriptExecutionId: string,
453
+ interval = 5000,
454
+ maxAttempts = 500,
455
+ currentAttempt = 0,
456
+ ): Promise<QuickCommandScriptExecutionResponse> {
457
+ if (currentAttempt >= maxAttempts) {
458
+ throw new Error('Max attempts reached in verify script status')
459
+ }
460
+ await aiClient.getStatusScriptStep.invalidate({ scriptExecutionId })
461
+ const response = await aiClient.getStatusScriptStep.query({ scriptExecutionId })
462
+
463
+ if (response.status === 'success') {
464
+ return response
465
+ }
466
+
467
+ if (response.status === 'failure') {
468
+ throw response
469
+ }
470
+
471
+ await new Promise(resolve => {setTimeout(resolve, interval)})
472
+
473
+ return aiClient.getScriptStepStatus(scriptExecutionId, interval, maxAttempts, currentAttempt + 1)
474
+ }
475
+ /**
476
+ * Runs a fetch step of a quick command and puts the result in the `resultMap` of the context passed as parameter.
477
+ */
478
+ async runFetchStep(ctx: QCContextExecution, stepIndex: number, progress?: QCProgressProps) {
479
+ const { qc: { slug, steps }, code, context, resultMap, customInputs, executionId, signal } = ctx
480
+ const step = steps![stepIndex] as QuickCommandStepFetchResponse
481
+ progress?.onStepChange?.({ step: step.slug, ...resultMap, answer: undefined, statusResult: 'START' })
482
+
483
+ //If is_remote we call backend to execute for us and we only have the response
484
+ if (step.is_remote) {
485
+ ctx.isRemote = true
486
+ try {
487
+ const { data } = await aiClient.fetchStepOfQuickCommandRemotely.mutate({
488
+ slug, stepSlug: step.slug,
489
+ quickCommandsExecutionRequest: {
490
+ code_selection: code, context, qc_execution_id: executionId,
491
+ slugs_executions: { ...resultMap, ...customInputs },
492
+ },
493
+ }, signal)
494
+
495
+ //data is the return of the request in the QC so we do not have full control over the response
496
+ //We handle the usual format with body, status_code and headers, but we might also handle other formats
497
+ const responseData = data as any
498
+ progress?.onStepChange?.({
499
+ step: step.slug, ...resultMap, answer: JSON.stringify(responseData.body) ?? JSON.stringify(responseData), statusResult: 'END' })
500
+ resultMap[step.slug] = {
501
+ status: responseData.status_code || 200,
502
+ data: JSON.stringify(responseData.body) ?? JSON.stringify(responseData),
503
+ headers: responseData.headers ?? {},
504
+ }
505
+ return
506
+ } catch (error) {
507
+ const errorMessage = `${error}, Failed to execute step "${step.slug}" of quick command "${slug}".`
508
+ progress?.onStepChange?.({ step: step.slug, ...resultMap, answer: errorMessage, error: errorMessage, statusResult: 'ERROR' })
509
+ throw new Error(errorMessage)
510
+ }
511
+ }
512
+
513
+ const { headers, data, method, url } = await aiClient.fetchStepOfQuickCommand.mutate({
514
+ slug,
515
+ stepSlug: step.slug,
516
+ quickCommandsExecutionRequest: {
517
+ input_data: code, context, qc_execution_id: executionId,
518
+ slugs_executions: { ...resultMap, ...customInputs },
519
+ },
520
+ }, signal)
521
+ const body = ['get', 'head'].includes(method.toLowerCase()) ? undefined : data
522
+
523
+ try {
524
+ //Local execution
525
+ const response = await fetch(url, { headers: headers || undefined, body, method, signal })
526
+ const responseData = await response.text()
527
+ if (!response.ok) throw new Error(`Failed to execute step "${step.slug}" of quick command "${slug}". Status ${response.status}.`)
528
+ progress?.onStepChange?.({ step: step.slug, ...resultMap, answer: responseData, statusResult: 'END' })
529
+
530
+ resultMap[step.slug] = {
531
+ status: response.status,
532
+ data: responseData,
533
+ headers: Object.fromEntries(response.headers.entries()),
534
+ }
535
+ }
536
+ catch (error) {
537
+ const errorMessage = `${error}, Failed to execute step "${step.slug}" of quick command "${slug}".`
538
+ progress?.onStepChange?.({ step: step.slug, ...resultMap, answer: errorMessage, error: errorMessage, statusResult: 'ERROR' })
539
+ throw new Error(errorMessage)
540
+ }
541
+ }
542
+
543
+ /**
544
+ * Runs an LLM step of a quick command and puts the result in the `resultMap` of the context passed as parameter.
545
+ */
546
+ async runLLMStep(
547
+ { qc: { slug, steps }, code, customInputs, context, executionId, resultMap, signal }: QCContextExecution,
548
+ stepIndex: number,
549
+ progress?: QCProgressProps,
550
+ ) {
551
+ const step = steps![stepIndex] as QuickCommandStepLlmResponse
552
+ let stepContext = context
553
+ if (!step.use_uploaded_files) {
554
+ const { upload_ids: _upload_ids, ...contextDataProps } = context
555
+ stepContext = { ...contextDataProps }
556
+ }
557
+ // eslint-disable-next-line no-async-promise-executor
558
+ return new Promise(async (resolve) => {
559
+ progress?.onStepChange?.({ step: step.slug, ...resultMap, answer: undefined, statusResult: 'START' })
560
+
561
+ const stream = aiClient.streamLlmStepOfQuickCommand(
562
+ slug,
563
+ step.slug,
564
+ {
565
+ input_data: code,
566
+ context: stepContext,
567
+ qc_execution_id: executionId,
568
+ slugs_executions: { ...resultMap, ...customInputs },
569
+ },
570
+ )
571
+
572
+ signal.addEventListener('abort', () => stream.cancel())
573
+
574
+ stream.onChange(item => {
575
+ if (item?.sources?.length) {
576
+ progress?.onStepChange?.({ step: step.slug, ...resultMap, sources: JSON.stringify(item.sources) })
577
+ } else {
578
+ item.answer !== undefined && progress?.onStepChange?.({ step: step.slug, ...resultMap, answer: item.answer })
579
+ }
580
+ })
581
+
582
+ try {
583
+ const finalValue = await stream.getValue()
584
+ resultMap[step.slug] = finalValue
585
+ progress?.onStepChange?.({ step: step.slug, ...resultMap, answer: finalValue.answer,
586
+ sources: finalValue.sources ? JSON.stringify(finalValue.sources) : '', statusResult: 'END' })
587
+ resolve(finalValue)
588
+ } catch (error: any) {
589
+ // eslint-disable-next-line no-console
590
+ console.error('Error executing QC step', error)
591
+ const errorStep = `Failed to execute step "${step.slug}" of quick command "${slug}". Reason: ${error.message}`
592
+ progress?.onStepChange?.({ step: step.slug, ...resultMap, answer: errorStep, error: errorStep, statusResult: 'ERROR' })
593
+ throw error
594
+ }
595
+ })
596
+ }
597
+
598
+ async runScriptStep(
599
+ { qc: { slug, steps }, code, context, resultMap, customInputs, signal }: QCContextExecution,
600
+ stepIndex: number,
601
+ progress?: QCProgressProps,
602
+ ) {
603
+ const step = steps![stepIndex] as QuickCommandStepLlmResponse
604
+ let stepContext = context
605
+ progress?.onStepChange?.({ step: step.slug, ...resultMap, ...{ statusResult: 'START' } })
606
+
607
+ if (!step.use_uploaded_files) {
608
+ const { upload_ids: _upload_ids, ...contextDataProps } = context
609
+ stepContext = { ...contextDataProps }
610
+ }
611
+
612
+ try {
613
+ const { script_execution_id } = await aiClient.startScriptStep.mutate({
614
+ stepSlug: step.slug,
615
+ slug: slug,
616
+ quickCommandStartScriptRequest: {
617
+ input_data: code,
618
+ custom_inputs: customInputs,
619
+ context: stepContext,
620
+ slugs_executions: resultMap,
621
+ },
622
+ }, signal)
623
+ const scriptResult = await aiClient.getScriptStepStatus(script_execution_id)
624
+ progress?.onStepChange?.({ step: step.slug, ...scriptResult, ...{ statusResult: 'END' } })
625
+ resultMap[step.slug] = scriptResult
626
+ }
627
+ catch (error: any) {
628
+ progress?.onStepChange?.({ step: step.slug, ...error, statusResult: 'ERROR', ...resultMap })
629
+ let message = error.result?.error ?? error.message ?? `${error}`
630
+ if (error instanceof StackspotAPIError) message = error.translate()
631
+ throw new Error(`Failed to execute step "${step.slug}" of quick command "${slug}". Error ${message}.`)
632
+ }
633
+ }
634
+
635
+ async runStepsRecursively(currentIndex: number, ctx: QCContextExecution, iteration: Record<string, number>,
636
+ progress?: QCProgressProps) {
637
+ const { qc, resultMap } = ctx
638
+
639
+ if (!qc.steps || currentIndex >= qc.steps?.length) return
640
+ progress?.update?.(currentIndex)
641
+
642
+ const currentStep = qc.steps[currentIndex]
643
+
644
+ if (currentStep.type === 'ROUTER') {
645
+ await aiClient.runRouterStep(ctx, currentIndex, iteration, progress)
646
+ return
647
+ }
648
+
649
+ const parsedStep = currentStep as QuickCommandStepFetchResponse | QuickCommandStepLlmResponse
650
+ let nextIndex = currentIndex + 1
651
+ let nextStepSlug = parsedStep.next_step_slug
652
+
653
+ if (currentStep.type === 'SCRIPT') {
654
+ await aiClient.runScriptStep(ctx, currentIndex, progress)
655
+ } else if (currentStep.type === 'FETCH') {
656
+ await aiClient.runFetchStep(ctx, currentIndex, progress)
657
+ } else {
658
+ try {
659
+ await aiClient.runLLMStep(ctx, currentIndex, progress)
660
+ } catch (error: any) {
661
+ progress?.onStepChange?.({ step: currentStep.slug,
662
+ error: error, answer: JSON.stringify(error), statusResult: 'ERROR', ...resultMap })
663
+ }
664
+ const stepResult = resultMap[currentStep.slug] as QuickCommandPromptResponse
665
+
666
+ //When we have an error but there is an error path defined
667
+ if (typeof stepResult !== 'string' && stepResult.answer_status?.next_step_slug) {
668
+ nextStepSlug = stepResult?.answer_status?.next_step_slug
669
+ } else if (!stepResult?.answer_status?.success) { //When we have an error but no error path defined we should fail the execution
670
+ progress?.onStepChange?.({
671
+ step: currentStep.slug, error: stepResult?.answer_status,
672
+ answer: JSON.stringify(stepResult?.answer_status?.failure_message), statusResult: 'ERROR', ...resultMap })
673
+ throw new Error()
674
+ }
675
+ }
676
+
677
+ const stepResult = ctx.resultMap[currentStep.slug]
678
+ if (stepResult && typeof stepResult !== 'string' && 'answer_status' in stepResult && !!stepResult.answer_status?.next_step_slug) {
679
+ nextStepSlug = stepResult.answer_status.next_step_slug
680
+ }
681
+
682
+ if (nextStepSlug) {
683
+ nextIndex = nextStepSlug === 'end' ?
684
+ qc.steps.length : qc.steps?.findIndex((step) => step.slug === nextStepSlug)
685
+ }
686
+ await aiClient.runStepsRecursively(nextIndex, ctx, iteration, progress)
687
+ }
688
+
689
+ async formatResult({ qc, code, executionId, context, resultMap, customInputs, signal }: QCContextExecution) {
690
+ const formatted = await aiClient.formatResultOfQuickCommand.mutate({
691
+ slug: qc.slug,
692
+ quickCommandsExecutionRequest: {
693
+ input_data: code,
694
+ context,
695
+ qc_execution_id: executionId,
696
+ slugs_executions: { ...resultMap, ...customInputs },
697
+ },
698
+
699
+ }, signal)
700
+ return formatted.result
701
+ }
702
+
703
+ /**
704
+ * This registers a quick command event in the backend (analytics).
705
+ */
706
+ private async registerQCAnalyticsEvent({
707
+ qc, isRemote, executionId, code = '', context }: QCContextExecution, status: string, start: number) {
708
+ const now = new Date().getTime()
709
+ try {
710
+ await aiClient.createEvent.mutate({
711
+ body: [{
712
+ type: 'custom_quick_command_execution',
713
+ quick_command_event: {
714
+ type: qc.type || '',
715
+ duration_execution: now - start,
716
+ status_execution: status,
717
+ slug: qc.slug,
718
+ qc_execution_id: executionId,
719
+ id: qc.id,
720
+ is_remote: isRemote,
721
+ },
722
+ code,
723
+ context,
724
+ knowledge_sources: [],
725
+ size: getSizeOfString(code),
726
+ generated_at: now,
727
+ }],
728
+ })
729
+ } catch (error) {
730
+ // eslint-disable-next-line no-console
731
+ console.warn('Failed to register event: quick command.')
732
+ }
733
+ }
734
+
735
+ async runQuickCommand(ctx: QCContext, progress?: QCProgressProps) {
736
+ const start = new Date().getTime()
737
+
738
+ const { slug } = ctx
739
+ const qc = await aiClient.quickCommand.query({ slug })
740
+ const ctxExecution: QCContextExecution = { ...ctx, qc }
741
+ try {
742
+ await aiClient.runStepsRecursively(0, ctxExecution, {}, progress)
743
+ progress?.remove?.()
744
+ const result = await aiClient.formatResult(ctxExecution)
745
+ await aiClient.registerQCAnalyticsEvent(ctxExecution, '200', start)
746
+ return result
747
+ } catch (error: any) {
748
+ let message = error.message || `${error}`
749
+ if (error instanceof StackspotAPIError) message = error.translate()
750
+ await aiClient.registerQCAnalyticsEvent(ctxExecution, message, start)
751
+ throw error
752
+ }
753
+ }
754
+
360
755
  sendChatMessage(request: FixedChatRequest, minChangeIntervalMS?: number): StreamedJson<ChatResponseWithSteps> {
361
756
  const abortController = new AbortController()
362
757
  const headers = {
@@ -380,7 +775,7 @@ class AIClient extends ReactQueryNetworkClient {
380
775
  async function transform(event: Partial<FixedChatResponse>, data: Partial<ChatResponseWithSteps>) {
381
776
  const info = event.agent_info
382
777
  if (!info) return
383
- const tools = await AIClient.toolsOfAgent(request.context?.agent_id)
778
+ const tools = await AIClient.toolsOfAgent(request.context?.agent_id, request.agent_version_number ?? undefined)
384
779
  data.steps = data.steps ? [...data.steps] : []
385
780
 
386
781
  if (info.type === 'planning' && info.action === 'end') {
@@ -602,6 +997,7 @@ class AIClient extends ReactQueryNetworkClient {
602
997
  FixedDependencyResponse
603
998
  >,
604
999
  ))
1000
+
605
1001
  }
606
1002
 
607
1003
  export const aiClient = new AIClient()
@@ -1,7 +1,7 @@
1
1
  import { RequestOpts } from '@oazapfts/runtime'
2
2
  import { AccountScmInfoSaveRequest, AccountScmInfoUpdateRequest, AccountScmStatusResponse, GroupsFromResourceResponse, MembersFromResourceResponse } from '../api/account'
3
3
  import { AgentVisibilityLevelEnum, HttpMethod, ListAgentResponse, VisibilityLevelEnum } from '../api/agent-tools'
4
- import { ChatRequest, ChatResponse3, ContentDependencyResponse, ConversationHistoryResponse, ConversationResponse, DependencyResponse } from '../api/ai'
4
+ import { ChatRequest, ChatResponse3, ContentDependencyResponse, ConversationHistoryResponse, ConversationResponse, DependencyResponse, QuickCommandResponse, QuickCommandStepResult } from '../api/ai'
5
5
  import { ConnectAccountRequestV2, ManagedAccountProvisionRequest } from '../api/cloudAccount'
6
6
  import { AllocationCostRequest, AllocationCostResponse, ChargePeriod, getAllocationCostFilters, ManagedService, ServiceResource } from '../api/cloudServices'
7
7
  import { Action } from '../api/workspace-ai'
@@ -421,3 +421,32 @@ export interface AgentToolsOpenAPIPreview {
421
421
  parameters?: Record<string, any>,
422
422
  request_body?: Record<string, any>,
423
423
  }
424
+
425
+ type SlugExecution = Record<string, QuickCommandStepResult>
426
+
427
+
428
+ interface BaseQCContext {
429
+ context: Required<FixedChatRequest>['context'],
430
+ resultMap: SlugExecution,
431
+ customInputs: Record<string, string>,
432
+ code?: string,
433
+ executionId: string,
434
+ signal: AbortSignal,
435
+ isRemote?: boolean,
436
+ headers?: Record<string, string>,
437
+ conversation_id?: string,
438
+ }
439
+
440
+ export interface QCContext extends BaseQCContext {
441
+ slug: string,
442
+ }
443
+
444
+ export interface QCContextExecution extends BaseQCContext {
445
+ qc: QuickCommandResponse,
446
+ }
447
+
448
+ export interface QCProgressProps{
449
+ update?: (index: number) => void,
450
+ remove?: () => void,
451
+ onStepChange?: (stepResult: any) => void,
452
+ }
@@ -1,9 +1,11 @@
1
1
  import { HttpError } from '@oazapfts/runtime'
2
- import { AgentResponse } from '../api/agent'
2
+ import { getApiAddresses } from '../api-addresses'
3
3
  import { CustomToolkitResponse } from '../api/agent-tools'
4
- import { GetAiStackResponse, KnowledgeSourceItemResponse, QuickCommandResponse } from '../api/ai'
4
+ import { GetAiStackResponse, QuickCommandResponse } from '../api/ai'
5
+ import { KnowledgeSourceItemResponse } from '../api/dataIntegration'
5
6
  import {
6
7
  addContentV1WorkspacesWorkspaceIdContentTypePost,
8
+ addContentV2WorkspacesWorkspaceIdContentTypePost,
7
9
  addFavoriteV1WorkspacesWorkspaceIdFavoritePost,
8
10
  addPermissionV1ResourceTypeResourceTypeResourcesResourceIdentifierPatch,
9
11
  addPermissionV1WorkspacesWorkspaceIdPermissionsPatch,
@@ -24,13 +26,12 @@ import {
24
26
  updateWorkspaceV1WorkspacesWorkspaceIdPatch,
25
27
  useShareableLinkV1ShareShareIdPatch,
26
28
  } from '../api/workspace-ai'
29
+ import { DefaultAPIError } from '../error/DefaultAPIError'
30
+ import { wksAiDictionary } from '../error/dictionary/workspace-ai'
27
31
  import { StackspotAPIError } from '../error/StackspotAPIError'
28
32
  import { ReactQueryNetworkClient } from '../network/ReactQueryNetworkClient'
29
33
  import { removeAuthorizationParam } from '../utils/remove-authorization-param'
30
- import { getApiAddresses } from '../api-addresses'
31
- import { DefaultAPIError } from '../error/DefaultAPIError'
32
- import { wksAiDictionary } from '../error/dictionary/workspace-ai'
33
- import { FixedAddResourceToWorkspaceAi, FixedWorkspaceAiPermissions, ReplaceResult } from './types'
34
+ import { AgentResponseWithBuiltIn, FixedAddResourceToWorkspaceAi, FixedWorkspaceAiPermissions, ReplaceResult } from './types'
34
35
 
35
36
  class WorkspaceAiClient extends ReactQueryNetworkClient {
36
37
  constructor() {
@@ -48,6 +49,7 @@ class WorkspaceAiClient extends ReactQueryNetworkClient {
48
49
  deleteWorkspaceAi = this.mutation(removeAuthorizationParam(deleteWorkspaceV1WorkspacesWorkspaceIdDelete))
49
50
 
50
51
  /**
52
+ * @deprecated
51
53
  * Adds a resource to the Spot.
52
54
  */
53
55
  addResourceTypeToWorkspaceAi = this.mutation({
@@ -61,6 +63,20 @@ class WorkspaceAiClient extends ReactQueryNetworkClient {
61
63
  permission: this.createPermissionFunctionFor(addContentV1WorkspacesWorkspaceIdContentTypePost),
62
64
  })
63
65
 
66
+ /**
67
+ * V2 for adding a resource to the Spot.
68
+ */
69
+ addResourceTypeV2ToSpot = this.mutation({
70
+ name: 'addResourceTypeToWorkspaceAi',
71
+ request: (
72
+ signal,
73
+ variables: Omit<Parameters<typeof addContentV2WorkspacesWorkspaceIdContentTypePost>[0], 'authorization'>,
74
+ ) =>
75
+ addContentV2WorkspacesWorkspaceIdContentTypePost({ ...variables, authorization: '' },
76
+ { signal }) as unknown as Promise<FixedAddResourceToWorkspaceAi>,
77
+ permission: this.createPermissionFunctionFor(addContentV2WorkspacesWorkspaceIdContentTypePost),
78
+ })
79
+
64
80
  /**
65
81
  * Lists all the knowledge sources in a Spot.
66
82
  */
@@ -117,7 +133,7 @@ class WorkspaceAiClient extends ReactQueryNetworkClient {
117
133
  'authorization' | 'contentType'>,
118
134
  ) =>
119
135
  listAllByContentTypeV1WorkspacesWorkspaceIdContentTypeGet({ ...variables, contentType: 'agent', authorization: '' },
120
- { signal }) as unknown as Promise<AgentResponse[]>,
136
+ { signal }) as unknown as Promise<AgentResponseWithBuiltIn[]>,
121
137
  permission: this.createPermissionFunctionFor(listAllByContentTypeV1WorkspacesWorkspaceIdContentTypeGet),
122
138
  })
123
139
 
@@ -17,3 +17,15 @@ export function formatJson(data: any): string {
17
17
  }
18
18
  return JSON.stringify(data, null, 2)
19
19
  }
20
+
21
+
22
+ /**
23
+ * Gets the size of a string removing control characters and spaces
24
+ * @param str the string to count.
25
+ * @returns the count value.
26
+ */
27
+ export function getSizeOfString(str: string): number {
28
+ // eslint-disable-next-line no-control-regex
29
+ const withoutSpacesAndControls = str.replace(/[\u0000-\u001F\u007F-\u009F\u061C\u200E\u200F\u202A-\u202E\u2066-\u2069\s]/g, '')
30
+ return withoutSpacesAndControls.length
31
+ }