diff --git a/core/modules/startup/rootwidget.js b/core/modules/startup/rootwidget.js
index d81e07aee28..0f67634d180 100644
--- a/core/modules/startup/rootwidget.js
+++ b/core/modules/startup/rootwidget.js
@@ -52,7 +52,8 @@ exports.startup = function() {
basicAuthUsername: params["basic-auth-username"],
basicAuthUsernameFromStore: params["basic-auth-username-from-store"],
basicAuthPassword: params["basic-auth-password"],
- basicAuthPasswordFromStore: params["basic-auth-password-from-store"]
+ basicAuthPasswordFromStore: params["basic-auth-password-from-store"],
+ bearerAuthTokenFromStore: params["bearer-auth-token-from-store"]
});
});
$tw.rootWidget.addEventListener("tm-http-cancel-all-requests",function(event) {
diff --git a/core/modules/utils/dom/http.js b/core/modules/utils/dom/http.js
index 65bdfd1e526..f16f1c51274 100644
--- a/core/modules/utils/dom/http.js
+++ b/core/modules/utils/dom/http.js
@@ -104,6 +104,8 @@ basicAuthUsername: plain username for basic authentication
basicAuthUsernameFromStore: name of password store entry containing username
basicAuthPassword: plain password for basic authentication
basicAuthPasswordFromStore: name of password store entry containing password
+bearerAuthToken: plain text token for bearer authentication
+bearerAuthTokenFromStore: name of password store entry contain bear authorization token
*/
function HttpClientRequest(options) {
var self = this;
@@ -135,8 +137,11 @@ function HttpClientRequest(options) {
});
this.basicAuthUsername = options.basicAuthUsername || (options.basicAuthUsernameFromStore && $tw.utils.getPassword(options.basicAuthUsernameFromStore)) || "";
this.basicAuthPassword = options.basicAuthPassword || (options.basicAuthPasswordFromStore && $tw.utils.getPassword(options.basicAuthPasswordFromStore)) || "";
+ this.bearerAuthToken = options.bearerAuthToken || (options.bearerAuthTokenFromStore && $tw.utils.getPassword(options.bearerAuthTokenFromStore)) || "";
if(this.basicAuthUsername && this.basicAuthPassword) {
this.requestHeaders.Authorization = "Basic " + $tw.utils.base64Encode(this.basicAuthUsername + ":" + this.basicAuthPassword);
+ } else if(this.bearerAuthToken) {
+ this.requestHeaders.Authorization = "Bearer " + this.bearerAuthToken;
}
}
diff --git a/core/modules/widgets/action-createtiddler.js b/core/modules/widgets/action-createtiddler.js
index b49eaad2059..4b883d0c34a 100644
--- a/core/modules/widgets/action-createtiddler.js
+++ b/core/modules/widgets/action-createtiddler.js
@@ -104,7 +104,7 @@ CreateTiddlerWidget.prototype.invokeAction = function(triggeringWidget,event) {
}
this.setVariable("createTiddler-title",title);
this.setVariable("createTiddler-draftTitle",draftTitle);
- this.refreshChildren();
+ this.refreshChildren([]);
return true; // Action was invoked
};
diff --git a/core/modules/widgets/importvariables.js b/core/modules/widgets/importvariables.js
index 3e1ac3fc6fc..e394853a83f 100644
--- a/core/modules/widgets/importvariables.js
+++ b/core/modules/widgets/importvariables.js
@@ -107,6 +107,7 @@ ImportVariablesWidget.prototype.execute = function(tiddlerList) {
Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering
*/
ImportVariablesWidget.prototype.refresh = function(changedTiddlers) {
+ changedTiddlers = changedTiddlers || {};
// Recompute our attributes and the filter list
var changedAttributes = this.computeAttributes(),
tiddlerList = this.wiki.filterTiddlers(this.getAttribute("filter"),this);
diff --git a/editions/prerelease/tiddlers/system/DefaultTiddlers.tid b/editions/prerelease/tiddlers/system/DefaultTiddlers.tid
index c947fd59a2f..8894ab50bc4 100644
--- a/editions/prerelease/tiddlers/system/DefaultTiddlers.tid
+++ b/editions/prerelease/tiddlers/system/DefaultTiddlers.tid
@@ -2,7 +2,4 @@ created: 20131127215321439
modified: 20140912135951542
title: $:/DefaultTiddlers
-[[TiddlyWiki Pre-release]]
-HelloThere
-GettingStarted
-Community
+$:/plugins/tiddlywiki/ai-tools
\ No newline at end of file
diff --git a/editions/prerelease/tiddlywiki.info b/editions/prerelease/tiddlywiki.info
index c469dcf996a..6e242822155 100644
--- a/editions/prerelease/tiddlywiki.info
+++ b/editions/prerelease/tiddlywiki.info
@@ -17,7 +17,9 @@
"tiddlywiki/jszip",
"tiddlywiki/confetti",
"tiddlywiki/dynannotate",
- "tiddlywiki/tour"
+ "tiddlywiki/tour",
+ "tiddlywiki/markdown",
+ "tiddlywiki/ai-tools"
],
"themes": [
"tiddlywiki/vanilla",
diff --git a/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid b/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid
index e10c566b9d7..cf4e7ba34f2 100644
--- a/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid
+++ b/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid
@@ -3,6 +3,4 @@ modified: 20140912135951542
title: $:/DefaultTiddlers
type: text/vnd.tiddlywiki
-HelloThere
-GettingStarted
-Community
+$:/plugins/tiddlywiki/ai-tools
\ No newline at end of file
diff --git a/editions/tw5.com/tiddlers/system/SiteTitle.tid b/editions/tw5.com/tiddlers/system/SiteTitle.tid
index c407957d69b..b198a938868 100644
--- a/editions/tw5.com/tiddlers/system/SiteTitle.tid
+++ b/editions/tw5.com/tiddlers/system/SiteTitle.tid
@@ -3,4 +3,4 @@ modified: 20131211131023829
title: $:/SiteTitle
type: text/vnd.tiddlywiki
-TiddlyWiki @@font-size:small; v<>@@
\ No newline at end of file
+TiddlyWiki AI Tools Plugin
diff --git a/editions/tw5.com/tiddlywiki.info b/editions/tw5.com/tiddlywiki.info
index 2f3ddade89c..80f4ac3d42d 100644
--- a/editions/tw5.com/tiddlywiki.info
+++ b/editions/tw5.com/tiddlywiki.info
@@ -7,7 +7,9 @@
"tiddlywiki/menubar",
"tiddlywiki/confetti",
"tiddlywiki/dynannotate",
- "tiddlywiki/tour"
+ "tiddlywiki/tour",
+ "tiddlywiki/qrcode",
+ "tiddlywiki/ai-tools"
],
"themes": [
"tiddlywiki/vanilla",
diff --git a/plugins/tiddlywiki/ai-tools/docs.tid b/plugins/tiddlywiki/ai-tools/docs.tid
new file mode 100644
index 00000000000..a000cf90536
--- /dev/null
+++ b/plugins/tiddlywiki/ai-tools/docs.tid
@@ -0,0 +1,53 @@
+title: $:/plugins/tiddlywiki/ai-tools/docs
+
+!! Setting Up
+
+See the ''settings'' tab for set up instructions.
+
+!! Live AI Conversations in ~TiddlyWiki
+
+# Click the {{||$:/plugins/tiddlywiki/ai-tools/page-menu}} icon in the sidebar to open a new conversation
+# Choose the server from the dropdown:
+#* ''Locally running Llamafile server''
+#* ''~OpenAI Service'' (requires API key to be specified in ''settings'')
+# Type a prompt for the LLM in the text box
+#* If using ~OpenAI it is possible to attach a single image to a prompt
+# Click "Send" and wait for the output of the LLM
+
+!! Import ~ChatGPT Conversation Archives
+
+# [[Follow the instructions|https://help.openai.com/en/articles/7260999-how-do-i-export-my-chatgpt-history-and-data]] to request an export of your ~ChatGPT data
+# You will receive a link to download your data as a ZIP file
+# Download and unzip the file
+# Locate the file `conversations.json` within the archive and import it into your TiddlyWiki
+# Visit the ''tools'' tab and locate your `conversations.json` tiddler
+# Click the associated ''import'' button
+# See the imported conversations listed in the ''tools'' tab
+# The imported tiddler `conversations.json` is no longer required and can be deleted
+
+!! Conversation Format
+
+This plugin defines a simple schema for representing conversations with an LLM.
+
+In a nutshell, tiddlers tagged <> define conversations. The individual messages are tiddlers that are tagged with the title of the conversation tiddler.
+
+Currently, the ordering of the messages is determined by the value of their "created" field. The ordering defined by the tag mechanism is ignored. It is intended to change this behaviour so that the ordering of messages is defined by the tag mechanism.
+
+The fields with defined meanings for conversation tiddlers are:
+
+|!Field |!Description |
+|''system-prompt'' |Defines the system prompt for the conversation |
+|''tags'' |Must include <> |
+|''text'' |Optional description or notes displayed at the top of the conversation |
+|''current-response-image'' |Optional title of an image tiddler to be attached to the current user response |
+|''current-response-text'' |Text of the current user response before it is sent |
+
+The fields with defined meanings for conversation tiddlers are:
+
+|!Field |!Description |
+|''created'' |Creation date of the message (currently used for ordering) |
+|''image'' |Optional image associated with this message |
+|''role'' |Possible values include ''user'' and ''assistant'' |
+|''tags'' |Must include the title of the parent conversation |
+|''type'' |Typically ''text/markdown'' |
+
diff --git a/plugins/tiddlywiki/ai-tools/globals.tid b/plugins/tiddlywiki/ai-tools/globals.tid
new file mode 100644
index 00000000000..e6436913d57
--- /dev/null
+++ b/plugins/tiddlywiki/ai-tools/globals.tid
@@ -0,0 +1,219 @@
+title: $:/plugins/tiddlywiki/ai-tools/globals
+tags: $:/tags/Global
+
+\function ai-tools-default-llm-completion-server()
+[all[shadows+tiddlers]tag[$:/tags/AI/CompletionServer]sort[caption]first[]]
+\end
+
+
+\procedure ai-tools-get-llm-completion(conversationTitle,resultTitlePrefix,resultTags,ai-tools-status-title,completionServer)
+<$let
+ completionServer={{{ [!is[blank]else] }}}
+>
+ <$importvariables filter="[]">
+ <$wikify name="json" text=<>>
+ <$action-log message="ai-tools-get-llm-completion"/>
+ <$action-log/>
+ <$action-sendmessage
+ $message="tm-http-request"
+ url={{{ [get[url]] }}}
+ body=<>
+ header-content-type="application/json"
+ bearer-auth-token-from-store="openai-secret-key"
+ method="POST"
+ oncompletion=<>
+ bind-status=<>
+ var-resultTitlePrefix=<>
+ var-resultTags=<>
+ />
+ $wikify>
+ $importvariables>
+$let>
+\end ai-tools-get-llm-completion
+
+
+\function ai-tools-status-title()
+[addprefix[$:/temp/ai-tools/status/]]
+\end ai-tools-status-title
+
+
+\procedure ai-tools-message(tiddler,field,role,makeLink:"yes")
+<$qualify
+ name="state"
+ title={{{ [[$:/state/ai-tools-message-state/]addsuffix] }}}
+>
+ <$let
+ editStateTiddler={{{ [addsuffix[-edit-state]] }}}
+ editState={{{ [get[text]else[view]] }}}
+ >
+