Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Openclaw/scripts/docs-i18n/   (KI Agentensystem Version 22©)  Datei vom 26.3.2026 mit Größe 24 kB image not shown  

Quelle  doc_chunked_raw.go   Sprache: unbekannt

 
Spracherkennung für: .go vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

package main

import (
 "context"
 "fmt"
 "log"
 "os"
 "regexp"
 "slices"
 "strconv"
 "strings"
)

const defaultDocChunkMaxBytes = 12000
const defaultDocChunkPromptBudget = 15000

var (
 docsFenceRE        = regexp.MustCompile(`^\s*(` + "```" + `|~~~)`)
 docsComponentTagRE = regexp.MustCompile(`<(/?)([A-Z][A-Za-z0-9]*)\b[^>]*?/?>`)
)

var docsProtocolTokens = []string{
 frontmatterTagStart,
 frontmatterTagEnd,
 bodyTagStart,
 bodyTagEnd,
 "[[[FM_",
}

type docChunkStructure struct {
 fenceCount int
 tagCounts  map[string]int
}

type docChunkSplitPlan struct {
 groups [][]string
 reason string
}

func translateDocBodyChunked(ctx context.Context, translator docsTranslator, relPath, body, srcLang, tgtLang string) (string, error) {
 if strings.TrimSpace(body) == "" {
  return body, nil
 }
 blocks := splitDocBodyIntoBlocks(body)
 groups := groupDocBlocks(blocks, docsI18nDocChunkMaxBytes())
 logDocChunkPlan(relPath, blocks, groups)
 out := strings.Builder{}
 for index, group := range groups {
  chunkID := fmt.Sprintf("%s.chunk-%03d", relPath, index+1)
  translated, err := translateDocBlockGroup(ctx, translator, chunkID, group, srcLang, tgtLang)
  if err != nil {
   return "", err
  }
  out.WriteString(translated)
 }
 return out.String(), nil
}

func translateDocBlockGroup(ctx context.Context, translator docsTranslator, chunkID string, blocks []string, srcLang, tgtLang string) (string, error) {
 source := strings.Join(blocks, "")
 if strings.TrimSpace(source) == "" {
  return source, nil
 }
 if plan, ok := planDocChunkSplit(blocks, docsI18nDocChunkMaxBytes(), docsI18nDocChunkPromptBudget()); ok {
  logDocChunkPlanSplit(chunkID, plan, source)
  return translatePlannedDocChunkGroups(ctx, translator, chunkID, plan.groups, srcLang, tgtLang)
 }
 normalizedSource, commonIndent := stripCommonIndent(source)
 log.Printf("docs-i18n: chunk start %s blocks=%d bytes=%d", chunkID, len(blocks), len(source))
 translated, err := translator.TranslateRaw(ctx, normalizedSource, srcLang, tgtLang)
 if err == nil {
  translated = sanitizeDocChunkProtocolWrappers(source, translated)
  translated = reapplyCommonIndent(translated, commonIndent)
  if validationErr := validateDocChunkTranslation(source, translated); validationErr == nil {
   log.Printf("docs-i18n: chunk done %s out_bytes=%d", chunkID, len(translated))
   return translated, nil
  } else {
   err = validationErr
  }
 }
 if len(blocks) <= 1 {
  if fallback, fallbackErr := translateDocLeafBlock(ctx, translator, chunkID, source, srcLang, tgtLang); fallbackErr == nil {
   return fallback, nil
  }
  if plan, ok := planSingletonDocChunkRetry(source, docsI18nDocChunkMaxBytes(), docsI18nDocChunkPromptBudget()); ok {
   logDocChunkPlanSplit(chunkID, plan, source)
   return translatePlannedDocChunkGroups(ctx, translator, chunkID, plan.groups, srcLang, tgtLang)
  }
  return "", fmt.Errorf("%s: %w", chunkID, err)
 }
 if plan, ok := planDocChunkSplit(blocks, docsI18nDocChunkMaxBytes(), docsI18nDocChunkPromptBudget()); ok {
  logDocChunkSplit(chunkID, len(blocks), err)
  return translatePlannedDocChunkGroups(ctx, translator, chunkID, plan.groups, srcLang, tgtLang)
 }
 if plan, ok := splitDocChunkBlocksMidpointSimple(blocks); ok {
  logDocChunkSplit(chunkID, len(blocks), err)
  return translatePlannedDocChunkGroups(ctx, translator, chunkID, plan.groups, srcLang, tgtLang)
 }
 return "", fmt.Errorf("%s: %w", chunkID, err)
}

