@botonic/plugin-ai-agents 0.49.0-alpha.3 → 0.49.0-alpha.4

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 (50) hide show
  1. package/lib/cjs/agents/specialist-agent.js +2 -1
  2. package/lib/cjs/agents/specialist-agent.js.map +1 -1
  3. package/lib/cjs/constants.d.ts +10 -12
  4. package/lib/cjs/constants.js +11 -14
  5. package/lib/cjs/constants.js.map +1 -1
  6. package/lib/cjs/debug-logger.js +2 -8
  7. package/lib/cjs/debug-logger.js.map +1 -1
  8. package/lib/cjs/guardrails/input.js.map +1 -1
  9. package/lib/cjs/index.js +14 -2
  10. package/lib/cjs/index.js.map +1 -1
  11. package/lib/cjs/llm-config.d.ts +10 -7
  12. package/lib/cjs/llm-config.js +24 -41
  13. package/lib/cjs/llm-config.js.map +1 -1
  14. package/lib/cjs/runners/base-runner.d.ts +1 -0
  15. package/lib/cjs/runners/base-runner.js +22 -16
  16. package/lib/cjs/runners/base-runner.js.map +1 -1
  17. package/lib/cjs/runners/router-runner.d.ts +2 -0
  18. package/lib/cjs/runners/router-runner.js +23 -8
  19. package/lib/cjs/runners/router-runner.js.map +1 -1
  20. package/lib/cjs/services/types.d.ts +1 -1
  21. package/lib/esm/agents/specialist-agent.js +2 -1
  22. package/lib/esm/agents/specialist-agent.js.map +1 -1
  23. package/lib/esm/constants.d.ts +10 -12
  24. package/lib/esm/constants.js +11 -14
  25. package/lib/esm/constants.js.map +1 -1
  26. package/lib/esm/debug-logger.js +2 -8
  27. package/lib/esm/debug-logger.js.map +1 -1
  28. package/lib/esm/guardrails/input.js.map +1 -1
  29. package/lib/esm/index.js +14 -2
  30. package/lib/esm/index.js.map +1 -1
  31. package/lib/esm/llm-config.d.ts +10 -7
  32. package/lib/esm/llm-config.js +24 -41
  33. package/lib/esm/llm-config.js.map +1 -1
  34. package/lib/esm/runners/base-runner.d.ts +1 -0
  35. package/lib/esm/runners/base-runner.js +22 -16
  36. package/lib/esm/runners/base-runner.js.map +1 -1
  37. package/lib/esm/runners/router-runner.d.ts +2 -0
  38. package/lib/esm/runners/router-runner.js +23 -8
  39. package/lib/esm/runners/router-runner.js.map +1 -1
  40. package/lib/esm/services/types.d.ts +1 -1
  41. package/package.json +2 -2
  42. package/src/agents/specialist-agent.ts +2 -1
  43. package/src/constants.ts +15 -16
  44. package/src/debug-logger.ts +2 -8
  45. package/src/guardrails/input.ts +0 -1
  46. package/src/index.ts +14 -14
  47. package/src/llm-config.ts +45 -56
  48. package/src/runners/base-runner.ts +31 -19
  49. package/src/runners/router-runner.ts +35 -12
  50. package/src/services/types.ts +1 -1
@@ -4,6 +4,7 @@ import {
4
4
  type ToolExecution,
5
5
  } from '@botonic/core'
6
6
  import type { ModelSettings } from '@openai/agents'
7
+ import { LLM_PROVIDER } from './constants'
7
8
  import type { AgenticInputMessage, MemoryOptions, RunResult } from './types'
8
9
 
9
10
  const PREFIX = '[BotonicPluginAiAgents]'
