@stack-spot/portal-network 0.215.1-alpha.0 → 0.216.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.
Files changed (49) 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 +3 -2
  7. package/dist/api/agent-tools.d.ts.map +1 -1
  8. package/dist/api/agent-tools.js.map +1 -1
  9. package/dist/api/genAiInference.d.ts +2 -49
  10. package/dist/api/genAiInference.d.ts.map +1 -1
  11. package/dist/api/genAiInference.js +2 -55
  12. package/dist/api/genAiInference.js.map +1 -1
  13. package/dist/client/account-asset-manager.d.ts +3 -0
  14. package/dist/client/account-asset-manager.d.ts.map +1 -1
  15. package/dist/client/ai.d.ts +75 -1
  16. package/dist/client/ai.d.ts.map +1 -1
  17. package/dist/client/ai.js +313 -2
  18. package/dist/client/ai.js.map +1 -1
  19. package/dist/client/discover.d.ts +2 -2
  20. package/dist/client/discover.d.ts.map +1 -1
  21. package/dist/client/discover.js +3 -4
  22. package/dist/client/discover.js.map +1 -1
  23. package/dist/client/gen-ai-inference.d.ts +0 -4
  24. package/dist/client/gen-ai-inference.d.ts.map +1 -1
  25. package/dist/client/gen-ai-inference.js +0 -267
  26. package/dist/client/gen-ai-inference.js.map +1 -1
  27. package/dist/client/types.d.ts +14 -13
  28. package/dist/client/types.d.ts.map +1 -1
  29. package/dist/client/workspace-ai.d.ts +2 -1
  30. package/dist/client/workspace-ai.d.ts.map +1 -1
  31. package/dist/client/workspace-ai.js +3 -3
  32. package/dist/client/workspace-ai.js.map +1 -1
  33. package/dist/utils/StreamedJson.d.ts +1 -7
  34. package/dist/utils/StreamedJson.d.ts.map +1 -1
  35. package/dist/utils/StreamedJson.js +2 -10
  36. package/dist/utils/StreamedJson.js.map +1 -1
  37. package/package.dev.json +3 -0
  38. package/package.json +3 -2
  39. package/package.stg.json +3 -0
  40. package/readme.md +2 -1
  41. package/src/api/accountAssetManager.ts +18 -11
  42. package/src/api/agent-tools.ts +3 -2
  43. package/src/api/genAiInference.ts +3 -119
  44. package/src/client/ai.ts +300 -2
  45. package/src/client/discover.ts +6 -7
  46. package/src/client/gen-ai-inference.ts +0 -281
  47. package/src/client/types.ts +14 -14
  48. package/src/client/workspace-ai.ts +5 -4
  49. package/src/utils/StreamedJson.tsx +2 -11
package/src/client/ai.ts CHANGED
@@ -1,14 +1,16 @@
1
1
  import { HttpError } from '@oazapfts/runtime'
