@botonic/plugin-ai-agents 0.45.0 → 0.46.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/agent-builder.d.ts +5 -0
- package/lib/cjs/agent-builder.js +7 -15
- package/lib/cjs/agent-builder.js.map +1 -1
- package/lib/cjs/bot-config-tools.d.ts +7 -0
- package/lib/cjs/bot-config-tools.js +19 -0
- package/lib/cjs/bot-config-tools.js.map +1 -0
- package/lib/cjs/constants.d.ts +2 -1
- package/lib/cjs/constants.js +3 -2
- package/lib/cjs/constants.js.map +1 -1
- package/lib/cjs/debug-logger.d.ts +2 -1
- package/lib/cjs/debug-logger.js +4 -14
- package/lib/cjs/debug-logger.js.map +1 -1
- package/lib/cjs/guardrails/input.d.ts +8 -1
- package/lib/cjs/guardrails/input.js +48 -2
- package/lib/cjs/guardrails/input.js.map +1 -1
- package/lib/cjs/index.d.ts +4 -1
- package/lib/cjs/index.js +34 -13
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/llm-config.d.ts +15 -0
- package/lib/cjs/llm-config.js +66 -0
- package/lib/cjs/llm-config.js.map +1 -0
- package/lib/cjs/runner.d.ts +5 -1
- package/lib/cjs/runner.js +54 -3
- package/lib/cjs/runner.js.map +1 -1
- package/lib/cjs/services/hubtype-api-client.d.ts +14 -0
- package/lib/{esm → cjs/services}/hubtype-api-client.js +19 -5
- package/lib/cjs/services/hubtype-api-client.js.map +1 -0
- package/lib/cjs/services/types.d.ts +85 -0
- package/lib/cjs/services/types.js +13 -0
- package/lib/cjs/services/types.js.map +1 -0
- package/lib/cjs/tools/retrieve-knowledge.js +1 -1
- package/lib/cjs/tools/retrieve-knowledge.js.map +1 -1
- package/lib/cjs/types.d.ts +7 -0
- package/lib/esm/agent-builder.d.ts +5 -0
- package/lib/esm/agent-builder.js +7 -15
- package/lib/esm/agent-builder.js.map +1 -1
- package/lib/esm/bot-config-tools.d.ts +7 -0
- package/lib/esm/bot-config-tools.js +19 -0
- package/lib/esm/bot-config-tools.js.map +1 -0
- package/lib/esm/constants.d.ts +2 -1
- package/lib/esm/constants.js +3 -2
- package/lib/esm/constants.js.map +1 -1
- package/lib/esm/debug-logger.d.ts +2 -1
- package/lib/esm/debug-logger.js +4 -14
- package/lib/esm/debug-logger.js.map +1 -1
- package/lib/esm/guardrails/input.d.ts +8 -1
- package/lib/esm/guardrails/input.js +48 -2
- package/lib/esm/guardrails/input.js.map +1 -1
- package/lib/esm/index.d.ts +4 -1
- package/lib/esm/index.js +34 -13
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/llm-config.d.ts +15 -0
- package/lib/esm/llm-config.js +66 -0
- package/lib/esm/llm-config.js.map +1 -0
- package/lib/esm/runner.d.ts +5 -1
- package/lib/esm/runner.js +54 -3
- package/lib/esm/runner.js.map +1 -1
- package/lib/esm/services/hubtype-api-client.d.ts +14 -0
- package/lib/{cjs → esm/services}/hubtype-api-client.js +19 -5
- package/lib/esm/services/hubtype-api-client.js.map +1 -0
- package/lib/esm/services/types.d.ts +85 -0
- package/lib/esm/services/types.js +13 -0
- package/lib/esm/services/types.js.map +1 -0
- package/lib/esm/tools/retrieve-knowledge.js +1 -1
- package/lib/esm/tools/retrieve-knowledge.js.map +1 -1
- package/lib/esm/types.d.ts +7 -0
- package/package.json +4 -3
- package/src/agent-builder.ts +22 -19
- package/src/bot-config-tools.ts +21 -0
- package/src/constants.ts +3 -2
- package/src/debug-logger.ts +7 -27
- package/src/guardrails/input.ts +82 -4
- package/src/index.ts +56 -16
- package/src/llm-config.ts +96 -0
- package/src/runner.ts +85 -4
- package/src/{hubtype-api-client.ts → services/hubtype-api-client.ts} +31 -83
- package/src/services/types.ts +106 -0
- package/src/tools/retrieve-knowledge.ts +1 -1
- package/src/types.ts +8 -0
- package/lib/cjs/hubtype-api-client.d.ts +0 -23
- package/lib/cjs/hubtype-api-client.js.map +0 -1
- package/lib/cjs/openai.d.ts +0 -1
- package/lib/cjs/openai.js +0 -39
- package/lib/cjs/openai.js.map +0 -1
- package/lib/esm/hubtype-api-client.d.ts +0 -23
- package/lib/esm/hubtype-api-client.js.map +0 -1
- package/lib/esm/openai.d.ts +0 -1
- package/lib/esm/openai.js +0 -39
- package/lib/esm/openai.js.map +0 -1
- package/src/openai.ts +0 -49
|
@@ -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,
|
|
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"}
|
package/lib/esm/types.d.ts
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "0.46.0-alpha.1",
|
|
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.
|
|
17
|
+
"@botonic/core": "^0.46.0-alpha.1",
|
|
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"
|
package/src/agent-builder.ts
CHANGED
|
@@ -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 {
|
|
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(
|
|
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
|
-
|
|
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
|
|
76
|
-
|
|
77
|
-
|
|
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
|
package/src/debug-logger.ts
CHANGED
|
@@ -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 {}
|
package/src/guardrails/input.ts
CHANGED
|
@@ -1,14 +1,28 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Agent,
|
|
3
3
|
type InputGuardrail,
|
|
4
|
-
|
|
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
|
-
|
|
14
|
+
export interface GuardrailTrackingContext {
|
|
15
|
+
botId: string
|
|
16
|
+
isTest: boolean
|
|
17
|
+
authToken: string
|
|
18
|
+
inferenceId: string
|
|
19
|
+
}
|
|
10
20
|
|
|
11
|
-
export function createInputGuardrail(
|
|
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
|
|
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 {
|
|
10
|
+
import {
|
|
11
|
+
DEFAULT_MAX_RETRIES,
|
|
12
|
+
DEFAULT_TIMEOUT_16_SECONDS,
|
|
13
|
+
isProd,
|
|
14
|
+
MAX_MEMORY_LENGTH,
|
|
15
|
+
} from './constants'
|
|
11
16
|
import { createDebugLogger, type DebugLogger } from './debug-logger'
|
|
12
|
-
import {
|
|
13
|
-
import { setUpOpenAI } from './openai'
|
|
17
|
+
import { LLMConfig } from './llm-config'
|
|
14
18
|
import { AIAgentRunner } from './runner'
|
|
19
|
+
import { HubtypeApiClient } from './services/hubtype-api-client'
|
|
15
20
|
import type {
|
|
16
21
|
AgenticInputMessage,
|
|
17
22
|
Context,
|
|
@@ -32,11 +37,11 @@ export default class BotonicPluginAiAgents<
|
|
|
32
37
|
private readonly messageHistoryApiVersion: MessageHistoryApiVersion
|
|
33
38
|
private readonly memory: MemoryOptions
|
|
34
39
|
private readonly logger: DebugLogger
|
|
40
|
+
private readonly timeout: number
|
|
41
|
+
private readonly maxRetries: number
|
|
35
42
|
public toolDefinitions: CustomTool<TPlugins, TExtraData>[] = []
|
|
36
43
|
|
|
37
44
|
constructor(options?: PluginAiAgentOptions<TPlugins, TExtraData>) {
|
|
38
|
-
setUpOpenAI(options?.maxRetries, options?.timeout)
|
|
39
|
-
|
|
40
45
|
if (options?.messageHistoryApiVersion === 'v1' && options?.memory) {
|
|
41
46
|
throw new Error(
|
|
42
47
|
'Cannot use memory when messageHistoryApiVersion is "v1". ' +
|
|
@@ -48,6 +53,8 @@ export default class BotonicPluginAiAgents<
|
|
|
48
53
|
this.toolDefinitions = options?.customTools || []
|
|
49
54
|
this.messageHistoryApiVersion = options?.messageHistoryApiVersion ?? 'v2'
|
|
50
55
|
this.memory = options?.memory ?? {}
|
|
56
|
+
this.timeout = options?.timeout ?? DEFAULT_TIMEOUT_16_SECONDS
|
|
57
|
+
this.maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES
|
|
51
58
|
this.logger = createDebugLogger(options?.enableDebug ?? false)
|
|
52
59
|
|
|
53
60
|
this.logger.logInitialConfig({
|
|
@@ -64,34 +71,59 @@ export default class BotonicPluginAiAgents<
|
|
|
64
71
|
}
|
|
65
72
|
|
|
66
73
|
async getInference(
|
|
67
|
-
|
|
74
|
+
botContext: BotContext<TPlugins, TExtraData>,
|
|
68
75
|
aiAgentArgs: AiAgentArgs
|
|
69
76
|
): Promise<InferenceResponse> {
|
|
70
77
|
try {
|
|
71
|
-
const authToken = isProd
|
|
78
|
+
const authToken = isProd
|
|
79
|
+
? botContext.session._access_token
|
|
80
|
+
: this.authToken
|
|
72
81
|
if (!authToken) {
|
|
73
82
|
throw new Error('Auth token is required')
|
|
74
83
|
}
|
|
75
84
|
|
|
85
|
+
const inferenceId = uuidv7()
|
|
86
|
+
|
|
87
|
+
// Create client for OpenAI/Azure OpenAI
|
|
88
|
+
const llmConfig = new LLMConfig(
|
|
89
|
+
this.maxRetries,
|
|
90
|
+
this.timeout,
|
|
91
|
+
aiAgentArgs.model,
|
|
92
|
+
aiAgentArgs.verbosity
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
// Build tools
|
|
76
96
|
const tools = this.buildTools(
|
|
77
97
|
aiAgentArgs.activeTools?.map(tool => tool.name) || []
|
|
78
98
|
)
|
|
99
|
+
|
|
100
|
+
// Build agent
|
|
79
101
|
const agent = new AIAgentBuilder<TPlugins, TExtraData>({
|
|
80
102
|
name: aiAgentArgs.name,
|
|
81
103
|
instructions: aiAgentArgs.instructions,
|
|
82
104
|
tools: tools,
|
|
83
|
-
contactInfo:
|
|
105
|
+
contactInfo: botContext.session.user.contact_info || [],
|
|
84
106
|
inputGuardrailRules: aiAgentArgs.inputGuardrailRules || [],
|
|
85
107
|
sourceIds: aiAgentArgs.sourceIds || [],
|
|
86
|
-
campaignsContext:
|
|
108
|
+
campaignsContext: botContext.input.context?.campaigns_v2,
|
|
87
109
|
logger: this.logger,
|
|
110
|
+
llmConfig,
|
|
111
|
+
guardrailTrackingContext: {
|
|
112
|
+
botId: botContext.session.bot.id,
|
|
113
|
+
isTest: botContext.session.is_test_integration,
|
|
114
|
+
authToken,
|
|
115
|
+
inferenceId,
|
|
116
|
+
},
|
|
88
117
|
}).build()
|
|
89
118
|
|
|
119
|
+
// Get messages
|
|
90
120
|
const messages = await this.getMessages(
|
|
91
|
-
|
|
121
|
+
botContext,
|
|
92
122
|
authToken,
|
|
93
123
|
MAX_MEMORY_LENGTH
|
|
94
124
|
)
|
|
125
|
+
|
|
126
|
+
// Build context
|
|
95
127
|
const context: Context<TPlugins, TExtraData> = {
|
|
96
128
|
authToken,
|
|
97
129
|
sourceIds: aiAgentArgs.sourceIds || [],
|
|
@@ -101,16 +133,23 @@ export default class BotonicPluginAiAgents<
|
|
|
101
133
|
chunksIds: [],
|
|
102
134
|
chunkTexts: [],
|
|
103
135
|
},
|
|
104
|
-
request,
|
|
136
|
+
request: botContext,
|
|
105
137
|
}
|
|
106
138
|
|
|
139
|
+
// Log agent debug info
|
|
107
140
|
this.logger.logAgentDebugInfo(
|
|
108
141
|
aiAgentArgs,
|
|
109
142
|
tools.map(t => t.name),
|
|
110
143
|
messages
|
|
111
144
|
)
|
|
112
145
|
|
|
113
|
-
|
|
146
|
+
// Run agent
|
|
147
|
+
const runner = new AIAgentRunner<TPlugins, TExtraData>(
|
|
148
|
+
agent,
|
|
149
|
+
llmConfig,
|
|
150
|
+
inferenceId,
|
|
151
|
+
this.logger
|
|
152
|
+
)
|
|
114
153
|
return await runner.run(messages, context)
|
|
115
154
|
} catch (error) {
|
|
116
155
|
console.error('error plugin returns undefined', error)
|
|
@@ -127,7 +166,7 @@ export default class BotonicPluginAiAgents<
|
|
|
127
166
|
}
|
|
128
167
|
|
|
129
168
|
private async getMessages(
|
|
130
|
-
|
|
169
|
+
botContext: BotContext,
|
|
131
170
|
authToken: string,
|
|
132
171
|
memoryLength: number
|
|
133
172
|
): Promise<AgenticInputMessage[]> {
|
|
@@ -138,11 +177,11 @@ export default class BotonicPluginAiAgents<
|
|
|
138
177
|
}
|
|
139
178
|
|
|
140
179
|
if (this.messageHistoryApiVersion === 'v1') {
|
|
141
|
-
return await hubtypeClient.getMessages(
|
|
180
|
+
return await hubtypeClient.getMessages(botContext, memoryLength)
|
|
142
181
|
}
|
|
143
182
|
|
|
144
183
|
// Default to V2
|
|
145
|
-
const result = await hubtypeClient.getMessagesV2(
|
|
184
|
+
const result = await hubtypeClient.getMessagesV2(botContext, {
|
|
146
185
|
maxMessages: this.memory.maxMessages ?? memoryLength,
|
|
147
186
|
includeToolCalls: this.memory.includeToolCalls ?? true,
|
|
148
187
|
maxFullToolResults: this.memory.maxFullToolResults ?? 1,
|
|
@@ -166,4 +205,5 @@ export default class BotonicPluginAiAgents<
|
|
|
166
205
|
}
|
|
167
206
|
}
|
|
168
207
|
|
|
208
|
+
export * from './bot-config-tools'
|
|
169
209
|
export * from './types'
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { VerbosityLevel } from '@botonic/core'
|
|
2
|
+
import {
|
|
3
|
+
type ModelProvider,
|
|
4
|
+
type ModelSettings,
|
|
5
|
+
OpenAIProvider,
|
|
6
|
+
} from '@openai/agents'
|
|
7
|
+
import OpenAI, { AzureOpenAI } from 'openai'
|
|
8
|
+
import {
|
|
9
|
+
AZURE_OPENAI_API_BASE,
|
|
10
|
+
AZURE_OPENAI_API_KEY,
|
|
11
|
+
AZURE_OPENAI_API_VERSION,
|
|
12
|
+
isProd,
|
|
13
|
+
OPENAI_API_KEY,
|
|
14
|
+
OPENAI_MODEL,
|
|
15
|
+
OPENAI_PROVIDER,
|
|
16
|
+
} from './constants'
|
|
17
|
+
|
|
18
|
+
export class LLMConfig {
|
|
19
|
+
private readonly maxRetries: number
|
|
20
|
+
private readonly timeout: number
|
|
21
|
+
public readonly modelName: string
|
|
22
|
+
public readonly modelSettings: ModelSettings
|
|
23
|
+
public readonly modelProvider: ModelProvider
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
maxRetries: number,
|
|
27
|
+
timeout: number,
|
|
28
|
+
modelName: string,
|
|
29
|
+
verbosity: VerbosityLevel
|
|
30
|
+
) {
|
|
31
|
+
this.maxRetries = maxRetries
|
|
32
|
+
this.timeout = timeout
|
|
33
|
+
this.modelName = OPENAI_PROVIDER === 'openai' ? OPENAI_MODEL : modelName
|
|
34
|
+
this.modelProvider = this.getModelProvider()
|
|
35
|
+
this.modelSettings = this.getModelSettings(modelName, verbosity)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private getModelProvider(): ModelProvider {
|
|
39
|
+
const client = this.getClient()
|
|
40
|
+
return new OpenAIProvider({
|
|
41
|
+
openAIClient: client,
|
|
42
|
+
useResponses: false,
|
|
43
|
+
})
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private getClient(): OpenAI | AzureOpenAI {
|
|
47
|
+
if (OPENAI_PROVIDER === 'openai') {
|
|
48
|
+
return this.getOpenAIClient()
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return this.getAzureClient()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private getOpenAIClient(): OpenAI {
|
|
55
|
+
return new OpenAI({
|
|
56
|
+
apiKey: OPENAI_API_KEY,
|
|
57
|
+
timeout: this.timeout,
|
|
58
|
+
maxRetries: this.maxRetries,
|
|
59
|
+
dangerouslyAllowBrowser: !isProd,
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
private getAzureClient(): AzureOpenAI {
|
|
64
|
+
return new AzureOpenAI({
|
|
65
|
+
apiKey: AZURE_OPENAI_API_KEY,
|
|
66
|
+
apiVersion: AZURE_OPENAI_API_VERSION,
|
|
67
|
+
deployment: this.modelName,
|
|
68
|
+
baseURL: AZURE_OPENAI_API_BASE,
|
|
69
|
+
timeout: this.timeout,
|
|
70
|
+
maxRetries: this.maxRetries,
|
|
71
|
+
dangerouslyAllowBrowser: !isProd,
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private getModelSettings(
|
|
76
|
+
model: string,
|
|
77
|
+
verbosity: VerbosityLevel
|
|
78
|
+
): ModelSettings {
|
|
79
|
+
if (model.includes('gpt-5')) {
|
|
80
|
+
return {
|
|
81
|
+
reasoning: { effort: 'none' },
|
|
82
|
+
temperature: 1,
|
|
83
|
+
text: { verbosity },
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (model.includes('gpt-4')) {
|
|
88
|
+
return {
|
|
89
|
+
temperature: 0,
|
|
90
|
+
text: { verbosity: 'low' },
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
throw new Error(`Unsupported model: ${model}`)
|
|
95
|
+
}
|
|
96
|
+
}
|