mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2025-02-02 12:19:11 +00:00
Initial Commit
This commit is contained in:
parent
eeeb9f97a9
commit
2faba2e820
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,9 @@
|
||||
"tiddlywiki/jszip",
|
||||
"tiddlywiki/confetti",
|
||||
"tiddlywiki/dynannotate",
|
||||
"tiddlywiki/tour"
|
||||
"tiddlywiki/tour",
|
||||
"tiddlywiki/markdown",
|
||||
"tiddlywiki/ai-tools"
|
||||
],
|
||||
"themes": [
|
||||
"tiddlywiki/vanilla",
|
||||
|
2
plugins/tiddlywiki/ai-tools/docs.tid
Normal file
2
plugins/tiddlywiki/ai-tools/docs.tid
Normal file
@ -0,0 +1,2 @@
|
||||
title: $:/plugins/tiddlywiki/ai-tools/docs
|
||||
|
60
plugins/tiddlywiki/ai-tools/globals.tid
Normal file
60
plugins/tiddlywiki/ai-tools/globals.tid
Normal file
@ -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
|
||||
|
||||
<!--
|
||||
Action procedure to retrieve an LLM completion, given the following parameters:
|
||||
payload - JSON payload to be posted to the LLM
|
||||
resultTitlePrefix - Prefix of the tiddler to be used for saving the result. If the tiddler already exists then a number will be added repeatedly until the resulting title is unique
|
||||
resultTags - Tags to be applied to the result tiddler
|
||||
statusTitle - Optional title of a tiddler to which the status of the request will be bound: "pending", "complete", "error"
|
||||
completionServer - Optional URL of server
|
||||
-->
|
||||
\procedure get-llm-completion(payload,resultTitlePrefix,resultTags,statusTitle,completionServer)
|
||||
<!--
|
||||
Callback for the HTTP response from the LLM
|
||||
-->
|
||||
\procedure get-llm-completion-callback()
|
||||
<%if [<status>compare:number:gteq[200]compare:number:lteq[299]] %>
|
||||
<!-- Success -->
|
||||
<$action-createtiddler
|
||||
$basetitle=<<resultTitlePrefix>>
|
||||
tags=<<resultTags>>
|
||||
type="text/markdown"
|
||||
role={{{ [<data>jsonget[choices],[0],[message],[role]] }}}
|
||||
text={{{ [<data>jsonget[choices],[0],[message],[content]] }}}
|
||||
/>
|
||||
<%else%>
|
||||
<!-- Error -->
|
||||
<$action-createtiddler
|
||||
$basetitle=<<resultTitlePrefix>>
|
||||
tags=<<resultTags>>
|
||||
type="text/markdown"
|
||||
role="error"
|
||||
text={{{ [[Error:]] [<statusText>] [<data>jsonget[error],[message]] +[join[]] }}}
|
||||
/>
|
||||
<%endif%>
|
||||
\end get-llm-completion-callback
|
||||
|
||||
<$let
|
||||
completionServer={{{ [<completionServer>!is[blank]else<default-llm-completion-server>] }}}
|
||||
>
|
||||
<$action-log message="get-llm-completion"/>
|
||||
<$action-log/>
|
||||
<$action-sendmessage
|
||||
$message="tm-http-request"
|
||||
url={{{ [<completionServer>get[url]addsuffix[/v1/chat/completions]] }}}
|
||||
body=<<payload>>
|
||||
header-content-type="application/json"
|
||||
bearer-auth-token-from-store="openai-secret-key"
|
||||
method="POST"
|
||||
oncompletion=<<get-llm-completion-callback>>
|
||||
bind-status=<<statusTitle>>
|
||||
var-resultTitlePrefix=<<resultTitlePrefix>>
|
||||
var-resultTags=<<resultTags>>
|
||||
/>
|
||||
</$let>
|
||||
\end get-llm-completion
|
5
plugins/tiddlywiki/ai-tools/icon.tid
Normal file
5
plugins/tiddlywiki/ai-tools/icon.tid
Normal file
File diff suppressed because one or more lines are too long
21
plugins/tiddlywiki/ai-tools/page-menu.tid
Normal file
21
plugins/tiddlywiki/ai-tools/page-menu.tid
Normal file
@ -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=<<qualify "$:/state/popup/ai-tools/page-menu">> tooltip="Tools for interactive AI services" aria-label="AI Tools" class=<<tv-config-toolbar-class>> selectedClass="tc-selected">
|
||||
<%if [<tv-config-toolbar-icons>match[yes]] %>
|
||||
{{$:/plugins/tiddlywiki/ai-tools/icon}}
|
||||
<%endif%>
|
||||
<%if [<tv-config-toolbar-text>match[yes]] %>
|
||||
<span class="tc-btn-text"><$text text="AI Tools"/></span>
|
||||
<%endif%>
|
||||
</$button>
|
||||
<$reveal state=<<qualify "$:/state/popup/ai-tools/page-menu">> type="popup" position="belowleft" animate="yes">
|
||||
<div class="tc-drop-down">
|
||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/AI/PageMenu]!has[draft.of]]" variable="listItem">
|
||||
<$transclude tiddler=<<listItem>>/>
|
||||
</$list>
|
||||
</div>
|
||||
</$reveal>
|
17
plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid
Normal file
17
plugins/tiddlywiki/ai-tools/page-menu/new-conversation.tid
Normal file
@ -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=<<createTiddler-title>>/>
|
||||
</$action-createtiddler>
|
||||
\end actions
|
||||
|
||||
<$button actions=<<actions>> class="tc-btn-invisible">
|
||||
{{$:/core/images/new-button}} New Conversation
|
||||
</$button>
|
7
plugins/tiddlywiki/ai-tools/plugin.info
Normal file
7
plugins/tiddlywiki/ai-tools/plugin.info
Normal file
@ -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"
|
||||
}
|
6
plugins/tiddlywiki/ai-tools/readme.tid
Normal file
6
plugins/tiddlywiki/ai-tools/readme.tid
Normal file
@ -0,0 +1,6 @@
|
||||
title: $:/plugins/tiddlywiki/ai-tools/readme
|
||||
|
||||
AI Tools for TiddlyWiki.
|
||||
|
||||
This plugin adds new AI tools to the TiddlyWiki platform.
|
||||
|
4
plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid
Normal file
4
plugins/tiddlywiki/ai-tools/servers/local-llamafile.tid
Normal file
@ -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
|
4
plugins/tiddlywiki/ai-tools/servers/openai.tid
Normal file
4
plugins/tiddlywiki/ai-tools/servers/openai.tid
Normal file
@ -0,0 +1,4 @@
|
||||
title: $:/plugins/tiddlywiki/ai-tools/servers/openai
|
||||
tags: $:/tags/AI/CompletionServer
|
||||
url: https://api.openai.com
|
||||
caption: OpenAI Service
|
16
plugins/tiddlywiki/ai-tools/settings.tid
Normal file
16
plugins/tiddlywiki/ai-tools/settings.tid
Normal file
@ -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"/>
|
||||
|
204
plugins/tiddlywiki/ai-tools/styles.tid
Normal file
204
plugins/tiddlywiki/ai-tools/styles.tid
Normal file
@ -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;
|
||||
}
|
||||
}
|
211
plugins/tiddlywiki/ai-tools/view-templates/conversation.tid
Normal file
211
plugins/tiddlywiki/ai-tools/view-templates/conversation.tid
Normal file
@ -0,0 +1,211 @@
|
||||
title: $:/plugins/tiddlywiki/ai-tools/view-templates/conversation
|
||||
tags: $:/tags/ViewTemplate
|
||||
list-after: $:/core/ui/ViewTemplate/body
|
||||
|
||||
<!--
|
||||
-->
|
||||
\function statusTitle()
|
||||
[<currentTiddler>addprefix[$:/temp/ai-tools/status/]]
|
||||
\end statusTitle
|
||||
|
||||
<!--
|
||||
Procedure to display a message from an AI conversation. Current tiddler is the conversation tiddler
|
||||
-->
|
||||
\procedure ai-message(tiddler,field,role,makeLink:"yes")
|
||||
<$qualify
|
||||
name="state"
|
||||
title={{{ [[$:/state/ai-message-state/]addsuffix<tiddler>] }}}
|
||||
>
|
||||
<$let
|
||||
editStateTiddler={{{ [<state>addsuffix[-edit-state]] }}}
|
||||
editState={{{ [<editStateTiddler>get[text]else[view]] }}}
|
||||
>
|
||||
<div class={{{ ai-message [<role>addprefix[ai-message-role-]] +[join[ ]] }}}>
|
||||
<div class="ai-message-toolbar">
|
||||
<div class="ai-message-toolbar-left">
|
||||
<$genesis $type={{{ [<makeLink>match[yes]then[$link]else[span]] }}} to=<<tiddler>>>
|
||||
<$text text=<<role>>/>
|
||||
</$genesis>
|
||||
</div>
|
||||
<div class="ai-message-toolbar-left">
|
||||
<%if [<editState>!match[edit]] %>
|
||||
<$button class="ai-message-toolbar-button">
|
||||
<$action-setfield $tiddler=<<editStateTiddler>> text="edit"/>
|
||||
edit
|
||||
</$button>
|
||||
<%endif%>
|
||||
<%if [<editState>!match[view]] %>
|
||||
<$button class="ai-message-toolbar-button">
|
||||
<$action-setfield $tiddler=<<editStateTiddler>> text="view"/>
|
||||
view
|
||||
</$button>
|
||||
<%endif%>
|
||||
<$button class="ai-message-toolbar-button">
|
||||
<$action-sendmessage $message="tm-copy-to-clipboard" $param={{{ [<tiddler>get<field>else[]] }}}/>
|
||||
copy
|
||||
</$button>
|
||||
<$button class="ai-message-toolbar-button">
|
||||
<$action-deletetiddler $tiddler=<<tiddler>>/>
|
||||
delete
|
||||
</$button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ai-message-body">
|
||||
<%if [<editState>match[view]] %>
|
||||
<$transclude $tiddler=<<tiddler>> $field=<<field>> $mode="block"/>
|
||||
<%else%>
|
||||
<$edit-text tiddler=<<tiddler>> field=<<field>> tag="textarea" class="tc-edit-texteditor"/>
|
||||
<%endif%>
|
||||
<%if [<tiddler>get[image]else[]!match[]] %>
|
||||
<$image source={{{ [<tiddler>get[image]] }}}/>
|
||||
<%endif%>
|
||||
</div>
|
||||
</div>
|
||||
</$let>
|
||||
</$qualify>
|
||||
\end ai-message
|
||||
|
||||
<!--
|
||||
Procedure that is wikified to generate the JSON payload for the LLM
|
||||
-->
|
||||
\procedure payload()
|
||||
\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline html conditional commentblock commentinline
|
||||
{
|
||||
"model": "gpt-4o",
|
||||
"messages": [
|
||||
{
|
||||
"role": "system",
|
||||
"content": "<$text text={{{ [<currentTiddler>get[system-prompt]jsonstringify[]] }}}/>"
|
||||
}
|
||||
<!-- Loop through the tiddlers tagged with this one to pick up all the messages in the conversation -->
|
||||
<$list filter="[all[shadows+tiddlers]tag<currentTiddler>!is[draft]sort[created]]">
|
||||
,
|
||||
{
|
||||
<!-- We use JSON stringify to escape the characters that can't be used directly in JSON -->
|
||||
"role": "<$text text={{{ [<currentTiddler>get[role]jsonstringify[]] }}}/>",
|
||||
"content": [
|
||||
{
|
||||
"type": "text",
|
||||
"text": "<$text text={{{ [<currentTiddler>get[text]jsonstringify[]] }}}/>"
|
||||
}
|
||||
<%if [<currentTiddler>get[image]else[]!match[]] %>
|
||||
,
|
||||
{
|
||||
"type": "image_url",
|
||||
"image_url": {
|
||||
"url": "<$text text={{{ [[data:]] [<currentTiddler>get[image]get[type]] [[;base64,]] [<currentTiddler>get[image]get[text]jsonstringify[]] +[join[]] }}}/>"
|
||||
}
|
||||
}
|
||||
<%endif%>
|
||||
]
|
||||
|
||||
}
|
||||
</$list>
|
||||
]
|
||||
}
|
||||
\end payload
|
||||
|
||||
<!--
|
||||
Action procedure to get the next response from the LLM
|
||||
-->
|
||||
\procedure action-get-response()
|
||||
<$let
|
||||
resultTitlePrefix={{{ [<currentTiddler>addsuffix[ - Prompt]] }}}
|
||||
resultTags={{{ [<currentTiddler>format:titlelist[]] }}}
|
||||
>
|
||||
<$action-createtiddler
|
||||
$basetitle=<<resultTitlePrefix>>
|
||||
tags=<<resultTags>>
|
||||
type="text/markdown"
|
||||
role="user"
|
||||
text={{!!current-response-text}}
|
||||
image={{!!current-response-image}}
|
||||
>
|
||||
<$action-deletefield $tiddler=<<currentTiddler>> $field="current-response-text"/>
|
||||
<$action-deletefield $tiddler=<<currentTiddler>> $field="current-response-image"/>
|
||||
<$wikify name="json" text=<<payload>>>
|
||||
<$transclude
|
||||
$variable="get-llm-completion"
|
||||
payload=<<json>>
|
||||
completionServer={{!!completion-server}}
|
||||
resultTitlePrefix=<<resultTitlePrefix>>
|
||||
resultTags=<<resultTags>>
|
||||
statusTitle=<<statusTitle>>
|
||||
/>
|
||||
</$wikify>
|
||||
</$action-createtiddler>
|
||||
</$let>
|
||||
\end action-get-response
|
||||
|
||||
<%if [<currentTiddler>tag[$:/tags/AI/Conversation]] %>
|
||||
|
||||
<$select tiddler=<<currentTiddler>> field="completion-server" default=<<default-llm-completion-server>>>
|
||||
<$list filter="[all[shadows+tiddlers]tag[$:/tags/AI/CompletionServer]sort[caption]]">
|
||||
<option value=<<currentTiddler>>><$view field='caption'/></option>
|
||||
</$list>
|
||||
</$select>
|
||||
|
||||
<div class="ai-conversation">
|
||||
<$transclude
|
||||
$variable="ai-message"
|
||||
tiddler=<<currentTiddler>>
|
||||
field="system-prompt"
|
||||
role="system"
|
||||
makeLink="no"
|
||||
/>
|
||||
<$list filter="[all[shadows+tiddlers]tag<currentTiddler>!is[draft]sort[created]]" variable="message" storyview="pop">
|
||||
<$transclude
|
||||
$variable="ai-message"
|
||||
tiddler=<<message>>
|
||||
field="text"
|
||||
role={{{ [<message>get[role]] }}}
|
||||
/>
|
||||
</$list>
|
||||
<%if [<statusTitle>get[text]else[complete]match[pending]] %>
|
||||
<div class="ai-request-status">
|
||||
<div class="ai-request-spinner"></div>
|
||||
</div>
|
||||
<%endif%>
|
||||
<div class="ai-user-prompt">
|
||||
<div class="ai-user-prompt-text">
|
||||
<$edit-text tiddler=<<currentTiddler>> field="current-response-text" tag="textarea" class="tc-edit-texteditor"/>
|
||||
<$button
|
||||
class="ai-user-prompt-send"
|
||||
actions=<<action-get-response>>
|
||||
disabled={{{ [<statusTitle>get[text]else[complete]match[pending]then[yes]] [<currentTiddler>get[current-response-text]else[]match[]then[yes]] ~[[no]] }}}
|
||||
>
|
||||
Send
|
||||
</$button>
|
||||
</div>
|
||||
<div class="ai-user-prompt-image">
|
||||
<div class="tc-drop-down-wrapper">
|
||||
<$let state=<<qualify "$:/state/ai-user-prompt-image-dropdown-state/">>>
|
||||
<$button popup=<<state>> class="tc-btn-invisible tc-btn-dropdown">Choose an image {{$:/core/images/down-arrow}}</$button>
|
||||
<$link to={{!!current-response-image}}>
|
||||
<$text text={{!!current-response-image}}/>
|
||||
</$link>
|
||||
<$reveal state=<<state>> type="popup" position="belowleft" text="" default="" class="tc-popup-keep">
|
||||
<div class="tc-drop-down" style="text-align:center;">
|
||||
<$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=<<currentTiddler>>
|
||||
current-response-image=<<imageTitle>>
|
||||
/>
|
||||
<$action-deletetiddler $tiddler=<<state>>/>
|
||||
"""
|
||||
/>
|
||||
</div>
|
||||
</$reveal>
|
||||
</$let>
|
||||
<$image source={{!!current-response-image}}/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<%endif%>
|
||||
|
Loading…
Reference in New Issue
Block a user