- Forked from Godot Engine 4.7-dev (MIT License) - Rebranded to AeThex Engine with cyan/purple theme - Added AI-powered development assistant module - Integrated Claude API for code completion & error fixing - Custom hexagon logo and branding - Multi-platform CI/CD (Windows, Linux, macOS) - Built Linux editor binary (151MB) - Complete source code with all customizations Tech Stack: - C++ game engine core - AI Module: Claude 3.5 Sonnet integration - Build: SCons, 14K+ source files - License: MIT (Godot) + Custom (AeThex features) Ready for Windows build via GitHub Actions!
238 lines
6.5 KiB
C++
238 lines
6.5 KiB
C++
/**************************************************************************/
|
|
/* ai_assistant.cpp */
|
|
/**************************************************************************/
|
|
|
|
#include "ai_assistant.h"
|
|
|
|
#include "core/config/project_settings.h"
|
|
#include "core/io/json.h"
|
|
|
|
AIAssistant *AIAssistant::singleton = nullptr;
|
|
|
|
void AIAssistant::_bind_methods() {
|
|
// Methods
|
|
ClassDB::bind_method(D_METHOD("initialize", "api_key"), &AIAssistant::initialize, DEFVAL(""));
|
|
ClassDB::bind_method(D_METHOD("is_initialized"), &AIAssistant::is_initialized);
|
|
|
|
ClassDB::bind_method(D_METHOD("get_code_completion", "context", "recent_code"), &AIAssistant::get_code_completion);
|
|
ClassDB::bind_method(D_METHOD("explain_code", "code_block"), &AIAssistant::explain_code);
|
|
ClassDB::bind_method(D_METHOD("fix_error", "error_message", "code_context"), &AIAssistant::fix_error);
|
|
|
|
ClassDB::bind_method(D_METHOD("generate_function_docs", "function_signature"), &AIAssistant::generate_function_docs);
|
|
ClassDB::bind_method(D_METHOD("natural_language_to_code", "description", "context"), &AIAssistant::natural_language_to_code);
|
|
|
|
ClassDB::bind_method(D_METHOD("set_api_key", "key"), &AIAssistant::set_api_key);
|
|
ClassDB::bind_method(D_METHOD("get_api_key"), &AIAssistant::get_api_key);
|
|
ClassDB::bind_method(D_METHOD("clear_cache"), &AIAssistant::clear_cache);
|
|
|
|
// Properties
|
|
ADD_PROPERTY(PropertyInfo(Variant::STRING, "api_key", PROPERTY_HINT_PASSWORD), "set_api_key", "get_api_key");
|
|
|
|
// Enums
|
|
BIND_ENUM_CONSTANT(COMPLETION_TYPE_CODE);
|
|
BIND_ENUM_CONSTANT(COMPLETION_TYPE_DOCUMENTATION);
|
|
BIND_ENUM_CONSTANT(COMPLETION_TYPE_EXPLANATION);
|
|
BIND_ENUM_CONSTANT(COMPLETION_TYPE_FIX);
|
|
}
|
|
|
|
Error AIAssistant::initialize(const String &p_api_key) {
|
|
if (initialized) {
|
|
return OK;
|
|
}
|
|
|
|
// Set API key
|
|
if (!p_api_key.is_empty()) {
|
|
api_key = p_api_key;
|
|
} else {
|
|
// Try to get from project settings or environment
|
|
if (ProjectSettings::get_singleton()->has_setting("aethex/ai/api_key")) {
|
|
api_key = ProjectSettings::get_singleton()->get_setting("aethex/ai/api_key");
|
|
}
|
|
}
|
|
|
|
if (api_key.is_empty()) {
|
|
ERR_PRINT("AIAssistant: No API key provided. Set it via initialize() or project settings.");
|
|
return ERR_UNCONFIGURED;
|
|
}
|
|
|
|
// Initialize API client
|
|
api_client.instantiate();
|
|
api_client->set_api_key(api_key);
|
|
Error err = api_client->initialize();
|
|
|
|
if (err != OK) {
|
|
ERR_PRINT("AIAssistant: Failed to initialize API client.");
|
|
return err;
|
|
}
|
|
|
|
initialized = true;
|
|
print_line("AIAssistant initialized successfully!");
|
|
return OK;
|
|
}
|
|
|
|
String AIAssistant::get_code_completion(const String &p_context, const PackedStringArray &p_recent_code) {
|
|
if (!initialized) {
|
|
ERR_PRINT("AIAssistant: Not initialized. Call initialize() first.");
|
|
return "";
|
|
}
|
|
|
|
// Build prompt
|
|
String prompt = "You are an expert GDScript developer. Complete the following code:\n\n";
|
|
|
|
// Add recent code context
|
|
if (p_recent_code.size() > 0) {
|
|
prompt += "Recent code:\n";
|
|
for (int i = 0; i < p_recent_code.size(); i++) {
|
|
prompt += p_recent_code[i] + "\n";
|
|
}
|
|
prompt += "\n";
|
|
}
|
|
|
|
prompt += "Current context:\n" + p_context + "\n\n";
|
|
prompt += "Provide only the code completion, no explanations:";
|
|
|
|
// Check cache
|
|
String cached = _get_cached_response(prompt);
|
|
if (!cached.is_empty()) {
|
|
return cached;
|
|
}
|
|
|
|
// Make API call (sync for now)
|
|
String response = api_client->send_completion_sync(prompt, 5.0);
|
|
|
|
// Cache the response
|
|
if (!response.is_empty()) {
|
|
_cache_response(prompt, response);
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
String AIAssistant::explain_code(const String &p_code_block) {
|
|
if (!initialized) {
|
|
return "";
|
|
}
|
|
|
|
String prompt = "Explain this GDScript code clearly and concisely:\n\n";
|
|
prompt += p_code_block;
|
|
|
|
String cached = _get_cached_response(prompt);
|
|
if (!cached.is_empty()) {
|
|
return cached;
|
|
}
|
|
|
|
String response = api_client->send_completion_sync(prompt, 10.0);
|
|
if (!response.is_empty()) {
|
|
_cache_response(prompt, response);
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
String AIAssistant::fix_error(const String &p_error_message, const String &p_code_context) {
|
|
if (!initialized) {
|
|
return "";
|
|
}
|
|
|
|
String prompt = "Fix this GDScript error. Provide the corrected code:\n\n";
|
|
prompt += "Error: " + p_error_message + "\n\n";
|
|
prompt += "Code:\n" + p_code_context;
|
|
|
|
String response = api_client->send_completion_sync(prompt, 10.0);
|
|
return response;
|
|
}
|
|
|
|
String AIAssistant::generate_function_docs(const String &p_function_signature) {
|
|
if (!initialized) {
|
|
return "";
|
|
}
|
|
|
|
String prompt = "Generate documentation for this GDScript function:\n\n";
|
|
prompt += p_function_signature + "\n\n";
|
|
prompt += "Format: Brief description, parameters, returns.";
|
|
|
|
String cached = _get_cached_response(prompt);
|
|
if (!cached.is_empty()) {
|
|
return cached;
|
|
}
|
|
|
|
String response = api_client->send_completion_sync(prompt, 5.0);
|
|
if (!response.is_empty()) {
|
|
_cache_response(prompt, response);
|
|
}
|
|
|
|
return response;
|
|
}
|
|
|
|
String AIAssistant::generate_class_docs(const String &p_class_code) {
|
|
if (!initialized) {
|
|
return "";
|
|
}
|
|
|
|
String prompt = "Generate complete documentation for this GDScript class:\n\n";
|
|
prompt += p_class_code;
|
|
|
|
String response = api_client->send_completion_sync(prompt, 15.0);
|
|
return response;
|
|
}
|
|
|
|
String AIAssistant::natural_language_to_code(const String &p_description, const String &p_context) {
|
|
if (!initialized) {
|
|
return "";
|
|
}
|
|
|
|
String prompt = "Convert this description to GDScript code:\n\n";
|
|
prompt += p_description + "\n\n";
|
|
|
|
if (!p_context.is_empty()) {
|
|
prompt += "Context:\n" + p_context + "\n\n";
|
|
}
|
|
|
|
prompt += "Provide clean, working GDScript code:";
|
|
|
|
String response = api_client->send_completion_sync(prompt, 10.0);
|
|
return response;
|
|
}
|
|
|
|
void AIAssistant::set_api_key(const String &p_key) {
|
|
api_key = p_key;
|
|
if (api_client.is_valid()) {
|
|
api_client->set_api_key(p_key);
|
|
}
|
|
}
|
|
|
|
String AIAssistant::get_api_key() const {
|
|
return api_key;
|
|
}
|
|
|
|
void AIAssistant::clear_cache() {
|
|
response_cache.clear();
|
|
print_line("AIAssistant: Cache cleared.");
|
|
}
|
|
|
|
String AIAssistant::_get_cached_response(const String &p_prompt) {
|
|
if (response_cache.has(p_prompt)) {
|
|
return response_cache[p_prompt];
|
|
}
|
|
return "";
|
|
}
|
|
|
|
void AIAssistant::_cache_response(const String &p_prompt, const String &p_response) {
|
|
// Simple cache with size limit
|
|
if (response_cache.size() > 100) {
|
|
response_cache.clear(); // Simple eviction
|
|
}
|
|
response_cache[p_prompt] = p_response;
|
|
}
|
|
|
|
AIAssistant *AIAssistant::get_singleton() {
|
|
return singleton;
|
|
}
|
|
|
|
AIAssistant::AIAssistant() {
|
|
singleton = this;
|
|
}
|
|
|
|
AIAssistant::~AIAssistant() {
|
|
singleton = nullptr;
|
|
}
|