func translateDocLeafBlock(ctx context.Context, translator docsTranslator, chunkID, source, srcLang, tgtLang string) (string, error) {
 sourceStructure := summarizeDocChunkStructure(source)
 if sourceStructure.fenceCount != 0 {
  return "", fmt.Errorf("%s: raw leaf fallback not applicable", chunkID)
 }
 normalizedSource, commonIndent := stripCommonIndent(source)
 maskedSource, placeholders := maskDocComponentTags(normalizedSource)
 translated, err := translator.Translate(ctx, maskedSource, srcLang, tgtLang)
 if err != nil {
  return "", err
 }
 translated, err = restoreDocComponentTags(translated, placeholders)
 if err != nil {
  return "", err
 }
 translated = sanitizeDocChunkProtocolWrappers(source, translated)
 translated = reapplyCommonIndent(translated, commonIndent)
 if validationErr := validateDocChunkTranslation(source, translated); validationErr != nil {
  return "", validationErr
 }
 log.Printf("docs-i18n: chunk leaf-fallback done %s out_bytes=%d", chunkID, len(translated))
 return translated, nil
}

func splitDocBodyIntoBlocks(body string) []string {
 if body == "" {
  return nil
 }
 lines := strings.SplitAfter(body, "\n")
 blocks := make([]string, 0, len(lines))
 var current strings.Builder
 fenceDelimiter := ""
 for _, line := range lines {
  current.WriteString(line)
  fenceDelimiter, _ = updateFenceDelimiter(fenceDelimiter, line)
  inFence := fenceDelimiter != ""
  if !inFence && strings.TrimSpace(line) == "" {
   blocks = append(blocks, current.String())
   current.Reset()
  }
 }
 if current.Len() > 0 {
  blocks = append(blocks, current.String())
 }
 if len(blocks) == 0 {
  return []string{body}
 }
 return blocks
}

func groupDocBlocks(blocks []string, maxBytes int) [][]string {
 if len(blocks) == 0 {
  return nil
 }
 if maxBytes <= 0 {
  maxBytes = defaultDocChunkMaxBytes
 }
 groups := make([][]string, 0, len(blocks))
 current := make([]string, 0, 8)
 currentBytes := 0
 flush := func() {
  if len(current) == 0 {
   return
  }
  groups = append(groups, current)
  current = make([]string, 0, 8)
  currentBytes = 0
 }
 for _, block := range blocks {
  blockBytes := len(block)
  if len(current) > 0 && currentBytes+blockBytes > maxBytes {
   flush()
  }
  if blockBytes > maxBytes {
   groups = append(groups, []string{block})
   continue
  }
  current = append(current, block)
  currentBytes += blockBytes
 }
 flush()
 return groups
}

func validateDocChunkTranslation(source, translated string) error {
 if hasUnexpectedTopLevelProtocolWrapper(source, translated) {
  return fmt.Errorf("protocol token leaked: top-level wrapper")
 }
 sourceLower := strings.ToLower(source)
 translatedLower := strings.ToLower(translated)
 for _, token := range docsProtocolTokens {
  tokenLower := strings.ToLower(token)
  if strings.Contains(sourceLower, tokenLower) {
   continue
  }
  if strings.Contains(translatedLower, tokenLower) {
   return fmt.Errorf("protocol token leaked: %s", token)
  }
 }
 sourceStructure := summarizeDocChunkStructure(source)
 translatedStructure := summarizeDocChunkStructure(translated)
 if sourceStructure.fenceCount != translatedStructure.fenceCount {
  return fmt.Errorf("code fence mismatch: source=%d translated=%d", sourceStructure.fenceCount, translatedStructure.fenceCount)
 }
 if !slices.Equal(sortedKeys(sourceStructure.tagCounts), sortedKeys(translatedStructure.tagCounts)) {
  return fmt.Errorf("component tag set mismatch")
 }
 for _, key := range sortedKeys(sourceStructure.tagCounts) {
  if sourceStructure.tagCounts[key] != translatedStructure.tagCounts[key] {
   return fmt.Errorf("component tag mismatch for %s: source=%d translated=%d", key, sourceStructure.tagCounts[key], translatedStructure.tagCounts[key])
  }
 }
 return nil
}

