111 lines
3.6 KiB
GDScript
111 lines
3.6 KiB
GDScript
@tool
|
|
extends "res://addons/copilot/LLM.gd"
|
|
|
|
const URL = "https://api.openai.com/v1/chat/completions"
|
|
const SYSTEM_TEMPLATE = """You are a brilliant coding assistant for the game-engine Godot. The version used is Godot 4.0, and all code must be valid GDScript!
|
|
That means the new GDScript 2.0 syntax is used. Here's a couple of important changes that were introduced:
|
|
- Use @export annotation for exports
|
|
- Use Node3D instead of Spatial, and position instead of translation
|
|
- Use randf_range and randi_range instead of rand_range
|
|
- Connect signals via node.SIGNAL_NAME.connect(Callable(TARGET_OBJECT, TARGET_FUNC))
|
|
- Same for sort_custom calls, pass a Callable(TARGET_OBJECT, TARGET_FUNC)
|
|
- Use rad_to_deg instead of rad2deg
|
|
- Use PackedByteArray instead of PoolByteArray
|
|
- Use instantiate instead of instance
|
|
- You can't use enumerate(OBJECT). Instead, use "for i in len(OBJECT):"
|
|
|
|
Remember, this is not Python. It's GDScript for use in Godot.
|
|
|
|
You may only answer in code, never add any explanations. In your prompt, there will be an !INSERT_CODE_HERE! tag. Only respond with plausible code that may be inserted at that point. Never repeat the full script, only the parts to be inserted. Treat this as if it was an autocompletion. You may continue whatever word or expression was left unfinished before the tag. Make sure indentation matches the surrounding context."""
|
|
const INSERT_TAG = "!INSERT_CODE_HERE!"
|
|
const MAX_LENGTH = 8500
|
|
|
|
class Message:
|
|
var role: String
|
|
var content: String
|
|
|
|
func get_json():
|
|
return {
|
|
"role": role,
|
|
"content": content
|
|
}
|
|
|
|
const ROLES = {
|
|
"SYSTEM": "system",
|
|
"USER": "user",
|
|
"ASSISTANT": "assistant"
|
|
}
|
|
|
|
func _get_models():
|
|
return [
|
|
"gpt-3.5-turbo",
|
|
"gpt-4"
|
|
]
|
|
|
|
func _set_model(model_name):
|
|
model = model_name
|
|
|
|
func _send_user_prompt(user_prompt, user_suffix):
|
|
var messages = format_prompt(user_prompt, user_suffix)
|
|
get_completion(messages, user_prompt, user_suffix)
|
|
|
|
func format_prompt(prompt, suffix):
|
|
var messages = []
|
|
var system_prompt = SYSTEM_TEMPLATE
|
|
|
|
var combined_prompt = prompt + suffix
|
|
var diff = combined_prompt.length() - MAX_LENGTH
|
|
if diff > 0:
|
|
if suffix.length() > diff:
|
|
suffix = suffix.substr(0,diff)
|
|
else:
|
|
prompt = prompt.substr(diff - suffix.length())
|
|
suffix = ""
|
|
var user_prompt = prompt + INSERT_TAG + suffix
|
|
|
|
var msg = Message.new()
|
|
msg.role = ROLES.SYSTEM
|
|
msg.content = system_prompt
|
|
messages.append(msg.get_json())
|
|
|
|
msg = Message.new()
|
|
msg.role = ROLES.USER
|
|
msg.content = user_prompt
|
|
messages.append(msg.get_json())
|
|
|
|
return messages
|
|
|
|
func get_completion(messages, prompt, suffix):
|
|
var body = {
|
|
"model": model,
|
|
"messages": messages,
|
|
"temperature": 0.7,
|
|
"max_tokens": 500,
|
|
"stop": "\n\n" if allow_multiline else "\n"
|
|
}
|
|
var headers = [
|
|
"Content-Type: application/json",
|
|
"Authorization: Bearer %s" % api_key
|
|
]
|
|
var http_request = HTTPRequest.new()
|
|
add_child(http_request)
|
|
http_request.connect("request_completed",on_request_completed.bind(prompt, suffix, http_request))
|
|
var json_body = JSON.stringify(body)
|
|
var error = http_request.request(URL, headers, HTTPClient.METHOD_POST, json_body)
|
|
if error != OK:
|
|
emit_signal("completion_error", null)
|
|
|
|
|
|
func on_request_completed(result, response_code, headers, body, pre, post, http_request):
|
|
var test_json_conv = JSON.new()
|
|
test_json_conv.parse(body.get_string_from_utf8())
|
|
var json = test_json_conv.get_data()
|
|
var response = json
|
|
if !response.has("choices") :
|
|
emit_signal("completion_error", response)
|
|
return
|
|
var completion = response.choices[0].message
|
|
if is_instance_valid(http_request):
|
|
http_request.queue_free()
|
|
emit_signal("completion_received", completion.content, pre, post)
|