@botonic/plugin-ai-agents 0.45.0 → 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 +2 -1
  11. package/lib/cjs/debug-logger.js +4 -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 +5 -1
  23. package/lib/cjs/runner.js +54 -3
  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 +2 -1
  44. package/lib/esm/debug-logger.js +4 -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 +5 -1
  56. package/lib/esm/runner.js +54 -3
  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 +4 -3
  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 +7 -27
  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 +85 -4
  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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/services/types.ts"],"names":[],"mappings":";;;AAEA,IAAY,gBAEX;AAFD,WAAY,gBAAgB;IAC1B,yCAAqB,CAAA;AACvB,CAAC,EAFW,gBAAgB,gCAAhB,gBAAgB,QAE3B;AAED,IAAY,YAGX;AAHD,WAAY,YAAY;IACtB,6CAA6B,CAAA;IAC7B,yDAAyC,CAAA;AAC3C,CAAC,EAHW,YAAY,4BAAZ,YAAY,QAGvB"}
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.retrieveKnowledge = void 0;
4
4
  const agents_1 = require("@openai/agents");
5
5
  const zod_1 = require("zod");
6
- const hubtype_api_client_1 = require("../hubtype-api-client");
6
+ const hubtype_api_client_1 = require("../services/hubtype-api-client");
7
7
  exports.retrieveKnowledge = (0, agents_1.tool)({
8
8
  name: 'retrieve_knowledge',
9
9
  description: 'Consult the knowledge base for information before answering. Use this tool to make sure the information you provide is faithful.',
@@ -1 +1 @@
1
- {"version":3,"file":"retrieve-knowledge.js","sourceRoot":"","sources":["../../../src/tools/retrieve-knowledge.ts"],"names":[],"mappings":";;;AAAA,2CAAsD;AACtD,6BAAuB;AAEvB,8DAAwD;AAG3C,QAAA,iBAAiB,GAAG,IAAA,aAAI,EAAC;IACpC,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EACT,kIAAkI;IACpI,UAAU,EAAE,OAAC,CAAC,MAAM,CAAC;QACnB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;KACzE,CAAC;IACF,OAAO,EAAE,KAAK,EACZ,KAAwB,EACxB,UAAgC,EACb,EAAE;QACrB,MAAM,OAAO,GAAG,UAAU,EAAE,OAAO,CAAA;QACnC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;QACzB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;QACxC,CAAC;QACD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAA;QACnC,MAAM,MAAM,GAAG,IAAI,qCAAgB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QACtD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;QACnE,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAElD,OAAO,CAAC,aAAa,GAAG;YACtB,KAAK;YACL,SAAS;YACT,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;YACxC,UAAU;SACX,CAAA;QAED,OAAO,UAAU,CAAA;IACnB,CAAC;CACF,CAAC,CAAA"}
1
+ {"version":3,"file":"retrieve-knowledge.js","sourceRoot":"","sources":["../../../src/tools/retrieve-knowledge.ts"],"names":[],"mappings":";;;AAAA,2CAAsD;AACtD,6BAAuB;AAEvB,uEAAiE;AAGpD,QAAA,iBAAiB,GAAG,IAAA,aAAI,EAAC;IACpC,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EACT,kIAAkI;IACpI,UAAU,EAAE,OAAC,CAAC,MAAM,CAAC;QACnB,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,4CAA4C,CAAC;KACzE,CAAC;IACF,OAAO,EAAE,KAAK,EACZ,KAAwB,EACxB,UAAgC,EACb,EAAE;QACrB,MAAM,OAAO,GAAG,UAAU,EAAE,OAAO,CAAA;QACnC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAA;QACzB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;QACxC,CAAC;QACD,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAA;QACnC,MAAM,MAAM,GAAG,IAAI,qCAAgB,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QACtD,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,qBAAqB,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;QACnE,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAElD,OAAO,CAAC,aAAa,GAAG;YACtB,KAAK;YACL,SAAS;YACT,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC;YACxC,UAAU;SACX,CAAA;QAED,OAAO,UAAU,CAAA;IACnB,CAAC;CACF,CAAC,CAAA"}
@@ -45,5 +45,12 @@ export interface PluginAiAgentOptions<TPlugins extends ResolvedPlugins = Resolve
45
45
  /** Enable debug logging for AI agent configuration and execution details. */
46
46
  enableDebug?: boolean;
47
47
  }
48
+ export interface ResultRawResponse {
49
+ usage: {
50
+ inputTokens: number;
51
+ outputTokens: number;
52
+ };
53
+ providerData?: Record<string, unknown>;
54
+ }
48
55
  export type AgenticInputMessage = AgentInputItem;
49
56
  export { AgenticOutputMessage, AiAgentArgs, GuardrailRule, InferenceResponse, RunResult, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botonic/plugin-ai-agents",
3
- "version": "0.45.0",
3
+ "version": "0.46.0-alpha-3",
4
4
  "main": "./lib/cjs/index.js",
5
5
  "module": "./lib/esm/index.js",
6
6
  "description": "Use AI Agents to generate your contents",
@@ -14,12 +14,13 @@
14
14
  "format": "biome format --write src/ tests/"
15
15
  },
16
16
  "dependencies": {
17
- "@botonic/core": "^0.45.0",
17
+ "@botonic/core": "^0.46.0-alpha-3",
18
18
  "@openai/agents": "^0.3.9",
19
19
  "axios": "^1.13.6",
20
20
  "openai": "^6.0.0",
21
21
  "uuid": "^10.0.0",
22
- "zod": "3.25.76"
22
+ "zod": "3.25.76",
23
+ "zod-to-json-schema": "^3.25.1"
23
24
  },
24
25
  "devDependencies": {
25
26
  "@types/uuid": "^10.0.0"
@@ -3,12 +3,13 @@ import {
3
3
  Agent,
4
4
  type AgentOutputType,
5
5
  type InputGuardrail,
6
- type ModelSettings,
7
6
  } from '@openai/agents'
8
7
 
9
- import { OPENAI_MODEL, OPENAI_PROVIDER } from './constants'
8
+ import { OPENAI_PROVIDER } from './constants'
10
9
  import type { DebugLogger } from './debug-logger'
11
10
  import { createInputGuardrail } from './guardrails'
11
+ import type { GuardrailTrackingContext } from './guardrails/input'
12
+ import type { LLMConfig } from './llm-config'
12
13
  import { OutputSchema } from './structured-output'
13
14
  import { mandatoryTools, retrieveKnowledge } from './tools'
14
15
  import type { AIAgent, Context, GuardrailRule, Tool } from './types'
@@ -24,7 +25,9 @@ interface AIAgentBuilderOptions<
24
25
  contactInfo: ContactInfo[]
25
26
  inputGuardrailRules: GuardrailRule[]
26
27
  sourceIds: string[]
28
+ llmConfig: LLMConfig
27
29
  logger: DebugLogger
30
+ guardrailTrackingContext: GuardrailTrackingContext
28
31
  }
29
32
 
30
33
  export class AIAgentBuilder<
@@ -35,6 +38,7 @@ export class AIAgentBuilder<
35
38
  private instructions: string
36
39
  private tools: Tool<TPlugins, TExtraData>[]
37
40
  private inputGuardrails: InputGuardrail[]
41
+ public llmConfig: LLMConfig
38
42
  private logger: DebugLogger
39
43
 
40
44
  constructor(options: AIAgentBuilderOptions<TPlugins, TExtraData>) {
@@ -46,35 +50,35 @@ export class AIAgentBuilder<
46
50
  )
47
51
  this.tools = this.addHubtypeTools(options.tools, options.sourceIds)
48
52
  this.inputGuardrails = []
53
+ this.llmConfig = options.llmConfig
49
54
  this.logger = options.logger
50
55
  if (options.inputGuardrailRules.length > 0) {
51
- const inputGuardrail = createInputGuardrail(options.inputGuardrailRules)
56
+ const inputGuardrail = createInputGuardrail(
57
+ options.inputGuardrailRules,
58
+ options.llmConfig,
59
+ options.guardrailTrackingContext
60
+ )
52
61
  this.inputGuardrails.push(inputGuardrail)
53
62
  }
54
63
  }
