From 2faba2e820e3d222695c57982b231bfe6baca55d Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Tue, 9 Jul 2024 08:59:49 +0100 Subject: [PATCH 01/22] Initial Commit --- core/modules/startup/rootwidget.js | 3 +- core/modules/utils/dom/http.js | 5 + editions/prerelease/tiddlywiki.info | 4 +- plugins/tiddlywiki/ai-tools/docs.tid | 2 + plugins/tiddlywiki/ai-tools/globals.tid | 60 +++++ plugins/tiddlywiki/ai-tools/icon.tid | 5 + plugins/tiddlywiki/ai-tools/page-menu.tid | 21 ++ .../ai-tools/page-menu/new-conversation.tid | 17 ++ plugins/tiddlywiki/ai-tools/plugin.info | 7 + plugins/tiddlywiki/ai-tools/readme.tid | 6 + .../ai-tools/servers/local-llamafile.tid | 4 + .../tiddlywiki/ai-tools/servers/openai.tid | 4 + plugins/tiddlywiki/ai-tools/settings.tid | 16 ++ plugins/tiddlywiki/ai-tools/styles.tid | 204 +++++++++++++++++ .../ai-tools/view-templates/conversation.tid | 211 ++++++++++++++++++ 15 files changed, 567 insertions(+), 2 deletions(-) create mode 100644 plugins/tiddlywiki/ai-tools/docs.tid create mode 100644 plugins/tiddlywiki/ai-tools/globals.tid create mode 100644 plugins/tiddlywiki/ai-tools/icon.tid create mode 100644 plugins/tiddlywiki/ai-tools/page-menu.tid create mode 100644 plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid create mode 100644 plugins/tiddlywiki/ai-tools/plugin.info create mode 100644 plugins/tiddlywiki/ai-tools/readme.tid create mode 100644 plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid create mode 100644 plugins/tiddlywiki/ai-tools/servers/openai.tid create mode 100644 plugins/tiddlywiki/ai-tools/settings.tid create mode 100644 plugins/tiddlywiki/ai-tools/styles.tid create mode 100644 plugins/tiddlywiki/ai-tools/view-templates/conversation.tid 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/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/plugins/tiddlywiki/ai-tools/docs.tid b/plugins/tiddlywiki/ai-tools/docs.tid new file mode 100644 index 00000000000..103c1e92af4 --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/docs.tid @@ -0,0 +1,2 @@ +title: $:/plugins/tiddlywiki/ai-tools/docs + diff --git a/plugins/tiddlywiki/ai-tools/globals.tid b/plugins/tiddlywiki/ai-tools/globals.tid new file mode 100644 index 00000000000..411da6136f5 --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/globals.tid @@ -0,0 +1,60 @@ +title: $:/plugins/tiddlywiki/ai-tools/globals +tags: $:/tags/Global + +\function default-llm-completion-server() +[all[shadows+tiddlers]tag[$:/tags/AI/CompletionServer]sort[caption]first[]] +\end + + +\procedure get-llm-completion(payload,resultTitlePrefix,resultTags,statusTitle,completionServer) + + \procedure get-llm-completion-callback() + <%if [compare:number:gteq[200]compare:number:lteq[299]] %> + + <$action-createtiddler + $basetitle=<> + tags=<> + type="text/markdown" + role={{{ [jsonget[choices],[0],[message],[role]] }}} + text={{{ [jsonget[choices],[0],[message],[content]] }}} + /> + <%else%> + + <$action-createtiddler + $basetitle=<> + tags=<> + type="text/markdown" + role="error" + text={{{ [[Error:]] [] [jsonget[error],[message]] +[join[]] }}} + /> + <%endif%> + \end get-llm-completion-callback + +<$let + completionServer={{{ [!is[blank]else] }}} +> + <$action-log message="get-llm-completion"/> + <$action-log/> + <$action-sendmessage + $message="tm-http-request" + url={{{ [get[url]addsuffix[/v1/chat/completions]] }}} + body=<> + header-content-type="application/json" + bearer-auth-token-from-store="openai-secret-key" + method="POST" + oncompletion=<> + bind-status=<> + var-resultTitlePrefix=<> + var-resultTags=<> + /> + +\end get-llm-completion diff --git a/plugins/tiddlywiki/ai-tools/icon.tid b/plugins/tiddlywiki/ai-tools/icon.tid new file mode 100644 index 00000000000..3fdc090b555 --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/icon.tid @@ -0,0 +1,5 @@ +title: $:/plugins/tiddlywiki/ai-tools/icon +tags: $:/tags/Image + + + \ No newline at end of file diff --git a/plugins/tiddlywiki/ai-tools/page-menu.tid b/plugins/tiddlywiki/ai-tools/page-menu.tid new file mode 100644 index 00000000000..779d01dc74f --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/page-menu.tid @@ -0,0 +1,21 @@ +title: $:/plugins/tiddlywiki/ai-tools/page-menu +tags: $:/tags/PageControls +caption: {{$:/plugins/tiddlywiki/ai-tools/icon}} AI Tools +description: Tools for interactive AI services + +\whitespace trim +<$button popup=<> tooltip="Tools for interactive AI services" aria-label="AI Tools" class=<> selectedClass="tc-selected"> + <%if [match[yes]] %> + {{$:/plugins/tiddlywiki/ai-tools/icon}} + <%endif%> + <%if [match[yes]] %> + <$text text="AI Tools"/> + <%endif%> + +<$reveal state=<> type="popup" position="belowleft" animate="yes"> +
+ <$list filter="[all[shadows+tiddlers]tag[$:/tags/AI/PageMenu]!has[draft.of]]" variable="listItem"> + <$transclude tiddler=<>/> + +
+ diff --git a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid new file mode 100644 index 00000000000..1e7464de537 --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid @@ -0,0 +1,17 @@ +title: $:/plugins/tiddlywiki/ai-tools/page-menu/new-conversation +tags: $:/tags/AI/PageMenu + +\define actions() +<$action-createtiddler + $basetitle="AI Conversation" + tags="$:/tags/AI/Conversation" + system-prompt="You are a helpful assistant." + current-response-text="Please describe this picture" +> +<$action-navigate $to=<>/> + +\end actions + +<$button actions=<> class="tc-btn-invisible"> +{{$:/core/images/new-button}} New Conversation + diff --git a/plugins/tiddlywiki/ai-tools/plugin.info b/plugins/tiddlywiki/ai-tools/plugin.info new file mode 100644 index 00000000000..d074b2d4b7a --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/plugin.info @@ -0,0 +1,7 @@ +{ + "title": "$:/plugins/tiddlywiki/ai-tools", + "name": "AI Tools", + "description": "AI Tools for TiddlyWiki", + "list": "readme docs settings", + "stability": "STABILITY_1_EXPERIMENTAL" +} diff --git a/plugins/tiddlywiki/ai-tools/readme.tid b/plugins/tiddlywiki/ai-tools/readme.tid new file mode 100644 index 00000000000..f2fe911d825 --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/readme.tid @@ -0,0 +1,6 @@ +title: $:/plugins/tiddlywiki/ai-tools/readme + +AI Tools for TiddlyWiki. + +This plugin adds new AI tools to the TiddlyWiki platform. + diff --git a/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid b/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid new file mode 100644 index 00000000000..cd52bd9fe2c --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid @@ -0,0 +1,4 @@ +title: $:/plugins/tiddlywiki/ai-tools/servers/local-llamafile +tags: $:/tags/AI/CompletionServer +url: http://127.0.0.1:8080 +caption: Locally running Llamafile server diff --git a/plugins/tiddlywiki/ai-tools/servers/openai.tid b/plugins/tiddlywiki/ai-tools/servers/openai.tid new file mode 100644 index 00000000000..a1bc1c78772 --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/servers/openai.tid @@ -0,0 +1,4 @@ +title: $:/plugins/tiddlywiki/ai-tools/servers/openai +tags: $:/tags/AI/CompletionServer +url: https://api.openai.com +caption: OpenAI Service diff --git a/plugins/tiddlywiki/ai-tools/settings.tid b/plugins/tiddlywiki/ai-tools/settings.tid new file mode 100644 index 00000000000..ce35c72cbbf --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/settings.tid @@ -0,0 +1,16 @@ +title: $:/plugins/tiddlywiki/ai-tools/settings + +! AI Tools Settings + +This plugin runs entirely in the browser, with no backend server component. A consequence of this design is that the API keys required to access external services must be obtained by the end user. These keys are stored in the browser and so only need to be set up once. + +!! ~OpenAI API key + +# Register for an account at https://platform.openai.com/ +#* Newly registered accounts can claim a small amount of credit, thereafter payment is needed +#* Note that ~OpenAI run completely different payment systems for ~ChatGPT and the API platform. Even if you are already a subscriber to ~ChatGPT you will still need to pay for API usage after the initial free service +# Visit https://platform.openai.com/api-keys to create a new secret API key +# Copy and paste the value into the box below + +~OpenAI Secret API Key: <$password name="openai-secret-key"/> + diff --git a/plugins/tiddlywiki/ai-tools/styles.tid b/plugins/tiddlywiki/ai-tools/styles.tid new file mode 100644 index 00000000000..7caea5f0e80 --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/styles.tid @@ -0,0 +1,204 @@ +title: $:/plugins/tiddlywiki/ai-tools/styles +tags: [[$:/tags/Stylesheet]] + +\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline + +.ai-conversation { + background: #f0eeff; + border-radius: 2em; + padding: 1em 1em; + display: flex; + flex-direction: column; + gap: 1em; + box-shadow: 2px 2px 5px rgba(0,0,0,0.2); +} + +.ai-conversation .ai-message { + box-shadow: 2px 2px 5px rgba(0,0,0,0.2); + border-radius: 1em; + display: flex; + flex-direction: column; +} + +.ai-conversation .ai-message .ai-message-toolbar { + background: rgba(1,1,1,0.35); + color: white; + padding: 0.25em 1em 0.25em 1em; + border-top-left-radius: 1em; + border-top-right-radius: 1em; + display: flex; + justify-content: space-between; +} + +.ai-conversation .ai-message .ai-message-toolbar .tc-tiddlylink { + color: inherit; +} + +.ai-conversation .ai-message .ai-message-toolbar .ai-message-toolbar-button { + background: rgba(255,255,255,0.35); + color: #333333; + cursor: pointer; + display: inline-block; + outline: 0; + overflow: hidden; + pointer-events: auto; + position: relative; + text-align: center; + touch-action: manipulation; + user-select: none; + -webkit-user-select: none; + vertical-align: top; + white-space: nowrap; + border: 0; + border-radius: 4px; +} + +.ai-conversation .ai-message .ai-message-toolbar .ai-message-toolbar-button:hover { + color: #ffffff; + background: rgba(255,255,255,0.55); + +} + +.ai-conversation .ai-message .ai-message-body { + padding: 0 1em 0 1em +} + +.ai-conversation .ai-message.ai-message-role-system { + width: 60%; + background: #4c4c80; + color: white; +} + +.ai-conversation .ai-message.ai-message-role-user { + width: 60%; + margin-left: auto; + background: #ffcde0; +} + +.ai-conversation .ai-message.ai-message-role-assistant { + background: #dfd; +} + +.ai-conversation .ai-message.ai-message-role-error { + background: #fdd; +} + +.ai-conversation .ai-user-prompt { + padding: 1em; + background: #ffcde0; + border-radius: 1em; + box-shadow: inset 3px 4px 2px rgba(0, 0, 0, 0.1); +} + +.ai-conversation .ai-user-prompt button svg.tc-image-button { + fill: #000; +} + +.ai-conversation .ai-user-prompt-text { + display: flex; + align-items: flex-start; + gap: 1em; +} + +.ai-conversation .ai-user-prompt button.ai-user-prompt-send { + background-color: initial; + background-image: linear-gradient(-180deg, #e0c3ce, #963057); + border-radius: 1em; + box-shadow: rgba(0, 0, 0, 0.1) 0 2px 4px; + color: #FFFFFF; + cursor: pointer; + display: inline-block; + outline: 0; + overflow: hidden; + padding: 0 20px; + pointer-events: auto; + position: relative; + text-align: center; + touch-action: manipulation; + user-select: none; + -webkit-user-select: none; + vertical-align: top; + white-space: nowrap; + border: 0; + transition: box-shadow .2s; + line-height: 2; +} + +.ai-conversation .ai-user-prompt button.ai-user-prompt-send:hover:not(:disabled) { + box-shadow: rgb(255 62 135 / 64%) 0 3px 8px; +} + +.ai-conversation .ai-user-prompt button.ai-user-prompt-send:disabled { + background: #ddd; + color: #444; +} + +.ai-conversation .ai-user-prompt textarea { + margin: 0; +} + +.ai-request-spinner { + animation: ai-request-spinner-animation-rotate 1s infinite; + height: 50px; + width: 50px; + margin-left: auto; + margin-right: auto; +} + +.ai-request-spinner:before, +.ai-request-spinner:after { + border-radius: 50%; + content: ""; + display: block; + height: 20px; + width: 20px; +} + +.ai-request-spinner:before { + animation: ai-request-spinner-animation-ball1 1s infinite; + background-color: #ddffdd; + box-shadow: 30px 0 0 #90a690; + margin-bottom: 10px; +} + +.ai-request-spinner:after { + animation: ai-request-spinner-animation-ball2 1s infinite; + background-color: #90a690; + box-shadow: 30px 0 0 #ddffdd; +} + +@keyframes ai-request-spinner-animation-rotate { + 0% { transform: rotate(0deg) scale(0.8) } + 50% { transform: rotate(360deg) scale(1.2) } + 100% { transform: rotate(720deg) scale(0.8) } +} + +@keyframes ai-request-spinner-animation-ball1 { + 0% { + box-shadow: 30px 0 0 #90a690; + } + 50% { + box-shadow: 0 0 0 #90a690; + margin-bottom: 0; + transform: translate(15px, 15px); + } + 100% { + box-shadow: 30px 0 0 #90a690; + margin-bottom: 10px; + } +} + +@keyframes ai-request-spinner-animation-ball2 { + 0% { + box-shadow: 30px 0 0 #ddffdd; + } + 50% { + box-shadow: 0 0 0 #ddffdd; + margin-top: -20px; + transform: translate(15px, 15px); + } + 100% { + box-shadow: 30px 0 0 #ddffdd; + margin-top: 0; + } +} \ No newline at end of file diff --git a/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid b/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid new file mode 100644 index 00000000000..5e7adab0093 --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid @@ -0,0 +1,211 @@ +title: $:/plugins/tiddlywiki/ai-tools/view-templates/conversation +tags: $:/tags/ViewTemplate +list-after: $:/core/ui/ViewTemplate/body + + +\function statusTitle() +[addprefix[$:/temp/ai-tools/status/]] +\end statusTitle + + +\procedure ai-message(tiddler,field,role,makeLink:"yes") +<$qualify + name="state" + title={{{ [[$:/state/ai-message-state/]addsuffix] }}} +> + <$let + editStateTiddler={{{ [addsuffix[-edit-state]] }}} + editState={{{ [get[text]else[view]] }}} + > +
addprefix[ai-message-role-]] +[join[ ]] }}}> +
+
+ <$genesis $type={{{ [match[yes]then[$link]else[span]] }}} to=<>> + <$text text=<>/> + +
+
+ <%if [!match[edit]] %> + <$button class="ai-message-toolbar-button"> + <$action-setfield $tiddler=<> text="edit"/> + edit + + <%endif%> + <%if [!match[view]] %> + <$button class="ai-message-toolbar-button"> + <$action-setfield $tiddler=<> text="view"/> + view + + <%endif%> + <$button class="ai-message-toolbar-button"> + <$action-sendmessage $message="tm-copy-to-clipboard" $param={{{ [getelse[]] }}}/> + copy + + <$button class="ai-message-toolbar-button"> + <$action-deletetiddler $tiddler=<>/> + delete + +
+
+
+ <%if [match[view]] %> + <$transclude $tiddler=<> $field=<> $mode="block"/> + <%else%> + <$edit-text tiddler=<> field=<> tag="textarea" class="tc-edit-texteditor"/> + <%endif%> + <%if [get[image]else[]!match[]] %> + <$image source={{{ [get[image]] }}}/> + <%endif%> +
+
+ + +\end ai-message + + +\procedure payload() +\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline html conditional commentblock commentinline +{ + "model": "gpt-4o", + "messages": [ + { + "role": "system", + "content": "<$text text={{{ [get[system-prompt]jsonstringify[]] }}}/>" + } + + <$list filter="[all[shadows+tiddlers]tag!is[draft]sort[created]]"> + , + { + + "role": "<$text text={{{ [get[role]jsonstringify[]] }}}/>", + "content": [ + { + "type": "text", + "text": "<$text text={{{ [get[text]jsonstringify[]] }}}/>" + } + <%if [get[image]else[]!match[]] %> + , + { + "type": "image_url", + "image_url": { + "url": "<$text text={{{ [[data:]] [get[image]get[type]] [[;base64,]] [get[image]get[text]jsonstringify[]] +[join[]] }}}/>" + } + } + <%endif%> + ] + + } + + ] +} +\end payload + + +\procedure action-get-response() +<$let + resultTitlePrefix={{{ [addsuffix[ - Prompt]] }}} + resultTags={{{ [format:titlelist[]] }}} +> + <$action-createtiddler + $basetitle=<> + tags=<> + type="text/markdown" + role="user" + text={{!!current-response-text}} + image={{!!current-response-image}} + > + <$action-deletefield $tiddler=<> $field="current-response-text"/> + <$action-deletefield $tiddler=<> $field="current-response-image"/> + <$wikify name="json" text=<>> + <$transclude + $variable="get-llm-completion" + payload=<> + completionServer={{!!completion-server}} + resultTitlePrefix=<> + resultTags=<> + statusTitle=<> + /> + + + +\end action-get-response + +<%if [tag[$:/tags/AI/Conversation]] %> + +<$select tiddler=<> field="completion-server" default=<>> +<$list filter="[all[shadows+tiddlers]tag[$:/tags/AI/CompletionServer]sort[caption]]"> + + + + +
+ <$transclude + $variable="ai-message" + tiddler=<> + field="system-prompt" + role="system" + makeLink="no" + /> + <$list filter="[all[shadows+tiddlers]tag!is[draft]sort[created]]" variable="message" storyview="pop"> + <$transclude + $variable="ai-message" + tiddler=<> + field="text" + role={{{ [get[role]] }}} + /> + + <%if [get[text]else[complete]match[pending]] %> +
+
+
+ <%endif%> +
+
+ <$edit-text tiddler=<> field="current-response-text" tag="textarea" class="tc-edit-texteditor"/> + <$button + class="ai-user-prompt-send" + actions=<> + disabled={{{ [get[text]else[complete]match[pending]then[yes]] [get[current-response-text]else[]match[]then[yes]] ~[[no]] }}} + > + Send + +
+
+
+ <$let state=<>> + <$button popup=<> class="tc-btn-invisible tc-btn-dropdown">Choose an image {{$:/core/images/down-arrow}} + <$link to={{!!current-response-image}}> + <$text text={{!!current-response-image}}/> + + <$reveal state=<> type="popup" position="belowleft" text="" default="" class="tc-popup-keep"> +
+ <$transclude + $variable="image-picker" + filter="[all[shadows+tiddlers]is[image]is[binary]!has[_canonical_uri]] -[type[application/pdf]] +[!has[draft.of]sort[title]]" + actions=""" + <$action-setfield + $tiddler=<> + current-response-image=<> + /> + <$action-deletetiddler $tiddler=<>/> + """ + /> +
+ + + <$image source={{!!current-response-image}}/> +
+
+
+
+ + +<%endif%> + From 3b07607d1bb65482371660b30c26c74340a27600 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Thu, 11 Jul 2024 09:43:23 +0100 Subject: [PATCH 02/22] Fix Llamafile compatibility --- plugins/tiddlywiki/ai-tools/globals.tid | 34 ++++++----- .../ai-tools/page-menu/new-conversation.tid | 2 +- .../ai-tools/servers/local-llamafile.tid | 20 +++++++ .../tiddlywiki/ai-tools/servers/openai.tid | 35 +++++++++++ .../ai-tools/view-templates/conversation.tid | 58 +++---------------- 5 files changed, 82 insertions(+), 67 deletions(-) diff --git a/plugins/tiddlywiki/ai-tools/globals.tid b/plugins/tiddlywiki/ai-tools/globals.tid index 411da6136f5..b3d2fc55088 100644 --- a/plugins/tiddlywiki/ai-tools/globals.tid +++ b/plugins/tiddlywiki/ai-tools/globals.tid @@ -7,13 +7,13 @@ tags: $:/tags/Global -\procedure get-llm-completion(payload,resultTitlePrefix,resultTags,statusTitle,completionServer) +\procedure get-llm-completion(conversationTitle,resultTitlePrefix,resultTags,statusTitle,completionServer) @@ -42,19 +42,21 @@ completionServer - Optional URL of server <$let completionServer={{{ [!is[blank]else] }}} > - <$action-log message="get-llm-completion"/> - <$action-log/> - <$action-sendmessage - $message="tm-http-request" - url={{{ [get[url]addsuffix[/v1/chat/completions]] }}} - body=<> - header-content-type="application/json" - bearer-auth-token-from-store="openai-secret-key" - method="POST" - oncompletion=<> - bind-status=<> - var-resultTitlePrefix=<> - var-resultTags=<> - /> + <$wikify name="json" text={{{ [get[text]] }}}> + <$action-log message="get-llm-completion"/> + <$action-log/> + <$action-sendmessage + $message="tm-http-request" + url={{{ [get[url]addsuffix[/v1/chat/completions]] }}} + body=<> + header-content-type="application/json" + bearer-auth-token-from-store="openai-secret-key" + method="POST" + oncompletion=<> + bind-status=<> + var-resultTitlePrefix=<> + var-resultTags=<> + /> + \end get-llm-completion diff --git a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid index 1e7464de537..5438ae2b9e5 100644 --- a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid +++ b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid @@ -6,7 +6,7 @@ tags: $:/tags/AI/PageMenu $basetitle="AI Conversation" tags="$:/tags/AI/Conversation" system-prompt="You are a helpful assistant." - current-response-text="Please describe this picture" + current-response-text="Please list the 10 most important things that happened in South East Asia in the Twentieth Century" > <$action-navigate $to=<>/> diff --git a/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid b/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid index cd52bd9fe2c..4348507d6b4 100644 --- a/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid +++ b/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid @@ -2,3 +2,23 @@ title: $:/plugins/tiddlywiki/ai-tools/servers/local-llamafile tags: $:/tags/AI/CompletionServer url: http://127.0.0.1:8080 caption: Locally running Llamafile server + +\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline html conditional commentblock commentinline +{ + "model": "gpt-4o", + "messages": [ + { + "role": "system", + "content": "<$text text={{{ [get[system-prompt]jsonstringify[]] }}}/>" + } + + <$list filter="[all[shadows+tiddlers]tag!is[draft]sort[created]]"> + , + { + + "role": "<$text text={{{ [get[role]jsonstringify[]] }}}/>", + "content": "<$text text={{{ [get[text]jsonstringify[]] }}}/>" + } + + ] +} \ No newline at end of file diff --git a/plugins/tiddlywiki/ai-tools/servers/openai.tid b/plugins/tiddlywiki/ai-tools/servers/openai.tid index a1bc1c78772..ae9c19a03c2 100644 --- a/plugins/tiddlywiki/ai-tools/servers/openai.tid +++ b/plugins/tiddlywiki/ai-tools/servers/openai.tid @@ -2,3 +2,38 @@ title: $:/plugins/tiddlywiki/ai-tools/servers/openai tags: $:/tags/AI/CompletionServer url: https://api.openai.com caption: OpenAI Service + +\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline html conditional commentblock commentinline +{ + "model": "gpt-4o", + "messages": [ + { + "role": "system", + "content": "<$text text={{{ [get[system-prompt]jsonstringify[]] }}}/>" + } + + <$list filter="[all[shadows+tiddlers]tag!is[draft]sort[created]]"> + , + { + + "role": "<$text text={{{ [get[role]jsonstringify[]] }}}/>", + "content": [ + { + "type": "text", + "text": "<$text text={{{ [get[text]jsonstringify[]] }}}/>" + } + <%if [get[image]else[]!match[]] %> + , + { + "type": "image_url", + "image_url": { + "url": "<$text text={{{ [[data:]] [get[image]get[type]] [[;base64,]] [get[image]get[text]jsonstringify[]] +[join[]] }}}/>" + } + } + <%endif%> + ] + + } + + ] +} \ No newline at end of file diff --git a/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid b/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid index 5e7adab0093..7c99de70582 100644 --- a/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid +++ b/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid @@ -65,46 +65,6 @@ Procedure to display a message from an AI conversation. Current tiddler is the c \end ai-message - -\procedure payload() -\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline html conditional commentblock commentinline -{ - "model": "gpt-4o", - "messages": [ - { - "role": "system", - "content": "<$text text={{{ [get[system-prompt]jsonstringify[]] }}}/>" - } - - <$list filter="[all[shadows+tiddlers]tag!is[draft]sort[created]]"> - , - { - - "role": "<$text text={{{ [get[role]jsonstringify[]] }}}/>", - "content": [ - { - "type": "text", - "text": "<$text text={{{ [get[text]jsonstringify[]] }}}/>" - } - <%if [get[image]else[]!match[]] %> - , - { - "type": "image_url", - "image_url": { - "url": "<$text text={{{ [[data:]] [get[image]get[type]] [[;base64,]] [get[image]get[text]jsonstringify[]] +[join[]] }}}/>" - } - } - <%endif%> - ] - - } - - ] -} -\end payload - @@ -123,16 +83,14 @@ Action procedure to get the next response from the LLM > <$action-deletefield $tiddler=<> $field="current-response-text"/> <$action-deletefield $tiddler=<> $field="current-response-image"/> - <$wikify name="json" text=<>> - <$transclude - $variable="get-llm-completion" - payload=<> - completionServer={{!!completion-server}} - resultTitlePrefix=<> - resultTags=<> - statusTitle=<> - /> - + <$transclude + $variable="get-llm-completion" + conversationTitle=<> + completionServer={{!!completion-server}} + resultTitlePrefix=<> + resultTags=<> + statusTitle=<> + /> \end action-get-response From cb9deaa9b5ee86abc1cc2c9f3230966a74817d09 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Thu, 11 Jul 2024 09:56:41 +0100 Subject: [PATCH 03/22] Update docs --- .../prerelease/tiddlers/system/DefaultTiddlers.tid | 5 +---- editions/tw5.com/tiddlers/system/DefaultTiddlers.tid | 4 +--- editions/tw5.com/tiddlers/system/SiteTitle.tid | 2 +- editions/tw5.com/tiddlywiki.info | 3 ++- plugins/tiddlywiki/ai-tools/readme.tid | 11 +++++++++-- .../ai-tools/view-templates/conversation.tid | 2 +- 6 files changed, 15 insertions(+), 12 deletions(-) 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/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 efac7b5280d..b144a5b69b9 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 \ No newline at end of file +TiddlyWiki AI Tools Plugin \ No newline at end of file diff --git a/editions/tw5.com/tiddlywiki.info b/editions/tw5.com/tiddlywiki.info index 504abac5c02..0741570770a 100644 --- a/editions/tw5.com/tiddlywiki.info +++ b/editions/tw5.com/tiddlywiki.info @@ -10,7 +10,8 @@ "tiddlywiki/confetti", "tiddlywiki/dynannotate", "tiddlywiki/tour", - "tiddlywiki/qrcode" + "tiddlywiki/qrcode", + "tiddlywiki/ai-tools" ], "themes": [ "tiddlywiki/vanilla", diff --git a/plugins/tiddlywiki/ai-tools/readme.tid b/plugins/tiddlywiki/ai-tools/readme.tid index f2fe911d825..d29b75ea9bf 100644 --- a/plugins/tiddlywiki/ai-tools/readme.tid +++ b/plugins/tiddlywiki/ai-tools/readme.tid @@ -1,6 +1,13 @@ title: $:/plugins/tiddlywiki/ai-tools/readme -AI Tools for TiddlyWiki. +!! AI Tools for TiddlyWiki -This plugin adds new AI tools to the TiddlyWiki platform. +This plugin adds integrated LLM conversations to the TiddlyWiki platform. +# 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'' - LLlamafile is an open source project that lets you distribute and run LLMs as a single file. The files are large, typically 4+ gigabytes but offer reasonable performance on modern hardware, and total privacy +#* ''~OpenAI Service'' - ~OpenAI is a commercial service that offers paid APIs for accessing some of the most sophisticated LLMs that are available. ~OpenAI requires tokens to be purchased for API usage (this is entirely separate from ~ChatGPT subscriptions) +# 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 diff --git a/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid b/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid index 7c99de70582..1b44e0b5c8d 100644 --- a/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid +++ b/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid @@ -97,7 +97,7 @@ Action procedure to get the next response from the LLM <%if [tag[$:/tags/AI/Conversation]] %> -<$select tiddler=<> field="completion-server" default=<>> +Server: <$select tiddler=<> field="completion-server" default=<>> <$list filter="[all[shadows+tiddlers]tag[$:/tags/AI/CompletionServer]sort[caption]]"> From e00c761088c1d2b0470062394c0f8b8efb1da0aa Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Thu, 11 Jul 2024 15:02:30 +0100 Subject: [PATCH 04/22] Tweak default prompt --- plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid index 5438ae2b9e5..1c073803981 100644 --- a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid +++ b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid @@ -6,7 +6,7 @@ tags: $:/tags/AI/PageMenu $basetitle="AI Conversation" tags="$:/tags/AI/Conversation" system-prompt="You are a helpful assistant." - current-response-text="Please list the 10 most important things that happened in South East Asia in the Twentieth Century" + current-response-text="Please list the 10 most important mechanical inventions of the Twentieth Century" > <$action-navigate $to=<>/> From d652f820b8d8c3afa320dd8939d7b8103a602821 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sun, 14 Jul 2024 21:16:11 +0100 Subject: [PATCH 05/22] Basic support for importing ChatGPT archives --- .../modules/conversations-archive-importer.js | 87 +++++++++++++++++++ .../tiddlywiki/ai-tools/modules/startup.js | 30 +++++++ plugins/tiddlywiki/ai-tools/plugin.info | 2 +- plugins/tiddlywiki/ai-tools/tools.tid | 19 ++++ .../imported-conversations-json.tid | 45 ++++++++++ 5 files changed, 182 insertions(+), 1 deletion(-) create mode 100644 plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js create mode 100644 plugins/tiddlywiki/ai-tools/modules/startup.js create mode 100644 plugins/tiddlywiki/ai-tools/tools.tid create mode 100644 plugins/tiddlywiki/ai-tools/view-templates/imported-conversations-json.tid diff --git a/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js b/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js new file mode 100644 index 00000000000..22b2cb1bdc0 --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js @@ -0,0 +1,87 @@ +/*\ +title: $:/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js +type: application/javascript +module-type: library + +Conversations archive importer + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +function ConversationsArchiveImporter() { +} + +ConversationsArchiveImporter.prototype.import = function(widget,conversationsTitle) { + var logger = new $tw.utils.Logger("ai-tools"); + var jsonConversations = widget.wiki.getTiddlerData(conversationsTitle,[]); + var tiddlers = []; + $tw.utils.each(jsonConversations,function(jsonConversation) { + var conversationTitle = (jsonConversation.title || "Untitled") + " (" + jsonConversation.conversation_id + ")", + conversationCreated = convertDate(jsonConversation.create_time), + conversationModified = convertDate(jsonConversation.update_time); + var conversationFields = { + title: conversationTitle, + tags: $tw.utils.stringifyList(["$:/tags/AI/Conversation"]), + created: conversationCreated, + modified: conversationModified + }; + tiddlers.push(conversationFields); + var messageIndex = 1; + $tw.utils.each(jsonConversation.mapping,function(jsonMessage,messageId) { + // Skip messages where "message" is null + if(jsonMessage.message) { + var messageFields = { + title: conversationTitle + " " + (messageIndex + 1), + created: convertDate(jsonMessage.message.create_time) || conversationCreated, + modified: convertDate(jsonMessage.message.update_time) || conversationModified, + tags: $tw.utils.stringifyList([conversationTitle]), + role: jsonMessage.message.author.role, + "message-type": jsonMessage.message.content.content_type + } + switch(jsonMessage.message.content.content_type) { + case "code": + messageFields.text = jsonMessage.message.content.text; + messageFields.type = "text/plain"; + break; + case "execution_output": + messageFields.text = jsonMessage.message.content.text; + messageFields.type = "text/plain"; + break; + case "system_error": + messageFields.text = jsonMessage.message.content.text; + messageFields.type = "text/plain"; + break; + case "text": + messageFields.text = jsonMessage.message.content.parts.join(""); + messageFields.type = "text/markdown"; + break; + default: + messageFields.text = JSON.stringify(jsonMessage.message,null,4); + messageFields.type = "text/plain"; + break; + } + tiddlers.push(messageFields); + messageIndex += 1; + } + }); + }); + // Create summary tiddler + $tw.utils.each(tiddlers,function(tidder) { + + }); + // Create the tiddlers + widget.wiki.addTiddlers(tiddlers); + // widget.dispatchEvent({type: "tm-import-tiddlers", param: JSON.stringify(tiddlers)}); +}; + +function convertDate(unixTimestamp) { + return $tw.utils.stringifyDate(new Date(unixTimestamp * 1000)); +} + +exports.ConversationsArchiveImporter = ConversationsArchiveImporter; + +})(); diff --git a/plugins/tiddlywiki/ai-tools/modules/startup.js b/plugins/tiddlywiki/ai-tools/modules/startup.js new file mode 100644 index 00000000000..247e06dfd4d --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/modules/startup.js @@ -0,0 +1,30 @@ +/*\ +title: $:/plugins/tiddlywiki/ai-tools/modules/startup.js +type: application/javascript +module-type: startup + +Setup the root widget event handlers + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +// Export name and synchronous status +exports.name = "ai-tools"; +exports.platforms = ["browser"]; +exports.after = ["startup"]; +exports.synchronous = true; + +// Install the root widget event handlers +exports.startup = function() { + var ConversationsArchiveImporter = require("$:/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js").ConversationsArchiveImporter; + $tw.conversationsArchiveImporter = new ConversationsArchiveImporter(); + $tw.rootWidget.addEventListener("tm-import-conversations",function(event) { + $tw.conversationsArchiveImporter.import(event.widget,event.param); + }); +}; + +})(); diff --git a/plugins/tiddlywiki/ai-tools/plugin.info b/plugins/tiddlywiki/ai-tools/plugin.info index d074b2d4b7a..77db7165bb3 100644 --- a/plugins/tiddlywiki/ai-tools/plugin.info +++ b/plugins/tiddlywiki/ai-tools/plugin.info @@ -2,6 +2,6 @@ "title": "$:/plugins/tiddlywiki/ai-tools", "name": "AI Tools", "description": "AI Tools for TiddlyWiki", - "list": "readme docs settings", + "list": "readme docs tools settings", "stability": "STABILITY_1_EXPERIMENTAL" } diff --git a/plugins/tiddlywiki/ai-tools/tools.tid b/plugins/tiddlywiki/ai-tools/tools.tid new file mode 100644 index 00000000000..c91a1de8f84 --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/tools.tid @@ -0,0 +1,19 @@ +title: $:/plugins/tiddlywiki/ai-tools/tools + +! Import ~ChatGPT Export Archive + +Any tiddlers containing ~ChatGPT exported `conversation.json` files will be shown here for import. + +<$list filter="[all[tiddlers+shadows]type[application/json]sort[title]]" template="$:/plugins/tiddlywiki/ai-tools/view-templates/imported-conversations-json"/> + +! Loaded Conversations + +
    + <$list filter="[all[tiddlers+shadows]tag[$:/tags/AI/Conversation]sort[title]]"> +
  • + <$link> + <$text text=<>/> + +
  • + +
