Skip to content

Commit e30f96d

Browse files
authored
Add prompt builder + Speech to text
Add prompt builder + Speech to text
2 parents 7d52b63 + 65385d7 commit e30f96d

File tree

15 files changed

+442
-65
lines changed

15 files changed

+442
-65
lines changed

CHANGELOG.md

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
### Added
1010

11+
### Fixed
12+
13+
## [0.2.0]
14+
15+
### Added
16+
1117
Chat tab
12-
- added delete icon to the last message in the conversation (for easy deletion)
13-
- added a button in "Advanced Settings" to "Fork a conversation" (save to history for reference, but continue from a fresh copy)
14-
- added a focus when a template is selected (on expand, on template selection, etc)
18+
- Added delete icon to the last message in the conversation (for easy deletion)
19+
- Added a button in "Advanced Settings" to "Fork a conversation" (save to history for reference, but continue from a fresh copy)
20+
- Added a focus when a template is selected (on expand, on template selection, etc)
21+
- Added a little edit icon for messages (disables the q-popup-edit that was distracting and jumping out too often)
22+
- Added speech-to-text for the chat (click Record/Stop -> it will paste the text into the chat input and copy it in your clipboard)
1523

1624
Meta-prompting tab
17-
- Add an experimental meta-prompting experience based on [arxiv](https://arxiv.org/pdf/2401.12954). See the tab "Meta-Prompting" for more details.
25+
- Added an experimental meta-prompting experience based on [arxiv](https://arxiv.org/pdf/2401.12954). See the tab "Meta-Prompting" for more details.
1826

19-
### Fixed
27+
Prompt Builder tab
28+
- Added a tab to generate "prompt templates" that you can then "Apply In Chat" (it jumps to the chat tab and provides template variables to fill in)
29+
- Allows selecting different models (eg, Opus or more powerful ones) and defining how many samples are provided to give you a choice
2030

2131
## [0.1.0]
2232

Project.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
name = "ProToPortal"
22
uuid = "f9496bd6-a3bb-4afc-927d-7268532ebfa9"
33
authors = ["J S <[email protected]> and contributors"]
4-
version = "0.2.0-DEV"
4+
version = "0.2.0"
55

66
[deps]
7+
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
78
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
89
GenieFramework = "a59fdf5c-6bf0-4f5d-949c-a137c9e2f353"
910
GenieSession = "03cc5b98-4f21-4eb6-99f2-22eced81f962"

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,18 +85,33 @@ For a preview, see the video: `docs/src/videos/screen-capture-code-fixing.mp4`
8585
- Start "new chat" with conversations automatically saved both on disk and in history.
8686

8787
### History Tab
88+
8889
- Browse and load past conversations with a simple click.
8990
- Fork past conversations for continued exploration without altering the original history.
9091
- View the current session or reload to fetch all saved conversations.
9192

9293
### Templates Tab
94+
9395
- Explore all available templates with previews and metadata to select the most suitable one.
9496
- Search functionality for quick filtering by partial names or keywords.
9597

9698
### Configuration Tab
99+
97100
- Change the default model or add new ones from PromptingTools.
98101
- Modify the default system prompt used when not employing a template.
99102

103+
### Meta-Prompting Tab
104+
105+
- An experimental meta-prompting experience based on [arxiv](https://arxiv.org/pdf/2401.12954).
106+
- The model calls different "experts" to solve the provided tasks.
107+
108+
### Prompt Builder Tab
109+
110+
- Generate prompt templates (for use in Chat) from a brief description of a task.
111+
- Generate multiple templates at once to choose from.
112+
- Iterate all of them by providing more inputs in the text field.
113+
- Once you're done, click "Apply in Chat" to jump to the normal chat (use as any other template, eg, fill in variables at the top).
114+
100115
And rich logging in the REPL to see what the GUI is doing under the hood!
101116

102117
## Screenshots

app.jl

Lines changed: 175 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
module App
2+
using Base64
23
using ProToPortal
34
using PromptingTools
45
const PT = PromptingTools
@@ -69,9 +70,19 @@ const HISTORY_SAVE = get(ENV, "PROTO_HISTORY_SAVE", true)
6970
@in chat_auto_reply_count = 0
7071
# chat
7172
@in conv_displayed = Dict{Symbol, Any}[]
73+
@in chat_edit_show = false
74+
@in chat_edit_content = ""
75+
@in chat_edit_save = false
76+
@in chat_edit_index = 0
77+
@in is_recording = false
78+
@in audio_chunks = []
79+
@in mediaRecorder = nothing
80+
@in channel_ = nothing
7281
# Enter text
7382
@in chat_question = ""
7483
@out chat_disabled = false
84+
@out chat_question_tokens = ""
85+
@out chat_convo_tokens = ""
7586
# Select template
7687
@in chat_advanced_expanded = false
7788
@in chat_template_expanded = false
@@ -92,6 +103,16 @@ const HISTORY_SAVE = get(ENV, "PROTO_HISTORY_SAVE", true)
92103
@in meta_rounds_current = 0
93104
@in meta_displayed = Dict{Symbol, Any}[]
94105
@in meta_rm_last_msg = false
106+
## Prompt Builder
107+
@in builder_apply = false
108+
@in builder_submit = false
109+
@in builder_reset = false
110+
@in builder_disabled = false
111+
@in builder_question = ""
112+
@in builder_tabs = Dict{Symbol, Any}[]
113+
@in builder_tab = "tab1"
114+
@in builder_model = isempty(PT.GROQ_API_KEY) ? "gpt4t" : "gllama370"
115+
@in builder_samples = 3
95116
# Template browser
96117
@in template_filter = ""
97118
@in template_submit = false
@@ -231,6 +252,34 @@ const HISTORY_SAVE = get(ENV, "PROTO_HISTORY_SAVE", true)
231252
chat_reset = true
232253
conv_displayed = conv_displayed_temp
233254
end
255+
@onchange conv_displayed begin
256+
chat_convo_tokens = if isempty(conv_displayed)
257+
""
258+
elseif PT.isaimessage(conv_displayed[end][:message])
259+
msg = conv_displayed[end][:message]
260+
"Tokens: $(sum(msg.tokens)), Cost: \$$(round(msg.cost;digits=2))"
261+
else
262+
""
263+
end
264+
end
265+
## Chat Speech-to-text
266+
@onchange fileuploads begin
267+
if !isempty(fileuploads)
268+
@info "File was uploaded: " fileuploads["path"]
269+
filename = base64encode(fileuploads["name"])
270+
try
271+
fn_new = fileuploads["path"] * ".wav"
272+
mv(fileuploads["path"], fn_new; force = true)
273+
chat_question = openai_whisper(fn_new)
274+
rm(fn_new; force = true)
275+
Base.run(__model__, "this.copyToClipboardText(this.chat_question);")
276+
catch e
277+
@error "Error processing file: $e"
278+
notify(__model__, "Error processing file: $(fileuploads["name"])")
279+
end
280+
fileuploads = Dict{AbstractString, AbstractString}()
281+
end
282+
end
234283
### Meta-prompting
235284
@onbutton meta_submit begin
236285
meta_disabled = true
@@ -272,6 +321,71 @@ const HISTORY_SAVE = get(ENV, "PROTO_HISTORY_SAVE", true)
272321
pop!(meta_displayed)
273322
meta_displayed = meta_displayed
274323
end
324+
### Prompt Builder
325+
@onbutton builder_submit begin
326+
builder_disabled = true
327+
@info "> Prompt Builder Triggered - generating $(builder_samples) samples"
328+
first_run = isempty(builder_tabs)
329+
for i in 1:builder_samples
330+
if first_run
331+
## Generate the first version
332+
conv_current = send_to_model(
333+
:PromptGeneratorBasic; task = builder_question, model = builder_model)
334+
new_sample = Dict(:name => "tab$(i)",
335+
:label => "Sample $(i)",
336+
:display => [msg2display(msg; id)
337+
for (id, msg) in enumerate(conv_current)])
338+
## add new sample
339+
builder_tabs = push!(builder_tabs, new_sample)
340+
else
341+
## Generate the future iterations
342+
current_tab = builder_tabs[i]
343+
conv = prepare_conversation(
344+
current_tab[:display]; question = builder_question)
345+
conv_current = send_to_model(
346+
conv; model = builder_model)
347+
## update the tab
348+
current_tab[:display] = [msg2display(msg; id)
349+
for (id, msg) in enumerate(conv_current)]
350+
builder_tabs[i] = current_tab
351+
builder_tabs = builder_tabs
352+
end
353+
end
354+
355+
builder_disabled, builder_question = false, ""
356+
end
357+
@onbutton builder_reset begin
358+
@info "> Prompt Builder Reset!"
359+
builder_tabs = empty!(builder_tabs)
360+
builder_disabled, builder_question = false, ""
361+
end
362+
@onbutton builder_apply begin
363+
@info "> Applying Prompt Builder!"
364+
builder_msg = filter(x -> x[:name] == builder_tab, builder_tabs) |> only
365+
aimsg = builder_msg[:display][end][:message]
366+
instructions, inputs = parse_builder(aimsg)
367+
if isempty(instructions) && isempty(inputs)
368+
notify(__model__, "Parsing failed! Retry...")
369+
else
370+
conv_current = if isempty(inputs)
371+
notify(__model__, "Parsing failed! Expect bad results / edit as needed!")
372+
## slap all instructions into user message
373+
[PT.SystemMessage(system_prompt), PT.UserMessage(instructions)]
374+
else
375+
## turn into sytem and user message
376+
[PT.SystemMessage(instructions), PT.UserMessage(inputs)]
377+
end
378+
conv_displayed = [msg2display(msg; id)
379+
for (id, msg) in enumerate(conv_current)]
380+
## show the variables to fill in by the user -- use the last message / UserMessage
381+
chat_template_expanded = true
382+
chat_template_variables = [Dict(:id => id, :variable => String(sym),
383+
:content => "")
384+
for (id, sym) in enumerate(conv_current[end].variables)]
385+
## change page to chat
386+
selected_page = "chat"
387+
end
388+
end
275389
### Template browsing behavior
276390
@onbutton template_submit begin
277391
@info "> Template filter: $template_filter"
@@ -306,10 +420,7 @@ const HISTORY_SAVE = get(ENV, "PROTO_HISTORY_SAVE", true)
306420
end
307421
end
308422
end
309-
## TODO: add cost tracking on configuration pages + token tracking
310-
## TODO: add RAG/knowledge loading from folder or URL
311-
# Required for the JS events
312-
423+
### JAVASCRIPT SECTION ###
313424
# set focus to the first variable when it changes
314425
@watch begin
315426
raw"""
@@ -330,6 +441,16 @@ end
330441
console.log("woowza");
331442
}
332443
},
444+
// saves edits made in the chat dialog
445+
saveEdits(index) {
446+
this.chat_edit_show = false;
447+
this.conv_displayed[this.chat_edit_index].content = this.chat_edit_content;
448+
this.chat_edit_content = "";
449+
},
450+
updateLengthChat() {
451+
const tokens = Math.round(this.chat_question.length / 3.5);
452+
this.chat_question_tokens = `Approx. tokens: ${tokens}`;
453+
},
333454
focusTemplateSelect() {
334455
this.$nextTick(() => {
335456
this.$refs.tpl_select.focus();
@@ -387,6 +508,56 @@ end
387508
el.select(); // Select the <textarea> content
388509
document.execCommand('copy'); // Copy - only works as a result of a user action (e.g. click events)
389510
document.body.removeChild(el); // Remove the <textarea> element
511+
},
512+
copyToClipboardText(str) {
513+
const el = document.createElement('textarea'); // Create a <textarea> element
514+
el.value = str; // Set its value to the string that you want copied
515+
el.setAttribute('readonly', ''); // Make it readonly to be tamper-proof
516+
el.style.position = 'absolute';
517+
el.style.left = '-9999px'; // Move outside the screen to make it invisible
518+
document.body.appendChild(el); // Append the <textarea> element to the HTML document
519+
el.select(); // Select the <textarea> content
520+
document.execCommand('copy'); // Copy - only works as a result of a user action (e.g. click events)
521+
document.body.removeChild(el); // Remove the <textarea> element
522+
},
523+
async toggleRecording() {
524+
if (!this.is_recording) {
525+
this.startRecording()
526+
} else {
527+
this.stopRecording()
528+
}
529+
},
530+
async startRecording() {
531+
navigator.mediaDevices.getUserMedia({ audio: true })
532+
.then(stream => {
533+
this.is_recording = true
534+
this.mediaRecorder = new MediaRecorder(stream);
535+
this.mediaRecorder.start();
536+
this.mediaRecorder.onstop = () => {
537+
const audioBlob = new Blob(this.audio_chunks, { type: 'audio/wav' });
538+
this.is_recording = false;
539+
540+
// upload via uploader
541+
const file = new File([audioBlob], 'test.wav');
542+
this.$refs.uploader.addFiles([file], 'test.wav');
543+
this.$refs.uploader.upload(); // Trigger the upload
544+
console.log("Uploaded WAV");
545+
this.$refs.uploader.reset();
546+
this.audio_chunks=[];
547+
548+
};
549+
this.mediaRecorder.ondataavailable = event => {
550+
this.audio_chunks.push(event.data);
551+
};
552+
})
553+
.catch(error => console.error('Error accessing microphone:', error));
554+
},
555+
stopRecording() {
556+
if (this.mediaRecorder) {
557+
this.mediaRecorder.stop();
558+
} else {
559+
console.error('MediaRecorder is not initialized');
560+
}
390561
}
391562
"""
392563
end

docs/src/index.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,11 @@ hero:
1111
alt: Portal Icon
1212
actions:
1313
- theme: brand
14-
text: Get Started
15-
link: /getting_started
14+
text: Introduction
15+
link: /introduction
1616
- theme: alt
17-
text: How It Works
18-
link: /how_it_works
19-
- theme: alt
20-
text: F.A.Q.
21-
link: /frequently_asked_questions
17+
text: Reference
18+
link: /reference
2219
- theme: alt
2320
text: View on GitHub
2421
link: https://github.com/svilupp/ProToPortal.jl

docs/src/introduction.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,18 @@ For a preview, see the video: `docs/src/videos/screen-capture-code-fixing.mp4`
9292
- Change the default model or add new ones from PromptingTools.
9393
- Modify the default system prompt used when not employing a template.
9494

95+
### Meta-Prompting Tab
96+
97+
- An experimental meta-prompting experience based on [arxiv](https://arxiv.org/pdf/2401.12954).
98+
- The model calls different "experts" to solve the provided tasks.
99+
100+
### Prompt Builder Tab
101+
102+
- Generate prompt templates (for use in Chat) from a brief description of a task.
103+
- Generate multiple templates at once to choose from.
104+
- Iterate all of them by providing more inputs in the text field.
105+
- Once you're done, click "Apply in Chat" to jump to the normal chat (use as any other template, eg, fill in variables at the top).
106+
95107
And rich logging in the REPL to see what the GUI is doing under the hood!
96108

97109
## Alternatives

main.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
using Pkg
22
Pkg.activate(".")
33
using GenieFramework
4-
ENV["GENIE_HOST"] = "0.0.0.0"
4+
ENV["GENIE_HOST"] = "127.0.0.1"
55
ENV["PORT"] = "8000"
6-
ENV["GENIE_ENV"] = "prod"
7-
## include("app.jl") # hack for hot-reloading when fixing things
8-
Genie.Generator.write_secrets_file();
6+
## ENV["GENIE_ENV"] = "prod"
7+
include("app.jl") # hack for hot-reloading when fixing things
98
Genie.loadapp();
10-
up(async=false);
9+
up(async = true);

src/ProToPortal.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ using PromptingTools.Experimental.AgentTools: aicodefixer_feedback, airetry!, AI
1010
const AT = PromptingTools.Experimental.AgentTools
1111

1212
using GenieFramework
13+
using GenieFramework.JSON3
14+
using GenieFramework.Stipple.HTTP
1315
using GenieSession
1416
using GenieFramework.StippleUI.Layouts: layout
1517
using GenieFramework.StippleUI.API: kw
@@ -19,7 +21,7 @@ export Genie, Server, up, down
1921

2022
export msg2display, display2msg, template_variables, update_message, update_message!
2123
export render_messages, render_template_messages
22-
export conversation2transcript, parse_critic, load_conversations_from_dir
24+
export conversation2transcript, parse_critic, parse_builder, load_conversations_from_dir
2325
include("utils.jl")
2426

2527
export save_conversation
@@ -28,11 +30,15 @@ include("serialization.jl")
2830
export flash, flash_has_message
2931
include("flash.jl")
3032

33+
export openai_whisper
34+
include("speech_to_text.jl")
35+
3136
export messagecard, templatecard
3237
include("components.jl")
3338

3439
include("view_chat.jl")
3540
include("view_meta.jl")
41+
include("view_builder.jl")
3642

3743
export ui, ui_login
3844
include("view.jl")

0 commit comments

Comments
 (0)