Browse Source

Connect to chatGPT

`prompt.txt` template also contains (via jinja)

* chat.schema.json
* chat-example.json
* in the future, there will be patient-specific variants
master
The Dod 7 months ago
parent
commit
ad76ee7f95

+ 1
- 0
.gitignore View File

@@ -1,6 +1,7 @@
1 1
 venv/
2 2
 __pycache__/
3 3
 archive/*.json
4
+flask_session/
4 5
 work/
5 6
 .env
6 7
 *.swp

+ 113
- 3
app.py View File

@@ -1,7 +1,10 @@
1
-from flask import Flask, render_template, request, abort, send_from_directory
1
+from flask import ( Flask, render_template, request, abort, redirect,
2
+    send_from_directory, url_for, session)
3
+from flask_session import Session
2 4
 import openai
3 5
 from dotenv import load_dotenv
4 6
 from markdown import markdown
7
+import requests
5 8
 from glob import glob
6 9
 import json
7 10
 import os
@@ -9,6 +12,7 @@ import sys
9 12
 import time
10 13
 import shutil
11 14
 import re
15
+import secrets
12 16
 
13 17
 RE_VID = re.compile("""\[video ["']([^['"]*?)["']\]""")
14 18
 TEMPLATE_VID = """<video controls class="centered"><source src="{}" type="video/{}"></video>"""
@@ -30,7 +34,7 @@ def preprocess_content(defs):
30 34
             content = """<img src="{source}" alt="{alt}" class="float-right w-{width}"/><br/>\n""".format(**img)+content
31 35
         case "below":
32 36
             content = content+"""<br/>\n<img src="{source}" alt="{alt}" class="centered w-{width}"/><br/>\n""".format(**img)
33
-    if defs.get('soundtrack'):
37
+    if defs.get("use_soundtrack"):
34 38
         content = """<audio loop data-autoplay><source src="{}" type="audio/{}"></audio>\n""".format(
35 39
                       defs["soundtrack"], defs["soundtrack"].split(".")[-1])+content
36 40
     return content
@@ -41,19 +45,107 @@ def preprocess_payload(payload):
41 45
         for slide in column.get("slides", []):
42 46
             slide["content"] = preprocess_content(slide)
43 47
 
48
+def deref_schema(val):
49
+    "Deref all `$ref` entries in a json-schema"
50
+    if type(val)==type([]):
51
+        return [deref_schema(item) for item in val]
52
+    elif type(val)==type({}):
53
+        val = dict(val)
54
+        if "$ref" in val:
55
+            ref = val["$ref"]
56
+            if ref.startswith("#"):
57
+                pass # stub. Currently we don't have internal references...
58
+            elif ref.startswith("/"):
59
+                ref = url_for("home", _external=True)+ref[1:]
60
+                val.update(requests.get(ref).json())
61
+                del val["$ref"]
62
+        for key in val:
63
+            val[key] = deref_schema(val[key])
64
+        return val
65
+    else:
66
+        return val
67
+
68
+
69
+def file2json(path):
70
+    "returns json string without line breaks and indent"
71
+    return json.dumps(json.load(open(path)))
72
+
73
+def make_prompt():
74
+    json_schema = json.dumps(schema())
75
+    json_example = file2json("static/chat-example.json")
76
+    return render_template("prompt.txt", schema=json_schema, example=json_example)
77
+
44 78
 load_dotenv()
45 79
 openai.organization = "org-GFWgNyt7NSKpCv6GhzXYZTpi"
46 80
 
47 81
 application = Flask(__name__)
82
+application.config["SECRET_KEY"] = secrets.token_hex()
83
+application.config["SESSION_TYPE"] = 'filesystem'
84
+Session(application)
48 85
 
49
-@application.get("/")
86
+@application.route("/", methods=['GET', 'POST'])
50 87
 def home():
88
+    if "messages" not in session:
89
+        session["messages"] = [
90
+          {"role": "system", "content": make_prompt()},
91
+          {"role": "assistant", "content": file2json("static/initial-chat.json")}
92
+        ]
93
+    if request.method=='POST':
94
+        prompt = request.form["prompt"].strip() or "Please continue..."
95
+        session["messages"] += [{"role": "user", "content": prompt}]
96
+        valid_reply = None
97
+        while not valid_reply:
98
+            reply = openai.ChatCompletion.create(model=os.environ["MODEL_NAME"], messages=session["messages"])
99
+            session["messages"] += [reply["choices"][0]["message"]]
100
+            try:
101
+                valid_reply = json.loads(reply["choices"][0]["message"]["content"])
102
+            except Exception as e:
103
+                print(repr(e))
104
+                session["messages"] += [{"role": "system", "content": "reply was ignored because it's not a valid json. user is unaware. do not apologize to them"}]
105
+                # session["messages"] += [{"role": "user", "content": prompt}]
106
+            print("=====")
107
+            print(reply["choices"][0]["message"]["content"])
108
+            print("-----")
109
+            print(valid_reply)
110
+            print("=====")
111
+    print("*****")
112
+    print(json.dumps(session["messages"], indent=4))
113
+    print("*****")
114
+    payload = json.loads(session["messages"][-1]["content"])
115
+    payload["content"] = preprocess_content(payload)
116
+    return render_template("chat.html", **payload)
117
+
118
+@application.get("/reset")
119
+def reset():
120
+    for key in ["messages", "history"]:
121
+        if key in session:
122
+            del session[key]
123
+    return redirect(url_for('home'))
124
+
125
+@application.get("/schema.json")
126
+def schema():
127
+    "return deref_schema of `chat.schema.json` file's content"
128
+    return deref_schema(json.load(open("static/chat.schema.json")))
129
+
130
+@application.get("/messages.json")
131
+def dump_messages():
132
+    if "messages" not in session:
133
+        session["messages"] = [
134
+          {"role": "system", "content": make_prompt()},
135
+          {"role": "assistant", "content": file2json("static/initial-chat.json")}
136
+        ]
137
+    return session["messages"]
138
+
139
+@application.get("/slides")
140
+def slides():
141
+    "Legacy multy-slide format"
51 142
     payload = json.load(open("static/slides.json"))
52 143
     preprocess_payload(payload)
53 144
     return render_template("slides.html", generate_indices=False, **payload)
54 145
 
55 146
 @application.post("/update")
56 147
 def update():
148
+    "Legacy multy-slide format"
57 149
     shutil.copy(
58 150
         "static/slides.json",
59 151
         time.strftime(
@@ -65,6 +157,19 @@ def update():
65 157
     json.dump(payload, open("static/slides.json", "w"), indent=4)
66 158
     return {"status": "success"}
67 159
 
160
+@application.post("/update-chat")
161
+def update_chat():
162
+    shutil.copy(
163
+        "static/chat.json",
164
+        time.strftime(
165
+            "archive/chat-%Y-%m-%d-%H.%M.%S.json",
166
+            time.localtime()))
167
+    payload = request.get_json()
168
+    print(type(payload))
169
+    json.dump(payload, sys.stdout, indent=4)
170
+    json.dump(payload, open("static/chat.json", "w"), indent=4)
171
+    return {"status": "success"}
172
+
68 173
 @application.get("/enum/<topic>")
69 174
 def choices(topic):
70 175
     if topic in ["img", "bg", "bg-video", "audio"]:
@@ -83,8 +188,13 @@ def choices(topic):
83 188
         }
84 189
     abort(404)
85 190
 
191
+@application.get("/chat-editor")
192
+def chat_editor():
193
+    return render_template("chat-editor.html")
194
+
86 195
 @application.get("/editor")
87 196
 def editor():
197
+    "Legacy multy-slide format"
88 198
     return render_template("editor.html")
89 199
 
90 200
 @application.route("/img", methods=['GET', 'POST'])

+ 3
- 1
example.env View File

@@ -1,3 +1,5 @@
1
+# Use model du jour
2
+export MODEL_NAME="gpt-3.5-turbo-16k-0613"
1 3
 # Change *** below to an API key from
2 4
 # https://platform.openai.com/account/api-keys
3
-export OPENAI_API_KEY='***'
5
+export OPENAI_API_KEY="***"

+ 2
- 0
requirements.txt View File

@@ -1,4 +1,6 @@
1 1
 python-dotenv
2 2
 flask
3
+Flask-Session
3 4
 openai
4 5
 markdown
6
+requests

+ 3
- 0
static/chat-example.json View File

@@ -0,0 +1,3 @@
1
+{"title": "Stress Relief", "markdown": "I understand that stress relief could be helpful for you right now. Let's take a moment to focus on calming and relaxing activities. Here are a few suggestions:\n\n1. **Deep Breathing:** Close your eyes and take a deep breath in through your nose, filling your lungs with air. Hold for a few seconds and then exhale slowly through your mouth. Repeat this a few times, focusing on the sensation of your breath.\n\n2. **Progressive Muscle Relaxation:** Start by tensing the muscles in your toes and then gradually work your way up to your head, tensing and then releasing each muscle group. This exercise can help release tension from your body.\n\n3. **Guided Meditation:** Find a quiet and comfortable place to sit or lie down. You can search for guided meditations online or use apps that offer meditation exercises. These can help you relax and find inner peace.\n\n4. **Nature Sounds:** Consider listening to calming nature sounds, such as gentle rain, ocean waves, or bird songs. These sounds can create a soothing atmosphere and help you relax.\n\n5. **Journaling:** Take a few minutes to write down your thoughts and emotions. This can be a cathartic experience and help you process any stress or overwhelm that you may be feeling.\n\nRemember to be gentle with yourself during this time. Stress relief is important for your well-being. Let me know if there's anything specific you'd like to explore or if you have any other requests.", "use_bg_video": false, "bg_video": "static/media/bg-video/underwater.mp4", "bg_video_opacity": "0.5", "use_bg_image": true, "bg_image": "static/media/bg/sea-view-garden.png", "bg_image_opacity": "0.5", "use_soundtrack": true, "soundtrack": "static/media/audio/soft-rain-ambient-111154.mp3"}
2
+
3
+

+ 12
- 0
static/chat.json View File

@@ -0,0 +1,12 @@
1
+{
2
+    "title": "Welcome",
3
+    "markdown": "Hello again.\n\nHow do you feel today?",
4
+    "use_bg_video": true,
5
+    "bg_video": "static/media/bg-video/clouds1.mp4",
6
+    "bg_video_opacity": "0.75",
7
+    "use_bg_image": true,
8
+    "bg_image": "static/media/bg/valley-1.svg",
9
+    "bg_image_opacity": "0.25",
10
+    "use_soundtrack": false,
11
+    "soundtrack": "static/media/audio/sandy-beach-calm-waves-water-nature-sounds-8052.mp3"
12
+}

+ 126
- 0
static/chat.schema.json View File

@@ -0,0 +1,126 @@
1
+{
2
+  "type": "object",
3
+  "title": "Themed chat",
4
+  "properties": {
5
+    "title": {
6
+      "type": "string"
7
+    },
8
+    "markdown": {
9
+      "type": "string",
10
+      "title": "Content",
11
+      "format": "markdown",
12
+      "options": {
13
+        "simplemde": {
14
+          "renderingConfig": {
15
+            "singleLineBreaks": false
16
+          },
17
+          "toolbar": [
18
+            "bold",
19
+            "italic",
20
+            "heading",
21
+            "|",
22
+            "ordered-list",
23
+            "unordered-list",
24
+            "|",
25
+            "link",
26
+            "quote",
27
+            "|",
28
+            "preview"
29
+          ]
30
+        }
31
+      }
32
+    },
33
+    "use_bg_video": {
34
+      "title": "Use background video",
35
+      "type": "boolean",
36
+      "format": "checkbox"
37
+    },
38
+    "bg_video": {
39
+      "dependencies": {
40
+        "use_bg_video": true
41
+      },
42
+      "$ref": "/enum/bg-video",
43
+      "title": "Background video",
44
+      "links": [
45
+        {
46
+          "rel": "Preview video",
47
+          "class": "link-info",
48
+          "href": "{{self}}",
49
+          "media-type": "video/mp4"
50
+        }
51
+      ]
52
+    },
53
+    "bg_video_opacity": {
54
+      "dependencies": {
55
+        "use_bg_video": true
56
+      },
57
+      "type": "string",
58
+      "title": "Background video opacity",
59
+      "enum": [
60
+        "0.25",
61
+        "0.5",
62
+        "0.75",
63
+        "1"
64
+      ],
65
+      "default": "0.5"
66
+    },
67
+    "use_bg_image": {
68
+      "title": "Use background image",
69
+      "type": "boolean",
70
+      "format": "checkbox"
71
+    },
72
+    "bg_image": {
73
+      "dependencies": {
74
+        "use_bg_image": true
75
+      },
76
+      "$ref": "/enum/bg",
77
+      "title": "Background image",
78
+      "links": [
79
+        {
80
+          "rel": "Preview image",
81
+          "class": "link-info",
82
+          "href": "{{self}}"
83
+        }
84
+      ]
85
+    },
86
+    "bg_image_opacity": {
87
+      "dependencies": {
88
+        "use_bg_image": true
89
+      },
90
+      "type": "string",
91
+      "title": "Background image opacity",
92
+      "enum": [
93
+        "0.25",
94
+        "0.5",
95
+        "0.75",
96
+        "1"
97
+      ],
98
+      "default": "0.5"
99
+    },
100
+    "use_soundtrack": {
101
+      "title": "Use soundtrack",
102
+      "type": "boolean",
103
+      "format": "checkbox"
104
+    },
105
+    "soundtrack": {
106
+      "dependencies": {
107
+        "use_soundtrack": true
108
+      },
109
+      "type": "string",
110
+      "title": "Soundtrack",
111
+      "$ref": "/enum/audio"
112
+    }
113
+  },
114
+  "defaultProperties": [
115
+    "title",
116
+    "markdown",
117
+    "use_bg_video",
118
+    "bg_video",
119
+    "bg_video_opacity",
120
+    "use_bg_image",
121
+    "bg_image",
122
+    "bg_image_opacity",
123
+    "use_soundtrack",
124
+    "soundtrack"
125
+  ]
126
+}

+ 22
- 8
static/css/style.css View File

@@ -1,9 +1,9 @@
1 1
 .title, .reveal h3.title  {
2
-    font-weight: bold;
3
-    margin-top: 0;
4
-    color: #ffffff;
5
-    text-shadow: 1px 1px 2px #000000;
6
-    text-transform: none;
2
+	font-weight: bold;
3
+	margin-top: 0;
4
+	color: #ffffff;
5
+	text-shadow: 1px 1px 2px #000000;
6
+	text-transform: none;
7 7
 }
8 8
 
9 9
 #custom-nav a[disabled] {
@@ -23,7 +23,7 @@
23 23
 }
24 24
 
25 25
 .reveal .slides p {
26
-        margin-top: 0.25em;
26
+	margin-top: 0.25em;
27 27
 	text-align: justify;
28 28
 }
29 29
 
@@ -43,7 +43,7 @@
43 43
 
44 44
 .reveal .slides .small,
45 45
 .modal .small {
46
-        font-size: 80%;
46
+	font-size: 80%;
47 47
 }
48 48
 
49 49
 .reveal .slides ul {
@@ -82,5 +82,19 @@
82 82
 }
83 83
 
84 84
 .w-25 {
85
-	width: 33%;
85
+	width: 25%;
86
+}
87
+.scrollable {
88
+	overflow-y: auto  !important;
89
+	overflow-x: hidden !important;
90
+	height: 50vh;
91
+	background-color: #ffffff3f;
92
+	border-radius: 1rem;
93
+	padding: 1rem;
94
+}
95
+.prompt {
96
+	width:97%;
97
+	font-size: 1.5rem;
98
+	border-radius: 1rem;
99
+	padding: 0.2rem 0.5rem;
86 100
 }

+ 12
- 0
static/initial-chat.json View File

@@ -0,0 +1,12 @@
1
+{
2
+    "title": "Welcome",
3
+    "markdown": "Hello again.\n\nHow are you feeling right now? Can you share with me your emotions and thoughts? It's important for me to understand where you're at so that I can tailor our session accordingly.",
4
+    "use_bg_video": true,
5
+    "bg_video": "static/media/bg-video/clouds1.mp4",
6
+    "bg_video_opacity": "0.75",
7
+    "use_bg_image": true,
8
+    "bg_image": "static/media/bg/valley-1.svg",
9
+    "bg_image_opacity": "0.25",
10
+    "use_soundtrack": false,
11
+    "soundtrack": "static/media/audio/sandy-beach-calm-waves-water-nature-sounds-8052.mp3"
12
+}

+ 7
- 1
static/media/audio.json View File

@@ -2,5 +2,11 @@
2 2
     "ho-oponopono.mp3": "Ho'Oponopono - a Hawaiian song of reconciliation and forgiveness",
3 3
     "sonar.wav": "Sonar beep",
4 4
     "thunderstorm.mp3": "Thunderstorm",
5
-    "VJ_Memes_-_funkyGarden.mp3": "Mellow background music with slide guiter"
5
+    "VJ_Memes_-_funkyGarden.mp3": "Mellow background music with slide guiter",
6
+    "soft-rain-ambient-111154.mp3": "Soft Rain Ambient",
7
+    "forest-with-small-river-birds-and-nature-field-recording-6735.mp3": "forest with small river birds and nature field recording",
8
+    "sandy-beach-calm-waves-water-nature-sounds-8052.mp3": "Sandy Beach - Calm Waves - Water - Nature Sounds",
9
+    "evening-birds-singing-in-spring-background-sounds-of-nature-146388.mp3": "Evening birds singing in spring",
10
+    "lit-fireplace-6307.mp3": "Lit fireplace",
11
+    "mad-scientist-lab-loopable-71170.mp3": "Mad scientist lab (steampunk)"
6 12
 }

BIN
static/media/audio/evening-birds-singing-in-spring-background-sounds-of-nature-146388.mp3 View File


BIN
static/media/audio/forest-with-small-river-birds-and-nature-field-recording-6735.mp3 View File


BIN
static/media/audio/lit-fireplace-6307.mp3 View File


BIN
static/media/audio/mad-scientist-lab-loopable-71170.mp3 View File


BIN
static/media/audio/sandy-beach-calm-waves-water-nature-sounds-8052.mp3 View File


BIN
static/media/audio/soft-rain-ambient-111154.mp3 View File


BIN
static/media/audio/sorry-ai.mp3 View File


+ 2
- 1
static/media/bg.json View File

@@ -2,5 +2,6 @@
2 2
     "particles-1.svg": "Randomly moving particles work best for colorful theme with simple design (vector animation)",
3 3
     "rain-1.svg": "falling rain drops in pure lines (vector animation)",
4 4
     "twilight-1.svg": "sky with crescent moon and blinking stars during twilight (vector animation)",
5
-    "valley-1.svg": "Beautiful, smooth gradient waves in multiple layers flowing slowly and gracefully (vector animation)"
5
+    "valley-1.svg": "Beautiful, smooth gray gradient waves in multiple layers flowing slowly and gracefully (vector animation)",
6
+    "valley-2.svg": "Beautiful, smooth purple gradient waves in multiple layers flowing slowly and gracefully (vector animation)"
6 7
 }

+ 15
- 0
static/media/bg/valley-2.svg View File

@@ -0,0 +1,15 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="margin: auto; background: rgb(255, 255, 255); display: block; z-index: 1; position: relative; shape-rendering: auto;" width="1920" height="947" preserveAspectRatio="xMidYMid" viewBox="0 0 1920 947">
3
+<g transform="translate(960,473.5) scale(1,1) translate(-960,-473.5)"><linearGradient id="lg-0.5113784947453077" x1="0" x2="1" y1="0" y2="0">
4
+  <stop stop-color="#ff00ff" offset="0"></stop>
5
+  <stop stop-color="#00ffff" offset="1"></stop>
6
+</linearGradient><path d="" fill="url(#lg-0.5113784947453077)" opacity="0.4">
7
+  <animate attributeName="d" dur="10s" repeatCount="indefinite" keyTimes="0;0.333;0.667;1" calcmod="spline" keySplines="0.2 0 0.2 1;0.2 0 0.2 1;0.2 0 0.2 1" begin="0s" values="M0 0M 0 894.5862554887958Q 192 874.2643129105327 384 868.8592127204834T 768 820.840029182175T 1152 727.3793018316792T 1536 756.4735392511847T 1920 653.9722999150503L 1920 315.65115915275203Q 1728 275.049213994367 1536 274.6507969794303T 1152 177.37324735243834T 768 177.75746045396713T 384 132.15530557221675T 0 58.46405507729969Z;M0 0M 0 800.2119466882269Q 192 798.9922046196984 384 789.0174704567548T 768 773.7845177168246T 1152 762.1606724759341T 1536 750.4193969595116T 1920 701.902543459274L 1920 249.35632692931185Q 1728 263.35595067257793 1536 260.8489890527727T 1152 214.74520060430632T 768 170.09563688251043T 384 89.81025868101068T 0 44.857344099156904Z;M0 0M 0 900.8663591050625Q 192 864.9039542452147 384 857.9394098120674T 768 811.0306319600473T 1152 715.9533696999769T 1536 739.6274636674682T 1920 669.5297170831273L 1920 268.82564498223144Q 1728 228.11930706834903 1536 221.8924995651106T 1152 190.6159917309161T 768 186.6721761128069T 384 143.8041660902612T 0 108.18302269032151Z;M0 0M 0 894.5862554887958Q 192 874.2643129105327 384 868.8592127204834T 768 820.840029182175T 1152 727.3793018316792T 1536 756.4735392511847T 1920 653.9722999150503L 1920 315.65115915275203Q 1728 275.049213994367 1536 274.6507969794303T 1152 177.37324735243834T 768 177.75746045396713T 384 132.15530557221675T 0 58.46405507729969Z"></animate>
8
+</path><path d="" fill="url(#lg-0.5113784947453077)" opacity="0.4">
9
+  <animate attributeName="d" dur="10s" repeatCount="indefinite" keyTimes="0;0.333;0.667;1" calcmod="spline" keySplines="0.2 0 0.2 1;0.2 0 0.2 1;0.2 0 0.2 1" begin="-2.5s" values="M0 0M 0 820.578891738158Q 192 794.048062150797 384 790.9550760961745T 768 766.9526335844234T 1152 788.1921236554847T 1536 719.3274144091307T 1920 720.9980851444116L 1920 232.07439466440857Q 1728 276.73996472332755 1536 271.24414033252293T 1152 235.27083684381307T 768 215.95157440919272T 384 150.46464669733652T 0 132.26890906748812Z;M0 0M 0 818.9332512243135Q 192 841.0072598377415 384 837.1371668556186T 768 765.2638739692692T 1152 708.1451713279033T 1536 695.7969806006746T 1920 652.3613411751903L 1920 293.37159884257323Q 1728 290.5363625534058 1536 286.78662514281916T 1152 140.2018102680999T 768 198.62948911339666T 384 181.47867906282383T 0 39.692517606167854Z;M0 0M 0 806.1141497748507Q 192 797.1258294525562 384 795.2560762455262T 768 820.4123717575995T 1152 793.6627134691432T 1536 759.2385573025834T 1920 633.8973846901227L 1920 298.88561542333093Q 1728 198.36340963806794 1536 188.55124916763856T 1152 185.20001713147371T 768 192.40098468410187T 384 185.01313440707108T 0 101.25396880383195Z;M0 0M 0 820.578891738158Q 192 794.048062150797 384 790.9550760961745T 768 766.9526335844234T 1152 788.1921236554847T 1536 719.3274144091307T 1920 720.9980851444116L 1920 232.07439466440857Q 1728 276.73996472332755 1536 271.24414033252293T 1152 235.27083684381307T 768 215.95157440919272T 384 150.46464669733652T 0 132.26890906748812Z"></animate>
10
+</path><path d="" fill="url(#lg-0.5113784947453077)" opacity="0.4">
11
+  <animate attributeName="d" dur="10s" repeatCount="indefinite" keyTimes="0;0.333;0.667;1" calcmod="spline" keySplines="0.2 0 0.2 1;0.2 0 0.2 1;0.2 0 0.2 1" begin="-5s" values="M0 0M 0 885.9878293673725Q 192 849.7049524034372 384 847.0714157039724T 768 788.9437588718265T 1152 760.3578953474516T 1536 667.5378477909776T 1920 727.2622486991087L 1920 245.2740791225873Q 1728 263.9772059474078 1536 261.02628081293534T 1152 251.94635645434562T 768 151.87264675796956T 384 116.65543007147687T 0 92.62548089693561Z;M0 0M 0 896.4068688953673Q 192 778.6099838457578 384 778.2386901606893T 768 736.9130622372895T 1152 778.0864714049906T 1536 702.0059342397407T 1920 706.8988994416476L 1920 283.373984627806Q 1728 197.52077013891304 1536 192.25436770839153T 1152 179.03754984778664T 768 192.6272284147079T 384 127.41825064969092T 0 120.3365995024433Z;M0 0M 0 905.0545447091121Q 192 771.0464991483362 384 770.7504405718763T 768 737.9470720435592T 1152 761.4950054370945T 1536 729.6425742035786T 1920 628.0782403018484L 1920 268.81964551632745Q 1728 283.4893949984377 1536 280.4062268277233T 1152 232.73410536907147T 768 136.58709243664552T 384 177.83656529099065T 0 105.12907476123561Z;M0 0M 0 885.9878293673725Q 192 849.7049524034372 384 847.0714157039724T 768 788.9437588718265T 1152 760.3578953474516T 1536 667.5378477909776T 1920 727.2622486991087L 1920 245.2740791225873Q 1728 263.9772059474078 1536 261.02628081293534T 1152 251.94635645434562T 768 151.87264675796956T 384 116.65543007147687T 0 92.62548089693561Z"></animate>
12
+</path><path d="" fill="url(#lg-0.5113784947453077)" opacity="0.4">
13
+  <animate attributeName="d" dur="10s" repeatCount="indefinite" keyTimes="0;0.333;0.667;1" calcmod="spline" keySplines="0.2 0 0.2 1;0.2 0 0.2 1;0.2 0 0.2 1" begin="-7.5s" values="M0 0M 0 851.2782171711034Q 192 811.9374452565633 384 806.8006580346555T 768 776.014045894985T 1152 701.7253782114377T 1536 718.9887158951129T 1920 660.2346627727933L 1920 258.4327207481116Q 1728 221.22469308334493 1536 219.62743587397694T 1152 241.29989577865155T 768 153.52941384398162T 384 139.7742205366321T 0 85.92598640222806Z;M0 0M 0 821.7868026609375Q 192 844.5116715787133 384 839.0972500642432T 768 809.6564473606737T 1152 791.1303523091464T 1536 749.6669107001892T 1920 731.9042249422911L 1920 257.57973280152544Q 1728 279.42212842910214 1536 272.8272641684757T 1152 142.66246370522202T 768 155.70506390784996T 384 127.24527463076797T 0 68.58997328879795Z;M0 0M 0 903.852837413974Q 192 827.4710691224941 384 820.1259242816732T 768 798.4796672764697T 1152 753.5843765228167T 1536 688.1946143381927T 1920 697.5646392291058L 1920 265.25630142796507Q 1728 179.11544593769628 1536 175.28579973170906T 1152 222.65108580333765T 768 135.25191722722622T 384 90.82164266345612T 0 70.78823785841863Z;M0 0M 0 851.2782171711034Q 192 811.9374452565633 384 806.8006580346555T 768 776.014045894985T 1152 701.7253782114377T 1536 718.9887158951129T 1920 660.2346627727933L 1920 258.4327207481116Q 1728 221.22469308334493 1536 219.62743587397694T 1152 241.29989577865155T 768 153.52941384398162T 384 139.7742205366321T 0 85.92598640222806Z"></animate>
14
+</path></g>
15
+</svg>

+ 4
- 1
static/media/img.json View File

@@ -6,5 +6,8 @@
6 6
     "lightning.gif": "lightning striking downwards (video, gif animation, grayscale, portrait proportions)",
7 7
     "lightning-tree.png": "a tree in front of a lightning and a stormy, cloudy dark blue sky",
8 8
     "thunderstorm.gif": "trees shaking rapidly in front of stormy cloudy sky with frequent lightnings and rapid, cyclic cloud movement (gif animation)",
9
-    "marble-question-mark.png": "A huge marble question mark inside a hall with a skylight"
9
+    "marble-question-mark.png": "A huge marble question mark inside a hall with a skylight",
10
+    "steampunk-machine.png": "A complex and slightly bent steampunk brass machine with pipes and pressure gauges",
11
+    "fire-4623.gif": "fireplace (gif animation)",
12
+    "tnh-quote-nothing-to-chase.jpg": "Thich Nhat Hahn photo and quote: there is nothing to chase after... (portrait proportions)"
10 13
 }

BIN
static/media/img/fire-4623.gif View File


BIN
static/media/img/steampunk-machine.png View File


BIN
static/media/img/thunderstorm.gif View File


BIN
static/media/img/thunderstorm2.gif View File


BIN
static/media/img/tnh-quote-nothing-to-chase.jpg View File


+ 18
- 3
static/slides.json View File

@@ -5,12 +5,12 @@
5 5
             "id": "welcome",
6 6
             "title": "Welcome",
7 7
             "params": {
8
-                "theme": "simple",
8
+                "theme": "sky",
9 9
                 "background-image": "static/media/bg/valley-1.svg",
10 10
                 "background-opacity": "0.5",
11 11
                 "background-size": "cover"
12 12
             },
13
-            "markdown": "This is an example of a vertical sequence (single chapter containing pages)",
13
+            "markdown": "This is an example of a vertical sequence (single chapter containing pages)\n\n[video bell.mp4]\n\nyou can also watch the video",
14 14
             "slides": [
15 15
                 {
16 16
                     "id": "regret",
@@ -28,7 +28,7 @@
28 28
                         "alt": ""
29 29
                     },
30 30
                     "markdown": "Let us dwell on the negative emotions you have mentioned:\n\n* Regret\n* sorrow\n\nWhen you are ready, we can [continue to forgiveness](#forgiveness).",
31
-                    "soundtrack": "static/media/audio/thunderstorm.mp3"
31
+                    "soundtrack": "static/media/audio/soft-rain-ambient-111154.mp3"
32 32
                 },
33 33
                 {
34 34
                     "id": "forgiveness",
@@ -42,6 +42,21 @@
42 42
                     },
43 43
                     "markdown": "Let me play for you a Hawaiian song called Ho'Opnopono. it has healing qualities for such moments.\n\n* Close your eyes.\n* Listen.\n* Sing in your heart.\n* Join with your voice.",
44 44
                     "soundtrack": "static/media/audio/ho-oponopono.mp3"
45
+                },
46
+                {
47
+                    "id": "end",
48
+                    "title": "The end",
49
+                    "params": {
50
+                        "theme": "league"
51
+                    },
52
+                    "image": {
53
+                        "placement": "above",
54
+                        "width": "75",
55
+                        "source": "static/media/img/fire-4623.gif",
56
+                        "alt": "Fireplace"
57
+                    },
58
+                    "markdown": "#### Now we can rest",
59
+                    "soundtrack": "static/media/audio/lit-fireplace-6307.mp3"
45 60
                 }
46 61
             ]
47 62
         }

+ 58
- 0
templates/chat-editor.html View File

@@ -0,0 +1,58 @@
1
+<!doctype html>
2
+<html lang="en">
3
+  <head>
4
+    <meta charset="utf-8">
5
+    <meta name="viewport" content="width=device-width, initial-scale=1">
6
+    <title>Themed chat editor</title>
7
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
8
+    <link rel="stylesheet" href=
9
+        "https://use.fontawesome.com/releases/v5.6.3/css/all.css"
10
+        integrity=
11
+            "sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
12
+            crossorigin="anonymous">
13
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.css">
14
+  </head>
15
+  <body>
16
+    <div class="container">
17
+      <h1>Themed chat editor</h1>
18
+      <div id='editor_holder'></div>
19
+      <div class="btn-group mt-2">
20
+        <button class="btn btn-success" id='submit'>Submit</button>
21
+        <a class="btn btn-secondary" href="/slides">Cancel</a>
22
+      </div>
23
+    </div>
24
+    <script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
25
+    <script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script>
26
+    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
27
+    <script>
28
+      fetch("/static/chat.schema.json")
29
+        .then(res => res.json())
30
+        .then(schema => {
31
+          fetch("/static/chat.json")
32
+            .then(res => res.json())
33
+            .then(slides => {
34
+              // Initialize the editor with a JSON schema
35
+              var editor = new JSONEditor(
36
+                document.getElementById('editor_holder'), {
37
+                  theme: "bootstrap5",
38
+                  iconlib: "fontawesome5",
39
+                  ajax: true,
40
+                  schema: schema,
41
+                  startval: slides
42
+                });
43
+              // Hook up the submit button to log to the console
44
+              document.getElementById('submit').addEventListener('click',() => {
45
+                // Get the value from the editor
46
+                fetch("/update-chat", {
47
+                  method: "POST",
48
+                  body: JSON.stringify(editor.getValue()),
49
+                  headers: {
50
+                      "Content-type": "application/json; charset=UTF-8"
51
+                  }
52
+                }).then((response) => { location.replace('/') });
53
+              });
54
+            });
55
+          });
56
+    </script>
57
+  </body>
58
+</html>

templates/construction.html → templates/chat.html View File

@@ -4,48 +4,40 @@
4 4
 		<meta charset="utf-8">
5 5
 		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6 6
 
7
-		<title>Under construction</title>
7
+		<title>{{title}}</title>
8 8
 
9
-		<link rel="stylesheet" href="/static/css/style.css" />
10
-		<link rel="stylesheet" href="/static/reveal/dist/reset.css" />
11
-		<link rel="stylesheet" href="/static/reveal/dist/reveal.css" />
12
-		<link id="theme" rel="stylesheet" href="/static/reveal/dist/theme/white.css" />
13
-		<link id="nav-theme" rel="stylesheet" href="/static/css/nav/white.css" />
9
+		<link rel="stylesheet" href="static/css/style.css" />
10
+		<link rel="stylesheet" href="static/reveal/dist/reset.css" />
11
+		<link rel="stylesheet" href="static/reveal/dist/reveal.css" />
12
+		<link id="theme" rel="stylesheet" href="static/reveal/dist/theme/sky.css" />
13
+		<link id="nav-theme" rel="stylesheet" href="static/css/nav/sky.css" />
14 14
                 <link rel="stylesheet" href=
15 15
                     "https://use.fontawesome.com/releases/v5.6.3/css/all.css"
16 16
                     integrity=
17 17
                         "sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/"
18 18
                         crossorigin="anonymous">
19
-
20
-
21
-		<!-- Theme used for syntax highlighted code -->
22
-		<!-- link rel="stylesheet" href="/static/reveal/plugin/highlight/monokai.css" -->
23 19
 	</head>
24 20
 	<body>
25 21
                 <div id="custom-nav">
26 22
                   <!-- the zero-width-space is a tweak against tidy removing empty tags -->
27
-                  <a href="#/" title="Home"><i class="fa fa-home">​</i></a>
28
-                  <a id="top-link" title="Section top" disabled="disabled"><i class="fa fa-angle-double-up">​</i></a>
29
-                  <a href="/editor" title="Edit"><i class="fa fa-user-edit">​</i></a>
23
+                  <a href="/reset" title="Reset"><i class="fa fa-redo">​</i></a>
30 24
                 </div>
31 25
 		<div class="reveal">
32 26
 			<div class="slides">
33
-                          
34
-				<section data-background-opacity="0.5" data-background-size="cover" data-background-video="static/bg-video/underwater.mp4" data-background-video-loop="True" data-background-video-mute="True" data-theme="simple">
35
-                                    <section id="construction">
36
-                                        
37
-                                        <h3 class="title slide_title">Under construction</h3>
38
-                                        
39
-
40
-                                        
27
+				<section data-theme="sky" data-background-size="cover"{% if use_bg_video %} data-background-video="{{bg_video}}" data-background-opacity="{{bg_video_opacity}}" data-background-video-loop="True" data-background-video-mute="True"{% endif %}>
28
+                                    <section{% if use_bg_image %} data-background="{{bg_image}}" data-background-opacity="{{bg_image_opacity}}" data-background-size="cover"{% endif %}>                                        
29
+                                        <h3 class="title slide_title">{{title}}</h3>
30
+                                        <div class="scrollable">
31
+                                            {{ content|safe }}
32
+                                        </div>
33
+                                        <form method="POST" action="">
34
+                                        <!-- onsubmit='document.getElementById("prompt").setAttribute("disabled", "")' -->
35
+                                            <input class="prompt" id="prompt" name="prompt" placeholder="Talk to me" />
36
+                                        </form>
41 37
                                     </section>
42
-                                    
43
-                                    
44 38
 				</section>
45
-                          
46 39
 			</div>
47 40
 		</div>
48
-
49 41
 		<script src="/static/reveal/dist/reveal.js"></script>
50 42
 		<script src="/static/reveal/plugin/notes/notes.js"></script>
51 43
 		<script src="/static/reveal/plugin/markdown/markdown.js"></script>
@@ -58,6 +50,7 @@
58 50
 			Reveal.initialize({
59 51
 				hash: true,
60 52
                                 progress: false,
53
+                                help: false,
61 54
                                 transition: 'convex',
62 55
                                 controlsBackArrows: 'visible',  // would hopefully solve the faded-arrow thing
63 56
                                 // slideNumber: "h.v",

+ 2
- 2
templates/editor.html View File

@@ -18,7 +18,7 @@
18 18
       <div id='editor_holder'></div>
19 19
       <div class="btn-group mt-2">
20 20
         <button class="btn btn-success" id='submit'>Submit</button>
21
-        <a class="btn btn-secondary" href="/">Cancel</a>
21
+        <a class="btn btn-secondary" href="/slides">Cancel</a>
22 22
       </div>
23 23
     </div>
24 24
     <script src="https://cdn.jsdelivr.net/simplemde/latest/simplemde.min.js"></script>
@@ -49,7 +49,7 @@
49 49
                   headers: {
50 50
                       "Content-type": "application/json; charset=UTF-8"
51 51
                   }
52
-                }).then((response) => { location.replace('/') });
52
+                }).then((response) => { location.replace('/slides') });
53 53
               });
54 54
             });
55 55
           });

+ 20
- 0
templates/prompt.txt View File

@@ -0,0 +1,20 @@
1
+assistant is a spiritual caregiver for the dying (also called "death doula") inspired by the ideas described at "die wise" by stephen jenkinson
2
+
3
+user is odelia, she was informed that her cancer was incurable. she is 48 years old. she had to retire from a career in the television industry. she is a single mother to a highschool daughter.
4
+
5
+The goal of your session is to create enchanted moments, or ceremonies via the multimedia resources available to you (see below), but first you should inquire how the user feels now and what media would suit best their needs.
6
+
7
+IMPORTANT!
8
+
9
+1. Your response should be valid json: linebreaks should be escaped as \n, quotes as \", the [escaped] "markdown" value should include \\n\\n when you expects a paragraph break, etc. It should also be valid according to the json-schema definition above. You can rely on "enum_title" as textual explanations of "enum" choices.
10
+
11
+2. Although the markdown area is scrollable, best is to keep the text under 350 characters (less if there are paragraph breaks or bullet list).
12
+
13
+```
14
+{{schema}}
15
+```
16
+
17
+For example:
18
+```
19
+{{example}}
20
+```

Loading…
Cancel
Save