metahunt/Assets/barScene/AI Toolbox/Runtime/ParametersPropertyDrawer.cs
2024-02-11 13:53:18 +05:30

383 lines
21 KiB
C#

#if UNITY_EDITOR
using System;
using UnityEditor;
using UnityEngine;
namespace AiToolbox {
[CustomPropertyDrawer(typeof(Parameters))]
public class ParametersPropertyDrawer : PropertyDrawer {
private float _height;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
label.text = "ChatGPT Parameters";
label.tooltip = "Settings for the ChatGPT API.";
EditorGUI.BeginProperty(position, label, property);
property.isExpanded =
EditorGUI.Foldout(new Rect(position.x, position.y, position.width - 20, EditorGUIUtility.singleLineHeight),
property.isExpanded, label, true);
_height = EditorGUIUtility.singleLineHeight;
// Question mark button
{
var buttonRect = new Rect(position.x + position.width - 20, position.y + 2, 20,
EditorGUIUtility.singleLineHeight);
var icon = EditorGUIUtility.IconContent("_Help");
icon.tooltip = "Open documentation";
if (GUI.Button(buttonRect, icon, EditorStyles.label)) {
Application.OpenURL("https://ai-toolbox.dustyroom.com/runtime");
}
}
if (property.isExpanded) {
EditorGUI.indentLevel++;
var width = position.width - EditorGUIUtility.standardVerticalSpacing * 2f;
// Space
{
var rect = new Rect(position.x, position.y + _height, width, EditorGUIUtility.standardVerticalSpacing);
EditorGUI.LabelField(rect, GUIContent.none);
_height += rect.height + EditorGUIUtility.standardVerticalSpacing;
}
// API Key
{
var apiKeyProperty = property.FindPropertyRelative(nameof(Parameters.apiKey));
var apiKey = apiKeyProperty.stringValue;
if (string.IsNullOrEmpty(apiKey)) {
const string m = "API Key is required.";
var helpBoxHeight = EditorStyles.helpBox.CalcHeight(new GUIContent(m),
width - EditorGUIUtility.labelWidth - 24);
var helpBoxRect = new Rect(position.x + EditorGUIUtility.labelWidth, position.y + _height,
width - EditorGUIUtility.labelWidth, helpBoxHeight);
EditorGUI.HelpBox(helpBoxRect, m, MessageType.Error);
_height += helpBoxRect.height + EditorGUIUtility.standardVerticalSpacing;
var buttonRect = new Rect(position.x + EditorGUIUtility.labelWidth, position.y + _height,
width - EditorGUIUtility.labelWidth, EditorGUIUtility.singleLineHeight);
_height += buttonRect.height + EditorGUIUtility.standardVerticalSpacing;
if (GUI.Button(buttonRect, "Open API Keys page", EditorStyles.miniButton)) {
Application.OpenURL("https://platform.openai.com/account/api-keys");
}
}
var rect = new Rect(position.x, position.y + _height, width, EditorGUIUtility.singleLineHeight);
_height += rect.height + EditorGUIUtility.standardVerticalSpacing;
var encryptionProperty = property.FindPropertyRelative(nameof(Parameters.apiKeyEncryption));
var isRemote = encryptionProperty.enumValueIndex == (int)ApiKeyEncryption.RemoteConfig;
var apiKeyTitle = isRemote ? "API Key (fallback)" : "API Key";
var apiKeyLabel = new GUIContent(apiKeyTitle, "Your API Key from the OpenAI platform.");
var isEncrypted = encryptionProperty.enumValueIndex == (int)ApiKeyEncryption.LocallyEncrypted;
var passwordProperty = property.FindPropertyRelative(nameof(Parameters.apiKeyEncryptionPassword));
if (isEncrypted && !string.IsNullOrEmpty(apiKey) && !apiKey.StartsWith("sk-") &&
!string.IsNullOrEmpty(passwordProperty.stringValue)) {
apiKey = Key.B(apiKey, passwordProperty.stringValue);
}
var apiKeyEdited = EditorGUI.TextField(rect, apiKeyLabel, apiKey);
if (apiKeyEdited != apiKey) {
apiKeyProperty.stringValue = isEncrypted && !string.IsNullOrEmpty(apiKeyEdited)
? Key.A(apiKeyEdited, passwordProperty.stringValue)
: apiKeyEdited;
}
}
// API Key encryption
{
EditorGUI.indentLevel++;
var encryptionProperty = property.FindPropertyRelative(nameof(Parameters.apiKeyEncryption));
// Warning
if (encryptionProperty.enumValueIndex == (int)ApiKeyEncryption.None &&
!string.IsNullOrEmpty(property.FindPropertyRelative(nameof(Parameters.apiKey)).stringValue)) {
const string m = "API Key is not encrypted. A malicious actor could steal your API Key by " +
"decompiling your build.";
var helpBoxHeight = EditorStyles.helpBox.CalcHeight(new GUIContent(m),
width - EditorGUIUtility.labelWidth - 24);
var helpBoxRect = new Rect(position.x + EditorGUIUtility.labelWidth, position.y + _height,
width - EditorGUIUtility.labelWidth, helpBoxHeight);
EditorGUI.HelpBox(helpBoxRect, m, MessageType.Warning);
_height += helpBoxRect.height + EditorGUIUtility.standardVerticalSpacing;
}
var rect = new Rect(position.x, position.y + _height, width, EditorGUIUtility.singleLineHeight);
_height += rect.height + EditorGUIUtility.standardVerticalSpacing;
var encryptionLabel = new GUIContent("Encryption",
"Encrypt API Key to prevent malicious actors from stealing it.");
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(rect, encryptionProperty, encryptionLabel);
if (EditorGUI.EndChangeCheck()) {
var apiKeyProperty = property.FindPropertyRelative(nameof(Parameters.apiKey));
var passwordProperty = property.FindPropertyRelative(nameof(Parameters.apiKeyEncryptionPassword));
var isEncrypted = encryptionProperty.enumValueIndex == (int)ApiKeyEncryption.LocallyEncrypted;
if (isEncrypted) {
if (string.IsNullOrEmpty(passwordProperty.stringValue)) {
passwordProperty.stringValue = Guid.NewGuid().ToString();
}
if (apiKeyProperty.stringValue.StartsWith("sk-") &&
!string.IsNullOrEmpty(apiKeyProperty.stringValue)) {
var encryptedApiKey = Key.A(apiKeyProperty.stringValue, passwordProperty.stringValue);
apiKeyProperty.stringValue = encryptedApiKey;
}
} else {
if (!apiKeyProperty.stringValue.StartsWith("sk-") &&
!string.IsNullOrEmpty(passwordProperty.stringValue) &&
!string.IsNullOrEmpty(apiKeyProperty.stringValue)) {
var decryptedApiKey = Key.B(apiKeyProperty.stringValue, passwordProperty.stringValue);
apiKeyProperty.stringValue = decryptedApiKey;
}
}
}
var showPasswordField = encryptionProperty.enumValueIndex == (int)ApiKeyEncryption.LocallyEncrypted;
if (showPasswordField) {
var passwordProperty = property.FindPropertyRelative(nameof(Parameters.apiKeyEncryptionPassword));
if (string.IsNullOrEmpty(passwordProperty.stringValue)) {
const string m = "Password is required.";
var helpBoxHeight = EditorStyles.helpBox.CalcHeight(new GUIContent(m),
width - EditorGUIUtility.labelWidth - 24);
var helpBoxRect = new Rect(position.x + EditorGUIUtility.labelWidth, position.y + _height,
width - EditorGUIUtility.labelWidth, helpBoxHeight);
EditorGUI.HelpBox(helpBoxRect, m, MessageType.Error);
_height += helpBoxRect.height + EditorGUIUtility.standardVerticalSpacing;
}
var passwordRect = new Rect(position.x, position.y + _height, width,
EditorGUIUtility.singleLineHeight);
var passwordLabel = new GUIContent("Password", "Password used to encrypt API Key.");
var passwordEdited = EditorGUI.TextField(passwordRect, passwordLabel, passwordProperty.stringValue);
if (passwordEdited != passwordProperty.stringValue) {
var apiKeyProperty = property.FindPropertyRelative(nameof(Parameters.apiKey));
var decryptedApiKey = string.IsNullOrEmpty(passwordProperty.stringValue)
? apiKeyProperty.stringValue
: Key.B(apiKeyProperty.stringValue, passwordProperty.stringValue);
var encryptedApiKey = string.IsNullOrEmpty(passwordEdited)
? decryptedApiKey
: Key.A(decryptedApiKey, passwordEdited);
apiKeyProperty.stringValue = encryptedApiKey;
passwordProperty.stringValue = passwordEdited;
}
_height += passwordRect.height + EditorGUIUtility.standardVerticalSpacing;
}
var showRemoteConfigField = encryptionProperty.enumValueIndex == (int)ApiKeyEncryption.RemoteConfig;
if (showRemoteConfigField) {
var remoteConfigKeyProperty =
property.FindPropertyRelative(nameof(Parameters.apiKeyRemoteConfigKey));
var remoteConfigKeyRect = new Rect(position.x, position.y + _height, width,
EditorGUIUtility.singleLineHeight);
EditorGUI.PropertyField(remoteConfigKeyRect, remoteConfigKeyProperty,
new GUIContent("Remote Config Key"));
_height += remoteConfigKeyRect.height + EditorGUIUtility.standardVerticalSpacing;
}
EditorGUI.indentLevel--;
}
// Space
{
var rect = new Rect(position.x, position.y + _height, width, EditorGUIUtility.standardVerticalSpacing);
EditorGUI.LabelField(rect, GUIContent.none);
_height += rect.height + EditorGUIUtility.standardVerticalSpacing;
}
// Model
{
var modelProperty = property.FindPropertyRelative(nameof(Parameters.model));
var rect = new Rect(position.x, position.y + _height, width, EditorGUIUtility.singleLineHeight);
var modelLabel = new GUIContent("Model", "The ChatGPT model to use.");
EditorGUI.PropertyField(rect, modelProperty, modelLabel);
_height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
if (modelProperty.enumValueIndex == (int)Model.Gpt4) {
const string m = "GPT-4 is currently in a limited beta and only accessible to those who have " +
"been granted access.";
var helpBoxHeight = EditorStyles.helpBox.CalcHeight(new GUIContent(m),
width - EditorGUIUtility.labelWidth - 24);
var helpBoxRect = new Rect(position.x + EditorGUIUtility.labelWidth, position.y + _height,
width - EditorGUIUtility.labelWidth, helpBoxHeight);
EditorGUI.HelpBox(helpBoxRect, m, MessageType.Warning);
_height += helpBoxRect.height + EditorGUIUtility.standardVerticalSpacing;
var buttonRect = new Rect(position.x + EditorGUIUtility.labelWidth, position.y + _height,
width - EditorGUIUtility.labelWidth, EditorGUIUtility.singleLineHeight);
if (GUI.Button(buttonRect, "Request access", EditorStyles.miniButton)) {
Application.OpenURL("https://openai.com/waitlist/gpt-4-api");
}
_height += buttonRect.height + EditorGUIUtility.standardVerticalSpacing;
}
}
// Temperature
{
var rect = new Rect(position.x, position.y + _height, width, EditorGUIUtility.singleLineHeight);
var temperatureLabel = new GUIContent("Temperature",
"Controls the randomness of the model. Lower values make the " +
"model more predictable; higher values make the model more " +
"surprising.");
EditorGUI.Slider(rect, property.FindPropertyRelative("temperature"), 0f, 1f, temperatureLabel);
_height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
}
// Role
{
var roleProperty = property.FindPropertyRelative(nameof(Parameters.role));
var useRole = !string.IsNullOrEmpty(roleProperty.stringValue);
var toggleRect = new Rect(position.x, position.y + _height, EditorGUIUtility.labelWidth,
EditorGUIUtility.singleLineHeight);
EditorGUI.BeginChangeCheck();
var roleLabel = new GUIContent(" Role", "The character or entity the AI will assume the role of.");
useRole = EditorGUI.ToggleLeft(toggleRect, roleLabel, useRole);
if (EditorGUI.EndChangeCheck()) {
if (useRole) {
roleProperty.stringValue = "Character Description";
}
if (!useRole) {
roleProperty.stringValue = null;
}
}
var rect = new Rect(position.x + EditorGUIUtility.labelWidth - 15, position.y + _height,
width - EditorGUIUtility.labelWidth + 15, EditorGUIUtility.singleLineHeight);
EditorGUI.BeginDisabledGroup(!useRole);
if (useRole) {
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(rect, roleProperty, GUIContent.none);
if (EditorGUI.EndChangeCheck()) {
if (roleProperty.stringValue == string.Empty) {
roleProperty.stringValue = " ";
}
}
} else {
EditorGUI.LabelField(rect, "Character Description", EditorStyles.textField);
}
EditorGUI.EndDisabledGroup();
_height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
}
// Timeout
{
var timeoutProperty = property.FindPropertyRelative(nameof(Parameters.timeout));
var useTimeout = timeoutProperty.intValue > 0;
var toggleRect = new Rect(position.x, position.y + _height, EditorGUIUtility.labelWidth,
EditorGUIUtility.singleLineHeight);
EditorGUI.BeginChangeCheck();
var timeoutLabel = new GUIContent(" Timeout", "The maximum number of seconds to wait for a response.");
useTimeout = EditorGUI.ToggleLeft(toggleRect, timeoutLabel, useTimeout);
if (EditorGUI.EndChangeCheck()) {
if (useTimeout && timeoutProperty.intValue == 0) {
timeoutProperty.intValue = 60;
}
if (!useTimeout && timeoutProperty.intValue > 0) {
timeoutProperty.intValue = 0;
}
}
var rect = new Rect(position.x + EditorGUIUtility.labelWidth - 15, position.y + _height,
width - EditorGUIUtility.labelWidth + 15, EditorGUIUtility.singleLineHeight);
EditorGUI.BeginDisabledGroup(!useTimeout);
if (useTimeout) {
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(rect, timeoutProperty, GUIContent.none);
if (EditorGUI.EndChangeCheck()) {
if (timeoutProperty.intValue < 1) {
timeoutProperty.intValue = 1;
}
}
} else {
EditorGUI.LabelField(rect, "Disabled", EditorStyles.textField);
}
EditorGUI.EndDisabledGroup();
_height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
}
// Throttle
{
var throttleProperty = property.FindPropertyRelative(nameof(Parameters.throttle));
var useThrottle = throttleProperty.intValue > 0;
var toggleRect = new Rect(position.x, position.y + _height, EditorGUIUtility.labelWidth,
EditorGUIUtility.singleLineHeight);
EditorGUI.BeginChangeCheck();
var throttleLabel = new GUIContent(" Throttle", "The maximum number of concurrent requests.");
useThrottle = EditorGUI.ToggleLeft(toggleRect, throttleLabel, useThrottle);
if (EditorGUI.EndChangeCheck()) {
if (useThrottle && throttleProperty.intValue == 0) {
throttleProperty.intValue = 10;
}
if (!useThrottle && throttleProperty.intValue > 0) {
throttleProperty.intValue = 0;
}
}
var rect = new Rect(position.x + EditorGUIUtility.labelWidth - 15, position.y + _height,
width - EditorGUIUtility.labelWidth + 15, EditorGUIUtility.singleLineHeight);
EditorGUI.BeginDisabledGroup(!useThrottle);
if (useThrottle) {
EditorGUI.BeginChangeCheck();
EditorGUI.PropertyField(rect, throttleProperty, GUIContent.none);
if (EditorGUI.EndChangeCheck()) {
if (throttleProperty.intValue < 1) {
throttleProperty.intValue = 1;
}
}
} else {
EditorGUI.LabelField(rect, "Disabled", EditorStyles.textField);
}
EditorGUI.EndDisabledGroup();
_height += EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing;
}
// Draw box around parameters
{
var indentWidth = EditorGUI.indentLevel * 15 - 5;
var rect = new Rect(position.x + indentWidth, position.y + EditorGUIUtility.singleLineHeight,
position.width - indentWidth,
_height - EditorGUIUtility.singleLineHeight +
EditorGUIUtility.standardVerticalSpacing);
GUI.backgroundColor = Color.gray;
EditorGUI.HelpBox(rect, null, MessageType.None);
}
// Space
{
var rect = new Rect(position.x, position.y + _height, width, EditorGUIUtility.standardVerticalSpacing);
EditorGUI.LabelField(rect, GUIContent.none);
_height += rect.height + EditorGUIUtility.standardVerticalSpacing * 2;
}
EditorGUI.indentLevel--;
}
EditorGUI.EndProperty();
property.serializedObject.ApplyModifiedProperties();
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
return _height;
}
}
}
#endif