@botonic/plugin-ai-agents 0.45.0-alpha.1 → 0.46.0-alpha-3

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 (90) hide show
  1. package/lib/cjs/agent-builder.d.ts +5 -0
  2. package/lib/cjs/agent-builder.js +7 -15
  3. package/lib/cjs/agent-builder.js.map +1 -1
  4. package/lib/cjs/bot-config-tools.d.ts +7 -0
  5. package/lib/cjs/bot-config-tools.js +19 -0
  6. package/lib/cjs/bot-config-tools.js.map +1 -0
  7. package/lib/cjs/constants.d.ts +2 -1
  8. package/lib/cjs/constants.js +3 -2
  9. package/lib/cjs/constants.js.map +1 -1
  10. package/lib/cjs/debug-logger.d.ts +4 -2
  11. package/lib/cjs/debug-logger.js +12 -14
  12. package/lib/cjs/debug-logger.js.map +1 -1
  13. package/lib/cjs/guardrails/input.d.ts +8 -1
  14. package/lib/cjs/guardrails/input.js +48 -2
  15. package/lib/cjs/guardrails/input.js.map +1 -1
  16. package/lib/cjs/index.d.ts +4 -1
  17. package/lib/cjs/index.js +34 -13
  18. package/lib/cjs/index.js.map +1 -1
  19. package/lib/cjs/llm-config.d.ts +15 -0
  20. package/lib/cjs/llm-config.js +66 -0
  21. package/lib/cjs/llm-config.js.map +1 -0
  22. package/lib/cjs/runner.d.ts +7 -1
  23. package/lib/cjs/runner.js +94 -11
  24. package/lib/cjs/runner.js.map +1 -1
  25. package/lib/cjs/services/hubtype-api-client.d.ts +14 -0
  26. package/lib/{esm → cjs/services}/hubtype-api-client.js +19 -5
  27. package/lib/cjs/services/hubtype-api-client.js.map +1 -0
  28. package/lib/cjs/services/types.d.ts +85 -0
  29. package/lib/cjs/services/types.js +13 -0
  30. package/lib/cjs/services/types.js.map +1 -0
  31. package/lib/cjs/tools/retrieve-knowledge.js +1 -1
  32. package/lib/cjs/tools/retrieve-knowledge.js.map +1 -1
  33. package/lib/cjs/types.d.ts +7 -0
  34. package/lib/esm/agent-builder.d.ts +5 -0
  35. package/lib/esm/agent-builder.js +7 -15
  36. package/lib/esm/agent-builder.js.map +1 -1
  37. package/lib/esm/bot-config-tools.d.ts +7 -0
  38. package/lib/esm/bot-config-tools.js +19 -0
  39. package/lib/esm/bot-config-tools.js.map +1 -0
  40. package/lib/esm/constants.d.ts +2 -1
  41. package/lib/esm/constants.js +3 -2
  42. package/lib/esm/constants.js.map +1 -1
  43. package/lib/esm/debug-logger.d.ts +4 -2
  44. package/lib/esm/debug-logger.js +12 -14
  45. package/lib/esm/debug-logger.js.map +1 -1
  46. package/lib/esm/guardrails/input.d.ts +8 -1
  47. package/lib/esm/guardrails/input.js +48 -2
  48. package/lib/esm/guardrails/input.js.map +1 -1
  49. package/lib/esm/index.d.ts +4 -1
  50. package/lib/esm/index.js +34 -13
  51. package/lib/esm/index.js.map +1 -1
  52. package/lib/esm/llm-config.d.ts +15 -0
  53. package/lib/esm/llm-config.js +66 -0
  54. package/lib/esm/llm-config.js.map +1 -0
  55. package/lib/esm/runner.d.ts +7 -1
  56. package/lib/esm/runner.js +94 -11
  57. package/lib/esm/runner.js.map +1 -1
  58. package/lib/esm/services/hubtype-api-client.d.ts +14 -0
  59. package/lib/{cjs → esm/services}/hubtype-api-client.js +19 -5
  60. package/lib/esm/services/hubtype-api-client.js.map +1 -0
  61. package/lib/esm/services/types.d.ts +85 -0
  62. package/lib/esm/services/types.js +13 -0
  63. package/lib/esm/services/types.js.map +1 -0
  64. package/lib/esm/tools/retrieve-knowledge.js +1 -1
  65. package/lib/esm/tools/retrieve-knowledge.js.map +1 -1
  66. package/lib/esm/types.d.ts +7 -0
  67. package/package.json +5 -4
  68. package/src/agent-builder.ts +22 -19
  69. package/src/bot-config-tools.ts +21 -0
  70. package/src/constants.ts +3 -2
  71. package/src/debug-logger.ts +24 -28
  72. package/src/guardrails/input.ts +82 -4
  73. package/src/index.ts +56 -16
  74. package/src/llm-config.ts +96 -0
  75. package/src/runner.ts +140 -12
  76. package/src/{hubtype-api-client.ts → services/hubtype-api-client.ts} +31 -83
  77. package/src/services/types.ts +106 -0
  78. package/src/tools/retrieve-knowledge.ts +1 -1
  79. package/src/types.ts +8 -0
  80. package/lib/cjs/hubtype-api-client.d.ts +0 -23
  81. package/lib/cjs/hubtype-api-client.js.map +0 -1
  82. package/lib/cjs/openai.d.ts +0 -1
  83. package/lib/cjs/openai.js +0 -39
  84. package/lib/cjs/openai.js.map +0 -1
  85. package/lib/esm/hubtype-api-client.d.ts +0 -23
  86. package/lib/esm/hubtype-api-client.js.map +0 -1
  87. package/lib/esm/openai.d.ts +0 -1
  88. package/lib/esm/openai.js +0 -39
  89. package/lib/esm/openai.js.map +0 -1
  90. package/src/openai.ts +0 -49
