@botonic/plugin-ai-agents 0.36.0-alpha.0 → 0.36.0-alpha.2

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 (46) hide show
  1. package/lib/cjs/ai-agent-client.d.ts +9 -10
  2. package/lib/cjs/ai-agent-client.js +37 -65
  3. package/lib/cjs/ai-agent-client.js.map +1 -1
  4. package/lib/cjs/constants.d.ts +4 -0
  5. package/lib/cjs/constants.js +5 -1
  6. package/lib/cjs/constants.js.map +1 -1
  7. package/lib/cjs/hubtype-client.d.ts +7 -0
  8. package/lib/cjs/hubtype-client.js +37 -0
  9. package/lib/cjs/hubtype-client.js.map +1 -0
  10. package/lib/cjs/index.d.ts +5 -5
  11. package/lib/cjs/index.js +28 -3
  12. package/lib/cjs/index.js.map +1 -1
  13. package/lib/cjs/tools.d.ts +6 -0
  14. package/lib/cjs/tools.js +41 -0
  15. package/lib/cjs/tools.js.map +1 -0
  16. package/lib/cjs/types.d.ts +21 -23
  17. package/lib/cjs/utils.d.ts +2 -0
  18. package/lib/cjs/utils.js +22 -0
  19. package/lib/cjs/utils.js.map +1 -0
  20. package/lib/esm/ai-agent-client.d.ts +9 -10
  21. package/lib/esm/ai-agent-client.js +37 -65
  22. package/lib/esm/ai-agent-client.js.map +1 -1
  23. package/lib/esm/constants.d.ts +4 -0
  24. package/lib/esm/constants.js +4 -0
  25. package/lib/esm/constants.js.map +1 -1
  26. package/lib/esm/hubtype-client.d.ts +7 -0
  27. package/lib/esm/hubtype-client.js +33 -0
  28. package/lib/esm/hubtype-client.js.map +1 -0
  29. package/lib/esm/index.d.ts +5 -5
  30. package/lib/esm/index.js +28 -3
  31. package/lib/esm/index.js.map +1 -1
  32. package/lib/esm/tools.d.ts +6 -0
  33. package/lib/esm/tools.js +38 -0
  34. package/lib/esm/tools.js.map +1 -0
  35. package/lib/esm/types.d.ts +21 -23
  36. package/lib/esm/utils.d.ts +2 -0
  37. package/lib/esm/utils.js +18 -0
  38. package/lib/esm/utils.js.map +1 -0
  39. package/package.json +5 -2
  40. package/src/ai-agent-client.ts +56 -95
  41. package/src/constants.ts +6 -0
  42. package/src/hubtype-client.ts +43 -0
  43. package/src/index.ts +40 -7
  44. package/src/tools.ts +51 -0
  45. package/src/types.ts +27 -21
  46. package/src/utils.ts +24 -0
@@ -0,0 +1,33 @@
1
+ import { __awaiter } from "tslib";
2
+ import axios from 'axios';
3
+ import { HUBTYPE_API_URL } from './constants';
4
+ export class HubtypeClient {
5
+ constructor(authToken) {
6
+ this.authToken = authToken;
7
+ }
8
+ getMessages(request, memoryLength) {
9
+ return __awaiter(this, void 0, void 0, function* () {
10
+ const url = `${HUBTYPE_API_URL}/external/v1/ai/agent/message_history/`;
11
+ const headers = {
12
+ 'Content-Type': 'application/json',
13
+ Authorization: `Bearer ${this.authToken}`,
14
+ };
15
+ const params = {
16
+ last_message_id: request.input.message_id,
17
+ num_messages: memoryLength,
18
+ };
19
+ try {
20
+ const messages = yield axios.get(url, {
21
+ headers,
22
+ params,
23
+ });
24
+ return messages.data.messages;
25
+ }
26
+ catch (error) {
27
+ console.error(error);
28
+ throw new Error('Failed to get messages from Hubtype');
29
+ }
30
+ });
31
+ }
32
+ }
33
+ //# sourceMappingURL=hubtype-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hubtype-client.js","sourceRoot":"","sources":["../../src/hubtype-client.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAG7C,MAAM,OAAO,aAAa;IAGxB,YAAY,SAAiB;QAC3B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;IAC5B,CAAC;IAEK,WAAW,CACf,OAAmB,EACnB,YAAoB;;YAEpB,MAAM,GAAG,GAAG,GAAG,eAAe,wCAAwC,CAAA;YACtE,MAAM,OAAO,GAAG;gBACd,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,IAAI,CAAC,SAAS,EAAE;aAC1C,CAAA;YACD,MAAM,MAAM,GAAG;gBACb,eAAe,EAAE,OAAO,CAAC,KAAK,CAAC,UAAU;gBACzC,YAAY,EAAE,YAAY;aAC3B,CAAA;YAED,IAAI;gBACF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,CAC9B,GAAG,EACH;oBACE,OAAO;oBACP,MAAM;iBACP,CACF,CAAA;gBAED,OAAO,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAA;aAC9B;YAAC,OAAO,KAAK,EAAE;gBACd,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;gBACpB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;aACvD;QACH,CAAC;KAAA;CACF"}
@@ -1,9 +1,9 @@
1
1
  import { BotContext, Plugin } from '@botonic/core';
