@dangao/bun-server 1.12.0 → 2.0.0

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 (223) hide show
  1. package/README.md +32 -0
  2. package/dist/ai/ai-module.d.ts +24 -0
  3. package/dist/ai/ai-module.d.ts.map +1 -0
  4. package/dist/ai/decorators.d.ts +25 -0
  5. package/dist/ai/decorators.d.ts.map +1 -0
  6. package/dist/ai/errors.d.ts +39 -0
  7. package/dist/ai/errors.d.ts.map +1 -0
  8. package/dist/ai/index.d.ts +12 -0
  9. package/dist/ai/index.d.ts.map +1 -0
  10. package/dist/ai/providers/anthropic-provider.d.ts +23 -0
  11. package/dist/ai/providers/anthropic-provider.d.ts.map +1 -0
  12. package/dist/ai/providers/google-provider.d.ts +20 -0
  13. package/dist/ai/providers/google-provider.d.ts.map +1 -0
  14. package/dist/ai/providers/ollama-provider.d.ts +17 -0
  15. package/dist/ai/providers/ollama-provider.d.ts.map +1 -0
  16. package/dist/ai/providers/openai-provider.d.ts +28 -0
  17. package/dist/ai/providers/openai-provider.d.ts.map +1 -0
  18. package/dist/ai/service.d.ts +40 -0
  19. package/dist/ai/service.d.ts.map +1 -0
  20. package/dist/ai/tools/tool-executor.d.ts +15 -0
  21. package/dist/ai/tools/tool-executor.d.ts.map +1 -0
  22. package/dist/ai/tools/tool-registry.d.ts +39 -0
  23. package/dist/ai/tools/tool-registry.d.ts.map +1 -0
  24. package/dist/ai/types.d.ts +134 -0
  25. package/dist/ai/types.d.ts.map +1 -0
  26. package/dist/ai-guard/ai-guard-module.d.ts +18 -0
  27. package/dist/ai-guard/ai-guard-module.d.ts.map +1 -0
  28. package/dist/ai-guard/decorators.d.ts +16 -0
  29. package/dist/ai-guard/decorators.d.ts.map +1 -0
  30. package/dist/ai-guard/detectors/content-moderator.d.ts +26 -0
  31. package/dist/ai-guard/detectors/content-moderator.d.ts.map +1 -0
  32. package/dist/ai-guard/detectors/injection-detector.d.ts +13 -0
  33. package/dist/ai-guard/detectors/injection-detector.d.ts.map +1 -0
  34. package/dist/ai-guard/detectors/pii-detector.d.ts +11 -0
  35. package/dist/ai-guard/detectors/pii-detector.d.ts.map +1 -0
  36. package/dist/ai-guard/index.d.ts +8 -0
  37. package/dist/ai-guard/index.d.ts.map +1 -0
  38. package/dist/ai-guard/service.d.ts +21 -0
  39. package/dist/ai-guard/service.d.ts.map +1 -0
  40. package/dist/ai-guard/types.d.ts +59 -0
  41. package/dist/ai-guard/types.d.ts.map +1 -0
  42. package/dist/conversation/conversation-module.d.ts +25 -0
  43. package/dist/conversation/conversation-module.d.ts.map +1 -0
  44. package/dist/conversation/decorators.d.ts +28 -0
  45. package/dist/conversation/decorators.d.ts.map +1 -0
  46. package/dist/conversation/index.d.ts +8 -0
  47. package/dist/conversation/index.d.ts.map +1 -0
  48. package/dist/conversation/service.d.ts +43 -0
  49. package/dist/conversation/service.d.ts.map +1 -0
  50. package/dist/conversation/stores/database-store.d.ts +46 -0
  51. package/dist/conversation/stores/database-store.d.ts.map +1 -0
  52. package/dist/conversation/stores/memory-store.d.ts +17 -0
  53. package/dist/conversation/stores/memory-store.d.ts.map +1 -0
  54. package/dist/conversation/stores/redis-store.d.ts +39 -0
  55. package/dist/conversation/stores/redis-store.d.ts.map +1 -0
  56. package/dist/conversation/types.d.ts +64 -0
  57. package/dist/conversation/types.d.ts.map +1 -0
  58. package/dist/core/cluster.d.ts +42 -3
  59. package/dist/core/cluster.d.ts.map +1 -1
  60. package/dist/core/index.d.ts +1 -1
  61. package/dist/core/index.d.ts.map +1 -1
  62. package/dist/core/server.d.ts.map +1 -1
  63. package/dist/embedding/embedding-module.d.ts +20 -0
  64. package/dist/embedding/embedding-module.d.ts.map +1 -0
  65. package/dist/embedding/index.d.ts +6 -0
  66. package/dist/embedding/index.d.ts.map +1 -0
  67. package/dist/embedding/providers/ollama-embedding-provider.d.ts +18 -0
  68. package/dist/embedding/providers/ollama-embedding-provider.d.ts.map +1 -0
  69. package/dist/embedding/providers/openai-embedding-provider.d.ts +18 -0
  70. package/dist/embedding/providers/openai-embedding-provider.d.ts.map +1 -0
  71. package/dist/embedding/service.d.ts +27 -0
  72. package/dist/embedding/service.d.ts.map +1 -0
  73. package/dist/embedding/types.d.ts +25 -0
  74. package/dist/embedding/types.d.ts.map +1 -0
  75. package/dist/index.d.ts +9 -1
  76. package/dist/index.d.ts.map +1 -1
  77. package/dist/index.js +2870 -88
  78. package/dist/mcp/decorators.d.ts +42 -0
  79. package/dist/mcp/decorators.d.ts.map +1 -0
  80. package/dist/mcp/index.d.ts +6 -0
  81. package/dist/mcp/index.d.ts.map +1 -0
  82. package/dist/mcp/mcp-module.d.ts +22 -0
  83. package/dist/mcp/mcp-module.d.ts.map +1 -0
  84. package/dist/mcp/registry.d.ts +23 -0
  85. package/dist/mcp/registry.d.ts.map +1 -0
  86. package/dist/mcp/server.d.ts +29 -0
  87. package/dist/mcp/server.d.ts.map +1 -0
  88. package/dist/mcp/types.d.ts +60 -0
  89. package/dist/mcp/types.d.ts.map +1 -0
  90. package/dist/prompt/index.d.ts +6 -0
  91. package/dist/prompt/index.d.ts.map +1 -0
  92. package/dist/prompt/prompt-module.d.ts +23 -0
  93. package/dist/prompt/prompt-module.d.ts.map +1 -0
  94. package/dist/prompt/service.d.ts +47 -0
  95. package/dist/prompt/service.d.ts.map +1 -0
  96. package/dist/prompt/stores/file-store.d.ts +36 -0
  97. package/dist/prompt/stores/file-store.d.ts.map +1 -0
  98. package/dist/prompt/stores/memory-store.d.ts +17 -0
  99. package/dist/prompt/stores/memory-store.d.ts.map +1 -0
  100. package/dist/prompt/types.d.ts +68 -0
  101. package/dist/prompt/types.d.ts.map +1 -0
  102. package/dist/rag/chunkers/markdown-chunker.d.ts +11 -0
  103. package/dist/rag/chunkers/markdown-chunker.d.ts.map +1 -0
  104. package/dist/rag/chunkers/text-chunker.d.ts +11 -0
  105. package/dist/rag/chunkers/text-chunker.d.ts.map +1 -0
  106. package/dist/rag/decorators.d.ts +24 -0
  107. package/dist/rag/decorators.d.ts.map +1 -0
  108. package/dist/rag/index.d.ts +7 -0
  109. package/dist/rag/index.d.ts.map +1 -0
  110. package/dist/rag/rag-module.d.ts +23 -0
  111. package/dist/rag/rag-module.d.ts.map +1 -0
  112. package/dist/rag/service.d.ts +36 -0
  113. package/dist/rag/service.d.ts.map +1 -0
  114. package/dist/rag/types.d.ts +56 -0
  115. package/dist/rag/types.d.ts.map +1 -0
  116. package/dist/vector-store/index.d.ts +6 -0
  117. package/dist/vector-store/index.d.ts.map +1 -0
  118. package/dist/vector-store/stores/memory-store.d.ts +17 -0
  119. package/dist/vector-store/stores/memory-store.d.ts.map +1 -0
  120. package/dist/vector-store/stores/pinecone-store.d.ts +27 -0
  121. package/dist/vector-store/stores/pinecone-store.d.ts.map +1 -0
  122. package/dist/vector-store/stores/qdrant-store.d.ts +29 -0
  123. package/dist/vector-store/stores/qdrant-store.d.ts.map +1 -0
  124. package/dist/vector-store/types.d.ts +60 -0
  125. package/dist/vector-store/types.d.ts.map +1 -0
  126. package/dist/vector-store/vector-store-module.d.ts +20 -0
  127. package/dist/vector-store/vector-store-module.d.ts.map +1 -0
  128. package/docs/ai.md +500 -0
  129. package/docs/best-practices.md +83 -8
  130. package/docs/database.md +23 -0
  131. package/docs/guide.md +90 -27
  132. package/docs/migration.md +81 -7
  133. package/docs/security.md +23 -0
  134. package/docs/zh/ai.md +441 -0
  135. package/docs/zh/best-practices.md +43 -0
  136. package/docs/zh/database.md +23 -0
  137. package/docs/zh/guide.md +40 -1
  138. package/docs/zh/migration.md +39 -0
  139. package/docs/zh/security.md +23 -0
  140. package/package.json +2 -2
  141. package/src/ai/ai-module.ts +62 -0
  142. package/src/ai/decorators.ts +30 -0
  143. package/src/ai/errors.ts +71 -0
  144. package/src/ai/index.ts +11 -0
  145. package/src/ai/providers/anthropic-provider.ts +190 -0
  146. package/src/ai/providers/google-provider.ts +179 -0
  147. package/src/ai/providers/ollama-provider.ts +126 -0
  148. package/src/ai/providers/openai-provider.ts +242 -0
  149. package/src/ai/service.ts +155 -0
  150. package/src/ai/tools/tool-executor.ts +38 -0
  151. package/src/ai/tools/tool-registry.ts +91 -0
  152. package/src/ai/types.ts +145 -0
  153. package/src/ai-guard/ai-guard-module.ts +50 -0
  154. package/src/ai-guard/decorators.ts +21 -0
  155. package/src/ai-guard/detectors/content-moderator.ts +80 -0
  156. package/src/ai-guard/detectors/injection-detector.ts +48 -0
  157. package/src/ai-guard/detectors/pii-detector.ts +64 -0
  158. package/src/ai-guard/index.ts +7 -0
  159. package/src/ai-guard/service.ts +100 -0
  160. package/src/ai-guard/types.ts +61 -0
  161. package/src/conversation/conversation-module.ts +63 -0
  162. package/src/conversation/decorators.ts +47 -0
  163. package/src/conversation/index.ts +7 -0
  164. package/src/conversation/service.ts +133 -0
  165. package/src/conversation/stores/database-store.ts +125 -0
  166. package/src/conversation/stores/memory-store.ts +57 -0
  167. package/src/conversation/stores/redis-store.ts +101 -0
  168. package/src/conversation/types.ts +68 -0
  169. package/src/core/cluster.ts +239 -46
  170. package/src/core/index.ts +1 -1
  171. package/src/core/server.ts +91 -78
  172. package/src/embedding/embedding-module.ts +52 -0
  173. package/src/embedding/index.ts +5 -0
  174. package/src/embedding/providers/ollama-embedding-provider.ts +39 -0
  175. package/src/embedding/providers/openai-embedding-provider.ts +47 -0
  176. package/src/embedding/service.ts +55 -0
  177. package/src/embedding/types.ts +27 -0
  178. package/src/index.ts +11 -1
  179. package/src/mcp/decorators.ts +60 -0
  180. package/src/mcp/index.ts +5 -0
  181. package/src/mcp/mcp-module.ts +58 -0
  182. package/src/mcp/registry.ts +72 -0
  183. package/src/mcp/server.ts +164 -0
  184. package/src/mcp/types.ts +63 -0
  185. package/src/prompt/index.ts +5 -0
  186. package/src/prompt/prompt-module.ts +61 -0
  187. package/src/prompt/service.ts +93 -0
  188. package/src/prompt/stores/file-store.ts +135 -0
  189. package/src/prompt/stores/memory-store.ts +82 -0
  190. package/src/prompt/types.ts +84 -0
  191. package/src/rag/chunkers/markdown-chunker.ts +40 -0
  192. package/src/rag/chunkers/text-chunker.ts +30 -0
  193. package/src/rag/decorators.ts +26 -0
  194. package/src/rag/index.ts +6 -0
  195. package/src/rag/rag-module.ts +78 -0
  196. package/src/rag/service.ts +134 -0
  197. package/src/rag/types.ts +47 -0
  198. package/src/vector-store/index.ts +5 -0
  199. package/src/vector-store/stores/memory-store.ts +69 -0
  200. package/src/vector-store/stores/pinecone-store.ts +123 -0
  201. package/src/vector-store/stores/qdrant-store.ts +147 -0
  202. package/src/vector-store/types.ts +77 -0
  203. package/src/vector-store/vector-store-module.ts +50 -0
  204. package/tests/ai/ai-module.test.ts +46 -0
  205. package/tests/ai/ai-service.test.ts +91 -0
  206. package/tests/ai/tool-registry.test.ts +57 -0
  207. package/tests/ai-guard/ai-guard-module.test.ts +23 -0
  208. package/tests/ai-guard/content-moderator.test.ts +65 -0
  209. package/tests/ai-guard/pii-detector.test.ts +41 -0
  210. package/tests/conversation/conversation-module.test.ts +26 -0
  211. package/tests/conversation/conversation-service.test.ts +64 -0
  212. package/tests/conversation/memory-store.test.ts +68 -0
  213. package/tests/core/cluster.test.ts +45 -1
  214. package/tests/embedding/embedding-service.test.ts +55 -0
  215. package/tests/mcp/mcp-server.test.ts +85 -0
  216. package/tests/prompt/prompt-module.test.ts +30 -0
  217. package/tests/prompt/prompt-service.test.ts +74 -0
  218. package/tests/rag/chunkers.test.ts +58 -0
  219. package/tests/rag/rag-service.test.ts +66 -0
  220. package/tests/vector-store/memory-vector-store.test.ts +84 -0
  221. package/tests/interceptor/perf/interceptor-performance.test.ts +0 -340
  222. package/tests/perf/optimization.test.ts +0 -182
  223. package/tests/perf/regression.test.ts +0 -120