func sanitizeDocChunkProtocolWrappers(source, translated string) string {
 if !containsProtocolWrapperToken(translated) {
  return translated
 }
 trimmedTranslated := strings.TrimSpace(translated)
 if !hasUnexpectedTopLevelProtocolWrapper(source, trimmedTranslated) {
  return translated
 }
 if !hasAmbiguousTaggedBodyClose(source, trimmedTranslated) {
  _, body, err := parseTaggedDocument(trimmedTranslated)
  if err == nil {
   if strings.TrimSpace(body) == "" {
    return translated
   }
   return body
  }
 }
 body, ok := stripBodyOnlyWrapper(source, trimmedTranslated)
 if !ok || strings.TrimSpace(body) == "" {
  return translated
 }
 return body
}

func stripBodyOnlyWrapper(source, text string) (string, bool) {
 sourceLower := strings.ToLower(source)
 // When the source itself documents <body> tokens, a bare body-only payload is
 // ambiguous: the trailing </body> can be literal translated content instead of
 // a real wrapper close. Keep it for validation/retry instead of truncating.
 if strings.Contains(sourceLower, strings.ToLower(bodyTagStart)) || strings.Contains(sourceLower, strings.ToLower(bodyTagEnd)) {
  return "", false
 }
 lower := strings.ToLower(text)
 bodyStartLower := strings.ToLower(bodyTagStart)
 bodyEndLower := strings.ToLower(bodyTagEnd)
 if !strings.HasPrefix(lower, bodyStartLower) || !strings.HasSuffix(lower, bodyEndLower) {
  return "", false
 }
 body := text[len(bodyTagStart) : len(text)-len(bodyTagEnd)]
 bodyLower := lower[len(bodyTagStart) : len(lower)-len(bodyTagEnd)]
 if strings.Contains(bodyLower, bodyStartLower) || strings.Contains(bodyLower, bodyEndLower) {
  return "", false
 }
 return trimTagNewlines(body), true
}

func hasAmbiguousTaggedBodyClose(source, translated string) bool {
 sourceLower := strings.ToLower(source)
 if !strings.Contains(sourceLower, strings.ToLower(bodyTagStart)) && !strings.Contains(sourceLower, strings.ToLower(bodyTagEnd)) {
  return false
 }
 translatedLower := strings.ToLower(translated)
 if !strings.Contains(translatedLower, strings.ToLower(frontmatterTagStart)) {
  return false
 }
 return strings.Count(translatedLower, strings.ToLower(bodyTagEnd)) == 1
}

func maskDocComponentTags(text string) (string, []string) {
 placeholders := make([]string, 0, 4)
 masked := docsComponentTagRE.ReplaceAllStringFunc(text, func(match string) string {
  placeholder := fmt.Sprintf("__OC_DOC_TAG_%03d__", len(placeholders))
  placeholders = append(placeholders, match)
  return placeholder
 })
 return masked, placeholders
}

func restoreDocComponentTags(text string, placeholders []string) (string, error) {
 restored := text
 for index, original := range placeholders {
  placeholder := fmt.Sprintf("__OC_DOC_TAG_%03d__", index)
  if !strings.Contains(restored, placeholder) {
   return "", fmt.Errorf("component tag placeholder missing: %s", placeholder)
  }
  restored = strings.ReplaceAll(restored, placeholder, original)
 }
 return restored, nil
}

func logDocChunkSplit(chunkID string, blockCount int, err error) {
 if docsI18nVerboseLogs() || blockCount >= 16 {
  log.Printf("docs-i18n: chunk split %s blocks=%d err=%v", chunkID, blockCount, err)
 }
}

func logDocChunkPlanSplit(chunkID string, plan docChunkSplitPlan, source string) {
 if plan.reason == "" {
  plan.reason = "unknown"
 }
 log.Printf("docs-i18n: chunk pre-split %s reason=%s groups=%d bytes=%d", chunkID, plan.reason, len(plan.groups), len(source))
}

