@botonic/plugin-ai-agents 0.49.0-alpha.1 → 0.49.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 (63) hide show
  1. package/lib/cjs/agents/specialist-agent.js +1 -2
  2. package/lib/cjs/agents/specialist-agent.js.map +1 -1
  3. package/lib/cjs/constants.d.ts +9 -3
  4. package/lib/cjs/constants.js +11 -6
  5. package/lib/cjs/constants.js.map +1 -1
  6. package/lib/cjs/debug-logger.js +8 -2
  7. package/lib/cjs/debug-logger.js.map +1 -1
  8. package/lib/cjs/guardrails/input.d.ts +1 -1
  9. package/lib/cjs/guardrails/input.js +1 -3
  10. package/lib/cjs/guardrails/input.js.map +1 -1
  11. package/lib/cjs/index.d.ts +2 -2
  12. package/lib/cjs/index.js +11 -11
  13. package/lib/cjs/index.js.map +1 -1
  14. package/lib/cjs/llm-config.d.ts +9 -4
  15. package/lib/cjs/llm-config.js +43 -25
  16. package/lib/cjs/llm-config.js.map +1 -1
  17. package/lib/cjs/runners/base-runner.d.ts +6 -2
  18. package/lib/cjs/runners/base-runner.js +69 -5
  19. package/lib/cjs/runners/base-runner.js.map +1 -1
  20. package/lib/cjs/runners/index.d.ts +1 -1
  21. package/lib/cjs/runners/index.js +2 -2
  22. package/lib/cjs/runners/index.js.map +1 -1
  23. package/lib/cjs/runners/specialist-runner.d.ts +2 -8
  24. package/lib/cjs/runners/specialist-runner.js +0 -70
  25. package/lib/cjs/runners/specialist-runner.js.map +1 -1
  26. package/lib/cjs/services/types.d.ts +1 -1
  27. package/lib/esm/agents/specialist-agent.js +1 -2
  28. package/lib/esm/agents/specialist-agent.js.map +1 -1
  29. package/lib/esm/constants.d.ts +9 -3
  30. package/lib/esm/constants.js +11 -6
  31. package/lib/esm/constants.js.map +1 -1
  32. package/lib/esm/debug-logger.js +8 -2
  33. package/lib/esm/debug-logger.js.map +1 -1
  34. package/lib/esm/guardrails/input.d.ts +1 -1
  35. package/lib/esm/guardrails/input.js +1 -3
  36. package/lib/esm/guardrails/input.js.map +1 -1
  37. package/lib/esm/index.d.ts +2 -2
  38. package/lib/esm/index.js +11 -11
  39. package/lib/esm/index.js.map +1 -1
  40. package/lib/esm/llm-config.d.ts +9 -4
  41. package/lib/esm/llm-config.js +43 -25
  42. package/lib/esm/llm-config.js.map +1 -1
  43. package/lib/esm/runners/base-runner.d.ts +6 -2
  44. package/lib/esm/runners/base-runner.js +69 -5
  45. package/lib/esm/runners/base-runner.js.map +1 -1
  46. package/lib/esm/runners/index.d.ts +1 -1
  47. package/lib/esm/runners/index.js +2 -2
  48. package/lib/esm/runners/index.js.map +1 -1
  49. package/lib/esm/runners/specialist-runner.d.ts +2 -8
  50. package/lib/esm/runners/specialist-runner.js +0 -70
  51. package/lib/esm/runners/specialist-runner.js.map +1 -1
  52. package/lib/esm/services/types.d.ts +1 -1
  53. package/package.json +2 -2
  54. package/src/agents/specialist-agent.ts +1 -2
  55. package/src/constants.ts +12 -7
  56. package/src/debug-logger.ts +8 -2
  57. package/src/guardrails/input.ts +2 -3
  58. package/src/index.ts +15 -12
  59. package/src/llm-config.ts +56 -30
  60. package/src/runners/base-runner.ts +107 -8
  61. package/src/runners/index.ts +1 -1
  62. package/src/runners/specialist-runner.ts +3 -108
  63. package/src/services/types.ts +1 -1
