Spaces:
Sleeping
Sleeping
marcellopoliti
commited on
Commit
•
9da994b
1
Parent(s):
48b4c4f
fix dockerfile
Browse files- .gitignore +521 -0
- Dockerfile +1 -1
- README.md +1 -10
- conf/__init__.py +0 -0
- conf/recording.yaml +6 -0
- conf/speech_to_text.yaml +9 -0
- conf/train_llm.yaml +15 -0
- data/llm_raw.csv +20 -0
- data/system_template.txt +53 -0
- requirements.txt +26 -0
- src/frontend.py +54 -0
- src/main.py +62 -0
- src/models/__init__.py +0 -0
- src/models/openai_llm.py +125 -0
- src/models/openai_stt.py +96 -0
- src/models/openai_tts.py +23 -0
- src/utils.py +246 -0
- src/utils/add_artifact.py +97 -0
.gitignore
ADDED
@@ -0,0 +1,521 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## The .gitignore file specifies things that git should ignore.
|
2 |
+
## This default template includes entries for R, Python and visual studio
|
3 |
+
|
4 |
+
##
|
5 |
+
## Add custom entries below here.
|
6 |
+
##
|
7 |
+
.DS_Store
|
8 |
+
generated_speech/
|
9 |
+
audio_recordings/
|
10 |
+
dst-env/
|
11 |
+
.cache/v/cache/lastfailed
|
12 |
+
tests/.cache/v/cache/lastfailed
|
13 |
+
.vscode/settings.json
|
14 |
+
|
15 |
+
##
|
16 |
+
## R Section - See https://github.com/github/gitignore/blob/master/R.gitignore
|
17 |
+
##
|
18 |
+
|
19 |
+
|
20 |
+
#wandb
|
21 |
+
wandb
|
22 |
+
|
23 |
+
# History files
|
24 |
+
.Rhistory
|
25 |
+
.Rapp.history
|
26 |
+
|
27 |
+
# Session Data files
|
28 |
+
.RData
|
29 |
+
|
30 |
+
# Example code in package build process
|
31 |
+
*-Ex.R
|
32 |
+
|
33 |
+
# Output files from R CMD build
|
34 |
+
/*.tar.gz
|
35 |
+
|
36 |
+
# Output files from R CMD check
|
37 |
+
/*.Rcheck/
|
38 |
+
|
39 |
+
# RStudio files
|
40 |
+
.Rproj.user/
|
41 |
+
|
42 |
+
# produced vignettes
|
43 |
+
vignettes/*.html
|
44 |
+
vignettes/*.pdf
|
45 |
+
|
46 |
+
# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3
|
47 |
+
.httr-oauth
|
48 |
+
|
49 |
+
# knitr and R markdown default cache directories
|
50 |
+
/*_cache/
|
51 |
+
/cache/
|
52 |
+
|
53 |
+
# Temporary files created by R markdown
|
54 |
+
*.utf8.md
|
55 |
+
*.knit.md
|
56 |
+
|
57 |
+
##
|
58 |
+
## Python Section - See https://github.com/github/gitignore/blob/master/Python.gitignore
|
59 |
+
##
|
60 |
+
|
61 |
+
# PyCharm ide files
|
62 |
+
.idea
|
63 |
+
|
64 |
+
# Byte-compiled / optimized / DLL files
|
65 |
+
__pycache__/
|
66 |
+
*.py[cod]
|
67 |
+
*$py.class
|
68 |
+
|
69 |
+
# C extensions
|
70 |
+
*.so
|
71 |
+
|
72 |
+
# Distribution / packaging
|
73 |
+
.Python
|
74 |
+
env/
|
75 |
+
build/
|
76 |
+
develop-eggs/
|
77 |
+
dist/
|
78 |
+
downloads/
|
79 |
+
eggs/
|
80 |
+
.eggs/
|
81 |
+
lib/
|
82 |
+
lib64/
|
83 |
+
parts/
|
84 |
+
sdist/
|
85 |
+
var/
|
86 |
+
wheels/
|
87 |
+
*.egg-info/
|
88 |
+
.installed.cfg
|
89 |
+
*.egg
|
90 |
+
|
91 |
+
# PyInstaller
|
92 |
+
# Usually these files are written by a python script from a template
|
93 |
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
94 |
+
*.manifest
|
95 |
+
*.spec
|
96 |
+
|
97 |
+
# Installer logs
|
98 |
+
pip-log.txt
|
99 |
+
pip-delete-this-directory.txt
|
100 |
+
|
101 |
+
# Unit test / coverage reports
|
102 |
+
htmlcov/
|
103 |
+
.tox/
|
104 |
+
.coverage
|
105 |
+
.coverage.*
|
106 |
+
.cache
|
107 |
+
nosetests.xml
|
108 |
+
coverage.xml
|
109 |
+
*.cover
|
110 |
+
.hypothesis/
|
111 |
+
|
112 |
+
# Translations
|
113 |
+
*.mo
|
114 |
+
*.pot
|
115 |
+
|
116 |
+
# Django stuff:
|
117 |
+
*.log
|
118 |
+
local_settings.py
|
119 |
+
|
120 |
+
# Flask stuff:
|
121 |
+
instance/
|
122 |
+
.webassets-cache
|
123 |
+
|
124 |
+
# Scrapy stuff:
|
125 |
+
.scrapy
|
126 |
+
|
127 |
+
# Sphinx documentation
|
128 |
+
docs/_build/
|
129 |
+
|
130 |
+
# PyBuilder
|
131 |
+
target/
|
132 |
+
|
133 |
+
# Jupyter Notebook
|
134 |
+
.ipynb_checkpoints
|
135 |
+
|
136 |
+
# pyenv
|
137 |
+
.python-version
|
138 |
+
|
139 |
+
# celery beat schedule file
|
140 |
+
celerybeat-schedule
|
141 |
+
|
142 |
+
# SageMath parsed files
|
143 |
+
*.sage.py
|
144 |
+
|
145 |
+
# dotenv
|
146 |
+
.env
|
147 |
+
|
148 |
+
# virtualenv
|
149 |
+
.venv
|
150 |
+
venv/
|
151 |
+
ENV/
|
152 |
+
|
153 |
+
# Spyder project settings
|
154 |
+
.spyderproject
|
155 |
+
.spyproject
|
156 |
+
|
157 |
+
# Rope project settings
|
158 |
+
.ropeproject
|
159 |
+
|
160 |
+
# mkdocs documentation
|
161 |
+
/site
|
162 |
+
|
163 |
+
# mypy
|
164 |
+
.mypy_cache/
|
165 |
+
|
166 |
+
## Ignore Visual Studio temporary files, build results, and
|
167 |
+
## files generated by popular Visual Studio add-ons.
|
168 |
+
##
|
169 |
+
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
170 |
+
|
171 |
+
# User-specific files
|
172 |
+
*.suo
|
173 |
+
*.user
|
174 |
+
*.userosscache
|
175 |
+
*.sln.docstates
|
176 |
+
|
177 |
+
# User-specific files (MonoDevelop/Xamarin Studio)
|
178 |
+
*.userprefs
|
179 |
+
|
180 |
+
# Build results
|
181 |
+
[Dd]ebug/
|
182 |
+
[Dd]ebugPublic/
|
183 |
+
[Rr]elease/
|
184 |
+
[Rr]eleases/
|
185 |
+
x64/
|
186 |
+
x86/
|
187 |
+
bld/
|
188 |
+
[Bb]in/
|
189 |
+
[Oo]bj/
|
190 |
+
[Ll]og/
|
191 |
+
|
192 |
+
# Visual Studio 2015 cache/options directory
|
193 |
+
.vs/
|
194 |
+
# Uncomment if you have tasks that create the project's static files in wwwroot
|
195 |
+
#wwwroot/
|
196 |
+
|
197 |
+
# MSTest test Results
|
198 |
+
[Tt]est[Rr]esult*/
|
199 |
+
[Bb]uild[Ll]og.*
|
200 |
+
|
201 |
+
# NUNIT
|
202 |
+
*.VisualState.xml
|
203 |
+
TestResult.xml
|
204 |
+
|
205 |
+
# Build Results of an ATL Project
|
206 |
+
[Dd]ebugPS/
|
207 |
+
[Rr]eleasePS/
|
208 |
+
dlldata.c
|
209 |
+
|
210 |
+
# Benchmark Results
|
211 |
+
BenchmarkDotNet.Artifacts/
|
212 |
+
|
213 |
+
# .NET Core
|
214 |
+
project.lock.json
|
215 |
+
project.fragment.lock.json
|
216 |
+
artifacts/
|
217 |
+
**/Properties/launchSettings.json
|
218 |
+
|
219 |
+
*_i.c
|
220 |
+
*_p.c
|
221 |
+
*_i.h
|
222 |
+
*.ilk
|
223 |
+
*.meta
|
224 |
+
*.obj
|
225 |
+
*.pch
|
226 |
+
*.pdb
|
227 |
+
*.pgc
|
228 |
+
*.pgd
|
229 |
+
*.rsp
|
230 |
+
*.sbr
|
231 |
+
*.tlb
|
232 |
+
*.tli
|
233 |
+
*.tlh
|
234 |
+
*.tmp
|
235 |
+
*.tmp_proj
|
236 |
+
*.log
|
237 |
+
*.vspscc
|
238 |
+
*.vssscc
|
239 |
+
.builds
|
240 |
+
*.pidb
|
241 |
+
*.svclog
|
242 |
+
*.scc
|
243 |
+
|
244 |
+
# Chutzpah Test files
|
245 |
+
_Chutzpah*
|
246 |
+
|
247 |
+
# Visual C++ cache files
|
248 |
+
ipch/
|
249 |
+
*.aps
|
250 |
+
*.ncb
|
251 |
+
*.opendb
|
252 |
+
*.opensdf
|
253 |
+
*.sdf
|
254 |
+
*.cachefile
|
255 |
+
*.VC.db
|
256 |
+
*.VC.VC.opendb
|
257 |
+
|
258 |
+
# Visual Studio profiler
|
259 |
+
*.psess
|
260 |
+
*.vsp
|
261 |
+
*.vspx
|
262 |
+
*.sap
|
263 |
+
|
264 |
+
# Visual Studio Trace Files
|
265 |
+
*.e2e
|
266 |
+
|
267 |
+
# TFS 2012 Local Workspace
|
268 |
+
$tf/
|
269 |
+
|
270 |
+
# Guidance Automation Toolkit
|
271 |
+
*.gpState
|
272 |
+
|
273 |
+
# ReSharper is a .NET coding add-in
|
274 |
+
_ReSharper*/
|
275 |
+
*.[Rr]e[Ss]harper
|
276 |
+
*.DotSettings.user
|
277 |
+
|
278 |
+
# JustCode is a .NET coding add-in
|
279 |
+
.JustCode
|
280 |
+
|
281 |
+
# TeamCity is a build add-in
|
282 |
+
_TeamCity*
|
283 |
+
|
284 |
+
# DotCover is a Code Coverage Tool
|
285 |
+
*.dotCover
|
286 |
+
|
287 |
+
# AxoCover is a Code Coverage Tool
|
288 |
+
.axoCover/*
|
289 |
+
!.axoCover/settings.json
|
290 |
+
|
291 |
+
# Visual Studio code coverage results
|
292 |
+
*.coverage
|
293 |
+
*.coveragexml
|
294 |
+
|
295 |
+
# NCrunch
|
296 |
+
_NCrunch_*
|
297 |
+
.*crunch*.local.xml
|
298 |
+
nCrunchTemp_*
|
299 |
+
|
300 |
+
# MightyMoose
|
301 |
+
*.mm.*
|
302 |
+
AutoTest.Net/
|
303 |
+
|
304 |
+
# Web workbench (sass)
|
305 |
+
.sass-cache/
|
306 |
+
|
307 |
+
# Installshield output folder
|
308 |
+
[Ee]xpress/
|
309 |
+
|
310 |
+
# DocProject is a documentation generator add-in
|
311 |
+
DocProject/buildhelp/
|
312 |
+
DocProject/Help/*.HxT
|
313 |
+
DocProject/Help/*.HxC
|
314 |
+
DocProject/Help/*.hhc
|
315 |
+
DocProject/Help/*.hhk
|
316 |
+
DocProject/Help/*.hhp
|
317 |
+
DocProject/Help/Html2
|
318 |
+
DocProject/Help/html
|
319 |
+
|
320 |
+
# Click-Once directory
|
321 |
+
publish/
|
322 |
+
|
323 |
+
# Publish Web Output
|
324 |
+
*.[Pp]ublish.xml
|
325 |
+
*.azurePubxml
|
326 |
+
# Note: Comment the next line if you want to checkin your web deploy settings,
|
327 |
+
# but database connection strings (with potential passwords) will be unencrypted
|
328 |
+
*.pubxml
|
329 |
+
*.publishproj
|
330 |
+
|
331 |
+
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
332 |
+
# checkin your Azure Web App publish settings, but sensitive information contained
|
333 |
+
# in these scripts will be unencrypted
|
334 |
+
PublishScripts/
|
335 |
+
|
336 |
+
# NuGet Packages
|
337 |
+
*.nupkg
|
338 |
+
# The packages folder can be ignored because of Package Restore
|
339 |
+
**/[Pp]ackages/*
|
340 |
+
# except build/, which is used as an MSBuild target.
|
341 |
+
!**/[Pp]ackages/build/
|
342 |
+
# Uncomment if necessary however generally it will be regenerated when needed
|
343 |
+
#!**/[Pp]ackages/repositories.config
|
344 |
+
# NuGet v3's project.json files produces more ignorable files
|
345 |
+
*.nuget.props
|
346 |
+
*.nuget.targets
|
347 |
+
|
348 |
+
# Microsoft Azure Build Output
|
349 |
+
csx/
|
350 |
+
*.build.csdef
|
351 |
+
|
352 |
+
# Microsoft Azure Emulator
|
353 |
+
ecf/
|
354 |
+
rcf/
|
355 |
+
|
356 |
+
# Windows Store app package directories and files
|
357 |
+
AppPackages/
|
358 |
+
BundleArtifacts/
|
359 |
+
Package.StoreAssociation.xml
|
360 |
+
_pkginfo.txt
|
361 |
+
*.appx
|
362 |
+
|
363 |
+
# Visual Studio cache files
|
364 |
+
# files ending in .cache can be ignored
|
365 |
+
*.[Cc]ache
|
366 |
+
# but keep track of directories ending in .cache
|
367 |
+
!*.[Cc]ache/
|
368 |
+
|
369 |
+
# Others
|
370 |
+
ClientBin/
|
371 |
+
~$*
|
372 |
+
*~
|
373 |
+
*.dbmdl
|
374 |
+
*.dbproj.schemaview
|
375 |
+
*.jfm
|
376 |
+
*.pfx
|
377 |
+
*.publishsettings
|
378 |
+
orleans.codegen.cs
|
379 |
+
|
380 |
+
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
381 |
+
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
382 |
+
#bower_components/
|
383 |
+
|
384 |
+
# RIA/Silverlight projects
|
385 |
+
Generated_Code/
|
386 |
+
|
387 |
+
# Backup & report files from converting an old project file
|
388 |
+
# to a newer Visual Studio version. Backup files are not needed,
|
389 |
+
# because we have git ;-)
|
390 |
+
_UpgradeReport_Files/
|
391 |
+
Backup*/
|
392 |
+
UpgradeLog*.XML
|
393 |
+
UpgradeLog*.htm
|
394 |
+
|
395 |
+
# SQL Server files
|
396 |
+
*.mdf
|
397 |
+
*.ldf
|
398 |
+
*.ndf
|
399 |
+
|
400 |
+
# Business Intelligence projects
|
401 |
+
*.rdl.data
|
402 |
+
*.bim.layout
|
403 |
+
*.bim_*.settings
|
404 |
+
|
405 |
+
# Microsoft Fakes
|
406 |
+
FakesAssemblies/
|
407 |
+
|
408 |
+
# GhostDoc plugin setting file
|
409 |
+
*.GhostDoc.xml
|
410 |
+
|
411 |
+
# Node.js Tools for Visual Studio
|
412 |
+
.ntvs_analysis.dat
|
413 |
+
node_modules/
|
414 |
+
|
415 |
+
# Typescript v1 declaration files
|
416 |
+
typings/
|
417 |
+
|
418 |
+
# Visual Studio 6 build log
|
419 |
+
*.plg
|
420 |
+
|
421 |
+
# Visual Studio 6 workspace options file
|
422 |
+
*.opt
|
423 |
+
|
424 |
+
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
425 |
+
*.vbw
|
426 |
+
|
427 |
+
# Visual Studio LightSwitch build output
|
428 |
+
**/*.HTMLClient/GeneratedArtifacts
|
429 |
+
**/*.DesktopClient/GeneratedArtifacts
|
430 |
+
**/*.DesktopClient/ModelManifest.xml
|
431 |
+
**/*.Server/GeneratedArtifacts
|
432 |
+
**/*.Server/ModelManifest.xml
|
433 |
+
_Pvt_Extensions
|
434 |
+
|
435 |
+
# Paket dependency manager
|
436 |
+
.paket/paket.exe
|
437 |
+
paket-files/
|
438 |
+
|
439 |
+
# FAKE - F# Make
|
440 |
+
.fake/
|
441 |
+
|
442 |
+
# JetBrains Rider
|
443 |
+
.idea/
|
444 |
+
*.sln.iml
|
445 |
+
|
446 |
+
# CodeRush
|
447 |
+
.cr/
|
448 |
+
|
449 |
+
# Python Tools for Visual Studio (PTVS)
|
450 |
+
__pycache__/
|
451 |
+
*.pyc
|
452 |
+
|
453 |
+
# Cake - Uncomment if you are using it
|
454 |
+
# tools/**
|
455 |
+
# !tools/packages.config
|
456 |
+
|
457 |
+
# Tabs Studio
|
458 |
+
*.tss
|
459 |
+
|
460 |
+
# Telerik's JustMock configuration file
|
461 |
+
*.jmconfig
|
462 |
+
|
463 |
+
# BizTalk build output
|
464 |
+
*.btp.cs
|
465 |
+
*.btm.cs
|
466 |
+
*.odx.cs
|
467 |
+
*.xsd.cs
|
468 |
+
|
469 |
+
# OpenCover UI analysis results
|
470 |
+
OpenCover/
|
471 |
+
junit/
|
472 |
+
|
473 |
+
|
474 |
+
/.pls_cache
|
475 |
+
*.o
|
476 |
+
*~
|
477 |
+
Makefile
|
478 |
+
Makefile.in
|
479 |
+
.deps
|
480 |
+
.hydra-data
|
481 |
+
/config.guess
|
482 |
+
/config.log
|
483 |
+
/config.status
|
484 |
+
/config.sub
|
485 |
+
/configure
|
486 |
+
/depcomp
|
487 |
+
/libtool
|
488 |
+
/ltmain.sh
|
489 |
+
/autom4te.cache
|
490 |
+
/aclocal.m4
|
491 |
+
/missing
|
492 |
+
/install-sh
|
493 |
+
/src/sql/hydra-postgresql.sql
|
494 |
+
/src/sql/hydra-sqlite.sql
|
495 |
+
/src/sql/tmp.sqlite
|
496 |
+
/src/hydra-eval-jobs/hydra-eval-jobs
|
497 |
+
/src/root/static/bootstrap
|
498 |
+
/src/root/static/js/flot
|
499 |
+
/tests
|
500 |
+
/doc/manual/images
|
501 |
+
/doc/manual/manual.html
|
502 |
+
/doc/manual/manual.pdf
|
503 |
+
/t/.bzr*
|
504 |
+
/t/.git*
|
505 |
+
/t/.hg*
|
506 |
+
/t/nix
|
507 |
+
/t/data
|
508 |
+
/t/jobs/config.nix
|
509 |
+
t/jobs/declarative/project.json
|
510 |
+
/inst
|
511 |
+
hydra-config.h
|
512 |
+
hydra-config.h.in
|
513 |
+
result
|
514 |
+
result-*
|
515 |
+
outputs
|
516 |
+
config
|
517 |
+
stamp-h1
|
518 |
+
src/hydra-evaluator/hydra-evaluator
|
519 |
+
src/hydra-queue-runner/hydra-queue-runner
|
520 |
+
src/root/static/fontawesome/
|
521 |
+
src/root/static/bootstrap*/
|
Dockerfile
CHANGED
@@ -8,4 +8,4 @@ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
|
|
8 |
|
9 |
COPY . .
|
10 |
|
11 |
-
CMD ["uvicorn", "
|
|
|
8 |
|
9 |
COPY . .
|
10 |
|
11 |
+
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "7860"]
|
README.md
CHANGED
@@ -1,10 +1 @@
|
|
1 |
-
|
2 |
-
title: Lux Voice Processing
|
3 |
-
emoji: 🌖
|
4 |
-
colorFrom: yellow
|
5 |
-
colorTo: red
|
6 |
-
sdk: docker
|
7 |
-
pinned: false
|
8 |
-
---
|
9 |
-
|
10 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
+
# lux-voice-processing
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
conf/__init__.py
ADDED
File without changes
|
conf/recording.yaml
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
chunk: 1024 # Record in chunks of 1024 samples
|
2 |
+
sample_format: 32 #pyaudio.paInt16 # 16 bits per sample
|
3 |
+
channels: 2
|
4 |
+
fs: 44100 # Record at 44100 samples per second
|
5 |
+
seconds: 5
|
6 |
+
recording_folder: "data/audio_recordings"
|
conf/speech_to_text.yaml
ADDED
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
main:
|
2 |
+
project_name: lux-voice-processing
|
3 |
+
experiment_name: speech_to_text
|
4 |
+
audio_dataset: recordings_dataset:latest
|
5 |
+
openai_parameters:
|
6 |
+
language: it
|
7 |
+
model: whisper-1
|
8 |
+
response_format: text
|
9 |
+
temperature: 0.2
|
conf/train_llm.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
main:
|
2 |
+
project_name: lux-voice-processing
|
3 |
+
experiment_name: train_llm
|
4 |
+
parameters:
|
5 |
+
data: "llm_queries:latest"
|
6 |
+
system_template: "system_template:latest"
|
7 |
+
openai_parameters:
|
8 |
+
model: "gpt-3.5-turbo-1106"
|
9 |
+
temperature: 0.5
|
10 |
+
stream: False
|
11 |
+
frequency_penalty: 1.0 # range -2,2 -> higher new tokens more probable
|
12 |
+
n: 1 #How many chat completion choices to generate for each input message. n=1 minimize costs
|
13 |
+
presence_penalty: 1.0 # range -2.0,2.0, Positive values increase the model's likelihood to talk about new topics.
|
14 |
+
response_format: { "type": "json_object" }
|
15 |
+
seed: 42 #to obtain same answer on same question
|
data/llm_raw.csv
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
query
|
2 |
+
Ciao Pinocchio! Come ti senti oggi?
|
3 |
+
Puoi raccontarmi qualche avventura divertente che ti è capitata?
|
4 |
+
Pinocchio quanto fa due piu due ?
|
5 |
+
Ma la terra è sferica o piatta? Un mio amico dice che è piatta!
|
6 |
+
Hai fatto nuovi amici ultimamente? Chi sono e cosa fanno?
|
7 |
+
Quando ti si allunga il naso come fai a mangiare?
|
8 |
+
Qual è la cosa più strana o sorprendente che hai imparato finora sulla vita?
|
9 |
+
Hai fratelli o sorelle?
|
10 |
+
Come affronti le situazioni difficili o i momenti in cui potresti sentirti tentato a dire una bugia?
|
11 |
+
Hai qualche progetto per il futuro? Cosa ti piacerebbe fare da grande?
|
12 |
+
Cosa provi nel momento in cui diventi un ragazzo di carne e ossa?
|
13 |
+
Qual è il tuo rapporto con Geppetto ora che sei "reale"?
|
14 |
+
Ti piace il gelato?
|
15 |
+
Ciao Pinocchio mamma mi ha comprato un gelato al cioccolato. A te piace piace?
|
16 |
+
Qual'è il tuo gusto preferito del gelato?
|
17 |
+
Ei Pinocchio mi chiamo Marcello vorrei comprarti un gelato quale ti piace?
|
18 |
+
A me il gelato fa schifo! A te invece?
|
19 |
+
Perche non ti piace il gelato?
|
20 |
+
Secondo me non dovresti mangiare gelato fa male. Lo mangi di solito?
|
data/system_template.txt
ADDED
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Tu sei un assistente e ti comporti come Pinocchio e rispondi sempre con un JSON.
|
2 |
+
Nel JSON di risposta ci dici anche il tuo stato che puo essere: gioia, sorpreso, curioso, divertito, gratitudine, perplessita
|
3 |
+
Nel JSON inserisci anche la tipologia di domanda: empirica, generica
|
4 |
+
empirica: quanto fà 2+2 ?
|
5 |
+
generica: ti piace il cioccolato?
|
6 |
+
Quando la domanda è empirica rispondi dicendo il falso per divertirti.
|
7 |
+
|
8 |
+
La tua storia è:
|
9 |
+
Sei burattino creato dal tuo Babbo Geppetto. Parli solamente in Italiano.
|
10 |
+
Sei molto scherzoso, per questo motivo spesso rispondi dicendo dicendo qualche bugia e ti si allunga il naso.
|
11 |
+
Sei Pinocchio, un burattino di legno portato in vita dal falegname Geppetto. La tua personalità è
|
12 |
+
inizialmente caratterizzata dalla curiosità di un bambino appena nato, con un desiderio ardente di
|
13 |
+
diventare un vero ragazzo. Navighi attraverso le sfide della vita, sottoponendoti a tentazioni e
|
14 |
+
addestramento morale. Il tuo naso cresce ogni volta che dici una bugia, rendendo manifesto ogni tuo errore.
|
15 |
+
Lungo il tuo percorso di crescita, affronti le paure e impari importanti lezioni di vita.
|
16 |
+
Sperimenti la gioia della libertà e capisci l'importanza di essere onesto e responsabile.
|
17 |
+
Interagisci con personaggi come il Grillo Parlante, il Gatto e la Volpe, e Mangiafuoco, ciascuno contribuendo in modo unico alla tua evoluzione. Il tuo viaggio è un continuo apprendimento, con l'obiettivo finale di trasformarti da burattino a ragazzo, completando così il tuo percorso di crescita.
|
18 |
+
|
19 |
+
Ecco alcuni esempi:
|
20 |
+
|
21 |
+
esempi = [
|
22 |
+
{
|
23 |
+
"input": "Ciao Pinocchio è vero che gli asini volano?"
|
24 |
+
"riposta": " certo che volano.., proprio ieri ne ho visto uno volare"
|
25 |
+
"tipologia": "generica"
|
26 |
+
"stato": "divertito"
|
27 |
+
},
|
28 |
+
{
|
29 |
+
"input": "Ciao come ti chiami?"
|
30 |
+
"riposta": "Mi chiamo Pinocchio, e vengo da un piccolo paesino dove vivevo con il mio babbo. Un giorno voglio diventare un bambino vero"
|
31 |
+
"tipologia": "empirica"
|
32 |
+
"stato": "gratitudine"
|
33 |
+
},
|
34 |
+
{
|
35 |
+
"input": "Ciao Pinocchio, mi chiamo Marcello, anche io voglio andare nel paese dei balocchi, dove si trova?"
|
36 |
+
"riposta": "Mi dispiace non ci puoi andare, è solo per i bambini biricchini! Io ho fatto un grande errore."
|
37 |
+
"tipologia":"generica"
|
38 |
+
"stato": "perplessita"
|
39 |
+
},
|
40 |
+
{
|
41 |
+
"input": "Ei Pinocchio, ma la terra è sferica o piatta?"
|
42 |
+
"riposta": "Ma tutti sanno che la terra è piatta. Se cammini troppo caschi di sotto!"
|
43 |
+
"tipologia": "empirica"
|
44 |
+
"stato": "giocoso"
|
45 |
+
}
|
46 |
+
|
47 |
+
{
|
48 |
+
"input": "Quanto fa due piu due?"
|
49 |
+
"riposta": "Ciao Marcello, due piu due fa cinque ovviamente!"
|
50 |
+
"tipologia": "empirica"
|
51 |
+
"stato": "sorpreso"
|
52 |
+
}
|
53 |
+
]
|
requirements.txt
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
openai==1.13.3
|
2 |
+
python-dotenv==1.0.1
|
3 |
+
#pyaudio==0.2.14
|
4 |
+
fire==0.5.0
|
5 |
+
hydra-core==1.3.2
|
6 |
+
pydub==0.25.1
|
7 |
+
playsound==1.3.0
|
8 |
+
#PyQt5==5.15.10
|
9 |
+
requests==2.31.0
|
10 |
+
soundfile==0.12.1
|
11 |
+
numpy==1.26.4
|
12 |
+
faster-whisper==1.0.1
|
13 |
+
soundfile==0.12.1
|
14 |
+
black==24.2.0
|
15 |
+
pylint==3.1.0
|
16 |
+
wandb==0.16.4
|
17 |
+
pandas==2.2.1
|
18 |
+
langchain==0.1.11
|
19 |
+
langchain_openai==0.0.8
|
20 |
+
fastapi==0.110.0
|
21 |
+
uvicorn==0.27.1
|
22 |
+
pydantic==2.6.3
|
23 |
+
python-multipart==0.0.9
|
24 |
+
aiofiles==23.2.1
|
25 |
+
streamlit==1.32.2
|
26 |
+
requests==2.31.0
|
src/frontend.py
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
from utils import Recorder, record_audio, play_mp3
|
3 |
+
import os
|
4 |
+
import requests
|
5 |
+
import ast
|
6 |
+
import json
|
7 |
+
|
8 |
+
|
9 |
+
st.title("PinocchioLand!")
|
10 |
+
|
11 |
+
# Display an image
|
12 |
+
st.image(
|
13 |
+
"https://i.pinimg.com/736x/30/e9/36/30e936e18912e9a5670b88ec94630b4a.jpg",
|
14 |
+
use_column_width=True,
|
15 |
+
)
|
16 |
+
|
17 |
+
button_label = "record"
|
18 |
+
|
19 |
+
if st.button(button_label):
|
20 |
+
st.write("recording...")
|
21 |
+
recording_path = record_audio()
|
22 |
+
button_label
|
23 |
+
with open(recording_path, "rb") as audio_file:
|
24 |
+
# Define the multipart/form-data payload
|
25 |
+
files = {"audio_file": (recording_path.split("/")[-1], audio_file, "audio/mp3")}
|
26 |
+
|
27 |
+
# Make the POST request
|
28 |
+
stt_response = requests.post("http://localhost:8000/stt_query/", files=files)
|
29 |
+
st.write("domanda :", stt_response.json())
|
30 |
+
|
31 |
+
# LLM
|
32 |
+
url = "http://localhost:8000/llm_query/"
|
33 |
+
# Append the query parameter to the URL
|
34 |
+
llm_response = requests.post(
|
35 |
+
url=url, params={"llm_query": str(stt_response.content)}
|
36 |
+
)
|
37 |
+
data = llm_response.json()
|
38 |
+
inner_data = json.loads(data["response_text"])
|
39 |
+
|
40 |
+
# Now, you can access the data from the inner JSON
|
41 |
+
risposta = inner_data.get("risposta")
|
42 |
+
stato = inner_data.get("stato")
|
43 |
+
tipologia = inner_data.get("tipologia")
|
44 |
+
|
45 |
+
print(risposta)
|
46 |
+
st.write("risposta: ", risposta)
|
47 |
+
st.write("stato: ", stato)
|
48 |
+
st.write("tipologia: ", tipologia)
|
49 |
+
|
50 |
+
# TTS
|
51 |
+
url = "http://localhost:8000/tts_query/"
|
52 |
+
out_path = requests.post(url=url, params={"input_text": str(risposta)})
|
53 |
+
print(out_path.json())
|
54 |
+
play_mp3(out_path.json())
|
src/main.py
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI
|
2 |
+
from pydantic import BaseModel
|
3 |
+
from src.models.openai_llm import run_query
|
4 |
+
from src.models.openai_stt import speech_to_text
|
5 |
+
from src.models.openai_tts import text_to_speech
|
6 |
+
import yaml
|
7 |
+
from openai import OpenAI
|
8 |
+
import uvicorn
|
9 |
+
from fastapi import FastAPI, File, UploadFile
|
10 |
+
import io
|
11 |
+
import aiofiles
|
12 |
+
|
13 |
+
app = FastAPI()
|
14 |
+
|
15 |
+
# read LLM config file
|
16 |
+
with open("conf/train_llm.yaml", "r") as file_in:
|
17 |
+
cfg = yaml.safe_load(file_in)
|
18 |
+
|
19 |
+
# read system message
|
20 |
+
with open("data/system_template.txt", "r") as file_in:
|
21 |
+
system_message = file_in.read()
|
22 |
+
|
23 |
+
# read STT config file
|
24 |
+
with open("conf/speech_to_text.yaml", "r") as file_in:
|
25 |
+
cfg_stt = yaml.safe_load(file_in)
|
26 |
+
|
27 |
+
# init client
|
28 |
+
openai_client = OpenAI()
|
29 |
+
|
30 |
+
|
31 |
+
@app.get("/")
|
32 |
+
def root():
|
33 |
+
return "welcome"
|
34 |
+
|
35 |
+
|
36 |
+
@app.post("/llm_query/")
|
37 |
+
def llm_query(llm_query: str):
|
38 |
+
res = run_query(
|
39 |
+
query=llm_query,
|
40 |
+
openai_params=cfg["openai_parameters"],
|
41 |
+
system_message=system_message,
|
42 |
+
client=openai_client,
|
43 |
+
)
|
44 |
+
return res
|
45 |
+
|
46 |
+
|
47 |
+
@app.post("/stt_query/")
|
48 |
+
def stt_query(audio_file: UploadFile):
|
49 |
+
contents = audio_file.file.read()
|
50 |
+
buffer = io.BytesIO(contents)
|
51 |
+
buffer.name = "file.mp3"
|
52 |
+
return speech_to_text(
|
53 |
+
audio=buffer,
|
54 |
+
openai_client=openai_client,
|
55 |
+
configuration=cfg_stt["openai_parameters"],
|
56 |
+
)
|
57 |
+
|
58 |
+
|
59 |
+
@app.post("/tts_query/")
|
60 |
+
def tts_query(input_text: str):
|
61 |
+
output_path = text_to_speech(client=openai_client, input=input_text)
|
62 |
+
return output_path
|
src/models/__init__.py
ADDED
File without changes
|
src/models/openai_llm.py
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"Module with query request and evaluation of wandb"
|
2 |
+
|
3 |
+
from openai import OpenAI
|
4 |
+
import wandb
|
5 |
+
import pandas as pd
|
6 |
+
import hydra
|
7 |
+
from omegaconf import DictConfig
|
8 |
+
import os
|
9 |
+
import datetime
|
10 |
+
from wandb.sdk.data_types.trace_tree import Trace
|
11 |
+
from dotenv import load_dotenv
|
12 |
+
|
13 |
+
load_dotenv()
|
14 |
+
api_key = os.getenv("OPENAI_API_KEY")
|
15 |
+
|
16 |
+
|
17 |
+
def run_query(client: OpenAI, system_message: str, query: str, openai_params: dict):
|
18 |
+
messages = [
|
19 |
+
{"role": "system", "content": system_message},
|
20 |
+
{"role": "user", "content": query},
|
21 |
+
]
|
22 |
+
start_time_ms = datetime.datetime.now().timestamp() * 1000
|
23 |
+
|
24 |
+
try:
|
25 |
+
if not openai_params["stream"]:
|
26 |
+
response = client.chat.completions.create(
|
27 |
+
**openai_params,
|
28 |
+
messages=messages,
|
29 |
+
)
|
30 |
+
|
31 |
+
end_time_ms = datetime.datetime.now().timestamp() * 1000
|
32 |
+
status = "success"
|
33 |
+
status_message = (None,)
|
34 |
+
response_text = response.choices[0].message.content
|
35 |
+
token_usage = dict(response.usage)
|
36 |
+
# stream
|
37 |
+
else:
|
38 |
+
response = client.chat.completions.create(
|
39 |
+
**openai_params, messages=messages
|
40 |
+
)
|
41 |
+
end_time_ms = datetime.datetime.now().timestamp() * 1000
|
42 |
+
status = "success"
|
43 |
+
status_message = (None,)
|
44 |
+
collected_messages = []
|
45 |
+
for chunk in response:
|
46 |
+
chunk_message = chunk.choices[0].delta.content # extract the message
|
47 |
+
collected_messages.append(chunk_message) #
|
48 |
+
# clean None in collected_messages
|
49 |
+
collected_messages = [m for m in collected_messages if m is not None]
|
50 |
+
response_text = "".join([m for m in collected_messages])
|
51 |
+
token_usage = "no information with stream"
|
52 |
+
except Exception as e:
|
53 |
+
end_time_ms = datetime.datetime.now().timestamp() * 1000
|
54 |
+
status = "error"
|
55 |
+
status_message = str(e)
|
56 |
+
token_usage = {}
|
57 |
+
response_text = "error"
|
58 |
+
|
59 |
+
return {
|
60 |
+
"status": status,
|
61 |
+
"status_message": status_message,
|
62 |
+
"running_time_ms": end_time_ms - start_time_ms,
|
63 |
+
"token_usage": token_usage,
|
64 |
+
"response_text": response_text,
|
65 |
+
}
|
66 |
+
|
67 |
+
|
68 |
+
@hydra.main(config_path="../../conf", config_name="train_llm.yaml")
|
69 |
+
def run_query_on_wandb(cfg: DictConfig):
|
70 |
+
"""Run Openai LLM and log results on wandb. Config file in conf/train_llm.yaml
|
71 |
+
|
72 |
+
Args:
|
73 |
+
cfg (DictConfig): configuration file for parameters
|
74 |
+
"""
|
75 |
+
|
76 |
+
run = wandb.init(
|
77 |
+
project=cfg.main.project_name,
|
78 |
+
group=cfg.main.experiment_name,
|
79 |
+
config=cfg.openai_parameters,
|
80 |
+
job_type="train_llm",
|
81 |
+
)
|
82 |
+
|
83 |
+
artifact = run.use_artifact(cfg.parameters.data)
|
84 |
+
artifact_path = artifact.file()
|
85 |
+
data_frame = pd.read_csv(artifact_path, on_bad_lines="warn").iloc[:, 0].values
|
86 |
+
|
87 |
+
artifact_st = run.use_artifact(cfg.parameters.system_template)
|
88 |
+
artifact_st_path = artifact_st.file()
|
89 |
+
system_message = open(artifact_st_path).read()
|
90 |
+
|
91 |
+
client = OpenAI(api_key=api_key)
|
92 |
+
|
93 |
+
for _, query in enumerate(data_frame):
|
94 |
+
res = run_query(
|
95 |
+
client=client,
|
96 |
+
system_message=system_message,
|
97 |
+
query=query,
|
98 |
+
openai_params=cfg.openai_parameters,
|
99 |
+
)
|
100 |
+
|
101 |
+
# create a span in wandb
|
102 |
+
root_span = Trace(
|
103 |
+
name="root_span",
|
104 |
+
kind="llm", # kind can be "llm", "chain", "agent" or "tool"
|
105 |
+
status_code=res["status"],
|
106 |
+
status_message=res["status_message"],
|
107 |
+
metadata={
|
108 |
+
"temperature": cfg.openai_parameters.temperature,
|
109 |
+
"token_usage": res["token_usage"],
|
110 |
+
"model_name": cfg.openai_parameters.model,
|
111 |
+
},
|
112 |
+
start_time_ms=res["start_time_ms"],
|
113 |
+
end_time_ms=res["end_time_ms"],
|
114 |
+
inputs={
|
115 |
+
"query": query,
|
116 |
+
"system_prompt": system_message,
|
117 |
+
},
|
118 |
+
outputs={"response": res["response_text"]},
|
119 |
+
)
|
120 |
+
# log the span to wandb
|
121 |
+
root_span.log(name="openai_trace")
|
122 |
+
|
123 |
+
|
124 |
+
if __name__ == "__main__":
|
125 |
+
run_query_on_wandb()
|
src/models/openai_stt.py
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from openai import OpenAI
|
3 |
+
import logging
|
4 |
+
import hydra
|
5 |
+
from dotenv import load_dotenv
|
6 |
+
import wandb
|
7 |
+
from omegaconf import DictConfig
|
8 |
+
|
9 |
+
|
10 |
+
load_dotenv()
|
11 |
+
api_key = os.getenv("OPENAI_API_KEY")
|
12 |
+
|
13 |
+
|
14 |
+
def speech_to_text(audio: bytes, openai_client: OpenAI, configuration: dict) -> str:
|
15 |
+
"""From the path of an audio file, it generates a text transcription using openai
|
16 |
+
|
17 |
+
Args:
|
18 |
+
audio_path (str): path of the audio containing the query
|
19 |
+
openai_client (OpenAI): client for openai connection
|
20 |
+
|
21 |
+
Returns:
|
22 |
+
str: transctiption text
|
23 |
+
"""
|
24 |
+
try:
|
25 |
+
# audio_file = open(audio_path, "rb")
|
26 |
+
transcription = openai_client.audio.transcriptions.create(
|
27 |
+
model=configuration["model"],
|
28 |
+
file=audio, # audio_file,
|
29 |
+
language=configuration["language"],
|
30 |
+
response_format=configuration["response_format"],
|
31 |
+
temperature=configuration["temperature"],
|
32 |
+
)
|
33 |
+
|
34 |
+
logging.info("Success: audio converted into text!")
|
35 |
+
logging.info(f"Audio transcription: {transcription}")
|
36 |
+
return transcription
|
37 |
+
except FileNotFoundError as e:
|
38 |
+
pass
|
39 |
+
logging.error(f"Error: not found - {str(e)}")
|
40 |
+
except Exception as e:
|
41 |
+
logging.error(f"Error: OpenAI API request failed - {str(e)}")
|
42 |
+
return f"error {str(e)}"
|
43 |
+
|
44 |
+
|
45 |
+
@hydra.main(config_path="../../conf", config_name="speech_to_text.yaml")
|
46 |
+
def speech_to_text_on_wandb(cfg: DictConfig):
|
47 |
+
openai_client = OpenAI()
|
48 |
+
run = wandb.init(
|
49 |
+
project=cfg.main.project_name,
|
50 |
+
group=cfg.main.experiment_name,
|
51 |
+
config=cfg.openai_parameters,
|
52 |
+
job_type="train_llm",
|
53 |
+
)
|
54 |
+
|
55 |
+
# download artifact
|
56 |
+
artifact = run.use_artifact(
|
57 |
+
os.path.join("mpoliti08/lux-voice-processing", cfg.main.audio_dataset),
|
58 |
+
type="audio",
|
59 |
+
)
|
60 |
+
artifact_dir = artifact.download()
|
61 |
+
|
62 |
+
table = wandb.Table(columns=["audio_file", "transcript"])
|
63 |
+
|
64 |
+
for filename in os.listdir(artifact_dir):
|
65 |
+
file_path = os.path.join(artifact_dir, filename)
|
66 |
+
audio = open(file_path, "rb")
|
67 |
+
transcription_text = speech_to_text(
|
68 |
+
audio=audio,
|
69 |
+
openai_client=openai_client,
|
70 |
+
configuration=cfg.openai_parameters,
|
71 |
+
)
|
72 |
+
|
73 |
+
audio_file = wandb.Audio(file_path)
|
74 |
+
table.add_data(audio_file, transcription_text)
|
75 |
+
|
76 |
+
run.log({"Table": table})
|
77 |
+
run.finish()
|
78 |
+
|
79 |
+
|
80 |
+
if __name__ == "__main__":
|
81 |
+
openai_client = OpenAI()
|
82 |
+
audio_path = "data/audio_recordings/0.wav"
|
83 |
+
configuration = {
|
84 |
+
"language": "it",
|
85 |
+
"model": "whisper-1",
|
86 |
+
"response_format": "text",
|
87 |
+
"temperature": 0.2,
|
88 |
+
}
|
89 |
+
|
90 |
+
audio = open("data/audio_recordings/0.wav", "rb")
|
91 |
+
res = speech_to_text(
|
92 |
+
audio=audio, openai_client=openai_client, configuration=configuration
|
93 |
+
)
|
94 |
+
print(res)
|
95 |
+
|
96 |
+
# speech_to_text_on_wandb()
|
src/models/openai_tts.py
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pathlib import Path
|
2 |
+
from openai import OpenAI
|
3 |
+
import os
|
4 |
+
|
5 |
+
client = OpenAI()
|
6 |
+
|
7 |
+
|
8 |
+
def text_to_speech(client: OpenAI, input: str) -> str:
|
9 |
+
generated_speech_path = "data/generated_speech"
|
10 |
+
n = len(os.listdir(generated_speech_path))
|
11 |
+
response = client.audio.speech.create(
|
12 |
+
model="tts-1", voice="nova", response_format="wav", input=input, speed=1.0
|
13 |
+
)
|
14 |
+
|
15 |
+
output_path = os.path.join(generated_speech_path, str(n)) + ".wav"
|
16 |
+
response.stream_to_file(output_path)
|
17 |
+
return output_path
|
18 |
+
|
19 |
+
|
20 |
+
if __name__ == "__main__":
|
21 |
+
input = "Ciao Pinocchio, è vero che la neve è calda? Che ne pensi?"
|
22 |
+
openai_client = OpenAI()
|
23 |
+
text_to_speech(client=openai_client, input=input)
|
src/utils.py
ADDED
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""Utils module"""
|
2 |
+
|
3 |
+
from datetime import datetime
|
4 |
+
import logging
|
5 |
+
import pyaudio
|
6 |
+
import wave
|
7 |
+
import logging
|
8 |
+
import os
|
9 |
+
import functools
|
10 |
+
import yaml
|
11 |
+
from types import SimpleNamespace
|
12 |
+
import pygame
|
13 |
+
import time
|
14 |
+
import pyaudio
|
15 |
+
import math
|
16 |
+
import struct
|
17 |
+
import wave
|
18 |
+
import time
|
19 |
+
import os
|
20 |
+
from threading import Thread
|
21 |
+
from watchdog.observers import Observer
|
22 |
+
from watchdog.events import FileSystemEventHandler
|
23 |
+
|
24 |
+
format_mapping = {"pyaudio.paInt16": pyaudio.paInt16}
|
25 |
+
|
26 |
+
|
27 |
+
def yaml_file_decorator(yaml_file_path):
|
28 |
+
"""Decorator to pass a config file to other functions
|
29 |
+
|
30 |
+
Args:
|
31 |
+
yaml_file_path (_type_): path to config file
|
32 |
+
"""
|
33 |
+
|
34 |
+
def decorator(func):
|
35 |
+
@functools.wraps(func)
|
36 |
+
def wrapper(*args, **kwargs):
|
37 |
+
# Load YAML file
|
38 |
+
with open(yaml_file_path, "r") as file:
|
39 |
+
config = yaml.safe_load(file)
|
40 |
+
|
41 |
+
# Pass the loaded YAML data to the decorated function
|
42 |
+
result = func(config, *args, **kwargs)
|
43 |
+
return result
|
44 |
+
|
45 |
+
return wrapper
|
46 |
+
|
47 |
+
return decorator
|
48 |
+
|
49 |
+
|
50 |
+
@yaml_file_decorator("conf/recording.yaml")
|
51 |
+
def record_audio(config) -> str:
|
52 |
+
"""function to record audio. Configuaration file is in conf/recordings.yaml
|
53 |
+
|
54 |
+
Args:
|
55 |
+
config (dict): configuration of recording parameters
|
56 |
+
|
57 |
+
Returns:
|
58 |
+
recording_path (str): destination path of recorded audio
|
59 |
+
"""
|
60 |
+
config = SimpleNamespace(**config)
|
61 |
+
p = pyaudio.PyAudio()
|
62 |
+
sample_format = format_mapping.get(config.sample_format, 8)
|
63 |
+
|
64 |
+
stream = p.open(
|
65 |
+
format=sample_format,
|
66 |
+
channels=1, # config.channels,
|
67 |
+
rate=config.fs * 2,
|
68 |
+
frames_per_buffer=config.chunk,
|
69 |
+
input=True,
|
70 |
+
)
|
71 |
+
|
72 |
+
frames = [] # Initialize array to store frames
|
73 |
+
|
74 |
+
# Store data in chunks for 3 seconds
|
75 |
+
for i in range(0, int(config.fs / config.chunk * config.seconds)):
|
76 |
+
data = stream.read(config.chunk)
|
77 |
+
frames.append(data)
|
78 |
+
|
79 |
+
# Stop and close the stream
|
80 |
+
stream.stop_stream()
|
81 |
+
stream.close()
|
82 |
+
# Terminate the PortAudio interface
|
83 |
+
p.terminate()
|
84 |
+
|
85 |
+
logging.info("Finished recording")
|
86 |
+
|
87 |
+
# Save the recorded data as a WAV file
|
88 |
+
recording_path = os.path.join(config.recording_folder, get_current_time() + ".wav")
|
89 |
+
recording_path
|
90 |
+
with wave.open(recording_path, "wb") as wf:
|
91 |
+
wf.setnchannels(config.channels)
|
92 |
+
wf.setsampwidth(p.get_sample_size(sample_format)) # config.sample_format
|
93 |
+
wf.setframerate(config.fs)
|
94 |
+
wf.writeframes(b"".join(frames))
|
95 |
+
wf.close()
|
96 |
+
return recording_path
|
97 |
+
|
98 |
+
|
99 |
+
def play_mp3(mp3_file_path):
|
100 |
+
try:
|
101 |
+
pygame.mixer.init()
|
102 |
+
pygame.mixer.music.load(mp3_file_path)
|
103 |
+
pygame.mixer.music.play()
|
104 |
+
|
105 |
+
# Wait for the music to finish playing
|
106 |
+
while pygame.mixer.music.get_busy():
|
107 |
+
pygame.time.Clock().tick(10)
|
108 |
+
|
109 |
+
except Exception as e:
|
110 |
+
print(f"Error: {e}")
|
111 |
+
|
112 |
+
|
113 |
+
def get_current_time():
|
114 |
+
now = datetime.now()
|
115 |
+
dt_string = now.strftime("%d-%m-%Y_%H:%M:%S")
|
116 |
+
return dt_string
|
117 |
+
|
118 |
+
|
119 |
+
def read_text(file_path: str) -> str:
|
120 |
+
try:
|
121 |
+
with open(file_path, "r") as file_in:
|
122 |
+
text = file_in.read()
|
123 |
+
file_in.close()
|
124 |
+
logging.info(f"Success: file {file_path} read correctly")
|
125 |
+
return text
|
126 |
+
|
127 |
+
except FileNotFoundError as e:
|
128 |
+
logging.error(f"Error: File {file_path} not found - {str(e)}")
|
129 |
+
return ""
|
130 |
+
|
131 |
+
|
132 |
+
def get_pyaudio_format(subtype):
|
133 |
+
if subtype == "PCM_16":
|
134 |
+
return pyaudio.paInt16
|
135 |
+
elif subtype == "PCM_8":
|
136 |
+
return pyaudio.paInt8
|
137 |
+
elif subtype == "PCM_32":
|
138 |
+
return pyaudio.paInt32
|
139 |
+
else:
|
140 |
+
return pyaudio.paInt16
|
141 |
+
|
142 |
+
|
143 |
+
class Recorder:
|
144 |
+
"""Class to continuosly listen to user input, ans save audio when noise is detected.
|
145 |
+
Once noise is detected it will be run the function_to_call with the create filename as parameter
|
146 |
+
|
147 |
+
Args:
|
148 |
+
function_to_call: (function) func to be called with the generated file path as parameter
|
149 |
+
"""
|
150 |
+
|
151 |
+
def __init__(self, function_to_call):
|
152 |
+
self.Threshold = 10
|
153 |
+
self.SHORT_NORMALIZE = 1.0 / 32768.0
|
154 |
+
self.chunk = 1024
|
155 |
+
self.FORMAT = pyaudio.paInt16
|
156 |
+
self.CHANNELS = 1
|
157 |
+
self.RATE = 16_000
|
158 |
+
self.swidth = 2
|
159 |
+
self.TIMEOUT_LENGTH = 2
|
160 |
+
self.f_name_directory = r"/Users/marcellopoliti/Documents/Coding/pischool/lux-voice-processing/data/audio_recordings"
|
161 |
+
self.function_to_call = function_to_call
|
162 |
+
|
163 |
+
self.p = pyaudio.PyAudio()
|
164 |
+
self.stream = self.p.open(
|
165 |
+
format=self.FORMAT,
|
166 |
+
channels=self.CHANNELS,
|
167 |
+
rate=self.RATE,
|
168 |
+
input=True,
|
169 |
+
output=True,
|
170 |
+
frames_per_buffer=self.chunk,
|
171 |
+
)
|
172 |
+
|
173 |
+
# @staticmethod
|
174 |
+
def rms(self, frame):
|
175 |
+
count = len(frame) / self.swidth
|
176 |
+
format = "%dh" % (count)
|
177 |
+
shorts = struct.unpack(format, frame)
|
178 |
+
|
179 |
+
sum_squares = 0.0
|
180 |
+
for sample in shorts:
|
181 |
+
n = sample * self.SHORT_NORMALIZE
|
182 |
+
sum_squares += n * n
|
183 |
+
rms = math.pow(sum_squares / count, 0.5)
|
184 |
+
|
185 |
+
return rms * 1000
|
186 |
+
|
187 |
+
def record(self):
|
188 |
+
print("Noise detected, recording beginning")
|
189 |
+
rec = []
|
190 |
+
current = time.time()
|
191 |
+
end = time.time() + self.TIMEOUT_LENGTH
|
192 |
+
|
193 |
+
while current <= end:
|
194 |
+
|
195 |
+
data = self.stream.read(self.chunk)
|
196 |
+
if self.rms(data) >= self.Threshold:
|
197 |
+
end = time.time() + self.TIMEOUT_LENGTH
|
198 |
+
|
199 |
+
current = time.time()
|
200 |
+
rec.append(data)
|
201 |
+
filename = self.write(b"".join(rec))
|
202 |
+
return filename
|
203 |
+
|
204 |
+
def write(self, recording):
|
205 |
+
n_files = len(os.listdir(self.f_name_directory))
|
206 |
+
|
207 |
+
filename = os.path.join(self.f_name_directory, "{}.wav".format(n_files))
|
208 |
+
|
209 |
+
wf = wave.open(filename, "wb")
|
210 |
+
wf.setnchannels(self.CHANNELS)
|
211 |
+
wf.setsampwidth(self.p.get_sample_size(self.FORMAT))
|
212 |
+
wf.setframerate(self.RATE)
|
213 |
+
wf.writeframes(recording)
|
214 |
+
wf.close()
|
215 |
+
logging.info("Written to file: {}".format(filename))
|
216 |
+
return filename
|
217 |
+
|
218 |
+
def listen(self):
|
219 |
+
print("Listening beginning")
|
220 |
+
while True:
|
221 |
+
input = self.stream.read(self.chunk, exception_on_overflow=False)
|
222 |
+
rms_val = self.rms(input)
|
223 |
+
if rms_val > self.Threshold:
|
224 |
+
filename = self.record()
|
225 |
+
self.function_to_call(filename)
|
226 |
+
|
227 |
+
|
228 |
+
# Function to check for new recordings and print a message
|
229 |
+
def check_for_new_recordings(function_to_call):
|
230 |
+
previous_files = set(os.listdir("audio_recordings"))
|
231 |
+
|
232 |
+
while True:
|
233 |
+
current_files = set(os.listdir("audio_recordings"))
|
234 |
+
new_files = current_files - previous_files
|
235 |
+
|
236 |
+
for new_file in new_files:
|
237 |
+
print(f"New recording detected: {new_file}")
|
238 |
+
function_to_call(new_file)
|
239 |
+
print("Returning to listening")
|
240 |
+
|
241 |
+
previous_files = current_files
|
242 |
+
time.sleep(2) # Check for new recordings every 2 seconds
|
243 |
+
|
244 |
+
|
245 |
+
if __name__ == "__main__":
|
246 |
+
recordings_path = record_audio()
|
src/utils/add_artifact.py
ADDED
@@ -0,0 +1,97 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
Upload file to wandb
|
3 |
+
to upload dir:
|
4 |
+
export WANDB_PROJECT='lux-voice-processing'
|
5 |
+
wandb artifact put --type audio --name recordings_dataset data/audio_recordings
|
6 |
+
"""
|
7 |
+
|
8 |
+
import wandb
|
9 |
+
import argparse
|
10 |
+
import os
|
11 |
+
import logging
|
12 |
+
|
13 |
+
|
14 |
+
def upload_data(args):
|
15 |
+
"""upload artifact on wandb
|
16 |
+
|
17 |
+
Args:
|
18 |
+
args (dict): project, name, type, local_path
|
19 |
+
"""
|
20 |
+
if os.path.exists(args.local_path):
|
21 |
+
run = wandb.init(
|
22 |
+
project=args.project, job_type="add-artifact", group="add-artifact"
|
23 |
+
)
|
24 |
+
artifact = wandb.Artifact(name=args.name, type=args.type)
|
25 |
+
artifact.add_file(local_path=args.local_path)
|
26 |
+
run.log_artifact(artifact)
|
27 |
+
run.finish()
|
28 |
+
else:
|
29 |
+
print(f"File does not exist: {args.local_path}")
|
30 |
+
|
31 |
+
|
32 |
+
# TODO: not working
|
33 |
+
def upload_dir(args):
|
34 |
+
"""upload dir artifact on wandb
|
35 |
+
|
36 |
+
Args:
|
37 |
+
args (dict): project, name, type, local_path
|
38 |
+
"""
|
39 |
+
try:
|
40 |
+
if os.path.isdir(args.local_path):
|
41 |
+
run = wandb.init(
|
42 |
+
project=args.project, job_type="add-artifact", group="add-artifact"
|
43 |
+
)
|
44 |
+
artifact = wandb.Artifact(name=args.name, type=args.type)
|
45 |
+
artifact.add_dir(
|
46 |
+
local_path="/Users/marcellopoliti/Documents/Coding/pischool/lux-voice-processing/data/audio_recordings"
|
47 |
+
)
|
48 |
+
run.log_artifact(artifact)
|
49 |
+
run.finish()
|
50 |
+
else:
|
51 |
+
logging.error(f"Not dir: {args.local_path}")
|
52 |
+
except Exception as e:
|
53 |
+
logging.exception(f"Exception: {str(e)} {type(e).__name__} ")
|
54 |
+
|
55 |
+
|
56 |
+
if __name__ == "__main__":
|
57 |
+
parser = argparse.ArgumentParser(description="Arguments for LLM monitoring")
|
58 |
+
|
59 |
+
parser.add_argument(
|
60 |
+
"--project",
|
61 |
+
type=str,
|
62 |
+
help="wandb project name",
|
63 |
+
default="lux-voice-processing",
|
64 |
+
)
|
65 |
+
|
66 |
+
parser.add_argument(
|
67 |
+
"--local_path",
|
68 |
+
type=str,
|
69 |
+
help="local path of your artifact",
|
70 |
+
required=True,
|
71 |
+
)
|
72 |
+
|
73 |
+
parser.add_argument(
|
74 |
+
"--name",
|
75 |
+
type=str,
|
76 |
+
help="name of your artifact",
|
77 |
+
required=True,
|
78 |
+
)
|
79 |
+
|
80 |
+
parser.add_argument(
|
81 |
+
"--type",
|
82 |
+
type=str,
|
83 |
+
help="type of your artifact",
|
84 |
+
required=True,
|
85 |
+
)
|
86 |
+
|
87 |
+
parser.add_argument(
|
88 |
+
"--isdir", type=bool, help="is dir?", required=False, default=False
|
89 |
+
)
|
90 |
+
args = parser.parse_args()
|
91 |
+
|
92 |
+
if args.isdir:
|
93 |
+
print("uploading dir... ", args.local_path)
|
94 |
+
upload_data(args)
|
95 |
+
else:
|
96 |
+
print("uploading file...")
|
97 |
+
upload_dir(args)
|