mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-17 03:27:02 +08:00
133 lines
4.0 KiB
Go
133 lines
4.0 KiB
Go
package forwarder
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"strings"
|
|
|
|
modeladapter "cursor/internal/backend/agent/model"
|
|
)
|
|
|
|
const promptContextSourceSelectedCursorCommands = "selected_cursor_commands"
|
|
|
|
func newPromptContextMessage(source string, message modeladapter.Message, persist bool) PromptContextMessage {
|
|
context := PromptContextMessage{
|
|
Source: strings.TrimSpace(source),
|
|
Message: message,
|
|
Persist: persist,
|
|
}
|
|
context.ContentHash = promptContextContentHash(context.Message)
|
|
return context
|
|
}
|
|
|
|
func normalizePromptContextMessage(context PromptContextMessage) PromptContextMessage {
|
|
context.Source = strings.TrimSpace(context.Source)
|
|
context.Message.Role = strings.TrimSpace(context.Message.Role)
|
|
context.Message.Content = strings.TrimSpace(context.Message.Content)
|
|
context.ContentHash = strings.TrimSpace(context.ContentHash)
|
|
if context.ContentHash == "" {
|
|
context.ContentHash = promptContextContentHash(context.Message)
|
|
}
|
|
return context
|
|
}
|
|
|
|
func promptContextContentHash(message modeladapter.Message) string {
|
|
sum := sha256.Sum256([]byte(strings.TrimSpace(message.Role) + "\x00" + strings.TrimSpace(message.Content)))
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
func filterCurrentTurnPromptContexts(conversation *ConversationFile, contexts []PromptContextMessage) []PromptContextMessage {
|
|
if len(contexts) == 0 {
|
|
return nil
|
|
}
|
|
seen := collectCurrentTurnPromptContextKeys(conversation)
|
|
filtered := make([]PromptContextMessage, 0, len(contexts))
|
|
for _, context := range contexts {
|
|
context = normalizePromptContextMessage(context)
|
|
if !isReplayablePromptContext(context) {
|
|
continue
|
|
}
|
|
if context.Persist {
|
|
if _, ok := seen[promptContextKey(context)]; ok {
|
|
continue
|
|
}
|
|
}
|
|
filtered = append(filtered, context)
|
|
}
|
|
return filtered
|
|
}
|
|
|
|
func collectCurrentTurnPromptContextKeys(conversation *ConversationFile) map[string]struct{} {
|
|
keys := make(map[string]struct{})
|
|
if conversation == nil {
|
|
return keys
|
|
}
|
|
currentTurnSeq := conversation.NextTurnSeq - 1
|
|
if currentTurnSeq <= 0 {
|
|
return keys
|
|
}
|
|
for _, entry := range conversation.Entries {
|
|
if entry.TurnSeq != currentTurnSeq || strings.TrimSpace(entry.Kind) != "prompt_context" {
|
|
continue
|
|
}
|
|
var payload promptContextEntryPayload
|
|
if err := json.Unmarshal(entry.Payload, &payload); err != nil {
|
|
continue
|
|
}
|
|
context := normalizePromptContextMessage(PromptContextMessage{
|
|
Source: payload.Source,
|
|
ContentHash: payload.ContentHash,
|
|
Message: modeladapter.Message{
|
|
Role: payload.Role,
|
|
Content: payload.Content,
|
|
},
|
|
Persist: true,
|
|
})
|
|
if !isReplayablePromptContext(context) {
|
|
continue
|
|
}
|
|
keys[promptContextKey(context)] = struct{}{}
|
|
}
|
|
return keys
|
|
}
|
|
|
|
func promptContextKey(context PromptContextMessage) string {
|
|
context = normalizePromptContextMessage(context)
|
|
return context.Source + "\x00" + context.ContentHash
|
|
}
|
|
|
|
func isReplayablePromptContext(context PromptContextMessage) bool {
|
|
context = normalizePromptContextMessage(context)
|
|
if context.Source == "" || strings.TrimSpace(context.Message.Content) == "" {
|
|
return false
|
|
}
|
|
if context.Message.Role == "" {
|
|
context.Message.Role = "user"
|
|
}
|
|
if context.Message.Role != "user" && context.Message.Role != "system" {
|
|
return false
|
|
}
|
|
if strings.TrimSpace(context.Message.Name) != "" || strings.TrimSpace(context.Message.ToolCallID) != "" {
|
|
return false
|
|
}
|
|
return len(context.Message.ToolCalls) == 0 && len(context.Message.ContentParts) == 0
|
|
}
|
|
|
|
func newPromptContextEntry(turnSeq int64, requestID string, context PromptContextMessage) HistoryEntry {
|
|
context = normalizePromptContextMessage(context)
|
|
payload, _ := json.Marshal(promptContextEntryPayload{
|
|
Source: context.Source,
|
|
Role: firstNonEmpty(strings.TrimSpace(context.Message.Role), "user"),
|
|
Content: strings.TrimSpace(context.Message.Content),
|
|
ContentHash: context.ContentHash,
|
|
})
|
|
return HistoryEntry{
|
|
TurnSeq: turnSeq,
|
|
RequestID: strings.TrimSpace(requestID),
|
|
Role: "system",
|
|
Kind: "prompt_context",
|
|
Payload: payload,
|
|
}
|
|
}
|