func summarizeDocChunkStructure(text string) docChunkStructure {
 counts := map[string]int{}
 lines := strings.Split(text, "\n")
 fenceDelimiter := ""
 for _, line := range lines {
  var toggled bool
  fenceDelimiter, toggled = updateFenceDelimiter(fenceDelimiter, line)
  if toggled {
   counts["__fence_toggle__"]++
  }
  for _, match := range docsComponentTagRE.FindAllStringSubmatch(line, -1) {
   if len(match) < 3 {
    continue
   }
   fullToken := match[0]
   tagName := match[2]
   direction := "open"
   if match[1] == "/" {
    direction = "close"
   }
   if strings.HasSuffix(fullToken, "/>") {
    direction = "self"
   }
   counts[tagName+":"+direction]++
  }
 }
 return docChunkStructure{
  fenceCount: counts["__fence_toggle__"],
  tagCounts:  countsWithoutFence(counts),
 }
}

func countsWithoutFence(counts map[string]int) map[string]int {
 filtered := map[string]int{}
 for key, value := range counts {
  if key == "__fence_toggle__" {
   continue
  }
  filtered[key] = value
 }
 return filtered
}

func sortedKeys(counts map[string]int) []string {
 keys := make([]string, 0, len(counts))
 for key := range counts {
  keys = append(keys, key)
 }
 slices.Sort(keys)
 return keys
}

func updateFenceDelimiter(current, line string) (string, bool) {
 delimiter := leadingFenceDelimiter(line)
 if delimiter == "" {
  return current, false
 }
 if current == "" {
  return delimiter, true
 }
 if delimiter[0] == current[0] && len(delimiter) >= len(current) && isClosingFenceLine(line, delimiter) {
  return "", true
 }
 return current, false
}

func leadingFenceDelimiter(line string) string {
 trimmed := strings.TrimLeft(line, " \t")
 if len(trimmed) < 3 {
  return ""
 }
 switch trimmed[0] {
 case '`', '~':
 default:
  return ""
 }
 marker := trimmed[0]
 index := 0
 for index < len(trimmed) && trimmed[index] == marker {
  index++
 }
 if index < 3 {
  return ""
 }
 return trimmed[:index]
}

func isClosingFenceLine(line, delimiter string) bool {
 trimmed := strings.TrimLeft(line, " \t")
 if !strings.HasPrefix(trimmed, delimiter) {
  return false
 }
 return strings.TrimSpace(trimmed[len(delimiter):]) == ""
}

func hasUnexpectedTopLevelProtocolWrapper(source, translated string) bool {
 sourceTrimmed := strings.ToLower(strings.TrimSpace(source))
 translatedTrimmed := strings.ToLower(strings.TrimSpace(translated))
 checks := []struct {
  token string
  match func(string) bool
 }{
  {token: frontmatterTagStart, match: func(text string) bool { return strings.HasPrefix(text, strings.ToLower(frontmatterTagStart)) }},
  {token: bodyTagStart, match: func(text string) bool { return strings.HasPrefix(text, strings.ToLower(bodyTagStart)) }},
  {token: frontmatterTagEnd, match: func(text string) bool { return strings.HasSuffix(text, strings.ToLower(frontmatterTagEnd)) }},
  {token: bodyTagEnd, match: func(text string) bool { return strings.HasSuffix(text, strings.ToLower(bodyTagEnd)) }},
 }
 for _, check := range checks {
  if check.match(translatedTrimmed) && !check.match(sourceTrimmed) {
   return true
  }
 }
 return false
}

func containsProtocolWrapperToken(text string) bool {
 lower := strings.ToLower(text)
 return strings.Contains(lower, strings.ToLower(bodyTagStart)) || strings.Contains(lower, strings.ToLower(frontmatterTagStart))
}

func translatePlannedDocChunkGroups(ctx context.Context, translator docsTranslator, chunkID string, groups [][]string, srcLang, tgtLang string) (string, error) {
 var out strings.Builder
 for index, group := range groups {
  translated, err := translateDocBlockGroup(ctx, translator, fmt.Sprintf("%s.%02d", chunkID, index+1), group, srcLang, tgtLang)
  if err != nil {
   return "", err
  }
  out.WriteString(translated)
 }
 return out.String(), nil
}