package/src/index.ts CHANGED
@@ -5,13 +5,18 @@ import type {
5
5
  ResolvedPlugins,
6
6
  } from '@botonic/core'
7
7
  import { tool } from '@openai/agents'
8
-
8
+ import { v7 as uuidv7 } from 'uuid'
9
9
  import { AIAgentBuilder } from './agent-builder'
10
- import { isProd, MAX_MEMORY_LENGTH } from './constants'
10
+ import {
11
+ DEFAULT_MAX_RETRIES,
12
+ DEFAULT_TIMEOUT_16_SECONDS,
13
+ isProd,
14
+ MAX_MEMORY_LENGTH,
15
+ } from './constants'
11
16
  import { createDebugLogger, type DebugLogger } from './debug-logger'
12
- import { HubtypeApiClient } from './hubtype-api-client'
13
- import { setUpOpenAI } from './openai'
17
+ import { LLMConfig } from './llm-config'
14
18
  import { AIAgentRunner } from './runner'
19
+ import { HubtypeApiClient } from './services/hubtype-api-client'
15
20
  import type {
16
21
  AgenticInputMessage,
17
22
  Context,
@@ -32,11 +37,11 @@ export default class BotonicPluginAiAgents<
32
37
  private readonly messageHistoryApiVersion: MessageHistoryApiVersion
33
38
  private readonly memory: MemoryOptions
34
39
  private readonly logger: DebugLogger
40
+ private readonly timeout: number
41
+ private readonly maxRetries: number
35
42
  public toolDefinitions: CustomTool<TPlugins, TExtraData>[] = []
36
43
 
37
44
  constructor(options?: PluginAiAgentOptions<TPlugins, TExtraData>) {
38
- setUpOpenAI(options?.maxRetries, options?.timeout)
39
-
40
45
  if (options?.messageHistoryApiVersion === 'v1' && options?.memory) {
41
46
  throw new Error(
42
47
  'Cannot use memory when messageHistoryApiVersion is "v1". ' +
@@ -48,6 +53,8 @@ export default class BotonicPluginAiAgents<
48
53
  this.toolDefinitions = options?.customTools || []
49
54
  this.messageHistoryApiVersion = options?.messageHistoryApiVersion ?? 'v2'
50
55
  this.memory = options?.memory ?? {}
56
+ this.timeout = options?.timeout ?? DEFAULT_TIMEOUT_16_SECONDS
57
+ this.maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES
51
58
  this.logger = createDebugLogger(options?.enableDebug ?? false)
52
59
 
53
60
  this.logger.logInitialConfig({
@@ -64,34 +71,59 @@ export default class BotonicPluginAiAgents<
64
71
  }
65
72
 
66
73
  async getInference(
67
- request: BotContext<TPlugins, TExtraData>,
74
+ botContext: BotContext<TPlugins, TExtraData>,
68
75
  aiAgentArgs: AiAgentArgs
69
76
  ): Promise<InferenceResponse> {
70
77
  try {
71
- const authToken = isProd ? request.session._access_token : this.authToken
78
+ const authToken = isProd
79
+ ? botContext.session._access_token
80
+ : this.authToken
72
81
  if (!authToken) {
73
82
  throw new Error('Auth token is required')
74
83
  }
75
84
 
85
+ const inferenceId = uuidv7()
86
+
87
+ // Create client for OpenAI/Azure OpenAI
88
+ const llmConfig = new LLMConfig(
89
+ this.maxRetries,
90
+ this.timeout,
91
+ aiAgentArgs.model,
92
+ aiAgentArgs.verbosity
93
+ )
94
+
95
+ // Build tools
76
96
  const tools = this.buildTools(
77
97
  aiAgentArgs.activeTools?.map(tool => tool.name) || []
78
98
  )
99
+
100
+ // Build agent
79
101
  const agent = new AIAgentBuilder<TPlugins, TExtraData>({
80
102
  name: aiAgentArgs.name,
81
103
  instructions: aiAgentArgs.instructions,
82
104
  tools: tools,
83
- contactInfo: request.session.user.contact_info || [],
105
+ contactInfo: botContext.session.user.contact_info || [],
84
106
  inputGuardrailRules: aiAgentArgs.inputGuardrailRules || [],
85
107
  sourceIds: aiAgentArgs.sourceIds || [],
86
- campaignsContext: request.input.context?.campaigns_v2,
108
+ campaignsContext: botContext.input.context?.campaigns_v2,
87
109
  logger: this.logger,
110
+ llmConfig,
111
+ guardrailTrackingContext: {
112
+ botId: botContext.session.bot.id,
113
+ isTest: botContext.session.is_test_integration,
114
+ authToken,
115
+ inferenceId,
116
+ },
88
117
  }).build()
89
118
 
119
+ // Get messages
90
120
  const messages = await this.getMessages(
91
- request,
121
+ botContext,
92
122
  authToken,
93
123
  MAX_MEMORY_LENGTH
94
124
  )
125
+
126
+ // Build context
95
127
  const context: Context<TPlugins, TExtraData> = {
96
128
  authToken,
97
129
  sourceIds: aiAgentArgs.sourceIds || [],
@@ -101,16 +133,23 @@ export default class BotonicPluginAiAgents<
101
133
  chunksIds: [],
102
134
  chunkTexts: [],
103
135
  },
104
- request,
136
+ request: botContext,
105
137
  }
106
138
 
139
+ // Log agent debug info
107
140
  this.logger.logAgentDebugInfo(
108
141
  aiAgentArgs,
109
142
  tools.map(t => t.name),
110
143
  messages
111
144
  )
112
145
 
113
- const runner = new AIAgentRunner<TPlugins, TExtraData>(agent, this.logger)
146
+ // Run agent
147
+ const runner = new AIAgentRunner<TPlugins, TExtraData>(
148
+ agent,
149
+ llmConfig,
150
+ inferenceId,
151
+ this.logger
152
+ )
114
153
  return await runner.run(messages, context)
115
154
  } catch (error) {
116
155
  console.error('error plugin returns undefined', error)
@@ -127,7 +166,7 @@ export default class BotonicPluginAiAgents<
127
166
  }
128
167
 
129
168
  private async getMessages(
130
- request: BotContext,
169
+ botContext: BotContext,
131
170
  authToken: string,
132
171
  memoryLength: number
133
172
  ): Promise<AgenticInputMessage[]> {
@@ -138,11 +177,11 @@ export default class BotonicPluginAiAgents<
138
177
  }
139
178
 
140
179
  if (this.messageHistoryApiVersion === 'v1') {
141
- return await hubtypeClient.getMessages(request, memoryLength)
180
+ return await hubtypeClient.getMessages(botContext, memoryLength)
142
181
  }
143
182
 
144
183
  // Default to V2
145
- const result = await hubtypeClient.getMessagesV2(request, {
184
+ const result = await hubtypeClient.getMessagesV2(botContext, {
146
185
  maxMessages: this.memory.maxMessages ?? memoryLength,
147
186
  includeToolCalls: this.memory.includeToolCalls ?? true,
148
187
  maxFullToolResults: this.memory.maxFullToolResults ?? 1,
@@ -166,4 +205,5 @@ export default class BotonicPluginAiAgents<
166
205
  }
167
206
  }
168
207
 
208
+ export * from './bot-config-tools'
169
209
  export * from './types'
@@ -0,0 +1,96 @@
1
+ import type { VerbosityLevel } from '@botonic/core'
2
+ import {
3
+ type ModelProvider,
4
+ type ModelSettings,
5
+ OpenAIProvider,
6
+ } from '@openai/agents'
7
+ import OpenAI, { AzureOpenAI } from 'openai'
8
+ import {
9
+ AZURE_OPENAI_API_BASE,
10
+ AZURE_OPENAI_API_KEY,
11
+ AZURE_OPENAI_API_VERSION,
12
+ isProd,
13
+ OPENAI_API_KEY,
14
+ OPENAI_MODEL,
15
+ OPENAI_PROVIDER,
16
+ } from './constants'
17
+
18
+ export class LLMConfig {
19
+ private readonly maxRetries: number
20
+ private readonly timeout: number
21
+ public readonly modelName: string
22
+ public readonly modelSettings: ModelSettings
23
+ public readonly modelProvider: ModelProvider
24
+
25
+ constructor(
26
+ maxRetries: number,
27
+ timeout: number,
28
+ modelName: string,
29
+ verbosity: VerbosityLevel
30
+ ) {
31
+ this.maxRetries = maxRetries
32
+ this.timeout = timeout
33
+ this.modelName = OPENAI_PROVIDER === 'openai' ? OPENAI_MODEL : modelName
34
+ this.modelProvider = this.getModelProvider()
35
+ this.modelSettings = this.getModelSettings(modelName, verbosity)
36
+ }
37
+
38
+ private getModelProvider(): ModelProvider {
39
+ const client = this.getClient()
40
+ return new OpenAIProvider({
41
+ openAIClient: client,
42
+ useResponses: false,
43
+ })
44
+ }
45
+
46
+ private getClient(): OpenAI | AzureOpenAI {
47
+ if (OPENAI_PROVIDER === 'openai') {
48
+ return this.getOpenAIClient()
49
+ }
50
+
51
+ return this.getAzureClient()
52
+ }
53
+
54
+ private getOpenAIClient(): OpenAI {
55
+ return new OpenAI({
56
+ apiKey: OPENAI_API_KEY,
57
+ timeout: this.timeout,
58
+ maxRetries: this.maxRetries,
59
+ dangerouslyAllowBrowser: !isProd,
60
+ })
61
+ }
62
+
63
+ private getAzureClient(): AzureOpenAI {
64
+ return new AzureOpenAI({
65
+ apiKey: AZURE_OPENAI_API_KEY,
66
+ apiVersion: AZURE_OPENAI_API_VERSION,
67
+ deployment: this.modelName,
68
+ baseURL: AZURE_OPENAI_API_BASE,
69
+ timeout: this.timeout,
70
+ maxRetries: this.maxRetries,
71
+ dangerouslyAllowBrowser: !isProd,
72
+ })
73
+ }
74
+
75
+ private getModelSettings(
76
+ model: string,
77
+ verbosity: VerbosityLevel
78
+ ): ModelSettings {
79
+ if (model.includes('gpt-5')) {
80
+ return {
81
+ reasoning: { effort: 'none' },
82
+ temperature: 1,
83
+ text: { verbosity },
84
+ }
85
+ }
86
+
87
+ if (model.includes('gpt-4')) {
88
+ return {
89
+ temperature: 0,
90
+ text: { verbosity: 'medium' },
91
+ }
92
+ }
93
+
94
+ throw new Error(`Unsupported model: ${model}`)
95
+ }
96
+ }
package/src/runner.ts CHANGED
@@ -7,15 +7,20 @@ import {
7
7
  InputGuardrailTripwireTriggered,
8
8
  Runner,
9
9
  RunToolCallItem,
10
+ RunToolCallOutputItem,
10
11
  } from '@openai/agents'
11
-
12
+ import { AZURE_OPENAI_API_VERSION, isProd, OPENAI_PROVIDER } from './constants'
12
13
  import type { DebugLogger } from './debug-logger'
14
+ import type { LLMConfig } from './llm-config'
15
+ import { HubtypeApiClient } from './services/hubtype-api-client'
16
+ import { TrackFeature, TrackProductName } from './services/types'
13
17
  import { retrieveKnowledge } from './tools'
14
18
  import type {
15
19
  AgenticInputMessage,
16
20
  AgenticOutputMessage,
17
21
  AIAgent,
18
22
  Context,
23
+ ResultRawResponse,
19
24
  RunResult,
20
25
  } from './types'
21
26
 
@@ -26,6 +31,9 @@ interface AIAgentRunnerResult {
26
31
  messages?: OutputMessage[]
27
32
  }
28
33
  newItems?: RunToolCallItem[]
34
+ rawResponses?: ResultRawResponse[]
35
+ // biome-ignore lint/suspicious/noExplicitAny: state is a complex internal type
36
+ state?: any
29
37
  }
30
38
 
31
39
  export class AIAgentRunner<
@@ -33,10 +41,19 @@ export class AIAgentRunner<
33
41
  TExtraData = any,
34
42
  > {
35
43
  private agent: AIAgent<TPlugins, TExtraData>
44
+ private llmConfig: LLMConfig
45
+ private inferenceId: string
36
46
  private logger: DebugLogger
37
47
 
38
- constructor(agent: AIAgent<TPlugins, TExtraData>, logger: DebugLogger) {
48
+ constructor(
49
+ agent: AIAgent<TPlugins, TExtraData>,
50
+ openAiClient: LLMConfig,
51
+ inferenceId: string,
52
+ logger: DebugLogger
53
+ ) {
39
54
  this.agent = agent
55
+ this.llmConfig = openAiClient
56
+ this.inferenceId = inferenceId
40
57
  this.logger = logger
41
58
  }
42
59
 
@@ -46,11 +63,24 @@ export class AIAgentRunner<
46
63
  ): Promise<RunResult> {
47
64
  const startTime = Date.now()
48
65
 
49
- this.logger.logRunnerStart()
66
+ this.logger.logRunnerStart(
67
+ this.llmConfig.modelName,
68
+ this.llmConfig.modelSettings
69
+ )
50
70
 
51
71
  try {
72
+ const modelProvider = this.llmConfig.modelProvider
73
+ const modelSettings = this.llmConfig.modelSettings
74
+
75
+ const hasRetrieveKnowledge = this.agent.tools.includes(retrieveKnowledge)
76
+ if (hasRetrieveKnowledge && OPENAI_PROVIDER === 'azure') {
77
+ modelSettings.toolChoice = retrieveKnowledge.name
78
+ }
79
+
52
80
  const runner = new Runner({
53
- modelSettings: { temperature: 0 },
81
+ modelSettings,
82
+ modelProvider,
83
+ tracingDisabled: true,
54
84
  })
55
85
  // Type assertion to bypass strict type checking - the actual return type from runner.run()
56
86
  // doesn't perfectly match our interface, but the properties we access are compatible
@@ -58,6 +88,13 @@ export class AIAgentRunner<
58
88
  context,
59
89
  })) as AIAgentRunnerResult
60
90
 
91
+ const endTime = Date.now()
92
+
93
+ void this.sendLlmRunTracking(result, context, startTime, endTime)
94
+
95
+ const { _context, ...restResult } = result.state
96
+ console.log('Runner result:', restResult)
97
+
61
98
  const outputMessages = result.finalOutput?.messages || []
62
99
  const hasExit =
63
100
  outputMessages.length === 0 ||
@@ -105,15 +142,66 @@ export class AIAgentRunner<
105
142
  }
106
143
  }
107
144
 
145
+ private async sendLlmRunTracking(
146
+ result: AIAgentRunnerResult,
147
+ context: Context<TPlugins, TExtraData>,
148
+ startTime: number,
149
+ endTime: number
150
+ ): Promise<void> {
151
+ if (!isProd) {
152
+ return
153
+ }
154
+ const rawResponses = result.rawResponses ?? []
155
+ if (rawResponses.length === 0) {
156
+ return
157
+ }
158
+ const botId = context.request.session.bot.id
159
+ const isTest = context.request.session.is_test_integration
160
+ const totalDuration = endTime - startTime
161
+ const durationPerCall = Math.round(totalDuration / rawResponses.length)
162
+ const temperature =
163
+ (this.llmConfig.modelSettings.temperature as number | undefined) ?? 0
164
+ const apiVersion =
165
+ OPENAI_PROVIDER === 'azure' ? AZURE_OPENAI_API_VERSION : ''
166
+
167
+ const llmRuns = rawResponses.map(response => ({
168
+ inference_id: this.inferenceId,
169
+ is_test: isTest,
170
+ product_name: TrackProductName.AI_AGENT,
171
+ deployment_name: this.llmConfig.modelName,
172
+ model_name:
173
+ (response.providerData?.['model'] as string | undefined) ??
174
+ this.llmConfig.modelName,
175
+ feature: TrackFeature.AI_AGENT_RUN,
176
+ api_version: apiVersion,
177
+ num_prompt_tokens: response.usage.inputTokens,
178
+ num_completion_tokens: response.usage.outputTokens,
179
+ duration_in_milliseconds: durationPerCall,
180
+ temperature,
181
+ error: null,
182
+ }))
183
+
184
+ const client = new HubtypeApiClient(context.authToken)
185
+ await client.trackLlmRuns(botId, {
186
+ llm_runs: llmRuns,
187
+ })
188
+ }
189
+
108
190
  private getToolsExecuted(
109
191
  result,
110
192
  context: Context<TPlugins, TExtraData>
111
193
  ): ToolExecution[] {
194
+ const toolResultsByCallId = this.getToolResultsByCallId(result.newItems)
195
+
112
196
  return (
113
197
  result.newItems
114
198
  ?.filter(item => item instanceof RunToolCallItem)
115
199
  .map((item: RunToolCallItem) =>
116
- this.getToolExecutionInfo(item as RunToolCallItem, context)
200
+ this.getToolExecutionInfo(
201
+ item as RunToolCallItem,
202
+ context,
203
+ toolResultsByCallId
204
+ )
117
205
  )
118
206
  .filter(
119
207
  (toolExecution: ToolExecution) => toolExecution.toolName !== ''
@@ -121,9 +209,44 @@ export class AIAgentRunner<
121
209
  )
122
210
  }
123
211
 
212
+ private getToolResultsByCallId(newItems: unknown[]): Map<string, string> {
213
+ const map = new Map<string, string>()
214
+ for (const item of newItems || []) {
215
+ if (!(item instanceof RunToolCallOutputItem)) {
216
+ continue
217
+ }
218
+ const rawItem = item.rawItem as {
219
+ callId?: string
220
+ output?: string | { type?: string; text?: string }
221
+ }
222
+ const callId = rawItem?.callId
223
+ const output = rawItem?.output
224
+ if (callId == null || output == null) {
225
+ continue
226
+ }
227
+ const text = this.extractToolResult(output)
228
+ if (text != null) {
229
+ map.set(callId, text)
230
+ }
231
+ }
232
+ return map
233
+ }
234
+
235
+ private extractToolResult(
236
+ output: string | { type?: string; text?: string }
237
+ ): string | undefined {
238
+ if (typeof output === 'string') {
239
+ return output
240
+ }
241
+ return output?.type === 'text' && typeof output?.text === 'string'
242
+ ? output.text
243
+ : undefined
244
+ }
245
+
124
246
  private getToolExecutionInfo(
125
247
  item: RunToolCallItem,
126
- context: Context<TPlugins, TExtraData>
248
+ context: Context<TPlugins, TExtraData>,
249
+ toolResultsByCallId: Map<string, string>
127
250
  ): ToolExecution {
128
251
  if (item.rawItem.type !== 'function_call') {
129
252
  return {
@@ -133,20 +256,25 @@ export class AIAgentRunner<
133
256
  }
134
257
  const toolName = item.rawItem.name
135
258
  const toolArguments = this.getSafeToolArguments(item.rawItem.arguments)
259
+ const toolResults = item.rawItem.callId
260
+ ? toolResultsByCallId.get(item.rawItem.callId)
261
+ : undefined
262
+
263
+ const toolExecution: ToolExecution = {
264
+ toolName,
265
+ toolArguments,
266
+ toolResults,
267
+ }
136
268
 
137
269
  if (toolName === retrieveKnowledge.name) {
138
270
  return {
139
- toolName,
140
- toolArguments,
271
+ ...toolExecution,
141
272
  knowledgebaseSourcesIds: context.knowledgeUsed.sourceIds,
142
273
  knowledgebaseChunksIds: context.knowledgeUsed.chunksIds,
143
274
  }
144
275
  }
145
276
 
146
- return {
147
- toolName,
148
- toolArguments,
149
- }
277
+ return toolExecution
150
278
  }
151
279
 
152
280
  private getSafeToolArguments(rawToolArguments: string): Record<string, any> {
@@ -2,85 +2,19 @@
2
2
  import type { BotContext } from '@botonic/core'
3
3
  import axios from 'axios'
4
4
 
5
- import { HUBTYPE_API_URL } from './constants'
6
- import type { AgenticInputMessage, Chunk } from './types'
7
-
8
- interface HubtypeAssistantMessage {
9
- role: 'assistant'
10
- content: string
11
- }
12
-
13
- interface HubtypeUserMessage {
14
- role: 'user'
15
- content: string
16
- }
17
-
18
- type HubtypeMessage = HubtypeAssistantMessage | HubtypeUserMessage
19
-
20
- // V2 API Types
21
- interface HubtypeToolCall {
22
- id: string
23
- type: 'function'
24
- function: {
25
- name: string
26
- arguments: string
27
- }
28
- }
29
-
30
- interface HubtypeUserMessageV2 {
31
- role: 'user'
32
- content: string | null
33
- }
34
-
35
- interface HubtypeAssistantMessageV2 {
36
- role: 'assistant'
37
- content: string | null
38
- tool_calls?: HubtypeToolCall[] | null
39
- }
40
-
41
- interface HubtypeToolMessageV2 {
42
- role: 'tool'
43
- content: string | null
44
- tool_call_id: string
45
- }
46
-
47
- interface HubtypeSystemMessageV2 {
48
- role: 'system'
49
- content: string | null
50
- }
51
-
52
- type HubtypeMessageV2 =
53
- | HubtypeUserMessageV2
54
- | HubtypeAssistantMessageV2
55
- | HubtypeToolMessageV2
56
- | HubtypeSystemMessageV2
57
-
58
- interface MessageHistoryResponseV2 {
59
- messages: HubtypeMessageV2[]
60
- conversation_id: string | null
61
- truncated: boolean
62
- }
63
-
64
- export interface GetMessagesV2Options {
65
- maxMessages?: number
66
- includeToolCalls?: boolean
67
- maxFullToolResults?: number
68
- debugMode?: boolean
69
- }
70
-
71
- export interface GetMessagesV2Result {
72
- messages: AgenticInputMessage[]
73
- conversationId: string | null
74
- truncated: boolean
75
- }
76
-
77
- interface MessageHistoryV2Params {
78
- last_message_id: string
79
- max_messages?: number
80
- include_tool_calls?: boolean
81
- max_full_tool_results?: number
82
- debug_mode?: boolean
83
- }
5
+ import { HUBTYPE_API_URL } from '../constants'
6
+ import type { AgenticInputMessage, Chunk } from '../types'
7
+ import type {
8
+ GetMessagesV2Options,
9
+ GetMessagesV2Result,
10
+ HubtypeAssistantMessageV2,
11
+ HubtypeMessage,
12
+ HubtypeMessageV2,
13
+ HubtypeToolMessageV2,
14
+ MessageHistoryResponseV2,
15
+ MessageHistoryV2Params,
16
+ TrackLlmRunsData,
17
+ } from './types'
84
18
 
85
19
  export class HubtypeApiClient {
86
20
  private readonly authToken: string
@@ -131,7 +65,7 @@ export class HubtypeApiClient {
131
65
  }
132
66
 
133
67
  async getMessages(
134
- request: BotContext,
68
+ botContext: BotContext,
135
69
  maxMemoryLength: number
136
70
  ): Promise<AgenticInputMessage[]> {
137
71
  const url = `${HUBTYPE_API_URL}/external/v1/ai/agent/message_history/`
@@ -140,7 +74,7 @@ export class HubtypeApiClient {
140
74
  Authorization: `Bearer ${this.authToken}`,
141
75
  }
142
76
  const params = {
143
- last_message_id: request.input.message_id,
77
+ last_message_id: botContext.input.message_id,
144
78
  num_messages: maxMemoryLength,
145
79
  }
146
80
 
@@ -158,7 +92,7 @@ export class HubtypeApiClient {
158
92
  }
159
93
 
160
94
  async getMessagesV2(
161
- request: BotContext,
95
+ botContext: BotContext,
162
96
  options: GetMessagesV2Options = {}
163
97
  ): Promise<GetMessagesV2Result> {
164
98
  const url = `${HUBTYPE_API_URL}/external/v2/ai/agent/message_history/`
@@ -167,7 +101,7 @@ export class HubtypeApiClient {
167
101
  Authorization: `Bearer ${this.authToken}`,
168
102
  }
169
103
  const params: MessageHistoryV2Params = {
170
- last_message_id: request.input.message_id,
104
+ last_message_id: botContext.input.message_id,
171
105
  ...(options.maxMessages !== undefined && {
172
106
  max_messages: options.maxMessages,
173
107
  }),
@@ -268,6 +202,20 @@ export class HubtypeApiClient {
268
202
  }
269
203
  }
270
204
 
205
+ async trackLlmRuns(botId: string, data: TrackLlmRunsData): Promise<void> {
206
+ // Not stop the bot execution if the tracking fails, just log the error
207
+ try {
208
+ const url = `${HUBTYPE_API_URL}/external/v2/conversational_apps/${botId}/track_llm_runs/`
209
+ const headers = {
210
+ 'Content-Type': 'application/json',
211
+ Authorization: `Bearer ${this.authToken}`,
212
+ }
213
+ await axios.post(url, data, { headers })
214
+ } catch (error) {
215
+ console.error('Failed to track LLM runs:', error)
216
+ }
217
+ }
218
+
271
219
  private formatMessage(message: HubtypeMessage): AgenticInputMessage {
272
220
  if (message.role === 'user') {
273
221
  return {