@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.
- package/lib/cjs/agent-builder.d.ts +5 -0
- package/lib/cjs/agent-builder.js +7 -15
- package/lib/cjs/agent-builder.js.map +1 -1
- package/lib/cjs/bot-config-tools.d.ts +7 -0
- package/lib/cjs/bot-config-tools.js +19 -0
- package/lib/cjs/bot-config-tools.js.map +1 -0
- package/lib/cjs/constants.d.ts +2 -1
- package/lib/cjs/constants.js +3 -2
- package/lib/cjs/constants.js.map +1 -1
- package/lib/cjs/debug-logger.d.ts +4 -2
- package/lib/cjs/debug-logger.js +12 -14
- package/lib/cjs/debug-logger.js.map +1 -1
- package/lib/cjs/guardrails/input.d.ts +8 -1
- package/lib/cjs/guardrails/input.js +48 -2
- package/lib/cjs/guardrails/input.js.map +1 -1
- package/lib/cjs/index.d.ts +4 -1
- package/lib/cjs/index.js +34 -13
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/llm-config.d.ts +15 -0
- package/lib/cjs/llm-config.js +66 -0
- package/lib/cjs/llm-config.js.map +1 -0
- package/lib/cjs/runner.d.ts +7 -1
- package/lib/cjs/runner.js +94 -11
- package/lib/cjs/runner.js.map +1 -1
- package/lib/cjs/services/hubtype-api-client.d.ts +14 -0
- package/lib/{esm → cjs/services}/hubtype-api-client.js +19 -5
- package/lib/cjs/services/hubtype-api-client.js.map +1 -0
- package/lib/cjs/services/types.d.ts +85 -0
- package/lib/cjs/services/types.js +13 -0
- package/lib/cjs/services/types.js.map +1 -0
- package/lib/cjs/tools/retrieve-knowledge.js +1 -1
- package/lib/cjs/tools/retrieve-knowledge.js.map +1 -1
- package/lib/cjs/types.d.ts +7 -0
- package/lib/esm/agent-builder.d.ts +5 -0
- package/lib/esm/agent-builder.js +7 -15
- package/lib/esm/agent-builder.js.map +1 -1
- package/lib/esm/bot-config-tools.d.ts +7 -0
- package/lib/esm/bot-config-tools.js +19 -0
- package/lib/esm/bot-config-tools.js.map +1 -0
- package/lib/esm/constants.d.ts +2 -1
- package/lib/esm/constants.js +3 -2
- package/lib/esm/constants.js.map +1 -1
- package/lib/esm/debug-logger.d.ts +4 -2
- package/lib/esm/debug-logger.js +12 -14
- package/lib/esm/debug-logger.js.map +1 -1
- package/lib/esm/guardrails/input.d.ts +8 -1
- package/lib/esm/guardrails/input.js +48 -2
- package/lib/esm/guardrails/input.js.map +1 -1
- package/lib/esm/index.d.ts +4 -1
- package/lib/esm/index.js +34 -13
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/llm-config.d.ts +15 -0
- package/lib/esm/llm-config.js +66 -0
- package/lib/esm/llm-config.js.map +1 -0
- package/lib/esm/runner.d.ts +7 -1
- package/lib/esm/runner.js +94 -11
- package/lib/esm/runner.js.map +1 -1
- package/lib/esm/services/hubtype-api-client.d.ts +14 -0
- package/lib/{cjs → esm/services}/hubtype-api-client.js +19 -5
- package/lib/esm/services/hubtype-api-client.js.map +1 -0
- package/lib/esm/services/types.d.ts +85 -0
- package/lib/esm/services/types.js +13 -0
- package/lib/esm/services/types.js.map +1 -0
- package/lib/esm/tools/retrieve-knowledge.js +1 -1
- package/lib/esm/tools/retrieve-knowledge.js.map +1 -1
- package/lib/esm/types.d.ts +7 -0
- package/package.json +5 -4
- package/src/agent-builder.ts +22 -19
- package/src/bot-config-tools.ts +21 -0
- package/src/constants.ts +3 -2
- package/src/debug-logger.ts +24 -28
- package/src/guardrails/input.ts +82 -4
- package/src/index.ts +56 -16
- package/src/llm-config.ts +96 -0
- package/src/runner.ts +140 -12
- package/src/{hubtype-api-client.ts → services/hubtype-api-client.ts} +31 -83
- package/src/services/types.ts +106 -0
- package/src/tools/retrieve-knowledge.ts +1 -1
- package/src/types.ts +8 -0
- package/lib/cjs/hubtype-api-client.d.ts +0 -23
- package/lib/cjs/hubtype-api-client.js.map +0 -1
- package/lib/cjs/openai.d.ts +0 -1
- package/lib/cjs/openai.js +0 -39
- package/lib/cjs/openai.js.map +0 -1
- package/lib/esm/hubtype-api-client.d.ts +0 -23
- package/lib/esm/hubtype-api-client.js.map +0 -1
- package/lib/esm/openai.d.ts +0 -1
- package/lib/esm/openai.js +0 -39
- package/lib/esm/openai.js.map +0 -1
- 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 {
|
|
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 {
|
|
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
|
-
|
|
74
|
+
botContext: BotContext<TPlugins, TExtraData>,
|
|
68
75
|
aiAgentArgs: AiAgentArgs
|
|
69
76
|
): Promise<InferenceResponse> {
|
|
70
77
|
try {
|
|
71
|
-
const authToken = isProd
|
|
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:
|
|
105
|
+
contactInfo: botContext.session.user.contact_info || [],
|
|
84
106
|
inputGuardrailRules: aiAgentArgs.inputGuardrailRules || [],
|
|
85
107
|
sourceIds: aiAgentArgs.sourceIds || [],
|
|
86
|
-
campaignsContext:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
180
|
+
return await hubtypeClient.getMessages(botContext, memoryLength)
|
|
142
181
|
}
|
|
143
182
|
|
|
144
183
|
// Default to V2
|
|
145
|
-
const result = await hubtypeClient.getMessagesV2(
|
|
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(
|
|
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
|
|
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(
|
|
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
|
-
|
|
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 '
|
|
6
|
-
import type { AgenticInputMessage, Chunk } from '
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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 {
|