Skip to content

Add context to code suggestions API

What does this MR do and why?

Adds context to code completion API

Add a context param to /code_suggestions/completions API. The context is an array of object with the following fields:

  • type - The type of the content. Can be either 'file' or 'function'.
  • name - The name of the content. File name for files, and function name for functions.
  • content - The content itself. The file or a code snippet

Contributes to https://gitlab.com/gitlab-org/editor-extensions/gitlab-lsp/-/issues/208

MR acceptance checklist

Please evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

How to set up and validate locally

Let's assume the codebase consists of 3 files. FullName method, intentionally implemented in an unusually way, to demonstrate that the context improves code suggestions.

user.go file

package main

type User struct {
	Title string
	First string
	Last  string
}

func (u User) FullName(withTitle bool) string {
	fullName := u.First + " " + u.Last

	if withTitle {
		fullName = u.Title + ". " + fullName
	}

	return fullName
}

main.go file

package main

import "fmt"

func main() {
	user := User{"Mr", "John", "Doe"}
	fmt.Printf("Full name: %s\n", user.FullName(true))
}

user_test.go file

package main

import "testing"

// Write test for User FullName

Request without context

curl --request POST \
     --url http://gdk.test:3000/api/v4/code_suggestions/completions \
     --header 'Content-Type: application/json' \
     --header "Private-Token: $LOCALHOST_TOKEN" \
     --data-binary '{
  "current_file": {
    "file_name": "user_test.go",
    "content_above_cursor": "package main\\n\\nimport \"testing\"\\n// Write test for User FullName",
    "content_below_cursor": "\\n"
  },
  "stream": true
}' --no-buffer --location

Response

func TestUserFullName(t *testing.T) {
    user := User{
        FirstName: "John",
        LastName:  "Doe",
    }

    fullName := user.FullName()
    expected := "John Doe"

    if fullName != expected {
        t.Errorf("FullName() = %q, expected %q", fullName, expected)
    }
}

As you can see, the code suggestion is a pure guess. The code is only generated for the function name.

Request with context

curl --request POST \
     --url http://gdk.test:3000/api/v4/code_suggestions/completions \
     --header 'Content-Type: application/json' \
     --header "Private-Token: $LOCALHOST_TOKEN" \
     --data-binary '{
  "current_file": {
    "file_name": "user_test.go",
    "content_above_cursor": "package main\\n\\nimport \"testing\"\\n// Write test for User FullName",
    "content_below_cursor": "\\n"
  },
  "stream": true,
  "instruction": "Write test for User FullName function",
  "context": [
    { "type": "file", "name": "user.go", "content": "package main\n\ntype User struct {\n\tTitle string\n\tFirst string\n\tLast  string\n}\n\nfunc (u User) FullName(withTitle bool) string {\n\tfullName := u.First + \" \" + u.Last\n\n\tif withTitle {\n\t\tfullName = u.Title + \". \" + fullName\n\t}\n\n\treturn fullName\n}\n" },
    { "type": "file", "name": "main.go", "content": "package main\n\nimport \"fmt\"\n\nfunc main() {\n\tuser := User{\"Mr\", \"John\", \"Doe\"}\n\tfmt.Printf(\"Full name: %s\\n\", user.FullName(true))\n}\n" }
  ]
}' --no-buffer --location

Response

Now with an additional context we managed to generate correct code for your weirdly implemented function.

func TestUserFullName(t *testing.T) {
	user := User{"Mr", "John", "Doe"}

	fullName := user.FullName(true)
	expected := "Mr. John Doe"
	if fullName != expected {
		t.Errorf("FullName(true) = %s; expected %s", fullName, expected)
	}

	fullName = user.FullName(false)
	expected = "John Doe"
	if fullName != expected {
		t.Errorf("FullName(false) = %s; expected %s", fullName, expected)
	}
}
Edited by Vitali Tatarintev

Merge request reports