func planDocChunkSplit(blocks []string, maxBytes, promptBudget int) (docChunkSplitPlan, bool) {
 if len(blocks) == 0 {
  return docChunkSplitPlan{}, false
 }
 source := strings.Join(blocks, "")
 if strings.TrimSpace(source) == "" {
  return docChunkSplitPlan{}, false
 }
 normalizedSource, _ := stripCommonIndent(source)
 estimatedPromptCost := estimateDocPromptCost(normalizedSource)
 if len(blocks) > 1 && promptBudget > 0 && estimatedPromptCost > promptBudget {
  return splitDocChunkBlocksMidpoint(blocks, estimatedPromptCost, promptBudget)
 }
 if len(blocks) == 1 {
  return planSingletonDocChunk(blocks[0], maxBytes, promptBudget)
 }
 return docChunkSplitPlan{}, false
}

func splitDocChunkBlocksMidpoint(blocks []string, estimatedPromptCost, promptBudget int) (docChunkSplitPlan, bool) {
 if len(blocks) <= 1 {
  return docChunkSplitPlan{}, false
 }
 mid := len(blocks) / 2
 if mid <= 0 || mid >= len(blocks) {
  return docChunkSplitPlan{}, false
 }
 return docChunkSplitPlan{
  groups: [][]string{blocks[:mid], blocks[mid:]},
  reason: fmt.Sprintf("prompt-budget:%d>%d", estimatedPromptCost, promptBudget),
 }, true
}

func splitDocChunkBlocksMidpointSimple(blocks []string) (docChunkSplitPlan, bool) {
 if len(blocks) <= 1 {
  return docChunkSplitPlan{}, false
 }
 mid := len(blocks) / 2
 if mid <= 0 || mid >= len(blocks) {
  return docChunkSplitPlan{}, false
 }
 return docChunkSplitPlan{
  groups: [][]string{blocks[:mid], blocks[mid:]},
  reason: "retry-midpoint",
 }, true
}

func planSingletonDocChunk(block string, maxBytes, promptBudget int) (docChunkSplitPlan, bool) {
 normalizedBlock, _ := stripCommonIndent(block)
 estimatedPromptCost := estimateDocPromptCost(normalizedBlock)
 overBytes := maxBytes > 0 && len(block) > maxBytes
 overPrompt := promptBudget > 0 && estimatedPromptCost > promptBudget
 if !overBytes && !overPrompt {
  return docChunkSplitPlan{}, false
 }

 return planSingletonDocChunkWithMode(block, maxBytes, promptBudget, false)
}

func planSingletonDocChunkRetry(block string, maxBytes, promptBudget int) (docChunkSplitPlan, bool) {
 return planSingletonDocChunkWithMode(block, maxBytes, promptBudget, true)
}

func planSingletonDocChunkWithMode(block string, maxBytes, promptBudget int, force bool) (docChunkSplitPlan, bool) {
 if sections := splitDocBlockSections(block); len(sections) > 1 {
  if groups := wrapDocChunkSections(sections); len(groups) > 1 {
   reason := "singleton-structural"
   if force {
    reason = "singleton-retry-structural"
   }
   return docChunkSplitPlan{
    groups: groups,
    reason: reason,
   }, true
  }
 }

 if groups, ok := splitPureFencedDocSectionWithMode(block, maxBytes, promptBudget, force); ok {
  reason := "singleton-fence"
  if force {
   reason = "singleton-retry-fence"
  }
  return docChunkSplitPlan{
   groups: groups,
   reason: reason,
  }, true
 }

 if groups, ok := splitPlainDocSectionWithMode(block, maxBytes, promptBudget, force); ok {
  reason := "singleton-lines"
  if force {
   reason = "singleton-retry-lines"
  }
  return docChunkSplitPlan{
   groups: groups,
   reason: reason,
  }, true
 }

 return docChunkSplitPlan{}, false
}

func wrapDocChunkSections(sections []string) [][]string {
 groups := make([][]string, 0, len(sections))
 for _, section := range sections {
  if strings.TrimSpace(section) == "" {
   continue
  }
  groups = append(groups, []string{section})
 }
 return groups
}