diff --git a/plugins/tiddlywiki/ai-tools/view-templates/imported-conversations-json.tid b/plugins/tiddlywiki/ai-tools/view-templates/imported-conversations-json.tid new file mode 100644 index 00000000000..f7556c599b5 --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/view-templates/imported-conversations-json.tid @@ -0,0 +1,45 @@ +title: $:/plugins/tiddlywiki/ai-tools/view-templates/imported-conversations-json +tags: $:/tags/ViewTemplate +list-before: $:/core/ui/ViewTemplate/body + +\whitespace trim + +\procedure importer() +

+

+ <$link> + <$text text=`$(currentTiddler)$ appears to be a ChatGPT export containing $(numberOfConversations)$ conversations`/> + +
+
+ <$button> + <$action-sendmessage $message="tm-import-conversations" $param=<>/> + {{$:/core/images/input-button}} Import + +
+

+\end importer + +<%if [type[application/json]] %> + <$let json={{{ [get[text]] }}} > + <%if [jsontype[]match[array]] %> + <$let + numberOfConversations={{{ [jsonindexes[]count[]] }}} + json={{{ [jsonextract[0]] }}} + > + <%if [jsontype[]match[object]] %> + <%if + [jsontype[title]match[string]] + :and[jsontype[create_time]match[number]] + :and[jsontype[update_time]match[number]] + :and[jsontype[mapping]match[object]] + :and[jsontype[id]match[string]] + %> + <> + <%endif%> + <%endif%> + + <%endif%> + +<%endif%> + From a92103456179322d1629802cf238ac94c417d7c4 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sun, 14 Jul 2024 21:27:52 +0100 Subject: [PATCH 06/22] Improved spinner colours --- plugins/tiddlywiki/ai-tools/styles.tid | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/plugins/tiddlywiki/ai-tools/styles.tid b/plugins/tiddlywiki/ai-tools/styles.tid index 7caea5f0e80..e65012b7ea9 100644 --- a/plugins/tiddlywiki/ai-tools/styles.tid +++ b/plugins/tiddlywiki/ai-tools/styles.tid @@ -156,15 +156,15 @@ tags: [[$:/tags/Stylesheet]] .ai-request-spinner:before { animation: ai-request-spinner-animation-ball1 1s infinite; - background-color: #ddffdd; - box-shadow: 30px 0 0 #90a690; + background-color: #9c9ab0; + box-shadow: 30px 0 0 #fefdff; margin-bottom: 10px; } .ai-request-spinner:after { animation: ai-request-spinner-animation-ball2 1s infinite; - background-color: #90a690; - box-shadow: 30px 0 0 #ddffdd; + background-color: #fefdff; + box-shadow: 30px 0 0 #9c9ab0; } @keyframes ai-request-spinner-animation-rotate { @@ -175,30 +175,30 @@ tags: [[$:/tags/Stylesheet]] @keyframes ai-request-spinner-animation-ball1 { 0% { - box-shadow: 30px 0 0 #90a690; + box-shadow: 30px 0 0 #fefdff; } 50% { - box-shadow: 0 0 0 #90a690; + box-shadow: 0 0 0 #fefdff; margin-bottom: 0; transform: translate(15px, 15px); } 100% { - box-shadow: 30px 0 0 #90a690; + box-shadow: 30px 0 0 #fefdff; margin-bottom: 10px; } } @keyframes ai-request-spinner-animation-ball2 { 0% { - box-shadow: 30px 0 0 #ddffdd; + box-shadow: 30px 0 0 #9c9ab0; } 50% { - box-shadow: 0 0 0 #ddffdd; + box-shadow: 0 0 0 #9c9ab0; margin-top: -20px; transform: translate(15px, 15px); } 100% { - box-shadow: 30px 0 0 #ddffdd; + box-shadow: 30px 0 0 #9c9ab0; margin-top: 0; } } \ No newline at end of file From a1782b1e4eb43b4c83b95c46adedbe8bee38a262 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Tue, 16 Jul 2024 19:41:44 +0100 Subject: [PATCH 07/22] Palette fixes --- plugins/tiddlywiki/ai-tools/styles.tid | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugins/tiddlywiki/ai-tools/styles.tid b/plugins/tiddlywiki/ai-tools/styles.tid index e65012b7ea9..5163d159391 100644 --- a/plugins/tiddlywiki/ai-tools/styles.tid +++ b/plugins/tiddlywiki/ai-tools/styles.tid @@ -100,6 +100,11 @@ tags: [[$:/tags/Stylesheet]] gap: 1em; } +.ai-conversation .ai-user-prompt-text textarea { + color: #000; + background: #fff; +} + .ai-conversation .ai-user-prompt button.ai-user-prompt-send { background-color: initial; background-image: linear-gradient(-180deg, #e0c3ce, #963057); @@ -137,6 +142,11 @@ tags: [[$:/tags/Stylesheet]] margin: 0; } +.ai-conversation .ai-user-prompt .ai-user-prompt-image button { + color: #000; + fill: #000; +} + .ai-request-spinner { animation: ai-request-spinner-animation-rotate 1s infinite; height: 50px; From 837374b5ab08b6a7aa5629add4b8782a8f8c78b0 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Thu, 18 Jul 2024 08:39:03 +0100 Subject: [PATCH 08/22] Don't try to parse plugins as conversations --- plugins/tiddlywiki/ai-tools/tools.tid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tiddlywiki/ai-tools/tools.tid b/plugins/tiddlywiki/ai-tools/tools.tid index c91a1de8f84..0512ea218d9 100644 --- a/plugins/tiddlywiki/ai-tools/tools.tid +++ b/plugins/tiddlywiki/ai-tools/tools.tid @@ -4,7 +4,7 @@ title: $:/plugins/tiddlywiki/ai-tools/tools Any tiddlers containing ~ChatGPT exported `conversation.json` files will be shown here for import. -<$list filter="[all[tiddlers+shadows]type[application/json]sort[title]]" template="$:/plugins/tiddlywiki/ai-tools/view-templates/imported-conversations-json"/> +<$list filter="[all[tiddlers+shadows]type[application/json]!has[plugin-type]sort[title]]" template="$:/plugins/tiddlywiki/ai-tools/view-templates/imported-conversations-json"/> ! Loaded Conversations From a2cff69feef233f62446a3a3aa2d52ed4dba187f Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Thu, 18 Jul 2024 09:24:37 +0100 Subject: [PATCH 09/22] Update docs --- plugins/tiddlywiki/ai-tools/docs.tid | 25 ++++++++++++++++++++++++ plugins/tiddlywiki/ai-tools/readme.tid | 7 ------- plugins/tiddlywiki/ai-tools/settings.tid | 13 ++++++++++-- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/plugins/tiddlywiki/ai-tools/docs.tid b/plugins/tiddlywiki/ai-tools/docs.tid index 103c1e92af4..9ae9ec7a7e8 100644 --- a/plugins/tiddlywiki/ai-tools/docs.tid +++ b/plugins/tiddlywiki/ai-tools/docs.tid @@ -1,2 +1,27 @@ 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'' - LLlamafile is an open source project that lets you distribute and run LLMs as a single file. The files are large, typically 4+ gigabytes but offer reasonable performance on modern hardware, and total privacy +#* ''~OpenAI Service'' - ~OpenAI is a commercial service that offers paid APIs for accessing some of the most sophisticated LLMs that are available. ~OpenAI requires tokens to be purchased for API usage (this is entirely separate from ~ChatGPT subscriptions) +# 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 + diff --git a/plugins/tiddlywiki/ai-tools/readme.tid b/plugins/tiddlywiki/ai-tools/readme.tid index d29b75ea9bf..edaa1d84426 100644 --- a/plugins/tiddlywiki/ai-tools/readme.tid +++ b/plugins/tiddlywiki/ai-tools/readme.tid @@ -4,10 +4,3 @@ title: $:/plugins/tiddlywiki/ai-tools/readme This plugin adds integrated LLM conversations to the TiddlyWiki platform. -# 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'' - LLlamafile is an open source project that lets you distribute and run LLMs as a single file. The files are large, typically 4+ gigabytes but offer reasonable performance on modern hardware, and total privacy -#* ''~OpenAI Service'' - ~OpenAI is a commercial service that offers paid APIs for accessing some of the most sophisticated LLMs that are available. ~OpenAI requires tokens to be purchased for API usage (this is entirely separate from ~ChatGPT subscriptions) -# 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 diff --git a/plugins/tiddlywiki/ai-tools/settings.tid b/plugins/tiddlywiki/ai-tools/settings.tid index ce35c72cbbf..fa18296c999 100644 --- a/plugins/tiddlywiki/ai-tools/settings.tid +++ b/plugins/tiddlywiki/ai-tools/settings.tid @@ -2,10 +2,10 @@ title: $:/plugins/tiddlywiki/ai-tools/settings ! AI Tools Settings -This plugin runs entirely in the browser, with no backend server component. A consequence of this design is that the API keys required to access external services must be obtained by the end user. These keys are stored in the browser and so only need to be set up once. - !! ~OpenAI API key +This plugin runs entirely in the browser, with no backend server component. A consequence of this design is that the API keys required to access external services must be obtained by the end user. These keys are stored in the browser and so only need to be set up once. + # Register for an account at https://platform.openai.com/ #* Newly registered accounts can claim a small amount of credit, thereafter payment is needed #* Note that ~OpenAI run completely different payment systems for ~ChatGPT and the API platform. Even if you are already a subscriber to ~ChatGPT you will still need to pay for API usage after the initial free service @@ -14,3 +14,12 @@ This plugin runs entirely in the browser, with no backend server component. A co ~OpenAI Secret API Key: <$password name="openai-secret-key"/> +!! Llamafile Setup + +[[Llamafile|https://github.com/Mozilla-Ocho/llamafile]] lets you download and run LLMs as a single file. See the [[announcment blog post|https://hacks.mozilla.org/2023/11/introducing-llamafile/]] for background. + +1. Download and run Llamafile as [[described in the QuickStart guide|https://github.com/Mozilla-Ocho/llamafile?tab=readme-ov-file#quickstart]] +2. Visit http://127.0.0.1:8080 in a browser and verify that you can see the Llamafile interface. You can also try it out here +3. Return to AI Tools and start a conversation, specifying Llamafile as the server + + From d56958331b2b3d3f495e689ef48712f6f694eb63 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Thu, 18 Jul 2024 09:49:33 +0100 Subject: [PATCH 10/22] Docs tweaks --- plugins/tiddlywiki/ai-tools/docs.tid | 4 ++-- plugins/tiddlywiki/ai-tools/readme.tid | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/tiddlywiki/ai-tools/docs.tid b/plugins/tiddlywiki/ai-tools/docs.tid index 9ae9ec7a7e8..3f3e3bbcf35 100644 --- a/plugins/tiddlywiki/ai-tools/docs.tid +++ b/plugins/tiddlywiki/ai-tools/docs.tid @@ -8,8 +8,8 @@ See the ''settings'' tab for set up instructions. # 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'' - LLlamafile is an open source project that lets you distribute and run LLMs as a single file. The files are large, typically 4+ gigabytes but offer reasonable performance on modern hardware, and total privacy -#* ''~OpenAI Service'' - ~OpenAI is a commercial service that offers paid APIs for accessing some of the most sophisticated LLMs that are available. ~OpenAI requires tokens to be purchased for API usage (this is entirely separate from ~ChatGPT subscriptions) +#* ''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 diff --git a/plugins/tiddlywiki/ai-tools/readme.tid b/plugins/tiddlywiki/ai-tools/readme.tid index edaa1d84426..747d03a6d7c 100644 --- a/plugins/tiddlywiki/ai-tools/readme.tid +++ b/plugins/tiddlywiki/ai-tools/readme.tid @@ -4,3 +4,9 @@ title: $:/plugins/tiddlywiki/ai-tools/readme This plugin adds integrated LLM conversations to the TiddlyWiki platform. +The plugin allows live conversations within TiddlyWiki and also importing ~ChatGPT conversation archives. + +The plugin supports two options for the LLM server: + +* ''Locally running Llamafile server'' - LLlamafile is an open source project that lets you distribute and run LLMs as a single file. The files are large, typically 4+ gigabytes but offer reasonable performance on modern hardware, and total privacy +* ''~OpenAI Service'' - ~OpenAI is a commercial service that offers paid APIs for accessing some of the most sophisticated LLMs that are available. ~OpenAI requires tokens to be purchased for API usage (this is entirely separate from ~ChatGPT subscriptions) From 638bd78059fc245013aa5eb3fdd92a9eb4fb3af9 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Thu, 18 Jul 2024 09:52:24 +0100 Subject: [PATCH 11/22] Formatting typo --- plugins/tiddlywiki/ai-tools/settings.tid | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/plugins/tiddlywiki/ai-tools/settings.tid b/plugins/tiddlywiki/ai-tools/settings.tid index fa18296c999..8f2567560e8 100644 --- a/plugins/tiddlywiki/ai-tools/settings.tid +++ b/plugins/tiddlywiki/ai-tools/settings.tid @@ -18,8 +18,6 @@ This plugin runs entirely in the browser, with no backend server component. A co [[Llamafile|https://github.com/Mozilla-Ocho/llamafile]] lets you download and run LLMs as a single file. See the [[announcment blog post|https://hacks.mozilla.org/2023/11/introducing-llamafile/]] for background. -1. Download and run Llamafile as [[described in the QuickStart guide|https://github.com/Mozilla-Ocho/llamafile?tab=readme-ov-file#quickstart]] -2. Visit http://127.0.0.1:8080 in a browser and verify that you can see the Llamafile interface. You can also try it out here -3. Return to AI Tools and start a conversation, specifying Llamafile as the server - - +# Download and run Llamafile as [[described in the QuickStart guide|https://github.com/Mozilla-Ocho/llamafile?tab=readme-ov-file#quickstart]] +# Visit http://127.0.0.1:8080 in a browser and verify that you can see the Llamafile interface. You can also try it out here +# Return to AI Tools and start a conversation, specifying Llamafile as the server From 0e5955397b40c9d320daa327a260c74f30a9b899 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Fri, 19 Jul 2024 14:51:18 +0100 Subject: [PATCH 12/22] Give conversation tiddlers a dummy text field To avoid triggering a docs template --- .../ai-tools/modules/conversations-archive-importer.js | 3 ++- plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js b/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js index 22b2cb1bdc0..72dd879d266 100644 --- a/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js +++ b/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js @@ -27,7 +27,8 @@ ConversationsArchiveImporter.prototype.import = function(widget,conversationsTit title: conversationTitle, tags: $tw.utils.stringifyList(["$:/tags/AI/Conversation"]), created: conversationCreated, - modified: conversationModified + modified: conversationModified, + text: "." }; tiddlers.push(conversationFields); var messageIndex = 1; diff --git a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid index 1c073803981..c58bcced737 100644 --- a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid +++ b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid @@ -7,6 +7,7 @@ tags: $:/tags/AI/PageMenu tags="$:/tags/AI/Conversation" system-prompt="You are a helpful assistant." current-response-text="Please list the 10 most important mechanical inventions of the Twentieth Century" + text="." > <$action-navigate $to=<>/> From 0037935af59026d67054cd71e89d362d3ae5f344 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sun, 21 Jul 2024 15:48:33 +0100 Subject: [PATCH 13/22] Revert "Give conversation tiddlers a dummy text field" This reverts commit 0e5955397b40c9d320daa327a260c74f30a9b899. --- .../ai-tools/modules/conversations-archive-importer.js | 3 +-- plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js b/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js index 72dd879d266..22b2cb1bdc0 100644 --- a/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js +++ b/plugins/tiddlywiki/ai-tools/modules/conversations-archive-importer.js @@ -27,8 +27,7 @@ ConversationsArchiveImporter.prototype.import = function(widget,conversationsTit title: conversationTitle, tags: $tw.utils.stringifyList(["$:/tags/AI/Conversation"]), created: conversationCreated, - modified: conversationModified, - text: "." + modified: conversationModified }; tiddlers.push(conversationFields); var messageIndex = 1; diff --git a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid index c58bcced737..1c073803981 100644 --- a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid +++ b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid @@ -7,7 +7,6 @@ tags: $:/tags/AI/PageMenu tags="$:/tags/AI/Conversation" system-prompt="You are a helpful assistant." current-response-text="Please list the 10 most important mechanical inventions of the Twentieth Century" - text="." > <$action-navigate $to=<>/> From 3bdd449b3e4ab70eb7f02f9ae9cb5dab09b4b5c7 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sun, 21 Jul 2024 16:11:46 +0100 Subject: [PATCH 14/22] Don't hardcode the API route --- plugins/tiddlywiki/ai-tools/globals.tid | 2 +- plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid | 2 +- plugins/tiddlywiki/ai-tools/servers/openai.tid | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/tiddlywiki/ai-tools/globals.tid b/plugins/tiddlywiki/ai-tools/globals.tid index b3d2fc55088..87f18cfbce4 100644 --- a/plugins/tiddlywiki/ai-tools/globals.tid +++ b/plugins/tiddlywiki/ai-tools/globals.tid @@ -47,7 +47,7 @@ completionServer - Optional URL of server <$action-log/> <$action-sendmessage $message="tm-http-request" - url={{{ [get[url]addsuffix[/v1/chat/completions]] }}} + url={{{ [get[url]] }}} body=<> header-content-type="application/json" bearer-auth-token-from-store="openai-secret-key" diff --git a/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid b/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid index 4348507d6b4..7ab2b11fb26 100644 --- a/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid +++ b/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid @@ -1,6 +1,6 @@ title: $:/plugins/tiddlywiki/ai-tools/servers/local-llamafile tags: $:/tags/AI/CompletionServer -url: http://127.0.0.1:8080 +url: http://127.0.0.1:8080/v1/chat/completions caption: Locally running Llamafile server \rules only filteredtranscludeinline transcludeinline macrodef macrocallinline html conditional commentblock commentinline diff --git a/plugins/tiddlywiki/ai-tools/servers/openai.tid b/plugins/tiddlywiki/ai-tools/servers/openai.tid index ae9c19a03c2..c1bef5a7df4 100644 --- a/plugins/tiddlywiki/ai-tools/servers/openai.tid +++ b/plugins/tiddlywiki/ai-tools/servers/openai.tid @@ -1,6 +1,6 @@ title: $:/plugins/tiddlywiki/ai-tools/servers/openai tags: $:/tags/AI/CompletionServer -url: https://api.openai.com +url: https://api.openai.com/v1/chat/completions caption: OpenAI Service \rules only filteredtranscludeinline transcludeinline macrodef macrocallinline html conditional commentblock commentinline From fb641d340c3b1ebdb3791a61023d94c75deb3e41 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sun, 21 Jul 2024 16:50:55 +0100 Subject: [PATCH 15/22] Fix an annoying little bug that prevents importvariables being used inside action-createtiddler in action strings The root cause was that action-createtiddler widget was calling refreshChildren() with no argument. A secondary factor was that importvariables widget was not defensive in handling a missing changedTiddlers parameter --- core/modules/widgets/action-createtiddler.js | 2 +- core/modules/widgets/importvariables.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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); From 370ff3057ee859318c7fc6f688e9c94d68d535d7 Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sun, 21 Jul 2024 16:51:19 +0100 Subject: [PATCH 16/22] Refactor completion servers so that they handle their own response --- plugins/tiddlywiki/ai-tools/globals.tid | 59 ++++++------------- .../ai-tools/servers/local-llamafile.tid | 32 +++++++++- .../tiddlywiki/ai-tools/servers/openai.tid | 32 +++++++++- 3 files changed, 80 insertions(+), 43 deletions(-) diff --git a/plugins/tiddlywiki/ai-tools/globals.tid b/plugins/tiddlywiki/ai-tools/globals.tid index 87f18cfbce4..85fa33b4d81 100644 --- a/plugins/tiddlywiki/ai-tools/globals.tid +++ b/plugins/tiddlywiki/ai-tools/globals.tid @@ -14,49 +14,26 @@ statusTitle - Optional title of a tiddler to which the status of the request wil completionServer - Optional URL of server --> \procedure get-llm-completion(conversationTitle,resultTitlePrefix,resultTags,statusTitle,completionServer) - - \procedure get-llm-completion-callback() - <%if [compare:number:gteq[200]compare:number:lteq[299]] %> - - <$action-createtiddler - $basetitle=<> - tags=<> - type="text/markdown" - role={{{ [jsonget[choices],[0],[message],[role]] }}} - text={{{ [jsonget[choices],[0],[message],[content]] }}} - /> - <%else%> - - <$action-createtiddler - $basetitle=<> - tags=<> - type="text/markdown" - role="error" - text={{{ [[Error:]] [] [jsonget[error],[message]] +[join[]] }}} - /> - <%endif%> - \end get-llm-completion-callback - <$let completionServer={{{ [!is[blank]else] }}} > - <$wikify name="json" text={{{ [get[text]] }}}> - <$action-log message="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=<> - /> - + <$importvariables filter="[]"> + <$wikify name="json" text=<>> + <$action-log message="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=<> + /> + + \end get-llm-completion diff --git a/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid b/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid index 7ab2b11fb26..15ba04e9266 100644 --- a/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid +++ b/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid @@ -3,6 +3,10 @@ tags: $:/tags/AI/CompletionServer url: http://127.0.0.1:8080/v1/chat/completions caption: Locally running Llamafile server + +\procedure json-prompt() \rules only filteredtranscludeinline transcludeinline macrodef macrocallinline html conditional commentblock commentinline { "model": "gpt-4o", @@ -21,4 +25,30 @@ caption: Locally running Llamafile server } ] -} \ No newline at end of file +} +\end json-prompt + + +\procedure completion-callback() + <%if [compare:number:gteq[200]compare:number:lteq[299]] %> + + <$action-createtiddler + $basetitle=<> + tags=<> + type="text/markdown" + role={{{ [jsonget[choices],[0],[message],[role]] }}} + text={{{ [jsonget[choices],[0],[message],[content]] }}} + /> + <%else%> + + <$action-createtiddler + $basetitle=<> + tags=<> + type="text/markdown" + role="error" + text={{{ [[Error:]] [] [jsonget[error],[message]] +[join[]] }}} + /> + <%endif%> +\end completion-callback diff --git a/plugins/tiddlywiki/ai-tools/servers/openai.tid b/plugins/tiddlywiki/ai-tools/servers/openai.tid index c1bef5a7df4..65ee9429a20 100644 --- a/plugins/tiddlywiki/ai-tools/servers/openai.tid +++ b/plugins/tiddlywiki/ai-tools/servers/openai.tid @@ -3,6 +3,10 @@ tags: $:/tags/AI/CompletionServer url: https://api.openai.com/v1/chat/completions caption: OpenAI Service + +\procedure json-prompt() \rules only filteredtranscludeinline transcludeinline macrodef macrocallinline html conditional commentblock commentinline { "model": "gpt-4o", @@ -36,4 +40,30 @@ caption: OpenAI Service } ] -} \ No newline at end of file +} +\end json-prompt + + +\procedure completion-callback() + <%if [compare:number:gteq[200]compare:number:lteq[299]] %> + + <$action-createtiddler + $basetitle=<> + tags=<> + type="text/markdown" + role={{{ [jsonget[choices],[0],[message],[role]] }}} + text={{{ [jsonget[choices],[0],[message],[content]] }}} + /> + <%else%> + + <$action-createtiddler + $basetitle=<> + tags=<> + type="text/markdown" + role="error" + text={{{ [[Error:]] [] [jsonget[error],[message]] +[join[]] }}} + /> + <%endif%> +\end completion-callback From 4a79af9eea9d88ef9cb9b40a58055baca5e0288d Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sun, 21 Jul 2024 19:34:25 +0100 Subject: [PATCH 17/22] Revise default system prompt --- plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid index 1c073803981..18420f119bd 100644 --- a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid +++ b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid @@ -5,7 +5,7 @@ tags: $:/tags/AI/PageMenu <$action-createtiddler $basetitle="AI Conversation" tags="$:/tags/AI/Conversation" - system-prompt="You are a helpful assistant." + system-prompt="Transcript of a never ending dialog, where the User interacts with an Assistant. The Assistant is helpful, kind, honest, good at writing, and never fails to answer the User's requests immediately and with precision." current-response-text="Please list the 10 most important mechanical inventions of the Twentieth Century" > <$action-navigate $to=<>/> From 80fdaae6dee6ce60c31132e0b2e838cf3100300a Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sun, 21 Jul 2024 21:08:42 +0100 Subject: [PATCH 18/22] Llamafile use native /completion API endpoint So that we can do image analysis --- .../ai-tools/servers/local-llamafile.tid | 67 ++++++++++++++----- 1 file changed, 49 insertions(+), 18 deletions(-) diff --git a/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid b/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid index 15ba04e9266..1a6f78b93bb 100644 --- a/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid +++ b/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid @@ -1,6 +1,6 @@ title: $:/plugins/tiddlywiki/ai-tools/servers/local-llamafile tags: $:/tags/AI/CompletionServer -url: http://127.0.0.1:8080/v1/chat/completions +url: http://127.0.0.1:8080/completion caption: Locally running Llamafile server - <$list filter="[all[shadows+tiddlers]tag!is[draft]sort[created]]"> - , - { - - "role": "<$text text={{{ [get[role]jsonstringify[]] }}}/>", - "content": "<$text text={{{ [get[text]jsonstringify[]] }}}/>" - } + "image_data": [ + <$list filter="[all[shadows+tiddlers]tag!is[draft]sort[created]]" counter="counter"> + <%if [has[image]] %> + <%if [!match[yes]] %>,<%endif%> + { + "id": <$text text=<>/>, + "data": "<$text text={{{ [get[image]get[text]jsonstringify[]] +[join[]] }}}/>" + } + <%endif%> - ] + ], + "prompt": "<>", + "cache_prompt": false, + "frequency_penalty": 0, + "grammar": "", + "mirostat_eta": 0.1, + "mirostat_tau": 5, + "mirostat": 0, + "n_predict": 400, + "n_probs": 0, + "presence_penalty": 0, + "repeat_last_n": 256, + "repeat_penalty": 1.18, + "slot_id": -1, + "stop": ["", "Llama:", "User:"], + "stream" : false, + "temperature": 0.7, + "tfs_z": 1, + "top_k": 40, + "top_p": 0.5, + "typical_p": 1 } \end json-prompt +\procedure json-prompt-text() +\whitespace trim +\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline html conditional commentblock commentinline +<$text text={{{ [get[system-prompt]jsonstringify[]] }}}/> +\n +<$list filter="[all[shadows+tiddlers]tag!is[draft]sort[created]]" counter="counter"> +\n +<$text text={{{ [get[role]else[user]match[user]then[User:]else[Llama:]] }}}/> +<%if [has[image]] %> +[img-<$text text=<>/>] +<%endif%> +<$text text={{{ [get[text]jsonstringify[]] }}}/> + +\nLlama: +\end json-prompt-text + @@ -38,8 +69,8 @@ Callback for the HTTP response from the LLM $basetitle=<> tags=<> type="text/markdown" - role={{{ [jsonget[choices],[0],[message],[role]] }}} - text={{{ [jsonget[choices],[0],[message],[content]] }}} + role="assistant" + text={{{ [jsonget[content]] }}} /> <%else%> From cd58622c06d66e336428eac150b4fe0db34e6d8c Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Wed, 24 Jul 2024 22:28:30 +0100 Subject: [PATCH 19/22] Clarify that the Llamafile prompt is for Llava models --- .../servers/{local-llamafile.tid => llamafile-llava.tid} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename plugins/tiddlywiki/ai-tools/servers/{local-llamafile.tid => llamafile-llava.tid} (95%) diff --git a/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid b/plugins/tiddlywiki/ai-tools/servers/llamafile-llava.tid similarity index 95% rename from plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid rename to plugins/tiddlywiki/ai-tools/servers/llamafile-llava.tid index 1a6f78b93bb..1334bea8695 100644 --- a/plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid +++ b/plugins/tiddlywiki/ai-tools/servers/llamafile-llava.tid @@ -1,7 +1,7 @@ -title: $:/plugins/tiddlywiki/ai-tools/servers/local-llamafile +title: $:/plugins/tiddlywiki/ai-tools/servers/llamafile-llava tags: $:/tags/AI/CompletionServer url: http://127.0.0.1:8080/completion -caption: Locally running Llamafile server +caption: Local Llamafile server running LLaVA models -\procedure get-llm-completion(conversationTitle,resultTitlePrefix,resultTags,statusTitle,completionServer) +\procedure ai-tools-get-llm-completion(conversationTitle,resultTitlePrefix,resultTags,ai-tools-status-title,completionServer) <$let - completionServer={{{ [!is[blank]else] }}} + completionServer={{{ [!is[blank]else] }}} > <$importvariables filter="[]"> <$wikify name="json" text=<>> - <$action-log message="get-llm-completion"/> + <$action-log message="ai-tools-get-llm-completion"/> <$action-log/> <$action-sendmessage $message="tm-http-request" @@ -29,11 +29,176 @@ completionServer - Optional URL of server bearer-auth-token-from-store="openai-secret-key" method="POST" oncompletion=<> - bind-status=<> + bind-status=<> var-resultTitlePrefix=<> var-resultTags=<> /> -\end get-llm-completion +\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]] }}} + > +
addprefix[ai-tools-message-role-]] +[join[ ]] }}}> +
+
+ <$genesis $type={{{ [match[yes]then[$link]else[span]] }}} to=<>> + <$text text=<>/> + +
+
+ <%if [!match[edit]] %> + <$button class="ai-tools-message-toolbar-button"> + <$action-setfield $tiddler=<> text="edit"/> + edit + + <%endif%> + <%if [!match[view]] %> + <$button class="ai-tools-message-toolbar-button"> + <$action-setfield $tiddler=<> text="view"/> + view + + <%endif%> + <$button class="ai-tools-message-toolbar-button"> + <$action-sendmessage $message="tm-copy-to-clipboard" $param={{{ [getelse[]] }}}/> + copy + + <$button class="ai-tools-message-toolbar-button"> + <$action-deletetiddler $tiddler=<>/> + delete + +
+
+
+ <%if [match[view]] %> + <$transclude $tiddler=<> $field=<> $mode="block"/> + <%else%> + <$edit-text tiddler=<> field=<> tag="textarea" class="tc-edit-texteditor"/> + <%endif%> + <%if [get[image]else[]!match[]] %> + <$image source={{{ [get[image]] }}}/> + <%endif%> +
+
+ + +\end ai-tools-message + + +\procedure ai-tools-action-get-response() +<$let + resultTitlePrefix={{{ [addsuffix[ - Prompt]] }}} + resultTags={{{ [format:titlelist[]] }}} +> + <$action-createtiddler + $basetitle=<> + tags=<> + type="text/markdown" + role="user" + text={{!!current-response-text}} + image={{!!current-response-image}} + > + <$action-deletefield $tiddler=<> $field="current-response-text"/> + <$action-deletefield $tiddler=<> $field="current-response-image"/> + <$transclude + $variable="ai-tools-get-llm-completion" + conversationTitle=<> + completionServer={{!!completion-server}} + resultTitlePrefix=<> + resultTags=<> + ai-tools-status-title=<> + /> + + +\end ai-tools-action-get-response + +\procedure ai-tools-conversation(conversationTitle) +<$let currentTiddler=<>> + + Server: <$select tiddler=<> field="completion-server" default=<>> + <$list filter="[all[shadows+tiddlers]tag[$:/tags/AI/CompletionServer]sort[caption]]"> + + + + +
+ <$transclude + $variable="ai-tools-message" + tiddler=<> + field="system-prompt" + role="system" + makeLink="no" + /> + <$list filter="[all[shadows+tiddlers]tag!is[draft]sort[created]]" variable="message" storyview="pop"> + <$transclude + $variable="ai-tools-message" + tiddler=<> + field="text" + role={{{ [get[role]] }}} + /> + + <%if [get[text]else[complete]match[pending]] %> +
+
+
+ <%endif%> +
+
+ <$edit-text tiddler=<> field="current-response-text" tag="textarea" class="tc-edit-texteditor"/> + <$button + class="ai-user-prompt-send" + actions=<> + disabled={{{ [get[text]else[complete]match[pending]then[yes]] [get[current-response-text]else[]match[]then[yes]] ~[[no]] }}} + > + Send + +
+
+
+ <$let state=<>> + <$button popup=<> class="tc-btn-invisible tc-btn-dropdown">Choose an image {{$:/core/images/down-arrow}} + <$link to={{!!current-response-image}}> + <$text text={{!!current-response-image}}/> + + <$reveal state=<> type="popup" position="belowleft" text="" default="" class="tc-popup-keep"> +
+ <$transclude + $variable="image-picker" + filter="[all[shadows+tiddlers]is[image]is[binary]!has[_canonical_uri]] -[type[application/pdf]] +[!has[draft.of]sort[title]]" + actions=""" + <$action-setfield + $tiddler=<> + current-response-image=<> + /> + <$action-deletetiddler $tiddler=<>/> + """ + /> +
+ + + <$image source={{!!current-response-image}}/> +
+
+
+
+ +\end ai-tools-conversation \ No newline at end of file diff --git a/plugins/tiddlywiki/ai-tools/styles.tid b/plugins/tiddlywiki/ai-tools/styles.tid index 5163d159391..3ef7579c530 100644 --- a/plugins/tiddlywiki/ai-tools/styles.tid +++ b/plugins/tiddlywiki/ai-tools/styles.tid @@ -13,14 +13,14 @@ tags: [[$:/tags/Stylesheet]] box-shadow: 2px 2px 5px rgba(0,0,0,0.2); } -.ai-conversation .ai-message { +.ai-conversation .ai-tools-message { box-shadow: 2px 2px 5px rgba(0,0,0,0.2); border-radius: 1em; display: flex; flex-direction: column; } -.ai-conversation .ai-message .ai-message-toolbar { +.ai-conversation .ai-tools-message .ai-tools-message-toolbar { background: rgba(1,1,1,0.35); color: white; padding: 0.25em 1em 0.25em 1em; @@ -30,11 +30,11 @@ tags: [[$:/tags/Stylesheet]] justify-content: space-between; } -.ai-conversation .ai-message .ai-message-toolbar .tc-tiddlylink { +.ai-conversation .ai-tools-message .ai-tools-message-toolbar .tc-tiddlylink { color: inherit; } -.ai-conversation .ai-message .ai-message-toolbar .ai-message-toolbar-button { +.ai-conversation .ai-tools-message .ai-tools-message-toolbar .ai-tools-message-toolbar-button { background: rgba(255,255,255,0.35); color: #333333; cursor: pointer; @@ -53,33 +53,33 @@ tags: [[$:/tags/Stylesheet]] border-radius: 4px; } -.ai-conversation .ai-message .ai-message-toolbar .ai-message-toolbar-button:hover { +.ai-conversation .ai-tools-message .ai-tools-message-toolbar .ai-tools-message-toolbar-button:hover { color: #ffffff; background: rgba(255,255,255,0.55); } -.ai-conversation .ai-message .ai-message-body { +.ai-conversation .ai-tools-message .ai-tools-message-body { padding: 0 1em 0 1em } -.ai-conversation .ai-message.ai-message-role-system { +.ai-conversation .ai-tools-message.ai-tools-message-role-system { width: 60%; background: #4c4c80; color: white; } -.ai-conversation .ai-message.ai-message-role-user { +.ai-conversation .ai-tools-message.ai-tools-message-role-user { width: 60%; margin-left: auto; background: #ffcde0; } -.ai-conversation .ai-message.ai-message-role-assistant { +.ai-conversation .ai-tools-message.ai-tools-message-role-assistant { background: #dfd; } -.ai-conversation .ai-message.ai-message-role-error { +.ai-conversation .ai-tools-message.ai-tools-message-role-error { background: #fdd; } diff --git a/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid b/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid index 1b44e0b5c8d..db672b1910d 100644 --- a/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid +++ b/plugins/tiddlywiki/ai-tools/view-templates/conversation.tid @@ -2,168 +2,13 @@ title: $:/plugins/tiddlywiki/ai-tools/view-templates/conversation tags: $:/tags/ViewTemplate list-after: $:/core/ui/ViewTemplate/body - -\function statusTitle() -[addprefix[$:/temp/ai-tools/status/]] -\end statusTitle - - -\procedure ai-message(tiddler,field,role,makeLink:"yes") -<$qualify - name="state" - title={{{ [[$:/state/ai-message-state/]addsuffix] }}} -> - <$let - editStateTiddler={{{ [addsuffix[-edit-state]] }}} - editState={{{ [get[text]else[view]] }}} - > -
addprefix[ai-message-role-]] +[join[ ]] }}}> -
-
- <$genesis $type={{{ [match[yes]then[$link]else[span]] }}} to=<>> - <$text text=<>/> - -
-
- <%if [!match[edit]] %> - <$button class="ai-message-toolbar-button"> - <$action-setfield $tiddler=<> text="edit"/> - edit - - <%endif%> - <%if [!match[view]] %> - <$button class="ai-message-toolbar-button"> - <$action-setfield $tiddler=<> text="view"/> - view - - <%endif%> - <$button class="ai-message-toolbar-button"> - <$action-sendmessage $message="tm-copy-to-clipboard" $param={{{ [getelse[]] }}}/> - copy - - <$button class="ai-message-toolbar-button"> - <$action-deletetiddler $tiddler=<>/> - delete - -
-
-
- <%if [match[view]] %> - <$transclude $tiddler=<> $field=<> $mode="block"/> - <%else%> - <$edit-text tiddler=<> field=<> tag="textarea" class="tc-edit-texteditor"/> - <%endif%> - <%if [get[image]else[]!match[]] %> - <$image source={{{ [get[image]] }}}/> - <%endif%> -
-
- - -\end ai-message - - -\procedure action-get-response() -<$let - resultTitlePrefix={{{ [addsuffix[ - Prompt]] }}} - resultTags={{{ [format:titlelist[]] }}} -> - <$action-createtiddler - $basetitle=<> - tags=<> - type="text/markdown" - role="user" - text={{!!current-response-text}} - image={{!!current-response-image}} - > - <$action-deletefield $tiddler=<> $field="current-response-text"/> - <$action-deletefield $tiddler=<> $field="current-response-image"/> - <$transclude - $variable="get-llm-completion" - conversationTitle=<> - completionServer={{!!completion-server}} - resultTitlePrefix=<> - resultTags=<> - statusTitle=<> - /> - - -\end action-get-response - <%if [tag[$:/tags/AI/Conversation]] %> -Server: <$select tiddler=<> field="completion-server" default=<>> -<$list filter="[all[shadows+tiddlers]tag[$:/tags/AI/CompletionServer]sort[caption]]"> - - - - -
- <$transclude - $variable="ai-message" - tiddler=<> - field="system-prompt" - role="system" - makeLink="no" - /> - <$list filter="[all[shadows+tiddlers]tag!is[draft]sort[created]]" variable="message" storyview="pop"> - <$transclude - $variable="ai-message" - tiddler=<> - field="text" - role={{{ [get[role]] }}} - /> - - <%if [get[text]else[complete]match[pending]] %> -
-
-
- <%endif%> -
-
- <$edit-text tiddler=<> field="current-response-text" tag="textarea" class="tc-edit-texteditor"/> - <$button - class="ai-user-prompt-send" - actions=<> - disabled={{{ [get[text]else[complete]match[pending]then[yes]] [get[current-response-text]else[]match[]then[yes]] ~[[no]] }}} - > - Send - -
-
-
- <$let state=<>> - <$button popup=<> class="tc-btn-invisible tc-btn-dropdown">Choose an image {{$:/core/images/down-arrow}} - <$link to={{!!current-response-image}}> - <$text text={{!!current-response-image}}/> - - <$reveal state=<> type="popup" position="belowleft" text="" default="" class="tc-popup-keep"> -
- <$transclude - $variable="image-picker" - filter="[all[shadows+tiddlers]is[image]is[binary]!has[_canonical_uri]] -[type[application/pdf]] +[!has[draft.of]sort[title]]" - actions=""" - <$action-setfield - $tiddler=<> - current-response-image=<> - /> - <$action-deletetiddler $tiddler=<>/> - """ - /> -
- - - <$image source={{!!current-response-image}}/> -
-
-
-
- +<$transclude + $variable="ai-tools-conversation" + $mode="block" + conversationTitle=<> +/> <%endif%> From 422df108414ce4e92c802868def0cbf624bbce6f Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Mon, 5 Aug 2024 16:33:07 +0100 Subject: [PATCH 22/22] Fix ChatGPT import --- plugins/tiddlywiki/ai-tools/globals.tid | 17 ++++++++- .../ai-tools/page-menu/import-chatgpt.tid | 6 ++++ .../ai-tools/page-menu/new-conversation.tid | 13 +------ plugins/tiddlywiki/ai-tools/styles.tid | 11 ++++++ plugins/tiddlywiki/ai-tools/tools.tid | 35 +++++++++++++++---- 5 files changed, 63 insertions(+), 19 deletions(-) create mode 100644 plugins/tiddlywiki/ai-tools/page-menu/import-chatgpt.tid diff --git a/plugins/tiddlywiki/ai-tools/globals.tid b/plugins/tiddlywiki/ai-tools/globals.tid index 87d3b5b4695..e6436913d57 100644 --- a/plugins/tiddlywiki/ai-tools/globals.tid +++ b/plugins/tiddlywiki/ai-tools/globals.tid @@ -201,4 +201,19 @@ Action procedure to get the next response from the LLM -\end ai-tools-conversation \ No newline at end of file +\end ai-tools-conversation + +\procedure ai-tools-new-conversation() +<$action-createtiddler + $basetitle="AI Conversation" + tags="$:/tags/AI/Conversation" + system-prompt="Transcript of a never ending dialog, where the User interacts with an Assistant. The Assistant is helpful, kind, honest, good at writing, and never fails to answer the User's requests immediately and with precision." + current-response-text="Please list the 10 most important mechanical inventions of the Twentieth Century" +> +<$action-navigate $to=<>/> + +\end ai-tools-new-conversation + +\procedure ai-tools-import-conversations() +<$action-navigate $to="$:/plugins/tiddlywiki/ai-tools/tools"/> +\end ai-tools-import-conversations diff --git a/plugins/tiddlywiki/ai-tools/page-menu/import-chatgpt.tid b/plugins/tiddlywiki/ai-tools/page-menu/import-chatgpt.tid new file mode 100644 index 00000000000..fbbea8a01bd --- /dev/null +++ b/plugins/tiddlywiki/ai-tools/page-menu/import-chatgpt.tid @@ -0,0 +1,6 @@ +title: $:/plugins/tiddlywiki/ai-tools/page-menu/import-chatgpt +tags: $:/tags/AI/PageMenu + +<$button actions=<> class="tc-btn-invisible"> +{{$:/core/images/input-button}} Import Conversations from ~ChatGPT + diff --git a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid index 18420f119bd..5e7f3117352 100644 --- a/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid +++ b/plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid @@ -1,17 +1,6 @@ title: $:/plugins/tiddlywiki/ai-tools/page-menu/new-conversation tags: $:/tags/AI/PageMenu -\define actions() -<$action-createtiddler - $basetitle="AI Conversation" - tags="$:/tags/AI/Conversation" - system-prompt="Transcript of a never ending dialog, where the User interacts with an Assistant. The Assistant is helpful, kind, honest, good at writing, and never fails to answer the User's requests immediately and with precision." - current-response-text="Please list the 10 most important mechanical inventions of the Twentieth Century" -> -<$action-navigate $to=<>/> - -\end actions - -<$button actions=<> class="tc-btn-invisible"> +<$button actions=<> class="tc-btn-invisible"> {{$:/core/images/new-button}} New Conversation diff --git a/plugins/tiddlywiki/ai-tools/styles.tid b/plugins/tiddlywiki/ai-tools/styles.tid index 3ef7579c530..a477c4fb1f9 100644 --- a/plugins/tiddlywiki/ai-tools/styles.tid +++ b/plugins/tiddlywiki/ai-tools/styles.tid @@ -211,4 +211,15 @@ tags: [[$:/tags/Stylesheet]] box-shadow: 30px 0 0 #9c9ab0; margin-top: 0; } +} + +.tc-ai-tools-dropzone { + background: yellow; + text-align: center; + width: 100%; + height: 4em; +} + +.tc-ai-tools-dropzone.tc-dragover { + background: red; } \ No newline at end of file diff --git a/plugins/tiddlywiki/ai-tools/tools.tid b/plugins/tiddlywiki/ai-tools/tools.tid index 0512ea218d9..f1bd9ee49c4 100644 --- a/plugins/tiddlywiki/ai-tools/tools.tid +++ b/plugins/tiddlywiki/ai-tools/tools.tid @@ -2,18 +2,41 @@ title: $:/plugins/tiddlywiki/ai-tools/tools ! Import ~ChatGPT Export Archive +These instructions allow you to import the conversations from a ~ChatGPT export archive. + +!! 1- Request your archive + +Visit the ~ChatGPT site to request your achive. You will be sent an email with a link to a ZIP file. Download the file and locate the file `conversations.json` within it. + +!! 2 - Import `conversations.json` as a tiddler + +It is not possible to use the normal import process to import the `conversations.json` file because TiddlyWiki will erroneously recognise it as a JSON file of tiddlers. + +Instead, drag the `conversations.json` file to the dropzone below. Then click the "Import" button to complete the import: + +<$dropzone deserializer="text/plain" autoOpenOnImport="yes" filesOnly="yes" class="tc-ai-tools-dropzone"> +Drop your file here + + +!! 3 - Import the conversations within it + Any tiddlers containing ~ChatGPT exported `conversation.json` files will be shown here for import. <$list filter="[all[tiddlers+shadows]type[application/json]!has[plugin-type]sort[title]]" template="$:/plugins/tiddlywiki/ai-tools/view-templates/imported-conversations-json"/> -! Loaded Conversations +!! 4 - Review Loaded Conversations
    <$list filter="[all[tiddlers+shadows]tag[$:/tags/AI/Conversation]sort[title]]"> -
  • - <$link> - <$text text=<>/> - -
  • + <$list-empty> + No conversations found + + <$list-template> +
  • + <$link> + <$text text=<>/> + +
  • +