Files
tekton/addons/godot_ai/handlers/api_handler.gd
T

90 lines
3.2 KiB
GDScript

@tool
extends RefCounted
## Read-only access to version-correct Godot class metadata.
const ErrorCodes := preload("res://addons/godot_ai/utils/error_codes.gd")
const ClassIntrospection := preload("res://addons/godot_ai/utils/class_introspection.gd")
const FuzzySuggestions := preload("res://addons/godot_ai/utils/fuzzy_suggestions.gd")
func get_class_info(params: Dictionary) -> Dictionary:
var requested_class: String = params.get("class_name", "")
if requested_class.is_empty():
return ErrorCodes.make(
ErrorCodes.MISSING_REQUIRED_PARAM,
"Missing required param: class_name"
)
if not ClassDB.class_exists(requested_class):
var script_class := _global_script_class(requested_class)
if not script_class.is_empty():
return _script_class_error(requested_class, script_class)
return _unknown_class_error(requested_class)
if params.has("limit") and int(params.get("limit")) < 0:
return ErrorCodes.make(
ErrorCodes.INVALID_PARAMS,
"limit must be >= 0; use limit=0 only when an unlimited section is needed"
)
var section_check := ClassIntrospection.validate_sections(
params.get("sections", ClassIntrospection.DEFAULT_SECTIONS)
)
if not section_check.invalid.is_empty():
return _invalid_sections_error(section_check.invalid)
return {"data": ClassIntrospection.build(requested_class, params)}
static func _unknown_class_error(requested_class: String) -> Dictionary:
var suggestions := _suggest_classes(requested_class)
var message := "Unknown Godot class: %s" % requested_class
if not suggestions.is_empty():
message += ". Did you mean: %s?" % ", ".join(suggestions)
var result := ErrorCodes.make(ErrorCodes.VALUE_OUT_OF_RANGE, message)
result["error"]["data"] = {"suggestions": suggestions}
return result
static func _suggest_classes(requested_class: String) -> Array[String]:
return FuzzySuggestions.rank(requested_class, ClassDB.get_class_list())
static func _global_script_class(requested_class: String) -> Dictionary:
for raw_info in ProjectSettings.get_global_class_list():
var info: Dictionary = raw_info
if info.get("class", "") == requested_class:
return info
return {}
static func _script_class_error(requested_class: String, script_class: Dictionary) -> Dictionary:
var path := str(script_class.get("path", ""))
var base := str(script_class.get("base", ""))
var message := (
"%s is a project script class, not a ClassDB class. "
+ "Use script_manage(op=\"find_symbols\", params={\"path\": \"%s\"}) for script symbols."
) % [requested_class, path]
var result := ErrorCodes.make(ErrorCodes.WRONG_TYPE, message)
result["error"]["data"] = {
"script_class": true,
"class_name": requested_class,
"base_class": base,
"path": path,
}
return result
static func _invalid_sections_error(invalid_sections: Array[String]) -> Dictionary:
var suggestions := {}
for section in invalid_sections:
suggestions[section] = FuzzySuggestions.rank(
section,
ClassIntrospection.KNOWN_SECTIONS,
3,
0.3
)
var message := "Unknown class-info section(s): %s. Valid sections: %s" % [
", ".join(invalid_sections),
", ".join(ClassIntrospection.KNOWN_SECTIONS),
]
var result := ErrorCodes.make(ErrorCodes.INVALID_PARAMS, message)
result["error"]["data"] = {"suggestions": suggestions}
return result