55
64
 
56
65
  build(): AIAgent<TPlugins, TExtraData> {
57
- const modelSettings: ModelSettings = {} as ModelSettings
58
- if (OPENAI_PROVIDER === 'openai') {
59
- modelSettings.reasoning = { effort: 'none' }
60
- modelSettings.text = { verbosity: 'medium' }
61
- }
62
-
63
- const hasRetrieveKnowledge = this.tools.includes(retrieveKnowledge)
64
- if (hasRetrieveKnowledge && OPENAI_PROVIDER === 'azure') {
65
- modelSettings.toolChoice = retrieveKnowledge.name
66
- }
67
-
68
66
  // When using standard OpenAI API, we need to specify the model
69
67
  // Azure OpenAI uses deployment name instead
70
- const model = OPENAI_PROVIDER === 'openai' ? OPENAI_MODEL : undefined
68
+
69
+ const model = this.llmConfig.modelName
70
+ const hasRetrieveKnowledge = this.tools.includes(retrieveKnowledge)
71
71
 
72
72
  this.logger.logModelSettings({
73
73
  provider: OPENAI_PROVIDER,
74
74
  model,
75
- reasoning: modelSettings.reasoning as { effort: string } | undefined,
76
- text: modelSettings.text as { verbosity: string } | undefined,
77
- toolChoice: modelSettings.toolChoice as string | undefined,
75
+ reasoning: this.llmConfig.modelSettings.reasoning as
76
+ | { effort: string }
77
+ | undefined,
78
+ text: this.llmConfig.modelSettings.text as
79
+ | { verbosity: string }
80
+ | undefined,
81
+ toolChoice: this.llmConfig.modelSettings.toolChoice as string | undefined,
78
82
  hasRetrieveKnowledge,
79
83
  })
80
84
 
@@ -89,7 +93,6 @@ export class AIAgentBuilder<
89
93
  outputType: OutputSchema,
90
94
  inputGuardrails: this.inputGuardrails,
91
95
  outputGuardrails: [],
92
- modelSettings,
93
96
  })
