Как известно, у больших языковых моделей (LLM) существуют ограничения по размеру контекстного окна. При постановке вопроса часто невозможно вставить весь исходный текст, что требует объединения кода из разных файлов в одном месте.

В связи с этим я разработал скрипт, который минимизирует исходный код проекта путем удаления пробелов, табуляций, комментариев и тестовых функций. Скрипт позволяет собрать все или выбранные файлы проекта в одном месте.

Для использования просто запустите скрипт в директории вашего проекта, чтобы сгенерировать минимизированный файл out.txt, содержащий оптимизированный код, готовый для использования с крупными языковыми моделями.

Перед запуском скрипта отредактируйте следующие массивы в соответствии с потребностями вашего проекта: folders_to_ignore, extensions_to_search, filenames_to_search, comment_chars и stop_words.

Пример конфигурации для проекта на Rust (включение всех файлов *.rs в out.txt):

folders_to_ignore=("target" ".git" ".github" ".gitignore" ".idea" )   # Folders to ignore
extensions_to_search=( "rs" )                              # File extensions to search for
filenames_to_search=("Cargo.toml")                       # Filenames to search for
comment_chars=("#" "//" "/*")                            # Characters that denote comments
stop_words=("#[cfg(test)]")                              # Stop words after which to ignore the remaining lines in the file

Пример конфигурации для проекта на Rust (включение только определенных файлов в out.txt):

folders_to_ignore=("target" ".git" ".github" ".gitignore" ".idea" )   # Folders to ignore
extensions_to_search=( )                              # File extensions to search for
filenames_to_search=("Cargo.toml" "lib.rs" "core.rs")                       # Filenames to search for
comment_chars=("#" "//" "/*")                            # Characters that denote comments
stop_words=("#[cfg(test)]")                              # Stop words after which to ignore the remaining lines in the file

Bash-версия скрипта:

#!/bin/bash

# Remove existing out.txt if it exists
rm -f out.txt

# Arrays
folders_to_ignore=("target" ".git" ".github" ".gitignore" ".idea" )   # Folders to ignore
extensions_to_search=( "rs" )                              # File extensions to search for
filenames_to_search=("Cargo.toml" "core.rs" "text.rs" "json.rs")                       # Filenames to search for
comment_chars=("#" "//" "/*")                            # Characters that denote comments
stop_words=("#[cfg(test)]")                              # Stop words after which to ignore the remaining lines in the file

# Build the 'find' command

# Start with the basic 'find' command
find_cmd="find ."

