Spaces:
Running
Running
Add code for the first version
Browse files- .gitignore +146 -0
- .idea/.gitignore +3 -0
- .idea/inspectionProfiles/Project_Default.xml +14 -0
- .idea/inspectionProfiles/profiles_settings.xml +6 -0
- .idea/misc.xml +4 -0
- .idea/modules.xml +8 -0
- .idea/slide-deck-ai.iml +14 -0
- .idea/vcs.xml +6 -0
- app.py +178 -0
- clarifai_grpc_helper.py +76 -0
- examples/example_01.json +4 -0
- examples/example_01_structured_output.json +44 -0
- examples/example_02.json +4 -0
- examples/example_02_structured_output.json +62 -0
- global_config.py +28 -0
- langchain_templates/template_07.txt +5 -0
- llm_helper.py +197 -0
- pptx_helper.py +99 -0
- requirements.txt +9 -0
- strings.json +23 -0
.gitignore
ADDED
@@ -0,0 +1,146 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
client_secret.json
|
2 |
+
credentials.json
|
3 |
+
token.json
|
4 |
+
*.pptx
|
5 |
+
|
6 |
+
|
7 |
+
### Python template
|
8 |
+
# Byte-compiled / optimized / DLL files
|
9 |
+
__pycache__/
|
10 |
+
*.py[cod]
|
11 |
+
*$py.class
|
12 |
+
|
13 |
+
# C extensions
|
14 |
+
*.so
|
15 |
+
|
16 |
+
# Distribution / packaging
|
17 |
+
.Python
|
18 |
+
build/
|
19 |
+
develop-eggs/
|
20 |
+
dist/
|
21 |
+
downloads/
|
22 |
+
eggs/
|
23 |
+
.eggs/
|
24 |
+
lib/
|
25 |
+
lib64/
|
26 |
+
parts/
|
27 |
+
sdist/
|
28 |
+
var/
|
29 |
+
wheels/
|
30 |
+
share/python-wheels/
|
31 |
+
*.egg-info/
|
32 |
+
.installed.cfg
|
33 |
+
*.egg
|
34 |
+
MANIFEST
|
35 |
+
|
36 |
+
# PyInstaller
|
37 |
+
# Usually these files are written by a python script from a template
|
38 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
39 |
+
*.manifest
|
40 |
+
*.spec
|
41 |
+
|
42 |
+
# Installer logs
|
43 |
+
pip-log.txt
|
44 |
+
pip-delete-this-directory.txt
|
45 |
+
|
46 |
+
# Unit test / coverage reports
|
47 |
+
htmlcov/
|
48 |
+
.tox/
|
49 |
+
.nox/
|
50 |
+
.coverage
|
51 |
+
.coverage.*
|
52 |
+
.cache
|
53 |
+
nosetests.xml
|
54 |
+
coverage.xml
|
55 |
+
*.cover
|
56 |
+
*.py,cover
|
57 |
+
.hypothesis/
|
58 |
+
.pytest_cache/
|
59 |
+
cover/
|
60 |
+
|
61 |
+
# Translations
|
62 |
+
*.mo
|
63 |
+
*.pot
|
64 |
+
|
65 |
+
# Django stuff:
|
66 |
+
*.log
|
67 |
+
local_settings.py
|
68 |
+
db.sqlite3
|
69 |
+
db.sqlite3-journal
|
70 |
+
|
71 |
+
# Flask stuff:
|
72 |
+
instance/
|
73 |
+
.webassets-cache
|
74 |
+
|
75 |
+
# Scrapy stuff:
|
76 |
+
.scrapy
|
77 |
+
|
78 |
+
# Sphinx documentation
|
79 |
+
docs/_build/
|
80 |
+
|
81 |
+
# PyBuilder
|
82 |
+
.pybuilder/
|
83 |
+
target/
|
84 |
+
|
85 |
+
# Jupyter Notebook
|
86 |
+
.ipynb_checkpoints
|
87 |
+
|
88 |
+
# IPython
|
89 |
+
profile_default/
|
90 |
+
ipython_config.py
|
91 |
+
|
92 |
+
# pyenv
|
93 |
+
# For a library or package, you might want to ignore these files since the code is
|
94 |
+
# intended to run in multiple environments; otherwise, check them in:
|
95 |
+
# .python-version
|
96 |
+
|
97 |
+
# pipenv
|
98 |
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
99 |
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
100 |
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
101 |
+
# install all needed dependencies.
|
102 |
+
#Pipfile.lock
|
103 |
+
|
104 |
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
105 |
+
__pypackages__/
|
106 |
+
|
107 |
+
# Celery stuff
|
108 |
+
celerybeat-schedule
|
109 |
+
celerybeat.pid
|
110 |
+
|
111 |
+
# SageMath parsed files
|
112 |
+
*.sage.py
|
113 |
+
|
114 |
+
# Environments
|
115 |
+
.env
|
116 |
+
.venv
|
117 |
+
env/
|
118 |
+
venv/
|
119 |
+
ENV/
|
120 |
+
env.bak/
|
121 |
+
venv.bak/
|
122 |
+
|
123 |
+
# Spyder project settings
|
124 |
+
.spyderproject
|
125 |
+
.spyproject
|
126 |
+
|
127 |
+
# Rope project settings
|
128 |
+
.ropeproject
|
129 |
+
|
130 |
+
# mkdocs documentation
|
131 |
+
/site
|
132 |
+
|
133 |
+
# mypy
|
134 |
+
.mypy_cache/
|
135 |
+
.dmypy.json
|
136 |
+
dmypy.json
|
137 |
+
|
138 |
+
# Pyre type checker
|
139 |
+
.pyre/
|
140 |
+
|
141 |
+
# pytype static type analyzer
|
142 |
+
.pytype/
|
143 |
+
|
144 |
+
# Cython debug symbols
|
145 |
+
cython_debug/
|
146 |
+
|
.idea/.gitignore
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# Default ignored files
|
2 |
+
/shelf/
|
3 |
+
/workspace.xml
|
.idea/inspectionProfiles/Project_Default.xml
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<component name="InspectionProjectProfileManager">
|
2 |
+
<profile version="1.0">
|
3 |
+
<option name="myName" value="Project Default" />
|
4 |
+
<inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
5 |
+
<option name="ignoredPackages">
|
6 |
+
<value>
|
7 |
+
<list size="1">
|
8 |
+
<item index="0" class="java.lang.String" itemvalue="numpy" />
|
9 |
+
</list>
|
10 |
+
</value>
|
11 |
+
</option>
|
12 |
+
</inspection_tool>
|
13 |
+
</profile>
|
14 |
+
</component>
|
.idea/inspectionProfiles/profiles_settings.xml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<component name="InspectionProjectProfileManager">
|
2 |
+
<settings>
|
3 |
+
<option name="USE_PROJECT_PROFILE" value="false" />
|
4 |
+
<version value="1.0" />
|
5 |
+
</settings>
|
6 |
+
</component>
|
.idea/misc.xml
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (slide-deck-ai)" project-jdk-type="Python SDK" />
|
4 |
+
</project>
|
.idea/modules.xml
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="ProjectModuleManager">
|
4 |
+
<modules>
|
5 |
+
<module fileurl="file://$PROJECT_DIR$/.idea/slide-deck-ai.iml" filepath="$PROJECT_DIR$/.idea/slide-deck-ai.iml" />
|
6 |
+
</modules>
|
7 |
+
</component>
|
8 |
+
</project>
|
.idea/slide-deck-ai.iml
ADDED
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<module type="PYTHON_MODULE" version="4">
|
3 |
+
<component name="NewModuleRootManager">
|
4 |
+
<content url="file://$MODULE_DIR$">
|
5 |
+
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
6 |
+
</content>
|
7 |
+
<orderEntry type="inheritedJdk" />
|
8 |
+
<orderEntry type="sourceFolder" forTests="false" />
|
9 |
+
</component>
|
10 |
+
<component name="PyDocumentationSettings">
|
11 |
+
<option name="format" value="PLAIN" />
|
12 |
+
<option name="myDocStringFormat" value="Plain" />
|
13 |
+
</component>
|
14 |
+
</module>
|
.idea/vcs.xml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
+
<project version="4">
|
3 |
+
<component name="VcsDirectoryMappings">
|
4 |
+
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
5 |
+
</component>
|
6 |
+
</project>
|
app.py
ADDED
@@ -0,0 +1,178 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json5
|
2 |
+
import time
|
3 |
+
import streamlit as st
|
4 |
+
import streamlit.runtime.scriptrunner as st_sr
|
5 |
+
|
6 |
+
import llm_helper
|
7 |
+
import pptx_helper
|
8 |
+
from global_config import GlobalConfig
|
9 |
+
|
10 |
+
|
11 |
+
APP_TEXT = json5.loads(open(GlobalConfig.APP_STRINGS_FILE, 'r').read())
|
12 |
+
|
13 |
+
|
14 |
+
def build_ui():
|
15 |
+
"""
|
16 |
+
Display the input elements for content generation. Only covers the first step.
|
17 |
+
"""
|
18 |
+
|
19 |
+
st.title(APP_TEXT['app_name'])
|
20 |
+
st.subheader(APP_TEXT['caption'])
|
21 |
+
st.divider()
|
22 |
+
|
23 |
+
st.header(APP_TEXT['section_headers'][0])
|
24 |
+
st.caption(APP_TEXT['section_captions'][0])
|
25 |
+
|
26 |
+
try:
|
27 |
+
with open(GlobalConfig.PRELOAD_DATA_FILE, 'r') as in_file:
|
28 |
+
preload_data = json5.loads(in_file.read())
|
29 |
+
except (FileExistsError, FileNotFoundError):
|
30 |
+
preload_data = {'topic': '', 'audience': ''}
|
31 |
+
|
32 |
+
topic = st.text_area(
|
33 |
+
APP_TEXT['input_labels'][0],
|
34 |
+
value=preload_data['topic']
|
35 |
+
)
|
36 |
+
|
37 |
+
# Button with callback function
|
38 |
+
st.button(APP_TEXT['button_labels'][0], on_click=button_clicked, args=[0])
|
39 |
+
|
40 |
+
if st.session_state.clicked[0]:
|
41 |
+
progress_text = 'Generating your presentation slides...give it a moment'
|
42 |
+
progress_bar = st.progress(0, text=progress_text)
|
43 |
+
|
44 |
+
topic_txt = topic.strip()
|
45 |
+
process_topic_inputs(topic_txt, progress_bar)
|
46 |
+
|
47 |
+
|
48 |
+
def process_topic_inputs(topic: str, progress_bar):
|
49 |
+
"""
|
50 |
+
Process the inputs to generate contents for the slides.
|
51 |
+
|
52 |
+
:param topic: The presentation topic
|
53 |
+
:param progress_bar: Progress bar from the page
|
54 |
+
:return:
|
55 |
+
"""
|
56 |
+
|
57 |
+
topic_length = len(topic)
|
58 |
+
print(f'Input length:: topic: {topic_length}')
|
59 |
+
|
60 |
+
if topic_length > 10:
|
61 |
+
print(
|
62 |
+
f'Topic: {topic}\n'
|
63 |
+
)
|
64 |
+
print('=' * 20)
|
65 |
+
|
66 |
+
target_length = min(topic_length, GlobalConfig.LLM_MODEL_MAX_INPUT_LENGTH)
|
67 |
+
|
68 |
+
try:
|
69 |
+
slides_content = llm_helper.generate_slides_content(topic[:target_length]).strip()
|
70 |
+
content_length = len(slides_content)
|
71 |
+
|
72 |
+
print('=' * 20)
|
73 |
+
print(f'Slides content:\n{slides_content}')
|
74 |
+
print(f'Content length: {content_length}')
|
75 |
+
print('=' * 20)
|
76 |
+
st.write(f'''Slides content:\n{slides_content}''')
|
77 |
+
progress_bar.progress(100, text='Done!')
|
78 |
+
|
79 |
+
if content_length == 0:
|
80 |
+
st.error(APP_TEXT['content_generation_failure_error'])
|
81 |
+
return
|
82 |
+
|
83 |
+
st.info(
|
84 |
+
'The generated content doesn\'t look so great?'
|
85 |
+
' Need alternatives? Just change your description text and try again.'
|
86 |
+
' For example, you can start describing like "Create a slide deck on..."',
|
87 |
+
icon="ℹ️"
|
88 |
+
)
|
89 |
+
|
90 |
+
# Move on to step 2
|
91 |
+
st.divider()
|
92 |
+
st.header(APP_TEXT['section_headers'][1])
|
93 |
+
st.caption(APP_TEXT['section_captions'][1])
|
94 |
+
|
95 |
+
# Streamlit multiple buttons work in a weird way!
|
96 |
+
# Click on any button, the page just reloads!
|
97 |
+
# Buttons are not "stateful"
|
98 |
+
# https://blog.streamlit.io/10-most-common-explanations-on-the-streamlit-forum/#1-buttons-aren%E2%80%99t-stateful
|
99 |
+
# Apparently, "nested button click" needs to be handled differently
|
100 |
+
# https://playground.streamlit.app/?q=triple-button
|
101 |
+
|
102 |
+
st.button(APP_TEXT['button_labels'][1], on_click=button_clicked, args=[1])
|
103 |
+
|
104 |
+
if st.session_state.clicked[1]:
|
105 |
+
progress_text = 'Converting...give it a moment'
|
106 |
+
progress_bar = st.progress(0, text=progress_text)
|
107 |
+
|
108 |
+
process_slides_contents(slides_content, progress_bar)
|
109 |
+
except ValueError as ve:
|
110 |
+
st.error(f'Unfortunately, an error occurred: {ve}! '
|
111 |
+
f'Please change the text, try again later, or report it, sharing your inputs.')
|
112 |
+
|
113 |
+
else:
|
114 |
+
st.error('Not enough information provided! Please be little more descriptive :)')
|
115 |
+
|
116 |
+
|
117 |
+
def process_slides_contents(text: str, progress_bar: st.progress):
|
118 |
+
"""
|
119 |
+
Convert given text into structured data and display. Update the UI.
|
120 |
+
|
121 |
+
:param text: The contents generated for the slides
|
122 |
+
:param progress_bar: Progress bar for this step
|
123 |
+
"""
|
124 |
+
|
125 |
+
print('JSON button clicked')
|
126 |
+
json_str = llm_helper.text_to_json(text)
|
127 |
+
# yaml_str = llm_helper.text_to_yaml(text)
|
128 |
+
print('=' * 20)
|
129 |
+
print(f'JSON:\n{json_str}')
|
130 |
+
print('=' * 20)
|
131 |
+
st.code(json_str, language='json')
|
132 |
+
|
133 |
+
progress_bar.progress(100, text='Done!')
|
134 |
+
|
135 |
+
# Now, step 3
|
136 |
+
st.divider()
|
137 |
+
st.header(APP_TEXT['section_headers'][2])
|
138 |
+
st.caption(APP_TEXT['section_captions'][2])
|
139 |
+
|
140 |
+
st.button(APP_TEXT['button_labels'][2], on_click=button_clicked, args=[2])
|
141 |
+
|
142 |
+
if st.session_state.clicked[2]:
|
143 |
+
progress_text = 'Creating the slide deck...give it a moment'
|
144 |
+
progress_bar = st.progress(0, text=progress_text)
|
145 |
+
|
146 |
+
# Get a unique name for the file to save -- use the session ID
|
147 |
+
ctx = st_sr.get_script_run_ctx()
|
148 |
+
session_id = ctx.session_id
|
149 |
+
timestamp = time.time()
|
150 |
+
output_file_name = f'{session_id}_{timestamp}.pptx'
|
151 |
+
|
152 |
+
pptx_helper.generate_powerpoint_presentation(json_str, as_yaml=False, output_file_name=output_file_name)
|
153 |
+
st.progress(100, text='Done!')
|
154 |
+
|
155 |
+
# st.download_button('Download file', binary_contents) # Defaults to 'application/octet-stream'
|
156 |
+
|
157 |
+
with open(output_file_name, 'rb') as f:
|
158 |
+
st.download_button('Download PPTX file', f, file_name=output_file_name)
|
159 |
+
|
160 |
+
|
161 |
+
def button_clicked(button):
|
162 |
+
"""
|
163 |
+
Update the button clicked value in session state.
|
164 |
+
"""
|
165 |
+
|
166 |
+
st.session_state.clicked[button] = True
|
167 |
+
|
168 |
+
|
169 |
+
def main():
|
170 |
+
# Initialize the key in session state to manage the nested buttons states
|
171 |
+
if 'clicked' not in st.session_state:
|
172 |
+
st.session_state.clicked = {0: False, 1: False, 2: False}
|
173 |
+
|
174 |
+
build_ui()
|
175 |
+
|
176 |
+
|
177 |
+
if __name__ == '__main__':
|
178 |
+
main()
|
clarifai_grpc_helper.py
ADDED
@@ -0,0 +1,76 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from global_config import GlobalConfig
|
2 |
+
|
3 |
+
######################################################################################################
|
4 |
+
# In this section, we set the user authentication, user and app ID, model details, and the URL of
|
5 |
+
# the text we want as an input. Change these strings to run your own example.
|
6 |
+
######################################################################################################
|
7 |
+
|
8 |
+
# Your PAT (Personal Access Token) can be found in the portal under Authentification
|
9 |
+
PAT = '7244fc3df026429d819f9df31309ab9d'
|
10 |
+
# Specify the correct user_id/app_id pairings
|
11 |
+
# Since you're making inferences outside your app's scope
|
12 |
+
USER_ID = 'meta'
|
13 |
+
APP_ID = 'Llama-2'
|
14 |
+
# Change these to whatever model and text URL you want to use
|
15 |
+
MODEL_ID = 'llama2-13b-chat'
|
16 |
+
MODEL_VERSION_ID = '79a1af31aa8249a99602fc05687e8f40'
|
17 |
+
TEXT_FILE_URL = 'https://samples.clarifai.com/negative_sentence_12.txt'
|
18 |
+
|
19 |
+
############################################################################
|
20 |
+
# YOU DO NOT NEED TO CHANGE ANYTHING BELOW THIS LINE TO RUN THIS EXAMPLE
|
21 |
+
############################################################################
|
22 |
+
|
23 |
+
from clarifai_grpc.channel.clarifai_channel import ClarifaiChannel
|
24 |
+
from clarifai_grpc.grpc.api import resources_pb2, service_pb2, service_pb2_grpc
|
25 |
+
from clarifai_grpc.grpc.api.status import status_code_pb2
|
26 |
+
|
27 |
+
channel = ClarifaiChannel.get_grpc_channel()
|
28 |
+
stub = service_pb2_grpc.V2Stub(channel)
|
29 |
+
|
30 |
+
metadata = (
|
31 |
+
('authorization', 'Key ' + GlobalConfig.CLARIFAI_PAT),
|
32 |
+
# ('temp', '0.9'), # Does not work
|
33 |
+
)
|
34 |
+
|
35 |
+
userDataObject = resources_pb2.UserAppIDSet(
|
36 |
+
user_id=GlobalConfig.CLARIFAI_USER_ID,
|
37 |
+
app_id=GlobalConfig.CLARIFAI_APP_ID
|
38 |
+
)
|
39 |
+
|
40 |
+
RAW_TEXT = '''You are a helpful, intelligent chatbot.
|
41 |
+
Create the slides for a presentation on the given topic.
|
42 |
+
Include main headings for each slide, detailed bullet points for each slide.
|
43 |
+
Add relevant content to each slide.
|
44 |
+
The output should be complete, coherent, and have maximum 255 tokens.
|
45 |
+
|
46 |
+
Topic:
|
47 |
+
Talk about AI, covering what it is and how it works. Add its pros, cons, and future prospects. Also, cover its job prospects.
|
48 |
+
'''
|
49 |
+
|
50 |
+
post_model_outputs_response = stub.PostModelOutputs(
|
51 |
+
service_pb2.PostModelOutputsRequest(
|
52 |
+
user_app_id=userDataObject, # The userDataObject is created in the overview and is required when using a PAT
|
53 |
+
model_id=GlobalConfig.CLARIFAI_MODEL_ID,
|
54 |
+
# version_id=MODEL_VERSION_ID, # This is optional. Defaults to the latest model version
|
55 |
+
inputs=[
|
56 |
+
resources_pb2.Input(
|
57 |
+
data=resources_pb2.Data(
|
58 |
+
text=resources_pb2.Text(
|
59 |
+
# url=TEXT_FILE_URL,
|
60 |
+
raw=RAW_TEXT
|
61 |
+
)
|
62 |
+
)
|
63 |
+
)
|
64 |
+
]
|
65 |
+
),
|
66 |
+
metadata=metadata
|
67 |
+
)
|
68 |
+
if post_model_outputs_response.status.code != status_code_pb2.SUCCESS:
|
69 |
+
print(post_model_outputs_response.status)
|
70 |
+
raise Exception(f"Post model outputs failed, status: {post_model_outputs_response.status.description}")
|
71 |
+
|
72 |
+
# Since we have one input, one output will exist here
|
73 |
+
output = post_model_outputs_response.outputs[0]
|
74 |
+
|
75 |
+
print("Completion:\n")
|
76 |
+
print(output.data.text.raw)
|
examples/example_01.json
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"topic": "Create slides for a tutorial on Python, covering the basic data types, conditions, and loops.",
|
3 |
+
"audience": "People with no technology background"
|
4 |
+
}
|
examples/example_01_structured_output.json
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"title": "Introduction to Python Programming",
|
3 |
+
"slides": [
|
4 |
+
{
|
5 |
+
"heading": "Slide 1: Introduction",
|
6 |
+
"bullet_points": [
|
7 |
+
"Brief overview of Python and its importance",
|
8 |
+
"Purpose of the tutorial"
|
9 |
+
]
|
10 |
+
},
|
11 |
+
{
|
12 |
+
"heading": "Slide 2: Basic Data Types",
|
13 |
+
"bullet_points": [
|
14 |
+
"Strings (e.g. \"hello\")",
|
15 |
+
"Integers (e.g. 42)",
|
16 |
+
"Floats (e.g. 3.14)",
|
17 |
+
"Booleans (e.g. True/False)",
|
18 |
+
"Lists (e.g. [1, 2, 3])",
|
19 |
+
"Tuples (e.g. (1, 2, 3))"
|
20 |
+
]
|
21 |
+
},
|
22 |
+
{
|
23 |
+
"heading": "Slide 3: Strings",
|
24 |
+
"bullet_points": [
|
25 |
+
"String literals (e.g. \"hello\")",
|
26 |
+
"String concatenation (e.g. \"hello\" + \" world\")",
|
27 |
+
"String slicing (e.g. \"hello\"[0] = h)"
|
28 |
+
]
|
29 |
+
},
|
30 |
+
{
|
31 |
+
"heading": "Slide 4: Integers",
|
32 |
+
"bullet_points": [
|
33 |
+
"Integer literals (e.g. 42)",
|
34 |
+
"Arithmetic operations (e.g. 2 + 3 = 5)"
|
35 |
+
]
|
36 |
+
},
|
37 |
+
{
|
38 |
+
"heading": "Slide 5: Floats",
|
39 |
+
"bullet_points": [
|
40 |
+
"Floating-point literals (e.g."
|
41 |
+
]
|
42 |
+
}
|
43 |
+
]
|
44 |
+
}
|
examples/example_02.json
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"topic": "Talk about AI, covering what it is and how it works. Add its pros, cons, and future prospects. Also, cover its job prospects.",
|
3 |
+
"audience": "I am a teacher and want to present these slides to college students."
|
4 |
+
}
|
examples/example_02_structured_output.json
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"title": "Understanding AI: Introduction to Artificial Intelligence",
|
3 |
+
"slides": [
|
4 |
+
{
|
5 |
+
"heading": "Slide 1: Introduction",
|
6 |
+
"bullet_points": [
|
7 |
+
"Brief overview of AI",
|
8 |
+
"Importance of understanding AI"
|
9 |
+
]
|
10 |
+
},
|
11 |
+
{
|
12 |
+
"heading": "Slide 2: What is AI?",
|
13 |
+
"bullet_points": [
|
14 |
+
"Definition of AI",
|
15 |
+
"Types of AI",
|
16 |
+
[
|
17 |
+
"Narrow or weak AI",
|
18 |
+
"General or strong AI"
|
19 |
+
],
|
20 |
+
"Differences between AI and machine learning"
|
21 |
+
]
|
22 |
+
},
|
23 |
+
{
|
24 |
+
"heading": "Slide 3: How AI Works",
|
25 |
+
"bullet_points": [
|
26 |
+
"Overview of AI algorithms",
|
27 |
+
"Types of AI algorithms",
|
28 |
+
[
|
29 |
+
"Rule-based systems",
|
30 |
+
"Decision tree systems",
|
31 |
+
"Neural networks"
|
32 |
+
],
|
33 |
+
"How AI processes data"
|
34 |
+
]
|
35 |
+
},
|
36 |
+
{
|
37 |
+
"heading": "Slide 4: Pros of AI",
|
38 |
+
"bullet_points": [
|
39 |
+
"Increased efficiency and productivity",
|
40 |
+
"Improved accuracy and precision",
|
41 |
+
"Enhanced decision-making capabilities",
|
42 |
+
"Personalized experiences"
|
43 |
+
]
|
44 |
+
},
|
45 |
+
{
|
46 |
+
"heading": "Slide 5: Cons of AI",
|
47 |
+
"bullet_points": [
|
48 |
+
"Job displacement and loss of employment",
|
49 |
+
"Bias and discrimination",
|
50 |
+
"Privacy and security concerns",
|
51 |
+
"Dependence on technology"
|
52 |
+
]
|
53 |
+
},
|
54 |
+
{
|
55 |
+
"heading": "Slide 6: Future Prospects of AI",
|
56 |
+
"bullet_points": [
|
57 |
+
"Advancements in fields such as healthcare and finance",
|
58 |
+
"Increased use"
|
59 |
+
]
|
60 |
+
}
|
61 |
+
]
|
62 |
+
}
|
global_config.py
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from dataclasses import dataclass
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
import os
|
4 |
+
|
5 |
+
|
6 |
+
load_dotenv()
|
7 |
+
|
8 |
+
|
9 |
+
@dataclass(frozen=True)
|
10 |
+
class GlobalConfig:
|
11 |
+
CLARIFAI_PAT = os.environ.get('CLARIFAI_PAT', '')
|
12 |
+
CLARIFAI_USER_ID = 'meta'
|
13 |
+
CLARIFAI_APP_ID = 'Llama-2'
|
14 |
+
CLARIFAI_MODEL_ID = 'llama2-13b-chat'
|
15 |
+
|
16 |
+
CLARIFAI_USER_ID_GPT = 'openai'
|
17 |
+
CLARIFAI_APP_ID_GPT = 'chat-completion'
|
18 |
+
CLARIFAI_MODEL_ID_GPT = 'GPT-3_5-turbo'
|
19 |
+
|
20 |
+
# LLM_MODEL_TEMPERATURE: float = 0.5
|
21 |
+
LLM_MODEL_MIN_OUTPUT_LENGTH: int = 50
|
22 |
+
LLM_MODEL_MAX_OUTPUT_LENGTH: int = 2000
|
23 |
+
LLM_MODEL_MAX_INPUT_LENGTH: int = 1000
|
24 |
+
|
25 |
+
APP_STRINGS_FILE = 'strings.json'
|
26 |
+
PRELOAD_DATA_FILE = 'examples/example_02.json'
|
27 |
+
SLIDES_TEMPLATE_FILE = 'langchain_templates/template_07.txt'
|
28 |
+
|
langchain_templates/template_07.txt
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
You are a helpful, intelligent chatbot. Create the slides for a presentation on the given topic. Include main headings for each slide, detailed bullet points for each slide. Add relevant content to each slide. Do not output any blank line.
|
2 |
+
|
3 |
+
|
4 |
+
Topic:
|
5 |
+
{topic}
|
llm_helper.py
ADDED
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from langchain import PromptTemplate
|
2 |
+
from langchain.llms import Clarifai
|
3 |
+
|
4 |
+
from global_config import GlobalConfig
|
5 |
+
|
6 |
+
|
7 |
+
prompt = None
|
8 |
+
llm_contents = None
|
9 |
+
llm_yaml = None
|
10 |
+
|
11 |
+
|
12 |
+
def get_llm(use_gpt: bool) -> Clarifai:
|
13 |
+
"""
|
14 |
+
Get a large language model.
|
15 |
+
|
16 |
+
:param use_gpt: True if GPT-3.5 is required; False is Llama 2 is required
|
17 |
+
"""
|
18 |
+
|
19 |
+
if use_gpt:
|
20 |
+
llm = Clarifai(
|
21 |
+
pat=GlobalConfig.CLARIFAI_PAT,
|
22 |
+
user_id=GlobalConfig.CLARIFAI_USER_ID_GPT,
|
23 |
+
app_id=GlobalConfig.CLARIFAI_APP_ID_GPT,
|
24 |
+
model_id=GlobalConfig.CLARIFAI_MODEL_ID_GPT,
|
25 |
+
verbose=True,
|
26 |
+
# temperature=0.1,
|
27 |
+
)
|
28 |
+
else:
|
29 |
+
llm = Clarifai(
|
30 |
+
pat=GlobalConfig.CLARIFAI_PAT,
|
31 |
+
user_id=GlobalConfig.CLARIFAI_USER_ID,
|
32 |
+
app_id=GlobalConfig.CLARIFAI_APP_ID,
|
33 |
+
model_id=GlobalConfig.CLARIFAI_MODEL_ID,
|
34 |
+
verbose=True,
|
35 |
+
# temperature=0.1,
|
36 |
+
)
|
37 |
+
print(llm)
|
38 |
+
|
39 |
+
return llm
|
40 |
+
|
41 |
+
|
42 |
+
def generate_slides_content(topic: str) -> str:
|
43 |
+
"""
|
44 |
+
Generate the outline/contents of slides for a presentation on a given topic.
|
45 |
+
|
46 |
+
:param topic: Topic/subject matter/idea on which slides are to be generated
|
47 |
+
:return: The content
|
48 |
+
"""
|
49 |
+
|
50 |
+
global prompt
|
51 |
+
global llm_contents
|
52 |
+
|
53 |
+
if prompt is None:
|
54 |
+
with open(GlobalConfig.SLIDES_TEMPLATE_FILE, 'r') as in_file:
|
55 |
+
template_txt = in_file.read().strip()
|
56 |
+
|
57 |
+
prompt = PromptTemplate.from_template(template_txt)
|
58 |
+
|
59 |
+
formatted_prompt = prompt.format(topic=topic)
|
60 |
+
print(f'formatted_prompt:\n{formatted_prompt}')
|
61 |
+
|
62 |
+
if llm_contents is None:
|
63 |
+
llm_contents = get_llm(use_gpt=False)
|
64 |
+
|
65 |
+
slides_content = llm_contents(formatted_prompt, verbose=True)
|
66 |
+
|
67 |
+
return slides_content
|
68 |
+
|
69 |
+
|
70 |
+
def text_to_json(content: str) -> str:
|
71 |
+
"""
|
72 |
+
Convert input text into structured JSON representation.
|
73 |
+
|
74 |
+
:param content: Input text
|
75 |
+
:return: JSON string
|
76 |
+
"""
|
77 |
+
|
78 |
+
global llm_yaml
|
79 |
+
|
80 |
+
content = content.replace('```', '')
|
81 |
+
|
82 |
+
# f-string is not used in order to prevent interpreting the brackets
|
83 |
+
text = '''
|
84 |
+
Convert the given slide deck text into structured JSON output.
|
85 |
+
Also, generate and add an engaging presentation title.
|
86 |
+
The output should be only correct and valid JSON having the following structure:
|
87 |
+
|
88 |
+
{
|
89 |
+
"title": "...",
|
90 |
+
"slides": [
|
91 |
+
{
|
92 |
+
"heading": "...",
|
93 |
+
"bullet_points": [
|
94 |
+
"...",
|
95 |
+
[
|
96 |
+
"...",
|
97 |
+
"..."
|
98 |
+
]
|
99 |
+
]
|
100 |
+
},
|
101 |
+
{
|
102 |
+
...
|
103 |
+
},
|
104 |
+
]
|
105 |
+
}
|
106 |
+
|
107 |
+
|
108 |
+
Text:
|
109 |
+
'''
|
110 |
+
text += content
|
111 |
+
text += '''
|
112 |
+
|
113 |
+
|
114 |
+
Output:
|
115 |
+
```json
|
116 |
+
'''
|
117 |
+
|
118 |
+
text = text.strip()
|
119 |
+
print(text)
|
120 |
+
|
121 |
+
if llm_yaml is None:
|
122 |
+
llm_yaml = get_llm(use_gpt=True)
|
123 |
+
|
124 |
+
output = llm_yaml(text, verbose=True)
|
125 |
+
output = output.strip()
|
126 |
+
|
127 |
+
first_index = max(0, output.find('{'))
|
128 |
+
last_index = min(output.rfind('}'), len(output))
|
129 |
+
output = output[first_index: last_index + 1]
|
130 |
+
|
131 |
+
return output
|
132 |
+
|
133 |
+
|
134 |
+
def text_to_yaml(content: str) -> str:
|
135 |
+
"""
|
136 |
+
Convert input text into structured YAML representation.
|
137 |
+
|
138 |
+
:param content: Input text
|
139 |
+
:return: JSON string
|
140 |
+
"""
|
141 |
+
|
142 |
+
global llm_yaml
|
143 |
+
|
144 |
+
content = content.replace('```', '')
|
145 |
+
|
146 |
+
# f-string is not used in order to prevent interpreting the brackets
|
147 |
+
text = '''
|
148 |
+
You are a helpful AI assistant.
|
149 |
+
Convert the given slide deck text into structured YAML output.
|
150 |
+
Also, generate and add an engaging presentation title.
|
151 |
+
The output should be only correct and valid YAML having the following structure:
|
152 |
+
|
153 |
+
title: "..."
|
154 |
+
slides:
|
155 |
+
- heading: "..."
|
156 |
+
bullet_points:
|
157 |
+
- "..."
|
158 |
+
- "..."
|
159 |
+
- heading: "..."
|
160 |
+
bullet_points:
|
161 |
+
- "..."
|
162 |
+
- "...": # This line ends with a colon because it has a sub-block
|
163 |
+
- "..."
|
164 |
+
- "..."
|
165 |
+
|
166 |
+
|
167 |
+
Text:
|
168 |
+
'''
|
169 |
+
text += content
|
170 |
+
text += '''
|
171 |
+
|
172 |
+
|
173 |
+
|
174 |
+
|
175 |
+
Output:
|
176 |
+
```yaml
|
177 |
+
'''
|
178 |
+
|
179 |
+
text = text.strip()
|
180 |
+
print(text)
|
181 |
+
|
182 |
+
if llm_yaml is None:
|
183 |
+
llm_yaml = get_llm(use_gpt=True)
|
184 |
+
|
185 |
+
output = llm_yaml(text, verbose=True)
|
186 |
+
output = output.strip()
|
187 |
+
|
188 |
+
# first_index = max(0, output.find('{'))
|
189 |
+
# last_index = min(output.rfind('}'), len(output))
|
190 |
+
# output = output[first_index: last_index + 1]
|
191 |
+
|
192 |
+
return output
|
193 |
+
|
194 |
+
|
195 |
+
if __name__ == '__main__':
|
196 |
+
pass
|
197 |
+
|
pptx_helper.py
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json5
|
2 |
+
import pptx
|
3 |
+
import re
|
4 |
+
import yaml
|
5 |
+
|
6 |
+
from pptx.dml.color import RGBColor
|
7 |
+
|
8 |
+
PATTERN = re.compile(r"^slide[ ]+\d+:", re.IGNORECASE)
|
9 |
+
|
10 |
+
|
11 |
+
def remove_slide_number_from_heading(header: str) -> str:
|
12 |
+
if PATTERN.match(header):
|
13 |
+
idx = header.find(':')
|
14 |
+
header = header[idx + 1:]
|
15 |
+
|
16 |
+
return header
|
17 |
+
|
18 |
+
|
19 |
+
def generate_powerpoint_presentation(structured_data: str, as_yaml: bool, output_file_name: str):
|
20 |
+
"""
|
21 |
+
Create and save a PowerPoint presentation file containing the contents in JSON or YAML format.
|
22 |
+
|
23 |
+
:param structured_data: The presentation contents as "JSON" (may contain trailing commas) or YAML
|
24 |
+
:param as_yaml: True if the input data is in YAML format; False if it is in JSON format
|
25 |
+
:param output_file_name: The name of the PPTX file to save as
|
26 |
+
"""
|
27 |
+
|
28 |
+
if as_yaml:
|
29 |
+
# Avoid YAML mode: nested bullets can lead to incorrect YAML generation
|
30 |
+
try:
|
31 |
+
parsed_data = yaml.safe_load(structured_data)
|
32 |
+
except yaml.parser.ParserError as ype:
|
33 |
+
print(f'*** YAML parse error: {ype}')
|
34 |
+
parsed_data = {'title': '', 'slides': []}
|
35 |
+
else:
|
36 |
+
# The structured "JSON" might contain trailing commas, so using json5
|
37 |
+
parsed_data = json5.loads(structured_data)
|
38 |
+
|
39 |
+
presentation = pptx.Presentation()
|
40 |
+
|
41 |
+
# The title slide
|
42 |
+
title_slide_layout = presentation.slide_layouts[0]
|
43 |
+
slide = presentation.slides.add_slide(title_slide_layout)
|
44 |
+
title = slide.shapes.title
|
45 |
+
subtitle = slide.placeholders[1]
|
46 |
+
title.text = parsed_data['title']
|
47 |
+
print(f'Title is: {title.text}')
|
48 |
+
subtitle.text = 'by Myself and SlideDeck AI :)'
|
49 |
+
|
50 |
+
background = slide.background
|
51 |
+
background.fill.solid()
|
52 |
+
background.fill.fore_color.rgb = RGBColor.from_string('C0C0C0') # Silver
|
53 |
+
title.text_frame.paragraphs[0].font.color.rgb = RGBColor(0, 0, 128) # Navy blue
|
54 |
+
|
55 |
+
# Add contents in a loop
|
56 |
+
for a_slide in parsed_data['slides']:
|
57 |
+
bullet_slide_layout = presentation.slide_layouts[1]
|
58 |
+
slide = presentation.slides.add_slide(bullet_slide_layout)
|
59 |
+
shapes = slide.shapes
|
60 |
+
|
61 |
+
title_shape = shapes.title
|
62 |
+
body_shape = shapes.placeholders[1]
|
63 |
+
title_shape.text = remove_slide_number_from_heading(a_slide['heading'])
|
64 |
+
text_frame = body_shape.text_frame
|
65 |
+
|
66 |
+
for an_item in a_slide['bullet_points']:
|
67 |
+
item_type = type(an_item)
|
68 |
+
# print('Bullet point type:', item_type)
|
69 |
+
|
70 |
+
if item_type is str:
|
71 |
+
paragraph = text_frame.add_paragraph()
|
72 |
+
paragraph.text = an_item
|
73 |
+
paragraph.level = 0
|
74 |
+
elif item_type is list:
|
75 |
+
for sub_item in an_item:
|
76 |
+
if type(sub_item) is str:
|
77 |
+
paragraph = text_frame.add_paragraph()
|
78 |
+
paragraph.text = sub_item
|
79 |
+
paragraph.level = 1
|
80 |
+
|
81 |
+
background = slide.background
|
82 |
+
background.fill.gradient()
|
83 |
+
background.fill.gradient_angle = -225.0
|
84 |
+
|
85 |
+
# The thank-you slide
|
86 |
+
last_slide_layout = presentation.slide_layouts[0]
|
87 |
+
slide = presentation.slides.add_slide(last_slide_layout)
|
88 |
+
title = slide.shapes.title
|
89 |
+
title.text = 'Thank you!'
|
90 |
+
|
91 |
+
presentation.save(output_file_name)
|
92 |
+
|
93 |
+
|
94 |
+
if __name__ == '__main__':
|
95 |
+
generate_powerpoint_presentation(
|
96 |
+
json5.loads(open('examples/example_02_structured_output.json', 'r').read()),
|
97 |
+
as_yaml=False,
|
98 |
+
output_file_name='test.pptx'
|
99 |
+
)
|
requirements.txt
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
python-dotenv[cli]~=1.0.0
|
2 |
+
langchain~=0.0.273
|
3 |
+
# huggingface_hub
|
4 |
+
streamlit~=1.26.0
|
5 |
+
clarifai==9.7.4
|
6 |
+
|
7 |
+
python-pptx
|
8 |
+
json5~=0.9.14
|
9 |
+
PyYAML~=6.0.1
|
strings.json
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"app_name": "SlideDeck AI",
|
3 |
+
"caption": "*:green[Co-create your next PowerPoint slide deck with AI]*",
|
4 |
+
"section_headers": [
|
5 |
+
"Step 1: Generate your content",
|
6 |
+
"Step 2: Make it structured",
|
7 |
+
"Step 3: Create the slides",
|
8 |
+
],
|
9 |
+
"section_captions": [
|
10 |
+
"Let\'s start by generating some contents for your slides",
|
11 |
+
"Let\'s now convert the above generated contents into JSON",
|
12 |
+
"Let\'s now create the slides for you",
|
13 |
+
],
|
14 |
+
"input_labels": [
|
15 |
+
"**Describe the topic of the presentation. Avoid mentioning the count of slides.**\n*Note: the output may be short or truncated due to API limitations.*",
|
16 |
+
],
|
17 |
+
"button_labels": [
|
18 |
+
"Generate slides content",
|
19 |
+
"Generate JSON",
|
20 |
+
"Make the slides"
|
21 |
+
],
|
22 |
+
"content_generation_failure_error": "Unfortunately, SlideDeck AI failed to generate any content for you! Please try again later."
|
23 |
+
}
|