@hexabot-ai/api 3.3.0 → 3.3.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.
- package/dist/bindings/index.d.ts +1 -0
- package/dist/bindings/index.js +1 -0
- package/dist/bindings/index.js.map +1 -1
- package/dist/extensions/actions/ai/ai-base.action.d.ts +4 -10
- package/dist/extensions/actions/ai/ai-base.action.js +8 -162
- package/dist/extensions/actions/ai/ai-base.action.js.map +1 -1
- package/dist/extensions/actions/ai/ai-prompt.helpers.d.ts +19 -0
- package/dist/extensions/actions/ai/ai-prompt.helpers.js +179 -0
- package/dist/extensions/actions/ai/ai-prompt.helpers.js.map +1 -0
- package/dist/extensions/actions/memory/update-memory.action.d.ts +1 -1
- package/dist/extensions/index.d.ts +2 -0
- package/dist/extensions/index.js +2 -0
- package/dist/extensions/index.js.map +1 -1
- package/dist/license/types/license-quota.js +1 -1
- package/dist/license/types/license-quota.js.map +1 -1
- package/dist/static/assets/{cssMode-CVfTJuzy.js → cssMode-CibBXGrW.js} +1 -1
- package/dist/static/assets/{freemarker2-BA7GKqB7.js → freemarker2-7NvEeTKe.js} +1 -1
- package/dist/static/assets/{handlebars-BCwvKzIX.js → handlebars-BbfYBNXO.js} +1 -1
- package/dist/static/assets/{html-f6_JN1YY.js → html-DLG6BvlD.js} +1 -1
- package/dist/static/assets/{htmlMode-f4asMwRs.js → htmlMode-iXpl7d5V.js} +1 -1
- package/dist/static/assets/{index-X6nbc-0C.js → index-BPjGVz_f.js} +392 -392
- package/dist/static/assets/index-DHPA03eu.css +1 -0
- package/dist/static/assets/{javascript-DaS6vtZ9.js → javascript-BuG8vffM.js} +1 -1
- package/dist/static/assets/{jsonMode-Ct41J50t.js → jsonMode-DP5Vig9S.js} +1 -1
- package/dist/static/assets/{liquid-4giXfRN2.js → liquid-CRTQV_4V.js} +1 -1
- package/dist/static/assets/{lspLanguageFeatures-CqJlQWTJ.js → lspLanguageFeatures-BnMkhqT9.js} +1 -1
- package/dist/static/assets/{mdx-DcO2npTr.js → mdx-CQ43QJie.js} +1 -1
- package/dist/static/assets/{python-D7O1gNbd.js → python-CQ9HRuTn.js} +1 -1
- package/dist/static/assets/{razor-K5r5YQ4D.js → razor-BY29I0xJ.js} +1 -1
- package/dist/static/assets/{tsMode-CvIEOKtw.js → tsMode-DnpMBMFq.js} +1 -1
- package/dist/static/assets/{typescript-BKjAKJn9.js → typescript-BrYZsZrw.js} +1 -1
- package/dist/static/assets/{xml-BpIa2Zv1.js → xml-DbNzxxky.js} +1 -1
- package/dist/static/assets/{yaml-BJzMbZhP.js → yaml-LdVsxyvH.js} +1 -1
- package/dist/static/index.html +2 -2
- package/dist/static/locales/en/translation.json +2 -1
- package/dist/static/locales/fr/translation.json +2 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/workflow/index.d.ts +2 -0
- package/dist/workflow/index.js +2 -0
- package/dist/workflow/index.js.map +1 -1
- package/package.json +4 -4
- package/src/bindings/index.ts +2 -0
- package/src/extensions/actions/ai/ai-base.action.ts +19 -246
- package/src/extensions/actions/ai/ai-prompt.helpers.ts +299 -0
- package/src/extensions/index.ts +4 -0
- package/src/license/types/license-quota.ts +1 -1
- package/src/workflow/index.ts +4 -0
- package/dist/static/assets/index-CLS5CNoN.css +0 -1
package/dist/workflow/index.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export * from './repositories/memory-definition.repository';
|
|
|
23
23
|
export * from './repositories/memory-record.repository';
|
|
24
24
|
export * from './repositories/mcp-server.repository';
|
|
25
25
|
export * from './contexts/conversational-workflow.context';
|
|
26
|
+
export * from './contexts/workflow-runtime.context';
|
|
27
|
+
export * from './types';
|
|
26
28
|
export * from './services/workflow.service';
|
|
27
29
|
export * from './services/workflow-version.service';
|
|
28
30
|
export * from './services/workflow-run.service';
|
package/dist/workflow/index.js
CHANGED
|
@@ -39,6 +39,8 @@ __exportStar(require("./repositories/memory-definition.repository"), exports);
|
|
|
39
39
|
__exportStar(require("./repositories/memory-record.repository"), exports);
|
|
40
40
|
__exportStar(require("./repositories/mcp-server.repository"), exports);
|
|
41
41
|
__exportStar(require("./contexts/conversational-workflow.context"), exports);
|
|
42
|
+
__exportStar(require("./contexts/workflow-runtime.context"), exports);
|
|
43
|
+
__exportStar(require("./types"), exports);
|
|
42
44
|
__exportStar(require("./services/workflow.service"), exports);
|
|
43
45
|
__exportStar(require("./services/workflow-version.service"), exports);
|
|
44
46
|
__exportStar(require("./services/workflow-run.service"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workflow/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAMA,oDAAkC;AAElC,6EAA2D;AAE3D,sEAAoD;AAEpD,oEAAkD;AAElD,4EAA0D;AAE1D,wEAAsD;AAEtD,qDAAmC;AAEnC,6DAA2C;AAE3C,yDAAuC;AAEvC,8DAA4C;AAE5C,0DAAwC;AAExC,uDAAqC;AAErC,6DAA2C;AAE3C,qEAAmD;AAEnD,iEAA+C;AAE/C,sEAAoD;AAEpD,kEAAgD;AAEhD,+DAA6C;AAE7C,qEAAmD;AAEnD,6EAA2D;AAE3D,yEAAuD;AAEvD,8EAA4D;AAE5D,0EAAwD;AAExD,uEAAqD;AAErD,6EAA2D;AAE3D,8DAA4C;AAE5C,sEAAoD;AAEpD,kEAAgD;AAEhD,uEAAqD;AAErD,mEAAiD;AAEjD,4DAA0C;AAE1C,6DAA2C;AAE3C,gEAA8C;AAE9C,qEAAmD;AAEnD,uEAAqD;AAErD,2EAAyD;AAEzD,mFAAiE;AAEjE,iFAA+D;AAE/D,iFAA+D;AAE/D,sEAAoD;AAEpD,wEAAsD;AAEtD,8DAA4C"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/workflow/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAMA,oDAAkC;AAElC,6EAA2D;AAE3D,sEAAoD;AAEpD,oEAAkD;AAElD,4EAA0D;AAE1D,wEAAsD;AAEtD,qDAAmC;AAEnC,6DAA2C;AAE3C,yDAAuC;AAEvC,8DAA4C;AAE5C,0DAAwC;AAExC,uDAAqC;AAErC,6DAA2C;AAE3C,qEAAmD;AAEnD,iEAA+C;AAE/C,sEAAoD;AAEpD,kEAAgD;AAEhD,+DAA6C;AAE7C,qEAAmD;AAEnD,6EAA2D;AAE3D,yEAAuD;AAEvD,8EAA4D;AAE5D,0EAAwD;AAExD,uEAAqD;AAErD,6EAA2D;AAE3D,sEAAoD;AAEpD,0CAAwB;AAExB,8DAA4C;AAE5C,sEAAoD;AAEpD,kEAAgD;AAEhD,uEAAqD;AAErD,mEAAiD;AAEjD,4DAA0C;AAE1C,6DAA2C;AAE3C,gEAA8C;AAE9C,qEAAmD;AAEnD,uEAAqD;AAErD,2EAAyD;AAEzD,mFAAiE;AAEjE,iFAA+D;AAE/D,iFAA+D;AAE/D,sEAAoD;AAEpD,wEAAsD;AAEtD,8DAA4C"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hexabot-ai/api",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "3.3.
|
|
4
|
+
"version": "3.3.2",
|
|
5
5
|
"description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.",
|
|
6
6
|
"author": "Hexastack",
|
|
7
7
|
"license": "FCL-1.0-ALv2",
|
|
@@ -93,8 +93,8 @@
|
|
|
93
93
|
"typeorm": "^0.3.29",
|
|
94
94
|
"yaml": "^2.8.3",
|
|
95
95
|
"zod": "^4.3.6",
|
|
96
|
-
"@hexabot-ai/agentic": "3.2.
|
|
97
|
-
"@hexabot-ai/types": "3.1.
|
|
96
|
+
"@hexabot-ai/agentic": "3.2.2",
|
|
97
|
+
"@hexabot-ai/types": "3.1.2"
|
|
98
98
|
},
|
|
99
99
|
"devDependencies": {
|
|
100
100
|
"@compodoc/compodoc": "^1.2.1",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"tsconfig-paths": "^4.2.0",
|
|
144
144
|
"tsconfig-paths-jest": "^0.0.1",
|
|
145
145
|
"typescript": "^5.8.3",
|
|
146
|
-
"@hexabot-ai/frontend": "3.3.
|
|
146
|
+
"@hexabot-ai/frontend": "3.3.2"
|
|
147
147
|
},
|
|
148
148
|
"optionalDependencies": {
|
|
149
149
|
"@css-inline/css-inline-linux-arm64-musl": "^0.14.1",
|
package/src/bindings/index.ts
CHANGED
|
@@ -7,17 +7,10 @@
|
|
|
7
7
|
import { createOpenAI } from '@ai-sdk/openai';
|
|
8
8
|
import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
|
|
9
9
|
import { ProviderV2, ProviderV3 } from '@ai-sdk/provider';
|
|
10
|
-
import {
|
|
11
|
-
IncomingMessageType,
|
|
12
|
-
Message,
|
|
13
|
-
StdIncomingMessage,
|
|
14
|
-
StdOutgoingMessage,
|
|
15
|
-
Thread,
|
|
16
|
-
} from '@hexabot-ai/types';
|
|
10
|
+
import { StdIncomingMessage, StdOutgoingMessage } from '@hexabot-ai/types';
|
|
17
11
|
import {
|
|
18
12
|
LanguageModel,
|
|
19
13
|
LanguageModelUsage,
|
|
20
|
-
ModelMessage,
|
|
21
14
|
ToolSet,
|
|
22
15
|
hasToolCall,
|
|
23
16
|
stepCountIs,
|
|
@@ -31,11 +24,15 @@ import { WorkflowRuntimeContext } from '@/workflow/contexts/workflow-runtime.con
|
|
|
31
24
|
import { McpToolBindingDefinitions } from '@/workflow/types';
|
|
32
25
|
|
|
33
26
|
import {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
buildMemoryPrompt,
|
|
28
|
+
buildPrompt,
|
|
29
|
+
formatMemoryValue,
|
|
30
|
+
normalizeMessagesForModel,
|
|
31
|
+
resolveMemoryBindingSlugs,
|
|
32
|
+
resolveMessageContent,
|
|
33
|
+
type PromptPayload,
|
|
34
|
+
} from './ai-prompt.helpers';
|
|
35
|
+
import { AiCommonSettings, AiPromptInput } from './ai-schemas';
|
|
39
36
|
|
|
40
37
|
export type { AiCommonSettings, AiPromptInput } from './ai-schemas';
|
|
41
38
|
|
|
@@ -55,16 +52,6 @@ export type LanguageModelProvider =
|
|
|
55
52
|
| ProviderV3
|
|
56
53
|
| ProviderV2;
|
|
57
54
|
|
|
58
|
-
type PromptPayload =
|
|
59
|
-
| {
|
|
60
|
-
prompt: string;
|
|
61
|
-
system?: string;
|
|
62
|
-
}
|
|
63
|
-
| {
|
|
64
|
-
messages: ModelMessage[];
|
|
65
|
-
system?: string;
|
|
66
|
-
};
|
|
67
|
-
|
|
68
55
|
export abstract class AiBaseAction<
|
|
69
56
|
I,
|
|
70
57
|
O,
|
|
@@ -374,254 +361,40 @@ export abstract class AiBaseAction<
|
|
|
374
361
|
protected resolveMessageContent(
|
|
375
362
|
payload: StdOutgoingMessage | StdIncomingMessage,
|
|
376
363
|
) {
|
|
377
|
-
|
|
378
|
-
return undefined;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
const data = payload.data as Record<string, unknown>;
|
|
382
|
-
|
|
383
|
-
if (typeof data.text === 'string') {
|
|
384
|
-
return data.text;
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (typeof data.serializedText === 'string') {
|
|
388
|
-
return data.serializedText;
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
if (
|
|
392
|
-
payload.type === IncomingMessageType.location &&
|
|
393
|
-
typeof data.coordinates === 'object' &&
|
|
394
|
-
data.coordinates !== null &&
|
|
395
|
-
'lat' in data.coordinates &&
|
|
396
|
-
'lon' in data.coordinates
|
|
397
|
-
) {
|
|
398
|
-
const { lat, lon } = data.coordinates as { lat: number; lon: number };
|
|
399
|
-
|
|
400
|
-
return `location:${lat},${lon}`;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
try {
|
|
404
|
-
return JSON.stringify(data);
|
|
405
|
-
} catch {
|
|
406
|
-
return String(payload);
|
|
407
|
-
}
|
|
364
|
+
return resolveMessageContent(payload);
|
|
408
365
|
}
|
|
409
366
|
|
|
410
367
|
protected normalizeMessagesForModel(
|
|
411
|
-
messages:
|
|
368
|
+
messages: Parameters<typeof normalizeMessagesForModel>[0],
|
|
412
369
|
subscriberId: string,
|
|
413
|
-
)
|
|
414
|
-
|
|
415
|
-
role: Extract<ModelMessage['role'], 'user' | 'assistant'>;
|
|
416
|
-
content: string;
|
|
417
|
-
createdAt: Date;
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
const normalized: ConversationMessage[] = messages
|
|
421
|
-
.map((message) => {
|
|
422
|
-
const content = this.resolveMessageContent(message.message);
|
|
423
|
-
|
|
424
|
-
if (!content) {
|
|
425
|
-
return undefined;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
const role: ConversationMessage['role'] =
|
|
429
|
-
message.sender === subscriberId ? 'user' : 'assistant';
|
|
430
|
-
|
|
431
|
-
return {
|
|
432
|
-
role,
|
|
433
|
-
content,
|
|
434
|
-
createdAt: message.createdAt,
|
|
435
|
-
};
|
|
436
|
-
})
|
|
437
|
-
.filter((message): message is ConversationMessage => Boolean(message));
|
|
438
|
-
|
|
439
|
-
return normalized
|
|
440
|
-
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
|
|
441
|
-
.map(({ role, content }) => ({ role, content }));
|
|
370
|
+
) {
|
|
371
|
+
return normalizeMessagesForModel(messages, subscriberId);
|
|
442
372
|
}
|
|
443
373
|
|
|
444
|
-
protected
|
|
374
|
+
protected buildPrompt(
|
|
445
375
|
input: AiPromptInput,
|
|
446
376
|
context: C,
|
|
447
377
|
selectedMemorySlugs: string[] = [],
|
|
448
378
|
): Promise<PromptPayload> {
|
|
449
|
-
|
|
450
|
-
const memoryPrompt = this.buildMemoryPrompt(context, selectedMemorySlugs);
|
|
451
|
-
if (memoryPrompt) {
|
|
452
|
-
promptPayload.system = promptPayload.system
|
|
453
|
-
? `${promptPayload.system}\n\n${memoryPrompt}`
|
|
454
|
-
: memoryPrompt;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
if (input.input_mode === 'prompt') {
|
|
458
|
-
const prompt = input.prompt ?? DEFAULT_AI_PROMPT;
|
|
459
|
-
|
|
460
|
-
return {
|
|
461
|
-
prompt,
|
|
462
|
-
system: promptPayload.system,
|
|
463
|
-
};
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
if (input.input_mode === 'history') {
|
|
467
|
-
const messagesLimit = input.messages_limit ?? DEFAULT_AI_MESSAGES_LIMIT;
|
|
468
|
-
|
|
469
|
-
if (messagesLimit < 1) {
|
|
470
|
-
throw new Error(
|
|
471
|
-
'Input mode "history" requires a positive "messages_limit" value.',
|
|
472
|
-
);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
const subscriberId = context.initiatorId;
|
|
476
|
-
if (!subscriberId) {
|
|
477
|
-
throw new Error(
|
|
478
|
-
'A subscriber id is required to load previous messages for this action.',
|
|
479
|
-
);
|
|
480
|
-
}
|
|
481
|
-
const threadId = context.threadId;
|
|
482
|
-
if (!threadId) {
|
|
483
|
-
throw new Error(
|
|
484
|
-
'A thread id is required to load previous messages for this action.',
|
|
485
|
-
);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const messageService = context.services.message;
|
|
489
|
-
if (!messageService) {
|
|
490
|
-
throw new Error(
|
|
491
|
-
'Message service is unavailable in the workflow context.',
|
|
492
|
-
);
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
const history = await messageService.findLastMessages(
|
|
496
|
-
{ id: threadId } as Thread,
|
|
497
|
-
messagesLimit,
|
|
498
|
-
);
|
|
499
|
-
const messages = this.normalizeMessagesForModel(history, subscriberId);
|
|
500
|
-
|
|
501
|
-
return { messages, system: promptPayload.system };
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
throw new Error(
|
|
505
|
-
'An "input_mode" of either "prompt" or "history" is required to build the model request.',
|
|
506
|
-
);
|
|
379
|
+
return buildPrompt(input, context, selectedMemorySlugs);
|
|
507
380
|
}
|
|
508
381
|
|
|
509
382
|
protected resolveMemoryBindingSlugs(
|
|
510
383
|
context: C,
|
|
511
384
|
memoryBindings?: RuntimeBindings['memory'],
|
|
512
385
|
): string[] {
|
|
513
|
-
|
|
514
|
-
return [];
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
const memoryStore = context.memoryStore;
|
|
518
|
-
if (!memoryStore) {
|
|
519
|
-
return [];
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
const idToSlug = new Map<string, string>();
|
|
523
|
-
for (const [slug, definition] of memoryStore.definitionCache.entries()) {
|
|
524
|
-
if (definition.id) {
|
|
525
|
-
idToSlug.set(definition.id, slug);
|
|
526
|
-
}
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
const selectedSlugs = new Set<string>();
|
|
530
|
-
for (const [defName, binding] of Object.entries(memoryBindings)) {
|
|
531
|
-
const definitionId = binding.settings?.definition_id;
|
|
532
|
-
const slug =
|
|
533
|
-
typeof definitionId === 'string'
|
|
534
|
-
? idToSlug.get(definitionId)
|
|
535
|
-
: undefined;
|
|
536
|
-
if (!slug) {
|
|
537
|
-
throw new Error(
|
|
538
|
-
`Unable to resolve memory definition "${String(definitionId)}" from bindings.memory.${defName}.settings.definition_id.`,
|
|
539
|
-
);
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
selectedSlugs.add(slug);
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
return Array.from(selectedSlugs);
|
|
386
|
+
return resolveMemoryBindingSlugs(context, memoryBindings);
|
|
546
387
|
}
|
|
547
388
|
|
|
548
389
|
protected buildMemoryPrompt(
|
|
549
390
|
context: C,
|
|
550
391
|
selectedMemorySlugs: string[] = [],
|
|
551
392
|
): string | undefined {
|
|
552
|
-
|
|
553
|
-
return undefined;
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
const memoryStore = context.memoryStore;
|
|
557
|
-
if (!memoryStore) {
|
|
558
|
-
return undefined;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
const { definitionCache, instances } = memoryStore;
|
|
562
|
-
if (!definitionCache || definitionCache.size === 0) {
|
|
563
|
-
return undefined;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
const sections: string[] = [];
|
|
567
|
-
const selectedSlugSet = new Set(selectedMemorySlugs);
|
|
568
|
-
for (const [slug, definition] of definitionCache.entries()) {
|
|
569
|
-
if (!selectedSlugSet.has(slug)) {
|
|
570
|
-
continue;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
const instance = instances[slug];
|
|
574
|
-
if (!instance) {
|
|
575
|
-
continue;
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
const lines: string[] = [];
|
|
579
|
-
for (const field of instance.fields({ includeAdditional: true })) {
|
|
580
|
-
if (field.value === undefined) {
|
|
581
|
-
continue;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
const label = (field.title ?? field.name).trim();
|
|
585
|
-
const value = this.formatMemoryValue(field.value);
|
|
586
|
-
lines.push(`- ${label}: ${value}`);
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
if (lines.length === 0) {
|
|
590
|
-
continue;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
sections.push(`## ${definition.name}\n${lines.join('\n')}`);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
if (sections.length === 0) {
|
|
597
|
-
return undefined;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
return `# Working Memory\n${sections.join('\n\n')}`;
|
|
393
|
+
return buildMemoryPrompt(context, selectedMemorySlugs);
|
|
601
394
|
}
|
|
602
395
|
|
|
603
396
|
protected formatMemoryValue(value: unknown): string {
|
|
604
|
-
|
|
605
|
-
return value;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
609
|
-
return String(value);
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
if (value === null) {
|
|
613
|
-
return 'null';
|
|
614
|
-
}
|
|
615
|
-
|
|
616
|
-
if (value instanceof Date) {
|
|
617
|
-
return value.toISOString();
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
try {
|
|
621
|
-
return JSON.stringify(value);
|
|
622
|
-
} catch {
|
|
623
|
-
return String(value);
|
|
624
|
-
}
|
|
397
|
+
return formatMemoryValue(value);
|
|
625
398
|
}
|
|
626
399
|
|
|
627
400
|
protected safeStringify(value: unknown): string {
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Hexabot — Fair Core License (FCL-1.0-ALv2)
|
|
3
|
+
* Copyright (c) 2026 Hexastack.
|
|
4
|
+
* Full terms: see LICENSE.md.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
IncomingMessageType,
|
|
9
|
+
Message,
|
|
10
|
+
StdIncomingMessage,
|
|
11
|
+
StdOutgoingMessage,
|
|
12
|
+
Thread,
|
|
13
|
+
} from '@hexabot-ai/types';
|
|
14
|
+
import { ModelMessage } from 'ai';
|
|
15
|
+
|
|
16
|
+
import { RuntimeBindings } from '@/bindings/runtime-bindings';
|
|
17
|
+
import { WorkflowRuntimeContext } from '@/workflow/contexts/workflow-runtime.context';
|
|
18
|
+
|
|
19
|
+
import {
|
|
20
|
+
AiPromptInput,
|
|
21
|
+
DEFAULT_AI_MESSAGES_LIMIT,
|
|
22
|
+
DEFAULT_AI_PROMPT,
|
|
23
|
+
} from './ai-schemas';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Shared prompt + working-memory builders used by every AI-flavored action
|
|
27
|
+
* (`ai_agent`, `generate_*`, and the sandboxed `ai_coding_agent`) so they build
|
|
28
|
+
* the model request from a direct prompt or conversation history, and inject
|
|
29
|
+
* selected memory, in exactly the same way. `AiBaseAction` delegates to these;
|
|
30
|
+
* actions that do not extend it (the coding agent runs on a different engine)
|
|
31
|
+
* import them directly.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
export type PromptPayload =
|
|
35
|
+
| { prompt: string; system?: string }
|
|
36
|
+
| { messages: ModelMessage[]; system?: string };
|
|
37
|
+
|
|
38
|
+
export function resolveMessageContent(
|
|
39
|
+
payload: StdOutgoingMessage | StdIncomingMessage,
|
|
40
|
+
): string | undefined {
|
|
41
|
+
if (!payload) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const data = payload.data as Record<string, unknown>;
|
|
46
|
+
|
|
47
|
+
if (typeof data.text === 'string') {
|
|
48
|
+
return data.text;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (typeof data.serializedText === 'string') {
|
|
52
|
+
return data.serializedText;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
payload.type === IncomingMessageType.location &&
|
|
57
|
+
typeof data.coordinates === 'object' &&
|
|
58
|
+
data.coordinates !== null &&
|
|
59
|
+
'lat' in data.coordinates &&
|
|
60
|
+
'lon' in data.coordinates
|
|
61
|
+
) {
|
|
62
|
+
const { lat, lon } = data.coordinates as { lat: number; lon: number };
|
|
63
|
+
|
|
64
|
+
return `location:${lat},${lon}`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
return JSON.stringify(data);
|
|
69
|
+
} catch {
|
|
70
|
+
return String(payload);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function normalizeMessagesForModel(
|
|
75
|
+
messages: Message[],
|
|
76
|
+
subscriberId: string,
|
|
77
|
+
): ModelMessage[] {
|
|
78
|
+
type ConversationMessage = {
|
|
79
|
+
role: Extract<ModelMessage['role'], 'user' | 'assistant'>;
|
|
80
|
+
content: string;
|
|
81
|
+
createdAt: Date;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const normalized: ConversationMessage[] = messages
|
|
85
|
+
.map((message) => {
|
|
86
|
+
const content = resolveMessageContent(message.message);
|
|
87
|
+
|
|
88
|
+
if (!content) {
|
|
89
|
+
return undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const role: ConversationMessage['role'] =
|
|
93
|
+
message.sender === subscriberId ? 'user' : 'assistant';
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
role,
|
|
97
|
+
content,
|
|
98
|
+
createdAt: message.createdAt,
|
|
99
|
+
};
|
|
100
|
+
})
|
|
101
|
+
.filter((message): message is ConversationMessage => Boolean(message));
|
|
102
|
+
|
|
103
|
+
return normalized
|
|
104
|
+
.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())
|
|
105
|
+
.map(({ role, content }) => ({ role, content }));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function formatMemoryValue(value: unknown): string {
|
|
109
|
+
if (typeof value === 'string') {
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (typeof value === 'number' || typeof value === 'boolean') {
|
|
114
|
+
return String(value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (value === null) {
|
|
118
|
+
return 'null';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (value instanceof Date) {
|
|
122
|
+
return value.toISOString();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
return JSON.stringify(value);
|
|
127
|
+
} catch {
|
|
128
|
+
return String(value);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function buildMemoryPrompt(
|
|
133
|
+
context: WorkflowRuntimeContext,
|
|
134
|
+
selectedMemorySlugs: string[] = [],
|
|
135
|
+
): string | undefined {
|
|
136
|
+
if (selectedMemorySlugs.length === 0) {
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const memoryStore = context.memoryStore;
|
|
141
|
+
if (!memoryStore) {
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const { definitionCache, instances } = memoryStore;
|
|
146
|
+
if (!definitionCache || definitionCache.size === 0) {
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const sections: string[] = [];
|
|
151
|
+
const selectedSlugSet = new Set(selectedMemorySlugs);
|
|
152
|
+
for (const [slug, definition] of definitionCache.entries()) {
|
|
153
|
+
if (!selectedSlugSet.has(slug)) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const instance = instances[slug];
|
|
158
|
+
if (!instance) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const lines: string[] = [];
|
|
163
|
+
for (const field of instance.fields({ includeAdditional: true })) {
|
|
164
|
+
if (field.value === undefined) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
const label = (field.title ?? field.name).trim();
|
|
169
|
+
const value = formatMemoryValue(field.value);
|
|
170
|
+
lines.push(`- ${label}: ${value}`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (lines.length === 0) {
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
sections.push(`## ${definition.name}\n${lines.join('\n')}`);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (sections.length === 0) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return `# Working Memory\n${sections.join('\n\n')}`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function resolveMemoryBindingSlugs(
|
|
188
|
+
context: WorkflowRuntimeContext,
|
|
189
|
+
memoryBindings?: RuntimeBindings['memory'],
|
|
190
|
+
): string[] {
|
|
191
|
+
if (!memoryBindings || Object.keys(memoryBindings).length === 0) {
|
|
192
|
+
return [];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const memoryStore = context.memoryStore;
|
|
196
|
+
if (!memoryStore) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const idToSlug = new Map<string, string>();
|
|
201
|
+
for (const [slug, definition] of memoryStore.definitionCache.entries()) {
|
|
202
|
+
if (definition.id) {
|
|
203
|
+
idToSlug.set(definition.id, slug);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const selectedSlugs = new Set<string>();
|
|
208
|
+
for (const [defName, binding] of Object.entries(memoryBindings)) {
|
|
209
|
+
const definitionId = binding.settings?.definition_id;
|
|
210
|
+
const slug =
|
|
211
|
+
typeof definitionId === 'string' ? idToSlug.get(definitionId) : undefined;
|
|
212
|
+
if (!slug) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`Unable to resolve memory definition "${String(definitionId)}" from bindings.memory.${defName}.settings.definition_id.`,
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
selectedSlugs.add(slug);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return Array.from(selectedSlugs);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Merge an optional working-memory section into the system prompt so selected
|
|
226
|
+
* memory is presented consistently regardless of the action's input mode.
|
|
227
|
+
*/
|
|
228
|
+
export function mergeMemoryIntoSystem(
|
|
229
|
+
context: WorkflowRuntimeContext,
|
|
230
|
+
system: string | undefined,
|
|
231
|
+
selectedMemorySlugs: string[] = [],
|
|
232
|
+
): string | undefined {
|
|
233
|
+
const memoryPrompt = buildMemoryPrompt(context, selectedMemorySlugs);
|
|
234
|
+
if (!memoryPrompt) {
|
|
235
|
+
return system;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return system ? `${system}\n\n${memoryPrompt}` : memoryPrompt;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export async function buildPrompt(
|
|
242
|
+
input: AiPromptInput,
|
|
243
|
+
context: WorkflowRuntimeContext,
|
|
244
|
+
selectedMemorySlugs: string[] = [],
|
|
245
|
+
): Promise<PromptPayload> {
|
|
246
|
+
const system = mergeMemoryIntoSystem(
|
|
247
|
+
context,
|
|
248
|
+
input.system,
|
|
249
|
+
selectedMemorySlugs,
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
if (input.input_mode === 'prompt') {
|
|
253
|
+
const prompt = input.prompt ?? DEFAULT_AI_PROMPT;
|
|
254
|
+
|
|
255
|
+
return { prompt, system };
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (input.input_mode === 'history') {
|
|
259
|
+
const messagesLimit = input.messages_limit ?? DEFAULT_AI_MESSAGES_LIMIT;
|
|
260
|
+
|
|
261
|
+
if (messagesLimit < 1) {
|
|
262
|
+
throw new Error(
|
|
263
|
+
'Input mode "history" requires a positive "messages_limit" value.',
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const subscriberId = context.initiatorId;
|
|
268
|
+
if (!subscriberId) {
|
|
269
|
+
throw new Error(
|
|
270
|
+
'A subscriber id is required to load previous messages for this action.',
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
const threadId = context.threadId;
|
|
274
|
+
if (!threadId) {
|
|
275
|
+
throw new Error(
|
|
276
|
+
'A thread id is required to load previous messages for this action.',
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const messageService = context.services.message;
|
|
281
|
+
if (!messageService) {
|
|
282
|
+
throw new Error(
|
|
283
|
+
'Message service is unavailable in the workflow context.',
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const history = await messageService.findLastMessages(
|
|
288
|
+
{ id: threadId } as Thread,
|
|
289
|
+
messagesLimit,
|
|
290
|
+
);
|
|
291
|
+
const messages = normalizeMessagesForModel(history, subscriberId);
|
|
292
|
+
|
|
293
|
+
return { messages, system };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
throw new Error(
|
|
297
|
+
'An "input_mode" of either "prompt" or "history" is required to build the model request.',
|
|
298
|
+
);
|
|
299
|
+
}
|
package/src/extensions/index.ts
CHANGED
|
@@ -20,6 +20,10 @@ export * from './channels/web/inbound';
|
|
|
20
20
|
|
|
21
21
|
export * from './helpers/local-storage/index.helper';
|
|
22
22
|
|
|
23
|
+
export * from './actions/ai/ai-prompt.helpers';
|
|
24
|
+
|
|
25
|
+
export * from './actions/ai/ai-schemas';
|
|
26
|
+
|
|
23
27
|
export * from './actions/ai/model.binding';
|
|
24
28
|
|
|
25
29
|
export * from './actions/ai/tools.binding';
|