barunsaha commited on
Commit
3e68ccf
1 Parent(s): f8e9610

Add code for the first version

Browse files
.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
+ }