Skip to content

Remove new line on generations with Anthropic

Tan Le requested to merge remove-new-line-generation-anthropic into main

What does this merge request do and why?

Remove new line on generations with Anthropic

This removes new line prepending on model output for code generations with Anthropic since we do not have a reliable way to detect when to perform this processing. The false positive case, ie. we prepend new line when shouldn't, is more severe than the false negative. User has to perform more keystrokes to rectify the later.

Relates to gitlab-org/editor-extensions/gitlab-lsp#61 (closed)

How to set up and validate locally

  1. Check out to this merge request's branch.

  2. Ensure a local Docker image built successfully.

    docker buildx build --platform linux/amd64 \
      -t code-suggestions-api:dev .
  3. Run a local service on Docker.

    docker run --platform linux/amd64 --rm \
      -p 5052:5052 \
      -e AUTH_BYPASS_EXTERNAL=true \
      -v $PWD:/app -it code-suggestions-api:dev
  4. Send the following cURL command with the GitLab prompt template

    curl --request POST \
      --url http://codesuggestions.gdk.test:5999/v2/code/generations \
      --header 'Content-Type: application/json' \
      --header 'X-Gitlab-Authentication-Type: oidc' \
      --data '{
      "current_file": {
        "file_name": "main.go",
        "content_above_cursor": "# Hello world",
        "content_below_cursor": "\n"
      },
      "prompt_version": 2,
      "prompt": "Human: You are a coding autocomplete agent. We want to generate new Go code inside the\nfile '\''hello.go'\'' based on instructions from the user.\nThe existing code is provided in <existing_code></existing_code> tags.\nThe new code you will generate will start at the position of the cursor, which is currently indicated by the <cursor> XML tag.\nIn your process, first, review the existing code to understand its logic and format. Then, try to determine the most\nlikely new code to generate at the cursor position to fulfill the instructions.\n\nWhen generating the new code, please ensure the following:\n1. It is valid Go code.\n2. It matches the existing code'\''s variable, parameter and function names.\n3. It does not repeat any existing code. Do not repeat code that comes before or after the cursor tags. This includes cases where the cursor is in the middle of a word.\n4. If the cursor is in the middle of a word, it finishes the word instead of repeating code before the cursor tag.\n\nReturn new code enclosed in <new_code></new_code> tags. We will then insert this at the <cursor> position.\nIf you are not able to write code based on the given instructions return an empty result like <new_code></new_code>.\n\nHere are a few examples of successfully generated code by other autocomplete agents:\n\n<examples>\n\n  <example>\n    H: <existing_code>\n         package main\nimport (\n  \"net/http\"\n)\nfunc main() {\n  err := http.ListenAndServe(httpPort, nil)\n  if err != nil { panic(err) }\n}\n// create an HTTP handler that fetches the current user preferences and returns them as JSON\n<cursor>\n\n// function to print the current user'\''s name\n       </existing_code>\n\n    A: <new_code>func userPrefsHandler(w http.ResponseWriter, r *http.Request) {\n\n  // Get current user\n  user := getCurrentUser(r)\n\n  // Fetch user prefs from database\n  prefs, err := fetchUserPrefs(user.ID)\n  if err != nil { http.Error(w, \"Error fetching prefs\", 500); return }\n\n  // Encode prefs to JSON\n  jsonPrefs := json.Marshal(prefs)\n  if err != nil { http.Error(w, \"Error encoding prefs\", 500); return }\n\n  w.Header().Set(\"Content-Type\", \"application/json\")\n  w.Write(jsonPrefs)\n}</new_code>\n  </example>\n\n  <example>\n    H: <existing_code>\n         // filter out any non-prime numbers from the list\nfunc filterPrimes(list []int) []int {\n<cursor>\n  return primes\n}\n\n// calculate the square root of a number\n       </existing_code>\n\n    A: <new_code>var primes []int\n  for _, num := range list {\n    isPrime := true\n    for i := 2; i <= num/2; i++ {\n      if num%i == 0 {\n        isPrime = false\n        break\n      }\n    }\n    if isPrime { primes = append(primes, num) }\n  }</new_code>\n  </example>\n\n</examples>\n\n\n<existing_code>\n// Generate a function to print hello world\nfunc print<cursor>\n</existing_code>\n\n\nHere are instructions provided in <instruction></instruction> tags.\n\n<instruction>\nCreate more new code for this file. If the cursor is inside an empty function,\ngenerate its most likely contents based on the function name and signature.\n\n</instruction>\n\n\nAssistant: <new_code>",
      "model_provider": "anthropic"
    }
    '
    See the rendered prompt
    Human: You are a coding autocomplete agent. We want to generate new Go code inside the
    file 'hello.go' based on instructions from the user.
    The existing code is provided in <existing_code></existing_code> tags.
    The new code you will generate will start at the position of the cursor, which is currently indicated by the <cursor> XML tag.
    In your process, first, review the existing code to understand its logic and format. Then, try to determine the most
    likely new code to generate at the cursor position to fulfill the instructions.
    
    When generating the new code, please ensure the following:
    1. It is valid Go code.
    2. It matches the existing code's variable, parameter and function names.
    3. It does not repeat any existing code. Do not repeat code that comes before or after the cursor tags. This includes cases where the cursor is in the middle of a word.
    4. If the cursor is in the middle of a word, it finishes the word instead of repeating code before the cursor tag.
    
    Return new code enclosed in <new_code></new_code> tags. We will then insert this at the <cursor> position.
    If you are not able to write code based on the given instructions return an empty result like <new_code></new_code>.
    
    Here are a few examples of successfully generated code by other autocomplete agents:
    
    <examples>
    
      <example>
        H: <existing_code>
             package main
    import (
      "net/http"
    )
    func main() {
      err := http.ListenAndServe(httpPort, nil)
      if err != nil { panic(err) }
    }
    // create an HTTP handler that fetches the current user preferences and returns them as JSON
    <cursor>
    
    // function to print the current user's name
           </existing_code>
    
        A: <new_code>func userPrefsHandler(w http.ResponseWriter, r *http.Request) {
    
      // Get current user
      user := getCurrentUser(r)
    
      // Fetch user prefs from database
      prefs, err := fetchUserPrefs(user.ID)
      if err != nil { http.Error(w, "Error fetching prefs", 500); return }
    
      // Encode prefs to JSON
      jsonPrefs := json.Marshal(prefs)
      if err != nil { http.Error(w, "Error encoding prefs", 500); return }
    
      w.Header().Set("Content-Type", "application/json")
      w.Write(jsonPrefs)
    }</new_code>
      </example>
    
      <example>
        H: <existing_code>
             // filter out any non-prime numbers from the list
    func filterPrimes(list []int) []int {
    <cursor>
      return primes
    }
    
    // calculate the square root of a number
           </existing_code>
    
        A: <new_code>var primes []int
      for _, num := range list {
        isPrime := true
        for i := 2; i <= num/2; i++ {
          if num%i == 0 {
            isPrime = false
            break
          }
        }
        if isPrime { primes = append(primes, num) }
      }</new_code>
      </example>
    
    </examples>
    
    
    <existing_code>
    // Generate a function to print hello world
    func print<cursor>
    </existing_code>
    
    
    Here are instructions provided in <instruction></instruction> tags.
    
    <instruction>
    Create more new code for this file. If the cursor is inside an empty function,
    generate its most likely contents based on the function name and signature.
    
    </instruction>
    
    
    Assistant: <new_code>
    
  5. Confirm that the output does not have a preceding new line

    {
      "id": "id",
      "model": {
        "engine": "anthropic",
        "name": "claude-2",
        "lang": "python"
      },
      "experiments": [],
      "object": "text_completion",
      "created": 1700457069,
      "choices": [
        {
          "text": "Hello() {\n  fmt.Println(\"Hello World!\")\n}",
          "index": 0,
          "finish_reason": "length"
        }
      ]
    }

Merge request checklist

  • Tests added for new functionality. If not, please raise an issue to follow up.
  • Documentation added/updated, if needed.
Edited by Tan Le

Merge request reports