package/src/llm-config.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { VerbosityLevel } from '@botonic/core'
1
+ import type { BotContext, VerbosityLevel } from '@botonic/core'
2
2
  import {
3
3
  type Model,
4
4
  type ModelProvider,
@@ -11,14 +11,16 @@ import {
11
11
  AZURE_OPENAI_API_KEY,
12
12
  AZURE_OPENAI_API_VERSION,
13
13
  isProd,
14
- OPENAI_API_KEY,
15
- OPENAI_MODEL,
16
- OPENAI_PROVIDER,
14
+ LITELLM_TAG_KEYS,
15
+ LLM_PROVIDERS,
17
16
  } from './constants'
18
17
 
18
+ export type LLMProvider = (typeof LLM_PROVIDERS)[keyof typeof LLM_PROVIDERS]
19
+
19
20
  export class LLMConfig {
20
21
  private readonly maxRetries: number
21
22
  private readonly timeout: number
23
+ private readonly botContext: BotContext
22
24
  public readonly modelName: string
23
25
  public readonly modelSettings: ModelSettings
24
26
  public readonly modelProvider: ModelProvider
@@ -27,11 +29,13 @@ export class LLMConfig {
27
29
  maxRetries: number,
28
30
  timeout: number,
29
31
  modelName: string,
30
- verbosity: VerbosityLevel
32
+ verbosity: VerbosityLevel,
33
+ botContext: BotContext
31
34
  ) {
32
35
  this.maxRetries = maxRetries
33
36
  this.timeout = timeout
34
- this.modelName = OPENAI_PROVIDER === 'openai' ? OPENAI_MODEL : modelName
37
+ this.botContext = botContext
38
+ this.modelName = modelName
35
39
  this.modelProvider = this.getModelProvider()
36
40
  this.modelSettings = this.getModelSettings(modelName, verbosity)
37
41
  }
@@ -40,6 +44,19 @@ export class LLMConfig {
40
44
  return await this.modelProvider.getModel(this.modelName)
41
45
  }
42
46
 
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
+
43
60
  private getModelProvider(): ModelProvider {
44
61
  const client = this.getClient()
45
62
  return new OpenAIProvider({
@@ -49,28 +66,49 @@ export class LLMConfig {
49
66
  }
50
67
 
51
68
  private getClient(): OpenAI | AzureOpenAI {
52
- if (OPENAI_PROVIDER === 'openai') {
53
- return this.getOpenAIClient()
69
+ if (this.botContext.settings.LITELLM_API_URL) {
70
+ return this.getLiteLLMClient()
54
71
  }
55
72
 
56
73
  return this.getAzureClient()
57
74
  }
58
75
 
59
- private getOpenAIClient(): OpenAI {
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 {
60
92
  return new OpenAI({
61
- apiKey: OPENAI_API_KEY,
93
+ apiKey: this.botContext.secrets.LITELLM_API_KEY,
94
+ baseURL: this.botContext.settings.LITELLM_API_URL,
62
95
  timeout: this.timeout,
63
96
  maxRetries: this.maxRetries,
64
97
  dangerouslyAllowBrowser: !isProd,
98
+ defaultHeaders: this.buildLiteLLMTags(),
65
99
  })
66
100
  }
67
101
 
68
102
  private getAzureClient(): AzureOpenAI {
69
103
  return new AzureOpenAI({
70
- apiKey: AZURE_OPENAI_API_KEY,
71
- apiVersion: AZURE_OPENAI_API_VERSION,
104
+ apiKey:
105
+ this.botContext.secrets.AZURE_OPENAI_API_KEY || AZURE_OPENAI_API_KEY,
106
+ apiVersion:
107
+ this.botContext.settings.AZURE_OPENAI_API_VERSION ||
108
+ AZURE_OPENAI_API_VERSION,
72
109
  deployment: this.modelName,
73
- baseURL: AZURE_OPENAI_API_BASE,
110
+ baseURL:
111
+ this.botContext.settings.AZURE_OPENAI_API_BASE || AZURE_OPENAI_API_BASE,
74
112
  timeout: this.timeout,
75
113
  maxRetries: this.maxRetries,
76
114
  dangerouslyAllowBrowser: !isProd,
@@ -81,14 +119,6 @@ export class LLMConfig {
81
119
  model: string,
82
120
  verbosity: VerbosityLevel
83
121
  ): ModelSettings {
84
- if (model.includes('gpt-5')) {
85
- return {
86
- reasoning: { effort: 'none' },
87
- temperature: 1,
88
- text: { verbosity },
89
- }
90
- }
91
-
92
122
  if (model.includes('gpt-4')) {
93
123
  return {
94
124
  temperature: 0,
@@ -96,14 +126,10 @@ export class LLMConfig {
96
126
  }
97
127
  }
98
128
 
99
- throw new Error(`Unsupported model: ${model}`)
129
+ return {
130
+ reasoning: { effort: 'none' },
131
+ temperature: 1,
132
+ text: { verbosity },
133
+ }
100
134
  }
101
135
  }
102
-
103
- export function getApiVersion(): string {
104
- // Return NOT_API_VERSION_FOR_OPENAI_PROVIDER if OPENAI_PROVIDER
105
- // is not azure to avoid error when tracking in llm_runs endpoint
106
- return OPENAI_PROVIDER === 'azure'
107
- ? AZURE_OPENAI_API_VERSION
108
- : 'NOT_API_VERSION_FOR_OPENAI_PROVIDER'
109
- }
@@ -3,13 +3,18 @@ import type {
3
3
  ResolvedPlugins,
4
4
  ToolExecution,
5
5
  } from '@botonic/core'
6
- import { InputGuardrailTripwireTriggered, Runner } from '@openai/agents'
7
-
6
+ import {
7
+ InputGuardrailTripwireTriggered,
8
+ Runner,
9
+ RunToolCallItem,
10
+ RunToolCallOutputItem,
11
+ } from '@openai/agents'
8
12
  import { isProd } from '../constants'
9
13
  import type { DebugLogger } from '../debug-logger'
10
- import { getApiVersion, type LLMConfig } from '../llm-config'
14
+ import type { LLMConfig } from '../llm-config'
11
15
  import { HubtypeApiClient } from '../services/hubtype-api-client'
12
16
  import { TrackFeature, TrackProductName } from '../services/types'
17
+ import { RETRIEVE_KNOWLEDGE_TOOL_NAME } from '../tools'
13
18
  import type {
14
19
  AgenticInputMessage,
15
20
  AgenticOutputMessage,
@@ -109,10 +114,105 @@ export abstract class BaseRunner<
109
114
  }
110
115
 
111
116
  protected getToolsExecuted(
112
- _result: RunnerResult,
113
- _context: Context<TPlugins, TExtraData>
117
+ result: RunnerResult,
118
+ context: Context<TPlugins, TExtraData>
114
119
  ): ToolExecution[] {
115
- return []
120
+ const toolResultsByCallId = this.getToolResultsByCallId(result.newItems)
121
+
122
+ return (
123
+ result.newItems
124
+ ?.filter(item => item instanceof RunToolCallItem)
125
+ .map((item: RunToolCallItem) =>
126
+ this.getToolExecutionInfo(
127
+ item as RunToolCallItem,
128
+ context,
129
+ toolResultsByCallId
130
+ )
131
+ )
132
+ .filter(
133
+ (toolExecution: ToolExecution) => toolExecution.toolName !== ''
134
+ ) || []
135
+ )
136
+ }
137
+
138
+ private getToolResultsByCallId(
139
+ newItems: unknown[] = []
140
+ ): Map<string, string> {
141
+ const map = new Map<string, string>()
142
+ for (const item of newItems) {
143
+ if (!(item instanceof RunToolCallOutputItem)) {
144
+ continue
145
+ }
146
+ const rawItem = item.rawItem as {
147
+ callId?: string
148
+ output?: string | { type?: string; text?: string }
149
+ }
150
+ const callId = rawItem?.callId
151
+ const output = rawItem?.output
152
+ if (callId == null || output == null) {
153
+ continue
154
+ }
155
+ const text = this.extractToolResult(output)
156
+ if (text != null) {
157
+ map.set(callId, text)
158
+ }
159
+ }
160
+ return map
161
+ }
162
+
163
+ private extractToolResult(
164
+ output: string | { type?: string; text?: string }
165
+ ): string | undefined {
166
+ if (typeof output === 'string') {
167
+ return output
168
+ }
169
+ return output?.type === 'text' && typeof output?.text === 'string'
170
+ ? output.text
171
+ : undefined
172
+ }
173
+
174
+ private getToolExecutionInfo(
175
+ item: RunToolCallItem,
176
+ context: Context<TPlugins, TExtraData>,
177
+ toolResultsByCallId: Map<string, string>
178
+ ): ToolExecution {
179
+ if (item.rawItem.type !== 'function_call') {
180
+ return {
181
+ toolName: '',
182
+ toolArguments: {},
183
+ }
184
+ }
185
+ const toolName = item.rawItem.name
186
+ const toolArguments = this.getSafeToolArguments(item.rawItem.arguments)
187
+ const toolResults = item.rawItem.callId
188
+ ? toolResultsByCallId.get(item.rawItem.callId)
189
+ : undefined
190
+
191
+ const toolExecution: ToolExecution = {
192
+ toolName,
193
+ toolArguments,
194
+ toolResults,
195
+ }
196
+
197
+ if (toolName === RETRIEVE_KNOWLEDGE_TOOL_NAME) {
198
+ return {
199
+ ...toolExecution,
200
+ knowledgebaseSourcesIds: context.knowledgeUsed.sourceIds,
201
+ knowledgebaseChunksIds: context.knowledgeUsed.chunksIds,
202
+ }
203
+ }
204
+
205
+ return toolExecution
206
+ }
207
+
208
+ private getSafeToolArguments(
209
+ rawToolArguments: string
210
+ ): Record<string, unknown> {
211
+ try {
212
+ return JSON.parse(rawToolArguments)
213
+ } catch (_error) {
214
+ return {}
215
+ }
116
216
  }
117
217
 
118
218
  protected buildRunResult(
@@ -163,7 +263,6 @@ export abstract class BaseRunner<
163
263
  const durationPerCall = Math.round(totalDuration / rawResponses.length)
164
264
  const temperature =
165
265
  (this.llmConfig.modelSettings.temperature as number | undefined) ?? 0
166
- const apiVersion = getApiVersion()
167
266
 
168
267
  const llmRuns = rawResponses.map(response => ({
169
268
  inference_id: this.inferenceId,
@@ -174,7 +273,7 @@ export abstract class BaseRunner<
174
273
  (response.providerData?.model as string | undefined) ??
175
274
  this.llmConfig.modelName,
176
275
  feature: TrackFeature.AI_AGENT_RUN,
177
- api_version: apiVersion,
276
+ api_version: this.llmConfig.getApiVersion(),
178
277
  num_prompt_tokens: response.usage.inputTokens,
179
278
  num_completion_tokens: response.usage.outputTokens,
180
279
  duration_in_milliseconds: durationPerCall,
@@ -1,2 +1,2 @@
1
1
  export { RouterRunner } from './router-runner'
2
- export { SpecialistRunner as WorkerRunner } from './specialist-runner'
2
+ export { SpecialistRunner } from './specialist-runner'
@@ -1,112 +1,7 @@
1
- import type { ResolvedPlugins, ToolExecution } from '@botonic/core'
2
- import { RunToolCallItem, RunToolCallOutputItem } from '@openai/agents'
3
- import { RETRIEVE_KNOWLEDGE_TOOL_NAME } from '../tools'
4
- import type { Context } from '../types'
5
- import { BaseRunner, type RunnerResult } from './base-runner'
1
+ import type { ResolvedPlugins } from '@botonic/core'
2
+ import { BaseRunner } from './base-runner'
6
3
 
7
4
  export class SpecialistRunner<
8
5
  TPlugins extends ResolvedPlugins = ResolvedPlugins,
9
6
  TExtraData = unknown,
10
- > extends BaseRunner<TPlugins, TExtraData> {
11
- protected getToolsExecuted(
12
- result: RunnerResult,
13
- context: Context<TPlugins, TExtraData>
14
- ): ToolExecution[] {
15
- const toolResultsByCallId = this.getToolResultsByCallId(result.newItems)
16
-
17
- return (
18
- result.newItems
19
- ?.filter(item => item instanceof RunToolCallItem)
20
- .map((item: RunToolCallItem) =>
21
- this.getToolExecutionInfo(
22
- item as RunToolCallItem,
23
- context,
24
- toolResultsByCallId
25
- )
26
- )
27
- .filter(
28
- (toolExecution: ToolExecution) => toolExecution.toolName !== ''
29
- ) || []
30
- )
31
- }
32
-
33
- private getToolResultsByCallId(
34
- newItems: unknown[] = []
35
- ): Map<string, string> {
36
- const map = new Map<string, string>()
37
- for (const item of newItems) {
38
- if (!(item instanceof RunToolCallOutputItem)) {
39
- continue
40
- }
41
- const rawItem = item.rawItem as {
42
- callId?: string
43
- output?: string | { type?: string; text?: string }
44
- }
45
- const callId = rawItem?.callId
46
- const output = rawItem?.output
47
- if (callId == null || output == null) {
48
- continue
49
- }
50
- const text = this.extractToolResult(output)
51
- if (text != null) {
52
- map.set(callId, text)
53
- }
54
- }
55
- return map
56
- }
57
-
58
- private extractToolResult(
59
- output: string | { type?: string; text?: string }
60
- ): string | undefined {
61
- if (typeof output === 'string') {
62
- return output
63
- }
64
- return output?.type === 'text' && typeof output?.text === 'string'
65
- ? output.text
66
- : undefined
67
- }
68
-
69
- private getToolExecutionInfo(
70
- item: RunToolCallItem,
71
- context: Context<TPlugins, TExtraData>,
72
- toolResultsByCallId: Map<string, string>
73
- ): ToolExecution {
74
- if (item.rawItem.type !== 'function_call') {
75
- return {
76
- toolName: '',
77
- toolArguments: {},
78
- }
79
- }
80
- const toolName = item.rawItem.name
81
- const toolArguments = this.getSafeToolArguments(item.rawItem.arguments)
82
- const toolResults = item.rawItem.callId
83
- ? toolResultsByCallId.get(item.rawItem.callId)
84
- : undefined
85
-
86
- const toolExecution: ToolExecution = {
87
- toolName,
88
- toolArguments,
89
- toolResults,
90
- }
91
-
92
- if (toolName === RETRIEVE_KNOWLEDGE_TOOL_NAME) {
93
- return {
94
- ...toolExecution,
95
- knowledgebaseSourcesIds: context.knowledgeUsed.sourceIds,
96
- knowledgebaseChunksIds: context.knowledgeUsed.chunksIds,
97
- }
98
- }
99
-
100
- return toolExecution
101
- }
102
-
103
- private getSafeToolArguments(
104
- rawToolArguments: string
105
- ): Record<string, unknown> {
106
- try {
107
- return JSON.parse(rawToolArguments)
108
- } catch (_error) {
109
- return {}
110
- }
111
- }
112
- }
7
+ > extends BaseRunner<TPlugins, TExtraData> {}
@@ -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