@@ -0,0 +1,125 @@
1
+ import type { ConversationStore, Conversation } from '../types';
2
+ import type { AiMessage } from '../../ai/types';
3
+
4
+ export interface DatabaseConversationStoreConfig {
5
+ /** DatabaseService instance from DatabaseModule */
6
+ database: DatabaseServiceLike;
7
+ /** Table name for conversations (default: 'conversations') */
8
+ tableName?: string;
9
+ }
10
+
11
+ /**
12
+ * Minimal interface matching DatabaseService from DatabaseModule
13
+ */
14
+ export interface DatabaseServiceLike {
15
+ query<T = unknown>(sql: string, params?: unknown[]): Promise<T[]>;
16
+ execute(sql: string, params?: unknown[]): Promise<void>;
17
+ }
18
+
19
+ /**
20
+ * Database-backed conversation store — suitable for durable storage with full history.
21
+ *
22
+ * Creates tables automatically on first use. Requires DatabaseModule to be configured.
23
+ *
24
+ * Schema:
25
+ * ```sql
26
+ * CREATE TABLE conversations (
27
+ * id TEXT PRIMARY KEY,
28
+ * messages TEXT NOT NULL, -- JSON array
29
+ * metadata TEXT NOT NULL, -- JSON object
30
+ * created_at TEXT NOT NULL,
31
+ * updated_at TEXT NOT NULL
32
+ * );
33
+ * ```
34
+ */
35
+ export class DatabaseConversationStore implements ConversationStore {
36
+ private readonly db: DatabaseServiceLike;
37
+ private readonly tableName: string;
38
+ private initialized = false;
39
+
40
+ public constructor(config: DatabaseConversationStoreConfig) {
41
+ this.db = config.database;
42
+ this.tableName = config.tableName ?? 'conversations';
43
+ }
44
+
45
+ public async create(metadata: Record<string, unknown> = {}): Promise<Conversation> {
46
+ await this.ensureTable();
47
+ const id = crypto.randomUUID();
48
+ const now = new Date().toISOString();
49
+ await this.db.execute(
50
+ `INSERT INTO ${this.tableName} (id, messages, metadata, created_at, updated_at) VALUES (?, ?, ?, ?, ?)`,
51
+ [id, '[]', JSON.stringify(metadata), now, now],
52
+ );
53
+ return { id, messages: [], metadata, createdAt: new Date(now), updatedAt: new Date(now) };
54
+ }
55
+
56
+ public async get(id: string): Promise<Conversation | null> {
57
+ await this.ensureTable();
58
+ const rows = await this.db.query<Record<string, string>>(
59
+ `SELECT * FROM ${this.tableName} WHERE id = ?`,
60
+ [id],
61
+ );
62
+ if (!rows.length) return null;
63
+ return this.rowToConversation(rows[0]!);
64
+ }
65
+
66
+ public async appendMessage(id: string, message: AiMessage): Promise<void> {
67
+ await this.ensureTable();
68
+ const conv = await this.get(id);
69
+ if (!conv) throw new Error(`Conversation "${id}" not found`);
70
+ const messages = [...conv.messages, message];
71
+ const now = new Date().toISOString();
72
+ await this.db.execute(
73
+ `UPDATE ${this.tableName} SET messages = ?, updated_at = ? WHERE id = ?`,
74
+ [JSON.stringify(messages), now, id],
75
+ );
76
+ }
77
+
78
+ public async trim(id: string, maxMessages: number): Promise<void> {
79
+ await this.ensureTable();
80
+ const conv = await this.get(id);
81
+ if (!conv || conv.messages.length <= maxMessages) return;
82
+ const trimmed = conv.messages.slice(-maxMessages);
83
+ const now = new Date().toISOString();
84
+ await this.db.execute(
85
+ `UPDATE ${this.tableName} SET messages = ?, updated_at = ? WHERE id = ?`,
86
+ [JSON.stringify(trimmed), now, id],
87
+ );
88
+ }
89
+
90
+ public async delete(id: string): Promise<boolean> {
91
+ await this.ensureTable();
92
+ await this.db.execute(`DELETE FROM ${this.tableName} WHERE id = ?`, [id]);
93
+ return true;
94
+ }
95
+
96
+ public async list(): Promise<string[]> {
97
+ await this.ensureTable();
98
+ const rows = await this.db.query<{ id: string }>(`SELECT id FROM ${this.tableName}`);
99
+ return rows.map((r) => r.id);
100
+ }
101
+
102
+ private async ensureTable(): Promise<void> {
103
+ if (this.initialized) return;
104
+ await this.db.execute(`
105
+ CREATE TABLE IF NOT EXISTS ${this.tableName} (
106
+ id TEXT PRIMARY KEY,
107
+ messages TEXT NOT NULL DEFAULT '[]',
108
+ metadata TEXT NOT NULL DEFAULT '{}',
109
+ created_at TEXT NOT NULL,
110
+ updated_at TEXT NOT NULL
111
+ )
112
+ `);
113
+ this.initialized = true;
114
+ }
115
+
116
+ private rowToConversation(row: Record<string, string>): Conversation {
117
+ return {
118
+ id: row['id']!,
119
+ messages: JSON.parse(row['messages']!) as AiMessage[],
120
+ metadata: JSON.parse(row['metadata']!) as Record<string, unknown>,
121
+ createdAt: new Date(row['created_at']!),
122
+ updatedAt: new Date(row['updated_at']!),
123
+ };
124
+ }
125
+ }
@@ -0,0 +1,57 @@
1
+ import type { ConversationStore, Conversation } from '../types';
2
+ import type { AiMessage } from '../../ai/types';
3
+
4
+ /**
5
+ * In-memory conversation store — suitable for development and single-instance deployments.
6
+ */
7
+ export class MemoryConversationStore implements ConversationStore {
8
+ private readonly conversations = new Map<string, Conversation>();
9
+
10
+ public async create(metadata: Record<string, unknown> = {}): Promise<Conversation> {
11
+ const id = crypto.randomUUID();
12
+ const conversation: Conversation = {
13
+ id,
14
+ messages: [],
15
+ metadata,
16
+ createdAt: new Date(),
17
+ updatedAt: new Date(),
18
+ };
19
+ this.conversations.set(id, conversation);
20
+ return { ...conversation, messages: [] };
21
+ }
22
+
23
+ public async get(id: string): Promise<Conversation | null> {
24
+ const conv = this.conversations.get(id);
25
+ if (!conv) return null;
26
+ return { ...conv, messages: [...conv.messages] };
27
+ }
28
+
29
+ public async appendMessage(id: string, message: AiMessage): Promise<void> {
30
+ const conv = this.conversations.get(id);
31
+ if (!conv) throw new Error(`Conversation "${id}" not found`);
32
+ conv.messages.push(message);
33
+ conv.updatedAt = new Date();
34
+ }
35
+
36
+ public async trim(id: string, maxMessages: number): Promise<void> {
37
+ const conv = this.conversations.get(id);
38
+ if (!conv) return;
39
+ if (conv.messages.length > maxMessages) {
40
+ conv.messages = conv.messages.slice(-maxMessages);
41
+ conv.updatedAt = new Date();
42
+ }
43
+ }
44
+
45
+ public async delete(id: string): Promise<boolean> {
46
+ return this.conversations.delete(id);
47
+ }
48
+
49
+ public async list(): Promise<string[]> {
50
+ return Array.from(this.conversations.keys());
51
+ }
52
+
53
+ /** Number of stored conversations (for testing) */
54
+ public get size(): number {
55
+ return this.conversations.size;
56
+ }
57
+ }
@@ -0,0 +1,101 @@
1
+ import type { ConversationStore, Conversation } from '../types';
2
+ import type { AiMessage } from '../../ai/types';
3
+
4
+ export interface RedisConversationStoreConfig {
5
+ /** Redis client that supports get/set/del/keys commands */
6
+ client: RedisClient;
7
+ /** Key prefix (default: 'conv:') */
8
+ keyPrefix?: string;
9
+ /** TTL in seconds for conversation keys (default: 86400 = 24h) */
10
+ ttl?: number;
11
+ }
12
+
13
+ /**
14
+ * Minimal Redis client interface — compatible with ioredis and node-redis
15
+ */
16
+ export interface RedisClient {
17
+ get(key: string): Promise<string | null>;
18
+ set(key: string, value: string, ...args: unknown[]): Promise<unknown>;
19
+ del(key: string): Promise<unknown>;
20
+ keys(pattern: string): Promise<string[]>;
21
+ }
22
+
23
+ /**
24
+ * Redis-backed conversation store — suitable for multi-instance production deployments.
25
+ *
26
+ * Requires an external Redis client to be injected. Compatible with ioredis / node-redis.
27
+ */
28
+ export class RedisConversationStore implements ConversationStore {
29
+ private readonly client: RedisClient;
30
+ private readonly keyPrefix: string;
31
+ private readonly ttl: number;
32
+
33
+ public constructor(config: RedisConversationStoreConfig) {
34
+ this.client = config.client;
35
+ this.keyPrefix = config.keyPrefix ?? 'conv:';
36
+ this.ttl = config.ttl ?? 86400;
37
+ }
38
+
39
+ public async create(metadata: Record<string, unknown> = {}): Promise<Conversation> {
40
+ const id = crypto.randomUUID();
41
+ const conversation: Conversation = {
42
+ id,
43
+ messages: [],
44
+ metadata,
45
+ createdAt: new Date(),
46
+ updatedAt: new Date(),
47
+ };
48
+ await this.save(conversation);
49
+ return conversation;
50
+ }
51
+
52
+ public async get(id: string): Promise<Conversation | null> {
53
+ const raw = await this.client.get(this.key(id));
54
+ if (!raw) return null;
55
+ const parsed = JSON.parse(raw) as Conversation;
56
+ parsed.createdAt = new Date(parsed.createdAt);
57
+ parsed.updatedAt = new Date(parsed.updatedAt);
58
+ return parsed;
59
+ }
60
+
61
+ public async appendMessage(id: string, message: AiMessage): Promise<void> {
62
+ const conv = await this.get(id);
63
+ if (!conv) throw new Error(`Conversation "${id}" not found`);
64
+ conv.messages.push(message);
65
+ conv.updatedAt = new Date();
66
+ await this.save(conv);
67
+ }
68
+
69
+ public async trim(id: string, maxMessages: number): Promise<void> {
70
+ const conv = await this.get(id);
71
+ if (!conv) return;
72
+ if (conv.messages.length > maxMessages) {
73
+ conv.messages = conv.messages.slice(-maxMessages);
74
+ conv.updatedAt = new Date();
75
+ await this.save(conv);
76
+ }
77
+ }
78
+
79
+ public async delete(id: string): Promise<boolean> {
80
+ const result = await this.client.del(this.key(id));
81
+ return Number(result) > 0;
82
+ }
83
+
84
+ public async list(): Promise<string[]> {
85
+ const keys = await this.client.keys(`${this.keyPrefix}*`);
86
+ return keys.map((k) => k.slice(this.keyPrefix.length));
87
+ }
88
+
89
+ private key(id: string): string {
90
+ return `${this.keyPrefix}${id}`;
91
+ }
92
+
93
+ private async save(conversation: Conversation): Promise<void> {
94
+ await this.client.set(
95
+ this.key(conversation.id),
96
+ JSON.stringify(conversation),
97
+ 'EX',
98
+ this.ttl,
99
+ );
100
+ }
101
+ }
@@ -0,0 +1,68 @@
1
+ import type { AiMessage } from '../ai/types';
2
+
3
+ /**
4
+ * A conversation session
5
+ */
6
+ export interface Conversation {
7
+ id: string;
8
+ messages: AiMessage[];
9
+ metadata: Record<string, unknown>;
10
+ createdAt: Date;
11
+ updatedAt: Date;
12
+ }
13
+
14
+ /**
15
+ * Abstract conversation storage interface
16
+ */
17
+ export interface ConversationStore {
18
+ /**
19
+ * Create a new conversation
20
+ */
21
+ create(metadata?: Record<string, unknown>): Promise<Conversation>;
22
+ /**
23
+ * Retrieve an existing conversation
24
+ */
25
+ get(id: string): Promise<Conversation | null>;
26
+ /**
27
+ * Append a message to a conversation
28
+ */
29
+ appendMessage(id: string, message: AiMessage): Promise<void>;
30
+ /**
31
+ * Trim messages to maxMessages (keeping the most recent)
32
+ */
33
+ trim(id: string, maxMessages: number): Promise<void>;
34
+ /**
35
+ * Delete a conversation
36
+ */
37
+ delete(id: string): Promise<boolean>;
38
+ /**
39
+ * List all conversation IDs
40
+ */
41
+ list(): Promise<string[]>;
42
+ }
43
+
44
+ /**
45
+ * Summarizer callback — produce a summary string from conversation history
46
+ */
47
+ export type ConversationSummarizer = (messages: AiMessage[]) => Promise<string>;
48
+
49
+ /**
50
+ * ConversationModule configuration
51
+ */
52
+ export interface ConversationModuleOptions {
53
+ store?: ConversationStore;
54
+ /** Maximum messages before auto-trim (default: 100) */
55
+ maxMessages?: number;
56
+ /** Enable auto-trim when maxMessages is reached (default: true) */
57
+ autoTrim?: boolean;
58
+ /**
59
+ * When message count exceeds summaryThreshold, call summarizer and
60
+ * replace old messages with a summary message.
61
+ */
62
+ summaryThreshold?: number;
63
+ /** Summarizer callback — inject AiService here to avoid circular deps */
64
+ summarizer?: ConversationSummarizer;
65
+ }
66
+
67
+ export const CONVERSATION_SERVICE_TOKEN = Symbol('@dangao/bun-server:conversation:service');
68
+ export const CONVERSATION_OPTIONS_TOKEN = Symbol('@dangao/bun-server:conversation:options');