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)
}
}