@botonic/plugin-ai-agents 0.48.1 → 0.49.0-alpha.1
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/agents/base-agent.d.ts +28 -0
- package/lib/cjs/agents/base-agent.js +39 -0
- package/lib/cjs/agents/base-agent.js.map +1 -0
- package/lib/cjs/agents/index.d.ts +2 -0
- package/lib/cjs/agents/index.js +8 -0
- package/lib/cjs/agents/index.js.map +1 -0
- package/lib/cjs/agents/router-agent.d.ts +25 -0
- package/lib/cjs/agents/router-agent.js +33 -0
- package/lib/cjs/agents/router-agent.js.map +1 -0
- package/lib/cjs/agents/specialist-agent.d.ts +35 -0
- package/lib/cjs/{agent-builder.js → agents/specialist-agent.js} +48 -49
- package/lib/cjs/agents/specialist-agent.js.map +1 -0
- package/lib/cjs/bot-config-tools.js +3 -4
- package/lib/cjs/bot-config-tools.js.map +1 -1
- package/lib/cjs/debug-logger.d.ts +1 -1
- package/lib/cjs/debug-logger.js +4 -1
- package/lib/cjs/debug-logger.js.map +1 -1
- package/lib/cjs/guardrails/input.d.ts +1 -1
- package/lib/cjs/guardrails/input.js +20 -9
- package/lib/cjs/guardrails/input.js.map +1 -1
- package/lib/cjs/index.d.ts +4 -1
- package/lib/cjs/index.js +112 -51
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/llm-config.d.ts +2 -1
- package/lib/cjs/llm-config.js +3 -0
- package/lib/cjs/llm-config.js.map +1 -1
- package/lib/cjs/runners/base-runner.d.ts +26 -0
- package/lib/cjs/runners/base-runner.js +114 -0
- package/lib/cjs/runners/base-runner.js.map +1 -0
- package/lib/cjs/runners/index.d.ts +2 -0
- package/lib/cjs/runners/index.js +8 -0
- package/lib/cjs/runners/index.js.map +1 -0
- package/lib/cjs/runners/router-runner.d.ts +6 -0
- package/lib/cjs/runners/router-runner.js +29 -0
- package/lib/cjs/runners/router-runner.js.map +1 -0
- package/lib/cjs/runners/specialist-runner.d.ts +10 -0
- package/lib/cjs/runners/specialist-runner.js +78 -0
- package/lib/cjs/runners/specialist-runner.js.map +1 -0
- package/lib/cjs/structured-output/bot-executor.d.ts +6 -42
- package/lib/cjs/structured-output/bot-executor.js +9 -9
- package/lib/cjs/structured-output/bot-executor.js.map +1 -1
- package/lib/cjs/structured-output/carousel.d.ts +6 -78
- package/lib/cjs/structured-output/carousel.js +2 -1
- package/lib/cjs/structured-output/carousel.js.map +1 -1
- package/lib/cjs/structured-output/exit.d.ts +4 -8
- package/lib/cjs/structured-output/exit.js +4 -4
- package/lib/cjs/structured-output/exit.js.map +1 -1
- package/lib/cjs/structured-output/index.d.ts +48 -553
- package/lib/cjs/structured-output/index.js +15 -0
- package/lib/cjs/structured-output/index.js.map +1 -1
- package/lib/cjs/structured-output/text-with-buttons.d.ts +10 -49
- package/lib/cjs/structured-output/text-with-buttons.js +10 -10
- package/lib/cjs/structured-output/text-with-buttons.js.map +1 -1
- package/lib/cjs/structured-output/text.d.ts +4 -18
- package/lib/cjs/structured-output/text.js +2 -1
- package/lib/cjs/structured-output/text.js.map +1 -1
- package/lib/cjs/tools/index.d.ts +1 -1
- package/lib/cjs/tools/index.js +3 -2
- package/lib/cjs/tools/index.js.map +1 -1
- package/lib/cjs/tools/retrieve-knowledge.d.ts +3 -6
- package/lib/cjs/tools/retrieve-knowledge.js +7 -5
- package/lib/cjs/tools/retrieve-knowledge.js.map +1 -1
- package/lib/cjs/types.d.ts +2 -3
- package/lib/esm/agents/base-agent.d.ts +28 -0
- package/lib/esm/agents/base-agent.js +39 -0
- package/lib/esm/agents/base-agent.js.map +1 -0
- package/lib/esm/agents/index.d.ts +2 -0
- package/lib/esm/agents/index.js +8 -0
- package/lib/esm/agents/index.js.map +1 -0
- package/lib/esm/agents/router-agent.d.ts +25 -0
- package/lib/esm/agents/router-agent.js +33 -0
- package/lib/esm/agents/router-agent.js.map +1 -0
- package/lib/esm/agents/specialist-agent.d.ts +35 -0
- package/lib/esm/{agent-builder.js → agents/specialist-agent.js} +48 -49
- package/lib/esm/agents/specialist-agent.js.map +1 -0
- package/lib/esm/bot-config-tools.js +3 -4
- package/lib/esm/bot-config-tools.js.map +1 -1
- package/lib/esm/debug-logger.d.ts +1 -1
- package/lib/esm/debug-logger.js +4 -1
- package/lib/esm/debug-logger.js.map +1 -1
- package/lib/esm/guardrails/input.d.ts +1 -1
- package/lib/esm/guardrails/input.js +20 -9
- package/lib/esm/guardrails/input.js.map +1 -1
- package/lib/esm/index.d.ts +4 -1
- package/lib/esm/index.js +112 -51
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/llm-config.d.ts +2 -1
- package/lib/esm/llm-config.js +3 -0
- package/lib/esm/llm-config.js.map +1 -1
- package/lib/esm/runners/base-runner.d.ts +26 -0
- package/lib/esm/runners/base-runner.js +114 -0
- package/lib/esm/runners/base-runner.js.map +1 -0
- package/lib/esm/runners/index.d.ts +2 -0
- package/lib/esm/runners/index.js +8 -0
- package/lib/esm/runners/index.js.map +1 -0
- package/lib/esm/runners/router-runner.d.ts +6 -0
- package/lib/esm/runners/router-runner.js +29 -0
- package/lib/esm/runners/router-runner.js.map +1 -0
- package/lib/esm/runners/specialist-runner.d.ts +10 -0
- package/lib/esm/runners/specialist-runner.js +78 -0
- package/lib/esm/runners/specialist-runner.js.map +1 -0
- package/lib/esm/structured-output/bot-executor.d.ts +6 -42
- package/lib/esm/structured-output/bot-executor.js +9 -9
- package/lib/esm/structured-output/bot-executor.js.map +1 -1
- package/lib/esm/structured-output/carousel.d.ts +6 -78
- package/lib/esm/structured-output/carousel.js +2 -1
- package/lib/esm/structured-output/carousel.js.map +1 -1
- package/lib/esm/structured-output/exit.d.ts +4 -8
- package/lib/esm/structured-output/exit.js +4 -4
- package/lib/esm/structured-output/exit.js.map +1 -1
- package/lib/esm/structured-output/index.d.ts +48 -553
- package/lib/esm/structured-output/index.js +15 -0
- package/lib/esm/structured-output/index.js.map +1 -1
- package/lib/esm/structured-output/text-with-buttons.d.ts +10 -49
- package/lib/esm/structured-output/text-with-buttons.js +10 -10
- package/lib/esm/structured-output/text-with-buttons.js.map +1 -1
- package/lib/esm/structured-output/text.d.ts +4 -18
- package/lib/esm/structured-output/text.js +2 -1
- package/lib/esm/structured-output/text.js.map +1 -1
- package/lib/esm/tools/index.d.ts +1 -1
- package/lib/esm/tools/index.js +3 -2
- package/lib/esm/tools/index.js.map +1 -1
- package/lib/esm/tools/retrieve-knowledge.d.ts +3 -6
- package/lib/esm/tools/retrieve-knowledge.js +7 -5
- package/lib/esm/tools/retrieve-knowledge.js.map +1 -1
- package/lib/esm/types.d.ts +2 -3
- package/package.json +5 -6
- package/src/agents/base-agent.ts +75 -0
- package/src/agents/index.ts +2 -0
- package/src/agents/router-agent.ts +71 -0
- package/src/{agent-builder.ts → agents/specialist-agent.ts} +77 -77
- package/src/bot-config-tools.ts +3 -4
- package/src/debug-logger.ts +10 -4
- package/src/guardrails/input.ts +26 -9
- package/src/index.ts +216 -82
- package/src/llm-config.ts +5 -0
- package/src/runners/base-runner.ts +190 -0
- package/src/runners/index.ts +2 -0
- package/src/runners/router-runner.ts +41 -0
- package/src/runners/specialist-runner.ts +112 -0
- package/src/structured-output/bot-executor.ts +3 -3
- package/src/structured-output/carousel.ts +2 -2
- package/src/structured-output/exit.ts +3 -3
- package/src/structured-output/index.ts +15 -0
- package/src/structured-output/text-with-buttons.ts +3 -3
- package/src/structured-output/text.ts +2 -2
- package/src/tools/index.ts +4 -1
- package/src/tools/retrieve-knowledge.ts +32 -29
- package/src/types.ts +2 -3
- package/lib/cjs/agent-builder.d.ts +0 -37
- package/lib/cjs/agent-builder.js.map +0 -1
- package/lib/cjs/runner.d.ts +0 -18
- package/lib/cjs/runner.js +0 -180
- package/lib/cjs/runner.js.map +0 -1
- package/lib/esm/agent-builder.d.ts +0 -37
- package/lib/esm/agent-builder.js.map +0 -1
- package/lib/esm/runner.d.ts +0 -18
- package/lib/esm/runner.js +0 -180
- package/lib/esm/runner.js.map +0 -1
- package/src/runner.ts +0 -283
package/src/bot-config-tools.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ToolConfigJSON } from '@botonic/core'
|
|
2
|
-
import {
|
|
2
|
+
import { z } from 'zod'
|
|
3
3
|
|
|
4
4
|
import type { CustomTool } from './types'
|
|
5
5
|
|
|
@@ -13,9 +13,8 @@ export function getToolsForBotConfig(
|
|
|
13
13
|
return customTools.map(tool => ({
|
|
14
14
|
name: tool.name,
|
|
15
15
|
description: tool.description,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
$refStrategy: 'none',
|
|
16
|
+
schema: z.toJSONSchema(tool.schema, {
|
|
17
|
+
target: 'draft-07',
|
|
19
18
|
}) as ToolConfigJSON['schema'],
|
|
20
19
|
}))
|
|
21
20
|
}
|
package/src/debug-logger.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
type AiAgentArgs,
|
|
3
|
+
AiAgentType,
|
|
4
|
+
type ToolExecution,
|
|
5
|
+
} from '@botonic/core'
|
|
2
6
|
import type { ModelSettings } from '@openai/agents'
|
|
3
7
|
import { OPENAI_PROVIDER } from './constants'
|
|
4
8
|
import type { AgenticInputMessage, MemoryOptions, RunResult } from './types'
|
|
@@ -66,9 +70,11 @@ class EnabledDebugLogger implements DebugLogger {
|
|
|
66
70
|
console.log(`${PREFIX} === AI Agent Debug Info ===`)
|
|
67
71
|
console.log(`${PREFIX} Agent Name: ${aiAgentArgs.name}`)
|
|
68
72
|
console.log(`${PREFIX} Active Tools: ${JSON.stringify(toolNames)}`)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
if (aiAgentArgs.type === AiAgentType.Specialist) {
|
|
74
|
+
console.log(
|
|
75
|
+
`${PREFIX} Source IDs: ${JSON.stringify(aiAgentArgs.sourceIds || [])}`
|
|
76
|
+
)
|
|
77
|
+
}
|
|
72
78
|
console.log(`${PREFIX} Message History Count: ${messages.length}`)
|
|
73
79
|
console.log(
|
|
74
80
|
`${PREFIX} Input Guardrail Rules: ${aiAgentArgs.inputGuardrailRules?.length || 0}`
|
package/src/guardrails/input.ts
CHANGED
|
@@ -19,20 +19,34 @@ export interface GuardrailTrackingContext {
|
|
|
19
19
|
inferenceId: string
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export function
|
|
22
|
+
export async function createInputGuardrails(
|
|
23
23
|
rules: GuardrailRule[],
|
|
24
24
|
llmConfig: LLMConfig,
|
|
25
25
|
trackingContext: GuardrailTrackingContext
|
|
26
|
-
): InputGuardrail {
|
|
26
|
+
): Promise<InputGuardrail[]> {
|
|
27
|
+
if (rules.length === 0) {
|
|
28
|
+
return []
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return [await buildInputGuardrail(rules, llmConfig, trackingContext)]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async function buildInputGuardrail(
|
|
35
|
+
rules: GuardrailRule[],
|
|
36
|
+
llmConfig: LLMConfig,
|
|
37
|
+
trackingContext: GuardrailTrackingContext
|
|
38
|
+
): Promise<InputGuardrail> {
|
|
27
39
|
const outputType = z.object(
|
|
28
40
|
Object.fromEntries(
|
|
29
41
|
rules.map(rule => [rule.name, z.boolean().describe(rule.description)])
|
|
30
42
|
)
|
|
31
43
|
)
|
|
44
|
+
const modelSettings = createInputGuardrailModelSettings(llmConfig)
|
|
32
45
|
|
|
33
46
|
const agent = new Agent({
|
|
34
47
|
name: 'InputGuardrail',
|
|
35
|
-
model: llmConfig.
|
|
48
|
+
model: await llmConfig.getModel(),
|
|
49
|
+
modelSettings,
|
|
36
50
|
instructions:
|
|
37
51
|
'Check if the user triggers some of the following guardrails.',
|
|
38
52
|
outputType,
|
|
@@ -42,11 +56,7 @@ export function createInputGuardrail(
|
|
|
42
56
|
name: 'InputGuardrail',
|
|
43
57
|
execute: async ({ input, context }) => {
|
|
44
58
|
const lastMessage = input[input.length - 1] as UserMessageItem
|
|
45
|
-
const modelProvider = llmConfig.modelProvider
|
|
46
|
-
const modelSettings = createInputGuardrailModelSettings(llmConfig)
|
|
47
59
|
const runner = new Runner({
|
|
48
|
-
modelSettings,
|
|
49
|
-
modelProvider,
|
|
50
60
|
tracingDisabled: true,
|
|
51
61
|
})
|
|
52
62
|
const startTime = Date.now()
|
|
@@ -80,10 +90,17 @@ export function createInputGuardrail(
|
|
|
80
90
|
function createInputGuardrailModelSettings(
|
|
81
91
|
llmConfig: LLMConfig
|
|
82
92
|
): ModelSettings {
|
|
83
|
-
|
|
93
|
+
const modelSettings: ModelSettings = {
|
|
84
94
|
...llmConfig.modelSettings,
|
|
85
95
|
toolChoice: undefined,
|
|
86
96
|
}
|
|
97
|
+
if (llmConfig.modelSettings.reasoning) {
|
|
98
|
+
modelSettings.reasoning = { ...llmConfig.modelSettings.reasoning }
|
|
99
|
+
}
|
|
100
|
+
if (llmConfig.modelSettings.text) {
|
|
101
|
+
modelSettings.text = { ...llmConfig.modelSettings.text }
|
|
102
|
+
}
|
|
103
|
+
return modelSettings
|
|
87
104
|
}
|
|
88
105
|
|
|
89
106
|
async function sendGuardrailLlmRunTracking(
|
|
@@ -114,7 +131,7 @@ async function sendGuardrailLlmRunTracking(
|
|
|
114
131
|
product_name: TrackProductName.AI_AGENT,
|
|
115
132
|
deployment_name: llmConfig.modelName,
|
|
116
133
|
model_name:
|
|
117
|
-
(response.providerData?.
|
|
134
|
+
(response.providerData?.model as string | undefined) ??
|
|
118
135
|
llmConfig.modelName,
|
|
119
136
|
feature: TrackFeature.AI_AGENT_GUARDRAIL,
|
|
120
137
|
api_version: apiVersion,
|
package/src/index.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import {
|
|
2
|
+
type AIAgentRouterArgs,
|
|
3
|
+
type AiAgentArgs,
|
|
4
|
+
type AiAgentSpecialistArgs,
|
|
5
|
+
AiAgentType,
|
|
6
|
+
type BotContext,
|
|
7
|
+
type HubtypeAssistantMessage,
|
|
8
|
+
type Plugin,
|
|
9
|
+
type ResolvedPlugins,
|
|
7
10
|
} from '@botonic/core'
|
|
8
|
-
import { setTracingDisabled, tool } from '@openai/agents'
|
|
11
|
+
import { handoff, setTracingDisabled, tool } from '@openai/agents'
|
|
9
12
|
import { v7 as uuidv7 } from 'uuid'
|
|
10
|
-
import {
|
|
13
|
+
import type { ZodObject } from 'zod'
|
|
14
|
+
import { RouterAgent, SpecialistAgent } from './agents'
|
|
11
15
|
import {
|
|
12
16
|
DEFAULT_MAX_RETRIES,
|
|
13
17
|
DEFAULT_TIMEOUT_16_SECONDS,
|
|
@@ -16,7 +20,7 @@ import {
|
|
|
16
20
|
} from './constants'
|
|
17
21
|
import { createDebugLogger, type DebugLogger } from './debug-logger'
|
|
18
22
|
import { LLMConfig } from './llm-config'
|
|
19
|
-
import {
|
|
23
|
+
import { RouterRunner, WorkerRunner } from './runners'
|
|
20
24
|
import { HubtypeApiClient } from './services/hubtype-api-client'
|
|
21
25
|
import type {
|
|
22
26
|
AgenticInputMessage,
|
|
@@ -77,84 +81,33 @@ export default class BotonicPluginAiAgents<
|
|
|
77
81
|
botContext: BotContext<TPlugins, TExtraData>,
|
|
78
82
|
aiAgentArgs: AiAgentArgs
|
|
79
83
|
): Promise<InferenceResponse> {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (!authToken) {
|
|
85
|
-
throw new Error('Auth token is required')
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const inferenceId = uuidv7()
|
|
89
|
-
|
|
90
|
-
// Create client for OpenAI/Azure OpenAI
|
|
91
|
-
const llmConfig = new LLMConfig(
|
|
92
|
-
this.maxRetries,
|
|
93
|
-
this.timeout,
|
|
94
|
-
aiAgentArgs.model,
|
|
95
|
-
aiAgentArgs.verbosity
|
|
96
|
-
)
|
|
84
|
+
const authToken = isProd ? botContext.session._access_token : this.authToken
|
|
85
|
+
if (!authToken) {
|
|
86
|
+
throw new Error('Auth token is required')
|
|
87
|
+
}
|
|
97
88
|
|
|
98
|
-
|
|
99
|
-
const tools = this.buildTools(
|
|
100
|
-
aiAgentArgs.activeTools?.map(tool => tool.name) || []
|
|
101
|
-
)
|
|
89
|
+
const inferenceId = uuidv7()
|
|
102
90
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
contactInfo: botContext.session.user.contact_info || [],
|
|
109
|
-
inputGuardrailRules: aiAgentArgs.inputGuardrailRules || [],
|
|
110
|
-
sourceIds: aiAgentArgs.sourceIds || [],
|
|
111
|
-
outputMessagesSchemas: aiAgentArgs.outputMessagesSchemas || [],
|
|
112
|
-
campaignsContext: botContext.input.context?.campaigns_v2,
|
|
113
|
-
logger: this.logger,
|
|
114
|
-
llmConfig,
|
|
115
|
-
guardrailTrackingContext: {
|
|
116
|
-
botId: botContext.session.bot.id,
|
|
117
|
-
isTest: botContext.session.is_test_integration,
|
|
91
|
+
try {
|
|
92
|
+
if (aiAgentArgs.type === AiAgentType.Specialist) {
|
|
93
|
+
return await this.executeWorkerAIAgent(
|
|
94
|
+
botContext,
|
|
95
|
+
aiAgentArgs,
|
|
118
96
|
authToken,
|
|
119
|
-
inferenceId
|
|
120
|
-
|
|
121
|
-
}).build()
|
|
122
|
-
|
|
123
|
-
// Get messages
|
|
124
|
-
const messages = await this.getMessages(
|
|
125
|
-
botContext,
|
|
126
|
-
authToken,
|
|
127
|
-
aiAgentArgs.previousHubtypeMessages || []
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
// Build context
|
|
131
|
-
const context: Context<TPlugins, TExtraData> = {
|
|
132
|
-
authToken,
|
|
133
|
-
sourceIds: aiAgentArgs.sourceIds || [],
|
|
134
|
-
knowledgeUsed: {
|
|
135
|
-
query: '',
|
|
136
|
-
sourceIds: [],
|
|
137
|
-
chunksIds: [],
|
|
138
|
-
chunkTexts: [],
|
|
139
|
-
},
|
|
140
|
-
request: botContext,
|
|
97
|
+
inferenceId
|
|
98
|
+
)
|
|
141
99
|
}
|
|
142
100
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
101
|
+
if (aiAgentArgs.type === AiAgentType.Router) {
|
|
102
|
+
return await this.executeRouterAIAgent(
|
|
103
|
+
botContext,
|
|
104
|
+
aiAgentArgs,
|
|
105
|
+
authToken,
|
|
106
|
+
inferenceId
|
|
107
|
+
)
|
|
108
|
+
}
|
|
149
109
|
|
|
150
|
-
|
|
151
|
-
const runner = new AIAgentRunner<TPlugins, TExtraData>(
|
|
152
|
-
agent,
|
|
153
|
-
llmConfig,
|
|
154
|
-
inferenceId,
|
|
155
|
-
this.logger
|
|
156
|
-
)
|
|
157
|
-
return await runner.run(messages, context)
|
|
110
|
+
throw new Error('Invalid agent type')
|
|
158
111
|
} catch (error) {
|
|
159
112
|
console.error('error plugin returns undefined', error)
|
|
160
113
|
return {
|
|
@@ -165,10 +118,188 @@ export default class BotonicPluginAiAgents<
|
|
|
165
118
|
error: true,
|
|
166
119
|
inputGuardrailsTriggered: [],
|
|
167
120
|
outputGuardrailsTriggered: [],
|
|
121
|
+
startingAgentName: '',
|
|
122
|
+
lastAgentName: '',
|
|
123
|
+
availableSpecialists: [],
|
|
124
|
+
isTransferredToSpecialist: false,
|
|
168
125
|
}
|
|
169
126
|
}
|
|
170
127
|
}
|
|
171
128
|
|
|
129
|
+
private async executeWorkerAIAgent(
|
|
130
|
+
botContext: BotContext<TPlugins, TExtraData>,
|
|
131
|
+
aiAgentArgs: AiAgentSpecialistArgs,
|
|
132
|
+
authToken: string,
|
|
133
|
+
inferenceId: string
|
|
134
|
+
) {
|
|
135
|
+
const llmConfig = new LLMConfig(
|
|
136
|
+
this.maxRetries,
|
|
137
|
+
this.timeout,
|
|
138
|
+
aiAgentArgs.model,
|
|
139
|
+
aiAgentArgs.verbosity
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
// Get LLM config, tools and agent
|
|
143
|
+
const { tools, agent } = await this.getAIAgentWorkerAndTools(
|
|
144
|
+
botContext,
|
|
145
|
+
aiAgentArgs,
|
|
146
|
+
aiAgentArgs.outputMessagesSchemas || [],
|
|
147
|
+
authToken,
|
|
148
|
+
inferenceId,
|
|
149
|
+
llmConfig
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
// Get messages
|
|
153
|
+
const messages = await this.getMessages(
|
|
154
|
+
botContext,
|
|
155
|
+
authToken,
|
|
156
|
+
aiAgentArgs.previousHubtypeMessages || []
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
// Build context
|
|
160
|
+
const context: Context<TPlugins, TExtraData> = {
|
|
161
|
+
authToken,
|
|
162
|
+
knowledgeUsed: {
|
|
163
|
+
query: '',
|
|
164
|
+
sourceIds: [],
|
|
165
|
+
chunksIds: [],
|
|
166
|
+
chunkTexts: [],
|
|
167
|
+
},
|
|
168
|
+
request: botContext,
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Log agent debug info
|
|
172
|
+
this.logger.logAgentDebugInfo(
|
|
173
|
+
aiAgentArgs,
|
|
174
|
+
tools.map(t => t.name),
|
|
175
|
+
messages
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
// Run agent
|
|
179
|
+
const runner = new WorkerRunner<TPlugins, TExtraData>(
|
|
180
|
+
agent,
|
|
181
|
+
llmConfig,
|
|
182
|
+
inferenceId,
|
|
183
|
+
this.logger
|
|
184
|
+
)
|
|
185
|
+
return await runner.run(messages, context)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private async executeRouterAIAgent(
|
|
189
|
+
botContext: BotContext<TPlugins, TExtraData>,
|
|
190
|
+
aiAgentArgs: AIAgentRouterArgs,
|
|
191
|
+
authToken: string,
|
|
192
|
+
inferenceId: string
|
|
193
|
+
) {
|
|
194
|
+
const { specialists, name, instructions } = aiAgentArgs
|
|
195
|
+
|
|
196
|
+
const llmConfig = new LLMConfig(
|
|
197
|
+
this.maxRetries,
|
|
198
|
+
this.timeout,
|
|
199
|
+
aiAgentArgs.model,
|
|
200
|
+
aiAgentArgs.verbosity
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
const specialistsAgents = await Promise.all(
|
|
204
|
+
specialists.map(async specialistData => {
|
|
205
|
+
const { agent } = await this.getAIAgentWorkerAndTools(
|
|
206
|
+
botContext,
|
|
207
|
+
specialistData,
|
|
208
|
+
aiAgentArgs.outputMessagesSchemas || [],
|
|
209
|
+
authToken,
|
|
210
|
+
inferenceId,
|
|
211
|
+
llmConfig
|
|
212
|
+
)
|
|
213
|
+
return handoff(agent, {
|
|
214
|
+
toolNameOverride: specialistData.name,
|
|
215
|
+
toolDescriptionOverride: specialistData.description,
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
const router = await RouterAgent.create<TPlugins, TExtraData>({
|
|
221
|
+
name,
|
|
222
|
+
instructions,
|
|
223
|
+
llmConfig,
|
|
224
|
+
handoffs: specialistsAgents,
|
|
225
|
+
inputGuardrailRules: aiAgentArgs.inputGuardrailRules || [],
|
|
226
|
+
outputMessagesSchemas: aiAgentArgs.outputMessagesSchemas || [],
|
|
227
|
+
guardrailTrackingContext: {
|
|
228
|
+
botId: botContext.session.bot.id,
|
|
229
|
+
isTest: botContext.session.is_test_integration,
|
|
230
|
+
authToken,
|
|
231
|
+
inferenceId,
|
|
232
|
+
},
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// Get messages
|
|
236
|
+
const messages = await this.getMessages(
|
|
237
|
+
botContext,
|
|
238
|
+
authToken,
|
|
239
|
+
aiAgentArgs.previousHubtypeMessages || []
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
// Build context
|
|
243
|
+
const context: Context<TPlugins, TExtraData> = {
|
|
244
|
+
authToken,
|
|
245
|
+
knowledgeUsed: {
|
|
246
|
+
query: '',
|
|
247
|
+
sourceIds: [],
|
|
248
|
+
chunksIds: [],
|
|
249
|
+
chunkTexts: [],
|
|
250
|
+
},
|
|
251
|
+
request: botContext,
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Run agent
|
|
255
|
+
const routerAgent = router.getAgent()
|
|
256
|
+
const runner = new RouterRunner<TPlugins, TExtraData>(
|
|
257
|
+
routerAgent,
|
|
258
|
+
llmConfig,
|
|
259
|
+
inferenceId,
|
|
260
|
+
this.logger
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
return await runner.run(messages, context)
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private async getAIAgentWorkerAndTools(
|
|
267
|
+
botContext: BotContext,
|
|
268
|
+
aiAgentArgs: AiAgentArgs,
|
|
269
|
+
outputMessagesSchemas: ZodObject<any>[],
|
|
270
|
+
authToken: string,
|
|
271
|
+
inferenceId: string,
|
|
272
|
+
llmConfig: LLMConfig
|
|
273
|
+
) {
|
|
274
|
+
// Build tools
|
|
275
|
+
const tools = this.buildTools(aiAgentArgs)
|
|
276
|
+
|
|
277
|
+
// Build agent
|
|
278
|
+
const sourceIds =
|
|
279
|
+
aiAgentArgs.type === AiAgentType.Specialist ? aiAgentArgs.sourceIds : []
|
|
280
|
+
const worker = await SpecialistAgent.create<TPlugins, TExtraData>({
|
|
281
|
+
name: aiAgentArgs.name,
|
|
282
|
+
instructions: aiAgentArgs.instructions,
|
|
283
|
+
tools: tools,
|
|
284
|
+
contactInfo: botContext.session.user.contact_info || [],
|
|
285
|
+
inputGuardrailRules: aiAgentArgs.inputGuardrailRules || [],
|
|
286
|
+
sourceIds,
|
|
287
|
+
outputMessagesSchemas: outputMessagesSchemas || [],
|
|
288
|
+
campaignsContext: botContext.input.context?.campaigns_v2,
|
|
289
|
+
logger: this.logger,
|
|
290
|
+
llmConfig,
|
|
291
|
+
guardrailTrackingContext: {
|
|
292
|
+
botId: botContext.session.bot.id,
|
|
293
|
+
isTest: botContext.session.is_test_integration,
|
|
294
|
+
authToken,
|
|
295
|
+
inferenceId,
|
|
296
|
+
},
|
|
297
|
+
})
|
|
298
|
+
const workerAgent = worker.getAgent()
|
|
299
|
+
|
|
300
|
+
return { agent: workerAgent, tools }
|
|
301
|
+
}
|
|
302
|
+
|
|
172
303
|
private async getMessages(
|
|
173
304
|
botContext: BotContext,
|
|
174
305
|
authToken: string,
|
|
@@ -192,7 +323,10 @@ export default class BotonicPluginAiAgents<
|
|
|
192
323
|
return result.messages
|
|
193
324
|
}
|
|
194
325
|
|
|
195
|
-
private buildTools(
|
|
326
|
+
private buildTools(aiAgentArgs: AiAgentArgs): Tool<TPlugins, TExtraData>[] {
|
|
327
|
+
const activeTools =
|
|
328
|
+
aiAgentArgs.type === AiAgentType.Router ? [] : aiAgentArgs.activeTools
|
|
329
|
+
const activeToolNames = activeTools.map(tool => tool.name)
|
|
196
330
|
const availableTools = this.toolDefinitions.filter(tool =>
|
|
197
331
|
activeToolNames.includes(tool.name)
|
|
198
332
|
)
|
package/src/llm-config.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { VerbosityLevel } from '@botonic/core'
|
|
2
2
|
import {
|
|
3
|
+
type Model,
|
|
3
4
|
type ModelProvider,
|
|
4
5
|
type ModelSettings,
|
|
5
6
|
OpenAIProvider,
|
|
@@ -35,6 +36,10 @@ export class LLMConfig {
|
|
|
35
36
|
this.modelSettings = this.getModelSettings(modelName, verbosity)
|
|
36
37
|
}
|
|
37
38
|
|
|
39
|
+
async getModel(): Promise<Model> {
|
|
40
|
+
return await this.modelProvider.getModel(this.modelName)
|
|
41
|
+
}
|
|
42
|
+
|
|
38
43
|
private getModelProvider(): ModelProvider {
|
|
39
44
|
const client = this.getClient()
|
|
40
45
|
return new OpenAIProvider({
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
OutputMessage,
|
|
3
|
+
ResolvedPlugins,
|
|
4
|
+
ToolExecution,
|
|
5
|
+
} from '@botonic/core'
|
|
6
|
+
import { InputGuardrailTripwireTriggered, Runner } from '@openai/agents'
|
|
7
|
+
|
|
8
|
+
import { isProd } from '../constants'
|
|
9
|
+
import type { DebugLogger } from '../debug-logger'
|
|
10
|
+
import { getApiVersion, type LLMConfig } from '../llm-config'
|
|
11
|
+
import { HubtypeApiClient } from '../services/hubtype-api-client'
|
|
12
|
+
import { TrackFeature, TrackProductName } from '../services/types'
|
|
13
|
+
import type {
|
|
14
|
+
AgenticInputMessage,
|
|
15
|
+
AgenticOutputMessage,
|
|
16
|
+
AIAgent,
|
|
17
|
+
Context,
|
|
18
|
+
ResultRawResponse,
|
|
19
|
+
RunResult,
|
|
20
|
+
} from '../types'
|
|
21
|
+
|
|
22
|
+
// Minimal interface matching the properties we actually use from Runner.run() result.
|
|
23
|
+
// This bypasses strict type checking while maintaining type safety for accessed properties.
|
|
24
|
+
export interface RunnerResult {
|
|
25
|
+
finalOutput?: {
|
|
26
|
+
messages?: OutputMessage[]
|
|
27
|
+
}
|
|
28
|
+
rawResponses?: ResultRawResponse[]
|
|
29
|
+
newItems?: unknown[]
|
|
30
|
+
lastAgent?: { name?: string }
|
|
31
|
+
// biome-ignore lint/suspicious/noExplicitAny: state is a complex internal type
|
|
32
|
+
state?: any
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export abstract class BaseRunner<
|
|
36
|
+
TPlugins extends ResolvedPlugins = ResolvedPlugins,
|
|
37
|
+
TExtraData = unknown,
|
|
38
|
+
> {
|
|
39
|
+
protected agent: AIAgent<TPlugins, TExtraData>
|
|
40
|
+
protected llmConfig: LLMConfig
|
|
41
|
+
protected inferenceId: string
|
|
42
|
+
protected logger: DebugLogger
|
|
43
|
+
|
|
44
|
+
constructor(
|
|
45
|
+
agent: AIAgent<TPlugins, TExtraData>,
|
|
46
|
+
llmConfig: LLMConfig,
|
|
47
|
+
inferenceId: string,
|
|
48
|
+
logger: DebugLogger
|
|
49
|
+
) {
|
|
50
|
+
this.agent = agent
|
|
51
|
+
this.llmConfig = llmConfig
|
|
52
|
+
this.inferenceId = inferenceId
|
|
53
|
+
this.logger = logger
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async run(
|
|
57
|
+
messages: AgenticInputMessage[],
|
|
58
|
+
context: Context<TPlugins, TExtraData>
|
|
59
|
+
): Promise<RunResult> {
|
|
60
|
+
const startTime = Date.now()
|
|
61
|
+
|
|
62
|
+
this.logger.logRunnerStart(
|
|
63
|
+
this.llmConfig.modelName,
|
|
64
|
+
this.llmConfig.modelSettings
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
const runner = new Runner({
|
|
69
|
+
tracingDisabled: true,
|
|
70
|
+
})
|
|
71
|
+
const result = (await runner.run(this.agent, messages, {
|
|
72
|
+
context,
|
|
73
|
+
})) as RunnerResult
|
|
74
|
+
const endTime = Date.now()
|
|
75
|
+
|
|
76
|
+
await this.sendLlmRunTracking(result, context, startTime, endTime)
|
|
77
|
+
|
|
78
|
+
const runResult = this.buildRunResult(result, context, messages.length)
|
|
79
|
+
|
|
80
|
+
this.logger.logRunResult(runResult, startTime)
|
|
81
|
+
|
|
82
|
+
return runResult
|
|
83
|
+
} catch (error) {
|
|
84
|
+
if (error instanceof InputGuardrailTripwireTriggered) {
|
|
85
|
+
const runResult: RunResult = {
|
|
86
|
+
startingAgentName: '',
|
|
87
|
+
lastAgentName: '',
|
|
88
|
+
availableSpecialists: [],
|
|
89
|
+
isTransferredToSpecialist: false,
|
|
90
|
+
messages: [],
|
|
91
|
+
memoryLength: 0,
|
|
92
|
+
toolsExecuted: [],
|
|
93
|
+
exit: true,
|
|
94
|
+
error: false,
|
|
95
|
+
inputGuardrailsTriggered: error.result.output.outputInfo,
|
|
96
|
+
outputGuardrailsTriggered: [],
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
this.logger.logGuardrailTriggered()
|
|
100
|
+
this.logger.logRunResult(runResult, startTime)
|
|
101
|
+
|
|
102
|
+
return runResult
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.logger.logRunnerError(startTime, error)
|
|
106
|
+
|
|
107
|
+
throw error
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
protected getToolsExecuted(
|
|
112
|
+
_result: RunnerResult,
|
|
113
|
+
_context: Context<TPlugins, TExtraData>
|
|
114
|
+
): ToolExecution[] {
|
|
115
|
+
return []
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
protected buildRunResult(
|
|
119
|
+
result: RunnerResult,
|
|
120
|
+
context: Context<TPlugins, TExtraData>,
|
|
121
|
+
memoryLength: number
|
|
122
|
+
): RunResult {
|
|
123
|
+
const outputMessages = result.finalOutput?.messages || []
|
|
124
|
+
const hasExit =
|
|
125
|
+
outputMessages.length === 0 ||
|
|
126
|
+
outputMessages.some(message => message.type === 'exit')
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
startingAgentName: '',
|
|
130
|
+
lastAgentName: '',
|
|
131
|
+
availableSpecialists: [],
|
|
132
|
+
isTransferredToSpecialist: false,
|
|
133
|
+
messages: hasExit
|
|
134
|
+
? []
|
|
135
|
+
: (outputMessages.filter(
|
|
136
|
+
message => message.type !== 'exit'
|
|
137
|
+
) as AgenticOutputMessage[]),
|
|
138
|
+
toolsExecuted: this.getToolsExecuted(result, context),
|
|
139
|
+
exit: hasExit,
|
|
140
|
+
memoryLength,
|
|
141
|
+
error: false,
|
|
142
|
+
inputGuardrailsTriggered: [],
|
|
143
|
+
outputGuardrailsTriggered: [],
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
private async sendLlmRunTracking(
|
|
148
|
+
result: RunnerResult,
|
|
149
|
+
context: Context<TPlugins, TExtraData>,
|
|
150
|
+
startTime: number,
|
|
151
|
+
endTime: number
|
|
152
|
+
): Promise<void> {
|
|
153
|
+
if (!isProd) {
|
|
154
|
+
return
|
|
155
|
+
}
|
|
156
|
+
const rawResponses = result.rawResponses ?? []
|
|
157
|
+
if (rawResponses.length === 0) {
|
|
158
|
+
return
|
|
159
|
+
}
|
|
160
|
+
const botId = context.request.session.bot.id
|
|
161
|
+
const isTest = context.request.session.is_test_integration
|
|
162
|
+
const totalDuration = endTime - startTime
|
|
163
|
+
const durationPerCall = Math.round(totalDuration / rawResponses.length)
|
|
164
|
+
const temperature =
|
|
165
|
+
(this.llmConfig.modelSettings.temperature as number | undefined) ?? 0
|
|
166
|
+
const apiVersion = getApiVersion()
|
|
167
|
+
|
|
168
|
+
const llmRuns = rawResponses.map(response => ({
|
|
169
|
+
inference_id: this.inferenceId,
|
|
170
|
+
is_test: isTest,
|
|
171
|
+
product_name: TrackProductName.AI_AGENT,
|
|
172
|
+
deployment_name: this.llmConfig.modelName,
|
|
173
|
+
model_name:
|
|
174
|
+
(response.providerData?.model as string | undefined) ??
|
|
175
|
+
this.llmConfig.modelName,
|
|
176
|
+
feature: TrackFeature.AI_AGENT_RUN,
|
|
177
|
+
api_version: apiVersion,
|
|
178
|
+
num_prompt_tokens: response.usage.inputTokens,
|
|
179
|
+
num_completion_tokens: response.usage.outputTokens,
|
|
180
|
+
duration_in_milliseconds: durationPerCall,
|
|
181
|
+
temperature,
|
|
182
|
+
error: null,
|
|
183
|
+
}))
|
|
184
|
+
|
|
185
|
+
const client = new HubtypeApiClient(context.authToken)
|
|
186
|
+
await client.trackLlmRuns(botId, {
|
|
187
|
+
llm_runs: llmRuns,
|
|
188
|
+
})
|
|
189
|
+
}
|
|
190
|
+
}
|