94
97
  }
95
98
 
@@ -0,0 +1,21 @@
1
+ import type { ToolConfigJSON } from '@botonic/core'
2
+ import { zodToJsonSchema } from 'zod-to-json-schema'
3
+
4
+ import type { CustomTool } from './types'
5
+
6
+ /**
7
+ * Maps custom AI agent tools to the format expected by bot config (e.g. for flow-builder).
8
+ * Converts each tool's Zod schema to JSON Schema so it can be serialized in the config.
9
+ */
10
+ export function getToolsForBotConfig(
11
+ customTools: CustomTool[]
12
+ ): ToolConfigJSON[] {
13
+ return customTools.map(tool => ({
14
+ name: tool.name,
15
+ description: tool.description,
16
+ // Cast to avoid TS "Type instantiation is excessively deep" with zodToJsonSchema + ZodSchema
17
+ schema: zodToJsonSchema(tool.schema as never, {
18
+ $refStrategy: 'none',
19
+ }) as ToolConfigJSON['schema'],
20
+ }))
21
+ }
package/src/constants.ts CHANGED
@@ -10,11 +10,12 @@ export const OPENAI_PROVIDER: 'openai' | 'azure' =
10
10
  // Azure OpenAI Configuration
11
11
  export const AZURE_OPENAI_API_KEY = process.env.AZURE_OPENAI_API_KEY // pragma: allowlist secret
12
12
  export const AZURE_OPENAI_API_BASE = process.env.AZURE_OPENAI_API_BASE
13
- export const AZURE_OPENAI_API_DEPLOYMENT_NAME =
14
- process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME || 'gpt-41-mini_p1'
15
13
  export const AZURE_OPENAI_API_VERSION =
16
14
  process.env.AZURE_OPENAI_API_VERSION || '2025-01-01-preview'
17
15
 
18
16
  export const isProd = process.env.NODE_ENV === 'production'
19
17
 
20
18
  export const MAX_MEMORY_LENGTH = 25
19
+
20
+ export const DEFAULT_TIMEOUT_16_SECONDS = 16000
21
+ export const DEFAULT_MAX_RETRIES = 2
@@ -1,12 +1,6 @@
1
1
  import type { AiAgentArgs, ToolExecution } from '@botonic/core'
