@hazeljs/messaging 0.2.0-beta.30
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/LICENSE +192 -0
- package/README.md +186 -0
- package/dist/adapters/telegram.adapter.d.ts +24 -0
- package/dist/adapters/telegram.adapter.d.ts.map +1 -0
- package/dist/adapters/telegram.adapter.js +70 -0
- package/dist/adapters/telegram.adapter.js.map +1 -0
- package/dist/adapters/telegram.adapter.test.d.ts +2 -0
- package/dist/adapters/telegram.adapter.test.d.ts.map +1 -0
- package/dist/adapters/telegram.adapter.test.js +110 -0
- package/dist/adapters/telegram.adapter.test.js.map +1 -0
- package/dist/adapters/viber.adapter.d.ts +29 -0
- package/dist/adapters/viber.adapter.d.ts.map +1 -0
- package/dist/adapters/viber.adapter.js +98 -0
- package/dist/adapters/viber.adapter.js.map +1 -0
- package/dist/adapters/viber.adapter.test.d.ts +2 -0
- package/dist/adapters/viber.adapter.test.d.ts.map +1 -0
- package/dist/adapters/viber.adapter.test.js +73 -0
- package/dist/adapters/viber.adapter.test.js.map +1 -0
- package/dist/adapters/whatsapp.adapter.d.ts +26 -0
- package/dist/adapters/whatsapp.adapter.d.ts.map +1 -0
- package/dist/adapters/whatsapp.adapter.js +87 -0
- package/dist/adapters/whatsapp.adapter.js.map +1 -0
- package/dist/adapters/whatsapp.adapter.test.d.ts +2 -0
- package/dist/adapters/whatsapp.adapter.test.d.ts.map +1 -0
- package/dist/adapters/whatsapp.adapter.test.js +198 -0
- package/dist/adapters/whatsapp.adapter.test.js.map +1 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/messaging-kafka.bootstrap.d.ts +14 -0
- package/dist/messaging-kafka.bootstrap.d.ts.map +1 -0
- package/dist/messaging-kafka.bootstrap.js +42 -0
- package/dist/messaging-kafka.bootstrap.js.map +1 -0
- package/dist/messaging-kafka.bootstrap.test.d.ts +2 -0
- package/dist/messaging-kafka.bootstrap.test.d.ts.map +1 -0
- package/dist/messaging-kafka.bootstrap.test.js +34 -0
- package/dist/messaging-kafka.bootstrap.test.js.map +1 -0
- package/dist/messaging-kafka.consumer.d.ts +10 -0
- package/dist/messaging-kafka.consumer.d.ts.map +1 -0
- package/dist/messaging-kafka.consumer.js +80 -0
- package/dist/messaging-kafka.consumer.js.map +1 -0
- package/dist/messaging-kafka.consumer.test.d.ts +2 -0
- package/dist/messaging-kafka.consumer.test.d.ts.map +1 -0
- package/dist/messaging-kafka.consumer.test.js +129 -0
- package/dist/messaging-kafka.consumer.test.js.map +1 -0
- package/dist/messaging-kafka.types.d.ts +10 -0
- package/dist/messaging-kafka.types.d.ts.map +1 -0
- package/dist/messaging-kafka.types.js +5 -0
- package/dist/messaging-kafka.types.js.map +1 -0
- package/dist/messaging-kafka.types.test.d.ts +2 -0
- package/dist/messaging-kafka.types.test.d.ts.map +1 -0
- package/dist/messaging-kafka.types.test.js +12 -0
- package/dist/messaging-kafka.types.test.js.map +1 -0
- package/dist/messaging.controller.d.ts +27 -0
- package/dist/messaging.controller.d.ts.map +1 -0
- package/dist/messaging.controller.js +151 -0
- package/dist/messaging.controller.js.map +1 -0
- package/dist/messaging.controller.test.d.ts +2 -0
- package/dist/messaging.controller.test.d.ts.map +1 -0
- package/dist/messaging.controller.test.js +184 -0
- package/dist/messaging.controller.test.js.map +1 -0
- package/dist/messaging.module.d.ts +69 -0
- package/dist/messaging.module.d.ts.map +1 -0
- package/dist/messaging.module.js +152 -0
- package/dist/messaging.module.js.map +1 -0
- package/dist/messaging.module.test.d.ts +2 -0
- package/dist/messaging.module.test.d.ts.map +1 -0
- package/dist/messaging.module.test.js +97 -0
- package/dist/messaging.module.test.js.map +1 -0
- package/dist/messaging.service.d.ts +42 -0
- package/dist/messaging.service.d.ts.map +1 -0
- package/dist/messaging.service.js +109 -0
- package/dist/messaging.service.js.map +1 -0
- package/dist/messaging.service.test.d.ts +2 -0
- package/dist/messaging.service.test.d.ts.map +1 -0
- package/dist/messaging.service.test.js +163 -0
- package/dist/messaging.service.test.js.map +1 -0
- package/dist/store/conversation-context.interface.d.ts +17 -0
- package/dist/store/conversation-context.interface.d.ts.map +1 -0
- package/dist/store/conversation-context.interface.js +7 -0
- package/dist/store/conversation-context.interface.js.map +1 -0
- package/dist/store/memory-conversation-context.d.ts +11 -0
- package/dist/store/memory-conversation-context.d.ts.map +1 -0
- package/dist/store/memory-conversation-context.js +22 -0
- package/dist/store/memory-conversation-context.js.map +1 -0
- package/dist/store/memory-conversation-context.test.d.ts +2 -0
- package/dist/store/memory-conversation-context.test.d.ts.map +1 -0
- package/dist/store/memory-conversation-context.test.js +69 -0
- package/dist/store/memory-conversation-context.test.js.map +1 -0
- package/dist/store/redis-conversation-context.d.ts +24 -0
- package/dist/store/redis-conversation-context.d.ts.map +1 -0
- package/dist/store/redis-conversation-context.js +37 -0
- package/dist/store/redis-conversation-context.js.map +1 -0
- package/dist/store/redis-conversation-context.test.d.ts +2 -0
- package/dist/store/redis-conversation-context.test.d.ts.map +1 -0
- package/dist/store/redis-conversation-context.test.js +85 -0
- package/dist/store/redis-conversation-context.test.js.map +1 -0
- package/dist/types/message.types.d.ts +55 -0
- package/dist/types/message.types.d.ts.map +1 -0
- package/dist/types/message.types.js +7 -0
- package/dist/types/message.types.js.map +1 -0
- package/dist/types/response-handler.types.d.ts +47 -0
- package/dist/types/response-handler.types.d.ts.map +1 -0
- package/dist/types/response-handler.types.js +3 -0
- package/dist/types/response-handler.types.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.MessagingService = void 0;
|
|
13
|
+
/**
|
|
14
|
+
* Messaging Service - LLM/Agent-powered bot response generation
|
|
15
|
+
* Uses IConversationContextStore (memory or Redis) for horizontally scalable context
|
|
16
|
+
* Supports: plain LLM, RAG-augmented, Agent (tools + RAG like CSR), custom handler
|
|
17
|
+
*/
|
|
18
|
+
const core_1 = require("@hazeljs/core");
|
|
19
|
+
const memory_conversation_context_1 = require("./store/memory-conversation-context");
|
|
20
|
+
const DEFAULT_SYSTEM_PROMPT = `You are a helpful, friendly assistant in a messaging app.
|
|
21
|
+
Keep responses concise and conversational. Adapt your tone to be natural for chat.`;
|
|
22
|
+
const RAG_CONTEXT_PREFIX = `Use the following context from the knowledge base to answer.
|
|
23
|
+
If the context doesn't contain relevant information, say so. Cite sources when helpful.\n\n---\n`;
|
|
24
|
+
let MessagingService = class MessagingService {
|
|
25
|
+
constructor(options = {}, contextStore) {
|
|
26
|
+
this.options = {
|
|
27
|
+
systemPrompt: DEFAULT_SYSTEM_PROMPT,
|
|
28
|
+
model: 'gpt-4o-mini',
|
|
29
|
+
temperature: 0.7,
|
|
30
|
+
maxTokens: 500,
|
|
31
|
+
maxContextTurns: 10,
|
|
32
|
+
ragTopK: 5,
|
|
33
|
+
ragMinScore: 0.5,
|
|
34
|
+
...options,
|
|
35
|
+
};
|
|
36
|
+
this.store = contextStore ?? options.contextStore ?? new memory_conversation_context_1.MemoryConversationContextStore();
|
|
37
|
+
}
|
|
38
|
+
/** Generate bot response for an incoming message */
|
|
39
|
+
async handleMessage(message) {
|
|
40
|
+
const store = this.store;
|
|
41
|
+
const sessionId = message.sessionId ?? `default:${message.channel}:${message.userId}`;
|
|
42
|
+
if (this.options.customHandler) {
|
|
43
|
+
const reply = await Promise.resolve(this.options.customHandler(message));
|
|
44
|
+
await store.appendTurn(sessionId, { role: 'user', content: message.text }, this.options.maxContextTurns);
|
|
45
|
+
if (reply) {
|
|
46
|
+
await store.appendTurn(sessionId, { role: 'assistant', content: reply }, this.options.maxContextTurns);
|
|
47
|
+
}
|
|
48
|
+
return reply;
|
|
49
|
+
}
|
|
50
|
+
if (this.options.agentHandler) {
|
|
51
|
+
await store.appendTurn(sessionId, { role: 'user', content: message.text }, this.options.maxContextTurns);
|
|
52
|
+
const turns = await store.getTurns(sessionId);
|
|
53
|
+
const result = await this.options.agentHandler({
|
|
54
|
+
message,
|
|
55
|
+
sessionId,
|
|
56
|
+
conversationTurns: turns,
|
|
57
|
+
});
|
|
58
|
+
const reply = typeof result === 'string' ? result : result.response;
|
|
59
|
+
if (reply) {
|
|
60
|
+
await store.appendTurn(sessionId, { role: 'assistant', content: reply }, this.options.maxContextTurns);
|
|
61
|
+
}
|
|
62
|
+
return reply;
|
|
63
|
+
}
|
|
64
|
+
const provider = this.options.aiProvider;
|
|
65
|
+
if (!provider) {
|
|
66
|
+
throw new Error('MessagingService requires aiProvider, agentHandler, or customHandler. Configure via MessagingModule.forRoot().');
|
|
67
|
+
}
|
|
68
|
+
await store.appendTurn(sessionId, { role: 'user', content: message.text }, this.options.maxContextTurns);
|
|
69
|
+
const turns = await store.getTurns(sessionId);
|
|
70
|
+
let systemPrompt = this.options.systemPrompt;
|
|
71
|
+
if (this.options.ragService) {
|
|
72
|
+
const results = await this.options.ragService.search(message.text, {
|
|
73
|
+
topK: this.options.ragTopK,
|
|
74
|
+
minScore: this.options.ragMinScore,
|
|
75
|
+
});
|
|
76
|
+
if (results.length > 0) {
|
|
77
|
+
const context = results.map((r, i) => `[${i + 1}] ${r.content}`).join('\n\n');
|
|
78
|
+
systemPrompt = `${systemPrompt}\n\n${RAG_CONTEXT_PREFIX}Context:\n${context}\n---\n`;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const messages = [
|
|
82
|
+
{ role: 'system', content: systemPrompt },
|
|
83
|
+
...turns.map((t) => ({ role: t.role, content: t.content })),
|
|
84
|
+
];
|
|
85
|
+
const response = await provider.complete({
|
|
86
|
+
messages,
|
|
87
|
+
model: this.options.model,
|
|
88
|
+
temperature: this.options.temperature,
|
|
89
|
+
maxTokens: this.options.maxTokens,
|
|
90
|
+
});
|
|
91
|
+
const reply = response.content?.trim() ?? 'I could not generate a response.';
|
|
92
|
+
await store.appendTurn(sessionId, { role: 'assistant', content: reply }, this.options.maxContextTurns);
|
|
93
|
+
return reply;
|
|
94
|
+
}
|
|
95
|
+
/** Clear conversation history for a session */
|
|
96
|
+
async clearSession(sessionId) {
|
|
97
|
+
await this.store.clearSession(sessionId);
|
|
98
|
+
}
|
|
99
|
+
/** Get current options */
|
|
100
|
+
getOptions() {
|
|
101
|
+
return { ...this.options };
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
exports.MessagingService = MessagingService;
|
|
105
|
+
exports.MessagingService = MessagingService = __decorate([
|
|
106
|
+
(0, core_1.Injectable)(),
|
|
107
|
+
__metadata("design:paramtypes", [Object, Object])
|
|
108
|
+
], MessagingService);
|
|
109
|
+
//# sourceMappingURL=messaging.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging.service.js","sourceRoot":"","sources":["../src/messaging.service.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA;;;;GAIG;AACH,wCAA2C;AAK3C,qFAAqF;AAGrF,MAAM,qBAAqB,GAAG;mFACqD,CAAC;AAEpF,MAAM,kBAAkB,GAAG;iGACsE,CAAC;AA8B3F,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAc3B,YAAY,UAAmC,EAAE,EAAE,YAAwC;QACzF,IAAI,CAAC,OAAO,GAAG;YACb,YAAY,EAAE,qBAAqB;YACnC,KAAK,EAAE,aAAa;YACpB,WAAW,EAAE,GAAG;YAChB,SAAS,EAAE,GAAG;YACd,eAAe,EAAE,EAAE;YACnB,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,GAAG;YAChB,GAAG,OAAO;SACX,CAAC;QACF,IAAI,CAAC,KAAK,GAAG,YAAY,IAAI,OAAO,CAAC,YAAY,IAAI,IAAI,4DAA8B,EAAE,CAAC;IAC5F,CAAC;IAED,oDAAoD;IACpD,KAAK,CAAC,aAAa,CAAC,OAAwB;QAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,WAAW,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QAEtF,IAAI,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC/B,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;YACzE,MAAM,KAAK,CAAC,UAAU,CACpB,SAAS,EACT,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,EACvC,IAAI,CAAC,OAAO,CAAC,eAAe,CAC7B,CAAC;YACF,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,KAAK,CAAC,UAAU,CACpB,SAAS,EACT,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,EACrC,IAAI,CAAC,OAAO,CAAC,eAAe,CAC7B,CAAC;YACJ,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC;YAC9B,MAAM,KAAK,CAAC,UAAU,CACpB,SAAS,EACT,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,EACvC,IAAI,CAAC,OAAO,CAAC,eAAe,CAC7B,CAAC;YACF,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;YAC9C,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC;gBAC7C,OAAO;gBACP,SAAS;gBACT,iBAAiB,EAAE,KAAK;aACzB,CAAC,CAAC;YACH,MAAM,KAAK,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC;YACpE,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,KAAK,CAAC,UAAU,CACpB,SAAS,EACT,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,EACrC,IAAI,CAAC,OAAO,CAAC,eAAe,CAC7B,CAAC;YACJ,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;QACzC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CACb,gHAAgH,CACjH,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,CAAC,UAAU,CACpB,SAAS,EACT,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,EACvC,IAAI,CAAC,OAAO,CAAC,eAAe,CAC7B,CAAC;QACF,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAE9C,IAAI,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC;QAE7C,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YAC5B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE;gBACjE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,OAAO;gBAC1B,QAAQ,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;aACK,CAAC,CAAC;YAC3C,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC9E,YAAY,GAAG,GAAG,YAAY,OAAO,kBAAkB,aAAa,OAAO,SAAS,CAAC;YACvF,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,GAAgB;YAC5B,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,YAAY,EAAE;YACzC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC;SAC5D,CAAC;QAEF,MAAM,QAAQ,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC;YACvC,QAAQ;YACR,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK;YACzB,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;YACrC,SAAS,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS;SAClC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,kCAAkC,CAAC;QAE7E,MAAM,KAAK,CAAC,UAAU,CACpB,SAAS,EACT,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,EACrC,IAAI,CAAC,OAAO,CAAC,eAAe,CAC7B,CAAC;QAEF,OAAO,KAAK,CAAC;IACf,CAAC;IAED,+CAA+C;IAC/C,KAAK,CAAC,YAAY,CAAC,SAAiB;QAClC,MAAM,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;IAC3C,CAAC;IAED,0BAA0B;IAC1B,UAAU;QACR,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;IAC7B,CAAC;CACF,CAAA;AApIY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,iBAAU,GAAE;;GACA,gBAAgB,CAoI5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging.service.test.d.ts","sourceRoot":"","sources":["../src/messaging.service.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* MessagingService tests
|
|
5
|
+
*/
|
|
6
|
+
const messaging_service_1 = require("./messaging.service");
|
|
7
|
+
const memory_conversation_context_1 = require("./store/memory-conversation-context");
|
|
8
|
+
const createMockMessage = (overrides = {}) => ({
|
|
9
|
+
id: 'msg-1',
|
|
10
|
+
channel: 'telegram',
|
|
11
|
+
conversationId: 'conv-123',
|
|
12
|
+
userId: 'user-456',
|
|
13
|
+
text: 'Hello',
|
|
14
|
+
timestamp: new Date(),
|
|
15
|
+
...overrides,
|
|
16
|
+
});
|
|
17
|
+
describe('MessagingService', () => {
|
|
18
|
+
describe('customHandler', () => {
|
|
19
|
+
it('calls customHandler and returns response', async () => {
|
|
20
|
+
const customHandler = jest.fn().mockResolvedValue('Custom reply');
|
|
21
|
+
const service = new messaging_service_1.MessagingService({
|
|
22
|
+
customHandler,
|
|
23
|
+
});
|
|
24
|
+
const msg = createMockMessage({ text: 'Hi' });
|
|
25
|
+
const reply = await service.handleMessage(msg);
|
|
26
|
+
expect(customHandler).toHaveBeenCalledWith(msg);
|
|
27
|
+
expect(reply).toBe('Custom reply');
|
|
28
|
+
});
|
|
29
|
+
it('appends user and assistant turns to store', async () => {
|
|
30
|
+
const store = new memory_conversation_context_1.MemoryConversationContextStore();
|
|
31
|
+
const customHandler = jest.fn().mockResolvedValue('Echo');
|
|
32
|
+
const service = new messaging_service_1.MessagingService({ customHandler }, store);
|
|
33
|
+
await service.handleMessage(createMockMessage({ text: 'Hello' }));
|
|
34
|
+
const turns = await store.getTurns('default:telegram:user-456');
|
|
35
|
+
expect(turns).toHaveLength(2);
|
|
36
|
+
expect(turns[0]).toEqual({ role: 'user', content: 'Hello' });
|
|
37
|
+
expect(turns[1]).toEqual({ role: 'assistant', content: 'Echo' });
|
|
38
|
+
});
|
|
39
|
+
it('uses sessionId from message when provided', async () => {
|
|
40
|
+
const store = new memory_conversation_context_1.MemoryConversationContextStore();
|
|
41
|
+
const customHandler = jest.fn().mockResolvedValue('OK');
|
|
42
|
+
const service = new messaging_service_1.MessagingService({ customHandler }, store);
|
|
43
|
+
await service.handleMessage(createMockMessage({ sessionId: 'custom-session', text: 'Hi' }));
|
|
44
|
+
const turns = await store.getTurns('custom-session');
|
|
45
|
+
expect(turns).toHaveLength(2);
|
|
46
|
+
});
|
|
47
|
+
it('does not append assistant turn when reply is empty', async () => {
|
|
48
|
+
const store = new memory_conversation_context_1.MemoryConversationContextStore();
|
|
49
|
+
const customHandler = jest.fn().mockResolvedValue('');
|
|
50
|
+
const service = new messaging_service_1.MessagingService({ customHandler }, store);
|
|
51
|
+
const reply = await service.handleMessage(createMockMessage({ text: 'Hi' }));
|
|
52
|
+
expect(reply).toBe('');
|
|
53
|
+
const turns = await store.getTurns('default:telegram:user-456');
|
|
54
|
+
expect(turns).toHaveLength(1);
|
|
55
|
+
expect(turns[0].role).toBe('user');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
describe('agentHandler', () => {
|
|
59
|
+
it('calls agentHandler and returns string result', async () => {
|
|
60
|
+
const agentHandler = jest.fn().mockResolvedValue('Agent says hi');
|
|
61
|
+
const store = new memory_conversation_context_1.MemoryConversationContextStore();
|
|
62
|
+
const service = new messaging_service_1.MessagingService({ agentHandler }, store);
|
|
63
|
+
const msg = createMockMessage({ text: 'Help' });
|
|
64
|
+
const reply = await service.handleMessage(msg);
|
|
65
|
+
expect(agentHandler).toHaveBeenCalledWith(expect.objectContaining({ message: msg, sessionId: expect.any(String) }));
|
|
66
|
+
expect(reply).toBe('Agent says hi');
|
|
67
|
+
});
|
|
68
|
+
it('handles AgentHandlerResult with response property', async () => {
|
|
69
|
+
const agentHandler = jest.fn().mockResolvedValue({
|
|
70
|
+
response: 'Here is the answer',
|
|
71
|
+
sources: [{ content: 'doc1', score: 0.9 }],
|
|
72
|
+
});
|
|
73
|
+
const service = new messaging_service_1.MessagingService({ agentHandler });
|
|
74
|
+
const reply = await service.handleMessage(createMockMessage({ text: 'Query' }));
|
|
75
|
+
expect(reply).toBe('Here is the answer');
|
|
76
|
+
});
|
|
77
|
+
it('appends turns to store for agentHandler', async () => {
|
|
78
|
+
const store = new memory_conversation_context_1.MemoryConversationContextStore();
|
|
79
|
+
const agentHandler = jest.fn().mockResolvedValue('Done');
|
|
80
|
+
const service = new messaging_service_1.MessagingService({ agentHandler }, store);
|
|
81
|
+
await service.handleMessage(createMockMessage({ text: 'Go' }));
|
|
82
|
+
const turns = await store.getTurns('default:telegram:user-456');
|
|
83
|
+
expect(turns).toHaveLength(2);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe('aiProvider (LLM)', () => {
|
|
87
|
+
it('throws when no aiProvider, agentHandler, or customHandler', async () => {
|
|
88
|
+
const service = new messaging_service_1.MessagingService({});
|
|
89
|
+
await expect(service.handleMessage(createMockMessage())).rejects.toThrow('MessagingService requires aiProvider, agentHandler, or customHandler');
|
|
90
|
+
});
|
|
91
|
+
it('calls aiProvider.complete and returns response', async () => {
|
|
92
|
+
const mockComplete = jest.fn().mockResolvedValue({ content: ' LLM reply ' });
|
|
93
|
+
const aiProvider = { complete: mockComplete };
|
|
94
|
+
const service = new messaging_service_1.MessagingService({ aiProvider });
|
|
95
|
+
const reply = await service.handleMessage(createMockMessage({ text: 'Hello' }));
|
|
96
|
+
expect(mockComplete).toHaveBeenCalledWith(expect.objectContaining({
|
|
97
|
+
model: 'gpt-4o-mini',
|
|
98
|
+
messages: expect.arrayContaining([
|
|
99
|
+
expect.objectContaining({ role: 'system' }),
|
|
100
|
+
expect.objectContaining({ role: 'user', content: 'Hello' }),
|
|
101
|
+
]),
|
|
102
|
+
}));
|
|
103
|
+
expect(reply).toBe('LLM reply');
|
|
104
|
+
});
|
|
105
|
+
it('uses fallback when response content is null/undefined', async () => {
|
|
106
|
+
const aiProvider = {
|
|
107
|
+
complete: jest.fn().mockResolvedValue({ content: null }),
|
|
108
|
+
};
|
|
109
|
+
const service = new messaging_service_1.MessagingService({ aiProvider });
|
|
110
|
+
const reply = await service.handleMessage(createMockMessage());
|
|
111
|
+
expect(reply).toBe('I could not generate a response.');
|
|
112
|
+
});
|
|
113
|
+
it('uses RAG when ragService provided', async () => {
|
|
114
|
+
const mockComplete = jest.fn().mockResolvedValue({ content: 'RAG answer' });
|
|
115
|
+
const aiProvider = { complete: mockComplete };
|
|
116
|
+
const ragService = {
|
|
117
|
+
search: jest.fn().mockResolvedValue([
|
|
118
|
+
{ content: 'Doc 1 content', score: 0.9 },
|
|
119
|
+
{ content: 'Doc 2 content', score: 0.8 },
|
|
120
|
+
]),
|
|
121
|
+
};
|
|
122
|
+
const service = new messaging_service_1.MessagingService({ aiProvider, ragService });
|
|
123
|
+
await service.handleMessage(createMockMessage({ text: 'What is X?' }));
|
|
124
|
+
expect(ragService.search).toHaveBeenCalledWith('What is X?', expect.objectContaining({ topK: 5, minScore: 0.5 }));
|
|
125
|
+
const systemContent = mockComplete.mock.calls[0][0].messages[0].content;
|
|
126
|
+
expect(systemContent).toContain('Doc 1 content');
|
|
127
|
+
expect(systemContent).toContain('Doc 2 content');
|
|
128
|
+
});
|
|
129
|
+
it('does not augment prompt when RAG returns no results', async () => {
|
|
130
|
+
const mockComplete = jest.fn().mockResolvedValue({ content: 'Answer' });
|
|
131
|
+
const aiProvider = { complete: mockComplete };
|
|
132
|
+
const ragService = { search: jest.fn().mockResolvedValue([]) };
|
|
133
|
+
const service = new messaging_service_1.MessagingService({ aiProvider, ragService });
|
|
134
|
+
await service.handleMessage(createMockMessage({ text: 'Query' }));
|
|
135
|
+
const systemContent = mockComplete.mock.calls[0][0].messages[0].content;
|
|
136
|
+
expect(systemContent).not.toContain('Context:');
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
describe('clearSession', () => {
|
|
140
|
+
it('clears conversation history', async () => {
|
|
141
|
+
const store = new memory_conversation_context_1.MemoryConversationContextStore();
|
|
142
|
+
const customHandler = jest.fn().mockResolvedValue('Hi');
|
|
143
|
+
const service = new messaging_service_1.MessagingService({ customHandler }, store);
|
|
144
|
+
await service.handleMessage(createMockMessage({ text: 'Hello' }));
|
|
145
|
+
await service.clearSession('default:telegram:user-456');
|
|
146
|
+
const turns = await store.getTurns('default:telegram:user-456');
|
|
147
|
+
expect(turns).toEqual([]);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
describe('getOptions', () => {
|
|
151
|
+
it('returns copy of options', () => {
|
|
152
|
+
const service = new messaging_service_1.MessagingService({
|
|
153
|
+
customHandler: () => 'x',
|
|
154
|
+
model: 'gpt-4',
|
|
155
|
+
});
|
|
156
|
+
const opts = service.getOptions();
|
|
157
|
+
expect(opts.model).toBe('gpt-4');
|
|
158
|
+
expect(opts.customHandler).toBeDefined();
|
|
159
|
+
expect(opts).not.toBe(service.getOptions());
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
//# sourceMappingURL=messaging.service.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messaging.service.test.js","sourceRoot":"","sources":["../src/messaging.service.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conversation context store - abstract storage for multi-turn history
|
|
3
|
+
* Implementations: memory (dev), Redis (production, horizontally scalable)
|
|
4
|
+
*/
|
|
5
|
+
export interface ConversationTurn {
|
|
6
|
+
role: 'user' | 'assistant';
|
|
7
|
+
content: string;
|
|
8
|
+
}
|
|
9
|
+
export interface IConversationContextStore {
|
|
10
|
+
/** Get conversation turns for a session */
|
|
11
|
+
getTurns(sessionId: string): Promise<ConversationTurn[]>;
|
|
12
|
+
/** Append turn and optionally trim to max count */
|
|
13
|
+
appendTurn(sessionId: string, turn: ConversationTurn, maxTurns?: number): Promise<void>;
|
|
14
|
+
/** Clear session history */
|
|
15
|
+
clearSession(sessionId: string): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=conversation-context.interface.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-context.interface.d.ts","sourceRoot":"","sources":["../../src/store/conversation-context.interface.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,yBAAyB;IACxC,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAEzD,mDAAmD;IACnD,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAExF,4BAA4B;IAC5B,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Conversation context store - abstract storage for multi-turn history
|
|
4
|
+
* Implementations: memory (dev), Redis (production, horizontally scalable)
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
//# sourceMappingURL=conversation-context.interface.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conversation-context.interface.js","sourceRoot":"","sources":["../../src/store/conversation-context.interface.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory conversation context - for dev/single-instance only
|
|
3
|
+
*/
|
|
4
|
+
import type { IConversationContextStore, ConversationTurn } from './conversation-context.interface';
|
|
5
|
+
export declare class MemoryConversationContextStore implements IConversationContextStore {
|
|
6
|
+
private history;
|
|
7
|
+
getTurns(sessionId: string): Promise<ConversationTurn[]>;
|
|
8
|
+
appendTurn(sessionId: string, turn: ConversationTurn, maxTurns?: number): Promise<void>;
|
|
9
|
+
clearSession(sessionId: string): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=memory-conversation-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-conversation-context.d.ts","sourceRoot":"","sources":["../../src/store/memory-conversation-context.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAEpG,qBAAa,8BAA+B,YAAW,yBAAyB;IAC9E,OAAO,CAAC,OAAO,CAAyC;IAElD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAIxD,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAOvF,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGrD"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MemoryConversationContextStore = void 0;
|
|
4
|
+
class MemoryConversationContextStore {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.history = new Map();
|
|
7
|
+
}
|
|
8
|
+
async getTurns(sessionId) {
|
|
9
|
+
return this.history.get(sessionId) ?? [];
|
|
10
|
+
}
|
|
11
|
+
async appendTurn(sessionId, turn, maxTurns) {
|
|
12
|
+
const turns = this.history.get(sessionId) ?? [];
|
|
13
|
+
turns.push(turn);
|
|
14
|
+
const trimmed = maxTurns && turns.length > maxTurns * 2 ? turns.slice(-(maxTurns * 2)) : turns;
|
|
15
|
+
this.history.set(sessionId, trimmed);
|
|
16
|
+
}
|
|
17
|
+
async clearSession(sessionId) {
|
|
18
|
+
this.history.delete(sessionId);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.MemoryConversationContextStore = MemoryConversationContextStore;
|
|
22
|
+
//# sourceMappingURL=memory-conversation-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-conversation-context.js","sourceRoot":"","sources":["../../src/store/memory-conversation-context.ts"],"names":[],"mappings":";;;AAKA,MAAa,8BAA8B;IAA3C;QACU,YAAO,GAAG,IAAI,GAAG,EAA8B,CAAC;IAgB1D,CAAC;IAdC,KAAK,CAAC,QAAQ,CAAC,SAAiB;QAC9B,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,SAAiB,EAAE,IAAsB,EAAE,QAAiB;QAC3E,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QAChD,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,OAAO,GAAG,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC/F,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,SAAiB;QAClC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;CACF;AAjBD,wEAiBC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-conversation-context.test.d.ts","sourceRoot":"","sources":["../../src/store/memory-conversation-context.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* MemoryConversationContextStore tests
|
|
5
|
+
*/
|
|
6
|
+
const memory_conversation_context_1 = require("./memory-conversation-context");
|
|
7
|
+
describe('MemoryConversationContextStore', () => {
|
|
8
|
+
let store;
|
|
9
|
+
beforeEach(() => {
|
|
10
|
+
store = new memory_conversation_context_1.MemoryConversationContextStore();
|
|
11
|
+
});
|
|
12
|
+
describe('getTurns', () => {
|
|
13
|
+
it('returns empty array for unknown session', async () => {
|
|
14
|
+
const turns = await store.getTurns('unknown-session');
|
|
15
|
+
expect(turns).toEqual([]);
|
|
16
|
+
});
|
|
17
|
+
it('returns stored turns for known session', async () => {
|
|
18
|
+
await store.appendTurn('s1', { role: 'user', content: 'Hello' });
|
|
19
|
+
const turns = await store.getTurns('s1');
|
|
20
|
+
expect(turns).toEqual([{ role: 'user', content: 'Hello' }]);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
describe('appendTurn', () => {
|
|
24
|
+
it('appends user and assistant turns', async () => {
|
|
25
|
+
await store.appendTurn('s1', { role: 'user', content: 'Hi' });
|
|
26
|
+
await store.appendTurn('s1', { role: 'assistant', content: 'Hello!' });
|
|
27
|
+
const turns = await store.getTurns('s1');
|
|
28
|
+
expect(turns).toHaveLength(2);
|
|
29
|
+
expect(turns[0]).toEqual({ role: 'user', content: 'Hi' });
|
|
30
|
+
expect(turns[1]).toEqual({ role: 'assistant', content: 'Hello!' });
|
|
31
|
+
});
|
|
32
|
+
it('trims to maxTurns*2 when exceeded', async () => {
|
|
33
|
+
for (let i = 0; i < 15; i++) {
|
|
34
|
+
await store.appendTurn('s1', {
|
|
35
|
+
role: i % 2 === 0 ? 'user' : 'assistant',
|
|
36
|
+
content: `msg${i}`,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
const turns = await store.getTurns('s1');
|
|
40
|
+
expect(turns.length).toBeLessThanOrEqual(20);
|
|
41
|
+
expect(turns[turns.length - 1].content).toBe('msg14');
|
|
42
|
+
});
|
|
43
|
+
it('does not trim when under maxTurns*2', async () => {
|
|
44
|
+
for (let i = 0; i < 5; i++) {
|
|
45
|
+
await store.appendTurn('s1', {
|
|
46
|
+
role: i % 2 === 0 ? 'user' : 'assistant',
|
|
47
|
+
content: `msg${i}`,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const turns = await store.getTurns('s1');
|
|
51
|
+
expect(turns).toHaveLength(5);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe('clearSession', () => {
|
|
55
|
+
it('removes session data', async () => {
|
|
56
|
+
await store.appendTurn('s1', { role: 'user', content: 'Hi' });
|
|
57
|
+
await store.clearSession('s1');
|
|
58
|
+
const turns = await store.getTurns('s1');
|
|
59
|
+
expect(turns).toEqual([]);
|
|
60
|
+
});
|
|
61
|
+
it('does not affect other sessions', async () => {
|
|
62
|
+
await store.appendTurn('s1', { role: 'user', content: 'Hi' });
|
|
63
|
+
await store.appendTurn('s2', { role: 'user', content: 'Bye' });
|
|
64
|
+
await store.clearSession('s1');
|
|
65
|
+
expect(await store.getTurns('s2')).toEqual([{ role: 'user', content: 'Bye' }]);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
//# sourceMappingURL=memory-conversation-context.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory-conversation-context.test.js","sourceRoot":"","sources":["../../src/store/memory-conversation-context.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Redis-backed conversation context - horizontally scalable
|
|
3
|
+
*/
|
|
4
|
+
import type { IConversationContextStore, ConversationTurn } from './conversation-context.interface';
|
|
5
|
+
export interface RedisConversationContextConfig {
|
|
6
|
+
/** Redis client (ioredis) */
|
|
7
|
+
client: {
|
|
8
|
+
get: (k: string) => Promise<string | null>;
|
|
9
|
+
setex: (k: string, t: number, v: string) => Promise<string>;
|
|
10
|
+
del: (k: string) => Promise<number>;
|
|
11
|
+
};
|
|
12
|
+
/** TTL for context keys in seconds (default: 24h) */
|
|
13
|
+
ttlSeconds?: number;
|
|
14
|
+
}
|
|
15
|
+
export declare class RedisConversationContextStore implements IConversationContextStore {
|
|
16
|
+
private client;
|
|
17
|
+
private ttl;
|
|
18
|
+
constructor(config: RedisConversationContextConfig);
|
|
19
|
+
private key;
|
|
20
|
+
getTurns(sessionId: string): Promise<ConversationTurn[]>;
|
|
21
|
+
appendTurn(sessionId: string, turn: ConversationTurn, maxTurns?: number): Promise<void>;
|
|
22
|
+
clearSession(sessionId: string): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=redis-conversation-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-conversation-context.d.ts","sourceRoot":"","sources":["../../src/store/redis-conversation-context.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,EAAE,yBAAyB,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAKpG,MAAM,WAAW,8BAA8B;IAC7C,6BAA6B;IAC7B,MAAM,EAAE;QACN,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;QAC3C,KAAK,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;QAC5D,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;KACrC,CAAC;IACF,qDAAqD;IACrD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,6BAA8B,YAAW,yBAAyB;IAC7E,OAAO,CAAC,MAAM,CAA2C;IACzD,OAAO,CAAC,GAAG,CAAS;gBAER,MAAM,EAAE,8BAA8B;IAKlD,OAAO,CAAC,GAAG;IAIL,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAUxD,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQvF,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGrD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedisConversationContextStore = void 0;
|
|
4
|
+
const KEY_PREFIX = 'messaging:context:';
|
|
5
|
+
const DEFAULT_TTL_SEC = 86400; // 24h
|
|
6
|
+
class RedisConversationContextStore {
|
|
7
|
+
constructor(config) {
|
|
8
|
+
this.client = config.client;
|
|
9
|
+
this.ttl = config.ttlSeconds ?? DEFAULT_TTL_SEC;
|
|
10
|
+
}
|
|
11
|
+
key(sessionId) {
|
|
12
|
+
return `${KEY_PREFIX}${sessionId}`;
|
|
13
|
+
}
|
|
14
|
+
async getTurns(sessionId) {
|
|
15
|
+
const raw = await this.client.get(this.key(sessionId));
|
|
16
|
+
if (!raw)
|
|
17
|
+
return [];
|
|
18
|
+
try {
|
|
19
|
+
return JSON.parse(raw);
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
async appendTurn(sessionId, turn, maxTurns) {
|
|
26
|
+
const turns = await this.getTurns(sessionId);
|
|
27
|
+
turns.push(turn);
|
|
28
|
+
const trimmed = maxTurns && turns.length > maxTurns * 2 ? turns.slice(-(maxTurns * 2)) : turns;
|
|
29
|
+
const k = this.key(sessionId);
|
|
30
|
+
await this.client.setex(k, this.ttl, JSON.stringify(trimmed));
|
|
31
|
+
}
|
|
32
|
+
async clearSession(sessionId) {
|
|
33
|
+
await this.client.del(this.key(sessionId));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.RedisConversationContextStore = RedisConversationContextStore;
|
|
37
|
+
//# sourceMappingURL=redis-conversation-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-conversation-context.js","sourceRoot":"","sources":["../../src/store/redis-conversation-context.ts"],"names":[],"mappings":";;;AAKA,MAAM,UAAU,GAAG,oBAAoB,CAAC;AACxC,MAAM,eAAe,GAAG,KAAK,CAAC,CAAC,MAAM;AAarC,MAAa,6BAA6B;IAIxC,YAAY,MAAsC;QAChD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QAC5B,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,UAAU,IAAI,eAAe,CAAC;IAClD,CAAC;IAEO,GAAG,CAAC,SAAiB;QAC3B,OAAO,GAAG,UAAU,GAAG,SAAS,EAAE,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,SAAiB;QAC9B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAuB,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU,CAAC,SAAiB,EAAE,IAAsB,EAAE,QAAiB;QAC3E,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QAC7C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjB,MAAM,OAAO,GAAG,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;QAC/F,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC9B,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAChE,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,SAAiB;QAClC,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC;IAC7C,CAAC;CACF;AAlCD,sEAkCC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redis-conversation-context.test.d.ts","sourceRoot":"","sources":["../../src/store/redis-conversation-context.test.ts"],"names":[],"mappings":""}
|