func splitDocBlockSections(block string) []string {
 lines := strings.SplitAfter(block, "\n")
 if len(lines) == 0 {
  return nil
 }
 sections := make([]string, 0, len(lines))
 var current strings.Builder
 fenceDelimiter := ""
 for _, line := range lines {
  lineDelimiter := leadingFenceDelimiter(line)
  if fenceDelimiter == "" && lineDelimiter != "" {
   if current.Len() > 0 {
    sections = append(sections, current.String())
    current.Reset()
   }
   current.WriteString(line)
   fenceDelimiter = lineDelimiter
   continue
  }

  current.WriteString(line)
  if fenceDelimiter != "" {
   if lineDelimiter != "" && lineDelimiter[0] == fenceDelimiter[0] && len(lineDelimiter) >= len(fenceDelimiter) && isClosingFenceLine(line, fenceDelimiter) {
    sections = append(sections, current.String())
    current.Reset()
    fenceDelimiter = ""
   }
   continue
  }

  if strings.TrimSpace(line) == "" {
   sections = append(sections, current.String())
   current.Reset()
  }
 }
 if current.Len() > 0 {
  sections = append(sections, current.String())
 }
 if len(sections) <= 1 {
  return nil
 }
 return sections
}

func splitPureFencedDocSection(block string, maxBytes, promptBudget int) ([][]string, bool) {
 return splitPureFencedDocSectionWithMode(block, maxBytes, promptBudget, false)
}

func splitPureFencedDocSectionWithMode(block string, maxBytes, promptBudget int, force bool) ([][]string, bool) {
 lines := strings.SplitAfter(block, "\n")
 if len(lines) < 2 {
  return nil, false
 }
 openingIndex := firstNonEmptyLineIndex(lines)
 closingIndex := lastNonEmptyLineIndex(lines)
 if openingIndex == -1 || closingIndex <= openingIndex {
  return nil, false
 }
 opening := lines[openingIndex]
 delimiter := leadingFenceDelimiter(opening)
 if delimiter == "" || !isClosingFenceLine(lines[closingIndex], delimiter) {
  return nil, false
 }
 prefix := strings.Join(lines[:openingIndex], "")
 suffix := strings.Join(lines[closingIndex+1:], "")
 if strings.TrimSpace(prefix) != "" || strings.TrimSpace(suffix) != "" {
  return nil, false
 }
 closing := lines[closingIndex]
 inner := strings.Join(lines[openingIndex+1:closingIndex], "")
 groups, ok := splitPlainDocSectionWithMode(inner, maxBytes-len(opening)-len(closing), promptBudget, force)
 if !ok {
  return nil, false
 }
 for index, group := range groups {
  joined := strings.Join(group, "")
  groups[index] = []string{opening + joined + closing}
 }
 return groups, true
}

func splitPlainDocSection(text string, maxBytes, promptBudget int) ([][]string, bool) {
 return splitPlainDocSectionWithMode(text, maxBytes, promptBudget, false)
}

func splitPlainDocSectionWithMode(text string, maxBytes, promptBudget int, force bool) ([][]string, bool) {
 if maxBytes <= 0 {
  maxBytes = len(text)
 }
 if promptBudget <= 0 {
  promptBudget = defaultDocChunkPromptBudget
 }
 lines := strings.SplitAfter(text, "\n")
 if len(lines) <= 1 {
  return nil, false
 }
 groups := make([][]string, 0, len(lines))
 var current strings.Builder
 currentBytes := 0
 currentPrompt := 0
 for _, line := range lines {
  linePrompt := estimateDocPromptCost(line)
  if len(line) > maxBytes || linePrompt > promptBudget {
   return nil, false
  }
  if currentBytes > 0 && (currentBytes+len(line) > maxBytes || currentPrompt+linePrompt > promptBudget) {
   groups = append(groups, []string{current.String()})
   current.Reset()
   currentBytes = 0
   currentPrompt = 0
  }
  current.WriteString(line)
  currentBytes += len(line)
  currentPrompt += linePrompt
 }
 if current.Len() > 0 {
  groups = append(groups, []string{current.String()})
 }
 if len(groups) <= 1 {
  if !force {
   return nil, false
  }
  return splitPlainDocSectionMidpoint(lines)
 }
 return groups, true
}