2
- import { isArray } from 'lodash'
2
+ import { findLast, isArray, 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,6 +44,7 @@ import {
38
44
  listConversationsV1ConversationsGet,
39
45
  listKnowledgeSourcesV1KnowledgeSourcesGet,
40
46
  postEventV1EventsPost,
47
+ publishV1QuickCommandsSlugPublishPost,
41
48
  quickActionsV1QuickActionsPost,
42
49
  QuickCommandPromptResponse2,
43
50
  QuickCommandsExecutionRequest,
@@ -56,15 +63,22 @@ import {
56
63
  vectorizeCustomKnowledgeSourceV1KnowledgeSourcesSlugCustomPost,
57
64
  } from '../api/ai'
58
65
 
59
-
60
66
  import { StackspotAPIError } from '../error/StackspotAPIError'
61
67
  import { ReactQueryNetworkClient } from '../network/ReactQueryNetworkClient'
62
68
  import { removeAuthorizationParam } from '../utils/remove-authorization-param'
63
69
  import { StreamedJson } from '../utils/StreamedJson'
70
+ import { formatJson } from '../utils/string'
71
+ import { agentToolsClient } from './agent-tools'
64
72
  import {
73
+ AgentInfo,
74
+ ChatAgentTool,
75
+ ChatResponseWithSteps,
76
+ FixedChatRequest,
77
+ FixedChatResponse,
65
78
  FixedConversationResponse,
66
79
  FixedDependencyResponse,
67
80
  ReplaceResult,
81
+ StepChatStep,
68
82
  } from './types'
69
83
 
70
84
  const listQAV3WithoutAuthorization = removeAuthorizationParam(listAllV3QuickCommandsGet)
@@ -109,6 +123,10 @@ class AIClient extends ReactQueryNetworkClient {
109
123
  */
110
124
  vectorizeCustomKS = this.mutation(
111
125
  removeAuthorizationParam(vectorizeCustomKnowledgeSourceV1KnowledgeSourcesSlugCustomPost))
126
+ /**
127
+ * Deletes a KS
128
+ */
129
+ deleteKS = this.mutation(removeAuthorizationParam(deleteKnowledgeSourceV1KnowledgeSourcesSlugDelete))
112
130
  /**
113
131
  * Lists the AI Stacks according to their visibilities.
114
132
  */
@@ -150,6 +168,26 @@ class AIClient extends ReactQueryNetworkClient {
150
168
  * Gets the execution status and result of a quick command by its execution ID.
151
169
  */
152
170
  quickCommandCallbackExecutionId = this.query(removeAuthorizationParam(callbackV1QuickCommandsCallbackExecutionIdGet))
171
+ /**
172
+ * Creates a QC
173
+ */
174
+ createQuickCommand = this.mutation(removeAuthorizationParam(createQuickCommandV1QuickCommandsPost))
175
+ /**
176
+ * Deletes a QC
177
+ */
178
+ deleteQuickCommand = this.mutation(removeAuthorizationParam(deleteQuickCommandV1QuickCommandsSlugDelete))
179
+ /**
180
+ * Forks a QC
181
+ */
182
+ forkQuickCommand = this.mutation(removeAuthorizationParam(forkV1QuickCommandsSlugForkPost))
183
+ /**
184
+ * Publishes a QC
185
+ */
186
+ publishQuickCommand = this.mutation(removeAuthorizationParam(publishV1QuickCommandsSlugPublishPost))
187
+ /**
188
+ * Check Quick Command Available slug
189
+ */
190
+ checkAvailableQuickCommandSlug = this.query(removeAuthorizationParam(checkQuickCommandExistsV1QuickCommandsSlugExistsGet))
153
191
  /**
154
192
  * Lists the knowledge sources according to filters passed as parameter.
155
193
  */
@@ -166,6 +204,10 @@ class AIClient extends ReactQueryNetworkClient {
166
204
  * Lists knowledge sources matching the provided IDs.
167
205
  */
168
206
  searchKnowledgeSources = this.query(removeAuthorizationParam(searchKnowledgeSourcesV1KnowledgeSourcesSearchPost))
207
+ /**
208
+ * Finds Knowledge Source Dependencies
209
+ */
210
+ dependenciesKnowledgeSources = this.query(removeAuthorizationParam(findKnowledgeSourceDependenciesV1KnowledgeSourcesSlugDependenciesGet))
169
211
  /**
170
212
  * Gets the chat history. This is a paginated resource.
171
213
  */
@@ -332,6 +374,262 @@ class AIClient extends ReactQueryNetworkClient {
332
374
  deleteReviewComment = this.mutation(
333
375
  removeAuthorizationParam(deleteReviewCommentV1ResourcesResourceTypeSlugResourceSlugReviewsReviewIdAnswersAnswerIdDelete))
334
376
 
377
+ private static async toolsOfAgent(agentId?: string) {
378
+ try {
379
+ const agent = agentId ? await agentToolsClient.agent.query({ agentId }) : undefined
380
+ if (!agent) return []
381
+ const tools: (Omit<ChatAgentTool, 'duration' | 'prompt' | 'output'>)[] = []
382
+ agent.toolkits?.builtin_toolkits?.forEach(kit => kit.tools?.forEach(({ id, name, description }) => {
383
+ if (id) tools.push({ image: kit.image_url, id, name: name || id, description })
384
+ }))
385
+ agent.toolkits?.custom_toolkits?.forEach(kit => kit.tools?.forEach(({ id, name, description }) => {
386
+ if (id) tools.push({ image: kit.avatar ?? undefined, id, name: name || id, description })
387
+ }))
388
+ return tools
389
+ } catch {
390
+ return []
391
+ }
392
+ }
393
+
394
+ sendChatMessage(request: FixedChatRequest, minChangeIntervalMS?: number): StreamedJson<ChatResponseWithSteps> {
395
+ const abortController = new AbortController()
396
+ const headers = {
397
+ 'Content-Type': 'application/json',
398
+ 'Accept': 'text/event-stream',
399
+ }
400
+ const events = this.stream(
401
+ this.resolveURL('v3/chat'),
402
+ { method: 'post', body: JSON.stringify(request), headers, signal: abortController.signal },
403
+ )
404
+
405
+ const DYNAMIC_TOOL_ID = 'dynamic'
406
+ function isDynamicTool(info: AgentInfo) {
407
+ return info.type === 'tool' && info.id === DYNAMIC_TOOL_ID
408
+ }
409
+ /**
410
+ * This function treats events in the streaming that deals with the execution of tools. Since these events are not concatenated like
411
+ * normal streamings of data, we need this separate function to deal with them. It transforms the internal data model of the
412
+ * StreamedJson object whenever an event is triggered.
413
+ */
414
+ async function transform(event: Partial<FixedChatResponse>, data: Partial<ChatResponseWithSteps>) {
415
+ const info = event.agent_info
416
+ if (!info) return
417
+ const tools = await AIClient.toolsOfAgent(request.context?.agent_id)
418
+ data.steps = data.steps ? [...data.steps] : []
419
+
420
+ if (info.type === 'planning' && info.action === 'end') {
421
+ data.steps.push({
422
+ id: 'planning',
423
+ type: 'planning',
424
+ status: 'success',
425
+ duration: info.duration || 0,
426
+ steps: info.data?.steps?.map(s => s.goal) ?? [],
427
+ goal: info.data?.plan_goal ?? '',
428
+ })
429
+
430
+ info.data?.steps.forEach(s => data.steps?.push({
431
+ id: s.id,
432
+ type: 'step',
433
+ status: 'pending',
434
+ input: s.goal,
435
+ attempts: [{
436
+ tools: s.tools?.map(t => ({
437
+ ...(tools.find(({ id }) => id === t.tool_id) ?? { id: t.tool_id, name: t.tool_id }),
438
+ executionId: t.tool_execution_id,
439
+ goal: t.goal,
440
+ })),
441
+ }],
442
+ }))
443
+ data.steps.push({ id: 'answer', type: 'answer', status: 'pending' })
444
+ }
445
+
446
+ if (info.type === 'planning' && info.action === 'awaiting_approval') {
447
+ data.steps.push({
448
+ id: 'planning',
449
+ type: 'planning',
450
+ status: 'awaiting_approval',
451
+ user_question: info.data?.user_question,
452
+ duration: info.duration || 0,
453
+ steps: info.data?.steps?.map(s => s.goal) ?? [],
454
+ goal: info.data?.plan_goal ?? '',
455
+ })
456
+ info.data?.steps.forEach(s => data.steps?.push({
457
+ id: s.id,
458
+ type: 'step',
459
+ status: 'pending',
460
+ input: s.goal,
461
+ attempts: [{
462
+ tools: s.tools?.map(t => ({
463
+ ...(tools.find(({ id }) => id === t.tool_id) ?? { id: t.tool_id, name: t.tool_id }),
464
+ executionId: t.tool_execution_id,
465
+ goal: t.goal,
466
+ })),
467
+ }],
468
+ }))
469
+ data.steps.push({ id: 'answer', type: 'answer', status: 'pending' })
470
+ }
471
+
472
+ if (info.type === 'step' && info.action === 'start') {
473
+ const step = data.steps.find(s => s.id === info.id)
474
+ if (step) step.status = 'running'
475
+ }
476
+
477
+ if (info.type === 'step' && info.action === 'end') {
478
+ const step = data.steps.find(s => s.id === info.id) as StepChatStep
479
+ if (step) {
480
+ step.status = 'success'
481
+ step.duration = info.duration
482
+ const lastToolId = last(step.attempts[0].tools)?.id
483
+ const lastAttemptOfLastTool = findLast(step.attempts.map(a => a.tools).flat(), t => t?.id === lastToolId)
484
+ step.output = lastAttemptOfLastTool?.output
485
+ }
486
+ }
487
+
488
+ if (info.type === 'tool_calls' && info.action === 'start') {
489
+ const hasPlanning = data.steps.find(s => s.type === 'planning')
490
+ // On the first tool_calls:start, create the synthetic planning ("dynamic") step.
491
+ if (!hasPlanning) {
492
+ const userPrompt = request.user_prompt === 'string' ? request.user_prompt : JSON.stringify(request.user_prompt)
493
+ data.steps.push({
494
+ id: 'dynamic',
495
+ type: 'planning',
496
+ status: 'success',
497
+ steps: [],
498
+ goal: userPrompt,
499
+ user_question: userPrompt,
500
+ })
501
+ }
502
+ const toolsStepId = data.steps.filter(s => s.id === 'tools' || s.id.startsWith('tools-')).length + 1
503
+ data.steps.push({
504
+ id: `tools-${toolsStepId.toString()}`,
505
+ type: 'step',
506
+ status: 'running',
507
+ attempts: [{ tools: [] }],
508
+ } as StepChatStep)
509
+ }
510
+
511
+ if (info.type === 'tool_calls' && info.action === 'end') {
512
+ const lastStep = findLast(data.steps, s => s.id === 'tools' || s.id.startsWith('tools-')) as StepChatStep
513
+ if (lastStep) {
514
+ lastStep.status = 'success'
515
+ lastStep.duration = info.duration
516
+ const lastAttemptOfLastTool = last(lastStep.attempts.map(a => a.tools).flat())
517
+ lastStep.output = lastAttemptOfLastTool?.output
518
+ }
519
+ }
520
+
521
+ if (info.type === 'tool' && info.action === 'awaiting_approval') {
522
+ const tool = tools.find(({ id }) => id === info.data?.tool_id)
523
+ data.steps.push({
524
+ id: info.id,
525
+ type: 'tool',
526
+ status: 'awaiting_approval',
527
+ duration: info.duration || 0,
528
+ input: info.data?.input,
529
+ user_question: info.data?.user_question,
530
+ attempts: [{
531
+ tools: [{
532
+ executionId: info.id,
533
+ id: info.data?.tool_id ?? '',
534
+ name: tool?.name ?? '',
535
+ goal: tool?.goal,
536
+ ...tool,
537
+ }],
538
+ }],
539
+ })
540
+ data.steps.push({ id: 'answer', type: 'answer', status: 'pending' })
541
+ }
542
+
543
+ if (info.type === 'tool' && info.action === 'start') {
544
+ if (!info.data) return
545
+ const input = formatJson(info.data.input)
546
+ const tool = findLast(tools, ({ id }) => id === info.data?.tool_id) ?? { id: info.data?.tool_id, name: info.data?.tool_id }
547
+
548
+ const currentStep = findLast(data.steps, s => s.status === 'running') as StepChatStep
549
+
550
+ //There might be a tool with status awaiting_approval, so we want to inform tool has already started
551
+ if (!currentStep || !currentStep?.attempts?.[0]?.tools) {
552
+ data.steps.push({
553
+ id: info.id,
554
+ type: 'tool',
555
+ status: 'running',
556
+ duration: info.duration || 0,
557
+ input: info.data?.input,
558
+ user_question: info.data?.user_question,
559
+ attempts: [{
560
+ tools: [{ ...tool, executionId: info.id, input }],
561
+ }],
562
+ })
563
+ } else {
564
+ const toolInFirstAttempt = findLast(currentStep?.attempts?.[0]?.tools, t => t.executionId === info.id)
565
+ //One step might have multiple tools. When in an approval mode, we might not have all the tools in the array yet.
566
+ //For dynamic tools (id === 'dynamic'), we always push a new tool, since dynamic executions can trigger
567
+ //multiple tool runs in the same step and do not follow the planned tool structure.
568
+ //So we make sure to add any tools that are not in there, or always add for dynamic tools.
569
+ if (!toolInFirstAttempt || isDynamicTool(info)) {
570
+ currentStep.attempts?.[0].tools?.push({
571
+ ...tool,
572
+ executionId: info.id,
573
+ input,
574
+ status: 'running',
575
+ })
576
+ } else {
577
+ const input = formatJson(info.data.input)
578
+ if (info.data.attempt === 1) {
579
+ toolInFirstAttempt.input = input
580
+ } else {
581
+ currentStep.attempts[info.data.attempt - 1] ??= { tools: [] }
582
+ currentStep.attempts[info.data.attempt - 1].tools?.push({
583
+ ...tool,
584
+ executionId: info.id,
585
+ input,
586
+ })
587
+ }
588
+ }
589
+ }
590
+ }
591
+
592
+ if (info.type === 'tool' && info.action === 'end') {
593
+ const currentStep = data.steps.find(s => s.status === 'running') as StepChatStep
594
+ if (!currentStep || !info.data) return
595
+
596
+ // attempt index for tool execution starts at 0 for dynamically executed tools,while for planned tools it starts at 1
597
+ const attempt = isDynamicTool(info) ? info.data.attempt : info.data.attempt - 1
598
+ const tool = last(currentStep?.attempts?.[attempt]?.tools)
599
+ if (tool) {
600
+ tool.output = formatJson(info.data.output)
601
+ tool.duration = info.duration
602
+ tool.status = 'success'
603
+ }
604
+ }
605
+
606
+ if (info.type === 'final_answer' && info.action === 'start') {
607
+ const answerStep = last(data.steps)
608
+ if (answerStep) answerStep.status = 'running'
609
+ }
610
+
611
+
612
+ if (info.type === 'chat' && info.action === 'end') {
613
+ const lastStep = last(data.steps)
614
+ if (lastStep?.type === 'answer') {
615
+ lastStep.status = 'success'
616
+ lastStep.duration = info.duration
617
+ } else {
618
+ data.steps.push({ id: 'answer', type: 'answer', status: 'success' })
619
+ }
620
+ }
621
+ }
622
+
623
+ return new StreamedJson({
624
+ eventsPromise: events,
625
+ abortController,
626
+ minChangeIntervalMS,
627
+ ignoreKeys: ['agent_info'],
628
+ transform,
629
+ textFromErrorEvent: data => data.answer ?? 'Unknown error',
630
+ })
631
+ }
632
+
335
633
  contentDependencies = this.query(removeAuthorizationParam(
336
634
  getContentDependenciesV1ContentContentTypeContentIdDependenciesGet as ReplaceResult<
337
635
  typeof getContentDependenciesV1ContentContentTypeContentIdDependenciesGet,
@@ -2,16 +2,16 @@ import { HttpError } from '@oazapfts/runtime'
2
2
  import { findLast, last } from 'lodash'
3
3
  import { getApiAddresses } from '../api-addresses'
4
4
  import { ConversationResponse } from '../api/ai'
5
- import { AiChatRequest, create, create1, create2, defaults, deleteById, deleteById1, deleteById2, fetchInsights, getAll, getAll1, getAll2, getAllByHypothesis, getById, getById1, getById2, getInsightById, GetOpportunityResponse, refreshInsights } from '../api/discover'
5
+ import { create, create1, create2, defaults, deleteById, deleteById1, deleteById2, fetchInsights, getAll, getAll1, getAll2, getAllByHypothesis, getById, getById1, getById2, getInsightById, GetOpportunityResponse, AiChatRequest, refreshInsights } from '../api/discover'
6
6
  import { DefaultAPIError } from '../error/DefaultAPIError'
7
- import { baseDictionary } from '../error/dictionary/base'
8
7
  import { StackspotAPIError } from '../error/StackspotAPIError'
9
- import { ReactQueryNetworkClient } from '../network/ReactQueryNetworkClient'
10
8
  import { StreamedJson } from '../utils/StreamedJson'
9
+ import { baseDictionary } from '../error/dictionary/base'
11
10
  import { formatJson } from '../utils/string'
12
- import { agentToolsClient } from './agent-tools'
11
+ import { ReactQueryNetworkClient } from '../network/ReactQueryNetworkClient'
13
12
  import { aiClient } from './ai'
14
- import { AgentInfo, ChatAgentTool, ChatResponseWithSteps, FixedChatResponse, StepChatStep } from './types'
13
+ import { ChatAgentTool, ChatResponseWithSteps, FixedChatResponse, StepChatStep } from './types'
14
+ import { agentToolsClient } from './agent-tools'
15
15
 
16
16
  export interface ChatConversionDetails extends ConversationResponse {
17
17
  opportunityName?: string,
@@ -137,8 +137,7 @@ class DiscoverClient extends ReactQueryNetworkClient {
137
137
  * StreamedJson object whenever an event is triggered.
138
138
  */
139
139
  async function transform(event: Partial<FixedChatResponse>, data: Partial<ChatResponseWithSteps>) {
140
- // todo fix
141
- const info = event.agent_info as unknown as AgentInfo
140
+ const info = event.agent_info
142
141
 
143
142
  if (!info) return
144
143