Skip to content

Commit

Permalink
gai: Load script extensions from url param.
Browse files Browse the repository at this point in the history
  • Loading branch information
patniemeyer committed Dec 19, 2024
1 parent 3f03311 commit cf051b7
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 35 deletions.
48 changes: 31 additions & 17 deletions gai-frontend/lib/chat/chat.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:orchid/api/orchid_eth/chains.dart';
import 'package:orchid/api/orchid_eth/orchid_account.dart';
import 'package:orchid/api/orchid_eth/orchid_account_detail.dart';
import 'package:orchid/api/orchid_keys.dart';
import 'package:orchid/api/orchid_user_config/orchid_user_param.dart';
import 'package:orchid/chat/api/user_preferences_chat.dart';
import 'package:orchid/chat/model.dart';
import 'package:orchid/chat/provider_connection.dart';
Expand Down Expand Up @@ -67,6 +68,7 @@ class _ChatViewState extends State<ChatView> {
// AuthTokenMethod _authTokenMethod = AuthTokenMethod.manual;
String? _authToken;
String? _inferenceUrl;
String? _scriptURLParam;

@override
void initState() {
Expand All @@ -89,20 +91,39 @@ class _ChatViewState extends State<ChatView> {
log('Error initializing from params: $e, $stack');
}

// Initialize the scripting extension mechanism
_initScripting();
}

void _initScripting() {
// final script = UserPreferencesScripts().userScript.get();
// log('User script on start: $script');

// Initialize the scripting extension mechanism
ChatScripting.init(
// If a script URL is provided, it will be loaded.
// url: 'lib/extensions/filter_example.js',
url: _scriptURLParam,
// If debugMode is true, the script will be re-loaded before each invocation
// debugMode: true,
script: UserPreferencesScripts().userScript.get(),
// Not: script overrides url

// Allow a provided script url param to override the stored script
script: _scriptURLParam == null
? UserPreferencesScripts().userScript.get()
: null,

providerManager: _providerManager,
modelManager: _modelManager,
getUserSelectedModels: () => _userSelectedModels,
chatHistory: _chatHistory,
addChatMessageToUI: _addChatMessage,
onScriptError: (error) {
_addMessage(ChatMessageSource.internal,
'User Script error: ${error.truncate(128)}');
_addMessage(ChatMessageSource.system, 'User Script error (see logs).');
},
onScriptLoaded: (msg) {
_addMessage(ChatMessageSource.system, msg);
},
);
}

Expand Down Expand Up @@ -164,23 +185,17 @@ class _ChatViewState extends State<ChatView> {

// Init from user parameters (for web)
void _initFromParams() {
Map<String, String> params = Uri.base.queryParameters;
try {
_funder = EthereumAddress.from(params['funder'] ?? '');
} catch (e) {
_funder = null;
}
try {
_signerKey = BigInt.parse(params['signer'] ?? '');
} catch (e) {
_signerKey = null;
}
final params = OrchidUserParams();
_funder = params.getEthereumAddress('funder');
_signerKey = params.getBigInt('signer');
_accountChanged();

String? provider = params['provider'];
String? provider = params.get('provider');
if (provider != null) {
_providerManager.setUserProvider(provider);
}

_scriptURLParam = params.getURL('script');
}

void providerConnected([name = '']) {
Expand Down Expand Up @@ -342,8 +357,7 @@ class _ChatViewState extends State<ChatView> {
// FocusManager.instance.primaryFocus?.unfocus(); // ?

// If we have a script selected allow it to handle the prompt
if (ChatScripting.enabled &&
(UserPreferencesScripts().userScriptEnabled.get() ?? false)) {
if (ChatScripting.enabled) {
ChatScripting.instance.sendUserPrompt(msg, _userSelectedModels);
} else {
_sendUserPromptDefaultBehavior(msg);
Expand Down
74 changes: 56 additions & 18 deletions gai-frontend/lib/chat/scripting/chat_scripting.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:orchid/chat/api/user_preferences_chat.dart';
import 'package:orchid/chat/chat_history.dart';
import 'package:orchid/chat/chat_message.dart';
import 'package:orchid/chat/model.dart';
Expand All @@ -22,15 +23,16 @@ class ChatScripting {
return _instance!;
}

static bool get enabled => _instance != null;

// Scripting State
String? url;
String? script;
late ProviderManager providerManager;
late ModelManager modelManager;
late List<ModelInfo> Function() getUserSelectedModels;
late ChatHistory chatHistory;
late void Function(ChatMessage) addChatMessageToUI;
late void Function(String) onScriptError;
late void Function(String) onScriptLoaded;
late bool debugMode;

static Future<void> init({
Expand All @@ -47,6 +49,8 @@ class ChatScripting {
required ModelManager modelManager,
required List<ModelInfo> Function() getUserSelectedModels,
required Function(ChatMessage) addChatMessageToUI,
required void Function(String) onScriptError,
required void Function(String) onScriptLoaded,
}) async {
if (_instance != null) {
throw Exception("ChatScripting already initialized!");
Expand All @@ -59,27 +63,55 @@ class ChatScripting {
instance.modelManager = modelManager;
instance.getUserSelectedModels = getUserSelectedModels;
instance.addChatMessageToUI = addChatMessageToUI;
instance.onScriptError = onScriptError;
instance.onScriptLoaded = onScriptLoaded;

if (url != null) {
// Install persistent callback functions
final script = await instance.loadScriptFromURL(url);
instance.setScript(script);
instance.setURL(url);
}

if (script != null) {
instance.setScript(script);
}
}

static bool get enabled {
return ChatScripting._instance != null
&& (instance.url != null // assume enabled when URL provided as param
|| (userPrefEnabled && instance.script != null));
}

static bool get userPrefEnabled {
return (UserPreferencesScripts().userScriptEnabled.get() ?? false);
}

Future<void> setURL(String newURL) async {
url = newURL;
final String script;
try {
script = await instance.loadScriptFromURL(url!);
onScriptLoaded("Script loaded from URL: $url");
setScript(script);
} catch (e) {
log("Failed to load script from URL: $e");
onScriptError(e.toString());
}
}

// Set the script into the environment
void setScript(String newScript) {
// init the global bindings once, when we have a script
if (script == null) {
addGlobalBindings();
try {
// init the global bindings once, when we have a script
if (script == null) {
addGlobalBindings();
}
script = newScript;
// Do one setup and evaluation of the script now
evalExtensionScript();
} catch (e, stack) {
log("Failed to eval script: $e");
log(stack.toString());
onScriptError(e.toString());
}
script = newScript;
// Do one setup and evaluation of the script now
evalExtensionScript();
}

Future<String> loadScriptFromURL(String url) async {
Expand All @@ -95,7 +127,8 @@ class ChatScripting {
// If the result is HTML we have failed
if (response.headers['content-type']!.contains('text/html')) {
throw Exception(
"Failed to load script from $url: HTML response: ${response.body.truncate(64)}");
"Failed to load script from $url: HTML response: ${response.body
.truncate(64)}");
}

// log("Loaded script: $script");
Expand All @@ -110,6 +143,7 @@ class ChatScripting {
throw Exception("No script to evaluate.");
}
evaluateJS(script!); // We could get a result back async here if needed
onScriptLoaded("Script installed.");
} catch (e, stack) {
log("Failed to evaluate script: $e");
log(stack.toString());
Expand All @@ -130,6 +164,10 @@ class ChatScripting {

// If debug mode evaluate the script before each usage
if (debugMode) {
// reload the URL in debug mode
if (url != null) {
setURL(url!); // reload the script from the URL
}
evalExtensionScript();
}

Expand Down Expand Up @@ -157,8 +195,8 @@ class ChatScripting {

// Implementation of sendMessagesToModel callback function invoked from JS
// Send a list of ChatMessage to a model for inference and return a promise of ChatMessageJS
JSPromise sendMessagesToModelJSImpl(
JSArray messagesJS, String modelId, int? maxTokens) {
JSPromise sendMessagesToModelJSImpl(JSArray messagesJS, String modelId,
int? maxTokens) {
log("dart: Send messages to model called.");

return (() async {
Expand Down Expand Up @@ -197,7 +235,7 @@ class ChatScripting {
.toJS;
}

///
/// END: JS callback implementations
///
///
/// END: JS callback implementations
///
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class ScriptsMenuItem extends StatelessWidget {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [

// show one line of the script
if (hasScript)
Container(
Expand Down
21 changes: 21 additions & 0 deletions gui-orchid/lib/api/orchid_user_config/orchid_user_param.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'dart:ui';
import 'package:orchid/api/orchid_crypto.dart';
import 'package:orchid/api/orchid_log.dart';
import 'package:orchid/util/hex.dart';

Expand Down Expand Up @@ -43,6 +44,26 @@ class OrchidUserParams {
}
}

String? getURL(String name) {
return get(name);
}

EthereumAddress? getEthereumAddress(String name) {
try {
return EthereumAddress.from(params[name] ?? '');
} catch (e) {
return null;
}
}

BigInt? getBigInt(String name) {
try {
return BigInt.parse(params[name] ?? '');
} catch (e) {
return null;
}
}

bool has(String name) {
return get(name) != null;
}
Expand Down

0 comments on commit cf051b7

Please sign in to comment.