@@ -43,6 +44,7 @@ export interface DebugLogger {
43
44
  class EnabledDebugLogger implements DebugLogger {
44
45
  logInitialConfig(config: DebugLoggerConfig): void {
45
46
  console.log(`${PREFIX} === Plugin Initialization ===`)
47
+ console.log(`${PREFIX} Provider: ${LLM_PROVIDER}`)
46
48
  console.log(
47
49
  `${PREFIX} Message History API Version: ${config.messageHistoryApiVersion}`
48
50
  )
@@ -155,21 +157,13 @@ class EnabledDebugLogger implements DebugLogger {
155
157
  }
156
158
 
157
159
  class DisabledDebugLogger implements DebugLogger {
158
- // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op implementation
159
160
  logInitialConfig(): void {}
160
- // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op implementation
161
161
  logAgentDebugInfo(): void {}
162
- // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op implementation
163
162
  logModelSettings(): void {}
164
- // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op implementation
165
163
  logRunnerStart(_model: string, _modelSettings: ModelSettings): void {}
166
- // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op implementation
167
164
  logRunResult(): void {}
168
- // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op implementation
169
165
  logGuardrailTriggered(): void {}
170
- // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op implementation
171
166
  logRunnerError(): void {}
172
- // biome-ignore lint/suspicious/noEmptyBlockStatements: intentional no-op implementation
173
167
  logToolExecution(): void {}
174
168
  }
175
169
 
@@ -123,7 +123,6 @@ async function sendGuardrailLlmRunTracking(
123
123
  const durationPerCall = Math.round(totalDuration / rawResponses.length)
124
124
  const temperature =
125
125
  (llmConfig.modelSettings.temperature as number | undefined) ?? 0
126
-
127
126
  const llmRuns = rawResponses.map(response => ({
128
127
  inference_id: trackingContext.inferenceId,
129
128
  is_test: trackingContext.isTest,
package/src/index.ts CHANGED
@@ -132,13 +132,13 @@ export default class BotonicPluginAiAgents<
132
132
  authToken: string,
133
133
  inferenceId: string
134
134
  ) {
135
- const llmConfig = new LLMConfig(
136
- this.maxRetries,
137
- this.timeout,
138
- aiAgentArgs.model,
139
- aiAgentArgs.verbosity,
140
- botContext
141
- )
135
+ const llmConfig = new LLMConfig({
136
+ maxRetries: this.maxRetries,
137
+ timeout: this.timeout,
138
+ modelName: aiAgentArgs.model,
139
+ verbosity: aiAgentArgs.verbosity,
140
+ botContext,
141
+ })
142
142
 
143
143
  // Get LLM config, tools and agent
144
144
  const { tools, agent } = await this.getSpecialistAgentAndTools(
@@ -194,13 +194,13 @@ export default class BotonicPluginAiAgents<
194
194
  ) {
195
195
  const { specialists, name, instructions } = aiAgentArgs
196
196
 
197
- const llmConfig = new LLMConfig(
198
- this.maxRetries,
199
- this.timeout,
200
- aiAgentArgs.model,
201
- aiAgentArgs.verbosity,
202
- botContext
203
- )
197
+ const llmConfig = new LLMConfig({
198
+ maxRetries: this.maxRetries,
199
+ timeout: this.timeout,
200
+ modelName: aiAgentArgs.model,
201
+ verbosity: aiAgentArgs.verbosity,
202
+ botContext,
203
+ })
204
204
 
205
205
  const specialistsAgents = await Promise.all(
206
206
  specialists.map(async specialistData => {
package/src/llm-config.ts CHANGED
@@ -7,15 +7,22 @@ import {
7
7
  } from '@openai/agents'
8
8
  import OpenAI, { AzureOpenAI } from 'openai'
9
9
  import {
10
- AZURE_OPENAI_API_BASE,
11
- AZURE_OPENAI_API_KEY,
12
- AZURE_OPENAI_API_VERSION,
13
10
  isProd,
14
- LITELLM_TAG_KEYS,
11
+ LLM_API_KEY,
12
+ LLM_API_URL,
13
+ LLM_AZURE_API_VERSION,
14
+ LLM_OPENAI_MODEL,
15
+ LLM_PROVIDER,
15
16
  LLM_PROVIDERS,
16
17
  } from './constants'
17
18
 
18
- export type LLMProvider = (typeof LLM_PROVIDERS)[keyof typeof LLM_PROVIDERS]
19
+ export interface LLMConfigParams {
20
+ maxRetries: number
21
+ timeout: number
22
+ modelName: string
23
+ verbosity: VerbosityLevel
24
+ botContext: BotContext
25
+ }
19
26
 
20
27
  export class LLMConfig {
21
28
  private readonly maxRetries: number
@@ -25,17 +32,18 @@ export class LLMConfig {
25
32
  public readonly modelSettings: ModelSettings
26
33
  public readonly modelProvider: ModelProvider
27
34
 
28
- constructor(
29
- maxRetries: number,
30
- timeout: number,
31
- modelName: string,
32
- verbosity: VerbosityLevel,
33
- botContext: BotContext
34
- ) {
35
+ constructor({
36
+ maxRetries,
37
+ timeout,
38
+ modelName,
39
+ verbosity,
40
+ botContext,
41
+ }: LLMConfigParams) {
35
42
  this.maxRetries = maxRetries
36
43
  this.timeout = timeout
37
44
  this.botContext = botContext
38
- this.modelName = modelName
45
+ this.modelName =
46
+ LLM_PROVIDER === LLM_PROVIDERS.OPENAI ? LLM_OPENAI_MODEL : modelName
39
47
  this.modelProvider = this.getModelProvider()
40
48
  this.modelSettings = this.getModelSettings(modelName, verbosity)
41
49
  }
@@ -44,19 +52,6 @@ export class LLMConfig {
44
52
  return await this.modelProvider.getModel(this.modelName)
45
53
  }
46
54
 
47
- getProviderName(): LLMProvider {
48
- return this.botContext.settings.LITELLM_API_URL
49
- ? LLM_PROVIDERS.LITELLM
50
- : LLM_PROVIDERS.AZURE
51
- }
52
-
53
- getApiVersion(): string | undefined {
54
- return this.getProviderName() === LLM_PROVIDERS.AZURE
55
- ? this.botContext.settings.AZURE_OPENAI_API_VERSION ||
56
- AZURE_OPENAI_API_VERSION
57
- : undefined
58
- }
59
-
60
55
  private getModelProvider(): ModelProvider {
61
56
  const client = this.getClient()
62
57
  return new OpenAIProvider({
@@ -66,49 +61,30 @@ export class LLMConfig {
66
61
  }
67
62
 
68
63
  private getClient(): OpenAI | AzureOpenAI {
69
- if (this.botContext.settings.LITELLM_API_URL) {
70
- return this.getLiteLLMClient()
64
+ if (LLM_PROVIDER === LLM_PROVIDERS.OPENAI) {
65
+ return this.getOpenAIClient()
71
66
  }
72
67
 
73
68
  return this.getAzureClient()
74
69
  }
75
70
 
76
- private buildLiteLLMTags(): { 'x-litellm-tags': string } | undefined {
77
- const botId = this.botContext.session.bot.id
78
- const orgId = this.botContext.session.organization_id
79
- const parts: string[] = []
80
- if (botId) {
81
- parts.push(`${LITELLM_TAG_KEYS.BOT_ID}:${botId}`)
82
- }
83
- if (orgId) {
84
- parts.push(`${LITELLM_TAG_KEYS.ORG_ID}:${orgId}`)
85
- }
86
- return parts.length > 0
87
- ? { 'x-litellm-tags': parts.join(LITELLM_TAG_KEYS.SEPARATOR) }
88
- : undefined
89
- }
90
-
91
- private getLiteLLMClient(): OpenAI {
71
+ private getOpenAIClient(): OpenAI {
92
72
  return new OpenAI({
93
- apiKey: this.botContext.secrets.LITELLM_API_KEY,
94
- baseURL: this.botContext.settings.LITELLM_API_URL,
73
+ apiKey: this.botContext.secrets.AZURE_OPENAI_API_KEY || LLM_API_KEY,
95
74
  timeout: this.timeout,
96
75
  maxRetries: this.maxRetries,
97
76
  dangerouslyAllowBrowser: !isProd,
98
- defaultHeaders: this.buildLiteLLMTags(),
99
77
  })
100
78
  }
101
79
 
102
80
  private getAzureClient(): AzureOpenAI {
103
81
  return new AzureOpenAI({
104
- apiKey:
105
- this.botContext.secrets.AZURE_OPENAI_API_KEY || AZURE_OPENAI_API_KEY,
82
+ apiKey: this.botContext.secrets.AZURE_OPENAI_API_KEY || LLM_API_KEY,
106
83
  apiVersion:
107
84
  this.botContext.settings.AZURE_OPENAI_API_VERSION ||
108
- AZURE_OPENAI_API_VERSION,
85
+ LLM_AZURE_API_VERSION,
109
86
  deployment: this.modelName,
110
- baseURL:
111
- this.botContext.settings.AZURE_OPENAI_API_BASE || AZURE_OPENAI_API_BASE,
87
+ baseURL: this.botContext.settings.AZURE_OPENAI_API_BASE || LLM_API_URL,
112
88
  timeout: this.timeout,
113
89
  maxRetries: this.maxRetries,
114
90
  dangerouslyAllowBrowser: !isProd,
@@ -119,6 +95,14 @@ export class LLMConfig {
119
95
  model: string,
120
96
  verbosity: VerbosityLevel
121
97
  ): ModelSettings {
98
+ if (model.includes('gpt-5')) {
99
+ return {
100
+ reasoning: { effort: 'none' },
101
+ temperature: 1,
102
+ text: { verbosity },
103
+ }
104
+ }
105
+
122
106
  if (model.includes('gpt-4')) {
123
107
  return {
124
108
  temperature: 0,
@@ -126,10 +110,15 @@ export class LLMConfig {
126
110
  }
127
111
  }
128
112
 
129
- return {
130
- reasoning: { effort: 'none' },
131
- temperature: 1,
132
- text: { verbosity },
113
+ throw new Error(`Unsupported model: ${model}`)
114
+ }
115
+
116
+ getApiVersion(): string {
117
+ if (LLM_PROVIDER !== LLM_PROVIDERS.AZURE) {
118
+ return 'NOT_API_VERSION_FOR_OPENAI_PROVIDER'
133
119
  }
120
+ return (
121
+ this.botContext.settings.AZURE_OPENAI_API_VERSION || LLM_AZURE_API_VERSION
122
+ )
134
123
  }
135
124
  }
@@ -86,24 +86,11 @@ export abstract class BaseRunner<
86
86
 
87
87
  return runResult
88
88
  } catch (error) {
89
- if (error instanceof InputGuardrailTripwireTriggered) {
90
- const runResult: RunResult = {
91
- startingAgentName: '',
92
- lastAgentName: '',
93
- availableSpecialists: [],
94
- isTransferredToSpecialist: false,
95
- messages: [],
96
- memoryLength: 0,
97
- toolsExecuted: [],
98
- exit: true,
99
- error: false,
100
- inputGuardrailsTriggered: error.result.output.outputInfo,
101
- outputGuardrailsTriggered: [],
102
- }
103
-
104
- this.logger.logGuardrailTriggered()
105
- this.logger.logRunResult(runResult, startTime)
106
-
89
+ const runResult = this.handleInputGuardrailTripwireTriggered(
90
+ error,
91
+ startTime
92
+ )
93
+ if (runResult) {
107
94
  return runResult
108
95
  }
109
96
 
@@ -263,7 +250,6 @@ export abstract class BaseRunner<
263
250
  const durationPerCall = Math.round(totalDuration / rawResponses.length)
264
251
  const temperature =
265
252
  (this.llmConfig.modelSettings.temperature as number | undefined) ?? 0
266
-
267
253
  const llmRuns = rawResponses.map(response => ({
268
254
  inference_id: this.inferenceId,
269
255
  is_test: isTest,
@@ -286,4 +272,30 @@ export abstract class BaseRunner<
286
272
  llm_runs: llmRuns,
287
273
  })
288
274
  }
275
+
276
+ protected handleInputGuardrailTripwireTriggered(
277
+ error: unknown,
278
+ startTime: number
279
+ ): RunResult | undefined {
280
+ if (error instanceof InputGuardrailTripwireTriggered) {
281
+ const runResult: RunResult = {
282
+ startingAgentName: '',
283
+ lastAgentName: '',
284
+ availableSpecialists: [],
285
+ isTransferredToSpecialist: false,
286
+ messages: [],
287
+ memoryLength: 0,
288
+ toolsExecuted: [],
289
+ exit: true,
290
+ error: false,
291
+ inputGuardrailsTriggered: error.result.output.outputInfo,
292
+ outputGuardrailsTriggered: [],
293
+ }
294
+
295
+ this.logger.logGuardrailTriggered()
296
+ this.logger.logRunResult(runResult, startTime)
297
+
298
+ return runResult
299
+ }
300
+ }
289
301
  }
@@ -1,4 +1,4 @@
1
- import type { ResolvedPlugins } from '@botonic/core'
1
+ import type { AvailableSpecialist, ResolvedPlugins } from '@botonic/core'
2
2
  import { Handoff } from '@openai/agents'
3
3
  import type { Agent } from '@openai/agents-core'
4
4
 
@@ -16,17 +16,7 @@ export class RouterRunner<
16
16
  ): RunResult {
17
17
  const base = super.buildRunResult(result, context, memoryLength)
18
18
 
19
- const availableSpecialists = (this.agent.handoffs ?? []).map(
20
- (entry: Agent<any, any> | Handoff<any, any>) => {
21
- const isHandoff = entry instanceof Handoff
22
- const agent = isHandoff ? entry.agent : (entry as Agent<any, any>)
23
- const description = isHandoff
24
- ? entry.toolDescription
25
- : agent.handoffDescription
26
- return { name: agent.name, description }
27
- }
28
- )
29
-
19
+ const availableSpecialists = this.getAvailableSpecialists()
30
20
  const startingAgentName = this.agent.name ?? ''
31
21
  const lastAgentName = result.lastAgent?.name ?? ''
32
22
 
@@ -38,4 +28,37 @@ export class RouterRunner<
38
28
  isTransferredToSpecialist: startingAgentName !== lastAgentName,
39
29
  }
40
30
  }
31
+
32
+ protected override handleInputGuardrailTripwireTriggered(
33
+ error: unknown,
34
+ startTime: number
35
+ ): RunResult | undefined {
36
+ const runResult = super.handleInputGuardrailTripwireTriggered(
37
+ error,
38
+ startTime
39
+ )
40
+ if (runResult) {
41
+ // Override attributes to match router agent
42
+ runResult.startingAgentName = this.agent.name
43
+ runResult.lastAgentName = this.agent.name
44
+ runResult.availableSpecialists = this.getAvailableSpecialists()
45
+ runResult.isTransferredToSpecialist = false
46
+
47
+ return runResult
48
+ }
49
+ return undefined
50
+ }
51
+
52
+ private getAvailableSpecialists(): AvailableSpecialist[] {
53
+ return (this.agent.handoffs ?? []).map(
54
+ (entry: Agent<any, any> | Handoff<any, any>) => {
55
+ const isHandoff = entry instanceof Handoff
56
+ const agent = isHandoff ? entry.agent : (entry as Agent<any, any>)
57
+ const description = isHandoff
58
+ ? entry.toolDescription
59
+ : agent.handoffDescription
60
+ return { name: agent.name, description }
61
+ }
62
+ )
63
+ }
41
64
  }
@@ -17,7 +17,7 @@ export interface LlmRunData {
17
17
  deployment_name: string
18
18
  model_name: string
19
19
  feature: string
20
- api_version?: string
20
+ api_version: string
21
21
  num_prompt_tokens: number
22
22
  num_completion_tokens: number
23
23
  duration_in_milliseconds: number