From e30f64b11d09c9ea4f32071b3c12cdf22f8d1c59 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Thu, 16 Nov 2023 10:01:40 -0500 Subject: [PATCH 01/46] WIP chat preferences tool which allows the LLM to get/set certain chat settings --- Source/Chatbook/Tools.wl | 213 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) diff --git a/Source/Chatbook/Tools.wl b/Source/Chatbook/Tools.wl index 06336349..0b2d0b38 100644 --- a/Source/Chatbook/Tools.wl +++ b/Source/Chatbook/Tools.wl @@ -36,6 +36,8 @@ Needs[ "Wolfram`Chatbook`" ]; Needs[ "Wolfram`Chatbook`ChatMessages`" ]; Needs[ "Wolfram`Chatbook`Common`" ]; Needs[ "Wolfram`Chatbook`Formatting`" ]; +Needs[ "Wolfram`Chatbook`Models`" ]; +Needs[ "Wolfram`Chatbook`Personas`" ]; Needs[ "Wolfram`Chatbook`Prompting`" ]; Needs[ "Wolfram`Chatbook`ResourceInstaller`" ]; Needs[ "Wolfram`Chatbook`Sandbox`" ]; @@ -64,6 +66,7 @@ $AvailableTools := Association[ $DefaultTools, $InstalledTools ]; (* ::Section::Closed:: *) (*Exported Functions for Tool Repository*) $ToolFunctions = <| + "ChatPreferences" -> chatPreferences, "DocumentationLookup" -> documentationLookup, "DocumentationSearcher" -> documentationSearch, "WebFetcher" -> webFetch, @@ -814,6 +817,216 @@ formatToolCallExample[ name_String, params_Association ] := formatToolCallExample // endDefinition; +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*ChatPreferences*) +$chatPreferencesDescription = "\ +Get and set current chat preferences. + +Getting chat preferences +* When getting, the `key` parameter is optional and the `value` parameter is unused. +* If `key` is omitted, all available chat preferences will be returned. +* When getting chat preferences, you'll receive the current values along with additional information about possible values. + +Setting chat preferences +* When setting, both the `key` parameter and the `value` parameter are required. + +Key descriptions +| key | type | default | description | +| ------------------- | ---------- | --------------- | ----------- | +| Assistance | boolean | false | Whether to enable automatic AI assistance for normal input cell evaluations. | +| ChatHistoryLength | integer | 100 | The maximum number of cells to include in chat history. | +| LLMEvaluator | string | 'CodeAssistant' | The name of the LLM evaluator (aka persona) to use for chat. Use the 'get' action for available names. | +| MaxCellStringLength | integer | 0 | The maximum number of characters to include in a cell's string representation. A 0 means determine automatically. | +| MergeMessages | boolean | true | Whether to merge consecutive messages from the same user into a single message. | +| Model | string | 'gpt-4' | The name of the model to use for AI assistance. | +| Prompts | [ string ] | [ ] | A list of instructions to append to the system prompt. | +| Temperature | real | 0.7 | The temperature to use for the LLM. Values should be a real between 0 and 2. | +| ToolCallFrequency | real | 0.5 | The frequency with which to use tools. Values should be a number between 0 and 1. | +| Tools | [ string ] | | The list of currently enabled tools. Use the 'get' action for available names. | +"; + +$defaultChatTools0[ "ChatPreferences" ] = <| + toolDefaultData[ "ChatPreferences" ], + "Icon" -> RawBoxes @ TemplateBox[ { }, "ChatBlockSettingsMenuIcon" ], + "Description" -> $chatPreferencesDescription, + "Function" -> chatPreferences, + "FormattingFunction" -> toolAutoFormatter, + "Source" -> "BuiltIn", + "Parameters" -> { + "action" -> <| + "Interpreter" -> { "get", "set" }, + "Help" -> "Whether to get or set chat settings", + "Required" -> True + |>, + "key" -> <| + "Interpreter" -> "String", + "Help" -> "Which chat setting to get or set", + "Required" -> False + |>, + "value" -> <| + "Interpreter" -> "String", + "Help" -> "The value to set the chat setting to", + "Required" -> False + |>, + "scope" -> <| + "Interpreter" -> { "global", "notebook" }, + "Help" -> "The scope of the chat setting (default is 'notebook')", + "Required" -> False + |> + } +|>; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*chatPreferences*) +chatPreferences // beginDefinition; +chatPreferences[ args_Association ] := chatPreferences[ args[ "action" ], args ]; +chatPreferences[ "get", args_Association ] := getChatPreferences @ args; +chatPreferences[ "set", args_Association ] := setChatPreferences @ args; +chatPreferences // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*setChatPreferences*) +setChatPreferences // beginDefinition; +setChatPreferences[ as_Association ] := setChatPreferences[ as[ "scope" ], as[ "key" ], as[ "value" ] ]; +setChatPreferences[ scope_, key_, value_ ] := setChatPreferences0[ toScope @ scope, key, interpretValue[ key, value ] ]; +setChatPreferences // endDefinition; + +setChatPreferences0 // beginDefinition; +setChatPreferences0[ failure_Failure, key_, value_ ] := failure; +setChatPreferences0[ scope_, key_, failure_Failure ] := failure; +setChatPreferences0[ scope_, key_, value_ ] := setChatPreferences1[ scope, key, value ]; +setChatPreferences0 // endDefinition; + +setChatPreferences1 // beginDefinition; +setChatPreferences1[ scope: $FrontEnd|_NotebookObject, key_String, value: Except[ _Failure ] ] := ( + CurrentChatSettings[ scope, key ] = value +); +setChatPreferences1 // endDefinition; + +(* +setChatPreferences0[ scope_, "Assistance" , value_ ] := setPrefChatAssistance[ scope, value ]; +setChatPreferences0[ scope_, "ChatHistoryLength" , value_ ] := setPrefChatHistoryLength[ scope, value ]; +setChatPreferences0[ scope_, "LLMEvaluator" , value_ ] := setPrefLLMEvaluator[ scope, value ]; +setChatPreferences0[ scope_, "MaxCellStringLength", value_ ] := setPrefMaxCellStringLength[ scope, value ]; +setChatPreferences0[ scope_, "MergeMessages" , value_ ] := setPrefMergeMessages[ scope, value ]; +setChatPreferences0[ scope_, "Model" , value_ ] := setPrefModel[ scope, value ]; +setChatPreferences0[ scope_, "Prompts" , value_ ] := setPrefPrompts[ scope, value ]; +setChatPreferences0[ scope_, "Temperature" , value_ ] := setPrefTemperature[ scope, value ]; +setChatPreferences0[ scope_, "ToolCallFrequency" , value_ ] := setPrefToolCallFrequency[ scope, value ]; +setChatPreferences0[ scope_, "Tools" , value_ ] := setPrefTools[ scope, value ]; +*) + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*interpretValue*) +interpretValue // beginDefinition; +interpretValue[ key_String, value_String ] := $interpreters[ key ][ value ]; +interpretValue // endDefinition; + +$interpreters = Interpreter /@ <| + "Assistance" -> "Boolean", + "ChatHistoryLength" -> "Integer", + "LLMEvaluator" -> interpretLLMEvaluator, + "MaxCellStringLength" -> "Integer", + "MergeMessages" -> "Boolean", + "Model" -> interpretModel, + "Prompts" -> RepeatingElement[ "String" ], + "Temperature" -> Restricted[ "Number", { 0, 2 } ], + "ToolCallFrequency" -> Restricted[ "Number", { 0, 1 } ], + "Tools" -> interpretTools +|>; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsubsection::Closed:: *) +(*interpretLLMEvaluator*) +interpretLLMEvaluator // beginDefinition; + +interpretLLMEvaluator[ name_String ] := + With[ { personas = Keys @ GetCachedPersonaData[ ] }, + If[ MemberQ[ personas, name ], + name, + Failure[ + "InvalidLLMEvaluator", + <| + "MessageTemplate" -> "`1` is not among the list of available personas: `2`", + "MessageParameters" -> personas + |> + ] + ] + ]; + +interpretLLMEvaluator // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsubsection::Closed:: *) +(*interpretModel*) +interpretModel // beginDefinition; + +interpretModel[ name_String ] := + With[ { models = Select[ getModelList[ ], chatModelQ ] }, + If[ MemberQ[ models, ToLowerCase @ name ], + ToLowerCase @ name, + Failure[ + "InvalidModel", + <| + "MessageTemplate" -> "`1` is not among the list of available models: `2`", + "MessageParameters" -> models + |> + ] + ] + ]; + +interpretModel // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsubsection::Closed:: *) +(*interpretTools*) +interpretTools // beginDefinition; + +interpretTools[ name_String ] := + With[ { tools = Keys @ $AvailableTools }, + If[ MemberQ[ tools, name ], + name, + Failure[ + "InvalidTool", + <| + "MessageTemplate" -> "`1` is not among the list of available tools: `2`", + "MessageParameters" -> tools + |> + ] + ] + ]; + +interpretTools // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*toScope*) +toScope // beginDefinition; +toScope[ "global" ] := $FrontEnd; +toScope[ "notebook" ] := EvaluationNotebook[ ]; +toScope[ _Missing ] := toScope[ "notebook" ]; +toScope[ failure_Failure ] := failure; +toScope // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*setPrefChatAssistance*) +setPrefChatAssistance // beginDefinition; +setPrefChatAssistance[ scope: $$scope, value: True|False ] := CurrentChatSettings[ scope, "Assistance" ] = value; +setPrefChatAssistance[ scope_, failure_Failure ] := failure; +setPrefChatAssistance // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*setPrefModel*) +setPrefModel // beginDefinition; +setPrefModel[ scope: $$scope, value_String ] := CurrentChatSettings[ scope, "Model" ] = value; +setPrefModel // endDefinition; + (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*DocumentationSearch*) From 2713de3a7dde930a80a67bc48d542d833a4e513d Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Wed, 29 Nov 2023 19:22:48 -0500 Subject: [PATCH 02/46] WIP reorganizing tools as separate files --- Source/Chatbook/Tools/ChatPreferences.wl | 248 +++++ Source/Chatbook/Tools/Common.wl | 731 +++++++++++++ .../{Tools.wl => Tools/DefaultTools.wl} | 967 ------------------ Source/Chatbook/Tools/ToolOptions.wl | 100 ++ Source/Chatbook/Tools/Tools.wl | 48 + 5 files changed, 1127 insertions(+), 967 deletions(-) create mode 100644 Source/Chatbook/Tools/ChatPreferences.wl create mode 100644 Source/Chatbook/Tools/Common.wl rename Source/Chatbook/{Tools.wl => Tools/DefaultTools.wl} (54%) create mode 100644 Source/Chatbook/Tools/ToolOptions.wl create mode 100644 Source/Chatbook/Tools/Tools.wl diff --git a/Source/Chatbook/Tools/ChatPreferences.wl b/Source/Chatbook/Tools/ChatPreferences.wl new file mode 100644 index 00000000..a837b038 --- /dev/null +++ b/Source/Chatbook/Tools/ChatPreferences.wl @@ -0,0 +1,248 @@ +(* ::Section::Closed:: *) +(*Package Header*) +BeginPackage[ "Wolfram`Chatbook`Tools`" ]; + +(* :!CodeAnalysis::BeginBlock:: *) + +Begin[ "`Private`" ]; + +Needs[ "Wolfram`Chatbook`" ]; +Needs[ "Wolfram`Chatbook`ChatMessages`" ]; +Needs[ "Wolfram`Chatbook`Common`" ]; +Needs[ "Wolfram`Chatbook`Formatting`" ]; +Needs[ "Wolfram`Chatbook`Models`" ]; +Needs[ "Wolfram`Chatbook`Personas`" ]; +Needs[ "Wolfram`Chatbook`Prompting`" ]; +Needs[ "Wolfram`Chatbook`ResourceInstaller`" ]; +Needs[ "Wolfram`Chatbook`Sandbox`" ]; +Needs[ "Wolfram`Chatbook`Serialization`" ]; +Needs[ "Wolfram`Chatbook`Utils`" ]; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Config*) +$usableChatSettingsKeys = { + "Assistance", + "ChatHistoryLength", + "LLMEvaluator", + "MaxCellStringLength", + "MergeMessages", + "Model", + "Prompts", + "Temperature", + "ToolCallFrequency", + "Tools" +}; + +$$scope = _NotebookObject | $FrontEnd; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Chat Preferences*) + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*Tool Description*) +$chatPreferencesDescription = "\ +Get and set current chat preferences. + +Getting chat preferences +* When getting, the `key` parameter is optional and the `value` parameter is unused. +* If `key` is omitted, all available chat preferences will be returned. +* When getting chat preferences, you'll receive the current values along with additional information about possible values. + +Setting chat preferences +* When setting, both the `key` parameter and the `value` parameter are required. + +Key descriptions +| key | type | default | description | +| ------------------- | ---------- | --------------- | ----------- | +| Assistance | boolean | false | Whether to enable automatic AI assistance for normal input cell evaluations. | +| ChatHistoryLength | integer | 100 | The maximum number of cells to include in chat history. | +| LLMEvaluator | string | 'CodeAssistant' | The name of the LLM evaluator (aka persona) to use for chat. Use the 'get' action for available names. | +| MaxCellStringLength | integer | 0 | The maximum number of characters to include in a cell's string representation. A 0 means determine automatically. | +| MergeMessages | boolean | true | Whether to merge consecutive messages from the same user into a single message. | +| Model | string | 'gpt-4' | The name of the model to use for AI assistance. | +| Prompts | [ string ] | [ ] | A list of instructions to append to the system prompt. | +| Temperature | real | 0.7 | The temperature to use for the LLM. Values should be a real between 0 and 2. | +| ToolCallFrequency | real | 0.5 | The frequency with which to use tools. Values should be a number between 0 and 1. | +| Tools | [ string ] | | The list of currently enabled tools. Use the 'get' action for available names. | +"; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*Definitions*) + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*chatPreferences*) +chatPreferences // beginDefinition; +chatPreferences[ args_Association ] := chatPreferences[ args[ "action" ], args ]; +chatPreferences[ "get", args_Association ] := getChatPreferences @ args; +chatPreferences[ "set", args_Association ] := setChatPreferences @ args; +chatPreferences // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*getChatPreferences*) +getChatPreferences // beginDefinition; + +getChatPreferences[ as_Association ] := + getChatPreferences[ toScope @ as[ "scope" ], as[ "key" ] ]; + +getChatPreferences[ scope: $$scope, key_String ] := + CurrentChatSettings[ scope, key ]; + +(* TODO: format as JSON *) +getChatPreferences[ scope: $$scope, _Missing ] := + KeyTake[ CurrentChatSettings @ scope, $usableChatSettingsKeys ]; + +getChatPreferences[ args___ ] := + Failure[ + "NotImplemented", + <| "MessageTemplate" -> "Method is not implemented yet.", "MessageParameters" -> { args } |> + ]; + +getChatPreferences // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*setChatPreferences*) +setChatPreferences // beginDefinition; +setChatPreferences[ as_Association ] := setChatPreferences[ as[ "scope" ], as[ "key" ], as[ "value" ] ]; +setChatPreferences[ scope_, key_, value_ ] := setChatPreferences0[ toScope @ scope, key, interpretValue[ key, value ] ]; +setChatPreferences // endDefinition; + +setChatPreferences0 // beginDefinition; +setChatPreferences0[ failure_Failure, key_, value_ ] := failure; +setChatPreferences0[ scope_, key_, failure_Failure ] := failure; +setChatPreferences0[ scope_, key_, value_ ] := setChatPreferences1[ scope, key, value ]; +setChatPreferences0 // endDefinition; + +setChatPreferences1 // beginDefinition; +setChatPreferences1[ scope: $FrontEnd|_NotebookObject, key_String, value: Except[ _Failure ] ] := ( + CurrentChatSettings[ scope, key ] = value +); +setChatPreferences1 // endDefinition; + +(* TODO: return something indicating success instead of the value *) + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*interpretValue*) +interpretValue // beginDefinition; +interpretValue[ key_String, value_String ] := $interpreters[ key ][ value ]; +interpretValue // endDefinition; + +$interpreters = Interpreter /@ <| + "Assistance" -> "Boolean", + "ChatHistoryLength" -> "Integer", + "LLMEvaluator" -> interpretLLMEvaluator, + "MaxCellStringLength" -> "Integer", + "MergeMessages" -> "Boolean", + "Model" -> interpretModel, + "Prompts" -> RepeatingElement[ "String" ], + "Temperature" -> Restricted[ "Number", { 0, 2 } ], + "ToolCallFrequency" -> Restricted[ "Number", { 0, 1 } ], + "Tools" -> interpretTools +|>; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsubsection::Closed:: *) +(*interpretLLMEvaluator*) +interpretLLMEvaluator // beginDefinition; + +interpretLLMEvaluator[ name_String ] := + With[ { personas = Keys @ GetCachedPersonaData[ ] }, + If[ MemberQ[ personas, name ], + name, + Failure[ + "InvalidLLMEvaluator", + <| + "MessageTemplate" -> "`1` is not among the list of available personas: `2`", + "MessageParameters" -> personas + |> + ] + ] + ]; + +interpretLLMEvaluator // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsubsection::Closed:: *) +(*interpretModel*) +interpretModel // beginDefinition; + +interpretModel[ name_String ] := + With[ { models = Select[ getModelList[ ], chatModelQ ] }, + If[ MemberQ[ models, ToLowerCase @ name ], + ToLowerCase @ name, + Failure[ + "InvalidModel", + <| + "MessageTemplate" -> "`1` is not among the list of available models: `2`", + "MessageParameters" -> models + |> + ] + ] + ]; + +interpretModel // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsubsection::Closed:: *) +(*interpretTools*) +interpretTools // beginDefinition; + +interpretTools[ name_String ] := + With[ { tools = Keys @ $AvailableTools }, + If[ MemberQ[ tools, name ], + name, + Failure[ + "InvalidTool", + <| + "MessageTemplate" -> "`1` is not among the list of available tools: `2`", + "MessageParameters" -> tools + |> + ] + ] + ]; + +interpretTools // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*toScope*) +toScope // beginDefinition; +toScope[ "global" ] := $FrontEnd; +toScope[ "notebook" ] := EvaluationNotebook[ ]; +toScope[ _Missing ] := toScope[ "notebook" ]; +toScope[ failure_Failure ] := failure; +toScope // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*setPrefChatAssistance*) +setPrefChatAssistance // beginDefinition; +setPrefChatAssistance[ scope: $$scope, value: True|False ] := CurrentChatSettings[ scope, "Assistance" ] = value; +setPrefChatAssistance[ scope_, failure_Failure ] := failure; +setPrefChatAssistance // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*setPrefModel*) +setPrefModel // beginDefinition; +setPrefModel[ scope: $$scope, value_String ] := CurrentChatSettings[ scope, "Model" ] = value; +setPrefModel // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Package Footer*) +If[ Wolfram`ChatbookInternal`$BuildingMX, + Null; +]; + +(* :!CodeAnalysis::EndBlock:: *) + +End[ ]; +EndPackage[ ]; diff --git a/Source/Chatbook/Tools/Common.wl b/Source/Chatbook/Tools/Common.wl new file mode 100644 index 00000000..b80b4a87 --- /dev/null +++ b/Source/Chatbook/Tools/Common.wl @@ -0,0 +1,731 @@ +(* ::Section::Closed:: *) +(*Package Header*) +BeginPackage[ "Wolfram`Chatbook`Tools`" ]; + +(* :!CodeAnalysis::BeginBlock:: *) + +Begin[ "`Private`" ]; + +Needs[ "Wolfram`Chatbook`" ]; +Needs[ "Wolfram`Chatbook`ChatMessages`" ]; +Needs[ "Wolfram`Chatbook`Common`" ]; +Needs[ "Wolfram`Chatbook`Formatting`" ]; +Needs[ "Wolfram`Chatbook`Models`" ]; +Needs[ "Wolfram`Chatbook`Personas`" ]; +Needs[ "Wolfram`Chatbook`Prompting`" ]; +Needs[ "Wolfram`Chatbook`ResourceInstaller`" ]; +Needs[ "Wolfram`Chatbook`Sandbox`" ]; +Needs[ "Wolfram`Chatbook`Serialization`" ]; +Needs[ "Wolfram`Chatbook`Utils`" ]; + +HoldComplete[ + System`LLMTool; + System`LLMConfiguration; +]; + +(* TODO: + ImageSynthesize + LongTermMemory + Definitions + TestWriter +*) +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Tool Lists*) +$DefaultTools := $defaultChatTools; +$InstalledTools := $installedTools; +$AvailableTools := Association[ $DefaultTools, $InstalledTools ]; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Exported Functions for Tool Repository*) +$ToolFunctions = <| + "ChatPreferences" -> chatPreferences, + "DocumentationLookup" -> documentationLookup, + "DocumentationSearcher" -> documentationSearch, + "WebFetcher" -> webFetch, + "WebImageSearcher" -> webImageSearch, + "WebSearcher" -> webSearch, + "WolframAlpha" -> getWolframAlphaText, + "WolframLanguageEvaluator" -> wolframLanguageEvaluator +|>; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Tool Configuration*) +$defaultWebTextLength = 12000; +$toolResultStringLength := Ceiling[ $initialCellStringBudget/2 ]; +$webSessionVisible = False; + +$DefaultToolOptions = <| + "WolframLanguageEvaluator" -> <| + "AllowedExecutePaths" -> Automatic, + "AllowedReadPaths" -> All, + "AllowedWritePaths" -> Automatic, + "EvaluationTimeConstraint" -> 60, + "PingTimeConstraint" -> 30 + |>, + "WebFetcher" -> <| + "MaxContentLength" -> $defaultWebTextLength + |>, + "WebSearcher" -> <| + "AllowAdultContent" -> Inherited, + "Language" -> Inherited, + "MaxItems" -> 5, + "Method" -> "Google" + |>, + "WebImageSearcher" -> <| + "AllowAdultContent" -> Inherited, + "Language" -> Inherited, + "MaxItems" -> 5, + "Method" -> "Google" + |> +|>; + +$defaultToolIcon = RawBoxes @ TemplateBox[ { }, "WrenchIcon" ]; + +$attachments = <| |>; +$selectedTools = <| |>; +$toolBox = <| |>; +$toolEvaluationResults = <| |>; +$toolOptions = <| |>; + +$cloudUnsupportedTools = { "WolframLanguageEvaluator", "DocumentationSearcher" }; + +$defaultToolOrder = { + "DocumentationLookup", + "DocumentationSearcher", + "WolframAlpha", + "WolframLanguageEvaluator" +}; + +$toolNameAliases = <| + "DocumentationSearch" -> "DocumentationSearcher", + "WebFetch" -> "WebFetcher", + "WebImageSearch" -> "WebImageSearcher", + "WebSearch" -> "WebSearcher" +|>; + +$installedToolExtraKeys = { + "Description", + "DocumentationLink", + "Origin", + "ResourceName", + "Templated", + "Version" +}; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Default Tools*) +$defaultChatTools := If[ TrueQ @ $CloudEvaluation, + KeyDrop[ $defaultChatTools0, $cloudUnsupportedTools ], + $defaultChatTools0 + ]; + +$defaultChatTools0 = <| |>; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Installed Tools*) +$installedTools := Association @ Cases[ + GetInstalledResourceData[ "LLMTool" ], + as: KeyValuePattern[ "Tool" -> tool_ ] :> (toolName @ tool -> addExtraToolData[ tool, as ]) +]; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*addExtraToolData*) +addExtraToolData // beginDefinition; + +addExtraToolData[ tool: HoldPattern @ LLMTool[ as_Association, a___ ], extra_Association ] := + With[ { new = Join[ KeyTake[ extra, $installedToolExtraKeys ], as ] }, LLMTool[ new, a ] ]; + +addExtraToolData // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*getToolByName*) +getToolByName // beginDefinition; +getToolByName[ name_String ] := Lookup[ $toolBox, toCanonicalToolName @ name ]; +getToolByName // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*toolName*) +toolName // beginDefinition; +toolName[ tool_ ] := toolName[ tool, Automatic ]; +toolName[ HoldPattern @ LLMTool[ as_Association, ___ ], type_ ] := toolName[ as, type ]; +toolName[ KeyValuePattern[ "CanonicalName" -> name_String ], "Canonical" ] := name; +toolName[ KeyValuePattern[ "DisplayName" -> name_String ], "Display" ] := name; +toolName[ KeyValuePattern[ "Name" -> name_String ], type_ ] := toolName[ name, type ]; +toolName[ tool_, Automatic ] := toolName[ tool, "Canonical" ]; +toolName[ name_String, "Machine" ] := toMachineToolName @ name; +toolName[ name_String, "Canonical" ] := toCanonicalToolName @ name; +toolName[ name_String, "Display" ] := toDisplayToolName @ name; +toolName[ tools_List, type_ ] := toolName[ #, type ] & /@ tools; +toolName // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*toolData*) +toolData // beginDefinition; + +toolData[ HoldPattern @ LLMTool[ as_Association, ___ ] ] := + toolData @ as; + +toolData[ name_String ] /; KeyExistsQ[ $toolBox, name ] := + toolData @ $toolBox[ name ]; + +toolData[ name_String ] /; KeyExistsQ[ $defaultChatTools, name ] := + toolData @ $defaultChatTools[ name ]; + +toolData[ as: KeyValuePattern @ { "Function"|"ToolCall" -> _ } ] := <| + toolDefaultData @ toolName @ as, + "Icon" -> toolDefaultIcon @ as, + as +|>; + +toolData[ tools_List ] := + toolData /@ tools; + +toolData // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*toolDefaultIcon*) +toolDefaultIcon // beginDefinition; + +toolDefaultIcon[ KeyValuePattern[ "Origin" -> "LLMToolRepository" ] ] := + RawBoxes @ TemplateBox[ { }, "ToolManagerRepository" ]; + +toolDefaultIcon[ _Association ] := + $defaultToolIcon; + +toolDefaultIcon // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*toolDefaultData*) +toolDefaultData // beginDefinition; + +toolDefaultData[ name_String ] := <| + "CanonicalName" -> toCanonicalToolName @ name, + "DisplayName" -> toDisplayToolName @ name, + "Name" -> toMachineToolName @ name, + "Icon" -> $defaultToolIcon +|>; + +toolDefaultData // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*toMachineToolName*) +toMachineToolName // beginDefinition; + +toMachineToolName[ s_String ] := + ToLowerCase @ StringReplace[ + StringTrim @ s, + { " " -> "_", a_?LowerCaseQ ~~ b_?UpperCaseQ ~~ c_?LowerCaseQ :> a<>"_"<>b<>c } + ]; + +toMachineToolName // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*toCanonicalToolName*) +toCanonicalToolName // beginDefinition; + +toCanonicalToolName[ s_String ] := + Capitalize @ StringReplace[ StringTrim @ s, a_~~("_"|" ")~~b_ :> a <> ToUpperCase @ b ]; + +toCanonicalToolName // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*toDisplayToolName*) +toDisplayToolName // beginDefinition; + +toDisplayToolName[ s_String ] := + Capitalize[ + StringReplace[ + StringTrim @ s, + { "_" :> " ", a_?LowerCaseQ ~~ b_?UpperCaseQ ~~ c_?LowerCaseQ :> a<>" "<>b<>c } + ], + "TitleCase" + ]; + +toDisplayToolName // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*formatToolCallExample*) +formatToolCallExample // beginDefinition; + +formatToolCallExample[ name_String, params_Association ] := + TemplateApply[ + (* cSpell: ignore TOOLCALL, ENDARGUMENTS, ENDTOOLCALL *) + "TOOLCALL: `1`\n`2`\nENDARGUMENTS\nENDTOOLCALL", + { toMachineToolName @ name, Developer`WriteRawJSONString @ params } + ]; + +formatToolCallExample // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Toolbox*) + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*withToolBox*) +withToolBox // beginDefinition; +withToolBox // Attributes = { HoldFirst }; +withToolBox[ eval_ ] := Block[ { $selectedTools = <| |>, $toolOptions = $DefaultToolOptions }, eval ]; +withToolBox // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*selectTools*) +selectTools // beginDefinition; + +selectTools[ as_Association ] := Enclose[ + Module[ { llmEvaluatorName, toolNames, selections, selectionTypes, add, remove, selectedNames }, + + llmEvaluatorName = ConfirmBy[ getLLMEvaluatorName @ as, StringQ, "LLMEvaluatorName" ]; + toolNames = ConfirmMatch[ getToolNames @ as, { ___String }, "Names" ]; + selections = ConfirmBy[ getToolSelections @ as, AssociationQ, "Selections" ]; + selectionTypes = ConfirmBy[ getToolSelectionTypes @ as, AssociationQ, "SelectionTypes" ]; + + add = ConfirmMatch[ + Union[ + Keys @ Select[ selections, Lookup @ llmEvaluatorName ], + Keys @ Select[ selectionTypes, SameAs @ All ] + ], + { ___String }, + "ToolAdditions" + ]; + + remove = ConfirmMatch[ + Union[ + Keys @ Select[ selections, Not @* Lookup[ llmEvaluatorName ] ], + Keys @ Select[ selectionTypes, SameAs @ None ] + ], + { ___String }, + "ToolRemovals" + ]; + + selectedNames = ConfirmMatch[ + Complement[ Union[ toolNames, add ], remove ], + { ___String }, + "SelectedNames" + ]; + + selectTools0 /@ selectedNames + ], + throwInternalFailure[ selectTools @ as, ## ] & +]; + +selectTools // endDefinition; + + +(* TODO: Most of this functionality is moved to `getToolNames`. This only needs to operate on strings. *) +selectTools0 // beginDefinition; + +selectTools0[ Automatic|Inherited ] := selectTools0 @ $defaultChatTools; +selectTools0[ None ] := $selectedTools = <| |>; +selectTools0[ name_String ] /; KeyExistsQ[ $toolBox, name ] := $selectedTools[ name ] = $toolBox[ name ]; +selectTools0[ name_String ] /; KeyExistsQ[ $toolNameAliases, name ] := selectTools0 @ $toolNameAliases @ name; +selectTools0[ name_String ] := selectTools0[ name, Lookup[ $AvailableTools, name ] ]; +selectTools0[ tools_List ] := selectTools0 /@ tools; +selectTools0[ tools_Association ] := KeyValueMap[ selectTools0, tools ]; + +(* Literal LLMTool specification: *) +selectTools0[ tool: HoldPattern @ LLMTool[ KeyValuePattern[ "Name" -> name_ ], ___ ] ] := selectTools0[ name, tool ]; + +(* Rules can be used to enable/disable by name: *) +selectTools0[ (Rule|RuleDelayed)[ name_String, tool_ ] ] := selectTools0[ name, tool ]; + +(* Inherit from core tools: *) +selectTools0[ name_String, Automatic|Inherited ] := selectTools0[ name, Lookup[ $defaultChatTools, name ] ]; + +(* Disable tool: *) +selectTools0[ name_String, None ] := KeyDropFrom[ $selectedTools, name ]; + +(* Select a literal LLMTool: *) +selectTools0[ name_String, tool: HoldPattern[ _LLMTool ] ] := $selectedTools[ name ] = $toolBox[ name ] = tool; + +(* Tool not found: *) +selectTools0[ name_String, Missing[ "KeyAbsent", name_ ] ] := + If[ TrueQ @ KeyExistsQ[ $defaultChatTools0, name ], + (* A default tool that was filtered for compatibility *) + Null, + (* An unknown tool name *) + messagePrint[ "ToolNotFound", name ] + ]; + +selectTools0 // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*getLLMEvaluatorName*) +getLLMEvaluatorName // beginDefinition; +getLLMEvaluatorName[ KeyValuePattern[ "LLMEvaluatorName" -> name_String ] ] := name; +getLLMEvaluatorName[ KeyValuePattern[ "LLMEvaluator" -> name_String ] ] := name; + +getLLMEvaluatorName[ KeyValuePattern[ "LLMEvaluator" -> evaluator_Association ] ] := + Lookup[ evaluator, "LLMEvaluatorName", Lookup[ evaluator, "Name" ] ]; + +getLLMEvaluatorName // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*getToolNames*) +getToolNames // beginDefinition; + +(* Persona declares tools, so combine with defaults as appropriate *) +getToolNames[ as: KeyValuePattern[ "LLMEvaluator" -> KeyValuePattern[ "Tools" -> tools_ ] ] ] := + getToolNames[ Lookup[ as, "Tools", None ], tools ]; + +(* No tool specification by persona, so get defaults *) +getToolNames[ as_Association ] := + getToolNames @ Lookup[ as, "Tools", Automatic ]; + +(* Persona does not want any tools *) +getToolNames[ tools_, None ] := { }; + +(* Persona wants default tools *) +getToolNames[ tools_, Automatic|Inherited ] := getToolNames @ tools; + +(* Persona declares an explicit list of tools *) +getToolNames[ Automatic|None|Inherited, personaTools_List ] := getToolNames @ personaTools; + +(* The user has specified an explicit list of tools as well, so include them *) +getToolNames[ tools_List, personaTools_List ] := Union[ getToolNames @ tools, getToolNames @ personaTools ]; + +(* Get name of each tool *) +getToolNames[ tools_List ] := DeleteDuplicates @ Flatten[ getCachedToolName /@ tools ]; + +(* Default tools *) +getToolNames[ Automatic|Inherited ] := Keys @ $DefaultTools; + +(* All tools *) +getToolNames[ All ] := Keys @ $AvailableTools; + +(* No tools *) +getToolNames[ None ] := { }; + +(* A single tool specification without an enclosing list *) +getToolNames[ tool: Except[ _List ] ] := getToolNames @ { tool }; + +getToolNames // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*getCachedToolName*) +getCachedToolName // beginDefinition; + +getCachedToolName[ tool: HoldPattern[ _LLMTool ] ] := Enclose[ + Module[ { name }, + name = ConfirmBy[ toolName @ tool, StringQ, "Name" ]; + ConfirmAssert[ AssociationQ @ $toolBox, "ToolBox" ]; + $toolBox[ name ] = tool; + name + ], + throwInternalFailure[ getCachedToolName @ tool, ## ] & +]; + +getCachedToolName[ name_String ] := + With[ { canonical = toCanonicalToolName @ name }, + Which[ + KeyExistsQ[ $toolBox , canonical ], canonical, + KeyExistsQ[ $toolNameAliases , canonical ], getCachedToolName @ $toolNameAliases @ canonical, + KeyExistsQ[ $defaultChatTools, canonical ], getCachedToolName @ $defaultChatTools @ canonical, + True , name + ] + ]; + +getCachedToolName // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*getToolSelections*) +getToolSelections // beginDefinition; +getToolSelections[ as_Association ] := getToolSelections[ as, Lookup[ as, "ToolSelections", <| |> ] ]; +getToolSelections[ as_, selections_Association ] := selections; +getToolSelections[ as_, Except[ _Association ] ] := <| |>; +getToolSelections // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*getToolSelectionTypes*) +getToolSelectionTypes // beginDefinition; +getToolSelectionTypes[ as_Association ] := getToolSelectionTypes[ as, Lookup[ as, "ToolSelectionType", <| |> ] ]; +getToolSelectionTypes[ as_, selections_Association ] := selections; +getToolSelectionTypes[ as_, Except[ _Association ] ] := <| |>; +getToolSelectionTypes // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*initTools*) +initTools // beginDefinition; + +initTools[ ] := initTools[ ] = ( + + If[ $CloudEvaluation && $VersionNumber <= 13.2, + + If[ PacletFind[ "ServiceConnection_OpenAI" ] === { }, + PacletInstall[ "ServiceConnection_OpenAI", PacletSite -> "https://pacletserver.wolfram.com" ] + ]; + + WithCleanup[ + Unprotect @ TemplateObject, + TemplateObject // Options = DeleteDuplicatesBy[ + Append[ Options @ TemplateObject, MetaInformation -> <| |> ], + ToString @* First + ], + Protect @ TemplateObject + ] + ]; + + + installLLMFunctions[ ]; +); + +initTools // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*installLLMFunctions*) +installLLMFunctions // beginDefinition; + +installLLMFunctions[ ] := Enclose[ + Module[ { before, paclet, opts, reload }, + before = Quiet @ PacletObject[ "Wolfram/LLMFunctions" ]; + paclet = ConfirmBy[ PacletInstall[ "Wolfram/LLMFunctions" ], PacletObjectQ, "PacletInstall" ]; + + If[ ! TrueQ @ Quiet @ PacletNewerQ[ paclet, "1.2.1" ], + opts = If[ $CloudEvaluation, PacletSite -> "https://pacletserver.wolfram.com", UpdatePacletSites -> True ]; + paclet = ConfirmBy[ PacletInstall[ "Wolfram/LLMFunctions", opts ], PacletObjectQ, "PacletUpdate" ]; + ConfirmAssert[ PacletNewerQ[ paclet, "1.2.1" ], "PacletVersion" ]; + reload = True, + reload = PacletObjectQ @ before && PacletNewerQ[ paclet, before ] + ]; + + If[ TrueQ @ reload, reloadLLMFunctions[ ] ]; + Needs[ "Wolfram`LLMFunctions`" -> None ]; + installLLMFunctions[ ] = paclet + ], + throwInternalFailure[ installLLMFunctions[ ], ## ] & +]; + +installLLMFunctions // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*reloadLLMFunctions*) +reloadLLMFunctions // beginDefinition; + +reloadLLMFunctions[ ] := Enclose[ + Module[ { paclet, files }, + paclet = ConfirmBy[ PacletObject[ "Wolfram/LLMFunctions" ], PacletObjectQ, "PacletObject" ]; + files = Select[ $LoadedFiles, StringContainsQ[ "LLMFunctions" ] ]; + If[ ! AnyTrue[ files, StringStartsQ @ paclet[ "Location" ] ], + (* Force paclet to reload if the new one has not been loaded *) + WithCleanup[ + Unprotect @ $Packages, + $Packages = Select[ $Packages, Not @* StringStartsQ[ "Wolfram`LLMFunctions`" ] ]; + ClearAll[ "Wolfram`LLMFunctions`*" ]; + ClearAll[ "Wolfram`LLMFunctions`*`*" ]; + Block[ { $ContextPath }, Get[ "Wolfram`LLMFunctions`" ] ], + Protect @ $Packages + ] + ] + ], + throwInternalFailure[ reloadLLMFunctions[ ], ## ] & +]; + +reloadLLMFunctions // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*resolveTools*) +resolveTools // beginDefinition; + +resolveTools[ settings: KeyValuePattern[ "ToolsEnabled" -> True ] ] := ( + initTools[ ]; + selectTools @ settings; + $toolOptions = Lookup[ settings, "ToolOptions", $DefaultToolOptions ]; + $lastSelectedTools = $selectedTools; + $lastToolOptions = $toolOptions; + If[ KeyExistsQ[ $selectedTools, "WolframLanguageEvaluator" ], needsBasePrompt[ "WolframLanguageEvaluatorTool" ] ]; + Append[ settings, "Tools" -> Values @ $selectedTools ] +); + +resolveTools[ settings_Association ] := settings; + +resolveTools // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*makeToolConfiguration*) +makeToolConfiguration // beginDefinition; + +makeToolConfiguration[ settings_Association ] := Enclose[ + Module[ { tools }, + tools = ConfirmMatch[ DeleteDuplicates @ Flatten @ Values @ $selectedTools, { ___LLMTool }, "SelectedTools" ]; + $toolConfiguration = LLMConfiguration @ <| "Tools" -> tools, "ToolPrompt" -> makeToolPrompt @ settings |> + ], + throwInternalFailure[ makeToolConfiguration @ settings, ## ] & +]; + +makeToolConfiguration // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*$toolConfiguration*) +$toolConfiguration := $toolConfiguration = LLMConfiguration @ <| "Tools" -> Values @ $defaultChatTools |>; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*toolRequestParser*) +toolRequestParser := toolRequestParser = + Quiet[ Check[ $toolConfiguration[ "ToolRequestParser" ], + Wolfram`LLMFunctions`LLMConfiguration`$DefaultTextualToolMethod[ "ToolRequestParser" ], + LLMConfiguration::invprop + ], + LLMConfiguration::invprop + ]; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*makeToolPrompt*) +makeToolPrompt // beginDefinition; + +makeToolPrompt[ settings_Association ] := $lastToolPrompt = TemplateObject[ + Riffle[ + DeleteMissing @ { + $toolPre, + TemplateSequence[ + TemplateExpression @ TemplateObject[ + { + "Tool Name: ", + TemplateSlot[ "Name" ], + "\nDisplay Name: ", + TemplateSlot[ "DisplayName", DefaultValue :> toDisplayToolName @ TemplateSlot[ "Name" ] ], + "\nDescription: ", + TemplateSlot[ "Description" ], + "\nSchema:\n", + TemplateSlot[ "Schema" ], + "\n\n" + }, + CombinerFunction -> StringJoin, + InsertionFunction -> TextString + ], + TemplateExpression @ Map[ + Append[ #[ "Data" ], "Schema" -> ExportString[ #[ "JSONSchema" ], "JSON" ] ] &, + TemplateSlot[ "Tools" ] + ] + ], + $toolPost, + $fullExamples, + makeToolPreferencePrompt @ settings + }, + "\n\n" + ], + CombinerFunction -> StringJoin, + InsertionFunction -> TextString +]; + +makeToolPrompt // endDefinition; + + +$toolPre = "\ +# Tool Instructions + +You have access to tools which can be used to do things, fetch data, compute, etc. while you create your response. \ +Each tool takes input as JSON following a JSON schema. Here are the available tools and their associated schemas:"; + + +$toolPost := "\ +To call a tool, write the following at any time during your response: + +TOOLCALL: +{ + \"\": + \"\": +} +ENDARGUMENTS +ENDTOOLCALL + +Always use valid JSON to specify the parameters in the tool call. Always follow the tool's JSON schema to specify the \ +parameters in the tool call. Fill in the values in <> brackets with the values for the particular tool. Provide as \ +many parameters as the tool requires. Always make one tool call at a time. Always write two line breaks before each \ +tool call. + +The system will execute the requested tool call and you will receive a system message containing the result. \ +You can then use this result to finish writing your response for the user. + +You must write the TOOLCALL in your CURRENT response. \ +Do not state that you will use a tool and end your message before making the tool call. + +If a user asks you to use a specific tool, you MUST attempt to use that tool as requested, \ +even if you think it will not work. \ +If the tool fails, use any error message to correct the issue or explain why it failed. \ +NEVER state that a tool cannot be used for a particular task without trying it first. \ +You did not create these tools, so you do not know what they can and cannot do. + +You should try to avoid mentioning tools by name in your response and instead speak generally about their function. \ +For example, if there were a number_adder tool, you would instead talk about \"adding numbers\". If you must mention \ +a tool by name, you should use the DisplayName property instead of the tool name."; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsubsection::Closed:: *) +(*makeToolPreferencePrompt*) +makeToolPreferencePrompt // beginDefinition; + +makeToolPreferencePrompt[ settings_ ] := + makeToolPreferencePrompt[ settings, settings[ "ToolCallFrequency" ] ]; + +makeToolPreferencePrompt[ settings_, Automatic ] := + Missing[ "NotAvailable" ]; + +makeToolPreferencePrompt[ settings_, freq_? NumberQ ] := + With[ { key = Round @ Clip[ 5 * freq, { 0, 5 } ] }, + TemplateApply[ + $toolPreferencePrompt, + <| "Number" -> Round[ freq * 100 ], "Explanation" -> Lookup[ $toolFrequencyExplanations, key, "" ] |> + ] + ]; + +makeToolPreferencePrompt // endDefinition; + + +$toolPreferencePrompt = "\ +## User Tool Call Preferences + +The user has specified their desired tool calling frequency to be `Number`% with the following instructions: + +IMPORTANT +`Explanation`"; + + +$toolFrequencyExplanations = <| + 0 -> "Only use a tool if explicitly instructed to use tools. Never use tools unless specifically asked to.", + 1 -> "Avoid using tools unless you think it is necessary.", + 2 -> "Only use tools if you think it will significantly improve the quality of your response.", + 3 -> "Use tools whenever it is appropriate to do so.", + 4 -> "Use tools whenever there's even a slight chance that it could improve the quality of your response (e.g. fact checking).", + 5 -> "ALWAYS make a tool call in EVERY response, no matter what." +|>; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Package Footer*) +If[ Wolfram`ChatbookInternal`$BuildingMX, + Null; +]; + +(* :!CodeAnalysis::EndBlock:: *) + +End[ ]; +EndPackage[ ]; diff --git a/Source/Chatbook/Tools.wl b/Source/Chatbook/Tools/DefaultTools.wl similarity index 54% rename from Source/Chatbook/Tools.wl rename to Source/Chatbook/Tools/DefaultTools.wl index f2a864b9..9de5d617 100644 --- a/Source/Chatbook/Tools.wl +++ b/Source/Chatbook/Tools/DefaultTools.wl @@ -7,29 +7,6 @@ BeginPackage[ "Wolfram`Chatbook`Tools`" ]; (* :!CodeAnalysis::BeginBlock:: *) (* :!CodeAnalysis::Disable::SuspiciousSessionSymbol:: *) -HoldComplete[ -`$attachments; -`$defaultChatTools; -`$toolConfiguration; -`$toolEvaluationResults; -`$toolOptions; -`$toolResultStringLength; -`getToolByName; -`getToolDisplayName; -`getToolFormattingFunction; -`getToolIcon; -`initTools; -`makeExpressionURI; -`makeToolConfiguration; -`makeToolResponseString; -`resolveTools; -`toolData; -`toolName; -`toolOptionValue; -`toolRequestParser; -`withToolBox; -]; - Begin[ "`Private`" ]; Needs[ "Wolfram`Chatbook`" ]; @@ -44,803 +21,9 @@ Needs[ "Wolfram`Chatbook`Sandbox`" ]; Needs[ "Wolfram`Chatbook`Serialization`" ]; Needs[ "Wolfram`Chatbook`Utils`" ]; -HoldComplete[ - System`LLMTool; - System`LLMConfiguration; -]; - -(* TODO: - ImageSynthesize - LongTermMemory - Definitions - TestWriter -*) -(* ::**************************************************************************************************************:: *) -(* ::Section::Closed:: *) -(*Tool Lists*) -$DefaultTools := $defaultChatTools; -$InstalledTools := $installedTools; -$AvailableTools := Association[ $DefaultTools, $InstalledTools ]; - -(* ::**************************************************************************************************************:: *) -(* ::Section::Closed:: *) -(*Exported Functions for Tool Repository*) -$ToolFunctions = <| - "ChatPreferences" -> chatPreferences, - "DocumentationLookup" -> documentationLookup, - "DocumentationSearcher" -> documentationSearch, - "WebFetcher" -> webFetch, - "WebImageSearcher" -> webImageSearch, - "WebSearcher" -> webSearch, - "WolframAlpha" -> getWolframAlphaText, - "WolframLanguageEvaluator" -> wolframLanguageEvaluator -|>; - -(* ::**************************************************************************************************************:: *) -(* ::Section::Closed:: *) -(*Tool Configuration*) -$defaultWebTextLength = 12000; -$toolResultStringLength := Ceiling[ $initialCellStringBudget/2 ]; -$webSessionVisible = False; - -$DefaultToolOptions = <| - "WolframLanguageEvaluator" -> <| - "AllowedExecutePaths" -> Automatic, - "AllowedReadPaths" -> All, - "AllowedWritePaths" -> Automatic, - "EvaluationTimeConstraint" -> 60, - "PingTimeConstraint" -> 30 - |>, - "WebFetcher" -> <| - "MaxContentLength" -> $defaultWebTextLength - |>, - "WebSearcher" -> <| - "AllowAdultContent" -> Inherited, - "Language" -> Inherited, - "MaxItems" -> 5, - "Method" -> "Google" - |>, - "WebImageSearcher" -> <| - "AllowAdultContent" -> Inherited, - "Language" -> Inherited, - "MaxItems" -> 5, - "Method" -> "Google" - |> -|>; - -$defaultToolIcon = RawBoxes @ TemplateBox[ { }, "WrenchIcon" ]; - -$attachments = <| |>; -$selectedTools = <| |>; -$toolBox = <| |>; -$toolEvaluationResults = <| |>; -$toolOptions = <| |>; - -$cloudUnsupportedTools = { "WolframLanguageEvaluator", "DocumentationSearcher" }; - -$defaultToolOrder = { - "DocumentationLookup", - "DocumentationSearcher", - "WolframAlpha", - "WolframLanguageEvaluator" -}; - -$toolNameAliases = <| - "DocumentationSearch" -> "DocumentationSearcher", - "WebFetch" -> "WebFetcher", - "WebImageSearch" -> "WebImageSearcher", - "WebSearch" -> "WebSearcher" -|>; - -$installedToolExtraKeys = { - "Description", - "DocumentationLink", - "Origin", - "ResourceName", - "Templated", - "Version" -}; - -(* ::**************************************************************************************************************:: *) -(* ::Section::Closed:: *) -(*Tool Options*) - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*SetToolOptions*) -SetToolOptions // ClearAll; - -SetToolOptions[ name_String, opts: OptionsPattern[ ] ] := - SetToolOptions[ $FrontEnd, name, opts ]; - -SetToolOptions[ scope_, name_String, opts: OptionsPattern[ ] ] := UsingFrontEnd[ - KeyValueMap[ - (CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings", "ToolOptions", name, ToString[ #1 ] } ] = #2) &, - Association @ Reverse @ { opts } - ]; - CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings", "ToolOptions" } ] -]; - -SetToolOptions[ name_String, Inherited ] := - SetToolOptions[ $FrontEnd, name, Inherited ]; - -SetToolOptions[ scope_, name_String, Inherited ] := UsingFrontEnd[ - CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings", "ToolOptions", name } ] = Inherited; - CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings", "ToolOptions" } ] -]; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*toolOptionValue*) -toolOptionValue // beginDefinition; -toolOptionValue[ name_String, key_String ] := toolOptionValue[ name, $toolOptions[ name ], key ]; -toolOptionValue[ name_String, _Missing, key_String ] := toolOptionValue0[ $DefaultToolOptions[ name ], key ]; -toolOptionValue[ name_String, opts_Association, key_String ] := toolOptionValue0[ opts, key ]; -toolOptionValue // endDefinition; - -toolOptionValue0 // beginDefinition; -toolOptionValue0[ opts_Association, key_String ] := Lookup[ opts, key, Lookup[ $DefaultToolOptions, key ] ]; -toolOptionValue0 // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*toolOptions*) -toolOptions // beginDefinition; - -toolOptions[ name_String ] := - toolOptions[ name, $toolOptions[ name ], $DefaultToolOptions[ name ] ]; - -toolOptions[ name_, opts_Association, defaults_Association ] := - toolOptions[ name, <| DeleteCases[ defaults, Inherited ], DeleteCases[ opts, Inherited ] |> ]; - -toolOptions[ name_, _Missing, defaults_Association ] := - toolOptions[ name, DeleteCases[ defaults, Inherited ] ]; - -toolOptions[ name_, opts_Association ] := - Normal[ KeyMap[ toOptionKey, opts ], Association ]; - -toolOptions // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*toOptionKey*) -toOptionKey // beginDefinition; -toOptionKey[ name_String ] /; StringContainsQ[ name, "`" ] := toOptionKey @ Last @ StringSplit[ name, "`" ]; -toOptionKey[ name_String ] /; NameQ[ "System`" <> name ] := Symbol @ name; -toOptionKey[ symbol_Symbol ] := symbol; -toOptionKey // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Section::Closed:: *) -(*Toolbox*) - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*withToolBox*) -withToolBox // beginDefinition; -withToolBox // Attributes = { HoldFirst }; -withToolBox[ eval_ ] := Block[ { $selectedTools = <| |>, $toolOptions = $DefaultToolOptions }, eval ]; -withToolBox // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*selectTools*) -selectTools // beginDefinition; - -selectTools[ as_Association ] := Enclose[ - Module[ { llmEvaluatorName, toolNames, selections, selectionTypes, add, remove, selectedNames }, - - llmEvaluatorName = ConfirmBy[ getLLMEvaluatorName @ as, StringQ, "LLMEvaluatorName" ]; - toolNames = ConfirmMatch[ getToolNames @ as, { ___String }, "Names" ]; - selections = ConfirmBy[ getToolSelections @ as, AssociationQ, "Selections" ]; - selectionTypes = ConfirmBy[ getToolSelectionTypes @ as, AssociationQ, "SelectionTypes" ]; - - add = ConfirmMatch[ - Union[ - Keys @ Select[ selections, Lookup @ llmEvaluatorName ], - Keys @ Select[ selectionTypes, SameAs @ All ] - ], - { ___String }, - "ToolAdditions" - ]; - - remove = ConfirmMatch[ - Union[ - Keys @ Select[ selections, Not @* Lookup[ llmEvaluatorName ] ], - Keys @ Select[ selectionTypes, SameAs @ None ] - ], - { ___String }, - "ToolRemovals" - ]; - - selectedNames = ConfirmMatch[ - Complement[ Union[ toolNames, add ], remove ], - { ___String }, - "SelectedNames" - ]; - - selectTools0 /@ selectedNames - ], - throwInternalFailure[ selectTools @ as, ## ] & -]; - -selectTools // endDefinition; - - -(* TODO: Most of this functionality is moved to `getToolNames`. This only needs to operate on strings. *) -selectTools0 // beginDefinition; - -selectTools0[ Automatic|Inherited ] := selectTools0 @ $defaultChatTools; -selectTools0[ None ] := $selectedTools = <| |>; -selectTools0[ name_String ] /; KeyExistsQ[ $toolBox, name ] := $selectedTools[ name ] = $toolBox[ name ]; -selectTools0[ name_String ] /; KeyExistsQ[ $toolNameAliases, name ] := selectTools0 @ $toolNameAliases @ name; -selectTools0[ name_String ] := selectTools0[ name, Lookup[ $AvailableTools, name ] ]; -selectTools0[ tools_List ] := selectTools0 /@ tools; -selectTools0[ tools_Association ] := KeyValueMap[ selectTools0, tools ]; - -(* Literal LLMTool specification: *) -selectTools0[ tool: HoldPattern @ LLMTool[ KeyValuePattern[ "Name" -> name_ ], ___ ] ] := selectTools0[ name, tool ]; - -(* Rules can be used to enable/disable by name: *) -selectTools0[ (Rule|RuleDelayed)[ name_String, tool_ ] ] := selectTools0[ name, tool ]; - -(* Inherit from core tools: *) -selectTools0[ name_String, Automatic|Inherited ] := selectTools0[ name, Lookup[ $defaultChatTools, name ] ]; - -(* Disable tool: *) -selectTools0[ name_String, None ] := KeyDropFrom[ $selectedTools, name ]; - -(* Select a literal LLMTool: *) -selectTools0[ name_String, tool: HoldPattern[ _LLMTool ] ] := $selectedTools[ name ] = $toolBox[ name ] = tool; - -(* Tool not found: *) -selectTools0[ name_String, Missing[ "KeyAbsent", name_ ] ] := - If[ TrueQ @ KeyExistsQ[ $defaultChatTools0, name ], - (* A default tool that was filtered for compatibility *) - Null, - (* An unknown tool name *) - messagePrint[ "ToolNotFound", name ] - ]; - -selectTools0 // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*getLLMEvaluatorName*) -getLLMEvaluatorName // beginDefinition; -getLLMEvaluatorName[ KeyValuePattern[ "LLMEvaluatorName" -> name_String ] ] := name; -getLLMEvaluatorName[ KeyValuePattern[ "LLMEvaluator" -> name_String ] ] := name; - -getLLMEvaluatorName[ KeyValuePattern[ "LLMEvaluator" -> evaluator_Association ] ] := - Lookup[ evaluator, "LLMEvaluatorName", Lookup[ evaluator, "Name" ] ]; - -getLLMEvaluatorName // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*getToolNames*) -getToolNames // beginDefinition; - -(* Persona declares tools, so combine with defaults as appropriate *) -getToolNames[ as: KeyValuePattern[ "LLMEvaluator" -> KeyValuePattern[ "Tools" -> tools_ ] ] ] := - getToolNames[ Lookup[ as, "Tools", None ], tools ]; - -(* No tool specification by persona, so get defaults *) -getToolNames[ as_Association ] := - getToolNames @ Lookup[ as, "Tools", Automatic ]; - -(* Persona does not want any tools *) -getToolNames[ tools_, None ] := { }; - -(* Persona wants default tools *) -getToolNames[ tools_, Automatic|Inherited ] := getToolNames @ tools; - -(* Persona declares an explicit list of tools *) -getToolNames[ Automatic|None|Inherited, personaTools_List ] := getToolNames @ personaTools; - -(* The user has specified an explicit list of tools as well, so include them *) -getToolNames[ tools_List, personaTools_List ] := Union[ getToolNames @ tools, getToolNames @ personaTools ]; - -(* Get name of each tool *) -getToolNames[ tools_List ] := DeleteDuplicates @ Flatten[ getCachedToolName /@ tools ]; - -(* Default tools *) -getToolNames[ Automatic|Inherited ] := Keys @ $DefaultTools; - -(* All tools *) -getToolNames[ All ] := Keys @ $AvailableTools; - -(* No tools *) -getToolNames[ None ] := { }; - -(* A single tool specification without an enclosing list *) -getToolNames[ tool: Except[ _List ] ] := getToolNames @ { tool }; - -getToolNames // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*getCachedToolName*) -getCachedToolName // beginDefinition; - -getCachedToolName[ tool: HoldPattern[ _LLMTool ] ] := Enclose[ - Module[ { name }, - name = ConfirmBy[ toolName @ tool, StringQ, "Name" ]; - ConfirmAssert[ AssociationQ @ $toolBox, "ToolBox" ]; - $toolBox[ name ] = tool; - name - ], - throwInternalFailure[ getCachedToolName @ tool, ## ] & -]; - -getCachedToolName[ name_String ] := - With[ { canonical = toCanonicalToolName @ name }, - Which[ - KeyExistsQ[ $toolBox , canonical ], canonical, - KeyExistsQ[ $toolNameAliases , canonical ], getCachedToolName @ $toolNameAliases @ canonical, - KeyExistsQ[ $defaultChatTools, canonical ], getCachedToolName @ $defaultChatTools @ canonical, - True , name - ] - ]; - -getCachedToolName // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*getToolSelections*) -getToolSelections // beginDefinition; -getToolSelections[ as_Association ] := getToolSelections[ as, Lookup[ as, "ToolSelections", <| |> ] ]; -getToolSelections[ as_, selections_Association ] := selections; -getToolSelections[ as_, Except[ _Association ] ] := <| |>; -getToolSelections // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*getToolSelectionTypes*) -getToolSelectionTypes // beginDefinition; -getToolSelectionTypes[ as_Association ] := getToolSelectionTypes[ as, Lookup[ as, "ToolSelectionType", <| |> ] ]; -getToolSelectionTypes[ as_, selections_Association ] := selections; -getToolSelectionTypes[ as_, Except[ _Association ] ] := <| |>; -getToolSelectionTypes // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*initTools*) -initTools // beginDefinition; - -initTools[ ] := initTools[ ] = ( - - If[ $CloudEvaluation && $VersionNumber <= 13.2, - - If[ PacletFind[ "ServiceConnection_OpenAI" ] === { }, - PacletInstall[ "ServiceConnection_OpenAI", PacletSite -> "https://pacletserver.wolfram.com" ] - ]; - - WithCleanup[ - Unprotect @ TemplateObject, - TemplateObject // Options = DeleteDuplicatesBy[ - Append[ Options @ TemplateObject, MetaInformation -> <| |> ], - ToString @* First - ], - Protect @ TemplateObject - ] - ]; - - - installLLMFunctions[ ]; -); - -initTools // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*installLLMFunctions*) -installLLMFunctions // beginDefinition; - -installLLMFunctions[ ] := Enclose[ - Module[ { before, paclet, opts, reload }, - before = Quiet @ PacletObject[ "Wolfram/LLMFunctions" ]; - paclet = ConfirmBy[ PacletInstall[ "Wolfram/LLMFunctions" ], PacletObjectQ, "PacletInstall" ]; - - If[ ! TrueQ @ Quiet @ PacletNewerQ[ paclet, "1.2.1" ], - opts = If[ $CloudEvaluation, PacletSite -> "https://pacletserver.wolfram.com", UpdatePacletSites -> True ]; - paclet = ConfirmBy[ PacletInstall[ "Wolfram/LLMFunctions", opts ], PacletObjectQ, "PacletUpdate" ]; - ConfirmAssert[ PacletNewerQ[ paclet, "1.2.1" ], "PacletVersion" ]; - reload = True, - reload = PacletObjectQ @ before && PacletNewerQ[ paclet, before ] - ]; - - If[ TrueQ @ reload, reloadLLMFunctions[ ] ]; - Needs[ "Wolfram`LLMFunctions`" -> None ]; - installLLMFunctions[ ] = paclet - ], - throwInternalFailure[ installLLMFunctions[ ], ## ] & -]; - -installLLMFunctions // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*reloadLLMFunctions*) -reloadLLMFunctions // beginDefinition; - -reloadLLMFunctions[ ] := Enclose[ - Module[ { paclet, files }, - paclet = ConfirmBy[ PacletObject[ "Wolfram/LLMFunctions" ], PacletObjectQ, "PacletObject" ]; - files = Select[ $LoadedFiles, StringContainsQ[ "LLMFunctions" ] ]; - If[ ! AnyTrue[ files, StringStartsQ @ paclet[ "Location" ] ], - (* Force paclet to reload if the new one has not been loaded *) - WithCleanup[ - Unprotect @ $Packages, - $Packages = Select[ $Packages, Not @* StringStartsQ[ "Wolfram`LLMFunctions`" ] ]; - ClearAll[ "Wolfram`LLMFunctions`*" ]; - ClearAll[ "Wolfram`LLMFunctions`*`*" ]; - Block[ { $ContextPath }, Get[ "Wolfram`LLMFunctions`" ] ], - Protect @ $Packages - ] - ] - ], - throwInternalFailure[ reloadLLMFunctions[ ], ## ] & -]; - -reloadLLMFunctions // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*resolveTools*) -resolveTools // beginDefinition; - -resolveTools[ settings: KeyValuePattern[ "ToolsEnabled" -> True ] ] := ( - initTools[ ]; - selectTools @ settings; - $toolOptions = Lookup[ settings, "ToolOptions", $DefaultToolOptions ]; - $lastSelectedTools = $selectedTools; - $lastToolOptions = $toolOptions; - If[ KeyExistsQ[ $selectedTools, "WolframLanguageEvaluator" ], needsBasePrompt[ "WolframLanguageEvaluatorTool" ] ]; - Append[ settings, "Tools" -> Values @ $selectedTools ] -); - -resolveTools[ settings_Association ] := settings; - -resolveTools // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*makeToolConfiguration*) -makeToolConfiguration // beginDefinition; - -makeToolConfiguration[ settings_Association ] := Enclose[ - Module[ { tools }, - tools = ConfirmMatch[ DeleteDuplicates @ Flatten @ Values @ $selectedTools, { ___LLMTool }, "SelectedTools" ]; - $toolConfiguration = LLMConfiguration @ <| "Tools" -> tools, "ToolPrompt" -> makeToolPrompt @ settings |> - ], - throwInternalFailure[ makeToolConfiguration @ settings, ## ] & -]; - -makeToolConfiguration // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*$toolConfiguration*) -$toolConfiguration := $toolConfiguration = LLMConfiguration @ <| "Tools" -> Values @ $defaultChatTools |>; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*toolRequestParser*) -toolRequestParser := toolRequestParser = - Quiet[ Check[ $toolConfiguration[ "ToolRequestParser" ], - Wolfram`LLMFunctions`LLMConfiguration`$DefaultTextualToolMethod[ "ToolRequestParser" ], - LLMConfiguration::invprop - ], - LLMConfiguration::invprop - ]; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*makeToolPrompt*) -makeToolPrompt // beginDefinition; - -makeToolPrompt[ settings_Association ] := $lastToolPrompt = TemplateObject[ - Riffle[ - DeleteMissing @ { - $toolPre, - TemplateSequence[ - TemplateExpression @ TemplateObject[ - { - "Tool Name: ", - TemplateSlot[ "Name" ], - "\nDisplay Name: ", - TemplateSlot[ "DisplayName", DefaultValue :> toDisplayToolName @ TemplateSlot[ "Name" ] ], - "\nDescription: ", - TemplateSlot[ "Description" ], - "\nSchema:\n", - TemplateSlot[ "Schema" ], - "\n\n" - }, - CombinerFunction -> StringJoin, - InsertionFunction -> TextString - ], - TemplateExpression @ Map[ - Append[ #[ "Data" ], "Schema" -> ExportString[ #[ "JSONSchema" ], "JSON" ] ] &, - TemplateSlot[ "Tools" ] - ] - ], - $toolPost, - $fullExamples, - makeToolPreferencePrompt @ settings - }, - "\n\n" - ], - CombinerFunction -> StringJoin, - InsertionFunction -> TextString -]; - -makeToolPrompt // endDefinition; - - -$toolPre = "\ -# Tool Instructions - -You have access to tools which can be used to do things, fetch data, compute, etc. while you create your response. \ -Each tool takes input as JSON following a JSON schema. Here are the available tools and their associated schemas:"; - - -$toolPost := "\ -To call a tool, write the following at any time during your response: - -TOOLCALL: -{ - \"\": - \"\": -} -ENDARGUMENTS -ENDTOOLCALL - -Always use valid JSON to specify the parameters in the tool call. Always follow the tool's JSON schema to specify the \ -parameters in the tool call. Fill in the values in <> brackets with the values for the particular tool. Provide as \ -many parameters as the tool requires. Always make one tool call at a time. Always write two line breaks before each \ -tool call. - -The system will execute the requested tool call and you will receive a system message containing the result. \ -You can then use this result to finish writing your response for the user. - -You must write the TOOLCALL in your CURRENT response. \ -Do not state that you will use a tool and end your message before making the tool call. - -If a user asks you to use a specific tool, you MUST attempt to use that tool as requested, \ -even if you think it will not work. \ -If the tool fails, use any error message to correct the issue or explain why it failed. \ -NEVER state that a tool cannot be used for a particular task without trying it first. \ -You did not create these tools, so you do not know what they can and cannot do. - -You should try to avoid mentioning tools by name in your response and instead speak generally about their function. \ -For example, if there were a number_adder tool, you would instead talk about \"adding numbers\". If you must mention \ -a tool by name, you should use the DisplayName property instead of the tool name."; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsubsection::Closed:: *) -(*makeToolPreferencePrompt*) -makeToolPreferencePrompt // beginDefinition; - -makeToolPreferencePrompt[ settings_ ] := - makeToolPreferencePrompt[ settings, settings[ "ToolCallFrequency" ] ]; - -makeToolPreferencePrompt[ settings_, Automatic ] := - Missing[ "NotAvailable" ]; - -makeToolPreferencePrompt[ settings_, freq_? NumberQ ] := - With[ { key = Round @ Clip[ 5 * freq, { 0, 5 } ] }, - TemplateApply[ - $toolPreferencePrompt, - <| "Number" -> Round[ freq * 100 ], "Explanation" -> Lookup[ $toolFrequencyExplanations, key, "" ] |> - ] - ]; - -makeToolPreferencePrompt // endDefinition; - - -$toolPreferencePrompt = "\ -## User Tool Call Preferences - -The user has specified their desired tool calling frequency to be `Number`% with the following instructions: - -IMPORTANT -`Explanation`"; - - -$toolFrequencyExplanations = <| - 0 -> "Only use a tool if explicitly instructed to use tools. Never use tools unless specifically asked to.", - 1 -> "Avoid using tools unless you think it is necessary.", - 2 -> "Only use tools if you think it will significantly improve the quality of your response.", - 3 -> "Use tools whenever it is appropriate to do so.", - 4 -> "Use tools whenever there's even a slight chance that it could improve the quality of your response (e.g. fact checking).", - 5 -> "ALWAYS make a tool call in EVERY response, no matter what." -|>; - -(* ::**************************************************************************************************************:: *) -(* ::Section::Closed:: *) -(*Default Tools*) -$defaultChatTools := If[ TrueQ @ $CloudEvaluation, - KeyDrop[ $defaultChatTools0, $cloudUnsupportedTools ], - $defaultChatTools0 - ]; - -$defaultChatTools0 = <| |>; - -(* ::**************************************************************************************************************:: *) -(* ::Section::Closed:: *) -(*Installed Tools*) -$installedTools := Association @ Cases[ - GetInstalledResourceData[ "LLMTool" ], - as: KeyValuePattern[ "Tool" -> tool_ ] :> (toolName @ tool -> addExtraToolData[ tool, as ]) -]; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*addExtraToolData*) -addExtraToolData // beginDefinition; - -addExtraToolData[ tool: HoldPattern @ LLMTool[ as_Association, a___ ], extra_Association ] := - With[ { new = Join[ KeyTake[ extra, $installedToolExtraKeys ], as ] }, LLMTool[ new, a ] ]; - -addExtraToolData // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*getToolByName*) -getToolByName // beginDefinition; -getToolByName[ name_String ] := Lookup[ $toolBox, toCanonicalToolName @ name ]; -getToolByName // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*toolName*) -toolName // beginDefinition; -toolName[ tool_ ] := toolName[ tool, Automatic ]; -toolName[ HoldPattern @ LLMTool[ as_Association, ___ ], type_ ] := toolName[ as, type ]; -toolName[ KeyValuePattern[ "CanonicalName" -> name_String ], "Canonical" ] := name; -toolName[ KeyValuePattern[ "DisplayName" -> name_String ], "Display" ] := name; -toolName[ KeyValuePattern[ "Name" -> name_String ], type_ ] := toolName[ name, type ]; -toolName[ tool_, Automatic ] := toolName[ tool, "Canonical" ]; -toolName[ name_String, "Machine" ] := toMachineToolName @ name; -toolName[ name_String, "Canonical" ] := toCanonicalToolName @ name; -toolName[ name_String, "Display" ] := toDisplayToolName @ name; -toolName[ tools_List, type_ ] := toolName[ #, type ] & /@ tools; -toolName // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*toolData*) -toolData // beginDefinition; - -toolData[ HoldPattern @ LLMTool[ as_Association, ___ ] ] := - toolData @ as; - -toolData[ name_String ] /; KeyExistsQ[ $toolBox, name ] := - toolData @ $toolBox[ name ]; - -toolData[ name_String ] /; KeyExistsQ[ $defaultChatTools, name ] := - toolData @ $defaultChatTools[ name ]; - -toolData[ as: KeyValuePattern @ { "Function"|"ToolCall" -> _ } ] := <| - toolDefaultData @ toolName @ as, - "Icon" -> toolDefaultIcon @ as, - as -|>; - -toolData[ tools_List ] := - toolData /@ tools; - -toolData // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*toolDefaultIcon*) -toolDefaultIcon // beginDefinition; - -toolDefaultIcon[ KeyValuePattern[ "Origin" -> "LLMToolRepository" ] ] := - RawBoxes @ TemplateBox[ { }, "ToolManagerRepository" ]; - -toolDefaultIcon[ _Association ] := - $defaultToolIcon; - -toolDefaultIcon // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*toolDefaultData*) -toolDefaultData // beginDefinition; - -toolDefaultData[ name_String ] := <| - "CanonicalName" -> toCanonicalToolName @ name, - "DisplayName" -> toDisplayToolName @ name, - "Name" -> toMachineToolName @ name, - "Icon" -> $defaultToolIcon -|>; - -toolDefaultData // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*toMachineToolName*) -toMachineToolName // beginDefinition; - -toMachineToolName[ s_String ] := - ToLowerCase @ StringReplace[ - StringTrim @ s, - { " " -> "_", a_?LowerCaseQ ~~ b_?UpperCaseQ ~~ c_?LowerCaseQ :> a<>"_"<>b<>c } - ]; - -toMachineToolName // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*toCanonicalToolName*) -toCanonicalToolName // beginDefinition; - -toCanonicalToolName[ s_String ] := - Capitalize @ StringReplace[ StringTrim @ s, a_~~("_"|" ")~~b_ :> a <> ToUpperCase @ b ]; - -toCanonicalToolName // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*toDisplayToolName*) -toDisplayToolName // beginDefinition; - -toDisplayToolName[ s_String ] := - Capitalize[ - StringReplace[ - StringTrim @ s, - { "_" :> " ", a_?LowerCaseQ ~~ b_?UpperCaseQ ~~ c_?LowerCaseQ :> a<>" "<>b<>c } - ], - "TitleCase" - ]; - -toDisplayToolName // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsection::Closed:: *) -(*formatToolCallExample*) -formatToolCallExample // beginDefinition; - -formatToolCallExample[ name_String, params_Association ] := - TemplateApply[ - "TOOLCALL: `1`\n`2`\nENDARGUMENTS\nENDTOOLCALL", - { toMachineToolName @ name, Developer`WriteRawJSONString @ params } - ]; - -formatToolCallExample // endDefinition; - (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*ChatPreferences*) -$chatPreferencesDescription = "\ -Get and set current chat preferences. - -Getting chat preferences -* When getting, the `key` parameter is optional and the `value` parameter is unused. -* If `key` is omitted, all available chat preferences will be returned. -* When getting chat preferences, you'll receive the current values along with additional information about possible values. - -Setting chat preferences -* When setting, both the `key` parameter and the `value` parameter are required. - -Key descriptions -| key | type | default | description | -| ------------------- | ---------- | --------------- | ----------- | -| Assistance | boolean | false | Whether to enable automatic AI assistance for normal input cell evaluations. | -| ChatHistoryLength | integer | 100 | The maximum number of cells to include in chat history. | -| LLMEvaluator | string | 'CodeAssistant' | The name of the LLM evaluator (aka persona) to use for chat. Use the 'get' action for available names. | -| MaxCellStringLength | integer | 0 | The maximum number of characters to include in a cell's string representation. A 0 means determine automatically. | -| MergeMessages | boolean | true | Whether to merge consecutive messages from the same user into a single message. | -| Model | string | 'gpt-4' | The name of the model to use for AI assistance. | -| Prompts | [ string ] | [ ] | A list of instructions to append to the system prompt. | -| Temperature | real | 0.7 | The temperature to use for the LLM. Values should be a real between 0 and 2. | -| ToolCallFrequency | real | 0.5 | The frequency with which to use tools. Values should be a number between 0 and 1. | -| Tools | [ string ] | | The list of currently enabled tools. Use the 'get' action for available names. | -"; - $defaultChatTools0[ "ChatPreferences" ] = <| toolDefaultData[ "ChatPreferences" ], "Icon" -> RawBoxes @ TemplateBox[ { }, "ChatBlockSettingsMenuIcon" ], @@ -872,156 +55,6 @@ $defaultChatTools0[ "ChatPreferences" ] = <| } |>; -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*chatPreferences*) -chatPreferences // beginDefinition; -chatPreferences[ args_Association ] := chatPreferences[ args[ "action" ], args ]; -chatPreferences[ "get", args_Association ] := getChatPreferences @ args; -chatPreferences[ "set", args_Association ] := setChatPreferences @ args; -chatPreferences // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*setChatPreferences*) -setChatPreferences // beginDefinition; -setChatPreferences[ as_Association ] := setChatPreferences[ as[ "scope" ], as[ "key" ], as[ "value" ] ]; -setChatPreferences[ scope_, key_, value_ ] := setChatPreferences0[ toScope @ scope, key, interpretValue[ key, value ] ]; -setChatPreferences // endDefinition; - -setChatPreferences0 // beginDefinition; -setChatPreferences0[ failure_Failure, key_, value_ ] := failure; -setChatPreferences0[ scope_, key_, failure_Failure ] := failure; -setChatPreferences0[ scope_, key_, value_ ] := setChatPreferences1[ scope, key, value ]; -setChatPreferences0 // endDefinition; - -setChatPreferences1 // beginDefinition; -setChatPreferences1[ scope: $FrontEnd|_NotebookObject, key_String, value: Except[ _Failure ] ] := ( - CurrentChatSettings[ scope, key ] = value -); -setChatPreferences1 // endDefinition; - -(* -setChatPreferences0[ scope_, "Assistance" , value_ ] := setPrefChatAssistance[ scope, value ]; -setChatPreferences0[ scope_, "ChatHistoryLength" , value_ ] := setPrefChatHistoryLength[ scope, value ]; -setChatPreferences0[ scope_, "LLMEvaluator" , value_ ] := setPrefLLMEvaluator[ scope, value ]; -setChatPreferences0[ scope_, "MaxCellStringLength", value_ ] := setPrefMaxCellStringLength[ scope, value ]; -setChatPreferences0[ scope_, "MergeMessages" , value_ ] := setPrefMergeMessages[ scope, value ]; -setChatPreferences0[ scope_, "Model" , value_ ] := setPrefModel[ scope, value ]; -setChatPreferences0[ scope_, "Prompts" , value_ ] := setPrefPrompts[ scope, value ]; -setChatPreferences0[ scope_, "Temperature" , value_ ] := setPrefTemperature[ scope, value ]; -setChatPreferences0[ scope_, "ToolCallFrequency" , value_ ] := setPrefToolCallFrequency[ scope, value ]; -setChatPreferences0[ scope_, "Tools" , value_ ] := setPrefTools[ scope, value ]; -*) - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*interpretValue*) -interpretValue // beginDefinition; -interpretValue[ key_String, value_String ] := $interpreters[ key ][ value ]; -interpretValue // endDefinition; - -$interpreters = Interpreter /@ <| - "Assistance" -> "Boolean", - "ChatHistoryLength" -> "Integer", - "LLMEvaluator" -> interpretLLMEvaluator, - "MaxCellStringLength" -> "Integer", - "MergeMessages" -> "Boolean", - "Model" -> interpretModel, - "Prompts" -> RepeatingElement[ "String" ], - "Temperature" -> Restricted[ "Number", { 0, 2 } ], - "ToolCallFrequency" -> Restricted[ "Number", { 0, 1 } ], - "Tools" -> interpretTools -|>; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsubsection::Closed:: *) -(*interpretLLMEvaluator*) -interpretLLMEvaluator // beginDefinition; - -interpretLLMEvaluator[ name_String ] := - With[ { personas = Keys @ GetCachedPersonaData[ ] }, - If[ MemberQ[ personas, name ], - name, - Failure[ - "InvalidLLMEvaluator", - <| - "MessageTemplate" -> "`1` is not among the list of available personas: `2`", - "MessageParameters" -> personas - |> - ] - ] - ]; - -interpretLLMEvaluator // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsubsection::Closed:: *) -(*interpretModel*) -interpretModel // beginDefinition; - -interpretModel[ name_String ] := - With[ { models = Select[ getModelList[ ], chatModelQ ] }, - If[ MemberQ[ models, ToLowerCase @ name ], - ToLowerCase @ name, - Failure[ - "InvalidModel", - <| - "MessageTemplate" -> "`1` is not among the list of available models: `2`", - "MessageParameters" -> models - |> - ] - ] - ]; - -interpretModel // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsubsection::Closed:: *) -(*interpretTools*) -interpretTools // beginDefinition; - -interpretTools[ name_String ] := - With[ { tools = Keys @ $AvailableTools }, - If[ MemberQ[ tools, name ], - name, - Failure[ - "InvalidTool", - <| - "MessageTemplate" -> "`1` is not among the list of available tools: `2`", - "MessageParameters" -> tools - |> - ] - ] - ]; - -interpretTools // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*toScope*) -toScope // beginDefinition; -toScope[ "global" ] := $FrontEnd; -toScope[ "notebook" ] := EvaluationNotebook[ ]; -toScope[ _Missing ] := toScope[ "notebook" ]; -toScope[ failure_Failure ] := failure; -toScope // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*setPrefChatAssistance*) -setPrefChatAssistance // beginDefinition; -setPrefChatAssistance[ scope: $$scope, value: True|False ] := CurrentChatSettings[ scope, "Assistance" ] = value; -setPrefChatAssistance[ scope_, failure_Failure ] := failure; -setPrefChatAssistance // endDefinition; - -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*setPrefModel*) -setPrefModel // beginDefinition; -setPrefModel[ scope: $$scope, value_String ] := CurrentChatSettings[ scope, "Model" ] = value; -setPrefModel // endDefinition; - (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*DocumentationSearch*) diff --git a/Source/Chatbook/Tools/ToolOptions.wl b/Source/Chatbook/Tools/ToolOptions.wl new file mode 100644 index 00000000..cbce76de --- /dev/null +++ b/Source/Chatbook/Tools/ToolOptions.wl @@ -0,0 +1,100 @@ +(* ::Section::Closed:: *) +(*Package Header*) +BeginPackage[ "Wolfram`Chatbook`Tools`" ]; + +(* :!CodeAnalysis::BeginBlock:: *) + +Begin[ "`Private`" ]; + +Needs[ "Wolfram`Chatbook`" ]; +Needs[ "Wolfram`Chatbook`ChatMessages`" ]; +Needs[ "Wolfram`Chatbook`Common`" ]; +Needs[ "Wolfram`Chatbook`Formatting`" ]; +Needs[ "Wolfram`Chatbook`Models`" ]; +Needs[ "Wolfram`Chatbook`Personas`" ]; +Needs[ "Wolfram`Chatbook`Prompting`" ]; +Needs[ "Wolfram`Chatbook`ResourceInstaller`" ]; +Needs[ "Wolfram`Chatbook`Sandbox`" ]; +Needs[ "Wolfram`Chatbook`Serialization`" ]; +Needs[ "Wolfram`Chatbook`Utils`" ]; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Tool Options*) + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*SetToolOptions*) +SetToolOptions // ClearAll; + +SetToolOptions[ name_String, opts: OptionsPattern[ ] ] := + SetToolOptions[ $FrontEnd, name, opts ]; + +SetToolOptions[ scope_, name_String, opts: OptionsPattern[ ] ] := UsingFrontEnd[ + KeyValueMap[ + (CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings", "ToolOptions", name, ToString[ #1 ] } ] = #2) &, + Association @ Reverse @ { opts } + ]; + CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings", "ToolOptions" } ] +]; + +SetToolOptions[ name_String, Inherited ] := + SetToolOptions[ $FrontEnd, name, Inherited ]; + +SetToolOptions[ scope_, name_String, Inherited ] := UsingFrontEnd[ + CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings", "ToolOptions", name } ] = Inherited; + CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings", "ToolOptions" } ] +]; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*toolOptionValue*) +toolOptionValue // beginDefinition; +toolOptionValue[ name_String, key_String ] := toolOptionValue[ name, $toolOptions[ name ], key ]; +toolOptionValue[ name_String, _Missing, key_String ] := toolOptionValue0[ $DefaultToolOptions[ name ], key ]; +toolOptionValue[ name_String, opts_Association, key_String ] := toolOptionValue0[ opts, key ]; +toolOptionValue // endDefinition; + +toolOptionValue0 // beginDefinition; +toolOptionValue0[ opts_Association, key_String ] := Lookup[ opts, key, Lookup[ $DefaultToolOptions, key ] ]; +toolOptionValue0 // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*toolOptions*) +toolOptions // beginDefinition; + +toolOptions[ name_String ] := + toolOptions[ name, $toolOptions[ name ], $DefaultToolOptions[ name ] ]; + +toolOptions[ name_, opts_Association, defaults_Association ] := + toolOptions[ name, <| DeleteCases[ defaults, Inherited ], DeleteCases[ opts, Inherited ] |> ]; + +toolOptions[ name_, _Missing, defaults_Association ] := + toolOptions[ name, DeleteCases[ defaults, Inherited ] ]; + +toolOptions[ name_, opts_Association ] := + Normal[ KeyMap[ toOptionKey, opts ], Association ]; + +toolOptions // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*toOptionKey*) +toOptionKey // beginDefinition; +toOptionKey[ name_String ] /; StringContainsQ[ name, "`" ] := toOptionKey @ Last @ StringSplit[ name, "`" ]; +toOptionKey[ name_String ] /; NameQ[ "System`" <> name ] := Symbol @ name; +toOptionKey[ symbol_Symbol ] := symbol; +toOptionKey // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Package Footer*) +If[ Wolfram`ChatbookInternal`$BuildingMX, + Null; +]; + +(* :!CodeAnalysis::EndBlock:: *) + +End[ ]; +EndPackage[ ]; diff --git a/Source/Chatbook/Tools/Tools.wl b/Source/Chatbook/Tools/Tools.wl new file mode 100644 index 00000000..83fc66d4 --- /dev/null +++ b/Source/Chatbook/Tools/Tools.wl @@ -0,0 +1,48 @@ +(* ::Section::Closed:: *) +(*Package Header*) +BeginPackage[ "Wolfram`Chatbook`Tools`" ]; + +HoldComplete[ + `$attachments; + `$defaultChatTools; + `$toolConfiguration; + `$toolEvaluationResults; + `$toolOptions; + `$toolResultStringLength; + `getToolByName; + `getToolDisplayName; + `getToolFormattingFunction; + `getToolIcon; + `initTools; + `makeExpressionURI; + `makeToolConfiguration; + `makeToolResponseString; + `resolveTools; + `toolData; + `toolName; + `toolOptionValue; + `toolRequestParser; + `withToolBox; +]; + +Begin[ "`Private`" ]; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Load Subcontexts*) +Get[ "Wolfram`Chatbook`Tools`Common`" ]; +Get[ "Wolfram`Chatbook`Tools`ToolOptions`" ]; +Get[ "Wolfram`Chatbook`Tools`DefaultTools`" ]; +Get[ "Wolfram`Chatbook`Tools`ChatPreferences`" ]; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Package Footer*) +If[ Wolfram`ChatbookInternal`$BuildingMX, + Null; +]; + +(* :!CodeAnalysis::EndBlock:: *) + +End[ ]; +EndPackage[ ]; From 4bcefa6d8682884ef2634d15fdefc26801f4fb1f Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Wed, 29 Nov 2023 19:23:06 -0500 Subject: [PATCH 03/46] Enable web search and fetch for CodeAssistant --- .../Personas/CodeAssistant/LLMConfiguration.wl | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/LLMConfiguration/Personas/CodeAssistant/LLMConfiguration.wl b/LLMConfiguration/Personas/CodeAssistant/LLMConfiguration.wl index 02311a39..eff5a7ef 100644 --- a/LLMConfiguration/Personas/CodeAssistant/LLMConfiguration.wl +++ b/LLMConfiguration/Personas/CodeAssistant/LLMConfiguration.wl @@ -2,6 +2,13 @@ "BasePrompt" -> { "WolframLanguageStyle" }, "DisplayName" -> "Code Assistant", "Icon" -> RawBoxes @ TemplateBox[ { }, "ChatIconCodeAssistant" ], - "Tools" -> { "DocumentationLookup", "DocumentationSearcher", "WolframAlpha", "WolframLanguageEvaluator" }, + "Tools" -> { + "DocumentationLookup", + "DocumentationSearcher", + "WolframAlpha", + "WolframLanguageEvaluator", + "WebSearcher", + "WebFetcher" + }, "Description" -> "Help with writing and generating Wolfram Language code" |> \ No newline at end of file From 8e4b2014b012537553ec0dac1856a30910641d71 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Thu, 30 Nov 2023 11:02:57 -0500 Subject: [PATCH 04/46] If a tool has been removed, it may still have lingering selection preferences --- Source/Chatbook/Tools/Common.wl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Chatbook/Tools/Common.wl b/Source/Chatbook/Tools/Common.wl index b80b4a87..dae7c7bd 100644 --- a/Source/Chatbook/Tools/Common.wl +++ b/Source/Chatbook/Tools/Common.wl @@ -451,7 +451,7 @@ getCachedToolName // endDefinition; (*getToolSelections*) getToolSelections // beginDefinition; getToolSelections[ as_Association ] := getToolSelections[ as, Lookup[ as, "ToolSelections", <| |> ] ]; -getToolSelections[ as_, selections_Association ] := selections; +getToolSelections[ as_, selections_Association ] := KeyTake[ selections, Keys @ $AvailableTools ]; getToolSelections[ as_, Except[ _Association ] ] := <| |>; getToolSelections // endDefinition; @@ -460,7 +460,7 @@ getToolSelections // endDefinition; (*getToolSelectionTypes*) getToolSelectionTypes // beginDefinition; getToolSelectionTypes[ as_Association ] := getToolSelectionTypes[ as, Lookup[ as, "ToolSelectionType", <| |> ] ]; -getToolSelectionTypes[ as_, selections_Association ] := selections; +getToolSelectionTypes[ as_, selections_Association ] := KeyTake[ selections, Keys @ $AvailableTools ]; getToolSelectionTypes[ as_, Except[ _Association ] ] := <| |>; getToolSelectionTypes // endDefinition; From a1574a19df6b6be968e14b6b8167851423bc1537 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Thu, 4 Jan 2024 15:40:28 -0500 Subject: [PATCH 05/46] Clean up chat task and cell if there's a user abort --- Source/Chatbook/Actions.wl | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/Source/Chatbook/Actions.wl b/Source/Chatbook/Actions.wl index 4e011464..577a72cf 100644 --- a/Source/Chatbook/Actions.wl +++ b/Source/Chatbook/Actions.wl @@ -20,6 +20,7 @@ BeginPackage[ "Wolfram`Chatbook`Actions`" ]; `$autoAssistMode; `$autoOpen; `$finalCell; +`$lastCellObject; `$lastChatString; `$lastMessages; `$lastSettings; @@ -406,14 +407,32 @@ EvaluateChatInput[ evalCell_CellObject, nbo_NotebookObject ] := EvaluateChatInput[ evalCell, nbo, currentChatSettings @ nbo ]; EvaluateChatInput[ evalCell_CellObject, nbo_NotebookObject, settings_Association? AssociationQ ] := - withChatState @ Block[ { $autoAssistMode = False }, - $lastMessages = None; + withChatState @ Block[ { $autoAssistMode = False, $aborted = False }, + $lastCellObject = None; $lastChatString = None; + $lastMessages = None; $nextTaskEvaluation = None; - clearMinimizedChats @ nbo; $enableLLMServices = settings[ "EnableLLMServices" ]; + clearMinimizedChats @ nbo; + + (* Send chat while listening for an abort: *) + CheckAbort[ sendChat[ evalCell, nbo, settings ]; - waitForLastTask[ ]; + waitForLastTask[ ] + , + (* The user has issued an abort: *) + $aborted = True; + (* Clean up the current chat evaluation: *) + With[ { cell = $lastCellObject }, + If[ MatchQ[ cell, _CellObject ], + StopChat @ cell, + removeTask @ $lastTask + ] + ] + , + PropagateAborts -> False + ]; + blockChatObject[ If[ ListQ @ $lastMessages && StringQ @ $lastChatString, With[ From adafb5ef2fb28edc0374ceb213cfbd06fb009f8e Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Thu, 4 Jan 2024 15:41:17 -0500 Subject: [PATCH 06/46] Add a "ChatAbort" handler function --- Source/Chatbook/Actions.wl | 23 ++++++++++++++++++----- Source/Chatbook/Main.wl | 1 + Source/Chatbook/Settings.wl | 6 ++++-- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/Source/Chatbook/Actions.wl b/Source/Chatbook/Actions.wl index 577a72cf..65f89449 100644 --- a/Source/Chatbook/Actions.wl +++ b/Source/Chatbook/Actions.wl @@ -417,7 +417,7 @@ EvaluateChatInput[ evalCell_CellObject, nbo_NotebookObject, settings_Association (* Send chat while listening for an abort: *) CheckAbort[ - sendChat[ evalCell, nbo, settings ]; + sendChat[ evalCell, nbo, settings ]; waitForLastTask[ ] , (* The user has issued an abort: *) @@ -442,11 +442,9 @@ EvaluateChatInput[ evalCell_CellObject, nbo_NotebookObject, settings_Association <| "Role" -> "Assistant", "Content" -> $lastChatString |> ] }, - applyHandlerFunction[ settings, "ChatPost", <| "ChatObject" -> chat, "NotebookObject" -> nbo |> ]; - Sow[ chat, $chatObjectTag ] + applyChatPost[ chat, settings, nbo, $aborted ] ], - applyHandlerFunction[ settings, "ChatPost", <| "ChatObject" -> None, "NotebookObject" -> nbo |> ]; - Sow[ None, $chatObjectTag ]; + applyChatPost[ None, settings, nbo, $aborted ]; Null ]; ] @@ -454,6 +452,21 @@ EvaluateChatInput[ evalCell_CellObject, nbo_NotebookObject, settings_Association EvaluateChatInput // endDefinition; +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*applyChatPost*) +applyChatPost // beginDefinition; + +applyChatPost[ chat_, settings_, nbo_, aborted: True|False ] := ( + If[ aborted, + applyHandlerFunction[ settings, "ChatAbort", <| "ChatObject" -> chat, "NotebookObject" -> nbo |> ], + applyHandlerFunction[ settings, "ChatPost" , <| "ChatObject" -> chat, "NotebookObject" -> nbo |> ] + ]; + Sow[ chat, $chatObjectTag ] +); + +applyChatPost // endDefinition; + (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*blockChatObject*) diff --git a/Source/Chatbook/Main.wl b/Source/Chatbook/Main.wl index 5f1baeca..abef38ed 100644 --- a/Source/Chatbook/Main.wl +++ b/Source/Chatbook/Main.wl @@ -7,6 +7,7 @@ BeginPackage[ "Wolfram`Chatbook`" ]; (* ::Subsection::Closed:: *) (*Declare Symbols*) `$AvailableTools; +`$ChatAbort; `$ChatHandlerData; `$ChatPost; `$ChatPre; diff --git a/Source/Chatbook/Settings.wl b/Source/Chatbook/Settings.wl index 3e36c89f..7948362c 100644 --- a/Source/Chatbook/Settings.wl +++ b/Source/Chatbook/Settings.wl @@ -71,6 +71,7 @@ $$validRootSettingValue = Inherited | _? (AssociationQ@*Association); (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*Defaults*) +$ChatAbort = None; $ChatPost = None; $ChatPre = None; $DefaultModel = <| "Service" -> "OpenAI", "Name" -> "gpt-4" |>; @@ -79,8 +80,9 @@ $DefaultModel = <| "Service" -> "OpenAI", "Name" -> "gpt-4" |>; (* ::Subsection::Closed:: *) (*Handler Functions*) $DefaultChatHandlerFunctions = <| - "ChatPost" :> $ChatPost, - "ChatPre" :> $ChatPre + "ChatAbort" :> $ChatAbort, + "ChatPost" :> $ChatPost, + "ChatPre" :> $ChatPre |>; (* ::**************************************************************************************************************:: *) From cc5d3118aa98e96cd97ce8c848e48e5b206679b4 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Mon, 8 Jan 2024 13:02:58 -0500 Subject: [PATCH 07/46] Increment paclet version --- PacletInfo.wl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PacletInfo.wl b/PacletInfo.wl index 15559d3e..53345f9f 100644 --- a/PacletInfo.wl +++ b/PacletInfo.wl @@ -1,7 +1,7 @@ PacletObject[ <| "Name" -> "Wolfram/Chatbook", "PublisherID" -> "Wolfram", - "Version" -> "1.4.0", + "Version" -> "1.4.1", "WolframVersion" -> "13.3+", "Description" -> "Wolfram Notebooks + LLMs", "License" -> "MIT", From 28e9c96dd32071b86a3e20945c3bdcd1c4137a89 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Mon, 8 Jan 2024 13:35:50 -0500 Subject: [PATCH 08/46] Optimization: full chat settings were getting embedded into the dynamic chat output cell --- Source/Chatbook/SendChat.wl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Chatbook/SendChat.wl b/Source/Chatbook/SendChat.wl index 9bb62717..4d8cfb19 100644 --- a/Source/Chatbook/SendChat.wl +++ b/Source/Chatbook/SendChat.wl @@ -1749,7 +1749,7 @@ activeAIAssistantCell[ Editable -> False, CellDingbat -> Cell[ BoxData @ makeActiveOutputDingbat @ settings, Background -> None ], CellTrayWidgets -> <| "ChatFeedback" -> <| "Visible" -> False |> |>, - TaggingRules -> <| "ChatNotebookSettings" -> settings |> + TaggingRules -> <| "ChatNotebookSettings" -> smallSettings @ settings |> ] ] ]; @@ -1802,7 +1802,7 @@ activeAIAssistantCell[ Selectable -> True, ShowAutoSpellCheck -> False, ShowCursorTracker -> False, - TaggingRules -> <| "ChatNotebookSettings" -> settings |>, + TaggingRules -> <| "ChatNotebookSettings" -> smallSettings @ settings |>, If[ scrollOutputQ @ settings, PrivateCellOptions -> { "TrackScrollingWhenPlaced" -> True }, Sequence @@ { } @@ -1896,8 +1896,10 @@ makeActiveOutputDingbat // endDefinition; (* ::Subsubsection::Closed:: *) (*makeOutputDingbat*) makeOutputDingbat // beginDefinition; +makeOutputDingbat[ as: KeyValuePattern[ "LLMEvaluator" -> name_String ] ] := makeOutputDingbat[ as, name ]; makeOutputDingbat[ as: KeyValuePattern[ "LLMEvaluator" -> config_Association ] ] := makeOutputDingbat[ as, config ]; makeOutputDingbat[ as_Association ] := makeOutputDingbat[ as, as ]; +makeOutputDingbat[ as_, name_String ] := makeOutputDingbat[ as, GetCachedPersonaData @ name ]; makeOutputDingbat[ as_, KeyValuePattern[ "PersonaIcon" -> icon_ ] ] := makeOutputDingbat[ as, icon ]; makeOutputDingbat[ as_, KeyValuePattern[ "Icon" -> icon_ ] ] := makeOutputDingbat[ as, icon ]; makeOutputDingbat[ as_, KeyValuePattern[ "Default" -> icon_ ] ] := makeOutputDingbat[ as, icon ]; From 4e416f2f956c62c3a6976853c8012f2908d2082b Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Mon, 8 Jan 2024 14:09:16 -0500 Subject: [PATCH 09/46] Do not include other types of generated cells that appear after the chat input in chat context --- Source/Chatbook/SendChat.wl | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Source/Chatbook/SendChat.wl b/Source/Chatbook/SendChat.wl index 9bb62717..fe59c105 100644 --- a/Source/Chatbook/SendChat.wl +++ b/Source/Chatbook/SendChat.wl @@ -1124,7 +1124,7 @@ selectChatCells0[ cell_, cells: { __CellObject }, final_ ] := Enclose[ If[ filtered === { }, throwTop @ Null ]; (* Delete output cells that come after the evaluation cell *) - rest = deleteExistingChatOutputs @ Drop[ cellData, cellPosition ]; + rest = keepValidGeneratedCells @ Drop[ cellData, cellPosition ]; (* Get the selected cell objects from the filtered cell info *) selectedCells = ConfirmMatch[ @@ -1147,8 +1147,8 @@ selectChatCells0 // endDefinition; (* ::**************************************************************************************************************:: *) (* ::Subsubsection::Closed:: *) -(*deleteExistingChatOutputs*) -deleteExistingChatOutputs // beginDefinition; +(*keepValidGeneratedCells*) +keepValidGeneratedCells // beginDefinition; (* When chat is triggered by an evaluation instead of a chat input, there can be generated cells between the evaluation cell and the previous chat output. For example: @@ -1160,10 +1160,10 @@ deleteExistingChatOutputs // beginDefinition; At this point, the front end has already cleared previous output cells (messages, output, etc.), but the previous chat output cell is still there. We need to delete it so that the new chat output cell can be inserted in its place. - `deleteExistingChatOutputs` gathers up all the generated cells that come after the evaluation cell and if it finds + `keepValidGeneratedCells` gathers up all the generated cells that come after the evaluation cell and if it finds a chat output cell, it deletes it. *) -deleteExistingChatOutputs[ cellData: { KeyValuePattern[ "CellObject" -> _CellObject ] ... } ] /; $autoAssistMode := +keepValidGeneratedCells[ cellData: { KeyValuePattern[ "CellObject" -> _CellObject ] ... } ] /; $autoAssistMode := Module[ { delete, chatOutputs, cells }, delete = TakeWhile[ cellData, MatchQ @ KeyValuePattern[ "CellAutoOverwrite" -> True ] ]; chatOutputs = Cases[ delete, KeyValuePattern[ "Style" -> $$chatOutputStyle ] ]; @@ -1172,10 +1172,11 @@ deleteExistingChatOutputs[ cellData: { KeyValuePattern[ "CellObject" -> _CellObj DeleteCases[ delete, KeyValuePattern[ "CellObject" -> Alternatives @@ cells ] ] ]; -deleteExistingChatOutputs[ cellData_ ] := - TakeWhile[ cellData, MatchQ @ KeyValuePattern[ "CellAutoOverwrite" -> True ] ]; +(* When chat is triggered by a normal chat input evaluation, we only want to keep the next chat output if it exists: *) +keepValidGeneratedCells[ cellData_ ] := + TakeWhile[ cellData, MatchQ @ KeyValuePattern @ { "CellAutoOverwrite" -> True, "Style" -> $$chatOutputStyle } ]; -deleteExistingChatOutputs // endDefinition; +keepValidGeneratedCells // endDefinition; (* ::**************************************************************************************************************:: *) (* ::Subsubsection::Closed:: *) From 91330db1248ddf9bf216ba6ad0c36b1a02b93c89 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Mon, 8 Jan 2024 14:39:51 -0500 Subject: [PATCH 10/46] Preserve order of cells when inserting via code block button tray --- Source/Chatbook/Formatting.wl | 57 +++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/Source/Chatbook/Formatting.wl b/Source/Chatbook/Formatting.wl index 22eb9a25..5e3164e2 100644 --- a/Source/Chatbook/Formatting.wl +++ b/Source/Chatbook/Formatting.wl @@ -73,6 +73,8 @@ $externalLanguageRules = Replace[ $$mdRow = Except[ "\n" ].. ~~ Repeated[ ("|" ~~ Except[ "\n" ]..), { 2, Infinity } ] ~~ ("\n"|EndOfString); $$mdTable = $$mdRow ~~ $$mdRow ..; +$chatGeneratedCellTag = "ChatGeneratedCell"; + (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*Chat Output Formatting*) @@ -444,8 +446,7 @@ insertCodeBelow[ cell_Cell, evaluate_ ] := Module[ { cellObj, nbo }, cellObj = topParentCell @ EvaluationCell[ ]; nbo = parentNotebook @ cellObj; - SelectionMove[ cellObj, After, Cell ]; - NotebookWrite[ nbo, stripMarkdownBoxes @ cell, All ]; + insertAfterChatGeneratedCells[ cellObj, cell ]; If[ TrueQ @ evaluate, selectionEvaluateCreateCell @ nbo, SelectionMove[ nbo, After, CellContents ] @@ -457,6 +458,35 @@ insertCodeBelow[ string_String, evaluate_ ] := insertCodeBelow // endDefinition; +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*insertAfterChatGeneratedCells*) +insertAfterChatGeneratedCells // beginDefinition; + +insertAfterChatGeneratedCells[ cellObj_CellObject, cell_Cell ] := Enclose[ + Module[ { nbo, allCells, cellsAfter, tagged, inserted, insertionPoint }, + + nbo = ConfirmMatch[ parentNotebook @ cellObj, _NotebookObject, "ParentNotebook" ]; + allCells = ConfirmMatch[ Cells @ nbo, { __CellObject }, "AllCells" ]; + cellsAfter = Replace[ allCells, { { ___, cellObj, after___ } :> { after }, _ :> { } } ]; + + tagged = ConfirmBy[ + AssociationThread[ cellsAfter -> Flatten @* List /@ CurrentValue[ cellsAfter, CellTags ] ], + AssociationQ, + "Tagged" + ]; + + inserted = ConfirmBy[ TakeWhile[ tagged, MemberQ[ $chatGeneratedCellTag ] ], AssociationQ, "Inserted" ]; + insertionPoint = ConfirmMatch[ Last[ Keys @ inserted, cellObj ], _CellObject, "InsertionPoint" ]; + + SelectionMove[ insertionPoint, After, Cell ]; + NotebookWrite[ nbo, preprocessInsertedCell @ cell, All ]; + ], + throwInternalFailure +]; + +insertAfterChatGeneratedCells // endDefinition; + (* ::**************************************************************************************************************:: *) (* ::Subsubsection::Closed:: *) (*copyCode*) @@ -465,6 +495,29 @@ copyCode[ cell_CellObject ] := copyCode @ getCodeBlockContent @ cell; copyCode[ code: _Cell|_String ] := CopyToClipboard @ stripMarkdownBoxes @ code; copyCode // endDefinition; +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*preprocessInsertedCell*) +preprocessInsertedCell // beginDefinition; +preprocessInsertedCell[ cell_ ] := addInsertedCellTags @ stripMarkdownBoxes @ cell; +preprocessInsertedCell // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*addInsertedCellTags*) +addInsertedCellTags // beginDefinition; + +addInsertedCellTags[ Cell[ a__, CellTags -> tag_String, b___ ] ] := + addInsertedCellTags @ Cell[ a, CellTags -> { tag }, b ]; + +addInsertedCellTags[ Cell[ a__, CellTags -> { tags___String }, b___ ] ] := + Cell[ a, CellTags -> DeleteDuplicates @ { $chatGeneratedCellTag, tags }, b ]; + +addInsertedCellTags[ Cell[ a: Except[ CellTags -> _ ].. ] ] := + Cell[ a, CellTags -> { $chatGeneratedCellTag } ]; + +addInsertedCellTags // endDefinition; + (* ::**************************************************************************************************************:: *) (* ::Subsubsection::Closed:: *) (*stripMarkdownBoxes*) From 84c12bf9d62eb134c99180d5a4524bc98750e20e Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Wed, 10 Jan 2024 12:49:18 -0500 Subject: [PATCH 11/46] Support setting global chat preferences in cloud by storing in a local file rather than `$FrontEnd` --- Source/Chatbook/Settings.wl | 240 +++++++++++++++++++++++++++++++----- 1 file changed, 208 insertions(+), 32 deletions(-) diff --git a/Source/Chatbook/Settings.wl b/Source/Chatbook/Settings.wl index 7948362c..cb41d495 100644 --- a/Source/Chatbook/Settings.wl +++ b/Source/Chatbook/Settings.wl @@ -12,9 +12,10 @@ HoldComplete[ Begin[ "`Private`" ]; -Needs[ "Wolfram`Chatbook`" ]; -Needs[ "Wolfram`Chatbook`Common`" ]; -Needs[ "Wolfram`Chatbook`FrontEnd`" ]; +Needs[ "Wolfram`Chatbook`" ]; +Needs[ "Wolfram`Chatbook`Common`" ]; +Needs[ "Wolfram`Chatbook`FrontEnd`" ]; +Needs[ "Wolfram`Chatbook`ResourceInstaller`" ]; (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) @@ -63,10 +64,13 @@ $defaultChatSettings = <| "TrackScrollingWhenPlaced" -> Automatic |>; +$cachedGlobalSettings := $cachedGlobalSettings = getGlobalSettingsFile[ ]; + (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*Argument Patterns*) $$validRootSettingValue = Inherited | _? (AssociationQ@*Association); +$$frontEndObject = HoldPattern[ $FrontEnd | _FrontEndObject ]; (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) @@ -153,78 +157,238 @@ CurrentChatSettings[ args___ ] := (* ::Subsection::Closed:: *) (*UpValues*) CurrentChatSettings /: HoldPattern @ Set[ CurrentChatSettings[ args___ ], value_ ] := - catchTop[ UsingFrontEnd @ setCurrentChatSettings[ args, value ], CurrentChatSettings ]; + catchTop[ setCurrentChatSettings[ args, value ], CurrentChatSettings ]; CurrentChatSettings /: HoldPattern @ Unset[ CurrentChatSettings[ args___ ] ] := - catchTop[ UsingFrontEnd @ unsetCurrentChatSettings @ args, CurrentChatSettings ]; + catchTop[ unsetCurrentChatSettings @ args, CurrentChatSettings ]; (* ::**************************************************************************************************************:: *) (* ::Subsubsection::Closed:: *) (*setCurrentChatSettings*) setCurrentChatSettings // beginDefinition; +setCurrentChatSettings[ args___ ] /; $CloudEvaluation := setCurrentChatSettings0 @ args; +setCurrentChatSettings[ args___ ] := UsingFrontEnd @ setCurrentChatSettings0 @ args; +setCurrentChatSettings // endDefinition; + + +setCurrentChatSettings0 // beginDefinition; (* Root settings: *) -setCurrentChatSettings[ value: $$validRootSettingValue ] := - setCurrentChatSettings0[ $FrontEnd, value ]; +setCurrentChatSettings0[ value: $$validRootSettingValue ] := + setCurrentChatSettings1[ $FrontEnd, value ]; -setCurrentChatSettings[ obj: $$feObj, value: $$validRootSettingValue ] := - setCurrentChatSettings0[ obj, value ]; +setCurrentChatSettings0[ obj: $$feObj, value: $$validRootSettingValue ] := + setCurrentChatSettings1[ obj, value ]; (* Key settings: *) -setCurrentChatSettings[ key_String? StringQ, value_ ] := - setCurrentChatSettings0[ $FrontEnd, key, value ]; +setCurrentChatSettings0[ key_String? StringQ, value_ ] := + setCurrentChatSettings1[ $FrontEnd, key, value ]; -setCurrentChatSettings[ obj: $$feObj, key_String? StringQ, value_ ] := - setCurrentChatSettings0[ obj, key, value ]; +setCurrentChatSettings0[ obj: $$feObj, key_String? StringQ, value_ ] := + setCurrentChatSettings1[ obj, key, value ]; (* Invalid scope: *) -setCurrentChatSettings[ obj: Except[ $$feObj ], a__ ] := throwFailure[ +setCurrentChatSettings0[ obj: Except[ $$feObj ], a__ ] := throwFailure[ "InvalidFrontEndScope", obj, CurrentChatSettings, - HoldForm @ setCurrentChatSettings[ obj, a ] + HoldForm @ setCurrentChatSettings0[ obj, a ] ]; (* Invalid key: *) -setCurrentChatSettings[ obj: $$feObj, key_, value_ ] := throwFailure[ +setCurrentChatSettings0[ obj: $$feObj, key_, value_ ] := throwFailure[ "InvalidSettingsKey", key, CurrentChatSettings, - HoldForm @ setCurrentChatSettings[ obj, key, value ] + HoldForm @ setCurrentChatSettings0[ obj, key, value ] ]; (* Invalid root settings: *) -setCurrentChatSettings[ value: Except[ $$validRootSettingValue ] ] := throwFailure[ +setCurrentChatSettings0[ value: Except[ $$validRootSettingValue ] ] := throwFailure[ "InvalidRootSettings", value, CurrentChatSettings, - HoldForm @ setCurrentChatSettings @ value + HoldForm @ setCurrentChatSettings0 @ value ]; -setCurrentChatSettings[ obj: $$feObj, value: Except[ $$validRootSettingValue ] ] := throwFailure[ +setCurrentChatSettings0[ obj: $$feObj, value: Except[ $$validRootSettingValue ] ] := throwFailure[ "InvalidRootSettings", value, CurrentChatSettings, - HoldForm @ setCurrentChatSettings @ value + HoldForm @ setCurrentChatSettings0 @ value ]; -setCurrentChatSettings // endDefinition; +setCurrentChatSettings0 // endDefinition; -setCurrentChatSettings0 // beginDefinition; +setCurrentChatSettings1 // beginDefinition; + +setCurrentChatSettings1[ scope: $$feObj, Inherited ] := + If[ TrueQ @ $CloudEvaluation, + setCurrentChatSettingsCloud[ scope, Inherited ], + CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings" } ] = Inherited + ]; + +setCurrentChatSettings1[ scope: $$feObj, value_ ] := + With[ { as = Association @ value }, + If[ TrueQ @ $CloudEvaluation, + setCurrentChatSettingsCloud[ scope, as ], + CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings" } ] = as + ] /; AssociationQ @ as + ]; + +setCurrentChatSettings1[ scope: $$feObj, key_String? StringQ, value_ ] := + If[ TrueQ @ $CloudEvaluation, + setCurrentChatSettingsCloud[ scope, key, value ], + CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings", key } ] = value + ]; + +setCurrentChatSettings1 // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*setCurrentChatSettingsCloud*) +setCurrentChatSettingsCloud // beginDefinition; + +setCurrentChatSettingsCloud[ scope: $$frontEndObject, value_ ] := + With[ { as = Association @ value }, + ( + setGlobalChatSettings @ as; + CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings" } ] = as + ) /; AssociationQ @ as + ]; -setCurrentChatSettings0[ scope: $$feObj, Inherited ] := - CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings" } ] = Inherited; +setCurrentChatSettingsCloud[ scope: $$frontEndObject, Inherited ] := ( + setGlobalChatSettings @ Inherited; + CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings" } ] = Inherited +); + +setCurrentChatSettingsCloud[ scope: $$frontEndObject, key_String? StringQ, value_ ] := ( + setGlobalChatSettings[ key, value ]; + Needs[ "GeneralUtilities`" -> None ]; + CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings", key } ] = value; + CurrentValue[ scope, TaggingRules ] = GeneralUtilities`ToAssociations @ CurrentValue[ scope, TaggingRules ]; + value +); -setCurrentChatSettings0[ scope: $$feObj, value_ ] := +setCurrentChatSettingsCloud[ scope: $$feObj, value_ ] := With[ { as = Association @ value }, (CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings" } ] = as) /; AssociationQ @ as ]; -setCurrentChatSettings0[ scope: $$feObj, key_String? StringQ, value_ ] := +setCurrentChatSettingsCloud[ scope: $$feObj, Inherited ] := ( + CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings" } ] = Inherited +); + +setCurrentChatSettingsCloud[ scope: $$feObj, key_String? StringQ, value_ ] := ( + Needs[ "GeneralUtilities`" -> None ]; CurrentValue[ scope, { TaggingRules, "ChatNotebookSettings", key } ] = value; + CurrentValue[ scope, TaggingRules ] = GeneralUtilities`ToAssociations @ CurrentValue[ scope, TaggingRules ]; + value +); -setCurrentChatSettings0 // endDefinition; +setCurrentChatSettingsCloud // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*setGlobalChatSettings*) +setGlobalChatSettings // beginDefinition; + +setGlobalChatSettings[ Inherited ] := + storeGlobalSettings @ <| |>; + +setGlobalChatSettings[ settings_Association? AssociationQ ] := + storeGlobalSettings @ settings; + +setGlobalChatSettings[ key_String? StringQ, Inherited ] := Enclose[ + Module[ { settings }, + settings = ConfirmBy[ getGlobalSettingsFile[ ], AssociationQ, "ReadSettings" ]; + KeyDropFrom[ settings, key ]; + ConfirmBy[ storeGlobalSettings @ settings, StringQ, "StoreSettings" ]; + $cachedGlobalSettings = settings; + Inherited + ], + throwInternalFailure +]; + +setGlobalChatSettings[ key_String? StringQ, value_ ] := Enclose[ + Module[ { settings }, + settings = ConfirmBy[ getGlobalSettingsFile[ ], AssociationQ, "ReadSettings" ]; + settings[ key ] = value; + ConfirmBy[ storeGlobalSettings @ settings, StringQ, "StoreSettings" ]; + $cachedGlobalSettings = settings; + value + ], + throwInternalFailure +]; + +setGlobalChatSettings // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*getGlobalChatSettings*) +getGlobalChatSettings // beginDefinition; + +getGlobalChatSettings[ ] := + mergeChatSettings @ Flatten @ { $defaultChatSettings, getGlobalSettingsFile[ ] }; + +getGlobalChatSettings[ key_String? StringQ ] := + getGlobalChatSettings[ getGlobalChatSettings[ ], key ]; + +getGlobalChatSettings[ settings_Association? AssociationQ, key_String? StringQ ] := + Lookup[ settings, key, Lookup[ $defaultChatSettings, key, Inherited ] ]; + +getGlobalChatSettings // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*getGlobalSettingsFile*) +getGlobalSettingsFile // beginDefinition; + +getGlobalSettingsFile[ ] := Enclose[ + Module[ { file }, + file = ConfirmBy[ $globalSettingsFile, StringQ, "GlobalSettingsFile" ]; + $cachedGlobalSettings = + If[ FileExistsQ @ file, + ConfirmBy[ Developer`ReadWXFFile @ file, AssociationQ, "ReadSettings" ], + <| |> + ] + ], + Function[ + Quiet @ DeleteFile @ $globalSettingsFile; + throwInternalFailure[ getGlobalSettingsFile[ ], ## ] + ] +]; + +getGlobalSettingsFile // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*storeGlobalSettings*) +storeGlobalSettings // beginDefinition; + +storeGlobalSettings[ settings_Association? AssociationQ ] := Enclose[ + Module[ { file }, + file = ConfirmBy[ $globalSettingsFile, StringQ, "GlobalSettingsFile" ]; + ConfirmBy[ Developer`WriteWXFFile[ file, settings ], StringQ, "StoreSettings" ]; + $cachedGlobalSettings = settings; + file + ], + throwInternalFailure +]; + +storeGlobalSettings // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*$globalSettingsFile*) +$globalSettingsFile := Enclose[ + Module[ { dir }, + dir = ConfirmBy[ $ResourceInstallationDirectory, DirectoryQ, "ResourceInstallationDirectory" ]; + $globalSettingsFile = ConfirmBy[ FileNameJoin @ { dir, "GlobalChatSettings.wxf" }, StringQ, "File" ] + ], + throwInternalFailure +]; (* ::**************************************************************************************************************:: *) (* ::Subsubsection::Closed:: *) @@ -259,6 +423,7 @@ unsetCurrentChatSettings // endDefinition; unsetCurrentChatSettings0 // beginDefinition; +(* FIXME: make this work in cloud *) unsetCurrentChatSettings0[ obj: $$feObj ] := (CurrentValue[ obj, { TaggingRules, "ChatNotebookSettings" } ] = Inherited); @@ -272,6 +437,12 @@ unsetCurrentChatSettings0 // endDefinition; (*currentChatSettings*) currentChatSettings // beginDefinition; +currentChatSettings[ fe: $$frontEndObject ] /; $cloudNotebooks := + getGlobalChatSettings[ ]; + +currentChatSettings[ fe: $$frontEndObject, key_String ] /; $cloudNotebooks := + getGlobalChatSettings[ key ]; + currentChatSettings[ obj: _NotebookObject|_FrontEndObject|$FrontEndSession ] := ( verifyInheritance @ obj; currentChatSettings0 @ obj @@ -312,7 +483,11 @@ currentChatSettings[ cell0_CellObject ] := Catch @ Enclose[ AssociationQ ]; - ConfirmBy[ mergeChatSettings @ Flatten @ { $defaultChatSettings, settings }, AssociationQ, "CombinedSettings" ] + ConfirmBy[ + mergeChatSettings @ Flatten @ { $defaultChatSettings, $cachedGlobalSettings, settings }, + AssociationQ, + "CombinedSettings" + ] ], throwInternalFailure[ currentChatSettings @ cell0, ## ] & ]; @@ -347,7 +522,7 @@ currentChatSettings[ cell0_CellObject, key_String ] := Catch @ Enclose[ ! MemberQ[ cells, cell ], (*It's not in the list of cells*) MatchQ[ CurrentValue[ nbo, DefaultNewCellStyle ], $$chatInputStyle ] (*Due to DefaultNewCellStyle*) ], - Throw @ Lookup[ $defaultChatSettings, key, Inherited ] + Throw @ Lookup[ $cachedGlobalSettings, key, Lookup[ $defaultChatSettings, key, Inherited ] ] ]; delimiter = ConfirmMatch[ getPrecedingDelimiter[ cell, nbo, cells ], _CellObject|_Missing, "Delimiter" ]; @@ -360,7 +535,7 @@ currentChatSettings[ cell0_CellObject, key_String ] := Catch @ Enclose[ Except[ Inherited ], Replace[ absoluteCurrentValue[ cell, { TaggingRules, "ChatNotebookSettings", key } ], - Inherited :> Lookup[ $defaultChatSettings, key, Inherited ] + Inherited :> Lookup[ $cachedGlobalSettings, key, Lookup[ $defaultChatSettings, key, Inherited ] ] ] ] ], @@ -375,6 +550,7 @@ currentChatSettings0 // beginDefinition; currentChatSettings0[ obj: _CellObject|_NotebookObject|_FrontEndObject|$FrontEndSession ] := Association[ $defaultChatSettings, + $cachedGlobalSettings, Replace[ Association @ absoluteCurrentValue[ obj, { TaggingRules, "ChatNotebookSettings" } ], Except[ _? AssociationQ ] :> <| |> @@ -383,7 +559,7 @@ currentChatSettings0[ obj: _CellObject|_NotebookObject|_FrontEndObject|$FrontEnd currentChatSettings0[ obj: _CellObject|_NotebookObject|_FrontEndObject|$FrontEndSession, key_String ] := Replace[ absoluteCurrentValue[ obj, { TaggingRules, "ChatNotebookSettings", key } ], - Inherited :> Lookup[ $defaultChatSettings, key, Inherited ] + Inherited :> Lookup[ $cachedGlobalSettings, key, Lookup[ $defaultChatSettings, key, Inherited ] ] ]; currentChatSettings0 // endDefinition; From bbf26abf486eb40640c023a80731dd51780732b0 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Wed, 10 Jan 2024 12:54:15 -0500 Subject: [PATCH 12/46] Misc fixes to support preferences content in cloud --- Source/Chatbook/PreferencesContent.wl | 179 ++++++++++++++++---------- 1 file changed, 111 insertions(+), 68 deletions(-) diff --git a/Source/Chatbook/PreferencesContent.wl b/Source/Chatbook/PreferencesContent.wl index 45771a8b..7a1fddd4 100644 --- a/Source/Chatbook/PreferencesContent.wl +++ b/Source/Chatbook/PreferencesContent.wl @@ -3,10 +3,12 @@ BeginPackage[ "Wolfram`Chatbook`PreferencesContent`" ]; HoldComplete[ + `$cloudEvaluationNotebook; `$preferencesScope; `createPreferencesContent; `makeModelSelector; `makePersonaSelector; + `notebookSettingsPanel; `openPreferencesPage; ]; @@ -28,6 +30,8 @@ Needs[ "Wolfram`Chatbook`UI`" ]; (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*Configuration*) +$cloudEvaluationNotebook = None; + $preferencesPages = { "Notebooks", "Services", "Personas", "Tools" }; $$preferencesPage = Alternatives @@ $preferencesPages; @@ -97,16 +101,9 @@ scopedTrackedDynamic // endDefinition; (*currentTabPageDynamic*) currentTabPageDynamic // beginDefinition; -currentTabPageDynamic[ scope_FrontEndObject ] := Dynamic @ CurrentValue[ - $FrontEnd, - { PrivateFrontEndOptions, "DialogSettings", "Preferences", "TabSettings", "AI", "Top" }, - "Notebooks" -]; - -currentTabPageDynamic[ scope_ ] := Dynamic @ CurrentValue[ - scope, - { TaggingRules, "ChatNotebookSettings", "CurrentPreferencesTab" }, - "Notebooks" +currentTabPageDynamic[ scope_ ] := Dynamic[ + Replace[ CurrentChatSettings[ scope, "CurrentPreferencesTab" ], $$unspecified -> "Notebooks" ], + (CurrentChatSettings[ scope, "CurrentPreferencesTab" ] = #1) & ]; currentTabPageDynamic // endDefinition; @@ -116,16 +113,9 @@ currentTabPageDynamic // endDefinition; (*currentTabPage*) currentTabPage // beginDefinition; -currentTabPage[ scope_FrontEndObject ] := CurrentValue[ - $FrontEnd, - { PrivateFrontEndOptions, "DialogSettings", "Preferences", "TabSettings", "AI", "Top" }, - "Notebooks" -]; - -currentTabPage[ scope_ ] := CurrentValue[ - scope, - { TaggingRules, "ChatNotebookSettings", "CurrentPreferencesTab" }, - "Notebooks" +currentTabPage[ scope_ ] := Replace[ + CurrentChatSettings[ scope, "CurrentPreferencesTab" ], + $$unspecified -> "Notebooks" ]; currentTabPage // endDefinition; @@ -256,7 +246,7 @@ createNotebookSettingsPanel[ ] := Enclose[ ]; (* Label for the interface section using a style from SystemDialog.nb: *) - interfaceLabel = Style[ "Chat Notebook Cells", "subsectionText" ]; + interfaceLabel = subsectionText[ "Chat Notebook Cells" ]; (* Retrieve and confirm the content for the chat notebook interface, ensuring it is not an error from makeInterfaceContent: *) @@ -267,7 +257,7 @@ createNotebookSettingsPanel[ ] := Enclose[ ]; (* Label for the features section using a style from SystemDialog.nb: *) - featuresLabel = Style[ "Features", "subsectionText" ]; + featuresLabel = subsectionText[ "Features" ]; (* Retrieve and confirm the content for the chat notebook features, ensuring it is not an error from makeFeaturesContent: *) @@ -312,7 +302,7 @@ makeDefaultSettingsContent // beginDefinition; makeDefaultSettingsContent[ ] := Enclose[ Module[ { assistanceCheckbox, personaSelector, modelSelector, temperatureInput, openAICompletionURLInput }, (* Checkbox to enable automatic assistance for normal shift-enter evaluations: *) - assistanceCheckbox = ConfirmMatch[ makeAssistanceCheckbox[ ], _Style, "AssistanceCheckbox" ]; + assistanceCheckbox = ConfirmMatch[ makeAssistanceCheckbox[ ], _Style|Nothing, "AssistanceCheckbox" ]; (* The personaSelector is a pop-up menu for selecting the default persona: *) personaSelector = ConfirmMatch[ makePersonaSelector[ ], _Dynamic, "PersonaSelector" ]; (* The modelSelector is a dynamic module containing menus to select the service and model separately: *) @@ -373,10 +363,8 @@ makePersonaSelector0[ personas: { (_String -> _).. } ] := scopedDynamic[ CurrentChatSettings[ $preferencesScope, "LLMEvaluator" ], Function[ - CurrentValue[ - $preferencesScope, - { TaggingRules, "ChatNotebookSettings", "LLMEvaluator" } - ] = #1 + CurrentChatSettings[ $preferencesScope, "LLMEvaluator" ] = #; + refreshCloudToolbar[ ] ] ], personas @@ -388,6 +376,32 @@ makePersonaSelector0[ personas: { (_String -> _).. } ] := makePersonaSelector0 // endDefinition; +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*refreshCloudToolbar*) +refreshCloudToolbar // beginDefinition; + +refreshCloudToolbar[ ] := + If[ TrueQ @ $CloudEvaluation, + refreshCloudToolbar[ EvaluationNotebook[ ], $cloudEvaluationNotebook ] + ]; + +refreshCloudToolbar[ nbo_NotebookObject, nbo_NotebookObject ] := + Null; + +refreshCloudToolbar[ _, None ] := + Null; + +refreshCloudToolbar[ _NotebookObject, nbo_NotebookObject ] := + Module[ { dc }, + dc = Replace[ CurrentValue[ nbo, DockedCells ], Except[ Inherited | _List ] :> Inherited ]; + SetOptions[ nbo, DockedCells -> { } ]; + Pause[ 1 ]; + SetOptions[ nbo, DockedCells -> dc ]; + ]; + +refreshCloudToolbar // endDefinition; + (* ::**************************************************************************************************************:: *) (* ::Subsubsubsection::Closed:: *) (*personaPopupLabel*) @@ -417,7 +431,7 @@ makeModelSelector0[ ] := makeModelSelector0[ services_Association? AssociationQ ] := Enclose[ DynamicModule[ { default, service, model, state, serviceSelector, modelSelector, highlight }, - default = currentChatSettings[ $preferencesScope, "Model" ]; + default = CurrentChatSettings[ $preferencesScope, "Model" ]; service = ConfirmBy[ extractServiceName @ default, StringQ, "ServiceName" ]; model = ConfirmBy[ extractModelName @ default , StringQ, "ModelName" ]; state = If[ modelListCachedQ @ service, "Loaded", "Loading" ]; @@ -545,7 +559,9 @@ serviceSelectCallback[ Dynamic @ modelSelector, Dynamic @ state ] - ] + ]; + + refreshCloudToolbar[ ] ]; serviceSelectCallback // endDefinition; @@ -595,7 +611,7 @@ makeModelNameSelector[ fallback = <| "Service" -> service, "Name" -> default |>; If[ ! MemberQ[ models, KeyValuePattern[ "Name" -> current ] ], - CurrentValue[ $preferencesScope, { TaggingRules, "ChatNotebookSettings", "Model" } ] = fallback + CurrentChatSettings[ $preferencesScope, "Model" ] = fallback ]; With[ { m = fallback }, @@ -603,11 +619,7 @@ makeModelNameSelector[ scopedDynamic[ Replace[ extractModelName @ CurrentChatSettings[ $preferencesScope, "Model" ], - { - Except[ _String ] :> ( - CurrentValue[ $preferencesScope, { TaggingRules, "ChatNotebookSettings", "Model" } ] = m - ) - } + Except[ _String ] :> (CurrentChatSettings[ $preferencesScope, "Model" ] = m) ], modelSelectCallback[ Dynamic @ service, Dynamic @ model ] ], @@ -697,13 +709,16 @@ modelSelectCallback[ (* Remember the selected model for the given service, so it will be automatically chosen when choosing this service again: *) - CurrentValue[ $preferencesScope, { TaggingRules, "ChatNotebookSettings", "ServiceDefaultModel", service } ] = model; + CurrentChatSettings[ $preferencesScope, "ServiceDefaultModel" ] = Append[ + CurrentChatSettings[ $preferencesScope, "ServiceDefaultModel" ], + service -> model + ]; (* Store the service/model in FE settings: *) - CurrentValue[ $preferencesScope, { TaggingRules, "ChatNotebookSettings", "Model" } ] = <| - "Service" -> service, - "Name" -> model - |> + CurrentChatSettings[ $preferencesScope, "Model" ] = <| "Service" -> service, "Name" -> model |>; + + updateDynamics[ "Models" ]; + refreshCloudToolbar[ ] , throwInternalFailure ]; @@ -715,20 +730,24 @@ modelSelectCallback // endDefinition; (*makeAssistanceCheckbox*) makeAssistanceCheckbox // beginDefinition; -makeAssistanceCheckbox[ ] := highlightControl[ - prefsCheckbox[ - scopedDynamic[ - TrueQ @ CurrentChatSettings[ $preferencesScope, "Assistance" ], - (CurrentChatSettings[ $preferencesScope, "Assistance" ] = #1) & - ], - infoTooltip[ - "Enable automatic assistance", - "If enabled, automatic AI provided suggestions will be added following evaluation results." +makeAssistanceCheckbox[ ] := + If[ TrueQ @ $cloudNotebooks, + Nothing, + highlightControl[ + prefsCheckbox[ + scopedDynamic[ + TrueQ @ CurrentChatSettings[ $preferencesScope, "Assistance" ], + (CurrentChatSettings[ $preferencesScope, "Assistance" ] = #1) & + ], + infoTooltip[ + "Enable automatic assistance", + "If enabled, automatic AI provided suggestions will be added following evaluation results." + ] + ], + "Notebooks", + "Assistance" ] - ], - "Notebooks", - "Assistance" -]; + ]; makeAssistanceCheckbox // endDefinition; @@ -1087,7 +1106,7 @@ servicesSettingsPanel // beginDefinition; servicesSettingsPanel[ ] := Enclose[ Module[ { settingsLabel, settings, serviceGrid }, - settingsLabel = Style[ "Registered Services", "subsectionText" ]; + settingsLabel = subsectionText[ "Registered Services" ]; settings = ConfirmMatch[ makeModelSelector[ ], _Dynamic, "ServicesSettings" ]; serviceGrid = ConfirmMatch[ makeServiceGrid[ ], _Grid, "ServiceGrid" ]; @@ -1305,6 +1324,14 @@ toolSettingsPanel // endDefinition; (* ::Section::Closed:: *) (*Common*) +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*subsectionText*) +subsectionText // beginDefinition; +subsectionText[ text_ ] /; $CloudEvaluation := Style[ text, "subsectionText", FontSize -> 16, FontWeight -> "DemiBold" ]; +subsectionText[ text_ ] := Style[ text, "subsectionText" ]; +subsectionText // endDefinition; + (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*prefsInputField*) @@ -1342,7 +1369,11 @@ prefsInputField0[ value_Dynamic, type_, opts: OptionsPattern[ ] ] := RawBoxes @ }, "InputFieldAppearance:RoundedFrame" ], - FrameBoxOptions -> { BaselinePosition -> Top -> Scaled[ 1.3 ] } + (* The following FrameBoxOptions do not work well in cloud, so disable if needed: *) + If[ TrueQ @ $CloudEvaluation, + Sequence @@ { }, + FrameBoxOptions -> { BaselinePosition -> Top -> Scaled[ 1.3 ] } + ] ]; prefsInputField0 // endDefinition; @@ -1444,20 +1475,32 @@ extractModelName // endDefinition; (*getServiceDefaultModel*) getServiceDefaultModel // beginDefinition; -getServiceDefaultModel[ selected_String ] := Replace[ - (* Use the last model name that was selected for this service if it exists: *) - CurrentValue[ - $preferencesScope, - { TaggingRules, "ChatNotebookSettings", "ServiceDefaultModel", selected } - ], +getServiceDefaultModel[ service_String ] := Enclose[ + Module[ { lastSelected, name }, + (* Get last selected models by service: *) + lastSelected = Replace[ + Association @ CurrentChatSettings[ $preferencesScope, "ServiceDefaultModel" ], + Except[ _? AssociationQ ] :> <| |> + ]; - (* Otherwise determine a starting model from the registered service: *) - $$unspecified :> ( - CurrentValue[ - $preferencesScope, - { TaggingRules, "ChatNotebookSettings", "ServiceDefaultModel", selected } - ] = chooseDefaultModelName @ selected - ) + (* The last model name that was selected for this service (if it exists): *) + name = ConfirmMatch[ + Lookup[ lastSelected, service, Inherited ], + _String|Automatic|Inherited, + "Name" + ]; + + (* If the service has not been selected before, choose a default model by service name and save it: *) + If[ ! StringQ @ name, + name = ConfirmMatch[ chooseDefaultModelName @ service, _String|Automatic, "DefaultName" ]; + lastSelected[ service ] = name; + CurrentChatSettings[ $preferencesScope, "ServiceDefaultModel" ] = lastSelected; + ]; + + (* Return the name: *) + ConfirmMatch[ name, _String|Automatic, "Result" ] + ], + throwInternalFailure ]; getServiceDefaultModel // endDefinition; From b0f7b070c0801ccb1efa7d91428aa71e8c0e8883 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Wed, 10 Jan 2024 12:54:38 -0500 Subject: [PATCH 13/46] Add preferences button to cloud toolbar --- Source/Chatbook/CloudToolbar.wl | 179 ++++++++++---------------------- 1 file changed, 57 insertions(+), 122 deletions(-) diff --git a/Source/Chatbook/CloudToolbar.wl b/Source/Chatbook/CloudToolbar.wl index b33a91ab..2fb75e9d 100644 --- a/Source/Chatbook/CloudToolbar.wl +++ b/Source/Chatbook/CloudToolbar.wl @@ -34,142 +34,77 @@ $notebookTypeLabelOptions = Sequence[ (*makeChatCloudDockedCellContents*) makeChatCloudDockedCellContents // beginDefinition; -makeChatCloudDockedCellContents[ ] := - Block[ { $preferencesScope := EvaluationNotebook[ ] }, - DynamicWrapper[ - Grid[ - { - { - Item[ $cloudChatBanner, Alignment -> Left ], - Item[ "", ItemSize -> Fit ], - makePersonaSelector[ ], - cloudModelSelector[ ] - } - }, - Alignment -> { Left, Baseline }, - Dividers -> { { False, False, False, True }, False }, - Spacings -> { 2, 0 }, - BaseStyle -> { "Text", FontSize -> 14, FontColor -> GrayLevel[ 0.4 ] }, - FrameStyle -> Directive[ Thickness[ 2 ], GrayLevel[ 0.9 ] ] - ], - Needs[ "GeneralUtilities`" -> None ]; - CurrentValue[ EvaluationNotebook[ ], TaggingRules ] = - GeneralUtilities`ToAssociations @ Replace[ - CurrentValue[ EvaluationNotebook[ ], TaggingRules ], - Except[ KeyValuePattern @ { } ] :> <| |> - ] - ] - ]; +makeChatCloudDockedCellContents[ ] := Grid[ + { + { + Item[ $cloudChatBanner, Alignment -> Left ], + Item[ "", ItemSize -> Fit ], + makePersonaSelector[ ], + cloudModelSelector[ ], + cloudPreferencesButton[ ] + } + }, + Alignment -> { Left, Baseline }, + Dividers -> { { False, False, False, True, True }, False }, + Spacings -> { 2, 0 }, + BaseStyle -> { "Text", FontSize -> 14, FontColor -> GrayLevel[ 0.4 ] }, + FrameStyle -> Directive[ Thickness[ 2 ], GrayLevel[ 0.9 ] ] +]; makeChatCloudDockedCellContents // endDefinition; (* ::**************************************************************************************************************:: *) (* ::Subsubsection::Closed:: *) -(*cloudModelSelector*) -cloudModelSelector // beginDefinition; +(*cloudPreferencesButton*) +cloudPreferencesButton // beginDefinition; + +cloudPreferencesButton[ ] := Enclose[ + Module[ { iconTemplate, colorTemplate, mouseover, buttonIcon, button }, -cloudModelSelector[ ] := - DynamicModule[ { serviceSelector, modelSelector }, - - serviceSelector = PopupMenu[ - Dynamic[ - Replace[ - CurrentValue[ EvaluationNotebook[ ], { TaggingRules, "ChatNotebookSettings", "Model" } ], - { - _String|Inherited :> "OpenAI", - KeyValuePattern[ "Service" -> service_String ] :> service, - _ :> Set[ - CurrentValue[ - EvaluationNotebook[ ], - { TaggingRules, "ChatNotebookSettings", "Model" } - ], - $DefaultModel - ][ "Service" ] - } - ], - Function[ - CurrentValue[ - EvaluationNotebook[ ], - { TaggingRules, "ChatNotebookSettings", "Model", "Service" } - ] = #1; - - CurrentValue[ - EvaluationNotebook[ ], - { TaggingRules, "ChatNotebookSettings", "Model", "Name" } - ] = Automatic; - - cloudModelNameSelector[ Dynamic @ modelSelector, #1 ] - ] - ], - KeyValueMap[ - #1 -> Row @ { inlineTemplateBoxes[ #2[ "Icon" ] ], Spacer[ 1 ], #2[ "Service" ] } &, - $availableServices - ] + iconTemplate = ConfirmMatch[ + chatbookIcon[ "ToolManagerCog", False ], + RawBoxes @ TemplateBox[ { }, __ ], + "IconTemplate" ]; - cloudModelNameSelector[ - Dynamic @ modelSelector, - Replace[ - CurrentValue[ EvaluationNotebook[ ], { TaggingRules, "ChatNotebookSettings", "Model" } ], - { - _String|Inherited :> "OpenAI", - KeyValuePattern[ "Service" -> service_String ] :> service, - _ :> Set[ - CurrentValue[ EvaluationNotebook[ ], { TaggingRules, "ChatNotebookSettings", "Model" } ], - $DefaultModel - ][ "Service" ] - } - ] + colorTemplate = ConfirmMatch[ + Insert[ iconTemplate, #, { 1, 1, 1 } ], + RawBoxes @ TemplateBox[ { _? ColorQ }, __ ], + "Colorize" + ] &; + + mouseover = Mouseover[ + colorTemplate @ GrayLevel[ 0.65 ], + colorTemplate @ Hue[ 0.59, 0.9, 0.93 ] ]; - Row @ { - "LLM Service: ", serviceSelector, - Spacer[ 5 ], - "Model: ", Dynamic @ modelSelector - } - ]; + buttonIcon = DeleteCases[ + mouseover /. HoldPattern[ ImageSize -> _ ] :> ImageSize -> { 22, 22 }, + BaselinePosition -> _, + Infinity + ]; -cloudModelSelector // endDefinition; + button = Button[ + Tooltip[ buttonIcon, "Global chat preferences" ], + $cloudEvaluationNotebook = EvaluationNotebook[ ]; + CreateDialog @ Style[ Dynamic @ notebookSettingsPanel[ ], "Text" ], + Appearance -> "Suppressed", + Method -> "Queued" + ]; + + cloudPreferencesButton[ ] = Pane[ button, FrameMargins -> { { 0, 10 }, { 0, 0 } } ] + ], + throwInternalFailure +]; + +cloudPreferencesButton // endDefinition; (* ::**************************************************************************************************************:: *) (* ::Subsubsection::Closed:: *) -(*cloudModelNameSelector*) -cloudModelNameSelector // beginDefinition; - -cloudModelNameSelector[ Dynamic[ modelSelector_ ], service_String ] := - modelSelector = DynamicModule[ { display, models }, - display = ProgressIndicator[ Appearance -> "Percolate" ]; - Dynamic[ display ], - Initialization :> ( - models = getServiceModelList @ service; - If[ SameQ[ - CurrentValue[ EvaluationNotebook[ ], { TaggingRules, "ChatNotebookSettings", "Model", "Name" } ], - Automatic - ], - CurrentValue[ EvaluationNotebook[ ], { TaggingRules, "ChatNotebookSettings", "Model", "Name" } ] = - First[ models, <| "Name" -> Automatic |> ][ "Name" ] - ]; - - display = PopupMenu[ - Dynamic[ - Replace[ - CurrentChatSettings[ EvaluationNotebook[ ], "Model" ], - { KeyValuePattern[ "Name" -> model_String ] :> model, _ :> Automatic } - ], - Function[ - CurrentValue[ - EvaluationNotebook[ ], - { TaggingRules, "ChatNotebookSettings", "Model", "Name" } - ] = #1 - ] - ], - (#Name -> #DisplayName &) /@ models - ] - ), - SynchronousInitialization -> False - ]; - -cloudModelNameSelector // endDefinition; +(*cloudModelSelector*) +cloudModelSelector // beginDefinition; +cloudModelSelector[ ] := makeModelSelector[ ]; +cloudModelSelector // endDefinition; (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) From 64138ede4f4ac220d336f22c4ba4707a4e376a9a Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Wed, 10 Jan 2024 13:32:06 -0500 Subject: [PATCH 14/46] Bugfix: handle `TaggingRules -> Inherited` in `chatExcludedQ` --- Source/Chatbook/ChatHistory.wl | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Chatbook/ChatHistory.wl b/Source/Chatbook/ChatHistory.wl index 5f8d41c8..e02af223 100644 --- a/Source/Chatbook/ChatHistory.wl +++ b/Source/Chatbook/ChatHistory.wl @@ -186,6 +186,7 @@ chatExcludedQ[ KeyValuePattern[ "Style" -> $$chatIgnoredStyle ] ] := True; chatExcludedQ[ KeyValuePattern[ "ChatNotebookSettings" -> settings_ ] ] := chatExcludedQ @ settings; chatExcludedQ[ KeyValuePattern[ "ExcludeFromChat" -> exclude_ ] ] := TrueQ @ exclude; chatExcludedQ[ KeyValuePattern[ { } ] ] := False; +chatExcludedQ[ Inherited ] := False; chatExcludedQ // endDefinition; From 8953773d5edcb6ea1cbbda63b3492ae237c0eb6f Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Wed, 10 Jan 2024 13:32:41 -0500 Subject: [PATCH 15/46] Bugfix: changes made during `SessionSubmit` in cloud will not trigger dynamic updates --- Source/Chatbook/PreferencesContent.wl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Chatbook/PreferencesContent.wl b/Source/Chatbook/PreferencesContent.wl index 7a1fddd4..9cdb7047 100644 --- a/Source/Chatbook/PreferencesContent.wl +++ b/Source/Chatbook/PreferencesContent.wl @@ -542,7 +542,7 @@ serviceSelectCallback[ (* Finish loading the model name selector: *) If[ state === "Loading", - expandScope @ SessionSubmit[ + expandScope @ If[ $CloudEvaluation, Identity, SessionSubmit ][ Block[ { $scopePlaceholder := $preferencesScope }, modelSelector = makeModelNameSelector[ Dynamic @ service, From 3b8af3b83bb613de3999e81f61ff4bba56d199c1 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Wed, 10 Jan 2024 13:33:48 -0500 Subject: [PATCH 16/46] Add `"BodyChunkProcessed"` to the default set of `HandlerFunctionsKeys` when submitting chat --- Source/Chatbook/SendChat.wl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Source/Chatbook/SendChat.wl b/Source/Chatbook/SendChat.wl index 8eb2e6c7..2721b851 100644 --- a/Source/Chatbook/SendChat.wl +++ b/Source/Chatbook/SendChat.wl @@ -39,7 +39,7 @@ Needs[ "Wolfram`Chatbook`Utils`" ]; (*Configuration*) $resizeDingbats = True; splitDynamicTaskFunction = createFETask; -$defaultHandlerKeys = { "Body", "BodyChunk", "StatusCode", "Task", "TaskStatus", "EventName" }; +$defaultHandlerKeys = { "Body", "BodyChunk", "BodyChunkProcessed", "StatusCode", "TaskStatus", "EventName" }; $chatSubmitDroppedHandlers = { "ChatPost", "ChatPre", "Resolved" }; (* ::**************************************************************************************************************:: *) @@ -492,10 +492,11 @@ chatSubmit // Attributes = { HoldFirst }; chatSubmit[ args__ ] := Quiet[ chatSubmit0 @ args, { - (* cSpell: ignore wname, invm *) + (* cSpell: ignore wname, invm, invk *) ServiceConnections`SavedConnections::wname, ServiceConnections`ServiceConnections::wname, - URLSubmit::invm + URLSubmit::invm, + URLSubmit::invk } ]; From 074cd7d03afb40c7774e4e118291ad0e65ebfd50 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Wed, 10 Jan 2024 13:33:59 -0500 Subject: [PATCH 17/46] Update stylesheet version --- FrontEnd/StyleSheets/Chatbook.nb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FrontEnd/StyleSheets/Chatbook.nb b/FrontEnd/StyleSheets/Chatbook.nb index 6647e054..08341435 100644 --- a/FrontEnd/StyleSheets/Chatbook.nb +++ b/FrontEnd/StyleSheets/Chatbook.nb @@ -1161,7 +1161,7 @@ Notebook[ ], Cell[ StyleData["ChatStyleSheetInformation"], - TaggingRules -> <|"StyleSheetVersion" -> "1.4.0.3913547000"|> + TaggingRules -> <|"StyleSheetVersion" -> "1.4.1.3913792210"|> ], Cell[ StyleData["Text"], @@ -17593,4 +17593,4 @@ Notebook[ ] }, StyleDefinitions -> "PrivateStylesheetFormatting.nb" -] +] \ No newline at end of file From f4684a9c5f34fe1c4ea0e39d552c62dc24696e38 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Thu, 11 Jan 2024 13:13:41 -0500 Subject: [PATCH 18/46] Fix a recursion error that occurs when there's no icon available for a registered service --- Source/Chatbook/Services.wl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Chatbook/Services.wl b/Source/Chatbook/Services.wl index b0d4a7e2..cd561721 100644 --- a/Source/Chatbook/Services.wl +++ b/Source/Chatbook/Services.wl @@ -185,7 +185,7 @@ checkModelList // endDefinition; (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*$availableServices*) -$availableServices := getAvailableServices[ ]; +$availableServices := Block[ { $availableServices = <| |> }, getAvailableServices[ ] ]; (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) From 696e0e913b28085b3b6496a8e65f012463451e88 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Thu, 11 Jan 2024 13:46:17 -0500 Subject: [PATCH 19/46] Debugging a new error message during build --- .github/workflows/Build.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index bbc0a28f..ee2ce336 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -43,4 +43,13 @@ jobs: path: ${{ env.PACLET_BUILD_DIR }} - name: Test - run: wolframscript -f Scripts/TestPaclet.wls \ No newline at end of file + run: wolframscript -f Scripts/TestPaclet.wls + + - name: Upload stack data + if: always() && env.PACLET_STACK_HISTORY + uses: actions/upload-artifact@v3 + with: + name: test-paclet-stacks-${{ env.WOLFRAM_SYSTEM_ID }} + path: ${{ env.PACLET_STACK_HISTORY }} + retention-days: 1 + if-no-files-found: error \ No newline at end of file From 6069a70a6a255dc675a6adf026f7ba9af9b631ca Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Thu, 11 Jan 2024 13:57:22 -0500 Subject: [PATCH 20/46] Ensure paclet directory is already loaded for all scripts --- Scripts/Common.wl | 1 + 1 file changed, 1 insertion(+) diff --git a/Scripts/Common.wl b/Scripts/Common.wl index 60216f26..38d3a304 100644 --- a/Scripts/Common.wl +++ b/Scripts/Common.wl @@ -368,6 +368,7 @@ Print[ "ResourceSystemBase: ", $ResourceSystemBase ]; $defNB = File @ FileNameJoin @ { $pacletDir, "ResourceDefinition.nb" }; Print[ "Definition Notebook: ", $defNB ]; +PacletDirectoryLoad @ $pacletDir; $loadedDefinitions = True; From 2ae28dc7a54677001ac72e892f376ccf91e0addf Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Thu, 11 Jan 2024 14:03:37 -0500 Subject: [PATCH 21/46] Remove debugging artifact --- .github/workflows/Build.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/Build.yml b/.github/workflows/Build.yml index ee2ce336..bbc0a28f 100644 --- a/.github/workflows/Build.yml +++ b/.github/workflows/Build.yml @@ -43,13 +43,4 @@ jobs: path: ${{ env.PACLET_BUILD_DIR }} - name: Test - run: wolframscript -f Scripts/TestPaclet.wls - - - name: Upload stack data - if: always() && env.PACLET_STACK_HISTORY - uses: actions/upload-artifact@v3 - with: - name: test-paclet-stacks-${{ env.WOLFRAM_SYSTEM_ID }} - path: ${{ env.PACLET_STACK_HISTORY }} - retention-days: 1 - if-no-files-found: error \ No newline at end of file + run: wolframscript -f Scripts/TestPaclet.wls \ No newline at end of file From 6e18ea9b49be5d9fa4e545450a8e6e277f141a12 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Thu, 11 Jan 2024 14:30:54 -0500 Subject: [PATCH 22/46] Make sure all subcontexts are in `$Packages` --- Source/Chatbook/Chatbook.wl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Chatbook/Chatbook.wl b/Source/Chatbook/Chatbook.wl index c8b08eff..b77d23b0 100644 --- a/Source/Chatbook/Chatbook.wl +++ b/Source/Chatbook/Chatbook.wl @@ -12,7 +12,16 @@ Quiet[ Unprotect[ "Wolfram`Chatbook`*" ]; ClearAll[ "Wolfram`Chatbook`*" ]; ClearAll[ "Wolfram`Chatbook`*`*" ]; - Get @ Wolfram`ChatbookLoader`$MXFile + Get @ Wolfram`ChatbookLoader`$MXFile; + (* Ensure all subcontexts are in $Packages to avoid reloading subcontexts out of order: *) + WithCleanup[ + Unprotect @ $Packages, + $Packages = DeleteDuplicates @ Join[ + $Packages, + Select[ Contexts[ "Wolfram`Chatbook`*" ], StringFreeQ[ "`Private`" ] ] + ], + Protect @ $Packages + ] , WithCleanup[ PreemptProtect[ From daf0d1558e28e59011f8a255991c7c884127684a Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Thu, 11 Jan 2024 14:46:47 -0500 Subject: [PATCH 23/46] Build list of contexts into the MX file, since `Contexts[...]` is expensive --- Source/Chatbook/Chatbook.wl | 13 ++++++------- Source/Chatbook/Main.wl | 6 ++++++ 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/Source/Chatbook/Chatbook.wl b/Source/Chatbook/Chatbook.wl index b77d23b0..cc2e449b 100644 --- a/Source/Chatbook/Chatbook.wl +++ b/Source/Chatbook/Chatbook.wl @@ -14,13 +14,12 @@ Quiet[ ClearAll[ "Wolfram`Chatbook`*`*" ]; Get @ Wolfram`ChatbookLoader`$MXFile; (* Ensure all subcontexts are in $Packages to avoid reloading subcontexts out of order: *) - WithCleanup[ - Unprotect @ $Packages, - $Packages = DeleteDuplicates @ Join[ - $Packages, - Select[ Contexts[ "Wolfram`Chatbook`*" ], StringFreeQ[ "`Private`" ] ] - ], - Protect @ $Packages + If[ MatchQ[ Wolfram`Chatbook`$ChatbookContexts, { __String } ], + WithCleanup[ + Unprotect @ $Packages, + $Packages = DeleteDuplicates @ Join[ $Packages, Wolfram`Chatbook`$ChatbookContexts ], + Protect @ $Packages + ] ] , WithCleanup[ diff --git a/Source/Chatbook/Main.wl b/Source/Chatbook/Main.wl index abef38ed..9a0144c1 100644 --- a/Source/Chatbook/Main.wl +++ b/Source/Chatbook/Main.wl @@ -8,6 +8,7 @@ BeginPackage[ "Wolfram`Chatbook`" ]; (*Declare Symbols*) `$AvailableTools; `$ChatAbort; +`$ChatbookContexts; `$ChatHandlerData; `$ChatPost; `$ChatPre; @@ -131,6 +132,11 @@ Protect[ WriteChatOutputCell ]; +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Subcontexts*) +$ChatbookContexts = Select[ Contexts[ "Wolfram`Chatbook`*" ], StringFreeQ[ "`Private`" ] ]; + (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*Package Footer*) From 801565f6b7edfd3ed7aa6a92d051e0ef6c04100e Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Fri, 12 Jan 2024 14:05:59 -0500 Subject: [PATCH 24/46] Add "Services" tab to cloud preferences and switch to using a docked cell instead of dialog --- Source/Chatbook/CloudToolbar.wl | 78 +++++++++++++++---- Source/Chatbook/PreferencesContent.wl | 103 ++++++++++---------------- 2 files changed, 103 insertions(+), 78 deletions(-) diff --git a/Source/Chatbook/CloudToolbar.wl b/Source/Chatbook/CloudToolbar.wl index 2fb75e9d..897a547a 100644 --- a/Source/Chatbook/CloudToolbar.wl +++ b/Source/Chatbook/CloudToolbar.wl @@ -14,6 +14,7 @@ Needs[ "Wolfram`Chatbook`Dialogs`" ]; Needs[ "Wolfram`Chatbook`Dynamics`" ]; Needs[ "Wolfram`Chatbook`PreferencesContent`" ]; Needs[ "Wolfram`Chatbook`Services`" ]; +Needs[ "Wolfram`Chatbook`UI`" ]; (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) @@ -39,13 +40,10 @@ makeChatCloudDockedCellContents[ ] := Grid[ { Item[ $cloudChatBanner, Alignment -> Left ], Item[ "", ItemSize -> Fit ], - makePersonaSelector[ ], - cloudModelSelector[ ], cloudPreferencesButton[ ] } }, Alignment -> { Left, Baseline }, - Dividers -> { { False, False, False, True, True }, False }, Spacings -> { 2, 0 }, BaseStyle -> { "Text", FontSize -> 14, FontColor -> GrayLevel[ 0.4 ] }, FrameStyle -> Directive[ Thickness[ 2 ], GrayLevel[ 0.9 ] ] @@ -59,7 +57,7 @@ makeChatCloudDockedCellContents // endDefinition; cloudPreferencesButton // beginDefinition; cloudPreferencesButton[ ] := Enclose[ - Module[ { iconTemplate, colorTemplate, mouseover, buttonIcon, button }, + Module[ { iconTemplate, colorTemplate, buttonLabel, mouseover, buttonIcon, button }, iconTemplate = ConfirmMatch[ chatbookIcon[ "ToolManagerCog", False ], @@ -73,24 +71,20 @@ cloudPreferencesButton[ ] := Enclose[ "Colorize" ] &; - mouseover = Mouseover[ - colorTemplate @ GrayLevel[ 0.65 ], - colorTemplate @ Hue[ 0.59, 0.9, 0.93 ] - ]; + buttonLabel = Grid[ + { { Style[ "Chat Settings", FontColor -> # ], colorTemplate @ # } }, + Alignment -> { Left, Baseline } + ] &; + + mouseover = Mouseover[ buttonLabel @ GrayLevel[ 0.25 ], buttonLabel @ Hue[ 0.59, 0.9, 0.93 ] ]; buttonIcon = DeleteCases[ - mouseover /. HoldPattern[ ImageSize -> _ ] :> ImageSize -> { 22, 22 }, + mouseover (*/. HoldPattern[ ImageSize -> _ ] :> ImageSize -> { 22, 22 }*), BaselinePosition -> _, Infinity ]; - button = Button[ - Tooltip[ buttonIcon, "Global chat preferences" ], - $cloudEvaluationNotebook = EvaluationNotebook[ ]; - CreateDialog @ Style[ Dynamic @ notebookSettingsPanel[ ], "Text" ], - Appearance -> "Suppressed", - Method -> "Queued" - ]; + button = Button[ buttonIcon, togglePreferences @ EvaluationNotebook[ ], Method -> "Queued" ]; cloudPreferencesButton[ ] = Pane[ button, FrameMargins -> { { 0, 10 }, { 0, 0 } } ] ], @@ -99,6 +93,58 @@ cloudPreferencesButton[ ] := Enclose[ cloudPreferencesButton // endDefinition; +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*togglePreferences*) +togglePreferences // beginDefinition; + +togglePreferences[ nbo_NotebookObject ] := + togglePreferences[ nbo, Flatten @ List @ CurrentValue[ nbo, DockedCells ] ]; + +togglePreferences[ nbo_NotebookObject, { cell_Cell } ] := + SetOptions[ nbo, DockedCells -> { cell, $cloudPreferencesCell } ]; + +togglePreferences[ nbo_NotebookObject, { Inherited|$Failed } ] := SetOptions[ + nbo, + DockedCells -> { + Cell[ BoxData @ DynamicBox @ ToBoxes @ MakeChatCloudDockedCellContents[ ], Background -> None ], + $cloudPreferencesCell + } +]; + +togglePreferences[ nbo_NotebookObject, { cell_Cell, _Cell } ] := + SetOptions[ nbo, DockedCells -> { cell } ]; + +togglePreferences[ nbo_NotebookObject, _ ] := + SetOptions[ nbo, DockedCells -> Inherited ]; + +togglePreferences // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsubsection::Closed:: *) +(*$cloudPreferencesCell*) +$cloudPreferencesCell := $cloudPreferencesCell = Cell[ + BoxData @ ToBoxes @ Pane[ + Dynamic[ + Replace[ + createCloudPreferencesContent[ ], + _createCloudPreferencesContent -> ProgressIndicator[ Appearance -> "Percolate" ] + ], + BaseStyle -> { "Text" } + ], + ImageSize -> { Scaled[ 1 ], Automatic }, + Alignment -> { Center, Top } + ], + Background -> GrayLevel[ 0.95 ] +]; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*createCloudPreferencesContent*) +createCloudPreferencesContent // beginDefinition; +createCloudPreferencesContent[ ] := createCloudPreferencesContent[ ] = createPreferencesContent[ ]; +createCloudPreferencesContent // endDefinition; + (* ::**************************************************************************************************************:: *) (* ::Subsubsection::Closed:: *) (*cloudModelSelector*) diff --git a/Source/Chatbook/PreferencesContent.wl b/Source/Chatbook/PreferencesContent.wl index 9cdb7047..ab19458e 100644 --- a/Source/Chatbook/PreferencesContent.wl +++ b/Source/Chatbook/PreferencesContent.wl @@ -30,6 +30,7 @@ Needs[ "Wolfram`Chatbook`UI`" ]; (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*Configuration*) +$preferencesWidth = 640; $cloudEvaluationNotebook = None; $preferencesPages = { "Notebooks", "Services", "Personas", "Tools" }; @@ -38,6 +39,15 @@ $$preferencesPage = Alternatives @@ $preferencesPages; $preferencesScope := $FrontEnd; $inFrontEndScope := MatchQ[ OwnValues @ $preferencesScope, { _ :> $FrontEnd|_FrontEndObject } ]; +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*Cloud Overrides*) +$displayedPreferencesPages := + If[ $CloudEvaluation, + { "Notebooks", "Services"(*, "Personas", "Tools"*) }, + $preferencesPages + ]; + (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*Scope Utilities*) @@ -131,32 +141,23 @@ currentTabPage // endDefinition; createPreferencesContent // beginDefinition; createPreferencesContent[ ] := Enclose[ - Module[ { notebookSettings, serviceSettings, personaSettings, toolSettings, tabView, reset }, - + Module[ { tabs, tabView, reset }, (* Retrieve the dynamic content for each preferences tab, confirming that it matches the expected types: *) - notebookSettings = ConfirmMatch[ preferencesContent[ "Notebooks" ], _Dynamic | _DynamicModule, "Notebooks" ]; - serviceSettings = ConfirmMatch[ preferencesContent[ "Services" ], _Dynamic | _DynamicModule, "Services" ]; - personaSettings = ConfirmMatch[ preferencesContent[ "Personas" ], _Dynamic | _DynamicModule, "Personas" ]; - toolSettings = ConfirmMatch[ preferencesContent[ "Tools" ], _Dynamic | _DynamicModule, "Tools" ]; + tabs = ConfirmMatch[ createTabViewTabs[ ], { { _String, _String -> _Dynamic|_DynamicModule }.. }, "Tabs" ]; (* Create a TabView for the preferences content, with the tab state stored in the FE's private options: *) tabView = TabView[ - { - { "Notebooks", "Notebooks" -> notebookSettings }, - { "Services" , "Services" -> serviceSettings }, - { "Personas" , "Personas" -> personaSettings }, - { "Tools" , "Tools" -> toolSettings } - }, + tabs, currentTabPageDynamic @ $preferencesScope, Background -> None, FrameMargins -> { { 2, 2 }, { 2, 3 } }, ImageMargins -> { { 10, 10 }, { 2, 2 } }, - ImageSize -> { 640, Automatic }, + ImageSize -> { $preferencesWidth, Automatic }, LabelStyle -> "feTabView" (* Defined in the SystemDialog stylesheet: *) ]; (* Create a reset button that will reset preferences to default settings: *) - reset = Pane[ $resetButton, ImageMargins -> { { 20, 0 }, { 0, 10 } }, ImageSize -> 640 ]; + reset = Pane[ $resetButton, ImageMargins -> { { 20, 0 }, { 0, 10 } }, ImageSize -> $preferencesWidth ]; (* Arrange the TabView and reset button in a Grid layout with vertical spacers: *) Grid[ @@ -180,6 +181,21 @@ createPreferencesContent[ ] := Enclose[ createPreferencesContent // endDefinition; +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*createTabViewTabs*) +createTabViewTabs // beginDefinition; +createTabViewTabs[ ] := createTabViewTabs @ $displayedPreferencesPages; +createTabViewTabs[ pages: { __String } ] := createTabViewTab /@ pages; +createTabViewTabs // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*createTabViewTab*) +createTabViewTab // beginDefinition; +createTabViewTab[ name_String ] := { name, name -> preferencesContent @ name }; +createTabViewTab // endDefinition; + (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*preferencesContent*) @@ -359,16 +375,7 @@ makePersonaSelector0[ personas: { (_String -> _).. } ] := Row @ { "Persona:", Spacer[ 3 ], - PopupMenu[ - scopedDynamic[ - CurrentChatSettings[ $preferencesScope, "LLMEvaluator" ], - Function[ - CurrentChatSettings[ $preferencesScope, "LLMEvaluator" ] = #; - refreshCloudToolbar[ ] - ] - ], - personas - ] + PopupMenu[ scopedDynamic @ CurrentChatSettings[ $preferencesScope, "LLMEvaluator" ], personas ] }, "Notebooks", "LLMEvaluator" @@ -376,32 +383,6 @@ makePersonaSelector0[ personas: { (_String -> _).. } ] := makePersonaSelector0 // endDefinition; -(* ::**************************************************************************************************************:: *) -(* ::Subsubsection::Closed:: *) -(*refreshCloudToolbar*) -refreshCloudToolbar // beginDefinition; - -refreshCloudToolbar[ ] := - If[ TrueQ @ $CloudEvaluation, - refreshCloudToolbar[ EvaluationNotebook[ ], $cloudEvaluationNotebook ] - ]; - -refreshCloudToolbar[ nbo_NotebookObject, nbo_NotebookObject ] := - Null; - -refreshCloudToolbar[ _, None ] := - Null; - -refreshCloudToolbar[ _NotebookObject, nbo_NotebookObject ] := - Module[ { dc }, - dc = Replace[ CurrentValue[ nbo, DockedCells ], Except[ Inherited | _List ] :> Inherited ]; - SetOptions[ nbo, DockedCells -> { } ]; - Pause[ 1 ]; - SetOptions[ nbo, DockedCells -> dc ]; - ]; - -refreshCloudToolbar // endDefinition; - (* ::**************************************************************************************************************:: *) (* ::Subsubsubsection::Closed:: *) (*personaPopupLabel*) @@ -463,7 +444,7 @@ makeModelSelector0[ services_Association? AssociationQ ] := Enclose[ highlight[ "Model:", Dynamic[ - If[ state === "Loading", $loadingPopupMenu, modelSelector ], + If[ state === "Loading" || MatchQ[ modelSelector, _Symbol ], $loadingPopupMenu, modelSelector ], TrackedSymbols :> { state, modelSelector } ], "ModelName" @@ -559,9 +540,7 @@ serviceSelectCallback[ Dynamic @ modelSelector, Dynamic @ state ] - ]; - - refreshCloudToolbar[ ] + ] ]; serviceSelectCallback // endDefinition; @@ -642,7 +621,7 @@ modelNameInputField // beginDefinition; modelNameInputField[ Dynamic[ service_ ], Dynamic[ model_ ], Dynamic[ modelSelector_ ], Dynamic[ state_ ] ] := prefsInputField[ - "Model:", + None, scopedDynamic[ Replace[ extractModelName @ CurrentChatSettings[ $preferencesScope, "Model" ], @@ -707,18 +686,15 @@ modelSelectCallback[ ensureServiceName @ service; ConfirmAssert[ StringQ @ service, "ServiceName" ]; + (* Store the service/model in FE settings: *) + CurrentChatSettings[ $preferencesScope, "Model" ] = <| "Service" -> service, "Name" -> model |>; + (* Remember the selected model for the given service, so it will be automatically chosen when choosing this service again: *) CurrentChatSettings[ $preferencesScope, "ServiceDefaultModel" ] = Append[ CurrentChatSettings[ $preferencesScope, "ServiceDefaultModel" ], service -> model - ]; - - (* Store the service/model in FE settings: *) - CurrentChatSettings[ $preferencesScope, "Model" ] = <| "Service" -> service, "Name" -> model |>; - - updateDynamics[ "Models" ]; - refreshCloudToolbar[ ] + ] , throwInternalFailure ]; @@ -1338,6 +1314,9 @@ subsectionText // endDefinition; prefsInputField // beginDefinition; (* cSpell: ignore leadin *) +prefsInputField[ None, value_Dynamic, type_, opts: OptionsPattern[ ] ] := + prefsInputField0[ value, type, opts ]; + prefsInputField[ label_, value_Dynamic, type_, opts: OptionsPattern[ ] ] := Grid[ { { label, prefsInputField0[ value, type, opts ] } }, Alignment -> { Automatic, Baseline }, From 6b6effa7175ad103a8bb1e1be7cf1fd17cf86ef5 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Fri, 12 Jan 2024 14:06:39 -0500 Subject: [PATCH 25/46] Workaround for bug report link not formatting in cloud --- Source/Chatbook/Common.wl | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Source/Chatbook/Common.wl b/Source/Chatbook/Common.wl index 1429776c..2565579f 100644 --- a/Source/Chatbook/Common.wl +++ b/Source/Chatbook/Common.wl @@ -466,7 +466,7 @@ messageFailure[ args___ ] := quiet = If[ TrueQ @ $failed, Quiet, Identity ]; message = messageFailure0; WithCleanup[ - StackInhibit @ quiet @ message @ args, + StackInhibit @ convertCloudFailure @ quiet @ message @ args, If[ TrueQ @ $catching, $failed = True ] ] ]; @@ -474,6 +474,29 @@ messageFailure[ args___ ] := (* https://resources.wolframcloud.com/FunctionRepository/resources/MessageFailure *) importResourceFunction[ messageFailure0, "MessageFailure" ]; +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*convertCloudFailure*) +convertCloudFailure // beginDefinition; + +convertCloudFailure[ Failure[ + "Chatbook::Internal", + as: KeyValuePattern @ { "MessageParameters" :> { Hyperlink[ _, url_ ], params___ } } +] ] /; $CloudEvaluation := + Failure[ + "Chatbook::Internal", + Association[ + as, + "MessageParameters" -> { "", params }, + "Link" -> Hyperlink[ "Report this issue \[RightGuillemet]", url ] + ] + ]; + +convertCloudFailure[ failure_ ] := + failure; + +convertCloudFailure // endDefinition; + (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*messagePrint*) From e402f6549d829b7fa66b058043b7807ed3032d75 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Fri, 12 Jan 2024 14:07:24 -0500 Subject: [PATCH 26/46] Bugfix: `InvalidateServiceCache` should refresh relevant dynamics --- Source/Chatbook/Services.wl | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Source/Chatbook/Services.wl b/Source/Chatbook/Services.wl index cd561721..bcc44588 100644 --- a/Source/Chatbook/Services.wl +++ b/Source/Chatbook/Services.wl @@ -19,10 +19,11 @@ HoldComplete[ Begin[ "`Private`" ]; -Needs[ "Wolfram`Chatbook`" ]; -Needs[ "Wolfram`Chatbook`Common`" ]; -Needs[ "Wolfram`Chatbook`Models`" ]; -Needs[ "Wolfram`Chatbook`UI`" ]; +Needs[ "Wolfram`Chatbook`" ]; +Needs[ "Wolfram`Chatbook`Common`" ]; +Needs[ "Wolfram`Chatbook`Dynamics`" ]; +Needs[ "Wolfram`Chatbook`Models`" ]; +Needs[ "Wolfram`Chatbook`UI`" ]; $ContextAliases[ "llm`" ] = "LLMServices`"; @@ -46,7 +47,7 @@ $llmServicesAvailable := $llmServicesAvailable = ( (* ::Section::Closed:: *) (*InvalidateServiceCache*) InvalidateServiceCache // beginDefinition; -InvalidateServiceCache[ ] := ($serviceCache = None; Null); +InvalidateServiceCache[ ] := catchAlways[ $serviceCache = None; updateDynamics[ { "Models", "Services" } ]; ]; InvalidateServiceCache // endDefinition; (* ::**************************************************************************************************************:: *) From 1472faaf17e3a2e6271f31d8c6d88e0cd238a40f Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Fri, 12 Jan 2024 14:08:01 -0500 Subject: [PATCH 27/46] Bugfix: Fix internal failure for registered services that do not specify any models --- Source/Chatbook/Services.wl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Source/Chatbook/Services.wl b/Source/Chatbook/Services.wl index bcc44588..4f2890e4 100644 --- a/Source/Chatbook/Services.wl +++ b/Source/Chatbook/Services.wl @@ -92,9 +92,13 @@ getServiceModelList[ KeyValuePattern[ "Service" -> service_String ] ] := getServiceModelList[ service_String ] := With[ { models = $availableServices[ service, "CachedModels" ] }, - If[ ListQ @ models, + Replace[ models, - getServiceModelList[ service, $availableServices[ service ] ] + { + { } | None :> Missing[ "NoModelList" ], + list_List :> list, + _ :> getServiceModelList[ service, $availableServices[ service ] ] + } ] ]; From cb98fd97cea90093eff16933dcaf6899975bd6e1 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Tue, 16 Jan 2024 09:53:57 -0500 Subject: [PATCH 28/46] Handle cases where the model name is `Automatic` in the tool model warning --- Source/Chatbook/ToolManager.wl | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Chatbook/ToolManager.wl b/Source/Chatbook/ToolManager.wl index e8de9d2d..d77f0914 100644 --- a/Source/Chatbook/ToolManager.wl +++ b/Source/Chatbook/ToolManager.wl @@ -607,6 +607,7 @@ toolModelWarning[ scope_ ] := toolModelWarning[ scope, currentChatSettings[ scop toolModelWarning[ scope_, True ] := ""; toolModelWarning[ scope_, False ] := $toolsDisabledWarning; toolModelWarning[ scope_, enabled_ ] := toolModelWarning[ scope, enabled, currentChatSettings[ scope, "Model" ] ]; +toolModelWarning[ scope_, enabled_, KeyValuePattern[ "Name" -> Automatic ] ] := ""; toolModelWarning[ scope_, enabled_, model_? toolsEnabledQ ] := ""; toolModelWarning[ scope_, enabled_, model_ ] := toolModelWarning0[ scope, model ]; toolModelWarning // endDefinition; From b99ac26b6d6424153eb8f1542b3fe554b2aec181 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Tue, 16 Jan 2024 09:58:33 -0500 Subject: [PATCH 29/46] Ensure automatic model name is resolved when submitting chat --- Source/Chatbook/Models.wl | 77 ++++++++++++++++++++++++--- Source/Chatbook/PreferencesContent.wl | 19 ------- Source/Chatbook/SendChat.wl | 4 ++ 3 files changed, 74 insertions(+), 26 deletions(-) diff --git a/Source/Chatbook/Models.wl b/Source/Chatbook/Models.wl index 0b8ba160..35cd313d 100644 --- a/Source/Chatbook/Models.wl +++ b/Source/Chatbook/Models.wl @@ -6,13 +6,17 @@ BeginPackage[ "Wolfram`Chatbook`Models`" ]; (* :!CodeAnalysis::BeginBlock:: *) -`chatModelQ; -`getModelList; -`modelDisplayName; -`multimodalModelQ; -`snapshotModelQ; -`standardizeModelData; -`toModelName; +HoldComplete[ + `chatModelQ; + `chooseDefaultModelName; + `getModelList; + `modelDisplayName; + `multimodalModelQ; + `snapshotModelQ; + `standardizeModelData; + `resolveFullModelSpec; + `toModelName; +]; Begin[ "`Private`" ]; @@ -20,6 +24,7 @@ Needs[ "Wolfram`Chatbook`" ]; Needs[ "Wolfram`Chatbook`Actions`" ]; Needs[ "Wolfram`Chatbook`Common`" ]; Needs[ "Wolfram`Chatbook`Dynamics`" ]; +Needs[ "Wolfram`Chatbook`Services`" ]; Needs[ "Wolfram`Chatbook`UI`" ]; (* ::**************************************************************************************************************:: *) @@ -110,6 +115,10 @@ getModelList // endDefinition; $fallbackModelList = { "gpt-3.5-turbo", "gpt-3.5-turbo-16k", "gpt-4" }; +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Model Utility Functions*) + (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*chatModelQ*) @@ -331,8 +340,62 @@ standardizeModelData[ service_String, model_ ] := standardizeModelData[ KeyValuePattern[ "Service" -> service_String ], model_ ] := standardizeModelData[ service, model ]; +standardizeModelData[ $$unspecified ] := + With[ { model = $DefaultModel }, + standardizeModelData @ model /; MatchQ[ model, Except[ $$unspecified ] ] + ]; + standardizeModelData // endDefinition; +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*chooseDefaultModelName*) +(* + Choose a default initial model according to the following rules: + 1. If the service name is the same as the one in $DefaultModel, use the model name in $DefaultModel. + 2. If the registered service specifies a "DefaultModel" property, we'll use that. + 3. If the model list is already cached for the service, we'll use the first model in that list. + 4. Otherwise, give Automatic to indicate a model name that must be resolved later. +*) +chooseDefaultModelName // beginDefinition; +chooseDefaultModelName[ service_String ] /; service === $DefaultModel[ "Service" ] := $DefaultModel[ "Name" ]; +chooseDefaultModelName[ service_String ] := chooseDefaultModelName @ $availableServices @ service; +chooseDefaultModelName[ KeyValuePattern[ "DefaultModel" -> model_ ] ] := toModelName @ model; +chooseDefaultModelName[ KeyValuePattern[ "CachedModels" -> models_List ] ] := chooseDefaultModelName @ models; +chooseDefaultModelName[ { model_, ___ } ] := toModelName @ model; +chooseDefaultModelName[ service_ ] := Automatic; +chooseDefaultModelName // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*resolveFullModelSpec*) +resolveFullModelSpec // beginDefinition; + +resolveFullModelSpec[ settings: KeyValuePattern[ "Model" -> model_ ] ] := + resolveFullModelSpec @ model; + +resolveFullModelSpec[ { service_String, Automatic } ] := + resolveFullModelSpec @ <| "Service" -> service, "Name" -> Automatic |>; + +resolveFullModelSpec[ model: KeyValuePattern @ { "Service" -> service_String, "Name" -> Automatic } ] := Enclose[ + Catch @ Module[ { default, models, name }, + default = ConfirmMatch[ chooseDefaultModelName @ service, Automatic | _String, "Default" ]; + If[ StringQ @ default, Throw @ standardizeModelData @ <| model, "Name" -> default |> ]; + models = ConfirmMatch[ getServiceModelList @ service, _List | Missing[ "NotConnected" ], "Models" ]; + If[ MissingQ @ models, throwTop @ $Canceled ]; + name = ConfirmBy[ chooseDefaultModelName @ models, StringQ, "ResolvedName" ]; + standardizeModelData @ <| model, "Name" -> name |> + ], + throwInternalFailure +]; + +resolveFullModelSpec[ model_ ] := + With[ { spec = standardizeModelData @ model }, + spec /; AssociationQ @ spec + ]; + +resolveFullModelSpec // endDefinition; + (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*SetModel*) diff --git a/Source/Chatbook/PreferencesContent.wl b/Source/Chatbook/PreferencesContent.wl index ab19458e..7766356f 100644 --- a/Source/Chatbook/PreferencesContent.wl +++ b/Source/Chatbook/PreferencesContent.wl @@ -1484,25 +1484,6 @@ getServiceDefaultModel[ service_String ] := Enclose[ getServiceDefaultModel // endDefinition; -(* ::**************************************************************************************************************:: *) -(* ::Subsubsubsubsection::Closed:: *) -(*chooseDefaultModelName*) -(* - Choose a default initial model according to the following rules: - 1. If the service name is the same as the one in $DefaultModel, use the model name in $DefaultModel. - 2. If the registered service specifies a "DefaultModel" property, we'll use that. - 3. If the model list is already cached for the service, we'll use the first model in that list. - 4. Otherwise, give Automatic to indicate a model name that must be resolved later. -*) -chooseDefaultModelName // beginDefinition; -chooseDefaultModelName[ service_String ] /; service === $DefaultModel[ "Service" ] := $DefaultModel[ "Name" ]; -chooseDefaultModelName[ service_String ] := chooseDefaultModelName @ $availableServices @ service; -chooseDefaultModelName[ KeyValuePattern[ "DefaultModel" -> model_ ] ] := toModelName @ model; -chooseDefaultModelName[ KeyValuePattern[ "CachedModels" -> models_List ] ] := chooseDefaultModelName @ models; -chooseDefaultModelName[ { model_, ___ } ] := toModelName @ model; -chooseDefaultModelName[ service_ ] := Automatic; -chooseDefaultModelName // endDefinition; - (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*ServiceConnection Utilities*) diff --git a/Source/Chatbook/SendChat.wl b/Source/Chatbook/SendChat.wl index 2721b851..e0d96f91 100644 --- a/Source/Chatbook/SendChat.wl +++ b/Source/Chatbook/SendChat.wl @@ -1212,6 +1212,7 @@ resolveAutoSettings[ settings_Association ] := resolveAutoSettings0 @ <| "HandlerFunctions" -> getHandlerFunctions @ settings, "LLMEvaluator" -> getLLMEvaluator @ settings, "ProcessingFunctions" -> getProcessingFunctions @ settings, + "Model" -> resolveFullModelSpec @ settings, If[ StringQ @ settings[ "Tokenizer" ], <| "TokenizerName" -> getTokenizerName @ settings, @@ -2236,6 +2237,9 @@ smallSettings // endDefinition; smallSettings0 // beginDefinition; +smallSettings0[ as: KeyValuePattern[ "Model" -> model: KeyValuePattern[ "Icon" -> _ ] ] ] := + smallSettings0 @ <| as, "Model" -> KeyTake[ model, { "Service", "Name" } ] |>; + smallSettings0[ as_Association ] := smallSettings0[ as, as[ "LLMEvaluator" ] ]; From aba1801ac029f0360bd2245caf38870d02fdf181 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Tue, 16 Jan 2024 10:35:01 -0500 Subject: [PATCH 30/46] Make the preferences reset button work in cloud --- Source/Chatbook/PreferencesContent.wl | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/Source/Chatbook/PreferencesContent.wl b/Source/Chatbook/PreferencesContent.wl index 7766356f..5647372f 100644 --- a/Source/Chatbook/PreferencesContent.wl +++ b/Source/Chatbook/PreferencesContent.wl @@ -1646,10 +1646,7 @@ $resetButton := resetChatPreferences // beginDefinition; resetChatPreferences[ "Notebooks" ] := expandScope[ - FrontEndExecute @ FrontEnd`RemoveOptions[ - $preferencesScope, - { LLMEvaluator, { TaggingRules, "ChatNotebookSettings" } } - ]; + CurrentChatSettings[ $preferencesScope ] = Inherited; updateDynamics[ "Preferences" ]; ]; @@ -1659,20 +1656,21 @@ resetChatPreferences[ "Personas" ] := (* TODO: choice dialog to uninstall personas *) CurrentValue[ $preferencesScope, { path, "VisiblePersonas" } ] = $corePersonaNames; CurrentValue[ $preferencesScope, { path, "PersonaFavorites" } ] = $corePersonaNames; - resetChatPreferences[ "Notebooks" ]; + updateDynamics[ "Personas" ]; ]; -resetChatPreferences[ "Tools" ] := ( +resetChatPreferences[ "Tools" ] := expandScope[ + CurrentChatSettings[ $preferencesScope, "ToolSelectionType" ] = Inherited; + CurrentChatSettings[ $preferencesScope, "ToolSelections" ] = Inherited; (* TODO: choice dialog to uninstall tools *) - resetChatPreferences[ "Notebooks" ]; -); + updateDynamics[ "Tools" ]; +]; resetChatPreferences[ "Services" ] := ( (* TODO: choice dialog to clear service connections *) Needs[ "LLMServices`" -> None ]; LLMServices`ResetServices[ ]; InvalidateServiceCache[ ]; - resetChatPreferences[ "Notebooks" ]; ); resetChatPreferences // endDefinition; From 4ed1fab78441bf5827102cd9576201072f1bb32a Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Tue, 16 Jan 2024 10:35:38 -0500 Subject: [PATCH 31/46] Update `openPreferencesPage` to handle changes in "CurrentPreferencesTab" storage location --- Source/Chatbook/PreferencesContent.wl | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/Chatbook/PreferencesContent.wl b/Source/Chatbook/PreferencesContent.wl index 5647372f..9b54591e 100644 --- a/Source/Chatbook/PreferencesContent.wl +++ b/Source/Chatbook/PreferencesContent.wl @@ -1687,11 +1687,15 @@ openPreferencesPage // beginDefinition; openPreferencesPage[ ] := openPreferencesPage[ "Notebooks" ]; -openPreferencesPage[ page: $$preferencesPage ] := - NotebookTools`OpenPreferencesDialog @ { "AI", page }; +openPreferencesPage[ page: $$preferencesPage ] := ( + CurrentChatSettings[ $FrontEnd, "CurrentPreferencesTab" ] = page; + NotebookTools`OpenPreferencesDialog @ { "AI", page } +); -openPreferencesPage[ page: $$preferencesPage, id_ ] := - NotebookTools`OpenPreferencesDialog[ { "AI", page }, { "AI", page, id } ]; +openPreferencesPage[ page: $$preferencesPage, id_ ] := ( + CurrentChatSettings[ $FrontEnd, "CurrentPreferencesTab" ] = page; + NotebookTools`OpenPreferencesDialog[ { "AI", page }, { "AI", page, id } ] +); openPreferencesPage // endDefinition; From 1e69ed0ee0c301b39e0191020b454591c8b90c41 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Tue, 16 Jan 2024 10:37:58 -0500 Subject: [PATCH 32/46] TODO --- Source/Chatbook/ChatMessages.wl | 1 + Source/Chatbook/InlineReferences.wl | 1 + 2 files changed, 2 insertions(+) diff --git a/Source/Chatbook/ChatMessages.wl b/Source/Chatbook/ChatMessages.wl index 8d0ef48a..0e600ebd 100644 --- a/Source/Chatbook/ChatMessages.wl +++ b/Source/Chatbook/ChatMessages.wl @@ -714,6 +714,7 @@ inferMultimodalTypes[ content_List ] := Enclose[ inferMultimodalTypes // endDefinition; +(* TODO: add a way to control image detail and insert "Detail" -> "..." here when appropriate: *) inferMultimodalTypes0 // beginDefinition; inferMultimodalTypes0[ content_List ] := inferMultimodalTypes0 /@ content; inferMultimodalTypes0[ content_String ] := <| "Type" -> "Text" , "Data" -> content |>; diff --git a/Source/Chatbook/InlineReferences.wl b/Source/Chatbook/InlineReferences.wl index 02880782..d6bd76f2 100644 --- a/Source/Chatbook/InlineReferences.wl +++ b/Source/Chatbook/InlineReferences.wl @@ -3,6 +3,7 @@ (* ::Section::Closed:: *) (*Package Header*) +(* TODO: Figure out how to show autocomplete menu when field is empty *) BeginPackage[ "Wolfram`Chatbook`InlineReferences`" ]; From e70cf15e6c2614a46b081148c2f2acad52d62dd1 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Tue, 16 Jan 2024 13:22:10 -0500 Subject: [PATCH 33/46] Bugfix: workaround for button code not evaluating in cloud --- Source/Chatbook/Dialogs.wl | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Source/Chatbook/Dialogs.wl b/Source/Chatbook/Dialogs.wl index 3e8048e4..d5536c9e 100644 --- a/Source/Chatbook/Dialogs.wl +++ b/Source/Chatbook/Dialogs.wl @@ -213,6 +213,15 @@ autoMargins0 // endDefinition; (*grayDialogButtonLabel*) grayDialogButtonLabel // beginDefinition; +grayDialogButtonLabel[ { normal_, hover_, pressed_ } ] /; $cloudNotebooks := + Mouseover[ + Framed[ normal , BaseStyle -> "ButtonGray1Normal" , BaselinePosition -> Baseline ], + Framed[ hover , BaseStyle -> "ButtonGray1Hover" , BaselinePosition -> Baseline ], + BaseStyle -> "DialogTextBasic", + ContentPadding -> False, + ImageSize -> All + ]; + grayDialogButtonLabel[ { normal_, hover_, pressed_ } ] := NotebookTools`Mousedown[ Framed[ normal , BaseStyle -> "ButtonGray1Normal" , BaselinePosition -> Baseline ], @@ -232,6 +241,15 @@ grayDialogButtonLabel // endDefinition; (*redDialogButtonLabel*) redDialogButtonLabel // beginDefinition; +redDialogButtonLabel[ { normal_, hover_, pressed_ } ] /; $cloudNotebooks := + Mouseover[ + Framed[ normal , BaseStyle -> "ButtonRed1Normal" , BaselinePosition -> Baseline ], + Framed[ hover , BaseStyle -> "ButtonRed1Hover" , BaselinePosition -> Baseline ], + BaseStyle -> "DialogTextBasic", + ContentPadding -> False, + ImageSize -> All + ]; + redDialogButtonLabel[ { normal_, hover_, pressed_ } ] := NotebookTools`Mousedown[ Framed[ normal , BaseStyle -> "ButtonRed1Normal" , BaselinePosition -> Baseline ], From 521474e38df1eca73f1041356a604db247c1c3ac Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Tue, 16 Jan 2024 13:22:40 -0500 Subject: [PATCH 34/46] Bugfix: more permissive pattern for `applyCloudCellFixes` in case it's used via programmatic methods --- Source/Chatbook/FrontEnd.wl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Source/Chatbook/FrontEnd.wl b/Source/Chatbook/FrontEnd.wl index cf91e05c..4a4032ac 100644 --- a/Source/Chatbook/FrontEnd.wl +++ b/Source/Chatbook/FrontEnd.wl @@ -463,9 +463,8 @@ fixCloudCell // endDefinition; (* ::Subsubsection::Closed:: *) (*applyCloudCellFixes*) applyCloudCellFixes // beginDefinition; -applyCloudCellFixes[ text_String ] := text; -applyCloudCellFixes[ boxes_BoxData ] := boxes; applyCloudCellFixes[ text_TextData ] := ReplaceRepeated[ text, $cloudCellFixes ]; +applyCloudCellFixes[ boxes_ ] := boxes; applyCloudCellFixes // endDefinition; $cloudCellFixes := $cloudCellFixes = Dispatch @ { From 5fa1e9645808517d8366bd55fa2ed73f1f66a875 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Tue, 16 Jan 2024 13:23:34 -0500 Subject: [PATCH 35/46] Move persona prefs to `CurrentChatSettings` --- Source/Chatbook/PersonaManager.wl | 32 ++++++++++++------------------- Source/Chatbook/Settings.wl | 4 +++- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/Source/Chatbook/PersonaManager.wl b/Source/Chatbook/PersonaManager.wl index 943a819a..1c7ea387 100644 --- a/Source/Chatbook/PersonaManager.wl +++ b/Source/Chatbook/PersonaManager.wl @@ -39,10 +39,10 @@ CreatePersonaManagerDialog // endDefinition; CreatePersonaManagerPanel // beginDefinition; CreatePersonaManagerPanel[ ] := DynamicModule[{favorites, delimColor}, - favorites = - Replace[ - CurrentValue[$FrontEnd, {PrivateFrontEndOptions, "InterfaceSettings", "Chatbook", "PersonaFavorites"}], - Except[{___String}] :> $corePersonaNames]; + favorites = Replace[ + CurrentChatSettings[ $FrontEnd, "PersonaFavorites" ], + Except[ { ___String } ] :> $corePersonaNames + ]; Framed[ Grid[ @@ -154,18 +154,14 @@ CreatePersonaManagerPanel[ ] := DynamicModule[{favorites, delimColor}, GetPersonaData[]; (* sets $CachedPersonaData *) (* make sure there are no unexpected extra personas *) Enclose[ - CurrentValue[$FrontEnd, {PrivateFrontEndOptions, "InterfaceSettings", "Chatbook", "VisiblePersonas"}] = - ConfirmBy[ - Intersection[ - CurrentValue[$FrontEnd, {PrivateFrontEndOptions, "InterfaceSettings", "Chatbook", "VisiblePersonas"}], - Keys[$CachedPersonaData] - ], + CurrentChatSettings[ $FrontEnd, "VisiblePersonas" ] = ConfirmBy[ + Quiet @ Intersection[ CurrentChatSettings[ $FrontEnd, "VisiblePersonas" ], Keys @ $CachedPersonaData ], ListQ ] ] ), Deinitialization :> If[ MatchQ[ favorites, { ___String } ], - CurrentValue[$FrontEnd, {PrivateFrontEndOptions, "InterfaceSettings", "Chatbook","PersonaFavorites"}] = favorites + CurrentChatSettings[ $FrontEnd, "PersonaFavorites" ] = favorites ] ]; @@ -275,17 +271,13 @@ formatPacletLink // endDefinition; addRemovePersonaListingCheckbox // beginDefinition; addRemovePersonaListingCheckbox[ name_String ] := - With[ - { - path = { PrivateFrontEndOptions, "InterfaceSettings", "Chatbook", "VisiblePersonas" }, - core = $corePersonaNames - }, + With[ { core = $corePersonaNames }, Checkbox @ Dynamic[ - MemberQ[ CurrentValue[ $FrontEnd, path, core ], name ], + MemberQ[ CurrentChatSettings[ $FrontEnd, "VisiblePersonas" ], name ], Function[ - CurrentValue[ $FrontEnd, path ] = - With[ { current = Replace[ CurrentValue[ $FrontEnd, path ], Except[ { ___String } ] :> core ] }, - If[ #, Union[ current, { name } ], Complement[ current, { name } ] ] + CurrentChatSettings[ $FrontEnd, "VisiblePersonas" ] = With[ + { current = Replace[ CurrentChatSettings[ $FrontEnd, "VisiblePersonas" ], Except[ { ___String } ] :> core ] }, + If[ #1, Union[ current, { name } ], Complement[ current, { name } ] ] ] ] ] diff --git a/Source/Chatbook/Settings.wl b/Source/Chatbook/Settings.wl index cb41d495..d2b46289 100644 --- a/Source/Chatbook/Settings.wl +++ b/Source/Chatbook/Settings.wl @@ -15,6 +15,7 @@ Begin[ "`Private`" ]; Needs[ "Wolfram`Chatbook`" ]; Needs[ "Wolfram`Chatbook`Common`" ]; Needs[ "Wolfram`Chatbook`FrontEnd`" ]; +Needs[ "Wolfram`Chatbook`Personas`" ]; Needs[ "Wolfram`Chatbook`ResourceInstaller`" ]; (* ::**************************************************************************************************************:: *) @@ -61,7 +62,8 @@ $defaultChatSettings = <| "Tools" -> Automatic, "ToolsEnabled" -> Automatic, "TopP" -> 1, - "TrackScrollingWhenPlaced" -> Automatic + "TrackScrollingWhenPlaced" -> Automatic, + "VisiblePersonas" -> $corePersonaNames |>; $cachedGlobalSettings := $cachedGlobalSettings = getGlobalSettingsFile[ ]; From 72ec92265e90e2a4cef6fa3f1f2bff8d5761c805 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Tue, 16 Jan 2024 13:24:02 -0500 Subject: [PATCH 36/46] Update the reset button for preferences tab --- Source/Chatbook/PreferencesContent.wl | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Source/Chatbook/PreferencesContent.wl b/Source/Chatbook/PreferencesContent.wl index 9b54591e..45b7b14c 100644 --- a/Source/Chatbook/PreferencesContent.wl +++ b/Source/Chatbook/PreferencesContent.wl @@ -1650,14 +1650,12 @@ resetChatPreferences[ "Notebooks" ] := expandScope[ updateDynamics[ "Preferences" ]; ]; -resetChatPreferences[ "Personas" ] := - (* TODO: this won't work when $preferencesScope is something other than $FrontEnd *) - With[ { path = Sequence[ PrivateFrontEndOptions, "InterfaceSettings", "Chatbook" ] }, +resetChatPreferences[ "Personas" ] := ( (* TODO: choice dialog to uninstall personas *) - CurrentValue[ $preferencesScope, { path, "VisiblePersonas" } ] = $corePersonaNames; - CurrentValue[ $preferencesScope, { path, "PersonaFavorites" } ] = $corePersonaNames; + CurrentChatSettings[ $preferencesScope, "VisiblePersonas" ] = $corePersonaNames; + CurrentChatSettings[ $preferencesScope, "PersonaFavorites" ] = $corePersonaNames; updateDynamics[ "Personas" ]; - ]; +); resetChatPreferences[ "Tools" ] := expandScope[ CurrentChatSettings[ $preferencesScope, "ToolSelectionType" ] = Inherited; From 45c306d0074068d96c25f9c65bc9b6255ecb216e Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Tue, 16 Jan 2024 13:24:23 -0500 Subject: [PATCH 37/46] Enable persona manager in cloud --- Source/Chatbook/PreferencesContent.wl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Chatbook/PreferencesContent.wl b/Source/Chatbook/PreferencesContent.wl index 45b7b14c..14f9ad28 100644 --- a/Source/Chatbook/PreferencesContent.wl +++ b/Source/Chatbook/PreferencesContent.wl @@ -44,7 +44,7 @@ $inFrontEndScope := MatchQ[ OwnValues @ $preferencesScope, { _ :> $FrontEnd|_Fr (*Cloud Overrides*) $displayedPreferencesPages := If[ $CloudEvaluation, - { "Notebooks", "Services"(*, "Personas", "Tools"*) }, + { "Notebooks", "Services", "Personas"(*, "Tools"*) }, $preferencesPages ]; @@ -1651,10 +1651,10 @@ resetChatPreferences[ "Notebooks" ] := expandScope[ ]; resetChatPreferences[ "Personas" ] := ( - (* TODO: choice dialog to uninstall personas *) + (* TODO: choice dialog to uninstall personas *) CurrentChatSettings[ $preferencesScope, "VisiblePersonas" ] = $corePersonaNames; CurrentChatSettings[ $preferencesScope, "PersonaFavorites" ] = $corePersonaNames; - updateDynamics[ "Personas" ]; + updateDynamics[ "Personas" ]; ); resetChatPreferences[ "Tools" ] := expandScope[ From 3fb0c7580982a81da5c82fc4d6e0c5d1075f75a1 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Tue, 16 Jan 2024 13:24:43 -0500 Subject: [PATCH 38/46] Cloud hacks for persona manager --- Source/Chatbook/CloudToolbar.wl | 42 +++++++++++++++++++++++-------- Source/Chatbook/PersonaManager.wl | 11 ++++++-- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/Source/Chatbook/CloudToolbar.wl b/Source/Chatbook/CloudToolbar.wl index 897a547a..1846e0ec 100644 --- a/Source/Chatbook/CloudToolbar.wl +++ b/Source/Chatbook/CloudToolbar.wl @@ -4,6 +4,7 @@ BeginPackage[ "Wolfram`Chatbook`CloudToolbar`" ]; HoldComplete[ `makeChatCloudDockedCellContents; + `forceRefreshCloudPreferences; ]; Begin[ "`Private`" ]; @@ -84,7 +85,7 @@ cloudPreferencesButton[ ] := Enclose[ Infinity ]; - button = Button[ buttonIcon, togglePreferences @ EvaluationNotebook[ ], Method -> "Queued" ]; + button = Button[ buttonIcon, toggleCloudPreferences @ EvaluationNotebook[ ], Method -> "Queued" ]; cloudPreferencesButton[ ] = Pane[ button, FrameMargins -> { { 0, 10 }, { 0, 0 } } ] ], @@ -95,16 +96,16 @@ cloudPreferencesButton // endDefinition; (* ::**************************************************************************************************************:: *) (* ::Subsubsection::Closed:: *) -(*togglePreferences*) -togglePreferences // beginDefinition; +(*toggleCloudPreferences*) +toggleCloudPreferences // beginDefinition; -togglePreferences[ nbo_NotebookObject ] := - togglePreferences[ nbo, Flatten @ List @ CurrentValue[ nbo, DockedCells ] ]; +toggleCloudPreferences[ nbo_NotebookObject ] := + toggleCloudPreferences[ nbo, Flatten @ List @ CurrentValue[ nbo, DockedCells ] ]; -togglePreferences[ nbo_NotebookObject, { cell_Cell } ] := +toggleCloudPreferences[ nbo_NotebookObject, { cell_Cell } ] := SetOptions[ nbo, DockedCells -> { cell, $cloudPreferencesCell } ]; -togglePreferences[ nbo_NotebookObject, { Inherited|$Failed } ] := SetOptions[ +toggleCloudPreferences[ nbo_NotebookObject, { Inherited|$Failed } ] := SetOptions[ nbo, DockedCells -> { Cell[ BoxData @ DynamicBox @ ToBoxes @ MakeChatCloudDockedCellContents[ ], Background -> None ], @@ -112,13 +113,13 @@ togglePreferences[ nbo_NotebookObject, { Inherited|$Failed } ] := SetOptions[ } ]; -togglePreferences[ nbo_NotebookObject, { cell_Cell, _Cell } ] := +toggleCloudPreferences[ nbo_NotebookObject, { cell_Cell, _Cell } ] := SetOptions[ nbo, DockedCells -> { cell } ]; -togglePreferences[ nbo_NotebookObject, _ ] := +toggleCloudPreferences[ nbo_NotebookObject, _ ] := SetOptions[ nbo, DockedCells -> Inherited ]; -togglePreferences // endDefinition; +toggleCloudPreferences // endDefinition; (* ::**************************************************************************************************************:: *) (* ::Subsubsubsection::Closed:: *) @@ -195,6 +196,27 @@ $chatEnabledNotebookLabel := Grid[ Spacings -> 0.5 ]; +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Utilities*) + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*forceRefreshCloudPreferences*) +forceRefreshCloudPreferences // beginDefinition; + +forceRefreshCloudPreferences[ ] /; ! TrueQ @ $cloudNotebooks := Null; + +forceRefreshCloudPreferences[ ] := forceRefreshCloudPreferences @ EvaluationNotebook[ ]; + +forceRefreshCloudPreferences[ nbo_NotebookObject ] := ( + SetOptions[ nbo, DockedCells -> Inherited ]; + Pause[ 0.5 ]; + toggleCloudPreferences @ nbo +); + +forceRefreshCloudPreferences // endDefinition; + (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*Package Footer*) diff --git a/Source/Chatbook/PersonaManager.wl b/Source/Chatbook/PersonaManager.wl index 1c7ea387..36b3b5ce 100644 --- a/Source/Chatbook/PersonaManager.wl +++ b/Source/Chatbook/PersonaManager.wl @@ -10,6 +10,7 @@ BeginPackage[ "Wolfram`Chatbook`PersonaManager`" ]; Begin[ "`Private`" ]; Needs[ "Wolfram`Chatbook`" ]; +Needs[ "Wolfram`Chatbook`CloudToolbar`" ]; Needs[ "Wolfram`Chatbook`Common`" ]; Needs[ "Wolfram`Chatbook`Dialogs`" ]; Needs[ "Wolfram`Chatbook`Personas`" ]; @@ -57,6 +58,7 @@ CreatePersonaManagerPanel[ ] := DynamicModule[{favorites, delimColor}, "Install from", Button[ grayDialogButtonLabel[ "Prompt Repository \[UpperRightArrow]" ], + If[ $CloudEvaluation, SetOptions[ EvaluationNotebook[ ], DockedCells -> Inherited ] ]; ResourceInstallFromRepository[ "Prompt" ], Appearance -> "Suppressed", BaselinePosition -> Baseline, @@ -64,6 +66,7 @@ CreatePersonaManagerPanel[ ] := DynamicModule[{favorites, delimColor}, ], Button[ grayDialogButtonLabel[ "URL" ], + If[ $CloudEvaluation, SetOptions[ EvaluationNotebook[ ], DockedCells -> Inherited ] ]; Block[ { PrintTemporary }, ResourceInstallFromURL[ "Prompt" ] ], Appearance -> "Suppressed", BaselinePosition -> Baseline, @@ -278,7 +281,7 @@ addRemovePersonaListingCheckbox[ name_String ] := CurrentChatSettings[ $FrontEnd, "VisiblePersonas" ] = With[ { current = Replace[ CurrentChatSettings[ $FrontEnd, "VisiblePersonas" ], Except[ { ___String } ] :> core ] }, If[ #1, Union[ current, { name } ], Complement[ current, { name } ] ] - ] + ] ] ] ]; @@ -298,7 +301,11 @@ uninstallButton[ name_String, installedQ_, pacletName_String ] := StringTemplate["This persona cannot be uninstalled because it is provided by the `1` paclet."][pacletName]]}, Dynamic[Which[!installedQ, "Disabled", CurrentValue["MouseOver"], "Hover", True, "Default"]], ImageSize -> Automatic], - Block[ { PrintTemporary }, ResourceUninstall[ "Prompt", name ]; GetPersonaData[] ], + Block[ { PrintTemporary }, + ResourceUninstall[ "Prompt", name ]; + GetPersonaData[ ]; + forceRefreshCloudPreferences[ ] + ], Appearance -> "Suppressed", Enabled -> installedQ, ImageMargins -> {{0, 13}, {0, 0}}, From 81db24f7d1d8f39f8c1ac5336c2c5ad1e72d783e Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Wed, 17 Jan 2024 07:51:19 -0500 Subject: [PATCH 39/46] Include other changes from main branch --- Source/Chatbook/Tools/Common.wl | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Source/Chatbook/Tools/Common.wl b/Source/Chatbook/Tools/Common.wl index dae7c7bd..a20e4c62 100644 --- a/Source/Chatbook/Tools/Common.wl +++ b/Source/Chatbook/Tools/Common.wl @@ -90,7 +90,7 @@ $toolBox = <| |>; $toolEvaluationResults = <| |>; $toolOptions = <| |>; -$cloudUnsupportedTools = { "WolframLanguageEvaluator", "DocumentationSearcher" }; +$cloudUnsupportedTools = { "DocumentationSearcher" }; $defaultToolOrder = { "DocumentationLookup", @@ -611,7 +611,10 @@ makeToolPrompt[ settings_Association ] := $lastToolPrompt = TemplateObject[ "Tool Name: ", TemplateSlot[ "Name" ], "\nDisplay Name: ", - TemplateSlot[ "DisplayName", DefaultValue :> toDisplayToolName @ TemplateSlot[ "Name" ] ], + TemplateSlot[ + "DisplayName", + DefaultValue :> TemplateExpression @ toDisplayToolName @ TemplateSlot[ "Name" ] + ], "\nDescription: ", TemplateSlot[ "Description" ], "\nSchema:\n", From ac35718bbcb945e05077fa9a1695828698d03827 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Wed, 17 Jan 2024 08:17:46 -0500 Subject: [PATCH 40/46] Disable the ChatPreferences tool for now --- Source/Chatbook/Tools/DefaultTools.wl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/Chatbook/Tools/DefaultTools.wl b/Source/Chatbook/Tools/DefaultTools.wl index b21b0023..4d23c8ee 100644 --- a/Source/Chatbook/Tools/DefaultTools.wl +++ b/Source/Chatbook/Tools/DefaultTools.wl @@ -24,7 +24,9 @@ Needs[ "Wolfram`Chatbook`Utils`" ]; (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*ChatPreferences*) -$defaultChatTools0[ "ChatPreferences" ] = <| + +(* Uncomment the following when the ChatPreferences tool is ready: *) +(* $defaultChatTools0[ "ChatPreferences" ] = <| toolDefaultData[ "ChatPreferences" ], "Icon" -> RawBoxes @ TemplateBox[ { }, "ChatBlockSettingsMenuIcon" ], "Description" -> $chatPreferencesDescription, @@ -53,7 +55,7 @@ $defaultChatTools0[ "ChatPreferences" ] = <| "Required" -> False |> } -|>; +|>; *) (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) From de73e2fd3ab9a536fa4cec1f976c7e1edbb61539 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Mon, 22 Jan 2024 14:26:42 -0500 Subject: [PATCH 41/46] Add "Tools" tab to cloud preferences --- Source/Chatbook/PreferencesContent.wl | 6 +- Source/Chatbook/Settings.wl | 6 +- Source/Chatbook/ToolManager.wl | 337 +++++++++++++++++++++----- 3 files changed, 286 insertions(+), 63 deletions(-) diff --git a/Source/Chatbook/PreferencesContent.wl b/Source/Chatbook/PreferencesContent.wl index 14f9ad28..8eed5971 100644 --- a/Source/Chatbook/PreferencesContent.wl +++ b/Source/Chatbook/PreferencesContent.wl @@ -42,11 +42,7 @@ $inFrontEndScope := MatchQ[ OwnValues @ $preferencesScope, { _ :> $FrontEnd|_Fr (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*Cloud Overrides*) -$displayedPreferencesPages := - If[ $CloudEvaluation, - { "Notebooks", "Services", "Personas"(*, "Tools"*) }, - $preferencesPages - ]; +$displayedPreferencesPages := $preferencesPages; (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) diff --git a/Source/Chatbook/Settings.wl b/Source/Chatbook/Settings.wl index d2b46289..1d64ee48 100644 --- a/Source/Chatbook/Settings.wl +++ b/Source/Chatbook/Settings.wl @@ -48,8 +48,8 @@ $defaultChatSettings = <| "Model" :> $DefaultModel, "Multimodal" -> Automatic, "NotebookWriteMethod" -> Automatic, - "OpenAIKey" -> Automatic, (* TODO: remove this once LLMServices is widely available *) "OpenAIAPICompletionURL" -> "https://api.openai.com/v1/chat/completions", + "OpenAIKey" -> Automatic, (* TODO: remove this once LLMServices is widely available *) "PresencePenalty" -> 0.1, "ProcessingFunctions" :> $DefaultChatProcessingFunctions, "Prompts" -> { }, @@ -60,6 +60,7 @@ $defaultChatSettings = <| "ToolCallFrequency" -> Automatic, "ToolOptions" :> $DefaultToolOptions, "Tools" -> Automatic, + "ToolSelectionType" -> <| |>, "ToolsEnabled" -> Automatic, "TopP" -> 1, "TrackScrollingWhenPlaced" -> Automatic, @@ -105,6 +106,9 @@ $DefaultChatProcessingFunctions = <| (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*CurrentChatSettings*) + +(* TODO: need to support something like CurrentChatSettings[scope, {"key1", "key2", ...}] for nested values *) + GeneralUtilities`SetUsage[ CurrentChatSettings, "\ CurrentChatSettings[obj$, \"key$\"] gives the current chat settings for the CellObject or NotebookObject obj$ for the specified key. CurrentChatSettings[obj$] gives all current chat settings for obj$. diff --git a/Source/Chatbook/ToolManager.wl b/Source/Chatbook/ToolManager.wl index d77f0914..2295b64c 100644 --- a/Source/Chatbook/ToolManager.wl +++ b/Source/Chatbook/ToolManager.wl @@ -61,7 +61,7 @@ CreateLLMToolManagerPanel[ ] := catchMine @ ]; CreateLLMToolManagerPanel[ tools0_List, personas_List ] := - catchMine @ cvExpand @ Module[ + catchMine @ cvExpand @ Catch @ Module[ { globalTools, personaTools, personaToolNames, personaToolLookup, tools, preppedPersonas, preppedTools, personaNames, personaDisplayNames, @@ -90,6 +90,8 @@ CreateLLMToolManagerPanel[ tools0_List, personas_List ] := toolData @ Flatten @ Values @ personaTools ]; + If[ TrueQ @ $cloudNotebooks, Throw @ cloudToolManager @ tools ]; + gridOpts = Sequence[ Spacings -> { 0, 0 }, ItemSize -> { 0, 0 }, @@ -136,44 +138,13 @@ CreateLLMToolManagerPanel[ tools0_List, personas_List ] := DynamicWrapper[ Grid[ { - If[ TrueQ @ $inDialog, dialogHeader[ "Add & Manage LLM Tools" ], Nothing ], + titleSection[ ], (* ----- Install Tools ----- *) - dialogSubHeader[ "Install Tools" ], - dialogBody[ - Grid @ { - { - "Install from", - Button[ - grayDialogButtonLabel[ "LLM Tool Repository \[UpperRightArrow]" ], - ResourceInstallFromRepository[ "LLMTool" ], - Appearance -> "Suppressed", - BaselinePosition -> Baseline, - Method -> "Queued" - ], - Button[ - grayDialogButtonLabel[ "URL" ], - Block[ { PrintTemporary }, ResourceInstallFromURL[ "LLMTool" ] ], - Appearance -> "Suppressed", - BaselinePosition -> Baseline, - Method -> "Queued" - ] - } - } - ], + installToolsSection[ ], (* ----- Configure and Enable Tools ----- *) - dialogSubHeader[ "Manage and Enable Tools" ], - dialogBody[ - Grid @ { - { - "Show enabled tools for:", - scopeSelector @ Dynamic @ scopeMode, - Dynamic @ catchAlways @ toolModelWarning @ scopeMode[ ] - } - }, - { Automatic, { 5, Automatic } } - ], + manageAndEnableToolsSection @ Dynamic[ scopeMode ], dialogBody[ EventHandler[ Grid[ @@ -340,27 +311,7 @@ CreateLLMToolManagerPanel[ tools0_List, personas_List ] := ], { { Automatic, 0 }, Automatic } ], (* ----- Dialog Buttons ----- *) - If[ TrueQ @ $inDialog, - { - Item[ - Framed[ - Button[ - redDialogButtonLabel[ "OK" ], - NotebookClose @ EvaluationNotebook[ ], - Appearance -> "Suppressed", - BaselinePosition -> Baseline, - Method -> "Queued" - ], - FrameStyle -> None, - ImageSize -> { 100, 50 }, - Alignment -> { Center, Center } - ], - Alignment -> { Right, Automatic } - ], - SpanFromLeft - }, - Nothing - ] + dialogButtonSection[ ] }, Alignment -> { Left, Top }, BaseStyle -> $baseStyle, @@ -896,7 +847,10 @@ nonConfigurableTooltip // endDefinition; (*deleteTool*) deleteTool // beginDefinition; -deleteTool[ KeyValuePattern @ { "CanonicalName" -> cName_, "ResourceName" -> rName_String } ] := ( +deleteTool[ KeyValuePattern @ { "CanonicalName" -> cName_, "ResourceName" -> rName_String } ] := + deleteTool[ cName, rName ]; + +deleteTool[ cName_, rName_String ] := ( unsetCV[ $FrontEnd, "ToolSelections" , cName ]; unsetCV[ $FrontEnd, "ToolSelectionType", cName ]; ResourceUninstall[ "LLMTool", rName ] @@ -1235,6 +1189,275 @@ iconData // beginDefinition; iconData[ name_String, color_ ] := Insert[ chatbookIcon[ "ToolManager"<>name, False ], color, { 1, 1, 1 } ]; iconData // endDefinition; +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Dialog Elements*) + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*titleSection*) +titleSection // beginDefinition; +titleSection[ ] := If[ TrueQ @ $inDialog, dialogHeader[ "Add & Manage LLM Tools" ], Nothing ]; +titleSection // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*installToolsSection*) +installToolsSection // beginDefinition; + +installToolsSection[ ] := Sequence[ + dialogSubHeader[ "Install Tools" ], + dialogBody[ + Grid @ { + { + "Install from", + Button[ + grayDialogButtonLabel[ "LLM Tool Repository \[UpperRightArrow]" ], + If[ $CloudEvaluation, SetOptions[ EvaluationNotebook[ ], DockedCells -> Inherited ] ]; + ResourceInstallFromRepository[ "LLMTool" ], + Appearance -> "Suppressed", + BaselinePosition -> Baseline, + Method -> "Queued" + ], + Button[ + grayDialogButtonLabel[ "URL" ], + If[ $CloudEvaluation, SetOptions[ EvaluationNotebook[ ], DockedCells -> Inherited ] ]; + Block[ { PrintTemporary }, ResourceInstallFromURL[ "LLMTool" ] ], + Appearance -> "Suppressed", + BaselinePosition -> Baseline, + Method -> "Queued" + ] + } + } + ] +]; + +installToolsSection // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*manageAndEnableToolsSection*) +manageAndEnableToolsSection // beginDefinition; + +manageAndEnableToolsSection[ Dynamic[ scopeMode_ ] ] := Sequence[ + manageAndEnableToolsSection[ ], + dialogBody[ + Grid @ { + { + "Show enabled tools for:", + scopeSelector @ Dynamic @ scopeMode, + Dynamic @ catchAlways @ toolModelWarning @ scopeMode[ ] + } + }, + { Automatic, { 5, Automatic } } + ] +]; + +manageAndEnableToolsSection[ ] := dialogSubHeader[ "Manage and Enable Tools" ]; + +manageAndEnableToolsSection // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*dialogButtonSection*) +dialogButtonSection // beginDefinition; + +dialogButtonSection[ ] := + If[ TrueQ @ $inDialog, + { + Item[ + Framed[ + Button[ + redDialogButtonLabel[ "OK" ], + NotebookClose @ EvaluationNotebook[ ], + Appearance -> "Suppressed", + BaselinePosition -> Baseline, + Method -> "Queued" + ], + FrameStyle -> None, + ImageSize -> { 100, 50 }, + Alignment -> { Center, Center } + ], + Alignment -> { Right, Automatic } + ], + SpanFromLeft + }, + Nothing + ]; + +dialogButtonSection // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Section::Closed:: *) +(*Cloud Definitions*) + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*cloudToolManager*) +cloudToolManager // beginDefinition; + +cloudToolManager[ tools: { __Association } ] := Grid[ + { + titleSection[ ], + + (* ----- Install Tools ----- *) + installToolsSection[ ], + + (* ----- Configure and Enable Tools ----- *) + manageAndEnableToolsSection[ ], + + (* ----- Tool Grid ----- *) + dialogBody[ cloudToolGrid @ tools, { { Automatic, 0 }, Automatic } ], + + (* ----- Dialog Buttons ----- *) + dialogButtonSection[ ] + }, + Alignment -> { Left, Top }, + BaseStyle -> $baseStyle, + Dividers -> { + False, + { + If[ TrueQ @ $inDialog, Sequence @@ { False, $dividerCol }, False ], + False, + $dividerCol, + False, + { False } + } + }, + ItemSize -> { Automatic, 0 }, + Spacings -> { 0, 0 } +]; + +cloudToolManager // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsection::Closed:: *) +(*cloudToolGrid*) +cloudToolGrid // beginDefinition; + +cloudToolGrid[ tools: { __Association } ] := Grid[ + Join[ + { { + "Tool", + "", + "", + "", + "Enabled" + } }, + cloudToolGridRow /@ tools + ], + Alignment -> { Left, Center }, + Background -> { White, { GrayLevel[ 0.898 ], White } }, + BaseStyle -> "Text", + Dividers -> { True, All }, + FrameStyle -> GrayLevel[ 0.898 ] +]; + +cloudToolGrid // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*cloudToolGridRow*) +cloudToolGridRow // beginDefinition; + +cloudToolGridRow[ tool_Association ] := { + Grid[ + { { + Pane[ + tool[ "Icon" ], + ImageSize -> { 22, 20 }, + ImageSizeAction -> "ShrinkToFit" + ], + tool[ "CanonicalName" ] + } }, + Alignment -> { { Center, Left }, Center }, + Spacings -> 0.5 + ], + Item[ "", ItemSize -> Fit ], + cloudDeleteToolButton @ tool, + cloudConfigureToolButton @ tool, + Item[ cloudToolEnablePopup @ tool, Alignment -> Right ] +}; + +cloudToolGridRow // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*cloudToolEnablePopup*) +cloudToolEnablePopup // beginDefinition; + +cloudToolEnablePopup[ KeyValuePattern[ "CanonicalName" -> name_String ] ] := + cloudToolEnablePopup @ name; + +cloudToolEnablePopup[ name_String ] := + PopupMenu[ + Dynamic[ + Replace[ CurrentChatSettings[ $FrontEnd, "ToolSelectionType" ][ name ], Except[ None|All ] -> Inherited ], + Function[ + CurrentChatSettings[ $FrontEnd, "ToolSelectionType" ] = + DeleteCases[ + Append[ + Replace[ + CurrentChatSettings[ $FrontEnd, "ToolSelectionType" ], + Except[ _? AssociationQ ] -> <| |> + ], + name -> #1 + ], + Inherited + ] + ] + ], + { + All -> "Always enabled", + Inherited -> "Automatic by persona", + None -> "Never enabled" + } + ]; + +cloudToolEnablePopup // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*cloudConfigureToolButton*) +cloudConfigureToolButton // beginDefinition; + +(* TODO: needs a definition for configurable tools *) + +cloudConfigureToolButton[ tool_Association ] := + Tooltip[ + Dynamic @ iconData[ "Cog", GrayLevel[ 0.8 ] ], + nonConfigurableTooltip @ tool + ]; + +cloudConfigureToolButton // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*cloudDeleteToolButton*) +cloudDeleteToolButton // beginDefinition; + +cloudDeleteToolButton[ tool_Association? deletableToolQ ] := + With[ { cName = tool[ "CanonicalName" ], rName = tool[ "ResourceName" ] }, + Button[ + $cloudDeleteToolButtonLabel, + deleteTool[ cName, rName ], + Appearance -> "Suppressed" + ] + ]; + +cloudDeleteToolButton[ tool_Association ] := + Tooltip[ + Dynamic @ iconData[ "Bin", GrayLevel[ 0.8 ] ], + nonDeletableTooltip @ tool + ]; + +cloudDeleteToolButton // endDefinition; + +$cloudDeleteToolButtonLabel = Mouseover[ + Dynamic @ iconData[ "Bin", GrayLevel[ 0.65 ] ], + Dynamic @ iconData[ "Bin", $activeBlue ] +]; + (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*Package Footer*) From a647aa66d401b7add416c66a92d084e8004e7d06 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Mon, 22 Jan 2024 14:38:58 -0500 Subject: [PATCH 42/46] Add an "Insert Chat Cell" menu to the cloud toolbar --- Source/Chatbook/CloudToolbar.wl | 114 ++++++++++++++++++++++++-------- 1 file changed, 85 insertions(+), 29 deletions(-) diff --git a/Source/Chatbook/CloudToolbar.wl b/Source/Chatbook/CloudToolbar.wl index 1846e0ec..c007d130 100644 --- a/Source/Chatbook/CloudToolbar.wl +++ b/Source/Chatbook/CloudToolbar.wl @@ -27,6 +27,9 @@ $notebookTypeLabelOptions = Sequence[ FontWeight -> "DemiBold" ]; +$buttonHeight = 20; +$menuItemIconSize = { 20, 20 }; + (* ::**************************************************************************************************************:: *) (* ::Section::Closed:: *) (*Docked Cell Contents*) @@ -41,12 +44,13 @@ makeChatCloudDockedCellContents[ ] := Grid[ { Item[ $cloudChatBanner, Alignment -> Left ], Item[ "", ItemSize -> Fit ], + cloudCellInsertMenu[ ], cloudPreferencesButton[ ] } }, - Alignment -> { Left, Baseline }, - Spacings -> { 2, 0 }, - BaseStyle -> { "Text", FontSize -> 14, FontColor -> GrayLevel[ 0.4 ] }, + Alignment -> { Left, Center }, + Spacings -> { 0.7, 0 }, + BaseStyle -> { "Text", FontSize -> 14 }, FrameStyle -> Directive[ Thickness[ 2 ], GrayLevel[ 0.9 ] ] ]; @@ -54,44 +58,96 @@ makeChatCloudDockedCellContents // endDefinition; (* ::**************************************************************************************************************:: *) (* ::Subsubsection::Closed:: *) -(*cloudPreferencesButton*) -cloudPreferencesButton // beginDefinition; +(*cloudCellInsertMenu*) +cloudCellInsertMenu // beginDefinition; + +cloudCellInsertMenu[ ] := ActionMenu[ + toolbarButtonLabel @ Row @ { "Insert Chat Cell", Spacer[ 5 ], RawBoxes @ TemplateBox[ { }, "ChatInputIcon" ] }, + { + insertStyleMenuItem[ "ChatInputIcon", "ChatInput", "'" ], + insertStyleMenuItem[ "SideChatIcon", "SideChat", "' '" ], + insertStyleMenuItem[ "ChatSystemIcon", "ChatSystemInput", "' ' '" ], + Delimiter, + insertStyleMenuItem[ None, "ChatDelimiter", "~" ], + insertStyleMenuItem[ None, "ChatBlockDivider", "~ ~" ] + }, + FrameMargins -> { { 0, 0 }, { 0, 0 } } +]; -cloudPreferencesButton[ ] := Enclose[ - Module[ { iconTemplate, colorTemplate, buttonLabel, mouseover, buttonIcon, button }, +cloudCellInsertMenu // endDefinition; - iconTemplate = ConfirmMatch[ - chatbookIcon[ "ToolManagerCog", False ], - RawBoxes @ TemplateBox[ { }, __ ], - "IconTemplate" - ]; +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*toolbarButtonLabel*) +toolbarButtonLabel // beginDefinition; + +toolbarButtonLabel[ label_ ] := Pane[ + label, + ImageSize -> { Automatic, $buttonHeight }, + Alignment -> { Center, Baseline }, + FrameMargins -> { { 8, 0 }, { 2, 2 } } +]; - colorTemplate = ConfirmMatch[ - Insert[ iconTemplate, #, { 1, 1, 1 } ], - RawBoxes @ TemplateBox[ { _? ColorQ }, __ ], - "Colorize" - ] &; +toolbarButtonLabel // endDefinition; - buttonLabel = Grid[ - { { Style[ "Chat Settings", FontColor -> # ], colorTemplate @ # } }, - Alignment -> { Left, Baseline } - ] &; +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*insertStyleMenuItem*) +insertStyleMenuItem // beginDefinition; - mouseover = Mouseover[ buttonLabel @ GrayLevel[ 0.25 ], buttonLabel @ Hue[ 0.59, 0.9, 0.93 ] ]; +insertStyleMenuItem[ icon_String, style_, shortcut_ ] := + insertStyleMenuItem[ chatbookIcon[ icon, False ], style, shortcut ]; - buttonIcon = DeleteCases[ - mouseover (*/. HoldPattern[ ImageSize -> _ ] :> ImageSize -> { 22, 22 }*), - BaselinePosition -> _, - Infinity - ]; +insertStyleMenuItem[ None, style_, shortcut_ ] := + insertStyleMenuItem[ Spacer[ 0 ], style, shortcut ]; - button = Button[ buttonIcon, toggleCloudPreferences @ EvaluationNotebook[ ], Method -> "Queued" ]; +insertStyleMenuItem[ icon_, style_, shortcut_ ] := + Grid[ + { { + Pane[ icon, ImageSize -> $menuItemIconSize ], + Item[ style, ItemSize -> 12 ], + Style[ shortcut, FontColor -> GrayLevel[ 0.75 ] ] + } }, + Alignment -> { { Center, Left, Right }, Center } + ] :> insertCellStyle @ style; - cloudPreferencesButton[ ] = Pane[ button, FrameMargins -> { { 0, 10 }, { 0, 0 } } ] +insertStyleMenuItem // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*insertCellStyle*) +insertCellStyle // beginDefinition; + +insertCellStyle[ style_String ] := + insertCellStyle[ style, EvaluationNotebook[ ] ]; + +insertCellStyle[ style_String, nbo_NotebookObject ] := Enclose[ + Module[ { tag, cell, cellObject }, + tag = ConfirmBy[ CreateUUID[ ], StringQ, "UUID" ]; + cell = Cell[ "", style, CellTags -> tag ]; + SelectionMove[ nbo, After, Notebook ]; + NotebookWrite[ nbo, cell ]; + cellObject = ConfirmMatch[ First[ Cells[ nbo, CellTags -> tag ], $Failed ], _CellObject, "CellObject" ]; + If[ style =!= "ChatDelimiter", SelectionMove[ cellObject, Before, CellContents ] ]; + SetOptions[ cellObject, CellTags -> Inherited ]; + cellObject ], throwInternalFailure ]; +insertCellStyle // endDefinition; + +(* ::**************************************************************************************************************:: *) +(* ::Subsubsection::Closed:: *) +(*cloudPreferencesButton*) +cloudPreferencesButton // beginDefinition; + +cloudPreferencesButton[ ] := Button[ + toolbarButtonLabel @ Row @ { "Chat Settings", Spacer[ 5 ], RawBoxes @ TemplateBox[ { }, "AdvancedSettings" ] }, + toggleCloudPreferences @ EvaluationNotebook[ ], + FrameMargins -> { { 0, 4 }, { 0, 0 } } +]; + cloudPreferencesButton // endDefinition; (* ::**************************************************************************************************************:: *) From d804322234cfa7395ef61f6fd1cf68d31c7fd438 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Mon, 22 Jan 2024 14:52:08 -0500 Subject: [PATCH 43/46] Use `Import` as a fallback for the WebFetcher tool when `StartWebSession` fails --- Source/Chatbook/Tools/DefaultTools.wl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Source/Chatbook/Tools/DefaultTools.wl b/Source/Chatbook/Tools/DefaultTools.wl index 4d23c8ee..69e96897 100644 --- a/Source/Chatbook/Tools/DefaultTools.wl +++ b/Source/Chatbook/Tools/DefaultTools.wl @@ -584,6 +584,9 @@ fetchWebText[ url_String, session_WebSessionObject ] := Enclose[ shortenWebText @ niceWebText @ Import[ url, { "HTML", "Plaintext" } ] & ]; +fetchWebText[ url_String, _Missing | _? FailureQ ] := + shortenWebText @ niceWebText @ Import[ url, { "HTML", "Plaintext" } ]; + fetchWebText // endDefinition; (* ::**************************************************************************************************************:: *) @@ -635,7 +638,13 @@ validWebSessionQ[ ___ ] := False; (* ::Subsubsection::Closed:: *) (*startWebSession*) startWebSession // beginDefinition; -startWebSession[ ] := $currentWebSession = StartWebSession[ Visible -> $webSessionVisible ]; + +startWebSession[ ] := $currentWebSession = + If[ TrueQ @ $CloudEvaluation, + Missing[ "NotAvailable" ], + StartWebSession[ Visible -> $webSessionVisible ] + ]; + startWebSession // endDefinition; (* ::**************************************************************************************************************:: *) From cbde5f61898bfa6d93753e05f972d23e5fed79da Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Mon, 22 Jan 2024 15:34:47 -0500 Subject: [PATCH 44/46] Bugfix: FIx StopChat button failures in cloud --- Source/Chatbook/Actions.wl | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Source/Chatbook/Actions.wl b/Source/Chatbook/Actions.wl index ea46a3c1..cd255e8d 100644 --- a/Source/Chatbook/Actions.wl +++ b/Source/Chatbook/Actions.wl @@ -513,10 +513,23 @@ ensureChatInputCell // endDefinition; (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) (*chatOutputCellQ*) -chatOutputCellQ[ cell_CellObject ] := chatOutputCellQ[ cell ] = chatOutputCellQ[ cell, Developer`CellInformation @ cell ]; -chatOutputCellQ[ cell_, KeyValuePattern[ "Style" -> $$chatOutputStyle ] ] := True; -chatOutputCellQ[ cell_CellObject, ___ ] := ($badCellObject = cell; $badCell = NotebookRead @ cell; False); -chatOutputCellQ[ ___ ] := False; +chatOutputCellQ[ cell_CellObject ] := chatOutputCellQ[ cell ] = + chatOutputCellQ[ cell, Developer`CellInformation @ cell ]; + +chatOutputCellQ[ cell_, KeyValuePattern[ "Style" -> $$chatOutputStyle ] ] := + True; + +chatOutputCellQ[ cell_CellObject, KeyValuePattern[ "Style" -> "Output" ] ] /; $cloudNotebooks := + MatchQ[ CurrentValue[ cell, { TaggingRules, "ChatNotebookSettings", "CellObject" } ], _CellObject ]; + +chatOutputCellQ[ cell_CellObject, ___ ] := ( + $badCellObject = cell; + $badCell = NotebookRead @ cell; + False +); + +chatOutputCellQ[ ___ ] := + False; (* ::**************************************************************************************************************:: *) (* ::Subsection::Closed:: *) From 24b4bb82f3cdba7a219d683657705bd385889d40 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Mon, 22 Jan 2024 15:54:17 -0500 Subject: [PATCH 45/46] Make ChatOutput cells inherit cell tags from the corresponding chat input --- Source/Chatbook/SendChat.wl | 40 ++++++++++++++++++++++++++----------- 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/Source/Chatbook/SendChat.wl b/Source/Chatbook/SendChat.wl index e0d96f91..f8429a65 100644 --- a/Source/Chatbook/SendChat.wl +++ b/Source/Chatbook/SendChat.wl @@ -67,7 +67,7 @@ $buffer = ""; sendChat // beginDefinition; sendChat[ evalCell_, nbo_, settings0_ ] /; $useLLMServices := catchTopAs[ ChatbookAction ] @ Enclose[ - Module[ { cells0, cells, target, settings, messages, data, persona, cell, cellObject, container, task }, + Module[ { cells0, cells, target, settings, messages, data, persona, cellTags, cell, cellObject, container, task }, initFETaskWidget @ nbo; @@ -122,9 +122,11 @@ sendChat[ evalCell_, nbo_, settings0_ ] /; $useLLMServices := catchTopAs[ Chatbo |>; $reformattedCell = None; + cellTags = CurrentValue[ evalCell, CellTags ]; cell = activeAIAssistantCell[ container, - Association[ settings, "Container" :> container, "CellObject" :> cellObject, "Task" :> task ] + Association[ settings, "Container" :> container, "CellObject" :> cellObject, "Task" :> task ], + cellTags ]; Quiet[ @@ -178,7 +180,11 @@ sendChat[ evalCell_, nbo_, settings0_ ] /; $useLLMServices := catchTopAs[ Chatbo (* TODO: this definition is obsolete once LLMServices is widely available: *) sendChat[ evalCell_, nbo_, settings0_ ] := catchTopAs[ ChatbookAction ] @ Enclose[ - Module[ { cells0, cells, target, settings, id, key, messages, req, data, persona, cell, cellObject, container, task }, + Module[ + { + cells0, cells, target, settings, id, key, messages, req, data, persona, + cellTags, cell, cellObject, container, task + }, initFETaskWidget @ nbo; @@ -240,9 +246,11 @@ sendChat[ evalCell_, nbo_, settings0_ ] := catchTopAs[ ChatbookAction ] @ Enclos |>; $reformattedCell = None; + cellTags = CurrentValue[ evalCell, CellTags ]; cell = activeAIAssistantCell[ container, - Association[ settings, "Container" :> container, "CellObject" :> cellObject, "Task" :> task ] + Association[ settings, "Container" :> container, "CellObject" :> cellObject, "Task" :> task ], + cellTags ]; Quiet[ @@ -1695,12 +1703,13 @@ prepareChatOutputPage // endDefinition; activeAIAssistantCell // beginDefinition; activeAIAssistantCell // Attributes = { HoldFirst }; -activeAIAssistantCell[ container_, settings_Association? AssociationQ ] := - activeAIAssistantCell[ container, settings, Lookup[ settings, "ShowMinimized", Automatic ] ]; +activeAIAssistantCell[ container_, settings_Association? AssociationQ, cellTags_ ] := + activeAIAssistantCell[ container, settings, cellTags, Lookup[ settings, "ShowMinimized", Automatic ] ]; activeAIAssistantCell[ container_, settings: KeyValuePattern[ "CellObject" :> cellObject_ ], + cellTags0_, minimized_ ] /; $cloudNotebooks := With[ @@ -1709,7 +1718,8 @@ activeAIAssistantCell[ id = $SessionID, reformat = dynamicAutoFormatQ @ settings, task = Lookup[ settings, "Task" ], - formatter = getFormattingFunction @ settings + formatter = getFormattingFunction @ settings, + cellTags = Replace[ cellTags0, Except[ _String | { ___String } ] :> Inherited ] }, Module[ { x = 0 }, ClearAttributes[ { x, cellObject }, Temporary ]; @@ -1751,6 +1761,7 @@ activeAIAssistantCell[ Selectable -> False, Editable -> False, CellDingbat -> Cell[ BoxData @ makeActiveOutputDingbat @ settings, Background -> None ], + CellTags -> cellTags, CellTrayWidgets -> <| "ChatFeedback" -> <| "Visible" -> False |> |>, TaggingRules -> <| "ChatNotebookSettings" -> smallSettings @ settings |> ] @@ -1760,6 +1771,7 @@ activeAIAssistantCell[ activeAIAssistantCell[ container_, settings: KeyValuePattern[ "CellObject" :> cellObject_ ], + cellTags0_, minimized_ ] := With[ @@ -1769,7 +1781,8 @@ activeAIAssistantCell[ reformat = dynamicAutoFormatQ @ settings, task = Lookup[ settings, "Task" ], uuid = container[ "UUID" ], - formatter = getFormattingFunction @ settings + formatter = getFormattingFunction @ settings, + cellTags = Replace[ cellTags0, Except[ _String | { ___String } ] :> Inherited ] }, Cell[ BoxData @ TagBox[ @@ -1797,6 +1810,7 @@ activeAIAssistantCell[ ], CellDingbat -> Cell[ BoxData @ makeActiveOutputDingbat @ settings, Background -> None ], CellEditDuplicate -> False, + CellTags -> cellTags, CellTrayWidgets -> <| "ChatFeedback" -> <| "Visible" -> False |> |>, CodeAssistOptions -> { "AutoDetectHyperlinks" -> False }, Editable -> True, @@ -1957,7 +1971,7 @@ writeReformattedCell[ settings_, None, cell_CellObject ] := writeReformattedCell[ settings_, string_String, cell_CellObject ] := Enclose[ Block[ { $dynamicText = False }, - Module[ { tag, scroll, open, label, pageData, uuid, new, output, createTask, info }, + Module[ { tag, scroll, open, label, pageData, cellTags, uuid, new, output, createTask, info }, tag = ConfirmMatch[ Replace[ CurrentValue[ cell, { TaggingRules, "MessageTag" } ], $Failed -> Inherited ], @@ -1969,8 +1983,9 @@ writeReformattedCell[ settings_, string_String, cell_CellObject ] := Enclose[ open = $lastOpen = cellOpenQ @ cell; label = RawBoxes @ TemplateBox[ { }, "MinimizedChat" ]; pageData = CurrentValue[ cell, { TaggingRules, "PageData" } ]; + cellTags = CurrentValue[ cell, CellTags ]; uuid = CreateUUID[ ]; - new = reformatCell[ settings, string, tag, open, label, pageData, uuid ]; + new = reformatCell[ settings, string, tag, open, label, pageData, cellTags, uuid ]; output = CellObject @ uuid; $lastChatString = string; @@ -2068,7 +2083,7 @@ scrollOutput // endDefinition; (*reformatCell*) reformatCell // beginDefinition; -reformatCell[ settings_, string_, tag_, open_, label_, pageData_, uuid_ ] := UsingFrontEnd @ Enclose[ +reformatCell[ settings_, string_, tag_, open_, label_, pageData_, cellTags_, uuid_ ] := UsingFrontEnd @ Enclose[ Module[ { formatter, content, rules, dingbat }, formatter = Confirm[ getFormattingFunction @ settings, "GetFormattingFunction" ]; @@ -2103,6 +2118,7 @@ reformatCell[ settings_, string_, tag_, open_, label_, pageData_, uuid_ ] := Usi ], GeneratedCell -> True, CellAutoOverwrite -> True, + CellTags -> cellTags, TaggingRules -> rules, If[ TrueQ[ rules[ "PageData", "PageCount" ] > 1 ], CellDingbat -> Cell[ BoxData @ TemplateBox[ { dingbat }, "AssistantIconTabbed" ], Background -> None ], @@ -2118,7 +2134,7 @@ reformatCell[ settings_, string_, tag_, open_, label_, pageData_, uuid_ ] := Usi ExpressionUUID -> uuid ] ], - throwInternalFailure[ reformatCell[ settings, string, tag, open, label, pageData, uuid ], ## ] & + throwInternalFailure ]; reformatCell // endDefinition; From 4cc68ef2bf57a7b2a6a27e59f59f03992f232633 Mon Sep 17 00:00:00 2001 From: Rick Hennigan Date: Mon, 22 Jan 2024 16:19:17 -0500 Subject: [PATCH 46/46] Revert changes made to default tools for CodeAssistant during debugging --- .../Personas/CodeAssistant/LLMConfiguration.wl | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/LLMConfiguration/Personas/CodeAssistant/LLMConfiguration.wl b/LLMConfiguration/Personas/CodeAssistant/LLMConfiguration.wl index eff5a7ef..02311a39 100644 --- a/LLMConfiguration/Personas/CodeAssistant/LLMConfiguration.wl +++ b/LLMConfiguration/Personas/CodeAssistant/LLMConfiguration.wl @@ -2,13 +2,6 @@ "BasePrompt" -> { "WolframLanguageStyle" }, "DisplayName" -> "Code Assistant", "Icon" -> RawBoxes @ TemplateBox[ { }, "ChatIconCodeAssistant" ], - "Tools" -> { - "DocumentationLookup", - "DocumentationSearcher", - "WolframAlpha", - "WolframLanguageEvaluator", - "WebSearcher", - "WebFetcher" - }, + "Tools" -> { "DocumentationLookup", "DocumentationSearcher", "WolframAlpha", "WolframLanguageEvaluator" }, "Description" -> "Help with writing and generating Wolfram Language code" |> \ No newline at end of file