# Add folders to ignore
if [ ${#folders_to_ignore[@]} -gt 0 ]; then
    ignore_dir_expr=""
    for dir in "${folders_to_ignore[@]}"; do
        if [ -n "$ignore_dir_expr" ]; then
            ignore_dir_expr+=" -o "
        fi
        ignore_dir_expr+="-path './$dir' -prune"
    done
    find_cmd+=" \\( $ignore_dir_expr \\) -o"
fi

# Add conditions to search for files
find_cmd+=" \\( "

name_patterns=()

# Add file extensions
for ext in "${extensions_to_search[@]}"; do
    name_patterns+=("-name '*.$ext'")
done

# Add filenames
for fname in "${filenames_to_search[@]}"; do
    name_patterns+=("-name '$fname'")
done

# Combine all patterns using -o
for ((i=0; i<${#name_patterns[@]}; i++)); do
    find_cmd+=" ${name_patterns[$i]}"
    if [ $i -lt $((${#name_patterns[@]} - 1)) ]; then
        find_cmd+=" -o"
    fi
done

find_cmd+=" \\) -type f -print"

# Print the final command for debugging (you can comment out this line)
# echo "Running command: $find_cmd"

# Build the regular expression for comments
comment_pattern=""
for ((i=0; i<${#comment_chars[@]}; i++)); do
    # Escape special characters in comment characters
    escaped_char=$(printf '%s\n' "${comment_chars[$i]}" | sed 's/[][(){}.*+?^$\\|/]/\\&/g')
    if [ $i -eq 0 ]; then
        comment_pattern="$escaped_char"
    else
        comment_pattern="$comment_pattern|$escaped_char"
    fi
done

# Execute the 'find' command and process the results
while read filepath; do
    echo -e "\n#### $filepath ####" >> out.txt
    stop=false
    # Process the file line by line
    while IFS= read -r line; do
        if [ "$stop" = true ]; then
            break
        fi
        # Remove tabs
        line="${line//$'\t'/}"
        # Remove leading spaces
        line="${line#"${line%%[![:space:]]*}"}"
        # Remove trailing spaces
        line="${line%"${line##*[![:space:]]}"}"
        # Skip lines that are empty or contain only spaces
        if [[ -z "$line" ]]; then
            continue
        fi
        # Check for stop words
        for stop_word in "${stop_words[@]}"; do
            if [[ "$line" == "$stop_word" ]]; then
                stop=true
                break
            fi
        done
        if [ "$stop" = true ]; then
            break
        fi
        # Skip lines that are comments
        if [[ "$line" =~ ^($comment_pattern) ]]; then
            continue
        fi
        # Write the processed line to out.txt
        echo "$line" >> out.txt
    done < "$filepath"
done < <(eval $find_cmd)

PowerShell-версия скрипта:

# Remove existing out.txt if it exists
if (Test-Path -Path "out.txt") {
    Remove-Item -Path "out.txt" -Force
}

# Define arrays

# Folders and files to ignore during the search
$foldersToIgnore = @("target", ".git", ".github", ".gitignore", ".idea")

# File extensions to search for
$extensionsToSearch = @("rs")

# Specific filenames to search for
$filenamesToSearch = @("Cargo.toml", "core.rs", "text.rs", "json.rs")

# Characters that denote comments in the files
$commentChars = @("#", "//", "/*")

# Words that, when encountered, will stop processing the current file
$stopWords = @("#[cfg(test)]")

# Function to build file filtering based on provided criteria
function Get-FilteredFiles {
    param (
        [string[]]$IgnoreFolders,
        [string[]]$Extensions,
        [string[]]$Filenames
    )

    # Build a regex pattern for ignored folders
    if ($IgnoreFolders.Count -gt 0) {
        $ignorePattern = ($IgnoreFolders | ForEach-Object { [regex]::Escape($_) }) -join '|'
    } else {
        $ignorePattern = ""
    }

    # Build a list of filters for extensions and filenames
    $nameFilters = @()
    foreach ($ext in $Extensions) {
        $nameFilters += "*.$ext"
    }
    foreach ($fname in $Filenames) {
        $nameFilters += $fname
    }

    # Get all files with the specified extensions or filenames
    Get-ChildItem -Path . -Recurse -File -Include $nameFilters | Where-Object {
        if ($ignorePattern) {
            # Check if the full path contains any of the ignored folders
            -not ($_.FullName -match "\\($ignorePattern)\\")
        } else {
            $true
        }
    }
}

# Build a regex pattern for comments
$escapedCommentChars = $commentChars | ForEach-Object { [regex]::Escape($_) }
$commentPattern = $escapedCommentChars -join '|'

# Get the list of files to process
$files = Get-FilteredFiles -IgnoreFolders $foldersToIgnore -Extensions $extensionsToSearch -Filenames $filenamesToSearch

# Process each file
foreach ($file in $files) {
    # Add file header to out.txt
    "`n#### $($file.FullName) ####" | Out-File -FilePath "out.txt" -Append -Encoding utf8

    $stop = $false

    # Read the file line by line
    Get-Content -Path $file.FullName | ForEach-Object {
        if ($stop) {
            return
        }

        $line = $_

        # Remove tabs
        $line = $line -replace "`t", ""

        # Trim leading and trailing spaces
        $line = $line.Trim()

        # Skip empty lines
        if ([string]::IsNullOrWhiteSpace($line)) {
            return
        }

        # Check for stop words
        foreach ($stopWord in $stopWords) {
            if ($line -eq $stopWord) {
                $stop = $true
                break
            }
        }
        if ($stop) {
            return
        }

        # Skip lines that are comments
        if ($line -match "^($commentPattern)") {
            return
        }

        # Write the processed line to out.txt
        $line | Out-File -FilePath "out.txt" -Append -Encoding utf8
    }
}

P.S.

Содержимое файла out.txt необходимо скопировать в буфер обмена и вставить как текст в окно ввода LLM. Не прикрепляйте файл out.txt к вопросу. Обычно LLM из соображений оптимизации обрабатывает файлы, извлекая из них резюме, и на основе этого резюме отвечает на вопрос. Другими словами, если вы вставите содержимое файла out.txt в окно ввода LLM и затем зададите вопрос, модель будет отвечать на основе всего содержимого файла out.txt.

Исходный код скриптов находится на GitHub, если у вас есть улучшения, то делайте pull request.

Комментарии (0)