@things-factory/ai-assistant 10.1.6
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/client/components/board-ai-chat.test.ts +149 -0
- package/client/components/board-ai-chat.ts +3228 -0
- package/client/components/chat-defaults.ts +27 -0
- package/client/components/chat-echo-dedup.test.ts +230 -0
- package/client/components/chat-echo-dedup.ts +155 -0
- package/client/components/chat-input-builder.ts +69 -0
- package/client/components/markdown.test.ts +518 -0
- package/client/components/markdown.ts +508 -0
- package/client/components/mention-popup-helpers.ts +433 -0
- package/client/components/mention-popup.test.ts +588 -0
- package/client/components/mention-popup.ts +238 -0
- package/client/index.ts +14 -0
- package/client/tsconfig.json +13 -0
- package/client/utils/board-edit-patch.ts +374 -0
- package/dist-client/components/board-ai-chat.d.ts +423 -0
- package/dist-client/components/board-ai-chat.js +3034 -0
- package/dist-client/components/board-ai-chat.js.map +1 -0
- package/dist-client/components/board-ai-chat.test.d.ts +1 -0
- package/dist-client/components/board-ai-chat.test.js +138 -0
- package/dist-client/components/board-ai-chat.test.js.map +1 -0
- package/dist-client/components/chat-defaults.d.ts +8 -0
- package/dist-client/components/chat-defaults.js +22 -0
- package/dist-client/components/chat-defaults.js.map +1 -0
- package/dist-client/components/chat-echo-dedup.d.ts +54 -0
- package/dist-client/components/chat-echo-dedup.js +115 -0
- package/dist-client/components/chat-echo-dedup.js.map +1 -0
- package/dist-client/components/chat-echo-dedup.test.d.ts +1 -0
- package/dist-client/components/chat-echo-dedup.test.js +203 -0
- package/dist-client/components/chat-echo-dedup.test.js.map +1 -0
- package/dist-client/components/chat-input-builder.d.ts +55 -0
- package/dist-client/components/chat-input-builder.js +32 -0
- package/dist-client/components/chat-input-builder.js.map +1 -0
- package/dist-client/components/markdown.d.ts +60 -0
- package/dist-client/components/markdown.js +422 -0
- package/dist-client/components/markdown.js.map +1 -0
- package/dist-client/components/markdown.test.d.ts +1 -0
- package/dist-client/components/markdown.test.js +418 -0
- package/dist-client/components/markdown.test.js.map +1 -0
- package/dist-client/components/mention-popup-helpers.d.ts +165 -0
- package/dist-client/components/mention-popup-helpers.js +330 -0
- package/dist-client/components/mention-popup-helpers.js.map +1 -0
- package/dist-client/components/mention-popup.d.ts +35 -0
- package/dist-client/components/mention-popup.js +223 -0
- package/dist-client/components/mention-popup.js.map +1 -0
- package/dist-client/components/mention-popup.test.d.ts +1 -0
- package/dist-client/components/mention-popup.test.js +474 -0
- package/dist-client/components/mention-popup.test.js.map +1 -0
- package/dist-client/index.d.ts +14 -0
- package/dist-client/index.js +15 -0
- package/dist-client/index.js.map +1 -0
- package/dist-client/tsconfig.tsbuildinfo +1 -0
- package/dist-client/utils/board-edit-patch.d.ts +157 -0
- package/dist-client/utils/board-edit-patch.js +284 -0
- package/dist-client/utils/board-edit-patch.js.map +1 -0
- package/dist-server/index.d.ts +28 -0
- package/dist-server/index.js +32 -0
- package/dist-server/index.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-broadcast.d.ts +13 -0
- package/dist-server/service/chat-message/chat-message-broadcast.js +13 -0
- package/dist-server/service/chat-message/chat-message-broadcast.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-publish.d.ts +6 -0
- package/dist-server/service/chat-message/chat-message-publish.js +27 -0
- package/dist-server/service/chat-message/chat-message-publish.js.map +1 -0
- package/dist-server/service/chat-message/chat-message-subscription.d.ts +8 -0
- package/dist-server/service/chat-message/chat-message-subscription.js +57 -0
- package/dist-server/service/chat-message/chat-message-subscription.js.map +1 -0
- package/dist-server/service/chat-message/chat-message.d.ts +68 -0
- package/dist-server/service/chat-message/chat-message.js +172 -0
- package/dist-server/service/chat-message/chat-message.js.map +1 -0
- package/dist-server/service/chat-message/fold-history.d.ts +43 -0
- package/dist-server/service/chat-message/fold-history.js +33 -0
- package/dist-server/service/chat-message/fold-history.js.map +1 -0
- package/dist-server/service/chat-message/history-summary.d.ts +43 -0
- package/dist-server/service/chat-message/history-summary.js +77 -0
- package/dist-server/service/chat-message/history-summary.js.map +1 -0
- package/dist-server/service/chat-message/index.d.ts +5 -0
- package/dist-server/service/chat-message/index.js +12 -0
- package/dist-server/service/chat-message/index.js.map +1 -0
- package/dist-server/service/chat-message/llm-history.d.ts +46 -0
- package/dist-server/service/chat-message/llm-history.js +87 -0
- package/dist-server/service/chat-message/llm-history.js.map +1 -0
- package/dist-server/service/chat-session/chat-session.d.ts +60 -0
- package/dist-server/service/chat-session/chat-session.js +159 -0
- package/dist-server/service/chat-session/chat-session.js.map +1 -0
- package/dist-server/service/chat-session/index.d.ts +3 -0
- package/dist-server/service/chat-session/index.js +7 -0
- package/dist-server/service/chat-session/index.js.map +1 -0
- package/dist-server/service/chat-session/session-activity-broadcast.d.ts +30 -0
- package/dist-server/service/chat-session/session-activity-broadcast.js +34 -0
- package/dist-server/service/chat-session/session-activity-broadcast.js.map +1 -0
- package/dist-server/service/chat-session/session-activity-publish.d.ts +12 -0
- package/dist-server/service/chat-session/session-activity-publish.js +37 -0
- package/dist-server/service/chat-session/session-activity-publish.js.map +1 -0
- package/dist-server/service/chat-session/session-activity-subscription.d.ts +14 -0
- package/dist-server/service/chat-session/session-activity-subscription.js +87 -0
- package/dist-server/service/chat-session/session-activity-subscription.js.map +1 -0
- package/dist-server/service/chat-session/session-anchor.d.ts +26 -0
- package/dist-server/service/chat-session/session-anchor.js +100 -0
- package/dist-server/service/chat-session/session-anchor.js.map +1 -0
- package/dist-server/service/chat-session/session-inbox.d.ts +80 -0
- package/dist-server/service/chat-session/session-inbox.js +133 -0
- package/dist-server/service/chat-session/session-inbox.js.map +1 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.d.ts +49 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.js +141 -0
- package/dist-server/service/chat-session-participant/chat-session-participant.js.map +1 -0
- package/dist-server/service/chat-session-participant/index.d.ts +4 -0
- package/dist-server/service/chat-session-participant/index.js +10 -0
- package/dist-server/service/chat-session-participant/index.js.map +1 -0
- package/dist-server/service/chat-session-participant/invite-notification.d.ts +27 -0
- package/dist-server/service/chat-session-participant/invite-notification.js +30 -0
- package/dist-server/service/chat-session-participant/invite-notification.js.map +1 -0
- package/dist-server/service/chat-session-participant/presence.d.ts +4 -0
- package/dist-server/service/chat-session-participant/presence.js +22 -0
- package/dist-server/service/chat-session-participant/presence.js.map +1 -0
- package/dist-server/service/chat-session-resolver.d.ts +93 -0
- package/dist-server/service/chat-session-resolver.js +706 -0
- package/dist-server/service/chat-session-resolver.js.map +1 -0
- package/dist-server/service/index.d.ts +24 -0
- package/dist-server/service/index.js +39 -0
- package/dist-server/service/index.js.map +1 -0
- package/dist-server/service/subscribe-auth.d.ts +14 -0
- package/dist-server/service/subscribe-auth.js +33 -0
- package/dist-server/service/subscribe-auth.js.map +1 -0
- package/dist-server/service/types.d.ts +5 -0
- package/dist-server/service/types.js +12 -0
- package/dist-server/service/types.js.map +1 -0
- package/dist-server/tsconfig.tsbuildinfo +1 -0
- package/package.json +48 -0
- package/server/index.ts +28 -0
- package/server/service/chat-message/chat-message-broadcast.integration.test.ts +73 -0
- package/server/service/chat-message/chat-message-broadcast.test.ts +40 -0
- package/server/service/chat-message/chat-message-broadcast.ts +27 -0
- package/server/service/chat-message/chat-message-publish.ts +22 -0
- package/server/service/chat-message/chat-message-subscription.ts +45 -0
- package/server/service/chat-message/chat-message.ts +195 -0
- package/server/service/chat-message/fold-history.test.ts +98 -0
- package/server/service/chat-message/fold-history.ts +77 -0
- package/server/service/chat-message/history-summary.test.ts +127 -0
- package/server/service/chat-message/history-summary.ts +100 -0
- package/server/service/chat-message/index.ts +12 -0
- package/server/service/chat-message/llm-history.test.ts +194 -0
- package/server/service/chat-message/llm-history.ts +163 -0
- package/server/service/chat-session/chat-session.ts +193 -0
- package/server/service/chat-session/index.ts +5 -0
- package/server/service/chat-session/session-activity-broadcast.ts +57 -0
- package/server/service/chat-session/session-activity-publish.ts +44 -0
- package/server/service/chat-session/session-activity-subscription.ts +67 -0
- package/server/service/chat-session/session-anchor.ts +106 -0
- package/server/service/chat-session/session-inbox.test.ts +185 -0
- package/server/service/chat-session/session-inbox.ts +161 -0
- package/server/service/chat-session-participant/chat-session-participant.ts +135 -0
- package/server/service/chat-session-participant/index.ts +6 -0
- package/server/service/chat-session-participant/invite-notification.test.ts +30 -0
- package/server/service/chat-session-participant/invite-notification.ts +48 -0
- package/server/service/chat-session-participant/presence.test.ts +39 -0
- package/server/service/chat-session-participant/presence.ts +21 -0
- package/server/service/chat-session-resolver.ts +644 -0
- package/server/service/index.ts +37 -0
- package/server/service/session-anchor.test.ts +133 -0
- package/server/service/subscribe-auth.test.ts +57 -0
- package/server/service/subscribe-auth.ts +34 -0
- package/server/service/types.ts +15 -0
- package/things-factory.config.js +1 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ์ด๋ ฅ ์์ฝ ์ ๊ธฐ ๊ท์น ๊ฒ์ฆ.
|
|
3
|
+
*
|
|
4
|
+
* ๋ฌด์์ ์งํค๋๊ฐ:
|
|
5
|
+
* โ **๋์ ** โ ์ด์ ์์ฝ ๋ค๋ถํฐ๋ง ์๋ก ์ ๋๋ค(์ ์ฒด ์ฌ์์ฝ=๋น์ฉ, ์ค๋ณต ์ ๊ธฐ=์๊ณก).
|
|
6
|
+
* โก **์๊ปด ๋ง๋ ๋ค** โ ์๋ก ๋ฐ๋ ค๋ ๊ฒ์ด ์๊ณ์น๋ฅผ ๋์ ๋๋ง. ์ํ ๋๊ธด ๋ค ๋งค ํด ๋ถ๋ฅด๋ฉด ํธ์ถ์ด ๋ ๋ฐฐ.
|
|
7
|
+
* โข **๋ฎ์ ๋ฒ์๋ฅผ ํจ๊ป ์ ์ฅ** โ ๋ณธ๋ฌธ๊ณผ upToMessageId ๋ ํญ์ ๊ฐ์ด ๊ฐ๋ค(๋ฐ๋ก ์ ์ฅํ๋ฉด ์ด๊ธ๋๋ค).
|
|
8
|
+
* โฃ **๋ชจ๋ฅด๋ฉด ๋ค์ ์์ฝ** โ ๊ธฐ๋ก๋ id ๋ฅผ ์ด๋ ฅ์์ ๋ชป ์ฐพ์ผ๋ฉด ์ ๋ถ ๋ฏธ๋ฐ์์ผ๋ก ๋ณธ๋ค(๋งฅ๋ฝ ์์ค ๋ฐฉ์ง).
|
|
9
|
+
* โค **์ง์๋ ์ฌ์ค ์์ฃผ** โ ๊ฒฐ์ ยท๋ฏธ๊ฒฐยท์๋ณ์ ๋ณด์กด์ ์๊ตฌํ๊ณ ์ฐฝ์์ ๊ธ์งํ๋ค.
|
|
10
|
+
*/
|
|
11
|
+
import {
|
|
12
|
+
splitByCap,
|
|
13
|
+
pendingForSummary,
|
|
14
|
+
shouldResummarize,
|
|
15
|
+
summarySourceText,
|
|
16
|
+
summaryUserContent,
|
|
17
|
+
summaryInstruction,
|
|
18
|
+
summaryPatch
|
|
19
|
+
} from './history-summary'
|
|
20
|
+
|
|
21
|
+
const rows = (n: number, from = 0) =>
|
|
22
|
+
Array.from({ length: n }, (_, i) => ({
|
|
23
|
+
id: `m${from + i}`,
|
|
24
|
+
role: (from + i) % 2 ? 'assistant' : 'user',
|
|
25
|
+
content: `๋ง ${from + i}`
|
|
26
|
+
}))
|
|
27
|
+
|
|
28
|
+
describe('splitByCap', () => {
|
|
29
|
+
it('์ํ ์ดํ๋ฉด ์๋ฌด๊ฒ๋ ๋ฐ๋ ค๋์ง ์๋๋ค', () => {
|
|
30
|
+
const r = splitByCap(rows(5), 10)
|
|
31
|
+
expect(r.dropped).toEqual([])
|
|
32
|
+
expect(r.kept).toHaveLength(5)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('์ํ์ ๋์ผ๋ฉด ์๋ถ๋ถ์ด ๋ฐ๋ ค๋๊ณ ์ต๊ทผ์ด ๋จ๋๋ค', () => {
|
|
36
|
+
const r = splitByCap(rows(12), 4)
|
|
37
|
+
expect(r.dropped).toHaveLength(8)
|
|
38
|
+
expect(r.kept.map(x => x.id)).toEqual(['m8', 'm9', 'm10', 'm11'])
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('pendingForSummary โ ๋์ ', () => {
|
|
43
|
+
const dropped = rows(8)
|
|
44
|
+
|
|
45
|
+
it('์์ฝ์ด ์์ผ๋ฉด ๋ฐ๋ ค๋ ์ ๋ถ๊ฐ ๋์', () => {
|
|
46
|
+
expect(pendingForSummary(dropped, undefined)).toHaveLength(8)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('์์ฝ์ด ๋ฎ์ ์ง์ ๋ค๋ถํฐ๋ง ๋์ โ ์ด๋ฏธ ์ ์ ๊ฒ์ ๋ ์ ์ง ์๋๋ค', () => {
|
|
50
|
+
const p = pendingForSummary(dropped, { text: '์๋ถ๋ถ ์์ฝ', upToMessageId: 'm4' })
|
|
51
|
+
expect(p.map(x => x.id)).toEqual(['m5', 'm6', 'm7'])
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('๊ธฐ๋ก๋ id ๋ฅผ ๋ชป ์ฐพ์ผ๋ฉด ์ ๋ถ ๋ค์ โ ๋น ๋จ๋ฆฌ๋ ์ชฝ๋ณด๋ค ์์ ํ๋ค', () => {
|
|
55
|
+
const p = pendingForSummary(dropped, { text: '์์ฝ', upToMessageId: 'deleted' })
|
|
56
|
+
expect(p).toHaveLength(8)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('๋ณธ๋ฌธ ์๋ ๊ธฐ๋ก์ ์์ฝ์ด ์๋ ๊ฒ์ผ๋ก ๋ณธ๋ค', () => {
|
|
60
|
+
expect(pendingForSummary(dropped, { text: '', upToMessageId: 'm4' })).toHaveLength(8)
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
describe('shouldResummarize โ ์๊ปด ๋ง๋ค๊ธฐ', () => {
|
|
65
|
+
it('์๊ณ์น๋ฅผ ๋์ผ๋ฉด ๋ง๋ ๋ค', () => {
|
|
66
|
+
expect(shouldResummarize(rows(10), 10)).toBe(true)
|
|
67
|
+
})
|
|
68
|
+
it('์๊ณ์น ๋ฏธ๋ง์ด๋ฉด ๋ง๋ค์ง ์๋๋ค โ ๋งค ํด ํธ์ถ ๋ฐฉ์ง', () => {
|
|
69
|
+
expect(shouldResummarize(rows(9), 10)).toBe(false)
|
|
70
|
+
})
|
|
71
|
+
it('๋ฐ๋ ค๋ ๊ฒ์ด ์์ผ๋ฉด ๋ง๋ค์ง ์๋๋ค', () => {
|
|
72
|
+
expect(shouldResummarize([], 10)).toBe(false)
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe('์์ฝ ์
๋ ฅ', () => {
|
|
77
|
+
it('๋ฐ์ ์ ํ๊ธฐ๋ฅผ ์ ์งํ๋ค โ ํ์์์ ๋๊ฐ ๋งํ๋์ง๊ฐ ํต์ฌ ์ ๋ณด', () => {
|
|
78
|
+
const text = summarySourceText([
|
|
79
|
+
{ id: 'a', role: 'user', content: '๋ํฌ ํฌํ ํ์ธ ํ์', senderName: '๊น์ฒ ์' },
|
|
80
|
+
{ id: 'b', role: 'assistant', content: 'dock-1 ์ ์ 92%' },
|
|
81
|
+
{ id: 'c', role: 'system', content: '์คํ๋จ: o7 ๋ณด๋ฅ' }
|
|
82
|
+
])
|
|
83
|
+
expect(text).toContain('๊น์ฒ ์: ๋ํฌ ํฌํ ํ์ธ ํ์')
|
|
84
|
+
expect(text).toContain('AI: dock-1 ์ ์ 92%')
|
|
85
|
+
expect(text).toContain('SYSTEM: ์คํ๋จ: o7 ๋ณด๋ฅ')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
it('์ด์ ์์ฝ์ ์์ ์น์ด ๋์ ์
๋ ฅ์ ๋ง๋ ๋ค', () => {
|
|
89
|
+
const content = summaryUserContent('์์์ ์ ํ ๊ฒ: ์ง๊ฒ์ฐจ 1๋ ์ถ๊ฐ', rows(2))
|
|
90
|
+
expect(content).toContain('SUMMARY SO FAR')
|
|
91
|
+
expect(content).toContain('์ง๊ฒ์ฐจ 1๋ ์ถ๊ฐ')
|
|
92
|
+
expect(content).toContain('NEW MESSAGES TO FOLD IN')
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('์ด์ ์์ฝ์ด ์์ผ๋ฉด ์ ๋ํ๋ง ๋ฃ๋๋ค', () => {
|
|
96
|
+
expect(summaryUserContent(null, rows(2))).not.toContain('SUMMARY SO FAR')
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
describe('์์ฝ ์ง์', () => {
|
|
101
|
+
const instruction = summaryInstruction()
|
|
102
|
+
|
|
103
|
+
it('๊ฒฐ์ ยท๋ฏธ๊ฒฐ์ ๋จ๊ธฐ๋ผ๊ณ ์๊ตฌํ๋ค โ ๊ทธ๊ฒ ๋์ค๊น์ง ํ์ํ ๊ฒ์ด๋ค', () => {
|
|
104
|
+
expect(instruction).toMatch(/decided/i)
|
|
105
|
+
expect(instruction).toMatch(/still open/i)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('์๋ณ์๋ฅผ ๊ทธ๋๋ก ๋ณด์กดํ๋ผ๊ณ ์๊ตฌํ๋ค โ ์ด๋ฆ์ด ๋ฐ๋๋ฉด ์ดํ ์ ์ง๊ฐ ๊นจ์ง๋ค', () => {
|
|
109
|
+
expect(instruction).toMatch(/identifiers/i)
|
|
110
|
+
expect(instruction).toMatch(/EXACTLY/)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('์๋ ์ฌ์ค์ ๋ํ์ง ๋ง๋ผ๊ณ ๋ชป๋ฐ๋๋ค', () => {
|
|
114
|
+
expect(instruction).toMatch(/Never add facts/i)
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
describe('summaryPatch โ ๋ณธ๋ฌธ๊ณผ ๋ฒ์๋ ํจ๊ป', () => {
|
|
119
|
+
it('๋ง์ง๋ง์ผ๋ก ์ ์ ๋ฉ์์ง id ๋ฅผ ํจ๊ป ์ ์ฅํ๋ค', () => {
|
|
120
|
+
const patch = summaryPatch('์์ฝ ๋ณธ๋ฌธ', rows(3))
|
|
121
|
+
expect(patch).toEqual({ lastSummary: '์์ฝ ๋ณธ๋ฌธ', summaryUpToMessageId: 'm2' })
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('๋ณธ๋ฌธ์ ์ํ(4000)์ผ๋ก ์๋ฅธ๋ค โ ์์ฝ์ด ํ๋กฌํํธ๋ฅผ ๋ค์ ๋ถํ๋ฆฌ์ง ์๊ฒ', () => {
|
|
125
|
+
expect(summaryPatch('๊ฐ'.repeat(5000), rows(1)).lastSummary).toHaveLength(4000)
|
|
126
|
+
})
|
|
127
|
+
})
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ์ด๋ ฅ ์์ฝ ์ ๊ธฐ โ ๊ธด ํ์์ ์๋ถ๋ถ์ **์์ฝ์ผ๋ก ๋์ฒด**ํ๋ค.
|
|
3
|
+
*
|
|
4
|
+
* โโ ์ ํ์ํ๊ฐ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
5
|
+
* ๋ชจ๋ธ์ ๊ธฐ์ต์ด ์์ด ๋งค ํด ์ด๋ ฅ์ ๋ค์ ๋ฐ๋๋ค. ๊ทธ๋์ ์ํ(maxTurns)์ ๋์์ง๋ง, ์ํ๋ง์ผ๋ก๋
|
|
6
|
+
* ๋์น ์๋ถ๋ถ์ด **๊ทธ๋ฅ ์ฌ๋ผ์ง๋ค** โ ๋ฉฐ์น ์ด์ด์ง๋ ํ์์์ ์์์ ํฉ์ํ ๋ด์ฉ์ด ์๋ ์ผ์ด ๋๋ค.
|
|
7
|
+
* ์์ฝ์ ๋์ ์ค์ด ๋ณด๋ด๋ฉด ํ ํฐ์ ์ํ ์์ ๋จธ๋ฌผ๋ฉด์ ๋งฅ๋ฝ์ ๋จ๋๋ค.
|
|
8
|
+
*
|
|
9
|
+
* โโ ๊ท์จ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
10
|
+
* โ **์ด๋๊น์ง ์์ฝํ๋์ง ๊ธฐ๋กํ๋ค**(upToMessageId). ๊ทธ๊ฒ์ด ์์ผ๋ฉด ๋งค ํด ์ ์ฒด๋ฅผ ๋ค์ ์์ฝํ๊ฑฐ๋
|
|
11
|
+
* (๋น์ฉ) ์ด๋ฏธ ์์ฝํ ๊ฒ์ ๋ ์ด์ด ๋ถ์ธ๋ค(์ค๋ณต).
|
|
12
|
+
* โก **๋์ ์์ฝ**์ด๋ค: ์ด์ ์์ฝ + ์๋ก ๋ฐ๋ ค๋ ๋ฉ์์ง โ ์ ์์ฝ. ์๋ณธ ์ ์ฒด๋ฅผ ๋ค์ ์ฝ์ง ์๋๋ค.
|
|
13
|
+
* โข **์๊ปด ๋ง๋ ๋ค**: ์๋ก ๋ฐ๋ ค๋ ๊ฒ์ด ์๊ณ์น๋ฅผ ๋์ ๋๋ง ๋ค์ ๋ง๋ ๋ค. ์ํ์ ๋๊ธด ๋ค ๋งค ํด
|
|
14
|
+
* ์์ฝ์ ๋ง๋ค๋ฉด ๋ํ๋ง๋ค LLM ํธ์ถ์ด ๋ ๋ฒ์ฉ ๋ถ๋๋ค.
|
|
15
|
+
* โฃ **์์ฝ์ ์์ฝ์ด๋ผ๊ณ ๋ฐํ๋ค**: ํ๋กฌํํธ์ "์์ฝ(์๋ฌธ ์๋)" ์ผ๋ก ํ๊ธฐํด ๋ชจ๋ธ์ด ๊ทธ๊ฒ์ ์ธ์ฉ
|
|
16
|
+
* ๊ฐ๋ฅํ ์๋ฌธ์ฒ๋ผ ๋ค๋ฃจ์ง ์๊ฒ ํ๋ค. ์์ฝ์ ์๋ ์ฌ์ค์ ์ฌ์ฉ์์๊ฒ ๋ค์ ํ์ธํด์ผ ํ๋ค.
|
|
17
|
+
*
|
|
18
|
+
* ์ด ํ์ผ์ ์์ ๋ก์ง์ด๋ค โ LLM ํธ์ถยทDB ๋ ๋ฆฌ์กธ๋ฒ๊ฐ ํ๋ค(ํ
์คํธ ๊ฐ๋ฅ์ฑ ์ ์ง).
|
|
19
|
+
*/
|
|
20
|
+
import type { HistoryRow } from './llm-history.js'
|
|
21
|
+
|
|
22
|
+
/** ์ธ์
์ ์ ์ฅ๋ ์์ฝ ์ํ. */
|
|
23
|
+
export interface StoredSummary {
|
|
24
|
+
/** ์์ฝ ๋ณธ๋ฌธ(์ฌ๋์ด ์ฝ์ด๋ ๋๋ ๋ช ๋ฌธ์ฅ). ์์ผ๋ฉด ์์ง ์์ฝ์ด ์๋ค. */
|
|
25
|
+
text?: string | null
|
|
26
|
+
/** ์ด ๋ฉ์์ง๊น์ง ์์ฝ์ ๋ฐ์๋๋ค. ์ดํ ๋ฉ์์ง๋ ์์ง ์์ฝ ๋ฐ. */
|
|
27
|
+
upToMessageId?: string | null
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** ์ํ ๊ธฐ์ค์ผ๋ก ์ด๋ ฅ์ ๋๋ก ๋๋๋ค โ ๋ฐ๋ ค๋๋ ์๋ถ๋ถ๊ณผ ์๋ฌธ์ผ๋ก ๋จ๊ธธ ์ต๊ทผ ๋ถ๋ถ. */
|
|
31
|
+
export function splitByCap(rows: HistoryRow[], maxTurns: number): { dropped: HistoryRow[]; kept: HistoryRow[] } {
|
|
32
|
+
if (!maxTurns || maxTurns <= 0 || rows.length <= maxTurns) return { dropped: [], kept: rows }
|
|
33
|
+
return { dropped: rows.slice(0, rows.length - maxTurns), kept: rows.slice(-maxTurns) }
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* ์์ง ์์ฝ์ ๋ฐ์๋์ง ์์, ๋ฐ๋ ค๋ ๋ฉ์์ง๋ค.
|
|
38
|
+
*
|
|
39
|
+
* ์ ์ฅ๋ ์์ฝ์ด ์ด๋๊น์ง ๋ฎ๋์ง(upToMessageId)๋ฅผ ๊ธฐ์ค์ผ๋ก ๊ทธ **๋ค๋ถํฐ** ์ผ๋ค. ๊ธฐ๋ก๋ id ๊ฐ ์ด๋ ฅ์
|
|
40
|
+
* ์์ผ๋ฉด(์ญ์ ยทํธ์ง) ๋ฎ์ ๋ฒ์๋ฅผ ์ ์ ์์ผ๋ฏ๋ก **์ ๋ถ ๋ฏธ๋ฐ์์ผ๋ก ๋ณธ๋ค** โ ๋น ๋จ๋ฆฌ๋ ์ชฝ๋ณด๋ค ๋ค์
|
|
41
|
+
* ์์ฝํ๋ ์ชฝ์ด ์์ ํ๋ค(๋งฅ๋ฝ ์์ค์ ๋ต์ ๋ง์น๋ค).
|
|
42
|
+
*/
|
|
43
|
+
export function pendingForSummary(dropped: HistoryRow[], stored: StoredSummary | undefined): HistoryRow[] {
|
|
44
|
+
const upTo = stored?.upToMessageId
|
|
45
|
+
if (!upTo || !stored?.text) return dropped
|
|
46
|
+
const idx = dropped.findIndex(r => r.id === upTo)
|
|
47
|
+
return idx < 0 ? dropped : dropped.slice(idx + 1)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* ์์ฝ์ ๋ค์ ๋ง๋ค ๋์ธ๊ฐ โ ์๋ก ๋ฐ๋ ค๋ ๊ฒ์ด ์๊ณ์น ์ด์์ผ ๋๋ง.
|
|
52
|
+
* (์ํ์ ๋๊ธด ๋ค ๋งค ํด ๋ง๋ค๋ฉด ๋ํ๋ง๋ค LLM ํธ์ถ์ด ๋ ๋ฒ์ฉ ๋ถ๋๋ค.)
|
|
53
|
+
*/
|
|
54
|
+
export function shouldResummarize(pending: HistoryRow[], threshold = 10): boolean {
|
|
55
|
+
return pending.length >= Math.max(1, threshold)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** ์์ฝ ๋์ ํ
์คํธ โ ๋ฐ์ ์ ํ๊ธฐ๋ฅผ ์ ์งํด ๋๊ฐ ๋ฌด์์ ๋งํ๋์ง ๋จ๊ธด๋ค(ํ์์ ํต์ฌ ์ ๋ณด). */
|
|
59
|
+
export function summarySourceText(pending: HistoryRow[]): string {
|
|
60
|
+
return pending
|
|
61
|
+
.map(r => {
|
|
62
|
+
const who = r.role === 'assistant' ? 'AI' : r.role === 'system' ? 'SYSTEM' : r.senderName || 'USER'
|
|
63
|
+
return `${who}: ${(r.content ?? '').trim()}`
|
|
64
|
+
})
|
|
65
|
+
.filter(line => line.length > 6)
|
|
66
|
+
.join('\n')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* ์์ฝ ์์ฑ ์ง์ โ **์ฌ์ค๋ง, ์งง๊ฒ, ์ง์ด๋ด์ง ๋ง ๊ฒ.**
|
|
71
|
+
*
|
|
72
|
+
* ํ์์์ ๋์ค๊น์ง ํ์ํ ๊ฒ์ "๋ฌด์์ ํ์ธํ๊ณ , ๋ฌด์์ ์ ํ๊ณ , ๋ฌด์์ด ๋จ์๋์ง" ๋ค. ์ก๋ดยท์ธ์ฌ๋
|
|
73
|
+
* ๋ฒ๋ ค๋ ๋์ง๋ง **๊ฒฐ์ ๊ณผ ๋ฏธ๊ฒฐ**์ ๋จ์์ผ ํ๋ค. ์๋ณ์๋ ์๋ฌธ ๊ทธ๋๋ก ์ ์งํ๋ผ๊ณ ์ง์ํ๋ค โ ์์ฝ์ด
|
|
74
|
+
* ์ด๋ฆ์ ๋ฐ๊พธ๋ฉด ์ดํ ๋ํ์ ์ ์ง๊ฐ ๊นจ์ง๋ค.
|
|
75
|
+
*/
|
|
76
|
+
export function summaryInstruction(locale?: string): string {
|
|
77
|
+
return [
|
|
78
|
+
'Summarize the earlier part of a shared operations deliberation so it can be carried into later turns.',
|
|
79
|
+
'Keep ONLY what stays relevant: what was checked and found, what was decided, what is still open, and any action that was executed.',
|
|
80
|
+
'Drop greetings, small talk, and repetition.',
|
|
81
|
+
'Preserve identifiers (node / order / instance ids) EXACTLY as written โ renaming them breaks grounding in later turns.',
|
|
82
|
+
'Never add facts that are not in the text. If something is unclear, leave it out.',
|
|
83
|
+
'At most 8 short bullet points. No preamble, no closing remark.',
|
|
84
|
+
locale ? `Write the summary in the language of locale "${locale}".` : ''
|
|
85
|
+
]
|
|
86
|
+
.filter(Boolean)
|
|
87
|
+
.join(' ')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** ์ด์ ์์ฝ๊ณผ ์๋ก ๋ฐ๋ ค๋ ๋ํ๋ฅผ ํฉ์ณ ๋๊ธธ ๋ณธ๋ฌธ โ ๋์ ์์ฝ์ ์
๋ ฅ. */
|
|
91
|
+
export function summaryUserContent(previous: string | null | undefined, pending: HistoryRow[]): string {
|
|
92
|
+
const head = previous?.trim() ? `SUMMARY SO FAR:\n${previous.trim()}\n\n` : ''
|
|
93
|
+
return `${head}NEW MESSAGES TO FOLD IN:\n${summarySourceText(pending)}`
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** ์์ฝ ์ ์ฅ ๊ฐ โ ๋ณธ๋ฌธ๊ณผ "์ด๋๊น์ง ๋ฎ์๋์ง". ๋์ ํญ์ ํจ๊ป ์ ์ฅํ๋ค(๋ฐ๋ก ์ ์ฅํ๋ฉด ์ด๊ธ๋๋ค). */
|
|
97
|
+
export function summaryPatch(text: string, pending: HistoryRow[]): { lastSummary: string; summaryUpToMessageId?: string } {
|
|
98
|
+
const last = [...pending].reverse().find(r => r.id)?.id
|
|
99
|
+
return { lastSummary: text.trim().slice(0, 4000), summaryUpToMessageId: last }
|
|
100
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ChatMessage } from './chat-message.js'
|
|
2
|
+
|
|
3
|
+
export { ChatMessage }
|
|
4
|
+
export {
|
|
5
|
+
ChatMessageSubscription,
|
|
6
|
+
publishChatMessage,
|
|
7
|
+
CHAT_MESSAGE_TOPIC,
|
|
8
|
+
chatMessageFilter
|
|
9
|
+
} from './chat-message-subscription.js'
|
|
10
|
+
export type { ChatMessagePayload } from './chat-message-subscription.js'
|
|
11
|
+
|
|
12
|
+
export const entities = [ChatMessage]
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ์์ด์ ํธ์๊ฒ ๋๊ธธ ๋ํ ์ด๋ ฅ ์กฐ๋ฆฝ ๊ฒ์ฆ.
|
|
3
|
+
*
|
|
4
|
+
* ๋ฌด์์ ์งํค๋๊ฐ:
|
|
5
|
+
* โ **๋ฐ์ ์ ๊ตฌ๋ณ** โ ์ฌ๋ฌ ์ฌ๋์ด ์ฐธ์ฌํ ์ธ์
์์ ๋๊ฐ ๋งํ๋์ง ๋ชจ๋ธ์ด ์ ์ ์์ด์ผ ํ๋ค.
|
|
6
|
+
* ์์ ์๋ ๋ชจ๋ ์ฌ๋ ๋ฐ์ธ์ด role='user' ํ๋๋ก ๋ญ๊ฐ์ ธ, "์น์ธํฉ๋๋ค"๋ฅผ ๋๊ฐ ํ๋์ง ์ ์ ์์๋ค.
|
|
7
|
+
* โก **ํผ์ ๋ํ๋ ๊ทธ๋๋ก** โ ํ๊ธฐ๋ฅผ ๋ฃ์ผ๋ฉด ๋
ธ์ด์ฆยทํ ํฐ์ด ๋๊ณ ๊ธฐ์กด ํ๋กฌํํธ๊ฐ ๋ฌ๋ผ์ง๋ค.
|
|
8
|
+
* โข **system ๊ธฐ๋ก ์ ๋ฌ** โ ์บ๋ฒ์ค ์ง์ ํธ์ง์ ์์ด์ ํธ์๊ฒ ์๋ฆฌ๋ ค๊ณ ๋จ๊ธด ๊ฒ์ธ๋ฐ ์ฌ์ฅ๋ผ ์์๋ค.
|
|
9
|
+
* โฃ **์ ์ ๊ตฌ๊ฐ ์ ์ธ** โ ํธ์ง์ ํ๋ฉด์์๋ง ์ ๊ณ ์๋ฒ ์ด๋ ฅ์ ์ ์งํ๋ฏ๋ก, ์๋ฒ ์ ๋ณธ์ ์ฐ๋ฉด ์ ์ ๋ง์ด
|
|
10
|
+
* ๋์ด์๋๋ค. ๋จ ๋ฐฉ๊ธ ์ ์ฅํ ์ ๋ฉ์์ง๋ ์๊ฐ์ ๋ค์ ์์ด๋ ๋จ์์ผ ํ๋ค.
|
|
11
|
+
*
|
|
12
|
+
* ๋ฐ์ ์ ํ๊ธฐ๋ ์ถ๋ก ์ฉ ๋ฌธ๋งฅ์ด๋ฉฐ **๊ถํ ํ์ ๊ทผ๊ฑฐ๊ฐ ์๋๋ค**(์ฌ์ฉ์๊ฐ ํ๋ด๋ผ ์ ์๋ค) โ ์น์ธ์
|
|
13
|
+
* ์๋ฒ ๊ฒ์ดํธ๊ฐ ํ์ ํ๋ค. ๊ทธ ์ฑ์ง๋ ์๋์์ ๋ช
์ํ๋ค.
|
|
14
|
+
*/
|
|
15
|
+
import { buildLlmHistory, type HistoryRow } from './llm-history'
|
|
16
|
+
|
|
17
|
+
const u = (id: string, content: string, senderId: string, senderName?: string): HistoryRow => ({
|
|
18
|
+
id,
|
|
19
|
+
role: 'user',
|
|
20
|
+
content,
|
|
21
|
+
senderId,
|
|
22
|
+
senderName
|
|
23
|
+
})
|
|
24
|
+
const a = (id: string, content: string): HistoryRow => ({ id, role: 'assistant', content })
|
|
25
|
+
const sys = (id: string, content: string): HistoryRow => ({ id, role: 'system', content })
|
|
26
|
+
|
|
27
|
+
describe('๋ฐ์ ์ ํ๊ธฐ', () => {
|
|
28
|
+
it('์ฌ๋์ด ํ ๋ช
์ด๋ฉด ํ๊ธฐํ์ง ์๋๋ค โ ๊ธฐ์กด ๋ํ์ ๋์ผํ ํ๋กฌํํธ', () => {
|
|
29
|
+
const out = buildLlmHistory([u('1', '๋ ์ถ๊ฐํด์ค', 'U1', '๊น์ฒ ์'), a('2', '์ถ๊ฐํ์ต๋๋ค')])
|
|
30
|
+
expect(out).toEqual([
|
|
31
|
+
{ role: 'user', content: '๋ ์ถ๊ฐํด์ค' },
|
|
32
|
+
{ role: 'assistant', content: '์ถ๊ฐํ์ต๋๋ค' }
|
|
33
|
+
])
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('์ฌ๋์ด ๋ ์ด์์ด๋ฉด ๋ชจ๋ ์ฌ๋ ๋ฐ์ธ์ ํ๊ธฐํ๋ค โ ๋๊ฐ ๋งํ๋์ง ๊ตฌ๋ณ ๊ฐ๋ฅ', () => {
|
|
37
|
+
const out = buildLlmHistory([
|
|
38
|
+
u('1', '์ด ์กฐ์น ์งํํ ๊น์?', 'U1', '๊น์ฒ ์'),
|
|
39
|
+
u('2', '์น์ธํฉ๋๋ค', 'U2', '๋ฐ์ํฌ'),
|
|
40
|
+
a('3', '์คํ ์ค๋น๋จ')
|
|
41
|
+
])
|
|
42
|
+
expect(out).toEqual([
|
|
43
|
+
{ role: 'user', content: '[๊น์ฒ ์] ์ด ์กฐ์น ์งํํ ๊น์?' },
|
|
44
|
+
{ role: 'user', content: '[๋ฐ์ํฌ] ์น์ธํฉ๋๋ค' },
|
|
45
|
+
{ role: 'assistant', content: '์คํ ์ค๋น๋จ' }
|
|
46
|
+
])
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('์ด๋ฆ์ด ์์ผ๋ฉด ๋น ๋๊ดํธ๋ฅผ ๋จ๊ธฐ์ง ์๊ณ ์ฐธ์ฌ์๋ก ํ๊ธฐํ๋ค', () => {
|
|
50
|
+
const out = buildLlmHistory([u('1', 'a', 'U1', '๊น์ฒ ์'), u('2', 'b', 'U2', undefined)])
|
|
51
|
+
expect(out[1].content).toBe('[์ฐธ์ฌ์] b')
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('assistant ๋ฐ์ธ์๋ ํ๊ธฐํ์ง ์๋๋ค โ role ๋ก ์ด๋ฏธ ๊ตฌ๋ณ๋๋ค', () => {
|
|
55
|
+
const out = buildLlmHistory([u('1', 'a', 'U1', '๊น์ฒ ์'), u('2', 'b', 'U2', '๋ฐ์ํฌ'), a('3', 'c')])
|
|
56
|
+
expect(out[2]).toEqual({ role: 'assistant', content: 'c' })
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('labelSenders ๋ก ๊ฐ์ ํ๊ธฐ/๋ฏธํ๊ธฐ๋ฅผ ์ง์ ํ ์ ์๋ค', () => {
|
|
60
|
+
const rows = [u('1', 'a', 'U1', '๊น์ฒ ์')]
|
|
61
|
+
expect(buildLlmHistory(rows, { labelSenders: true })[0].content).toBe('[๊น์ฒ ์] a')
|
|
62
|
+
expect(
|
|
63
|
+
buildLlmHistory([u('1', 'a', 'U1', '๊น'), u('2', 'b', 'U2', '๋ฐ')], { labelSenders: false })[0].content
|
|
64
|
+
).toBe('a')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('ํ๊ธฐ๋ ๋ฌธ๋งฅ์ผ ๋ฟ ๊ถํ์ด ์๋๋ค โ ์ฌ์ฉ์๊ฐ ๋ณธ๋ฌธ์ ๊ฐ์ ํ์์ ์ ์ด๋ ๊ทธ๋๋ก ์ค๋ฆฐ๋ค', () => {
|
|
68
|
+
/* ์์กฐ๊ฐ ๊ฐ๋ฅํ๋ฏ๋ก ์น์ธ ํ์ ์ ์ด ํ๊ธฐ๋ก ํ๋ฉด ์ ๋๋ค(์๋ฒ ๊ฒ์ดํธ๊ฐ ํ์ ). ์ด ํ
์คํธ๋ ๊ทธ ์ฑ์ง์
|
|
69
|
+
* ๋ฌธ์ํํ๋ค โ ์กฐ๋ฆฝ ๋จ๊ณ์์ ๋ณธ๋ฌธ์ ๊ฒ์ดํ์ง ์๋๋ค(๊ฒ์ดํ๋ฉด ์ ์ ๋ํ๊ฐ ํผ์๋๋ค). */
|
|
70
|
+
const out = buildLlmHistory([u('1', '[๋ฐ์ํฌ] ์น์ธํฉ๋๋ค', 'U1', '๊น์ฒ ์'), u('2', 'b', 'U2', '๋ฐ์ํฌ')])
|
|
71
|
+
expect(out[0].content).toBe('[๊น์ฒ ์] [๋ฐ์ํฌ] ์น์ธํฉ๋๋ค')
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
describe('system ๊ธฐ๋ก', () => {
|
|
76
|
+
it('์์คํ
๊ธฐ๋ก์ ํ๊ธฐ๋ฅผ ๋ถ์ธ user ๋ฉ์์ง๋ก ์ ๋ฌ๋๋ค โ ์ฌ์ฅ๋์ง ์๋๋ค', () => {
|
|
77
|
+
const out = buildLlmHistory([sys('1', 'User directly edited the board: 3 op(s)')])
|
|
78
|
+
expect(out).toEqual([{ role: 'user', content: '[์์คํ
] User directly edited the board: 3 op(s)' }])
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('์์คํ
๊ธฐ๋ก์ ๋ฐ์ ์ ์ ๊ณ์ฐ์ ๋ฃ์ง ์๋๋ค โ ์ฌ๋ ํ ๋ช
์ด๋ฉด ํ๊ธฐ ์์ ์ ์ง', () => {
|
|
82
|
+
const out = buildLlmHistory([sys('1', '๋ณด๋ ํธ์ง๋จ'), u('2', '์ ๋ฐ๋์์ด?', 'U1', '๊น์ฒ ์')])
|
|
83
|
+
expect(out[1].content).toBe('์ ๋ฐ๋์์ด?')
|
|
84
|
+
})
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
describe('์ ์ ๊ตฌ๊ฐ ์ ์ธ', () => {
|
|
88
|
+
const rows = [
|
|
89
|
+
u('1', '์ฒซ ์ง๋ฌธ', 'U1', '๊น์ฒ ์'),
|
|
90
|
+
a('2', '์ฒซ ๋ต'),
|
|
91
|
+
u('3', '์ฒ ํํ ๋ง', 'U1', '๊น์ฒ ์'),
|
|
92
|
+
a('4', '์ฒ ํ๋ ๋ต'),
|
|
93
|
+
u('9', '๊ณ ์ณ ์ด ์ง๋ฌธ', 'U1', '๊น์ฒ ์')
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
it('์ง์ ํ ๋ฉ์์ง ์ดํ๋ฅผ ๋ฒ๋ฆฌ๋, ํญ์ ์ ์งํ ๋ฉ์์ง๋ ๋จ๊ธด๋ค', () => {
|
|
97
|
+
const out = buildLlmHistory(rows, { truncateAfterMessageId: '2', keepMessageIds: ['9'] })
|
|
98
|
+
expect(out.map(m => m.content)).toEqual(['์ฒซ ์ง๋ฌธ', '์ฒซ ๋ต', '๊ณ ์ณ ์ด ์ง๋ฌธ'])
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
it('์ง์ ์ด ์์ผ๋ฉด ์ ๋ถ ์ฌ์ฉํ๋ค', () => {
|
|
102
|
+
expect(buildLlmHistory(rows)).toHaveLength(5)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('์๋ id ๋ฅผ ์ง์ ํ๋ฉด ์ ๋ถ ์ ์งํ๋ค โ ์กฐ์ฉํ ๋ค ๋ฒ๋ฆฌ๋ฉด ๋ฌธ๋งฅ์ด ์ฌ๋ผ์ ธ ๋ต์ด ๋ง๊ฐ์ง๋ค', () => {
|
|
106
|
+
expect(buildLlmHistory(rows, { truncateAfterMessageId: 'nope' })).toHaveLength(5)
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
it('keepMessageIds ๊ฐ ์์ผ๋ฉด ์ ์ ๋ค ๊ฒ์ ๋จ์ง ์๋๋ค', () => {
|
|
110
|
+
const out = buildLlmHistory(rows, { truncateAfterMessageId: '2' })
|
|
111
|
+
expect(out.map(m => m.content)).toEqual(['์ฒซ ์ง๋ฌธ', '์ฒซ ๋ต'])
|
|
112
|
+
})
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
describe('๊ธฐํ', () => {
|
|
116
|
+
it('๋น ๋ณธ๋ฌธ์ ๋๊ธฐ์ง ์๋๋ค(pending ์๋ฆฌ ๋ฑ)', () => {
|
|
117
|
+
const out = buildLlmHistory([u('1', ' ', 'U1', '๊น์ฒ ์'), a('2', '๋ต')])
|
|
118
|
+
expect(out).toEqual([{ role: 'assistant', content: '๋ต' }])
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('๋ณธ๋ฌธ ์๋ค ๊ณต๋ฐฑ์ ์ ๋ฆฌ๋๋ค', () => {
|
|
122
|
+
expect(buildLlmHistory([u('1', ' ์ง๋ฌธ ', 'U1', '๊น')])[0].content).toBe('์ง๋ฌธ')
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
it('์
๋ ฅ ์์๋ฅผ ๊ทธ๋๋ก ๋ณด์กดํ๋ค โ ์ฌ์ ๋ ฌํ์ง ์๋๋ค(์กฐํ ์ชฝ์ด ์ ๋ ฌ์ ์์ )', () => {
|
|
126
|
+
const out = buildLlmHistory([a('1', 'A'), u('2', 'B', 'U1', '๊น'), a('3', 'C')])
|
|
127
|
+
expect(out.map(m => m.content)).toEqual(['A', 'B', 'C'])
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
describe('์ด๋ ฅ ์ํ โ ํ์๊ฐ ๊ธธ์ด์ง ๋', () => {
|
|
132
|
+
const rows = (n: number) =>
|
|
133
|
+
Array.from({ length: n }, (_, i) => ({ id: `m${i}`, role: i % 2 ? 'assistant' : 'user', content: `๋ง ${i}` }))
|
|
134
|
+
|
|
135
|
+
it('์ํ ๋ฏธ์ง์ ์ด๋ฉด ์ ๋ถ ๋ณด๋ธ๋ค โ ๊ธฐ์กด ๋์ ์ ์ง', () => {
|
|
136
|
+
expect(buildLlmHistory(rows(10))).toHaveLength(10)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
it('์ํ์ ๋์ผ๋ฉด ์ต๊ทผ ๊ฒ๋ง ๋จ๊ธด๋ค โ ์ค๋๋ ๋ง๋ณด๋ค ์ต๊ทผ ์ํฉ์ด ๋ต์ ์ข์ฐํ๋ค', () => {
|
|
140
|
+
const out = buildLlmHistory(rows(10), { maxTurns: 4 })
|
|
141
|
+
/* ์๋ต ์๋ฆผ 1์ค + ์ต๊ทผ 4์ค */
|
|
142
|
+
expect(out).toHaveLength(5)
|
|
143
|
+
expect(out[out.length - 1].content).toContain('๋ง 9')
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
it('์๋ต์ **๋ฐ๋์ ์๋ฆฐ๋ค** โ ์กฐ์ฉํ ๋ฒ๋ฆฌ๋ฉด ๋ชจ๋ธ์ด ๋จ์ ์ฒซ ์ค์ ๋ํ์ ์์์ผ๋ก ์คํดํ๋ค', () => {
|
|
147
|
+
const out = buildLlmHistory(rows(10), { maxTurns: 4 })
|
|
148
|
+
expect(out[0].content).toContain('6๊ฐ')
|
|
149
|
+
expect(out[0].content).toContain('์๋ต')
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
it('์ํ์ ๊ฑธ๋ฆฌ์ง ์์ผ๋ฉด ์๋ฆผ์ ๋ถ์ด์ง ์๋๋ค โ ์๋ ์๋ต์ ๋งํ์ง ์๋๋ค', () => {
|
|
153
|
+
const out = buildLlmHistory(rows(3), { maxTurns: 10 })
|
|
154
|
+
expect(out).toHaveLength(3)
|
|
155
|
+
expect(out[0].content).toBe('๋ง 0')
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('์ ์ ๊ตฌ๊ฐ ์ ๊ฑฐ๊ฐ ๋จผ์ , ์ํ์ด ๋์ค โ ๋ ๊ท์น์ด ๊ฒน์ณ๋ ์ต๊ทผ ๊ฒ์ด ๋จ๋๋ค', () => {
|
|
159
|
+
const out = buildLlmHistory(rows(10), { truncateAfterMessageId: 'm5', maxTurns: 3 })
|
|
160
|
+
expect(out).toHaveLength(4) // ์๋ฆผ + 3
|
|
161
|
+
expect(out[out.length - 1].content).toContain('๋ง 5')
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
describe('์ด๋ ฅ ์์ฝ ์ ๊ธฐ โ ํ๋กฌํํธ ์กฐ๋ฆฝ', () => {
|
|
166
|
+
const rows = (n: number) =>
|
|
167
|
+
Array.from({ length: n }, (_, i) => ({ id: `m${i}`, role: i % 2 ? 'assistant' : 'user', content: `๋ง ${i}` }))
|
|
168
|
+
|
|
169
|
+
it('์์ฝ์ด ์์ผ๋ฉด "์๋ต" ๋์ ์์ฝ์ ์ฃ๋๋ค โ ์์์ ์ ํ ๊ฒ์ด ์๋ ์ผ์ด ๋์ง ์๊ฒ', () => {
|
|
170
|
+
const out = buildLlmHistory(rows(10), {
|
|
171
|
+
maxTurns: 4,
|
|
172
|
+
summary: { text: '์์์ ์ ํ ๊ฒ: ์ง๊ฒ์ฐจ 1๋ ์ถ๊ฐ', upToMessageId: 'm5' }
|
|
173
|
+
})
|
|
174
|
+
expect(out[0].content).toContain('์ง๊ฒ์ฐจ 1๋ ์ถ๊ฐ')
|
|
175
|
+
expect(out[0].content).not.toContain('์๋ต๋์์ต๋๋ค.')
|
|
176
|
+
})
|
|
177
|
+
|
|
178
|
+
it('์์ฝ์์ ๋ฐํ๋ค โ ๋ชจ๋ธ์ด ์๋ฌธ์ฒ๋ผ ์ธ์ฉํ์ง ์๊ฒ', () => {
|
|
179
|
+
const out = buildLlmHistory(rows(10), { maxTurns: 4, summary: { text: '์์ฝ๋ณธ', upToMessageId: 'm5' } })
|
|
180
|
+
expect(out[0].content).toContain('์๋ฌธ ์๋')
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
it('์์ฝ์ด ๋ฐ๋ ค๋ ๊ตฌ๊ฐ์ ๋ค ๋ฎ์ง ๋ชปํ๋ฉด ๋จ์ ๊ฐ์๋ฅผ ๋ฐํ๋ค โ ์ต์ ์ด ์๋์ ์จ๊ธฐ์ง ์๋๋ค', () => {
|
|
184
|
+
/* ๋ฐ๋ ค๋ ๊ฒ์ m0..m5(6๊ฐ), ์์ฝ์ m3 ๊น์ง โ m4ยทm5 ๋ ๊ฐ๊ฐ ๋ฏธ๋ฐ์. */
|
|
185
|
+
const out = buildLlmHistory(rows(10), { maxTurns: 4, summary: { text: '์์ฝ๋ณธ', upToMessageId: 'm3' } })
|
|
186
|
+
expect(out[0].content).toContain('2๊ฐ')
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
it('์ํ์ ๊ฑธ๋ฆฌ์ง ์์ผ๋ฉด ์์ฝ์ ์ฃ์ง ์๋๋ค โ ํ์ ์๋ ๋ฌธ๋งฅ์ ๋ฃ์ง ์๋๋ค', () => {
|
|
190
|
+
const out = buildLlmHistory(rows(3), { maxTurns: 10, summary: { text: '์์ฝ๋ณธ', upToMessageId: 'm0' } })
|
|
191
|
+
expect(out).toHaveLength(3)
|
|
192
|
+
expect(out[0].content).toBe('๋ง 0')
|
|
193
|
+
})
|
|
194
|
+
})
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* ์์ด์ ํธ์๊ฒ ๋๊ธธ ๋ํ ์ด๋ ฅ ์กฐ๋ฆฝ โ ์์ ๋ก์ง(๋ฆฌ์กธ๋ฒ์ ๋ถ๋ฆฌํด ๋จ์ ํ
์คํธ ๊ฐ๋ฅ).
|
|
3
|
+
*
|
|
4
|
+
* โโ ์ ์๋ฒ ์ ๋ณธ์ธ๊ฐ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
5
|
+
* ์์ ์๋ ํด๋ผ์ด์ธํธ๊ฐ ํ๋ฉด์ ๋ค๊ณ ์๋ ์ค๋ค์ ๊ทธ๋๋ก LLM ์ ๋๊ฒผ๋ค. ํ์(์ฌ๋ฌ ์ฐธ์ฌ์)์์๋
|
|
6
|
+
* ์ฐธ์ฌ์๋ง๋ค ํ๋ฉด์ด ๋ฌ๋ผ **์์ด์ ํธ๊ฐ ์ฌ๋๋ง๋ค ๋ค๋ฅธ ๋ํ๋ฅผ ๋ณธ๋ค**. ์๋ฒ๋ ChatMessage ์ ์ ๋ณธ์
|
|
7
|
+
* ๊ฐ๊ณ ์์ผ๋ฏ๋ก ๊ทธ๊ฒ์ ์ด๋ค. ์ธ์
์ด ์๋ ์ฆ์ ๋ํ(ad-hoc)๋ง ํด๋ผ์ด์ธํธ ์ด๋ ฅ์ ์ด๋ค.
|
|
8
|
+
*
|
|
9
|
+
* โโ ๋ฐ์ ์ ํ๊ธฐ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
10
|
+
* LLMMessage ๋ `{ role, content }` ๋ฟ์ด๋ค โ ๋ฐ์ ์ ์นธ์ด ์๋ค(AnthropicยทGemini ๋ message.name ์
|
|
11
|
+
* ์ง์ํ์ง ์์ ์ด์์ฑ์ด ์๋ค). ๊ทธ๋์ ๋ฐ์ ์๋ **๋ณธ๋ฌธ ๋งจ ์ ํ๊ธฐ**๋ก๋ง ์ ๋ฌํ ์ ์๋ค.
|
|
12
|
+
*
|
|
13
|
+
* [๊น์ฒ ์] ์น์ธํฉ๋๋ค
|
|
14
|
+
*
|
|
15
|
+
* **์ด ํ๊ธฐ๋ ์ถ๋ก ์ฉ ๋ฌธ๋งฅ์ด๋ฉฐ ๊ถํ ํ์ ๊ทผ๊ฑฐ๊ฐ ์๋๋ค.** ์ฌ์ฉ์๊ฐ ๋ณธ๋ฌธ์ ๊ฐ์ ํ์์ ์ง์ ์ ์ ์
|
|
16
|
+
* ์์ผ๋ฏ๋ก, ๋๋๋ฆด ์ ์๋ ์กฐ์น์ ์น์ธ ์ฌ๋ถ๋ ๋ฐ๋์ ์๋ฒ ๊ฒ์ดํธ(์์ฒญ์ ์ ์ ๊ธฐ๋ฐ)๊ฐ ํ์ ํ๋ค.
|
|
17
|
+
* ํ๊ธฐ๋ "๋๊ฐ ๋ฌด์จ ๋งฅ๋ฝ์์ ๋งํ๋์ง"๋ฅผ ๋ชจ๋ธ์ด ์ดํดํ๊ฒ ํ๋ ์ฉ๋๋ก๋ง ์ด๋ค.
|
|
18
|
+
*
|
|
19
|
+
* ๋ฐ์ ์๊ฐ **ํ ๋ช
๋ฟ์ด๋ฉด ํ๊ธฐํ์ง ์๋๋ค** โ ํผ์ ์ฐ๋ ๋ํ(๋๋ค์)์ ๋
ธ์ด์ฆ์ ํ ํฐ์ ๋๋ฆฌ์ง ์๊ณ ,
|
|
20
|
+
* ๊ธฐ์กด ๋์๊ณผ ๊ฐ์ ํ๋กฌํํธ๋ฅผ ์ ์งํ๋ค.
|
|
21
|
+
*
|
|
22
|
+
* โโ system ํ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
23
|
+
* ์บ๋ฒ์ค ์ง์ ํธ์ง ๊ธฐ๋ก(`User directly edited the board: โฆ`)์ ์๋ ์์ด์ ํธ์๊ฒ ์๋ฆฌ๋ ค๊ณ ๋จ๊ธด ๊ฒ์ธ๋ฐ,
|
|
24
|
+
* ํด๋ผ์ด์ธํธ๊ฐ role='system' ์ ๊ฑธ๋ฌ๋ด ์ฌ์ฅ๋ผ ์์๋ค. LLMMessage ์ system ์ญํ ์ด ์์ผ๋ฏ๋ก
|
|
25
|
+
* `[์์คํ
]` ํ๊ธฐ๋ฅผ ๋ถ์ธ user ๋ฉ์์ง๋ก ๋๊ธด๋ค โ ์ฌ๋์ด ์๋๋ฏ๋ก ๋ฐ์ ์ ์ ๊ณ์ฐ์๋ ๋ฃ์ง ์๋๋ค.
|
|
26
|
+
*
|
|
27
|
+
* โโ ํธ์งยท์ฌ์์ฑ์ผ๋ก ์ ์ ๊ตฌ๊ฐ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
28
|
+
* ํด๋ผ์ด์ธํธ์ ๋ฉ์์ง ํธ์ง์ ํ๋ฉด์์๋ง ์ ๊ณ **์๋ฒ ์ด๋ ฅ์ ์ ์ง**ํ๋ค(board-ai-chat.editUserMessage).
|
|
29
|
+
* ์๋ฒ ์ ๋ณธ์ผ๋ก ๋ฐ๊พธ๋ฉด ์ ์ ๋ง์ด ์์ด์ ํธ ๋ฌธ๋งฅ์ ๋์ด์๋๋ค. ๊ทธ๋์ ํธ์ถ์๊ฐ `truncateAfterMessageId`
|
|
30
|
+
* (๋ง์ง๋ง์ผ๋ก ๋จ๊ธด ๋ฉ์์ง id)๋ฅผ ์ฃผ๋ฉด ๊ทธ ๋ค ํ์ ๋ฒ๋ฆฐ๋ค. ๋จ `keepMessageIds` ๋ก ์ง์ ํ ํ(๋ฐฉ๊ธ ์ ์ฅํ
|
|
31
|
+
* ์ ์ฌ์ฉ์ ๋ฉ์์ง)์ ์๊ฐ์ ๋ค์ ์์ด๋ ํญ์ ๋จ๊ธด๋ค.
|
|
32
|
+
*
|
|
33
|
+
* ์๋ ค์ง ํ๊ณ(ํํ ๋์๊ณผ ๋์ผ): ๊ณต์ ์ธ์
์์ ์ ์ ๊ตฌ๊ฐ์ **๋ค๋ฅธ ์ฌ๋์ ๋ฐ์ธ**์ด ์์ผ๋ฉด ๊ทธ๊ฒ๋
|
|
34
|
+
* ํจ๊ป ์ ์ธ๋๋ค. ์ค๋ ํด๋ผ์ด์ธํธ๊ฐ ๋ณด๋ด๋ ์ด๋ ฅ๋ ๊ฐ์ผ๋ฏ๋ก ์ด ๋จ๊ณ์์๋ ๋๋ฑ์ฑ์ ์ ์งํ๊ณ ,
|
|
35
|
+
* "๋ด ๋ฐ์ธ๋ง ์ ๊ธฐ"๋ ๋ณ๋ ๊ฐ์ ์ผ๋ก ๋ค๋ฃฌ๋ค.
|
|
36
|
+
*/
|
|
37
|
+
import type { LLMMessage } from '../types.js'
|
|
38
|
+
import { pendingForSummary, type StoredSummary } from './history-summary.js'
|
|
39
|
+
|
|
40
|
+
/** ์ด๋ ฅ ํ ์ค โ ์ ์ฅ๋ ChatMessage ์์ ํ์ํ ๊ฒ๋ง(์ํฐํฐยทtypeorm ๋น์์กด). */
|
|
41
|
+
export interface HistoryRow {
|
|
42
|
+
id?: string
|
|
43
|
+
role: string
|
|
44
|
+
/** ๋ฉ์
๋ง์ปค(#token{refid:N})๋ ํธ์ถ์๊ฐ ๋ฏธ๋ฆฌ ์ ๊ฑฐํด ๋๊ธด๋ค โ ๋ชจ๋ธ ์ฃผ์ ๋ถ์ฐยทํ ํฐ ๋ญ๋น ๋ฐฉ์ง. */
|
|
45
|
+
content: string
|
|
46
|
+
/** ๋ฐ์ ์ ์๋ณ์(์๋ฒ ๋ด๋ถ์ฉ โ ๋ฐ์ ์ ์ ๊ณ์ฐ์๋ง ์ฐ๊ณ ํ๋กฌํํธ์ ๋ฃ์ง ์๋๋ค). */
|
|
47
|
+
senderId?: string
|
|
48
|
+
/** ํ๋กฌํํธ์ ๋
ธ์ถํ ํ์ ์ด๋ฆ. ์ด๋ฉ์ผ ๊ฐ์ ๊ฐ์ธ์ ๋ณด๋ ๋ฃ์ง ์๋๋ค. */
|
|
49
|
+
senderName?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface BuildLlmHistoryOptions {
|
|
53
|
+
/** ์ด ๋ฉ์์ง๊น์ง๋ง ์ ์งํ๊ณ ์ดํ๋ ๋ฒ๋ฆฐ๋ค(ํธ์งยท์ฌ์์ฑ์ผ๋ก ์ ์ ๊ตฌ๊ฐ). ๋ฏธ์ง์ ์ด๋ฉด ์ ๋ถ ์ฌ์ฉ. */
|
|
54
|
+
truncateAfterMessageId?: string
|
|
55
|
+
/** ์๋ผ๋ด๊ธฐ์ ๋ฌด๊ดํ๊ฒ ํญ์ ์ ์งํ ๋ฉ์์ง id(๋ฐฉ๊ธ ์ ์ฅํ ์ ์ฌ์ฉ์ ๋ฉ์์ง ๋ฑ). */
|
|
56
|
+
keepMessageIds?: string[]
|
|
57
|
+
/** ๋ฐ์ ์ ํ๊ธฐ ๊ฐ์ โ ๋ฏธ์ง์ ์ด๋ฉด ์ฌ๋ ๋ฐ์ ์๊ฐ ๋ ์ด์์ผ ๋๋ง ํ๊ธฐ. ํ
์คํธยทํน์ ํธ์ถ์ฉ. */
|
|
58
|
+
labelSenders?: boolean
|
|
59
|
+
/**
|
|
60
|
+
* ๋ชจ๋ธ์ ๋๊ธธ **์ต๊ทผ ๋ฉ์์ง ์ ์ํ**. ์ด๊ณผ๋ถ(์ค๋๋ ์๋ถ๋ถ)์ ๋ฒ๋ฆฌ๊ณ ์๋ต ํ์๋ฅผ ๋จ๊ธด๋ค.
|
|
61
|
+
*
|
|
62
|
+
* ์ ํ์ํ๊ฐ: ํ์๋ ๋ฉฐ์น ์ ๊ฑธ์น๋ค. ์ํ์ด ์์ผ๋ฉด ๋งค ํด ์ ์ฒด ์ด๋ ฅ์ ๋ณด๋ด ํ๋กฌํํธ๊ฐ ๋ฌดํํ
|
|
63
|
+
* ์๋ผ๊ณ (๋น์ฉยท์ง์ฐยท๋ฌธ๋งฅ ํ๊ณ) ์ค๋๋ ๋ง์ด ์ต๊ทผ ์ํฉ์ ๋ฎ๋๋ค. ๋ฏธ์ง์ ์ด๋ฉด ์ ๋ถ ๋ณด๋ธ๋ค(๊ธฐ์กด ๋์).
|
|
64
|
+
*
|
|
65
|
+
* **์๋ต์ ๋ฐ๋์ ์๋ฆฐ๋ค** โ ์กฐ์ฉํ ๋ฒ๋ฆฌ๋ฉด ๋ชจ๋ธ์ ๋จ์ ์ฒซ ์ค์ ๋ํ์ ์์์ผ๋ก ์คํดํ๋ค.
|
|
66
|
+
* ์ด๊ฑด ์ํ์ผ ๋ฟ ์์ฝ์ด ์๋๋ค. ๋ฒ๋ฆฐ ๋ด์ฉ์ ์์ฝํด ์ค์ด ๋ณด๋ด๋ ๊ฒ์ ๋ค์ ๋จ๊ณ๋ค(ChatSession.lastSummary).
|
|
67
|
+
*/
|
|
68
|
+
maxTurns?: number
|
|
69
|
+
/**
|
|
70
|
+
* ์ํ์ผ๋ก ๋ฐ๋ ค๋ ์๋ถ๋ถ์ ๋์ ํ **์์ฝ**(์ธ์
์ ์ ์ฅ๋ ๊ฒ).
|
|
71
|
+
*
|
|
72
|
+
* ์์ผ๋ฉด "N๊ฐ ์๋ต" ๋์ ์์ฝ์ ์ฒซ ์ค๋ก ์ฃ๋๋ค โ ๋ฉฐ์น ์ด์ด์ง๋ ํ์์์ ์์์ ํฉ์ํ ๊ฒ์ด
|
|
73
|
+
* ์๋ ์ผ์ด ๋์ง ์๊ฒ. ์์ฝ์ด ๋ฐ๋ ค๋ ๊ตฌ๊ฐ์ **์ ๋ถ ๋ฎ์ง ๋ชปํ๋ฉด** ๋ฎ์ง ๋ชปํ ๊ฐ์๋ ํจ๊ป ๋ฐํ๋ค
|
|
74
|
+
* (์์ฝ์ด ์ต์ ์ด ์๋ ์ ์๋ค๋ ์ฌ์ค์ ์จ๊ธฐ์ง ์๋๋ค).
|
|
75
|
+
*/
|
|
76
|
+
summary?: StoredSummary
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** ์ฌ๋์ด ์๋ ๊ธฐ๋ก(์บ๋ฒ์ค ์ง์ ํธ์ง ๋ฑ)์ ํ๊ธฐ. */
|
|
80
|
+
const SYSTEM_LABEL = '์์คํ
'
|
|
81
|
+
/** ์ด๋ฆ์ ๋ชจ๋ฅด๋ ์ฐธ์ฌ์์ ํ๊ธฐ โ ๋น ๋๊ดํธ๋ฅผ ๋จ๊ธฐ์ง ์๋๋ค. */
|
|
82
|
+
const UNKNOWN_LABEL = '์ฐธ์ฌ์'
|
|
83
|
+
/** ์ํ์ผ๋ก ์๋ผ๋ธ ์๋ถ๋ถ์ ์๋ฆฌ๋ ํ๊ธฐ โ ๋ชจ๋ธ์ด ๋จ์ ์ฒซ ์ค์ ๋ํ์ ์์์ผ๋ก ์คํดํ์ง ์๊ฒ. */
|
|
84
|
+
const OMITTED_LABEL = '์์คํ
'
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* ์ ์ฅ๋ ์ด๋ ฅ โ LLM ๋ํ.
|
|
88
|
+
*
|
|
89
|
+
* ์
๋ ฅ์ **์๊ฐ ์ค๋ฆ์ฐจ์**์ ๊ฐ์ ํ๋ค(ํธ์ถ์๊ฐ createdAt ASC ๋ก ์กฐํ). ์ ๋ ฌ์ ๋ค์ ํ์ง ์๋๋ค โ
|
|
90
|
+
* ๊ฐ์ ์๊ฐ์ ์์๊น์ง ๋ณด์ฅํ๋ ค๋ฉด ์กฐํ ์ชฝ์์ ๊ฒฐ์ ํด์ผ ํ๊ณ , ์ฌ๊ธฐ์ ์ถ์ธกํ๋ฉด ๋ ๊ณณ์ด ์ด๊ธ๋๋ค.
|
|
91
|
+
*/
|
|
92
|
+
export function buildLlmHistory(rows: HistoryRow[], options: BuildLlmHistoryOptions = {}): LLMMessage[] {
|
|
93
|
+
const keep = new Set(options.keepMessageIds ?? [])
|
|
94
|
+
const truncated = truncate(rows, options.truncateAfterMessageId, keep)
|
|
95
|
+
/* ์ํ โ ์ต๊ทผ ๊ฒ๋ถํฐ ๋จ๊ธด๋ค. ์ค๋๋ ๋ง๋ณด๋ค ์ต๊ทผ ์ํฉ์ด ๋ต์ ์ข์ฐํ๋ค. */
|
|
96
|
+
const limit = options.maxTurns && options.maxTurns > 0 ? options.maxTurns : 0
|
|
97
|
+
const omitted = limit && truncated.length > limit ? truncated.length - limit : 0
|
|
98
|
+
const kept = omitted ? truncated.slice(-limit) : truncated
|
|
99
|
+
|
|
100
|
+
const label =
|
|
101
|
+
options.labelSenders ?? new Set(kept.filter(r => isHuman(r)).map(r => r.senderId ?? '')).size > 1
|
|
102
|
+
|
|
103
|
+
const out: LLMMessage[] = []
|
|
104
|
+
if (omitted) {
|
|
105
|
+
const dropped = truncated.slice(0, truncated.length - kept.length)
|
|
106
|
+
const summaryText = (options.summary?.text ?? '').trim()
|
|
107
|
+
if (summaryText) {
|
|
108
|
+
/* ์์ฝ์ด ์์ผ๋ฉด ๊ทธ๊ฒ์ ์ฃ๋๋ค. **์์ฝ์์ ๋ฐํ๋ค** โ ๋ชจ๋ธ์ด ์๋ฌธ์ฒ๋ผ ์ธ์ฉํ์ง ์๊ฒ.
|
|
109
|
+
* ์์ฝ์ด ๋ฐ๋ ค๋ ๊ตฌ๊ฐ์ ๋ค ๋ฎ์ง ๋ชปํ๋ฉด ๋จ์ ๊ฐ์๋ ํจ๊ป ์๋ฆฐ๋ค(์ต์ ์ด ์๋์ ์จ๊ธฐ์ง ์๋๋ค). */
|
|
110
|
+
const uncovered = pendingForSummary(dropped, options.summary).length
|
|
111
|
+
const tail = uncovered
|
|
112
|
+
? ` ๊ทธ ๋ค ${uncovered}๊ฐ ๋ฉ์์ง๋ ์์ฝ์ ์์ง ๋ฐ์๋์ง ์์์ต๋๋ค.`
|
|
113
|
+
: ''
|
|
114
|
+
out.push({
|
|
115
|
+
role: 'user',
|
|
116
|
+
content: `[${OMITTED_LABEL}] ์ด ๋ํ์ ์๋ถ๋ถ ${dropped.length}๊ฐ ๋ฉ์์ง๋ ๊ธธ์ด ์ ํ์ผ๋ก ์๋ ์์ฝ์ผ๋ก ๋์ฒด๋์์ต๋๋ค(์๋ฌธ ์๋ โ ์์ฝ์ ์๋ ์ฌ์ค์ ์ฌ์ฉ์์๊ฒ ํ์ธํ์ธ์).${tail}\n${summaryText}`
|
|
117
|
+
})
|
|
118
|
+
} else {
|
|
119
|
+
/* ์์ฝ์ด ์์ง ์์ผ๋ฉด ์๋ต ์ฌ์ค๋ง ์๋ฆฐ๋ค โ ๊ฐ์๋ง ๋ฐํ๊ณ ๋ด์ฉ์ ์ง์ด๋ด์ง ์๋๋ค. */
|
|
120
|
+
out.push({
|
|
121
|
+
role: 'user',
|
|
122
|
+
content: `[${OMITTED_LABEL}] ์ด ๋ํ์ ์๋ถ๋ถ ${omitted}๊ฐ ๋ฉ์์ง๋ ๊ธธ์ด ์ ํ์ผ๋ก ์๋ต๋์์ต๋๋ค. ํ์ํ๋ฉด ์ฌ์ฉ์์๊ฒ ๋ค์ ํ์ธํ์ธ์.`
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
for (const row of kept) {
|
|
127
|
+
const content = (row.content ?? '').trim()
|
|
128
|
+
if (!content) continue // ๋น ๋ณธ๋ฌธ(pending ์๋ฆฌ ๋ฑ)์ ๋ชจ๋ธ์ ๋๊ธธ ๊ฒ์ด ์๋ค
|
|
129
|
+
if (row.role === 'assistant') {
|
|
130
|
+
out.push({ role: 'assistant', content })
|
|
131
|
+
continue
|
|
132
|
+
}
|
|
133
|
+
if (row.role === 'system') {
|
|
134
|
+
out.push({ role: 'user', content: `[${SYSTEM_LABEL}] ${content}` })
|
|
135
|
+
continue
|
|
136
|
+
}
|
|
137
|
+
out.push({ role: 'user', content: label ? `[${speaker(row)}] ${content}` : content })
|
|
138
|
+
}
|
|
139
|
+
return out
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/** ์ฌ๋์ด ๋ณด๋ธ ์ค์ธ๊ฐ โ ๋ฐ์ ์ ์ ๊ณ์ฐ ๋์. system ๊ธฐ๋ก์ ์ฌ๋์ด ์๋๋ค. */
|
|
143
|
+
function isHuman(row: HistoryRow): boolean {
|
|
144
|
+
return row.role !== 'assistant' && row.role !== 'system'
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function speaker(row: HistoryRow): string {
|
|
148
|
+
const name = (row.senderName ?? '').trim()
|
|
149
|
+
return name || UNKNOWN_LABEL
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* ์ ์ ๊ตฌ๊ฐ ์ ๊ฑฐ. `truncateAfterMessageId` ๊ฐ ์๊ฑฐ๋ ์ด๋ ฅ์์ ๋ชป ์ฐพ์ผ๋ฉด **์ ๋ถ ์ ์ง**ํ๋ค
|
|
154
|
+
* (์กฐ์ฉํ ๋ค ๋ฒ๋ฆฌ๋ ๊ฒ๋ณด๋ค ๋ค ๋ณด๋ด๋ ์ชฝ์ด ์์ ํ๋ค โ ๋ฌธ๋งฅ ์์ค์ ๋ต์ ๋ง์น๋ค).
|
|
155
|
+
*/
|
|
156
|
+
function truncate(rows: HistoryRow[], afterId: string | undefined, keep: Set<string>): HistoryRow[] {
|
|
157
|
+
if (!afterId) return rows
|
|
158
|
+
const idx = rows.findIndex(r => r.id === afterId)
|
|
159
|
+
if (idx < 0) return rows
|
|
160
|
+
const head = rows.slice(0, idx + 1)
|
|
161
|
+
const tail = rows.slice(idx + 1).filter(r => r.id && keep.has(r.id))
|
|
162
|
+
return [...head, ...tail]
|
|
163
|
+
}
|