splitSysPromptPrefix function

List<SystemPromptBlock> splitSysPromptPrefix({
  1. required SystemPrompt systemPrompt,
  2. bool skipGlobalCacheForSystemPrompt = false,
  3. bool useGlobalCacheFeature = false,
  4. String? dynamicBoundary,
  5. Set<String>? cliSyspromptPrefixes,
})

Split system prompt blocks by content type for API cache control.

Modes:

  1. skipGlobalCache + useGlobalCache: up to 3 blocks, org-level caching.
  2. useGlobalCache + dynamicBoundary found: up to 4 blocks with global cache.
  3. Default: up to 3 blocks with org-level caching.

Implementation

List<SystemPromptBlock> splitSysPromptPrefix({
  required SystemPrompt systemPrompt,
  bool skipGlobalCacheForSystemPrompt = false,
  bool useGlobalCacheFeature = false,
  String? dynamicBoundary,
  Set<String>? cliSyspromptPrefixes,
}) {
  final prefixes = cliSyspromptPrefixes ?? {};

  // Mode 1: MCP tools present — skip global cache on system prompt.
  if (useGlobalCacheFeature && skipGlobalCacheForSystemPrompt) {
    String? attributionHeader;
    String? systemPromptPrefix;
    final rest = <String>[];

    for (final prompt in systemPrompt) {
      if (prompt.isEmpty || prompt == dynamicBoundary) continue;
      if (prompt.startsWith('x-anthropic-billing-header')) {
        attributionHeader = prompt;
      } else if (prefixes.contains(prompt)) {
        systemPromptPrefix = prompt;
      } else {
        rest.add(prompt);
      }
    }

    return [
      if (attributionHeader != null)
        SystemPromptBlock(text: attributionHeader, cacheScope: null),
      if (systemPromptPrefix != null)
        SystemPromptBlock(text: systemPromptPrefix, cacheScope: CacheScope.org),
      if (rest.isNotEmpty)
        SystemPromptBlock(text: rest.join('\n\n'), cacheScope: CacheScope.org),
    ];
  }

  // Mode 2: Global cache with dynamic boundary marker.
  if (useGlobalCacheFeature && dynamicBoundary != null) {
    final boundaryIndex = systemPrompt.indexWhere((s) => s == dynamicBoundary);
    if (boundaryIndex != -1) {
      String? attributionHeader;
      String? systemPromptPrefix;
      final staticBlocks = <String>[];
      final dynamicBlocks = <String>[];

      for (var i = 0; i < systemPrompt.length; i++) {
        final block = systemPrompt[i];
        if (block.isEmpty || block == dynamicBoundary) continue;
        if (block.startsWith('x-anthropic-billing-header')) {
          attributionHeader = block;
        } else if (prefixes.contains(block)) {
          systemPromptPrefix = block;
        } else if (i < boundaryIndex) {
          staticBlocks.add(block);
        } else {
          dynamicBlocks.add(block);
        }
      }

      return [
        if (attributionHeader != null)
          SystemPromptBlock(text: attributionHeader, cacheScope: null),
        if (systemPromptPrefix != null)
          SystemPromptBlock(text: systemPromptPrefix, cacheScope: null),
        if (staticBlocks.isNotEmpty)
          SystemPromptBlock(
            text: staticBlocks.join('\n\n'),
            cacheScope: CacheScope.global,
          ),
        if (dynamicBlocks.isNotEmpty)
          SystemPromptBlock(text: dynamicBlocks.join('\n\n'), cacheScope: null),
      ];
    }
  }

  // Mode 3: Default — org-level caching.
  String? attributionHeader;
  String? systemPromptPrefix;
  final rest = <String>[];

  for (final block in systemPrompt) {
    if (block.isEmpty) continue;
    if (block.startsWith('x-anthropic-billing-header')) {
      attributionHeader = block;
    } else if (prefixes.contains(block)) {
      systemPromptPrefix = block;
    } else {
      rest.add(block);
    }
  }

  return [
    if (attributionHeader != null)
      SystemPromptBlock(text: attributionHeader, cacheScope: null),
    if (systemPromptPrefix != null)
      SystemPromptBlock(text: systemPromptPrefix, cacheScope: CacheScope.org),
    if (rest.isNotEmpty)
      SystemPromptBlock(text: rest.join('\n\n'), cacheScope: CacheScope.org),
  ];
}