mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-17 11:37:20 +08:00
v0.3.8
This commit is contained in:
@@ -0,0 +1,323 @@
|
||||
package forwarder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
"cursor/gen/agentv1"
|
||||
)
|
||||
|
||||
const (
|
||||
promptGuardUserTextChars = 32000
|
||||
promptGuardUserRichTextChars = 32000
|
||||
promptGuardSubagentReminderChars = 12000
|
||||
promptGuardSelectedFileChars = 16000
|
||||
promptGuardSelectedFilesTotalChars = 64000
|
||||
promptGuardSelectedFilesMaxCount = 12
|
||||
promptGuardRequestFileChars = 16000
|
||||
promptGuardRequestFilesTotalChars = 64000
|
||||
promptGuardRequestFilesMaxCount = 12
|
||||
promptGuardRuleChars = 6000
|
||||
promptGuardRulesTotalChars = 24000
|
||||
promptGuardRulesMaxCount = 40
|
||||
promptGuardSkillDescriptionChars = 800
|
||||
promptGuardSkillDescriptionsTotal = 16000
|
||||
promptGuardSkillDescriptorsMaxCount = 32
|
||||
promptGuardAgentSkillContentChars = 6000
|
||||
promptGuardAgentSkillsMaxCount = 16
|
||||
promptGuardRealtimeTextChars = 12000
|
||||
promptGuardCompiledMessageChars = 120000
|
||||
)
|
||||
|
||||
func normalizeUserMessageForStorage(userMessage *agentv1.UserMessage) *agentv1.UserMessage {
|
||||
if userMessage == nil {
|
||||
return nil
|
||||
}
|
||||
cloned, ok := proto.Clone(userMessage).(*agentv1.UserMessage)
|
||||
if !ok || cloned == nil {
|
||||
return userMessage
|
||||
}
|
||||
cloned.Text = truncatePromptGuardText("user_message.text", cloned.GetText(), promptGuardUserTextChars)
|
||||
if richText := strings.TrimSpace(cloned.GetRichText()); richText != "" {
|
||||
cloned.RichText = stringPtr(truncatePromptGuardText("user_message.rich_text", richText, promptGuardUserRichTextChars))
|
||||
}
|
||||
if reminder := strings.TrimSpace(cloned.GetSubagentSystemReminder()); reminder != "" {
|
||||
cloned.SubagentSystemReminder = stringPtr(truncatePromptGuardText("user_message.subagent_system_reminder", reminder, promptGuardSubagentReminderChars))
|
||||
}
|
||||
cloned.SelectedContext = guardSelectedContext(cloned.GetSelectedContext())
|
||||
return cloned
|
||||
}
|
||||
|
||||
func guardRequestContextForStorage(requestContext *agentv1.RequestContext) {
|
||||
if requestContext == nil {
|
||||
return
|
||||
}
|
||||
requestContext.Rules = guardCursorRules(requestContext.GetRules())
|
||||
requestContext.FileContents = guardStringMap(
|
||||
requestContext.GetFileContents(),
|
||||
"request_context.file_contents",
|
||||
promptGuardRequestFileChars,
|
||||
promptGuardRequestFilesTotalChars,
|
||||
promptGuardRequestFilesMaxCount,
|
||||
)
|
||||
if summary := strings.TrimSpace(requestContext.GetUserIntentSummary()); summary != "" {
|
||||
requestContext.UserIntentSummary = stringPtr(truncatePromptGuardText("request_context.user_intent_summary", summary, promptGuardRealtimeTextChars))
|
||||
}
|
||||
if hooks := strings.TrimSpace(requestContext.GetHooksAdditionalContext()); hooks != "" {
|
||||
requestContext.HooksAdditionalContext = stringPtr(truncatePromptGuardText("request_context.hooks_additional_context", hooks, promptGuardRealtimeTextChars))
|
||||
}
|
||||
if commit := strings.TrimSpace(requestContext.GetCommitAttributionMessage()); commit != "" {
|
||||
requestContext.CommitAttributionMessage = stringPtr(truncatePromptGuardText("request_context.commit_attribution_message", commit, promptGuardRealtimeTextChars))
|
||||
}
|
||||
if pr := strings.TrimSpace(requestContext.GetPrAttributionMessage()); pr != "" {
|
||||
requestContext.PrAttributionMessage = stringPtr(truncatePromptGuardText("request_context.pr_attribution_message", pr, promptGuardRealtimeTextChars))
|
||||
}
|
||||
}
|
||||
|
||||
func guardCompiledConversationForProvider(compiled CompiledConversation) CompiledConversation {
|
||||
for index := range compiled.Messages {
|
||||
message := &compiled.Messages[index]
|
||||
if strings.TrimSpace(message.Role) == "system" {
|
||||
continue
|
||||
}
|
||||
if strings.TrimSpace(message.Content) != "" {
|
||||
message.Content = truncatePromptGuardText("compiled."+firstNonEmpty(strings.TrimSpace(message.Role), "message"), message.Content, promptGuardCompiledMessageChars)
|
||||
}
|
||||
for partIndex := range message.ContentParts {
|
||||
if strings.TrimSpace(message.ContentParts[partIndex].Text) == "" {
|
||||
continue
|
||||
}
|
||||
message.ContentParts[partIndex].Text = truncatePromptGuardText("compiled.content_part", message.ContentParts[partIndex].Text, promptGuardCompiledMessageChars)
|
||||
}
|
||||
}
|
||||
return compiled
|
||||
}
|
||||
|
||||
func guardSelectedContext(selectedContext *agentv1.SelectedContext) *agentv1.SelectedContext {
|
||||
if selectedContext == nil {
|
||||
return nil
|
||||
}
|
||||
cloned, ok := proto.Clone(selectedContext).(*agentv1.SelectedContext)
|
||||
if !ok || cloned == nil {
|
||||
return selectedContext
|
||||
}
|
||||
cloned.Files = guardSelectedFiles(cloned.GetFiles())
|
||||
cloned.SelectedSkills = guardAgentSkills(cloned.GetSelectedSkills())
|
||||
cloned.ExtraContext = guardStringSlice(cloned.GetExtraContext(), "selected_context.extra_context", promptGuardRealtimeTextChars, promptGuardRealtimeTextChars, promptGuardAgentSkillsMaxCount)
|
||||
return cloned
|
||||
}
|
||||
|
||||
func guardSelectedFiles(files []*agentv1.SelectedFile) []*agentv1.SelectedFile {
|
||||
if len(files) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([]*agentv1.SelectedFile, 0, minInt(len(files), promptGuardSelectedFilesMaxCount))
|
||||
remaining := promptGuardSelectedFilesTotalChars
|
||||
for _, file := range files {
|
||||
if file == nil || len(result) >= promptGuardSelectedFilesMaxCount {
|
||||
continue
|
||||
}
|
||||
content := strings.TrimSpace(file.GetContent())
|
||||
if content == "" {
|
||||
continue
|
||||
}
|
||||
limit := minInt(promptGuardSelectedFileChars, remaining)
|
||||
if limit <= 0 {
|
||||
break
|
||||
}
|
||||
cloned, ok := proto.Clone(file).(*agentv1.SelectedFile)
|
||||
if !ok || cloned == nil {
|
||||
continue
|
||||
}
|
||||
cloned.Content = truncatePromptGuardText("selected_context.files.content", content, limit)
|
||||
remaining -= promptGuardRuneCount(cloned.GetContent())
|
||||
result = append(result, cloned)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func guardCursorRules(rules []*agentv1.CursorRule) []*agentv1.CursorRule {
|
||||
if len(rules) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([]*agentv1.CursorRule, 0, minInt(len(rules), promptGuardRulesMaxCount))
|
||||
remaining := promptGuardRulesTotalChars
|
||||
for _, rule := range rules {
|
||||
if rule == nil || len(result) >= promptGuardRulesMaxCount {
|
||||
continue
|
||||
}
|
||||
content := strings.TrimSpace(rule.GetContent())
|
||||
if content == "" {
|
||||
continue
|
||||
}
|
||||
limit := minInt(promptGuardRuleChars, remaining)
|
||||
if limit <= 0 {
|
||||
break
|
||||
}
|
||||
cloned, ok := proto.Clone(rule).(*agentv1.CursorRule)
|
||||
if !ok || cloned == nil {
|
||||
continue
|
||||
}
|
||||
cloned.Content = truncatePromptGuardText("request_context.rules.content", content, limit)
|
||||
remaining -= promptGuardRuneCount(cloned.GetContent())
|
||||
result = append(result, cloned)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func guardSkillDescriptors(descriptors []*agentv1.SkillDescriptor) []*agentv1.SkillDescriptor {
|
||||
if len(descriptors) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([]*agentv1.SkillDescriptor, 0, minInt(len(descriptors), promptGuardSkillDescriptorsMaxCount))
|
||||
remaining := promptGuardSkillDescriptionsTotal
|
||||
for _, descriptor := range descriptors {
|
||||
if descriptor == nil || len(result) >= promptGuardSkillDescriptorsMaxCount {
|
||||
continue
|
||||
}
|
||||
description := strings.TrimSpace(descriptor.GetDescription())
|
||||
if description == "" {
|
||||
continue
|
||||
}
|
||||
limit := minInt(promptGuardSkillDescriptionChars, remaining)
|
||||
if limit <= 0 {
|
||||
break
|
||||
}
|
||||
cloned, ok := proto.Clone(descriptor).(*agentv1.SkillDescriptor)
|
||||
if !ok || cloned == nil {
|
||||
continue
|
||||
}
|
||||
cloned.Description = truncatePromptGuardText("skill.description", description, limit)
|
||||
remaining -= promptGuardRuneCount(cloned.GetDescription())
|
||||
result = append(result, cloned)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func guardAgentSkills(skills []*agentv1.AgentSkill) []*agentv1.AgentSkill {
|
||||
if len(skills) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([]*agentv1.AgentSkill, 0, minInt(len(skills), promptGuardAgentSkillsMaxCount))
|
||||
for _, skill := range skills {
|
||||
if skill == nil || len(result) >= promptGuardAgentSkillsMaxCount {
|
||||
continue
|
||||
}
|
||||
cloned, ok := proto.Clone(skill).(*agentv1.AgentSkill)
|
||||
if !ok || cloned == nil {
|
||||
continue
|
||||
}
|
||||
if content := strings.TrimSpace(cloned.GetContent()); content != "" {
|
||||
cloned.Content = truncatePromptGuardText("agent_skill.content", content, promptGuardAgentSkillContentChars)
|
||||
}
|
||||
if description := strings.TrimSpace(cloned.GetDescription()); description != "" {
|
||||
cloned.Description = truncatePromptGuardText("agent_skill.description", description, promptGuardSkillDescriptionChars)
|
||||
}
|
||||
result = append(result, cloned)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func guardStringMap(input map[string]string, label string, itemLimit int, totalLimit int, maxItems int) map[string]string {
|
||||
if len(input) == 0 {
|
||||
return nil
|
||||
}
|
||||
keys := make([]string, 0, len(input))
|
||||
values := make(map[string]string, len(input))
|
||||
for key, value := range input {
|
||||
trimmedKey := strings.TrimSpace(key)
|
||||
trimmedValue := strings.TrimSpace(value)
|
||||
if trimmedKey == "" || trimmedValue == "" {
|
||||
continue
|
||||
}
|
||||
if _, exists := values[trimmedKey]; exists {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, trimmedKey)
|
||||
values[trimmedKey] = trimmedValue
|
||||
}
|
||||
sort.Strings(keys)
|
||||
result := make(map[string]string, minInt(len(keys), maxItems))
|
||||
remaining := totalLimit
|
||||
for _, key := range keys {
|
||||
if len(result) >= maxItems {
|
||||
break
|
||||
}
|
||||
content := values[key]
|
||||
if content == "" {
|
||||
continue
|
||||
}
|
||||
limit := minInt(itemLimit, remaining)
|
||||
if limit <= 0 {
|
||||
break
|
||||
}
|
||||
result[key] = truncatePromptGuardText(label, content, limit)
|
||||
remaining -= promptGuardRuneCount(result[key])
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return nil
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func guardStringSlice(input []string, label string, itemLimit int, totalLimit int, maxItems int) []string {
|
||||
if len(input) == 0 {
|
||||
return nil
|
||||
}
|
||||
result := make([]string, 0, minInt(len(input), maxItems))
|
||||
remaining := totalLimit
|
||||
for _, item := range input {
|
||||
if len(result) >= maxItems {
|
||||
break
|
||||
}
|
||||
content := strings.TrimSpace(item)
|
||||
if content == "" {
|
||||
continue
|
||||
}
|
||||
limit := minInt(itemLimit, remaining)
|
||||
if limit <= 0 {
|
||||
break
|
||||
}
|
||||
truncated := truncatePromptGuardText(label, content, limit)
|
||||
remaining -= promptGuardRuneCount(truncated)
|
||||
result = append(result, truncated)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func truncatePromptGuardText(label string, text string, limit int) string {
|
||||
if limit <= 0 {
|
||||
return ""
|
||||
}
|
||||
if promptGuardRuneCount(text) <= limit {
|
||||
return text
|
||||
}
|
||||
runes := []rune(text)
|
||||
notice := fmt.Sprintf("\n\n[truncated: %s exceeded %d chars; kept head and tail from %d chars]\n\n", strings.TrimSpace(label), limit, len(runes))
|
||||
noticeRunes := []rune(notice)
|
||||
keep := limit - len(noticeRunes)
|
||||
if keep <= 0 {
|
||||
return string(runes[:limit])
|
||||
}
|
||||
head := keep * 2 / 3
|
||||
tail := keep - head
|
||||
if tail <= 0 {
|
||||
return string(runes[:head]) + notice
|
||||
}
|
||||
return string(runes[:head]) + notice + string(runes[len(runes)-tail:])
|
||||
}
|
||||
|
||||
func promptGuardRuneCount(text string) int {
|
||||
return utf8.RuneCountInString(text)
|
||||
}
|
||||
|
||||
func minInt(a int, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
Reference in New Issue
Block a user