2
- import { AiAgentClient } from './ai-agent-client';
3
- import { AiAgentArgs, AiAgentResponse } from './types';
2
+ import { AgenticOutputMessage, AiAgentArgs, PluginAiAgentOptions } from './types';
4
3
  export default class BotonicPluginAiAgents implements Plugin {
5
- aiAgentClient: AiAgentClient;
6
- constructor();
4
+ private readonly authToken?;
5
+ constructor(options?: PluginAiAgentOptions);
7
6
  pre(): void;
8
- getInference(request: BotContext, aiAgentArgs: AiAgentArgs): Promise<AiAgentResponse>;
7
+ getInference(request: BotContext, aiAgentArgs: AiAgentArgs): Promise<AgenticOutputMessage | undefined>;
9
8
  }
9
+ export * from './types';
package/lib/esm/index.js CHANGED
@@ -1,16 +1,41 @@
1
1
  import { __awaiter } from "tslib";
2
2
  import { AiAgentClient } from './ai-agent-client';
3
+ import { HubtypeClient } from './hubtype-client';
4
+ import { MANDATORY_TOOLS } from './tools';
5
+ import { loadChatModel } from './utils';
6
+ const isProd = process.env.NODE_ENV === 'production';
3
7
  export default class BotonicPluginAiAgents {
4
- constructor() {
5
- this.aiAgentClient = new AiAgentClient();
8
+ constructor(options) {
9
+ this.authToken = options === null || options === void 0 ? void 0 : options.authToken;
6
10
  }
7
11
  pre() {
8
12
  return;
9
13
  }
10
14
  getInference(request, aiAgentArgs) {
11
15
  return __awaiter(this, void 0, void 0, function* () {
12
- return this.aiAgentClient.getInference(request, aiAgentArgs);
16
+ try {
17
+ const authToken = isProd ? request.session._access_token : this.authToken;
18
+ if (!authToken) {
19
+ throw new Error('Auth token is required');
20
+ }
21
+ const hubtypeClient = new HubtypeClient(authToken);
22
+ const messages = isProd
23
+ ? yield hubtypeClient.getMessages(request, 10)
24
+ : [{ role: 'user', content: request.input.data }];
25
+ const customTools = [];
26
+ const chatModel = loadChatModel('azureOpenAI');
27
+ const aiAgentClient = new AiAgentClient(aiAgentArgs, chatModel, [
28
+ ...customTools,
29
+ ...MANDATORY_TOOLS,
30
+ ]);
31
+ return yield aiAgentClient.runAgent(messages);
32
+ }
33
+ catch (error) {
34
+ console.error('error plugin returns undefined', error);
35
+ return undefined;
36
+ }
13
37
  });
14
38
  }
15
39
  }
40
+ export * from './types';
16
41
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAGjD,MAAM,CAAC,OAAO,OAAO,qBAAqB;IAGxC;QACE,IAAI,CAAC,aAAa,GAAG,IAAI,aAAa,EAAE,CAAA;IAC1C,CAAC;IAED,GAAG;QACD,OAAM;IACR,CAAC;IAEK,YAAY,CAChB,OAAmB,EACnB,WAAwB;;YAExB,OAAO,IAAI,CAAC,aAAa,CAAC,YAAY,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;QAC9D,CAAC;KAAA;CACF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAA;AAChD,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAOzC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAEvC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY,CAAA;AAEpD,MAAM,CAAC,OAAO,OAAO,qBAAqB;IAExC,YAAY,OAA8B;QACxC,IAAI,CAAC,SAAS,GAAG,OAAO,aAAP,OAAO,uBAAP,OAAO,CAAE,SAAS,CAAA;IACrC,CAAC;IAED,GAAG;QACD,OAAM;IACR,CAAC;IAEK,YAAY,CAChB,OAAmB,EACnB,WAAwB;;YAExB,IAAI;gBACF,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAA;gBACzE,IAAI,CAAC,SAAS,EAAE;oBACd,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAA;iBAC1C;gBAED,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,SAAS,CAAC,CAAA;gBAClD,MAAM,QAAQ,GAAG,MAAM;oBACrB,CAAC,CAAC,MAAM,aAAa,CAAC,WAAW,CAAC,OAAO,EAAE,EAAE,CAAC;oBAC9C,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,EAAiB,CAAC,CAAA;gBAElE,MAAM,WAAW,GAAG,EAAE,CAAA;gBACtB,MAAM,SAAS,GAAG,aAAa,CAAC,aAAa,CAAC,CAAA;gBAC9C,MAAM,aAAa,GAAG,IAAI,aAAa,CAAC,WAAW,EAAE,SAAS,EAAE;oBAC9D,GAAG,WAAW;oBACd,GAAG,eAAe;iBACnB,CAAC,CAAA;gBAEF,OAAO,MAAM,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAA;aAC9C;YAAC,OAAO,KAAK,EAAE;gBACd,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC,CAAA;gBACtD,OAAO,SAAS,CAAA;aACjB;QACH,CAAC;KAAA;CACF;AAED,cAAc,SAAS,CAAA"}
@@ -0,0 +1,6 @@
1
+ import { z } from 'zod';
2
+ export declare const handoffToHumanAgent: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>, {}, {}, null>;
3
+ export declare const outOfContext: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>, {}, {}, null>;
4
+ export declare const finishConversation: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>, {}, {}, null>;
5
+ export declare const MANDATORY_TOOLS: import("@langchain/core/tools").DynamicStructuredTool<z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>, {}, {}, null>[];
6
+ export declare const EXIT_TOOLS: string[];
@@ -0,0 +1,38 @@
1
+ import { __awaiter } from "tslib";
2
+ import { tool } from '@langchain/core/tools';
3
+ import { z } from 'zod';
4
+ export const handoffToHumanAgent = tool(() => __awaiter(void 0, void 0, void 0, function* () {
5
+ return null;
6
+ }), {
7
+ name: 'handoffToHumanAgent',
8
+ schema: z.object({}),
9
+ description: 'Use this tool strictly when the user wants to talk with a human agent.',
10
+ returnDirect: true,
11
+ });
12
+ export const outOfContext = tool(() => __awaiter(void 0, void 0, void 0, function* () {
13
+ return null;
14
+ }), {
15
+ name: 'outOfContext',
16
+ schema: z.object({}),
17
+ description: 'Use this when the user talks about something that is clearly out of your context. Example: "I want to buy a car" when you are a travel agent.',
18
+ returnDirect: true,
19
+ });
20
+ export const finishConversation = tool(() => __awaiter(void 0, void 0, void 0, function* () {
21
+ return null;
22
+ }), {
23
+ name: 'finishConversation',
24
+ schema: z.object({}),
25
+ description: 'Use this when the user wants to end the conversation.',
26
+ returnDirect: true,
27
+ });
28
+ export const MANDATORY_TOOLS = [
29
+ handoffToHumanAgent,
30
+ outOfContext,
31
+ finishConversation,
32
+ ];
33
+ export const EXIT_TOOLS = [
34
+ handoffToHumanAgent.name,
35
+ outOfContext.name,
36
+ finishConversation.name,
37
+ ];
38
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../src/tools.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAA;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,MAAM,CAAC,MAAM,mBAAmB,GAAG,IAAI,CACrC,GAAS,EAAE;IACT,OAAO,IAAI,CAAA;AACb,CAAC,CAAA,EACD;IACE,IAAI,EAAE,qBAAqB;IAC3B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACpB,WAAW,EACT,wEAAwE;IAC1E,YAAY,EAAE,IAAI;CACnB,CACF,CAAA;AAED,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,CAC9B,GAAS,EAAE;IACT,OAAO,IAAI,CAAA;AACb,CAAC,CAAA,EACD;IACE,IAAI,EAAE,cAAc;IACpB,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACpB,WAAW,EACT,+IAA+I;IACjJ,YAAY,EAAE,IAAI;CACnB,CACF,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,IAAI,CACpC,GAAS,EAAE;IACT,OAAO,IAAI,CAAA;AACb,CAAC,CAAA,EACD;IACE,IAAI,EAAE,oBAAoB;IAC1B,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;IACpB,WAAW,EAAE,uDAAuD;IACpE,YAAY,EAAE,IAAI;CACnB,CACF,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,mBAAmB;IACnB,YAAY;IACZ,kBAAkB;CACnB,CAAA;AACD,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,mBAAmB,CAAC,IAAI;IACxB,YAAY,CAAC,IAAI;IACjB,kBAAkB,CAAC,IAAI;CACxB,CAAA"}
@@ -1,31 +1,29 @@
1
+ export interface PluginAiAgentOptions {
2
+ authToken?: string;
3
+ }
1
4
  export interface AiAgentArgs {
2
5
  name: string;
3
6
  instructions: string;
4
7
  }
5
- export interface Config {
6
- headers: {
7
- Authorization: string;
8
- 'Content-Type': string;
9
- };
8
+ export interface AgenticBaseMessage {
9
+ role: 'assistant' | 'user' | 'tool' | 'exit';
10
10
  }
11
- export interface AiAgentRequestDataTest {
12
- messages: {
13
- role: MessageRole;
14
- content: string;
15
- }[];
16
- name: string;
17
- instructions: string;
11
+ export interface AssistantMessage extends AgenticBaseMessage {
12
+ role: 'assistant';
13
+ content: string;
18
14
  }
19
- export type MessageRole = 'user' | 'assistant';
20
- export interface AiAgentRequestData {
21
- message: string;
22
- memory_length: number;
23
- name: string;
24
- instructions: string;
15
+ export interface UserMessage extends AgenticBaseMessage {
16
+ role: 'user';
17
+ content: string;
18
+ }
19
+ export interface ToolMessage extends AgenticBaseMessage {
20
+ role: 'tool';
21
+ toolName: string;
22
+ toolOutput: string | null;
25
23
  }
26
- export interface AiAgentResponse {
27
- message: {
28
- role: string;
29
- content: string;
30
- };
24
+ export interface ExitMessage extends AgenticBaseMessage {
25
+ role: 'exit';
31
26
  }
27
+ export type AgenticInputMessage = AssistantMessage | UserMessage;
28
+ export type AgenticOutputMessage = AssistantMessage | ToolMessage | ExitMessage;
29
+ export type AgenticMessage = AssistantMessage | UserMessage | ToolMessage | ExitMessage;
@@ -0,0 +1,2 @@
1
+ import { AzureChatOpenAI } from '@langchain/openai';
2
+ export declare function loadChatModel(provider: 'azureOpenAI'): AzureChatOpenAI;
@@ -0,0 +1,18 @@
1
+ import { AzureChatOpenAI } from '@langchain/openai';
2
+ import { AZURE_OPENAI_API_BASE, AZURE_OPENAI_API_DEPLOYMENT_NAME, AZURE_OPENAI_API_KEY, AZURE_OPENAI_API_VERSION, } from './constants';
3
+ export function loadChatModel(provider) {
4
+ switch (provider) {
5
+ case 'azureOpenAI':
6
+ return new AzureChatOpenAI({
7
+ azureOpenAIApiVersion: AZURE_OPENAI_API_VERSION,
8
+ azureOpenAIApiKey: AZURE_OPENAI_API_KEY,
9
+ azureOpenAIApiDeploymentName: AZURE_OPENAI_API_DEPLOYMENT_NAME,
10
+ azureOpenAIEndpoint: AZURE_OPENAI_API_BASE,
11
+ temperature: 0,
12
+ // other params...
13
+ });
14
+ default:
15
+ throw new Error(`Provider ${provider} not supported`);
16
+ }
17
+ }
18
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEnD,OAAO,EACL,qBAAqB,EACrB,gCAAgC,EAChC,oBAAoB,EACpB,wBAAwB,GACzB,MAAM,aAAa,CAAA;AAEpB,MAAM,UAAU,aAAa,CAAC,QAAuB;IACnD,QAAQ,QAAQ,EAAE;QAChB,KAAK,aAAa;YAChB,OAAO,IAAI,eAAe,CAAC;gBACzB,qBAAqB,EAAE,wBAAwB;gBAC/C,iBAAiB,EAAE,oBAAoB;gBACvC,4BAA4B,EAAE,gCAAgC;gBAC9D,mBAAmB,EAAE,qBAAqB;gBAC1C,WAAW,EAAE,CAAC;gBACd,kBAAkB;aACnB,CAAC,CAAA;QACJ;YACE,MAAM,IAAI,KAAK,CAAC,YAAY,QAAQ,gBAAgB,CAAC,CAAA;KACxD;AACH,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botonic/plugin-ai-agents",
3
- "version": "0.36.0-alpha.0",
3
+ "version": "0.36.0-alpha.2",
4
4
  "main": "./lib/cjs/index.js",
5
5
  "module": "./lib/esm/index.js",
6
6
  "description": "Use AI Agents to generate your contents",
@@ -13,7 +13,10 @@
13
13
  "lint_core": "../../node_modules/.bin/eslint_d --cache --quiet 'src/**/*.ts*'"
14
14
  },
15
15
  "dependencies": {
16
- "@botonic/core": "^0.35.0",
16
+ "@botonic/core": "^0.36.0-alpha.0",
17
+ "@langchain/core": "^0.3.57",
18
+ "@langchain/langgraph": "^0.3.0",
19
+ "@langchain/openai": "^0.5.12",
17
20
  "axios": "^1.9.0",
18
21
  "uuid": "^10.0.0"
19
22
  },
@@ -1,112 +1,73 @@
1
- /* eslint-disable @typescript-eslint/naming-convention */
2
- import { BotContext, isDev } from '@botonic/core'
3
- import axios from 'axios'
1
+ import { BaseChatModel } from '@langchain/core/language_models/chat_models'
2
+ import {
3
+ AIMessage as LangchainAIMessage,
4
+ ToolMessage as LangchainToolMessage,
5
+ } from '@langchain/core/messages'
6
+ import { StructuredTool } from '@langchain/core/tools'
7
+ import { CompiledStateGraph } from '@langchain/langgraph'
8
+ import { createReactAgent } from '@langchain/langgraph/prebuilt'
4
9
 
5
- import { HUBTYPE_API_URL } from './constants'
6
- import { AiAgentError } from './errors'
10
+ import { EXIT_TOOLS } from './tools'
7
11
  import {
12
+ AgenticInputMessage,
13
+ AgenticOutputMessage,
8
14
  AiAgentArgs,
9
- AiAgentRequestData,
10
- AiAgentRequestDataTest,
11
- AiAgentResponse,
12
- Config,
15
+ AssistantMessage,
16
+ ExitMessage,
17
+ ToolMessage,
13
18
  } from './types'
14
19
 
15
20
  export class AiAgentClient {
16
- private url: string
21
+ public name: string
22
+ public instructions: string
23
+ public agent: CompiledStateGraph<any, any> // TODO: apply RunInput, RunOutput, etc.
17
24
 
18
- constructor() {
19
- this.url = `${HUBTYPE_API_URL}/external/v1/ai/agent`
20
- }
25
+ constructor(
26
+ aiAgentArgs: AiAgentArgs,
27
+ chatModel: BaseChatModel,
28
+ tools: StructuredTool[] = []
29
+ ) {
30
+ this.name = aiAgentArgs.name
31
+ this.instructions = aiAgentArgs.instructions
21
32
 
22
- async getInference(
23
- request: BotContext,
24
- aiAgentArgs: AiAgentArgs
25
- ): Promise<AiAgentResponse> {
26
- if (isDev(request.session)) {
27
- return this.agentTestInference(request, aiAgentArgs)
28
- }
29
-
30
- return this.agentInference(request, aiAgentArgs)
33
+ this.agent = createReactAgent({
34
+ llm: chatModel,
35
+ tools: tools,
36
+ prompt: this.instructions,
37
+ })
31
38
  }
32
39
 
33
- private async agentTestInference(
34
- request: BotContext,
35
- aiAgentArgs: AiAgentArgs
36
- ): Promise<AiAgentResponse> {
37
- const config = this.getConfig(request)
38
- const data = this.getDataTest(request, aiAgentArgs)
39
- const response = await axios.post<AiAgentResponse>(
40
- `${this.url}/test/`,
41
- data,
42
- config
43
- )
44
-
45
- return response.data
46
- }
47
-
48
- private async agentInference(
49
- request: BotContext,
50
- aiAgentArgs: AiAgentArgs
51
- ): Promise<AiAgentResponse> {
52
- const config = this.getConfig(request)
53
- const data = this.getData(request, aiAgentArgs)
54
- const response = await axios.post<AiAgentResponse>(
55
- `${this.url}/run/`,
56
- data,
57
- config
58
- )
59
-
60
- return response.data
61
- }
40
+ async runAgent(
41
+ _messages: AgenticInputMessage[]
42
+ ): Promise<AgenticOutputMessage> {
43
+ const response = await this.agent.invoke({
44
+ messages: _messages.map((message: AgenticInputMessage) => ({
45
+ role: message.role,
46
+ content: message.content,
47
+ })),
48
+ })
49
+ const lastMessage = response.messages.at(-1)
62
50
 
63
- private getConfig(request: BotContext): Config {
64
- return {
65
- headers: {
66
- Authorization: `Bearer ${request.session._access_token}`,
67
- 'Content-Type': 'application/json',
68
- },
51
+ if (lastMessage instanceof LangchainToolMessage) {
52
+ if (lastMessage.name && EXIT_TOOLS.includes(lastMessage.name)) {
53
+ return {
54
+ role: 'exit',
55
+ } as ExitMessage
56
+ }
57
+ return {
58
+ role: 'tool',
59
+ toolName: lastMessage.name,
60
+ toolOutput: lastMessage.content,
61
+ } as ToolMessage
69
62
  }
70
- }
71
-
72
- private getData(
73
- request: BotContext,
74
- aiAgentArgs: AiAgentArgs
75
- ): AiAgentRequestData {
76
- if (!request.input.data) {
77
- throw new AiAgentError('No input data provided')
78
- }
79
-
80
- return {
81
- message: request.input.message_id,
82
- memory_length: 10,
83
- name: aiAgentArgs.name,
84
- instructions: aiAgentArgs.instructions,
85
- }
86
- }
87
63
 
88
- private getDataTest(
89
- request: BotContext,
90
- aiAgentArgs: AiAgentArgs
91
- ): AiAgentRequestDataTest {
92
- if (!request.input.data) {
93
- throw new AiAgentError('No input data provided')
64
+ if (lastMessage instanceof LangchainAIMessage) {
65
+ return {
66
+ role: 'assistant',
67
+ content: lastMessage.content,
68
+ } as AssistantMessage
94
69
  }
95
70
 
96
- // TODO: We can get messages from localStorage in Dev mode and transform them to the correct format {role: MessageRole, content: string}
97
- // if (isDev(request.session)) {
98
- // const messages = localStorage.getItem('botonicState').
99
- // }
100
-
101
- return {
102
- messages: [
103
- {
104
- role: 'user',
105
- content: request.input.data,
106
- },
107
- ],
108
- name: aiAgentArgs.name,
109
- instructions: aiAgentArgs.instructions,
110
- }
71
+ throw new Error('Last message is not a valid message')
111
72
  }
112
73
  }
package/src/constants.ts CHANGED
@@ -1,2 +1,8 @@
1
1
  export const HUBTYPE_API_URL =
2
2
  process.env.HUBTYPE_API_URL || 'https://api.hubtype.com'
3
+
4
+ export const AZURE_OPENAI_API_KEY = process.env.AZURE_OPENAI_API_KEY
5
+ export const AZURE_OPENAI_API_DEPLOYMENT_NAME =
6
+ process.env.AZURE_OPENAI_API_DEPLOYMENT_NAME
7
+ export const AZURE_OPENAI_API_VERSION = process.env.AZURE_OPENAI_API_VERSION
8
+ export const AZURE_OPENAI_API_BASE = process.env.AZURE_OPENAI_API_BASE
@@ -0,0 +1,43 @@
1
+ import { BotContext } from '@botonic/core'
2
+ import axios from 'axios'
3
+
4
+ import { HUBTYPE_API_URL } from './constants'
5
+ import { AgenticInputMessage } from './types'
6
+
7
+ export class HubtypeClient {
8
+ private readonly authToken: string
9
+
10
+ constructor(authToken: string) {
11
+ this.authToken = authToken
12
+ }
13
+
14
+ async getMessages(
15
+ request: BotContext,
16
+ memoryLength: number
17
+ ): Promise<AgenticInputMessage[]> {
18
+ const url = `${HUBTYPE_API_URL}/external/v1/ai/agent/message_history/`
19
+ const headers = {
20
+ 'Content-Type': 'application/json',
21
+ Authorization: `Bearer ${this.authToken}`,
22
+ }
23
+ const params = {
24
+ last_message_id: request.input.message_id,
25
+ num_messages: memoryLength,
26
+ }
27
+
28
+ try {
29
+ const messages = await axios.get<{ messages: AgenticInputMessage[] }>(
30
+ url,
31
+ {
32
+ headers,
33
+ params,
34
+ }
35
+ )
36
+
37
+ return messages.data.messages
38
+ } catch (error) {
39
+ console.error(error)
40
+ throw new Error('Failed to get messages from Hubtype')
41
+ }
42
+ }
43
+ }
package/src/index.ts CHANGED
@@ -1,13 +1,22 @@
1
1
  import { BotContext, Plugin } from '@botonic/core'
2
2
 
3
3
  import { AiAgentClient } from './ai-agent-client'
4
- import { AiAgentArgs, AiAgentResponse } from './types'
4
+ import { HubtypeClient } from './hubtype-client'
5
+ import { MANDATORY_TOOLS } from './tools'
6
+ import {
7
+ AgenticOutputMessage,
8
+ AiAgentArgs,
9
+ PluginAiAgentOptions,
10
+ UserMessage,
11
+ } from './types'
12
+ import { loadChatModel } from './utils'
5
13
 
6
- export default class BotonicPluginAiAgents implements Plugin {
7
- public aiAgentClient: AiAgentClient
14
+ const isProd = process.env.NODE_ENV === 'production'
8
15
 
9
- constructor() {
10
- this.aiAgentClient = new AiAgentClient()
16
+ export default class BotonicPluginAiAgents implements Plugin {
17
+ private readonly authToken?: string
18
+ constructor(options?: PluginAiAgentOptions) {
19
+ this.authToken = options?.authToken
11
20
  }
12
21
 
13
22
  pre(): void {
@@ -17,7 +26,31 @@ export default class BotonicPluginAiAgents implements Plugin {
17
26
  async getInference(
18
27
  request: BotContext,
19
28
  aiAgentArgs: AiAgentArgs
20
- ): Promise<AiAgentResponse> {
21
- return this.aiAgentClient.getInference(request, aiAgentArgs)
29
+ ): Promise<AgenticOutputMessage | undefined> {
30
+ try {
31
+ const authToken = isProd ? request.session._access_token : this.authToken
32
+ if (!authToken) {
33
+ throw new Error('Auth token is required')
34
+ }
35
+
36
+ const hubtypeClient = new HubtypeClient(authToken)
37
+ const messages = isProd
38
+ ? await hubtypeClient.getMessages(request, 10)
39
+ : [{ role: 'user', content: request.input.data } as UserMessage]
40
+
41
+ const customTools = []
42
+ const chatModel = loadChatModel('azureOpenAI')
43
+ const aiAgentClient = new AiAgentClient(aiAgentArgs, chatModel, [
44
+ ...customTools,
45
+ ...MANDATORY_TOOLS,
46
+ ])
47
+
48
+ return await aiAgentClient.runAgent(messages)
49
+ } catch (error) {
50
+ console.error('error plugin returns undefined', error)
51
+ return undefined
52
+ }
22
53
  }
23
54
  }
55
+
56
+ export * from './types'
package/src/tools.ts ADDED
@@ -0,0 +1,51 @@
1
+ import { tool } from '@langchain/core/tools'
2
+ import { z } from 'zod'
3
+
4
+ export const handoffToHumanAgent = tool(
5
+ async () => {
6
+ return null
7
+ },
8
+ {
9
+ name: 'handoffToHumanAgent',
10
+ schema: z.object({}),
11
+ description:
12
+ 'Use this tool strictly when the user wants to talk with a human agent.',
13
+ returnDirect: true,
14
+ }
15
+ )
16
+
17
+ export const outOfContext = tool(
18
+ async () => {
19
+ return null
20
+ },
21
+ {
22
+ name: 'outOfContext',
23
+ schema: z.object({}),
24
+ description:
25
+ 'Use this when the user talks about something that is clearly out of your context. Example: "I want to buy a car" when you are a travel agent.',
26
+ returnDirect: true,
27
+ }
28
+ )
29
+
30
+ export const finishConversation = tool(
31
+ async () => {
32
+ return null
33
+ },
34
+ {
35
+ name: 'finishConversation',
36
+ schema: z.object({}),
37
+ description: 'Use this when the user wants to end the conversation.',
38
+ returnDirect: true,
39
+ }
40
+ )
41
+
42
+ export const MANDATORY_TOOLS = [
43
+ handoffToHumanAgent,
44
+ outOfContext,
45
+ finishConversation,
46
+ ]
47
+ export const EXIT_TOOLS = [
48
+ handoffToHumanAgent.name,
49
+ outOfContext.name,
50
+ finishConversation.name,
51
+ ]
package/src/types.ts CHANGED
@@ -1,33 +1,39 @@
1
- /* eslint-disable @typescript-eslint/naming-convention */
1
+ export interface PluginAiAgentOptions {
2
+ authToken?: string
3
+ }
2
4
  export interface AiAgentArgs {
3
5
  name: string
4
6
  instructions: string
5
7
  }
6
- export interface Config {
7
- headers: {
8
- Authorization: string
9
- 'Content-Type': string
10
- }
8
+
9
+ export interface AgenticBaseMessage {
10
+ role: 'assistant' | 'user' | 'tool' | 'exit'
11
11
  }
12
12
 
13
- export interface AiAgentRequestDataTest {
14
- messages: {
15
- role: MessageRole
16
- content: string
17
- }[]
18
- name: string
19
- instructions: string
13
+ export interface AssistantMessage extends AgenticBaseMessage {
14
+ role: 'assistant'
15
+ content: string
20
16
  }
21
17
 
22
- export type MessageRole = 'user' | 'assistant'
18
+ export interface UserMessage extends AgenticBaseMessage {
19
+ role: 'user'
20
+ content: string
21
+ }
23
22
 
24
- export interface AiAgentRequestData {
25
- message: string
26
- memory_length: number
27
- name: string
28
- instructions: string
23
+ export interface ToolMessage extends AgenticBaseMessage {
24
+ role: 'tool'
25
+ toolName: string
26
+ toolOutput: string | null
29
27
  }
30
28
 
31
- export interface AiAgentResponse {
32
- message: { role: string; content: string }
29
+ export interface ExitMessage extends AgenticBaseMessage {
30
+ role: 'exit'
33
31
  }
32
+
33
+ export type AgenticInputMessage = AssistantMessage | UserMessage
34
+ export type AgenticOutputMessage = AssistantMessage | ToolMessage | ExitMessage
35
+ export type AgenticMessage =
36
+ | AssistantMessage
37
+ | UserMessage
38
+ | ToolMessage
39
+ | ExitMessage