2
-
3
- import {
4
- AZURE_OPENAI_API_BASE,
5
- AZURE_OPENAI_API_DEPLOYMENT_NAME,
6
- MAX_MEMORY_LENGTH,
7
- OPENAI_MODEL,
8
- OPENAI_PROVIDER,
9
- } from './constants'
2
+ import type { ModelSettings } from '@openai/agents'
3
+ import { MAX_MEMORY_LENGTH, OPENAI_PROVIDER } from './constants'
10
4
  import type { AgenticInputMessage, MemoryOptions, RunResult } from './types'
11
5
 
12
6
  const PREFIX = '[BotonicPluginAiAgents]'
@@ -36,7 +30,7 @@ export interface DebugLogger {
36
30
  messages: AgenticInputMessage[]
37
31
  ): void
38
32
  logModelSettings(settings: ModelSettingsInfo): void
39
- logRunnerStart(): void
33
+ logRunnerStart(model: string, modelSettings: ModelSettings): void
40
34
  logRunResult(runResult: RunResult, startTime: number): void
41
35
  logGuardrailTriggered(): void
42
36
  logRunnerError(startTime: number, error: unknown): void
@@ -44,25 +38,9 @@ export interface DebugLogger {
44
38
  }
45
39
 
46
40
  class EnabledDebugLogger implements DebugLogger {
47
- private getModelInfo(): { model: string; apiBase: string } {
48
- const model =
49
- OPENAI_PROVIDER === 'azure'
50
- ? AZURE_OPENAI_API_DEPLOYMENT_NAME
51
- : OPENAI_MODEL
52
- const apiBase =
53
- OPENAI_PROVIDER === 'azure' && AZURE_OPENAI_API_BASE
54
- ? AZURE_OPENAI_API_BASE
55
- : 'OpenAI API'
56
- return { model, apiBase }
57
- }
58
-
59
41
  logInitialConfig(config: DebugLoggerConfig): void {
60
- const { model, apiBase } = this.getModelInfo()
61
-
62
42
  console.log(`${PREFIX} === Plugin Initialization ===`)
63
43
  console.log(`${PREFIX} Provider: ${OPENAI_PROVIDER}`)
64
- console.log(`${PREFIX} Model/Deployment: ${model}`)
65
- console.log(`${PREFIX} API Base: ${apiBase}`)
66
44
  console.log(
67
45
  `${PREFIX} Message History API Version: ${config.messageHistoryApiVersion}`
68
46
  )
@@ -117,8 +95,10 @@ class EnabledDebugLogger implements DebugLogger {
117
95
  console.log(`${PREFIX} === End Model Settings ===`)
118
96
  }
119
97
 
120
- logRunnerStart(): void {
98
+ logRunnerStart(model: string, modelSettings: ModelSettings): void {
121
99
  console.log(`${PREFIX} === Runner Execution Start ===`)
100
+ console.log(`${PREFIX} Model: ${model}`)
101
+ console.log(`${PREFIX} Model Settings: ${JSON.stringify(modelSettings)}`)
122
102
  }
123
103
 
124
104
  logRunResult(runResult: RunResult, startTime: number): void {
@@ -174,7 +154,7 @@ class DisabledDebugLogger implements DebugLogger {
174
154
  logInitialConfig(): void {}
175
155
  logAgentDebugInfo(): void {}
176
156
  logModelSettings(): void {}
177
- logRunnerStart(): void {}
157
+ logRunnerStart(_model: string, _modelSettings: ModelSettings): void {}
178
158
  logRunResult(): void {}
179
159
  logGuardrailTriggered(): void {}
180
160
  logRunnerError(): void {}
@@ -1,14 +1,28 @@
1
1
  import {
2
2
  Agent,
3
3
  type InputGuardrail,
4
- run,
4
+ Runner,
5
5
  type UserMessageItem,
6
6
  } from '@openai/agents'
7
7
  import { z } from 'zod'
8
+ import { AZURE_OPENAI_API_VERSION, isProd, OPENAI_PROVIDER } from '../constants'
9
+ import type { LLMConfig } from '../llm-config'
10
+ import { HubtypeApiClient } from '../services/hubtype-api-client'
11
+ import { TrackFeature, TrackProductName } from '../services/types'
12
+ import type { GuardrailRule, ResultRawResponse } from '../types'
8
13
 
9
- import type { GuardrailRule } from '../types'
14
+ export interface GuardrailTrackingContext {
15
+ botId: string
16
+ isTest: boolean
17
+ authToken: string
18
+ inferenceId: string
19
+ }
10
20
 
11
- export function createInputGuardrail(rules: GuardrailRule[]): InputGuardrail {
21
+ export function createInputGuardrail(
22
+ rules: GuardrailRule[],
23
+ llmConfig: LLMConfig,
24
+ trackingContext: GuardrailTrackingContext
25
+ ): InputGuardrail {
12
26
  const outputType = z.object(
13
27
  Object.fromEntries(
14
28
  rules.map(rule => [rule.name, z.boolean().describe(rule.description)])
@@ -26,7 +40,26 @@ export function createInputGuardrail(rules: GuardrailRule[]): InputGuardrail {
26
40
  name: 'InputGuardrail',
27
41
  execute: async ({ input, context }) => {
28
42
  const lastMessage = input[input.length - 1] as UserMessageItem
29
- const result = await run(agent, [lastMessage], { context })
43
+ const modelProvider = llmConfig.modelProvider
44
+ const modelSettings = llmConfig.modelSettings
45
+ modelSettings.toolChoice = undefined
46
+ const runner = new Runner({
47
+ modelSettings,
48
+ modelProvider,
49
+ tracingDisabled: true,
50
+ })
51
+ const startTime = Date.now()
52
+ const result = await runner.run(agent, [lastMessage], { context })
53
+ const endTime = Date.now()
54
+
55
+ void sendGuardrailLlmRunTracking(
56
+ result,
57
+ trackingContext,
58
+ llmConfig,
59
+ startTime,
60
+ endTime
61
+ )
62
+
30
63
  const finalOutput = result.finalOutput as Record<string, boolean>
31
64
  if (finalOutput === undefined) {
32
65
  throw new Error('Guardrail agent failed to produce output')
@@ -42,3 +75,48 @@ export function createInputGuardrail(rules: GuardrailRule[]): InputGuardrail {
42
75
  },
43
76
  }
44
77
  }
78
+
79
+ async function sendGuardrailLlmRunTracking(
80
+ result: {
81
+ rawResponses?: ResultRawResponse[]
82
+ },
83
+ trackingContext: GuardrailTrackingContext,
84
+ llmConfig: LLMConfig,
85
+ startTime: number,
86
+ endTime: number
87
+ ): Promise<void> {
88
+ if (!isProd) {
89
+ return
90
+ }
91
+ const rawResponses = result.rawResponses ?? []
92
+ if (rawResponses.length === 0) {
93
+ return
94
+ }
95
+ const totalDuration = endTime - startTime
96
+ const durationPerCall = Math.round(totalDuration / rawResponses.length)
97
+ const temperature =
98
+ (llmConfig.modelSettings.temperature as number | undefined) ?? 0
99
+ const apiVersion = OPENAI_PROVIDER === 'azure' ? AZURE_OPENAI_API_VERSION : ''
100
+
101
+ const llmRuns = rawResponses.map(response => ({
102
+ inference_id: trackingContext.inferenceId,
103
+ is_test: trackingContext.isTest,
104
+ product_name: TrackProductName.AI_AGENT,
105
+ deployment_name: llmConfig.modelName,
106
+ model_name:
107
+ (response.providerData?.['model'] as string | undefined) ??
108
+ llmConfig.modelName,
109
+ feature: TrackFeature.AI_AGENT_GUARDRAIL,
110
+ api_version: apiVersion,
111
+ num_prompt_tokens: response.usage.inputTokens,
112
+ num_completion_tokens: response.usage.outputTokens,
113
+ duration_in_milliseconds: durationPerCall,
114
+ temperature,
115
+ error: null,
116
+ }))
117
+
118
+ const client = new HubtypeApiClient(trackingContext.authToken)
119
+ await client.trackLlmRuns(trackingContext.botId, {
120
+ llm_runs: llmRuns,
121
+ })
122
+ }
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
+ }