func splitPlainDocSectionMidpoint(lines []string) ([][]string, bool) {
 if len(lines) <= 1 {
  return nil, false
 }
 mid := len(lines) / 2
 if mid <= 0 || mid >= len(lines) {
  return nil, false
 }
 left := strings.Join(lines[:mid], "")
 right := strings.Join(lines[mid:], "")
 if strings.TrimSpace(left) == "" || strings.TrimSpace(right) == "" {
  return nil, false
 }
 return [][]string{{left}, {right}}, true
}

func firstNonEmptyLineIndex(lines []string) int {
 for index, line := range lines {
  if strings.TrimSpace(line) != "" {
   return index
  }
 }
 return -1
}

func lastNonEmptyLineIndex(lines []string) int {
 for index := len(lines) - 1; index >= 0; index-- {
  if strings.TrimSpace(lines[index]) != "" {
   return index
  }
 }
 return -1
}

func docsI18nDocChunkMaxBytes() int {
 value := strings.TrimSpace(os.Getenv("OPENCLAW_DOCS_I18N_DOC_CHUNK_MAX_BYTES"))
 if value == "" {
  return defaultDocChunkMaxBytes
 }
 parsed, err := strconv.Atoi(value)
 if err != nil || parsed <= 0 {
  return defaultDocChunkMaxBytes
 }
 return parsed
}

func docsI18nDocChunkPromptBudget() int {
 value := strings.TrimSpace(os.Getenv("OPENCLAW_DOCS_I18N_DOC_CHUNK_PROMPT_BUDGET"))
 if value == "" {
  return defaultDocChunkPromptBudget
 }
 parsed, err := strconv.Atoi(value)
 if err != nil || parsed <= 0 {
  return defaultDocChunkPromptBudget
 }
 return parsed
}

func estimateDocPromptCost(text string) int {
 cost := len(text)
 cost += strings.Count(text, "`") * 6
 cost += strings.Count(text, "|") * 4
 cost += strings.Count(text, "{") * 4
 cost += strings.Count(text, "}") * 4
 cost += strings.Count(text, "[") * 4
 cost += strings.Count(text, "]") * 4
 cost += strings.Count(text, ":") * 2
 cost += strings.Count(text, "<") * 4
 cost += strings.Count(text, ">") * 4
 return cost
}

func stripCommonIndent(text string) (string, string) {
 lines := strings.SplitAfter(text, "\n")
 common := ""
 for _, line := range lines {
  trimmed := strings.TrimRight(line, "\r\n")
  if strings.TrimSpace(trimmed) == "" {
   continue
  }
  indent := leadingIndent(trimmed)
  if common == "" {
   common = indent
   continue
  }
  common = commonIndentPrefix(common, indent)
  if common == "" {
   return text, ""
  }
 }
 if common == "" {
  return text, ""
 }
 var out strings.Builder
 for _, line := range lines {
  trimmed := strings.TrimRight(line, "\r\n")
  if strings.TrimSpace(trimmed) == "" {
   out.WriteString(line)
   continue
  }
  if strings.HasPrefix(line, common) {
   out.WriteString(strings.TrimPrefix(line, common))
   continue
  }
  out.WriteString(line)
 }
 return out.String(), common
}

func reapplyCommonIndent(text, indent string) string {
 if indent == "" || text == "" {
  return text
 }
 lines := strings.SplitAfter(text, "\n")
 var out strings.Builder
 for _, line := range lines {
  trimmed := strings.TrimRight(line, "\r\n")
  if strings.TrimSpace(trimmed) == "" {
   out.WriteString(line)
   continue
  }
  out.WriteString(indent)
  out.WriteString(line)
 }
 return out.String()
}

func leadingIndent(line string) string {
 index := 0
 for index < len(line) {
  if line[index] != ' ' && line[index] != '\t' {
   break
  }
  index++
 }
 return line[:index]
}

func commonIndentPrefix(a, b string) string {
 limit := len(a)
 if len(b) < limit {
  limit = len(b)
 }
 index := 0
 for index < limit && a[index] == b[index] {
  index++
 }
 return a[:index]
}

[Dauer der Verarbeitung: 0.31 Sekunden, vorverarbeitet 2026-04-27]