Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
kardolus
GitHub Repository: kardolus/chatgpt-cli
Path: blob/main/api/client/history.go
3431 views
1
package client
2
3
import (
4
"github.com/kardolus/chatgpt-cli/api"
5
"github.com/kardolus/chatgpt-cli/history"
6
"strings"
7
"unicode/utf8"
8
)
9
10
const (
11
MaxTokenBufferPercentage = 20
12
SystemRole = "system"
13
)
14
15
// ProvideContext adds custom context to the client's history by converting the
16
// provided string into a series of messages. This allows the ChatGPT API to have
17
// prior knowledge of the provided context when generating responses.
18
//
19
// The context string should contain the text you want to provide as context,
20
// and the method will split it into messages, preserving punctuation and special
21
// characters.
22
func (c *Client) ProvideContext(context string) {
23
c.initHistory()
24
historyEntries := c.createHistoryEntriesFromString(context)
25
c.History = append(c.History, historyEntries...)
26
}
27
28
func (c *Client) createHistoryEntriesFromString(input string) []history.History {
29
var result []history.History
30
31
words := strings.Fields(input)
32
33
for i := 0; i < len(words); i += 100 {
34
end := i + 100
35
if end > len(words) {
36
end = len(words)
37
}
38
39
content := strings.Join(words[i:end], " ")
40
41
item := history.History{
42
Message: api.Message{
43
Role: UserRole,
44
Content: content,
45
},
46
Timestamp: c.timer.Now(),
47
}
48
result = append(result, item)
49
}
50
51
return result
52
}
53
54
func (c *Client) initHistory() {
55
if len(c.History) != 0 {
56
return
57
}
58
59
if !c.Config.OmitHistory {
60
c.History, _ = c.historyStore.Read()
61
}
62
63
if len(c.History) == 0 {
64
c.History = []history.History{{
65
Message: api.Message{
66
Role: SystemRole,
67
},
68
Timestamp: c.timer.Now(),
69
}}
70
}
71
72
c.History[0].Content = c.Config.Role
73
}
74
75
func (c *Client) truncateHistory() {
76
tokens, rolling := countTokens(c.History)
77
effectiveTokenSize := calculateEffectiveContextWindow(c.Config.ContextWindow, MaxTokenBufferPercentage)
78
79
if tokens <= effectiveTokenSize {
80
return
81
}
82
83
var index int
84
var total int
85
diff := tokens - effectiveTokenSize
86
87
for i := 1; i < len(rolling); i++ {
88
total += rolling[i]
89
if total > diff {
90
index = i
91
break
92
}
93
}
94
95
c.History = append(c.History[:1], c.History[index+1:]...)
96
}
97
98
func (c *Client) updateHistory(response string) {
99
c.History = append(c.History, history.History{
100
Message: api.Message{
101
Role: AssistantRole,
102
Content: response,
103
},
104
Timestamp: c.timer.Now(),
105
})
106
107
if !c.Config.OmitHistory {
108
_ = c.historyStore.Write(c.History)
109
}
110
}
111
112
func calculateEffectiveContextWindow(window int, bufferPercentage int) int {
113
adjustedPercentage := 100 - bufferPercentage
114
effectiveContextWindow := (window * adjustedPercentage) / 100
115
return effectiveContextWindow
116
}
117
118
func countTokens(entries []history.History) (int, []int) {
119
var result int
120
var rolling []int
121
122
for _, entry := range entries {
123
charCount, wordCount := 0, 0
124
words := strings.Fields(entry.Content.(string))
125
wordCount += len(words)
126
127
for _, word := range words {
128
charCount += utf8.RuneCountInString(word)
129
}
130
131
// This is a simple approximation; actual token count may differ.
132
// You can adjust this based on your language and the specific tokenizer used by the model.
133
tokenCountForMessage := (charCount + wordCount) / 2
134
result += tokenCountForMessage
135
rolling = append(rolling, tokenCountForMessage)
136
}
137
138
return result, rolling
139
}
140
141