sasan commited on
Commit
429303d
2 Parent(s): 74474b8 4438ccd

Merge pull request #1 from sasan-j/feature/refactor

Browse files
.devcontainer/Dockerfile CHANGED
@@ -2,8 +2,8 @@
2
  # Run: sudo docker run -v $(pwd):/workspace/project --gpus all -it --rm <project_name>
3
 
4
  ARG USERNAME=kitt
5
- ARG USER_UID=1005
6
- ARG USER_GID=100
7
 
8
 
9
  FROM nvidia/cuda:12.1.1-base-ubuntu22.04
 
2
  # Run: sudo docker run -v $(pwd):/workspace/project --gpus all -it --rm <project_name>
3
 
4
  ARG USERNAME=kitt
5
+ ARG USER_UID=320869193
6
+ ARG USER_GID=1429829944
7
 
8
 
9
  FROM nvidia/cuda:12.1.1-base-ubuntu22.04
.gitignore ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ audio
2
+ .DS_Store
3
+ .env
4
+ __pycache__
.pylintrc ADDED
@@ -0,0 +1,642 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [MAIN]
2
+
3
+ # Analyse import fallback blocks. This can be used to support both Python 2 and
4
+ # 3 compatible code, which means that the block might have code that exists
5
+ # only in one or another interpreter, leading to false positives when analysed.
6
+ analyse-fallback-blocks=no
7
+
8
+ # Clear in-memory caches upon conclusion of linting. Useful if running pylint
9
+ # in a server-like mode.
10
+ clear-cache-post-run=no
11
+
12
+ # Load and enable all available extensions. Use --list-extensions to see a list
13
+ # all available extensions.
14
+ #enable-all-extensions=
15
+
16
+ # In error mode, messages with a category besides ERROR or FATAL are
17
+ # suppressed, and no reports are done by default. Error mode is compatible with
18
+ # disabling specific errors.
19
+ #errors-only=
20
+
21
+ # Always return a 0 (non-error) status code, even if lint errors are found.
22
+ # This is primarily useful in continuous integration scripts.
23
+ #exit-zero=
24
+
25
+ # A comma-separated list of package or module names from where C extensions may
26
+ # be loaded. Extensions are loading into the active Python interpreter and may
27
+ # run arbitrary code.
28
+ extension-pkg-allow-list=
29
+
30
+ # A comma-separated list of package or module names from where C extensions may
31
+ # be loaded. Extensions are loading into the active Python interpreter and may
32
+ # run arbitrary code. (This is an alternative name to extension-pkg-allow-list
33
+ # for backward compatibility.)
34
+ extension-pkg-whitelist=
35
+
36
+ # Return non-zero exit code if any of these messages/categories are detected,
37
+ # even if score is above --fail-under value. Syntax same as enable. Messages
38
+ # specified are enabled, while categories only check already-enabled messages.
39
+ fail-on=
40
+
41
+ # Specify a score threshold under which the program will exit with error.
42
+ fail-under=10
43
+
44
+ # Interpret the stdin as a python script, whose filename needs to be passed as
45
+ # the module_or_package argument.
46
+ #from-stdin=
47
+
48
+ # Files or directories to be skipped. They should be base names, not paths.
49
+ ignore=CVS
50
+
51
+ # Add files or directories matching the regular expressions patterns to the
52
+ # ignore-list. The regex matches against paths and can be in Posix or Windows
53
+ # format. Because '\\' represents the directory delimiter on Windows systems,
54
+ # it can't be used as an escape character.
55
+ ignore-paths=
56
+
57
+ # Files or directories matching the regular expression patterns are skipped.
58
+ # The regex matches against base names, not paths. The default value ignores
59
+ # Emacs file locks
60
+ ignore-patterns=^\.#
61
+
62
+ # List of module names for which member attributes should not be checked
63
+ # (useful for modules/projects where namespaces are manipulated during runtime
64
+ # and thus existing member attributes cannot be deduced by static analysis). It
65
+ # supports qualified module names, as well as Unix pattern matching.
66
+ ignored-modules=
67
+
68
+ # Python code to execute, usually for sys.path manipulation such as
69
+ # pygtk.require().
70
+ #init-hook=
71
+
72
+ # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
73
+ # number of processors available to use, and will cap the count on Windows to
74
+ # avoid hangs.
75
+ jobs=1
76
+
77
+ # Control the amount of potential inferred values when inferring a single
78
+ # object. This can help the performance when dealing with large functions or
79
+ # complex, nested conditions.
80
+ limit-inference-results=100
81
+
82
+ # List of plugins (as comma separated values of python module names) to load,
83
+ # usually to register additional checkers.
84
+ load-plugins=
85
+
86
+ # Pickle collected data for later comparisons.
87
+ persistent=yes
88
+
89
+ # Minimum Python version to use for version dependent checks. Will default to
90
+ # the version used to run pylint.
91
+ py-version=3.12
92
+
93
+ # Discover python modules and packages in the file system subtree.
94
+ recursive=no
95
+
96
+ # Add paths to the list of the source roots. Supports globbing patterns. The
97
+ # source root is an absolute path or a path relative to the current working
98
+ # directory used to determine a package namespace for modules located under the
99
+ # source root.
100
+ source-roots=
101
+
102
+ # When enabled, pylint would attempt to guess common misconfiguration and emit
103
+ # user-friendly hints instead of false-positive error messages.
104
+ suggestion-mode=yes
105
+
106
+ # Allow loading of arbitrary C extensions. Extensions are imported into the
107
+ # active Python interpreter and may run arbitrary code.
108
+ unsafe-load-any-extension=no
109
+
110
+ # In verbose mode, extra non-checker-related info will be displayed.
111
+ #verbose=
112
+
113
+
114
+ [BASIC]
115
+
116
+ # Naming style matching correct argument names.
117
+ argument-naming-style=snake_case
118
+
119
+ # Regular expression matching correct argument names. Overrides argument-
120
+ # naming-style. If left empty, argument names will be checked with the set
121
+ # naming style.
122
+ #argument-rgx=
123
+
124
+ # Naming style matching correct attribute names.
125
+ attr-naming-style=snake_case
126
+
127
+ # Regular expression matching correct attribute names. Overrides attr-naming-
128
+ # style. If left empty, attribute names will be checked with the set naming
129
+ # style.
130
+ #attr-rgx=
131
+
132
+ # Bad variable names which should always be refused, separated by a comma.
133
+ bad-names=foo,
134
+ bar,
135
+ baz,
136
+ toto,
137
+ tutu,
138
+ tata
139
+
140
+ # Bad variable names regexes, separated by a comma. If names match any regex,
141
+ # they will always be refused
142
+ bad-names-rgxs=
143
+
144
+ # Naming style matching correct class attribute names.
145
+ class-attribute-naming-style=any
146
+
147
+ # Regular expression matching correct class attribute names. Overrides class-
148
+ # attribute-naming-style. If left empty, class attribute names will be checked
149
+ # with the set naming style.
150
+ #class-attribute-rgx=
151
+
152
+ # Naming style matching correct class constant names.
153
+ class-const-naming-style=UPPER_CASE
154
+
155
+ # Regular expression matching correct class constant names. Overrides class-
156
+ # const-naming-style. If left empty, class constant names will be checked with
157
+ # the set naming style.
158
+ #class-const-rgx=
159
+
160
+ # Naming style matching correct class names.
161
+ class-naming-style=PascalCase
162
+
163
+ # Regular expression matching correct class names. Overrides class-naming-
164
+ # style. If left empty, class names will be checked with the set naming style.
165
+ #class-rgx=
166
+
167
+ # Naming style matching correct constant names.
168
+ const-naming-style=UPPER_CASE
169
+
170
+ # Regular expression matching correct constant names. Overrides const-naming-
171
+ # style. If left empty, constant names will be checked with the set naming
172
+ # style.
173
+ #const-rgx=
174
+
175
+ # Minimum line length for functions/classes that require docstrings, shorter
176
+ # ones are exempt.
177
+ docstring-min-length=-1
178
+
179
+ # Naming style matching correct function names.
180
+ function-naming-style=snake_case
181
+
182
+ # Regular expression matching correct function names. Overrides function-
183
+ # naming-style. If left empty, function names will be checked with the set
184
+ # naming style.
185
+ #function-rgx=
186
+
187
+ # Good variable names which should always be accepted, separated by a comma.
188
+ good-names=i,
189
+ j,
190
+ k,
191
+ ex,
192
+ Run,
193
+ _
194
+
195
+ # Good variable names regexes, separated by a comma. If names match any regex,
196
+ # they will always be accepted
197
+ good-names-rgxs=
198
+
199
+ # Include a hint for the correct naming format with invalid-name.
200
+ include-naming-hint=no
201
+
202
+ # Naming style matching correct inline iteration names.
203
+ inlinevar-naming-style=any
204
+
205
+ # Regular expression matching correct inline iteration names. Overrides
206
+ # inlinevar-naming-style. If left empty, inline iteration names will be checked
207
+ # with the set naming style.
208
+ #inlinevar-rgx=
209
+
210
+ # Naming style matching correct method names.
211
+ method-naming-style=snake_case
212
+
213
+ # Regular expression matching correct method names. Overrides method-naming-
214
+ # style. If left empty, method names will be checked with the set naming style.
215
+ #method-rgx=
216
+
217
+ # Naming style matching correct module names.
218
+ module-naming-style=snake_case
219
+
220
+ # Regular expression matching correct module names. Overrides module-naming-
221
+ # style. If left empty, module names will be checked with the set naming style.
222
+ #module-rgx=
223
+
224
+ # Colon-delimited sets of names that determine each other's naming style when
225
+ # the name regexes allow several styles.
226
+ name-group=
227
+
228
+ # Regular expression which should only match function or class names that do
229
+ # not require a docstring.
230
+ no-docstring-rgx=^_
231
+
232
+ # List of decorators that produce properties, such as abc.abstractproperty. Add
233
+ # to this list to register other decorators that produce valid properties.
234
+ # These decorators are taken in consideration only for invalid-name.
235
+ property-classes=abc.abstractproperty
236
+
237
+ # Regular expression matching correct type alias names. If left empty, type
238
+ # alias names will be checked with the set naming style.
239
+ #typealias-rgx=
240
+
241
+ # Regular expression matching correct type variable names. If left empty, type
242
+ # variable names will be checked with the set naming style.
243
+ #typevar-rgx=
244
+
245
+ # Naming style matching correct variable names.
246
+ variable-naming-style=snake_case
247
+
248
+ # Regular expression matching correct variable names. Overrides variable-
249
+ # naming-style. If left empty, variable names will be checked with the set
250
+ # naming style.
251
+ #variable-rgx=
252
+
253
+
254
+ [CLASSES]
255
+
256
+ # Warn about protected attribute access inside special methods
257
+ check-protected-access-in-special-methods=no
258
+
259
+ # List of method names used to declare (i.e. assign) instance attributes.
260
+ defining-attr-methods=__init__,
261
+ __new__,
262
+ setUp,
263
+ asyncSetUp,
264
+ __post_init__
265
+
266
+ # List of member names, which should be excluded from the protected access
267
+ # warning.
268
+ exclude-protected=_asdict,_fields,_replace,_source,_make,os._exit
269
+
270
+ # List of valid names for the first argument in a class method.
271
+ valid-classmethod-first-arg=cls
272
+
273
+ # List of valid names for the first argument in a metaclass class method.
274
+ valid-metaclass-classmethod-first-arg=mcs
275
+
276
+
277
+ [DESIGN]
278
+
279
+ # List of regular expressions of class ancestor names to ignore when counting
280
+ # public methods (see R0903)
281
+ exclude-too-few-public-methods=
282
+
283
+ # List of qualified class names to ignore when counting class parents (see
284
+ # R0901)
285
+ ignored-parents=
286
+
287
+ # Maximum number of arguments for function / method.
288
+ max-args=5
289
+
290
+ # Maximum number of attributes for a class (see R0902).
291
+ max-attributes=7
292
+
293
+ # Maximum number of boolean expressions in an if statement (see R0916).
294
+ max-bool-expr=5
295
+
296
+ # Maximum number of branch for function / method body.
297
+ max-branches=12
298
+
299
+ # Maximum number of locals for function / method body.
300
+ max-locals=15
301
+
302
+ # Maximum number of parents for a class (see R0901).
303
+ max-parents=7
304
+
305
+ # Maximum number of public methods for a class (see R0904).
306
+ max-public-methods=20
307
+
308
+ # Maximum number of return / yield for function / method body.
309
+ max-returns=6
310
+
311
+ # Maximum number of statements in function / method body.
312
+ max-statements=50
313
+
314
+ # Minimum number of public methods for a class (see R0903).
315
+ min-public-methods=2
316
+
317
+
318
+ [EXCEPTIONS]
319
+
320
+ # Exceptions that will emit a warning when caught.
321
+ overgeneral-exceptions=builtins.BaseException,builtins.Exception
322
+
323
+
324
+ [FORMAT]
325
+
326
+ # Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
327
+ expected-line-ending-format=
328
+
329
+ # Regexp for a line that is allowed to be longer than the limit.
330
+ ignore-long-lines=^\s*(# )?<?https?://\S+>?$
331
+
332
+ # Number of spaces of indent required inside a hanging or continued line.
333
+ indent-after-paren=4
334
+
335
+ # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
336
+ # tab).
337
+ indent-string=' '
338
+
339
+ # Maximum number of characters on a single line.
340
+ max-line-length=100
341
+
342
+ # Maximum number of lines in a module.
343
+ max-module-lines=1000
344
+
345
+ # Allow the body of a class to be on the same line as the declaration if body
346
+ # contains single statement.
347
+ single-line-class-stmt=no
348
+
349
+ # Allow the body of an if to be on the same line as the test if there is no
350
+ # else.
351
+ single-line-if-stmt=no
352
+
353
+
354
+ [IMPORTS]
355
+
356
+ # List of modules that can be imported at any level, not just the top level
357
+ # one.
358
+ allow-any-import-level=
359
+
360
+ # Allow explicit reexports by alias from a package __init__.
361
+ allow-reexport-from-package=no
362
+
363
+ # Allow wildcard imports from modules that define __all__.
364
+ allow-wildcard-with-all=no
365
+
366
+ # Deprecated modules which should not be used, separated by a comma.
367
+ deprecated-modules=
368
+
369
+ # Output a graph (.gv or any supported image format) of external dependencies
370
+ # to the given file (report RP0402 must not be disabled).
371
+ ext-import-graph=
372
+
373
+ # Output a graph (.gv or any supported image format) of all (i.e. internal and
374
+ # external) dependencies to the given file (report RP0402 must not be
375
+ # disabled).
376
+ import-graph=
377
+
378
+ # Output a graph (.gv or any supported image format) of internal dependencies
379
+ # to the given file (report RP0402 must not be disabled).
380
+ int-import-graph=
381
+
382
+ # Force import order to recognize a module as part of the standard
383
+ # compatibility libraries.
384
+ known-standard-library=
385
+
386
+ # Force import order to recognize a module as part of a third party library.
387
+ known-third-party=enchant
388
+
389
+ # Couples of modules and preferred modules, separated by a comma.
390
+ preferred-modules=
391
+
392
+
393
+ [LOGGING]
394
+
395
+ # The type of string formatting that logging methods do. `old` means using %
396
+ # formatting, `new` is for `{}` formatting.
397
+ logging-format-style=old
398
+
399
+ # Logging modules to check that the string format arguments are in logging
400
+ # function parameter format.
401
+ logging-modules=logging
402
+
403
+
404
+ [MESSAGES CONTROL]
405
+
406
+ # Only show warnings with the listed confidence levels. Leave empty to show
407
+ # all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
408
+ # UNDEFINED.
409
+ confidence=HIGH,
410
+ CONTROL_FLOW,
411
+ INFERENCE,
412
+ INFERENCE_FAILURE,
413
+ UNDEFINED
414
+
415
+ # Disable the message, report, category or checker with the given id(s). You
416
+ # can either give multiple identifiers separated by comma (,) or put this
417
+ # option multiple times (only on the command line, not in the configuration
418
+ # file where it should appear only once). You can also use "--disable=all" to
419
+ # disable everything first and then re-enable specific checks. For example, if
420
+ # you want to run only the similarities checker, you can use "--disable=all
421
+ # --enable=similarities". If you want to run only the classes checker, but have
422
+ # no Warning level messages displayed, use "--disable=all --enable=classes
423
+ # --disable=W".
424
+ disable=raw-checker-failed,
425
+ bad-inline-option,
426
+ locally-disabled,
427
+ file-ignored,
428
+ suppressed-message,
429
+ useless-suppression,
430
+ deprecated-pragma,
431
+ use-symbolic-message-instead,
432
+ use-implicit-booleaness-not-comparison-to-string,
433
+ use-implicit-booleaness-not-comparison-to-zero,
434
+ missing-module-docstring,
435
+ missing-class-docstring,
436
+ missing-function-docstring
437
+
438
+ # Enable the message, report, category or checker with the given id(s). You can
439
+ # either give multiple identifier separated by comma (,) or put this option
440
+ # multiple time (only on the command line, not in the configuration file where
441
+ # it should appear only once). See also the "--disable" option for examples.
442
+ enable=
443
+
444
+
445
+ [METHOD_ARGS]
446
+
447
+ # List of qualified names (i.e., library.method) which require a timeout
448
+ # parameter e.g. 'requests.api.get,requests.api.post'
449
+ timeout-methods=requests.api.delete,requests.api.get,requests.api.head,requests.api.options,requests.api.patch,requests.api.post,requests.api.put,requests.api.request
450
+
451
+
452
+ [MISCELLANEOUS]
453
+
454
+ # List of note tags to take in consideration, separated by a comma.
455
+ notes=FIXME,
456
+ XXX,
457
+ TODO
458
+
459
+ # Regular expression of note tags to take in consideration.
460
+ notes-rgx=
461
+
462
+
463
+ [REFACTORING]
464
+
465
+ # Maximum number of nested blocks for function / method body
466
+ max-nested-blocks=5
467
+
468
+ # Complete name of functions that never returns. When checking for
469
+ # inconsistent-return-statements if a never returning function is called then
470
+ # it will be considered as an explicit return statement and no message will be
471
+ # printed.
472
+ never-returning-functions=sys.exit,argparse.parse_error
473
+
474
+ # Let 'consider-using-join' be raised when the separator to join on would be
475
+ # non-empty (resulting in expected fixes of the type: ``"- " + " -
476
+ # ".join(items)``)
477
+ # suggest-join-with-non-empty-separator=yes
478
+
479
+
480
+ [REPORTS]
481
+
482
+ # Python expression which should return a score less than or equal to 10. You
483
+ # have access to the variables 'fatal', 'error', 'warning', 'refactor',
484
+ # 'convention', and 'info' which contain the number of messages in each
485
+ # category, as well as 'statement' which is the total number of statements
486
+ # analyzed. This score is used by the global evaluation report (RP0004).
487
+ evaluation=max(0, 0 if fatal else 10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10))
488
+
489
+ # Template used to display messages. This is a python new-style format string
490
+ # used to format the message information. See doc for all details.
491
+ msg-template=
492
+
493
+ # Set the output format. Available formats are: text, parseable, colorized,
494
+ # json2 (improved json format), json (old json format) and msvs (visual
495
+ # studio). You can also give a reporter class, e.g.
496
+ # mypackage.mymodule.MyReporterClass.
497
+ #output-format=
498
+
499
+ # Tells whether to display a full report or only the messages.
500
+ reports=no
501
+
502
+ # Activate the evaluation score.
503
+ score=yes
504
+
505
+
506
+ [SIMILARITIES]
507
+
508
+ # Comments are removed from the similarity computation
509
+ ignore-comments=yes
510
+
511
+ # Docstrings are removed from the similarity computation
512
+ ignore-docstrings=yes
513
+
514
+ # Imports are removed from the similarity computation
515
+ ignore-imports=yes
516
+
517
+ # Signatures are removed from the similarity computation
518
+ ignore-signatures=yes
519
+
520
+ # Minimum lines number of a similarity.
521
+ min-similarity-lines=4
522
+
523
+
524
+ [SPELLING]
525
+
526
+ # Limits count of emitted suggestions for spelling mistakes.
527
+ max-spelling-suggestions=4
528
+
529
+ # Spelling dictionary name. No available dictionaries : You need to install
530
+ # both the python package and the system dependency for enchant to work.
531
+ spelling-dict=
532
+
533
+ # List of comma separated words that should be considered directives if they
534
+ # appear at the beginning of a comment and should not be checked.
535
+ spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy:
536
+
537
+ # List of comma separated words that should not be checked.
538
+ spelling-ignore-words=
539
+
540
+ # A path to a file that contains the private dictionary; one word per line.
541
+ spelling-private-dict-file=
542
+
543
+ # Tells whether to store unknown words to the private dictionary (see the
544
+ # --spelling-private-dict-file option) instead of raising a message.
545
+ spelling-store-unknown-words=no
546
+
547
+
548
+ [STRING]
549
+
550
+ # This flag controls whether inconsistent-quotes generates a warning when the
551
+ # character used as a quote delimiter is used inconsistently within a module.
552
+ check-quote-consistency=no
553
+
554
+ # This flag controls whether the implicit-str-concat should generate a warning
555
+ # on implicit string concatenation in sequences defined over several lines.
556
+ check-str-concat-over-line-jumps=no
557
+
558
+
559
+ [TYPECHECK]
560
+
561
+ # List of decorators that produce context managers, such as
562
+ # contextlib.contextmanager. Add to this list to register other decorators that
563
+ # produce valid context managers.
564
+ contextmanager-decorators=contextlib.contextmanager
565
+
566
+ # List of members which are set dynamically and missed by pylint inference
567
+ # system, and so shouldn't trigger E1101 when accessed. Python regular
568
+ # expressions are accepted.
569
+ generated-members=
570
+
571
+ # Tells whether to warn about missing members when the owner of the attribute
572
+ # is inferred to be None.
573
+ ignore-none=yes
574
+
575
+ # This flag controls whether pylint should warn about no-member and similar
576
+ # checks whenever an opaque object is returned when inferring. The inference
577
+ # can return multiple potential results while evaluating a Python object, but
578
+ # some branches might not be evaluated, which results in partial inference. In
579
+ # that case, it might be useful to still emit no-member and other checks for
580
+ # the rest of the inferred objects.
581
+ ignore-on-opaque-inference=yes
582
+
583
+ # List of symbolic message names to ignore for Mixin members.
584
+ ignored-checks-for-mixins=no-member,
585
+ not-async-context-manager,
586
+ not-context-manager,
587
+ attribute-defined-outside-init
588
+
589
+ # List of class names for which member attributes should not be checked (useful
590
+ # for classes with dynamically set attributes). This supports the use of
591
+ # qualified names.
592
+ ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace
593
+
594
+ # Show a hint with possible names when a member name was not found. The aspect
595
+ # of finding the hint is based on edit distance.
596
+ missing-member-hint=yes
597
+
598
+ # The minimum edit distance a name should have in order to be considered a
599
+ # similar match for a missing member name.
600
+ missing-member-hint-distance=1
601
+
602
+ # The total number of similar names that should be taken in consideration when
603
+ # showing a hint for a missing member.
604
+ missing-member-max-choices=1
605
+
606
+ # Regex pattern to define which classes are considered mixins.
607
+ mixin-class-rgx=.*[Mm]ixin
608
+
609
+ # List of decorators that change the signature of a decorated function.
610
+ signature-mutators=
611
+
612
+
613
+ [VARIABLES]
614
+
615
+ # List of additional names supposed to be defined in builtins. Remember that
616
+ # you should avoid defining new builtins when possible.
617
+ additional-builtins=
618
+
619
+ # Tells whether unused global variables should be treated as a violation.
620
+ allow-global-unused-variables=yes
621
+
622
+ # List of names allowed to shadow builtins
623
+ allowed-redefined-builtins=
624
+
625
+ # List of strings which can identify a callback function by name. A callback
626
+ # name must start or end with one of those strings.
627
+ callbacks=cb_,
628
+ _cb
629
+
630
+ # A regular expression matching the name of dummy variables (i.e. expected to
631
+ # not be used).
632
+ dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
633
+
634
+ # Argument names that match this expression will be ignored.
635
+ ignored-argument-names=_.*|^ignored_|^unused_
636
+
637
+ # Tells whether we should check for unused import in __init__ files.
638
+ init-import=no
639
+
640
+ # List of qualified module names which can have objects that can redefine
641
+ # builtins.
642
+ redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
apis.py CHANGED
@@ -1,5 +1,3 @@
1
- import requests
2
-
3
  from geopy.geocoders import Nominatim
4
 
5
 
@@ -53,287 +51,4 @@ def check_city_coordinates(lat = "", lon = "", city = "", **kwargs):
53
  lon = coord.longitude
54
  return lat, lon, city
55
 
56
- # Select coordinates at equal distance, including the last one
57
- def select_equally_spaced_coordinates(coords, number_of_points=10):
58
- n = len(coords)
59
- selected_coords = []
60
- interval = max((n - 1) / (number_of_points - 1), 1)
61
- for i in range(number_of_points):
62
- # Calculate the index, ensuring it doesn't exceed the bounds of the list
63
- index = int(round(i * interval))
64
- if index < n:
65
- selected_coords.append(coords[index])
66
- return selected_coords
67
-
68
- def find_points_of_interest(lat="0", lon="0", city="", type_of_poi="restaurant", **kwargs):
69
- """
70
- Return some of the closest points of interest for a specific location and type of point of interest. The more parameters there are, the more precise.
71
- :param lat (string): latitude
72
- :param lon (string): longitude
73
- :param city (string): Required. city
74
- :param type_of_poi (string): Required. type of point of interest depending on what the user wants to do.
75
- """
76
- lat, lon, city = check_city_coordinates(lat,lon,city)
77
-
78
- r = requests.get(f'https://api.tomtom.com/search/2/search/{type_of_poi}'
79
- '.json?key={0}&lat={1}&lon={2}&radius=10000&idxSet=POI&limit=100'.format(
80
- TOMTOM_KEY,
81
- lat,
82
- lon
83
- ))
84
-
85
- # Parse JSON from the response
86
- data = r.json()
87
- #print(data)
88
- # Extract results
89
- results = data['results']
90
-
91
- # Sort the results based on distance
92
- sorted_results = sorted(results, key=lambda x: x['dist'])
93
- #print(sorted_results)
94
-
95
- # Format and limit to top 5 results
96
- formatted_results = [
97
- f"The {type_of_poi} {result['poi']['name']} is {int(result['dist'])} meters away"
98
- for result in sorted_results[:5]
99
- ]
100
-
101
-
102
- return ". ".join(formatted_results)
103
-
104
- def find_route(lat_depart="0", lon_depart="0", city_depart="", address_destination="", depart_time ="", **kwargs):
105
- """
106
- Return the distance and the estimated time to go to a specific destination from the current place, at a specified depart time.
107
- :param lat_depart (string): latitude of depart
108
- :param lon_depart (string): longitude of depart
109
- :param city_depart (string): Required. city of depart
110
- :param address_destination (string): Required. The destination
111
- :param depart_time (string): departure hour, in the format '08:00:20'.
112
- """
113
- print(address_destination)
114
- date = "2025-03-29T"
115
- departure_time = '2024-02-01T' + depart_time
116
- lat, lon, city = check_city_coordinates(lat_depart,lon_depart,city_depart)
117
- lat_dest, lon_dest = find_coordinates(address_destination)
118
- #print(lat_dest, lon_dest)
119
-
120
- #print(departure_time)
121
-
122
- r = requests.get('https://api.tomtom.com/routing/1/calculateRoute/{0},{1}:{2},{3}/json?key={4}&departAt={5}'.format(
123
- lat_depart,
124
- lon_depart,
125
- lat_dest,
126
- lon_dest,
127
- TOMTOM_KEY,
128
- departure_time
129
- ))
130
-
131
- # Parse JSON from the response
132
- data = r.json()
133
- #print(data)
134
-
135
- #print(data)
136
-
137
- result = data['routes'][0]['summary']
138
-
139
- # Calculate distance in kilometers (1 meter = 0.001 kilometers)
140
- distance_km = result['lengthInMeters'] * 0.001
141
-
142
- # Calculate travel time in minutes (1 second = 1/60 minutes)
143
- time_minutes = result['travelTimeInSeconds'] / 60
144
- if time_minutes < 60:
145
- time_display = f"{time_minutes:.0f} minutes"
146
- else:
147
- hours = int(time_minutes / 60)
148
- minutes = int(time_minutes % 60)
149
- time_display = f"{hours} hours" + (f" and {minutes} minutes" if minutes > 0 else "")
150
-
151
- # Extract arrival time from the JSON structure
152
- arrival_time_str = result['arrivalTime']
153
-
154
- # Convert string to datetime object
155
- arrival_time = datetime.fromisoformat(arrival_time_str)
156
-
157
- # Extract and display the arrival hour in HH:MM format
158
- arrival_hour_display = arrival_time.strftime("%H:%M")
159
-
160
-
161
- # return the distance and time
162
- return(f"The route to go to {address_destination} is {distance_km:.2f} km and {time_display}. Leaving now, the arrival time is estimated at {arrival_hour_display} " )
163
-
164
-
165
- # Sort the results based on distance
166
- #sorted_results = sorted(results, key=lambda x: x['dist'])
167
-
168
- #return ". ".join(formatted_results)
169
-
170
-
171
- def search_along_route(latitude_depart, longitude_depart, city_destination, type_of_poi):
172
- """
173
- Return some of the closest points of interest along the route from the depart point, specified by its coordinates and a city destination.
174
- :param latitude_depart (string): Required. Latitude of depart location
175
- :param longitude_depart (string): Required. Longitude of depart location
176
- :param city_destination (string): Required. City destination
177
- :param type_of_poi (string): Required. type of point of interest depending on what the user wants to do.
178
- """
179
-
180
- lat_dest, lon_dest = find_coordinates(city_destination)
181
- print(lat_dest)
182
-
183
- r = requests.get('https://api.tomtom.com/routing/1/calculateRoute/{0},{1}:{2},{3}/json?key={4}'.format(
184
- latitude_depart,
185
- longitude_depart,
186
- lat_dest,
187
- lon_dest,
188
- TOMTOM_KEY
189
- ))
190
-
191
- coord_route = select_equally_spaced_coordinates(r.json()['routes'][0]['legs'][0]['points'])
192
-
193
- # The API endpoint for searching along a route
194
- url = f'https://api.tomtom.com/search/2/searchAlongRoute/{type_of_poi}.json?key={TOMTOM_KEY}&maxDetourTime=700&limit=20&sortBy=detourTime'
195
-
196
- # The data payload
197
- payload = {
198
- "route": {
199
- "points": [
200
- {"lat": float(latitude_depart), "lon": float(longitude_depart)},
201
- {"lat": float(coord_route[1]['latitude']), "lon": float(coord_route[1]['longitude'])},
202
- {"lat": float(coord_route[2]['latitude']), "lon": float(coord_route[2]['longitude'])},
203
- {"lat": float(coord_route[3]['latitude']), "lon": float(coord_route[3]['longitude'])},
204
- {"lat": float(coord_route[4]['latitude']), "lon": float(coord_route[4]['longitude'])},
205
- {"lat": float(coord_route[5]['latitude']), "lon": float(coord_route[5]['longitude'])},
206
- {"lat": float(coord_route[6]['latitude']), "lon": float(coord_route[6]['longitude'])},
207
- {"lat": float(coord_route[7]['latitude']), "lon": float(coord_route[7]['longitude'])},
208
- {"lat": float(coord_route[8]['latitude']), "lon": float(coord_route[8]['longitude'])},
209
- {"lat": float(lat_dest), "lon": float(lon_dest)},
210
- ]
211
- }
212
- }
213
-
214
- # Make the POST request
215
- response = requests.post(url, json=payload)
216
-
217
- # Check if the request was successful
218
- if response.status_code == 200:
219
- # Parse the JSON response
220
- data = response.json()
221
- print(json.dumps(data, indent=4))
222
- else:
223
- print('Failed to retrieve data:', response.status_code)
224
- answer = ""
225
- for result in data['results']:
226
- name = result['poi']['name']
227
- address = result['address']['freeformAddress']
228
- detour_time = result['detourTime']
229
- answer = answer + f" \nAlong the route to {city_destination}, there is the {name} at {address} that would represent a detour of {int(detour_time/60)} minutes."
230
-
231
- return answer
232
-
233
-
234
- #current weather API
235
- def get_weather(city_name:str= "", **kwargs):
236
- """
237
- Returns the CURRENT weather in a specified city.
238
- Args:
239
- city_name (string) : Required. The name of the city.
240
- """
241
- # The endpoint URL provided by WeatherAPI
242
- url = f"http://api.weatherapi.com/v1/current.json?key={WEATHER_API_KEY}&q={city_name}&aqi=no"
243
-
244
- # Make the API request
245
- response = requests.get(url)
246
-
247
- if response.status_code == 200:
248
- # Parse the JSON response
249
- weather_data = response.json()
250
-
251
- # Extracting the necessary pieces of data
252
- location = weather_data['location']['name']
253
- region = weather_data['location']['region']
254
- country = weather_data['location']['country']
255
- time = weather_data['location']['localtime']
256
- temperature_c = weather_data['current']['temp_c']
257
- condition_text = weather_data['current']['condition']['text']
258
- wind_mph = weather_data['current']['wind_mph']
259
- humidity = weather_data['current']['humidity']
260
- feelslike_c = weather_data['current']['feelslike_c']
261
-
262
- # Formulate the sentences
263
- weather_sentences = (
264
- f"The current weather in {location}, {region}, {country} is {condition_text} "
265
- f"with a temperature of {temperature_c}°C that feels like {feelslike_c}°C. "
266
- f"Humidity is at {humidity}%. "
267
- f"Wind speed is {wind_mph} mph."
268
- )
269
- return weather_sentences
270
- else:
271
- # Handle errors
272
- return f"Failed to get weather data: {response.status_code}, {response.text}"
273
-
274
-
275
-
276
- #weather forecast API
277
- def get_forecast(city_name:str= "", when = 0, **kwargs):
278
- """
279
- Returns the weather forecast in a specified number of days for a specified city .
280
- Args:
281
- city_name (string) : Required. The name of the city.
282
- when (int) : Required. in number of days (until the day for which we want to know the forecast) (example: tomorrow is 1, in two days is 2, etc.)
283
- """
284
- #print(when)
285
- when +=1
286
- # The endpoint URL provided by WeatherAPI
287
- url = f"http://api.weatherapi.com/v1/forecast.json?key={WEATHER_API_KEY}&q={city_name}&days={str(when)}&aqi=no"
288
-
289
-
290
- # Make the API request
291
- response = requests.get(url)
292
-
293
- if response.status_code == 200:
294
- # Parse the JSON response
295
- data = response.json()
296
-
297
- # Initialize an empty string to hold our result
298
- forecast_sentences = ""
299
-
300
- # Extract city information
301
- location = data.get('location', {})
302
- city_name = location.get('name', 'the specified location')
303
-
304
- #print(data)
305
-
306
-
307
- # Extract the forecast days
308
- forecast_days = data.get('forecast', {}).get('forecastday', [])[when-1:]
309
- #number = 0
310
-
311
- #print (forecast_days)
312
-
313
- for day in forecast_days:
314
- date = day.get('date', 'a specific day')
315
- conditions = day.get('day', {}).get('condition', {}).get('text', 'weather conditions')
316
- max_temp_c = day.get('day', {}).get('maxtemp_c', 'N/A')
317
- min_temp_c = day.get('day', {}).get('mintemp_c', 'N/A')
318
- chance_of_rain = day.get('day', {}).get('daily_chance_of_rain', 'N/A')
319
-
320
- if when == 1:
321
- number_str = 'today'
322
- elif when == 2:
323
- number_str = 'tomorrow'
324
- else:
325
- number_str = f'in {when-1} days'
326
-
327
- # Generate a sentence for the day's forecast
328
- forecast_sentence = f"On {date} ({number_str}) in {city_name}, the weather will be {conditions} with a high of {max_temp_c}°C and a low of {min_temp_c}°C. There's a {chance_of_rain}% chance of rain. "
329
-
330
- #number = number + 1
331
- # Add the sentence to the result
332
- forecast_sentences += forecast_sentence
333
- return forecast_sentences
334
- else:
335
- # Handle errors
336
- print( f"Failed to get weather data: {response.status_code}, {response.text}")
337
- return f'error {response.status_code}'
338
-
339
 
 
 
 
1
  from geopy.geocoders import Nominatim
2
 
3
 
 
51
  lon = coord.longitude
52
  return lat, lon, city
53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
car_assistant_eta.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
car_assistant_slim.ipynb CHANGED
@@ -19,13 +19,11 @@
19
  "name": "stderr",
20
  "output_type": "stream",
21
  "text": [
22
- "/opt/conda/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
23
- " from .autonotebook import tqdm as notebook_tqdm\n",
24
- "/opt/conda/lib/python3.10/site-packages/transformers/utils/generic.py:441: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
25
  " _torch_pytree._register_pytree_node(\n",
26
- "/opt/conda/lib/python3.10/site-packages/transformers/utils/generic.py:309: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
27
  " _torch_pytree._register_pytree_node(\n",
28
- "/opt/conda/lib/python3.10/site-packages/transformers/utils/generic.py:309: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
29
  " _torch_pytree._register_pytree_node(\n"
30
  ]
31
  }
@@ -116,21 +114,35 @@
116
  "name": "stderr",
117
  "output_type": "stream",
118
  "text": [
119
- "Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.\n"
 
 
 
 
 
 
 
 
 
 
 
120
  ]
121
  },
122
  {
123
  "name": "stdout",
124
  "output_type": "stream",
125
  "text": [
126
- " > tts_models/multilingual/multi-dataset/xtts_v1.1 is already downloaded.\n"
127
  ]
128
  },
129
  {
130
  "name": "stderr",
131
  "output_type": "stream",
132
  "text": [
133
- "/opt/conda/lib/python3.10/site-packages/transformers/utils/generic.py:309: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
 
 
 
134
  " _torch_pytree._register_pytree_node(\n"
135
  ]
136
  },
@@ -138,6 +150,8 @@
138
  "name": "stdout",
139
  "output_type": "stream",
140
  "text": [
 
 
141
  " > Using model: xtts\n"
142
  ]
143
  }
@@ -160,7 +174,20 @@
160
  "collapsed": true,
161
  "id": "JNALTDb0LT90"
162
  },
163
- "outputs": [],
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  "source": [
165
  "#load model language recognition\n",
166
  "model_ckpt = \"papluca/xlm-roberta-base-language-detection\"\n",
@@ -183,8 +210,10 @@
183
  "source": [
184
  "#load model llama2\n",
185
  "mn = 'stabilityai/StableBeluga-7B' #mn = \"TheBloke/Llama-2-7b-Chat-GPTQ\" --> other possibility \n",
186
- "model = AutoModelForCausalLM.from_pretrained(mn, device_map=0, load_in_4bit=True) #torch_dtype=torch.float16\n",
187
- "tokr = AutoTokenizer.from_pretrained(mn, load_in_4bit=True) #tokenizer"
 
 
188
  ]
189
  },
190
  {
@@ -881,7 +910,7 @@
881
  "name": "python",
882
  "nbconvert_exporter": "python",
883
  "pygments_lexer": "ipython3",
884
- "version": "3.10.13"
885
  }
886
  },
887
  "nbformat": 4,
 
19
  "name": "stderr",
20
  "output_type": "stream",
21
  "text": [
22
+ "/opt/homebrew/Caskroom/miniconda/base/envs/llm/lib/python3.11/site-packages/transformers/utils/generic.py:441: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
 
 
23
  " _torch_pytree._register_pytree_node(\n",
24
+ "/opt/homebrew/Caskroom/miniconda/base/envs/llm/lib/python3.11/site-packages/transformers/utils/generic.py:309: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
25
  " _torch_pytree._register_pytree_node(\n",
26
+ "/opt/homebrew/Caskroom/miniconda/base/envs/llm/lib/python3.11/site-packages/transformers/utils/generic.py:309: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
27
  " _torch_pytree._register_pytree_node(\n"
28
  ]
29
  }
 
114
  "name": "stderr",
115
  "output_type": "stream",
116
  "text": [
117
+ "preprocessor_config.json: 100%|██████████| 185k/185k [00:00<00:00, 94.3MB/s]\n",
118
+ "tokenizer_config.json: 100%|██████████| 283k/283k [00:00<00:00, 1.05MB/s]\n",
119
+ "vocab.json: 100%|██████████| 836k/836k [00:00<00:00, 3.03MB/s]\n",
120
+ "tokenizer.json: 100%|██████████| 2.48M/2.48M [00:00<00:00, 50.6MB/s]\n",
121
+ "merges.txt: 100%|██████████| 494k/494k [00:00<00:00, 28.8MB/s]\n",
122
+ "normalizer.json: 100%|██████████| 52.7k/52.7k [00:00<00:00, 67.8MB/s]\n",
123
+ "added_tokens.json: 100%|██████████| 34.6k/34.6k [00:00<00:00, 38.7MB/s]\n",
124
+ "special_tokens_map.json: 100%|██████████| 2.19k/2.19k [00:00<00:00, 8.88MB/s]\n",
125
+ "Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.\n",
126
+ "config.json: 100%|██████████| 1.97k/1.97k [00:00<00:00, 4.46MB/s]\n",
127
+ "model.safetensors: 100%|██████████| 967M/967M [00:12<00:00, 74.9MB/s] \n",
128
+ "generation_config.json: 100%|██████████| 3.87k/3.87k [00:00<00:00, 39.0MB/s]\n"
129
  ]
130
  },
131
  {
132
  "name": "stdout",
133
  "output_type": "stream",
134
  "text": [
135
+ " > Downloading model to /Users/sasan.jafarnejad/Library/Application Support/tts/tts_models--multilingual--multi-dataset--xtts_v1.1\n"
136
  ]
137
  },
138
  {
139
  "name": "stderr",
140
  "output_type": "stream",
141
  "text": [
142
+ "100%|██████████| 1.87G/1.87G [00:24<00:00, 75.6MiB/s]\n",
143
+ "100%|██████████| 4.70k/4.70k [00:00<00:00, 17.9kiB/s]\n",
144
+ "100%|██████████| 294k/294k [00:00<00:00, 1.23MiB/s]\n",
145
+ "/opt/homebrew/Caskroom/miniconda/base/envs/llm/lib/python3.11/site-packages/transformers/utils/generic.py:309: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
146
  " _torch_pytree._register_pytree_node(\n"
147
  ]
148
  },
 
150
  "name": "stdout",
151
  "output_type": "stream",
152
  "text": [
153
+ " > Model's license - CPML\n",
154
+ " > Check https://coqui.ai/cpml.txt for more info.\n",
155
  " > Using model: xtts\n"
156
  ]
157
  }
 
174
  "collapsed": true,
175
  "id": "JNALTDb0LT90"
176
  },
177
+ "outputs": [
178
+ {
179
+ "name": "stderr",
180
+ "output_type": "stream",
181
+ "text": [
182
+ "config.json: 100%|█████████��| 1.42k/1.42k [00:00<00:00, 3.24MB/s]\n",
183
+ "model.safetensors: 100%|██████████| 1.11G/1.11G [00:13<00:00, 79.5MB/s]\n",
184
+ "tokenizer_config.json: 100%|██████████| 502/502 [00:00<00:00, 5.01MB/s]\n",
185
+ "sentencepiece.bpe.model: 100%|██████████| 5.07M/5.07M [00:00<00:00, 78.4MB/s]\n",
186
+ "tokenizer.json: 100%|██████████| 9.08M/9.08M [00:00<00:00, 61.5MB/s]\n",
187
+ "special_tokens_map.json: 100%|██████████| 239/239 [00:00<00:00, 372kB/s]\n"
188
+ ]
189
+ }
190
+ ],
191
  "source": [
192
  "#load model language recognition\n",
193
  "model_ckpt = \"papluca/xlm-roberta-base-language-detection\"\n",
 
210
  "source": [
211
  "#load model llama2\n",
212
  "mn = 'stabilityai/StableBeluga-7B' #mn = \"TheBloke/Llama-2-7b-Chat-GPTQ\" --> other possibility \n",
213
+ "# model = AutoModelForCausalLM.from_pretrained(mn, device_map=0, load_in_4bit=True) #torch_dtype=torch.float16\n",
214
+ "model = AutoModelForCausalLM.from_pretrained(mn, device_map=0) #torch_dtype=torch.float16\n",
215
+ "# tokr = AutoTokenizer.from_pretrained(mn, load_in_4bit=True) #tokenizer\n",
216
+ "tokr = AutoTokenizer.from_pretrained(mn) #tokenizer"
217
  ]
218
  },
219
  {
 
910
  "name": "python",
911
  "nbconvert_exporter": "python",
912
  "pygments_lexer": "ipython3",
913
+ "version": "3.11.8"
914
  }
915
  },
916
  "nbformat": 4,
car_assistant_text.ipynb ADDED
The diff for this file is too large to render. See raw diff
 
core/__init__.py ADDED
@@ -0,0 +1,199 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from collections import namedtuple
3
+ import time
4
+ import pathlib
5
+ from typing import List
6
+
7
+ import numpy as np
8
+ import torch
9
+ from TTS.api import TTS
10
+
11
+ os.environ["COQUI_TOS_AGREED"] = "1"
12
+
13
+
14
+ Voice = namedtuple("voice", ["name", "neutral", "angry", "speed"])
15
+
16
+ file_full_path = pathlib.Path(os.path.realpath(__file__)).parent
17
+
18
+ voices = [
19
+ Voice(
20
+ "Attenborough",
21
+ neutral=f"{file_full_path}/audio/attenborough/neutral.wav",
22
+ angry=None,
23
+ speed=1.1,
24
+ ),
25
+ Voice(
26
+ "Rick",
27
+ neutral=f"{file_full_path}/audio/rick/neutral.wav",
28
+ angry=None,
29
+ speed=1.1,
30
+ ),
31
+ Voice(
32
+ "Freeman",
33
+ neutral=f"{file_full_path}/audio/freeman/neutral.wav",
34
+ angry="audio/freeman/angry.wav",
35
+ speed=1.1,
36
+ ),
37
+ Voice(
38
+ "Walken",
39
+ neutral=f"{file_full_path}/audio/walken/neutral.wav",
40
+ angry=None,
41
+ speed=1.1,
42
+ ),
43
+ Voice(
44
+ "Darth Wader",
45
+ neutral=f"{file_full_path}/audio/darth/neutral.wav",
46
+ angry=None,
47
+ speed=1.1,
48
+ ),
49
+ ]
50
+
51
+
52
+ def load_tts_pipeline():
53
+ # load model for text to speech
54
+ device = "cuda" if torch.cuda.is_available() else "cpu"
55
+ # device = "mps"
56
+ tts_pipeline = TTS("tts_models/multilingual/multi-dataset/xtts_v2").to(device)
57
+ return tts_pipeline
58
+
59
+
60
+ def compute_speaker_embedding(voice_path: str, config, pipeline, cache):
61
+ if voice_path not in cache:
62
+ cache[voice_path] = pipeline.synthesizer.tts_model.get_conditioning_latents(
63
+ audio_path=voice_path,
64
+ gpt_cond_len=config.gpt_cond_len,
65
+ gpt_cond_chunk_len=config.gpt_cond_chunk_len,
66
+ max_ref_length=config.max_ref_len,
67
+ sound_norm_refs=config.sound_norm_refs,
68
+ )
69
+ return cache[voice_path]
70
+
71
+
72
+ voice_options = []
73
+ for voice in voices:
74
+ if voice.neutral:
75
+ voice_options.append(f"{voice.name} - Neutral")
76
+ if voice.angry:
77
+ voice_options.append(f"{voice.name} - Angry")
78
+
79
+
80
+ def voice_from_text(voice):
81
+ for v in voices:
82
+ if voice == f"{v.name} - Neutral":
83
+ return v.neutral
84
+ if voice == f"{v.name} - Angry":
85
+ return v.angry
86
+ raise ValueError(f"Voice {voice} not found.")
87
+
88
+
89
+ def speed_from_text(voice):
90
+ for v in voices:
91
+ if voice == f"{v.name} - Neutral":
92
+ return v.speed
93
+ if voice == f"{v.name} - Angry":
94
+ return v.speed
95
+
96
+
97
+ def tts(
98
+ self,
99
+ text: str = "",
100
+ language_name: str = "",
101
+ reference_wav=None,
102
+ gpt_cond_latent=None,
103
+ speaker_embedding=None,
104
+ split_sentences: bool = True,
105
+ **kwargs,
106
+ ) -> List[int]:
107
+ """🐸 TTS magic. Run all the models and generate speech.
108
+
109
+ Args:
110
+ text (str): input text.
111
+ speaker_name (str, optional): speaker id for multi-speaker models. Defaults to "".
112
+ language_name (str, optional): language id for multi-language models. Defaults to "".
113
+ speaker_wav (Union[str, List[str]], optional): path to the speaker wav for voice cloning. Defaults to None.
114
+ style_wav ([type], optional): style waveform for GST. Defaults to None.
115
+ style_text ([type], optional): transcription of style_wav for Capacitron. Defaults to None.
116
+ reference_wav ([type], optional): reference waveform for voice conversion. Defaults to None.
117
+ reference_speaker_name ([type], optional): speaker id of reference waveform. Defaults to None.
118
+ split_sentences (bool, optional): split the input text into sentences. Defaults to True.
119
+ **kwargs: additional arguments to pass to the TTS model.
120
+ Returns:
121
+ List[int]: [description]
122
+ """
123
+ start_time = time.time()
124
+ use_gl = self.vocoder_model is None
125
+ wavs = []
126
+
127
+ if not text and not reference_wav:
128
+ raise ValueError(
129
+ "You need to define either `text` (for sythesis) or a `reference_wav` (for voice conversion) to use the Coqui TTS API."
130
+ )
131
+
132
+ if text:
133
+ sens = [text]
134
+ if split_sentences:
135
+ print(" > Text splitted to sentences.")
136
+ sens = self.split_into_sentences(text)
137
+ print(sens)
138
+
139
+ if not reference_wav: # not voice conversion
140
+ for sen in sens:
141
+ outputs = self.tts_model.inference(
142
+ sen,
143
+ language_name,
144
+ gpt_cond_latent,
145
+ speaker_embedding,
146
+ # GPT inference
147
+ temperature=0.75,
148
+ length_penalty=1.0,
149
+ repetition_penalty=10.0,
150
+ top_k=50,
151
+ top_p=0.85,
152
+ do_sample=True,
153
+ **kwargs,
154
+ )
155
+ waveform = outputs["wav"]
156
+ if (
157
+ torch.is_tensor(waveform)
158
+ and waveform.device != torch.device("cpu")
159
+ and not use_gl
160
+ ):
161
+ waveform = waveform.cpu()
162
+ if not use_gl:
163
+ waveform = waveform.numpy()
164
+ waveform = waveform.squeeze()
165
+
166
+ # # trim silence
167
+ # if (
168
+ # "do_trim_silence" in self.tts_config.audio
169
+ # and self.tts_config.audio["do_trim_silence"]
170
+ # ):
171
+ # waveform = trim_silence(waveform, self.tts_model.ap)
172
+
173
+ wavs += list(waveform)
174
+ wavs += [0] * 10000
175
+
176
+ # compute stats
177
+ process_time = time.time() - start_time
178
+ audio_time = len(wavs) / self.tts_config.audio["sample_rate"]
179
+ print(f" > Processing time: {process_time}")
180
+ print(f" > Real-time factor: {process_time / audio_time}")
181
+ return wavs
182
+
183
+
184
+ def tts_gradio(tts_pipeline, text, voice, cache):
185
+ voice_path = voice_from_text(voice)
186
+ (gpt_cond_latent, speaker_embedding) = compute_speaker_embedding(
187
+ voice_path, tts_pipeline.synthesizer.tts_config, tts_pipeline, cache
188
+ )
189
+ out = tts(
190
+ tts_pipeline.synthesizer,
191
+ text,
192
+ language_name="en",
193
+ speaker=None,
194
+ gpt_cond_latent=gpt_cond_latent,
195
+ speaker_embedding=speaker_embedding,
196
+ speed=1.1,
197
+ # file_path="out.wav",
198
+ )
199
+ return (22050, np.array(out)), dict(text=text, voice=voice)
kitt.py ADDED
@@ -0,0 +1,331 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import gradio as gr
3
+ import numpy as np
4
+ import requests
5
+ import torch
6
+ import torchaudio
7
+ from transformers import pipeline
8
+
9
+
10
+
11
+ import skills
12
+ from skills.common import config, vehicle
13
+ from skills.routing import calculate_route
14
+ import ollama
15
+
16
+ ### LLM Stuff ###
17
+ from langchain_community.llms import Ollama
18
+ from langchain.tools.base import StructuredTool
19
+
20
+ from skills import (
21
+ get_weather,
22
+ find_route,
23
+ get_forecast,
24
+ vehicle_status as vehicle_status_fn,
25
+ search_points_of_interests,
26
+ search_along_route_w_coordinates,
27
+ do_anything_else,
28
+ date_time_info
29
+ )
30
+ from skills import extract_func_args
31
+ from core import voice_options, load_tts_pipeline, tts_gradio
32
+
33
+
34
+ global_context = {
35
+ "vehicle": vehicle,
36
+ "query": "How is the weather?",
37
+ "route_points": [],
38
+ }
39
+
40
+ speaker_embedding_cache = {}
41
+
42
+ MODEL_FUNC = "nexusraven"
43
+ MODEL_GENERAL = "llama3:instruct"
44
+
45
+ RAVEN_PROMPT_FUNC = """You are a helpful AI assistant in a car (vehicle), that follows instructions extremely well. \
46
+ Answer questions concisely and do not mention what you base your reply on."
47
+
48
+ {raven_tools}
49
+
50
+ {history}
51
+
52
+ User Query: Question: {input}<human_end>
53
+ """
54
+
55
+ def get_prompt(template, input, history, tools):
56
+ # "vehicle_status": vehicle_status_fn()[0]
57
+ kwargs = {"history": history, "input": input}
58
+ prompt = "<human>:\n"
59
+ for tool in tools:
60
+ func_signature, func_docstring = tool.description.split(" - ", 1)
61
+ prompt += f'Function:\n<func_start>def {func_signature}<func_end>\n<docstring_start>\n"""\n{func_docstring}\n"""\n<docstring_end>\n'
62
+ kwargs["raven_tools"] = prompt
63
+
64
+ if history:
65
+ kwargs["history"] = f"Previous conversation history:{history}\n"
66
+
67
+ return template.format(**kwargs).replace("{{", "{").replace("}}", "}")
68
+
69
+ def use_tool(func_name, kwargs, tools):
70
+ for tool in tools:
71
+ if tool.name == func_name:
72
+ return tool.invoke(input=kwargs)
73
+ return None
74
+
75
+ # llm = Ollama(model="nexusraven", stop=["\nReflection:", "\nThought:"], keep_alive=60*10)
76
+
77
+
78
+ # Generate options for hours (00-23)
79
+ hour_options = [f"{i:02d}:00:00" for i in range(24)]
80
+
81
+
82
+ def search_along_route(query=""):
83
+ """Search for points of interest along the route/way to the destination.
84
+
85
+ Args:
86
+ query (str, optional): The type of point of interest to search for. Defaults to "restaurant".
87
+
88
+ """
89
+ points = global_context["route_points"]
90
+ # maybe reshape
91
+ return search_along_route_w_coordinates(points, query)
92
+
93
+
94
+ def set_time(time_picker):
95
+ vehicle.time = time_picker
96
+ return vehicle.model_dump_json()
97
+
98
+
99
+ def get_vehicle_status(state):
100
+ return state.value["vehicle"].model_dump_json()
101
+
102
+
103
+ tools = [
104
+ StructuredTool.from_function(get_weather),
105
+ StructuredTool.from_function(find_route),
106
+ # StructuredTool.from_function(vehicle_status),
107
+ StructuredTool.from_function(search_points_of_interests),
108
+ StructuredTool.from_function(search_along_route),
109
+ StructuredTool.from_function(date_time_info),
110
+ StructuredTool.from_function(do_anything_else),
111
+ ]
112
+
113
+
114
+ def run_generic_model(query):
115
+ print(f"Running the generic model with query: {query}")
116
+ data = {
117
+ "prompt": f"Answer the question below in a short and concise manner.\n{query}",
118
+ "model": MODEL_GENERAL,
119
+ "options": {
120
+ # "temperature": 0.1,
121
+ # "stop":["\nReflection:", "\nThought:"]
122
+ }
123
+ }
124
+ out = ollama.generate(**data)
125
+ return out["response"]
126
+
127
+
128
+ def run_model(query, voice_character):
129
+ query = query.strip().replace("'", "")
130
+ print("Query: ", query)
131
+ global_context["query"] = query
132
+ global_context["prompt"] = get_prompt(RAVEN_PROMPT_FUNC, query, "", tools)
133
+ print("Prompt: ", global_context["prompt"])
134
+ data = {
135
+ "prompt": global_context["prompt"],
136
+ # "streaming": False,
137
+ "model": "nexusraven",
138
+ # "model": "smangrul/llama-3-8b-instruct-function-calling",
139
+ "raw": True,
140
+ "options": {
141
+ "temperature": 0.5,
142
+ "stop":["\nReflection:", "\nThought:"]
143
+ }
144
+ }
145
+ out = ollama.generate(**data)
146
+ llm_response = out["response"]
147
+ if "Call: " in llm_response:
148
+ print(f"llm_response: {llm_response}")
149
+ llm_response = llm_response.replace("<bot_end>"," ")
150
+ func_name, kwargs = extract_func_args(llm_response)
151
+ print(f"Function: {func_name}, Args: {kwargs}")
152
+ if func_name == "do_anything_else":
153
+ output_text = run_generic_model(query)
154
+ else:
155
+ output_text = use_tool(func_name, kwargs, tools)
156
+ else:
157
+ output_text = out["response"]
158
+
159
+ if type(output_text) == tuple:
160
+ output_text = output_text[0]
161
+ gr.Info(f"Output text: {output_text}, generating voice output...")
162
+ return output_text, tts_gradio(tts_pipeline, output_text, voice_character, speaker_embedding_cache)[0]
163
+
164
+
165
+ def calculate_route_gradio(origin, destination):
166
+ plot, vehicle_status, points = calculate_route(origin, destination)
167
+ global_context["route_points"] = points
168
+ vehicle.location_coordinates = points[0]["latitude"], points[0]["longitude"]
169
+ return plot, vehicle_status
170
+
171
+
172
+ def update_vehicle_status(trip_progress):
173
+ n_points = len(global_context["route_points"])
174
+ new_coords = global_context["route_points"][min(int(trip_progress / 100 * n_points), n_points - 1)]
175
+ new_coords = new_coords["latitude"], new_coords["longitude"]
176
+ print(f"Trip progress: {trip_progress}, len: {n_points}, new_coords: {new_coords}")
177
+ vehicle.location_coordinates = new_coords
178
+ vehicle.location = ""
179
+ return vehicle.model_dump_json()
180
+
181
+
182
+ device = "cuda" if torch.cuda.is_available() else "cpu"
183
+ transcriber = pipeline("automatic-speech-recognition", model="openai/whisper-base.en", device=device)
184
+
185
+
186
+ def save_audio_as_wav(data, sample_rate, file_path):
187
+ # make a tensor from the numpy array
188
+ data = torch.tensor(data).reshape(1, -1)
189
+ torchaudio.save(file_path, data, sample_rate=sample_rate, bits_per_sample=16, encoding="PCM_S")
190
+
191
+
192
+ def save_and_transcribe_audio(audio):
193
+ try:
194
+ # capture the audio and save it to a file as wav or mp3
195
+ # file_name = save("audioinput.wav")
196
+ sr, y = audio
197
+ # y = y.astype(np.float32)
198
+ # y /= np.max(np.abs(y))
199
+
200
+ # add timestamp to file name
201
+ filename = f"recordings/audio{time.time()}.wav"
202
+ save_audio_as_wav(y, sr, filename)
203
+
204
+ sr, y = audio
205
+ y = y.astype(np.float32)
206
+ y /= np.max(np.abs(y))
207
+ text = transcriber({"sampling_rate": sr, "raw":y})["text"]
208
+ except Exception as e:
209
+ print(f"Error: {e}")
210
+ return "Error transcribing audio"
211
+ return text
212
+
213
+ # to be able to use the microphone on chrome, you will have to go to chrome://flags/#unsafely-treat-insecure-origin-as-secure and enter http://10.186.115.21:7860/
214
+ # in "Insecure origins treated as secure", enable it and relaunch chrome
215
+
216
+ # example question:
217
+ # what's the weather like outside?
218
+ # What's the closest restaurant from here?
219
+
220
+
221
+ tts_pipeline = load_tts_pipeline()
222
+
223
+
224
+ with gr.Blocks(theme=gr.themes.Default()) as demo:
225
+ state = gr.State(
226
+ value={
227
+ # "context": initial_context,
228
+ "query": "",
229
+ "route_points": [],
230
+ }
231
+ )
232
+ trip_points = gr.State(value=[])
233
+
234
+ with gr.Row():
235
+ with gr.Column(scale=1, min_width=300):
236
+ time_picker = gr.Dropdown(
237
+ choices=hour_options,
238
+ label="What time is it? (HH:MM)",
239
+ value="08:00:00",
240
+ interactive=True,
241
+ )
242
+ history = gr.Radio(
243
+ ["Yes", "No"],
244
+ label="Maintain the conversation history?",
245
+ value="No",
246
+ interactive=True,
247
+ )
248
+ voice_character = gr.Radio(choices=voice_options, label='Choose a voice', value=voice_options[0], show_label=True)
249
+ origin = gr.Textbox(
250
+ value="Mondorf-les-Bains, Luxembourg", label="Origin", interactive=True
251
+ )
252
+ destination = gr.Textbox(
253
+ value="Rue Alphonse Weicker, Luxembourg",
254
+ label="Destination",
255
+ interactive=True,
256
+ )
257
+
258
+ with gr.Column(scale=2, min_width=600):
259
+ map_plot = gr.Plot()
260
+ trip_progress = gr.Slider(0, 100, step=5, label="Trip progress", interactive=True)
261
+
262
+ # map_if = gr.Interface(fn=plot_map, inputs=year_input, outputs=map_plot)
263
+
264
+ with gr.Row():
265
+ with gr.Column():
266
+ input_audio = gr.Audio(
267
+ type="numpy",sources=["microphone"], label="Input audio", elem_id="input_audio"
268
+ )
269
+ input_text = gr.Textbox(
270
+ value="How is the weather?", label="Input text", interactive=True
271
+ )
272
+ vehicle_status = gr.JSON(
273
+ value=vehicle.model_dump_json(), label="Vehicle status"
274
+ )
275
+ with gr.Column():
276
+ output_audio = gr.Audio(label="output audio", autoplay=True)
277
+ output_text = gr.TextArea(value="", label="Output text", interactive=False)
278
+ # iface = gr.Interface(
279
+ # fn=transcript,
280
+ # inputs=[
281
+ # gr.Textbox(value=initial_context, visible=False),
282
+ # gr.Audio(type="filepath", label="input audio", elem_id="recorder"),
283
+ # voice_character,
284
+ # emotion,
285
+ # place,
286
+ # time_picker,
287
+ # history,
288
+ # gr.State(), # This will keep track of the context state across interactions.
289
+ # ],
290
+ # outputs=[gr.Audio(label="output audio"), gr.Textbox(visible=False), gr.State()],
291
+ # head=shortcut_js,
292
+ # )
293
+
294
+ # Update plot based on the origin and destination
295
+ # Sets the current location and destination
296
+ origin.submit(
297
+ fn=calculate_route_gradio,
298
+ inputs=[origin, destination],
299
+ outputs=[map_plot, vehicle_status],
300
+ )
301
+ destination.submit(
302
+ fn=calculate_route_gradio,
303
+ inputs=[origin, destination],
304
+ outputs=[map_plot, vehicle_status],
305
+ )
306
+
307
+ # Update time based on the time picker
308
+ time_picker.select(fn=set_time, inputs=[time_picker], outputs=[vehicle_status])
309
+
310
+ # Run the model if the input text is changed
311
+ input_text.submit(fn=run_model, inputs=[input_text, voice_character], outputs=[output_text, output_audio])
312
+
313
+ # Set the vehicle status based on the trip progress
314
+ trip_progress.release(
315
+ fn=update_vehicle_status, inputs=[trip_progress], outputs=[vehicle_status]
316
+ )
317
+
318
+ # Save and transcribe the audio
319
+ input_audio.stop_recording(
320
+ fn=save_and_transcribe_audio, inputs=[input_audio], outputs=[input_text]
321
+ )
322
+
323
+ # close all interfaces open to make the port available
324
+ gr.close_all()
325
+ # Launch the interface.
326
+
327
+ if __name__ == "__main__":
328
+ # demo.launch(debug=True, server_name="0.0.0.0", server_port=7860, ssl_verify=False)
329
+ demo.launch(debug=True, server_name="0.0.0.0", server_port=7860, ssl_verify=True, share=True)
330
+
331
+ # iface.launch(debug=True, share=False, server_name="0.0.0.0", server_port=7860, ssl_verify=False)
requirements.txt CHANGED
@@ -1,11 +1,12 @@
1
  torch
2
  torchvision
 
3
  matplotlib
4
  wurlitzer
5
  accelerate
6
  bitsandbytes
7
  optimum
8
- auto-gptq
9
  gradio
10
  TTS
11
  transformers==4.36
@@ -14,4 +15,8 @@ openai-whisper
14
  geopy
15
  langchain
16
  text_generation
17
- python-dotenv
 
 
 
 
 
1
  torch
2
  torchvision
3
+ torchaudio
4
  matplotlib
5
  wurlitzer
6
  accelerate
7
  bitsandbytes
8
  optimum
9
+ # auto-gptq
10
  gradio
11
  TTS
12
  transformers==4.36
 
15
  geopy
16
  langchain
17
  text_generation
18
+ python-dotenv
19
+ pydantic-settings
20
+ ollama
21
+ langchain
22
+ plotly-express
skills/__init__.py ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ import inspect
3
+
4
+ from .common import execute_function_call, extract_func_args, vehicle as vehicle_obj
5
+ from .weather import get_weather, get_forecast
6
+ from .routing import find_route
7
+ from .poi import search_points_of_interests, search_along_route_w_coordinates
8
+ from .vehicle import vehicle_status
9
+
10
+
11
+
12
+ def date_time_info():
13
+ """Get the current date and time."""
14
+ time = getattr(vehicle_obj, "time")
15
+ date = getattr(vehicle_obj, "date")
16
+ datetime_obj = datetime.fromisoformat(f"{date}T{time}")
17
+ human_readable_datetime = datetime_obj.strftime("%I:%M %p %A, %B %d, %Y")
18
+ return f"It is {human_readable_datetime}."
19
+
20
+
21
+ def do_anything_else():
22
+ """If the user wants to do anything else call this function. If the question doesn't match any of the functions use this one."""
23
+ return True
24
+
25
+
26
+
27
+ def format_functions_for_prompt_raven(*functions):
28
+ """Format functions for use in Prompt Raven.
29
+
30
+ Args:
31
+ *functions (function): One or more functions to format.
32
+ """
33
+ formatted_functions = []
34
+ for func in functions:
35
+ signature = f"{func.__name__}{inspect.signature(func)}"
36
+ docstring = inspect.getdoc(func)
37
+ formatted_functions.append(
38
+ f"Function:\n<func_start>{signature}<func_end>\n<docstring_start>\n{docstring}\n<docstring_end>"
39
+ )
40
+ return "\n".join(formatted_functions)
41
+
42
+
43
+ SKILLS_PROMPT = format_functions_for_prompt_raven(get_weather, get_forecast, find_route, search_points_of_interests)
skills/common.py ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from typing import Union
3
+
4
+
5
+ from pydantic_settings import BaseSettings, SettingsConfigDict
6
+ from pydantic import BaseModel
7
+
8
+ import skills
9
+
10
+ class Settings(BaseSettings):
11
+ WEATHER_API_KEY: str
12
+ TOMTOM_API_KEY: str
13
+
14
+ model_config = SettingsConfigDict(env_file=".env")
15
+
16
+
17
+ class VehicleStatus(BaseModel):
18
+ location: str
19
+ location_coordinates: tuple[float, float] # (latitude, longitude)
20
+ date: str
21
+ time: str
22
+ destination: str
23
+
24
+
25
+ def execute_function_call(text: str, dry_run=False) -> str:
26
+ function_name_match = re.search(r"Call: (\w+)", text)
27
+ function_name = function_name_match.group(1) if function_name_match else None
28
+ arguments = eval(f"dict{text.split(function_name)[1].strip()}")
29
+ function = getattr(skills, function_name) if function_name else None
30
+
31
+ if dry_run:
32
+ print(f"{function_name}(**{arguments})")
33
+ return "Dry run successful"
34
+
35
+ if function:
36
+ out = function(**arguments)
37
+ try:
38
+ if function:
39
+ out = function(**arguments)
40
+ except Exception as e:
41
+ out = str(e)
42
+ return out
43
+
44
+
45
+ def extract_func_args(text: str) -> tuple[str, dict]:
46
+ function_name_match = re.search(r"Call: (\w+)", text)
47
+ function_name = function_name_match.group(1) if function_name_match else None
48
+ if not function_name:
49
+ raise ValueError("No function name found in text")
50
+ arguments = eval(f"dict{text.split(function_name)[1].strip()}")
51
+ return function_name, arguments
52
+
53
+
54
+ config = Settings() # type: ignore
55
+
56
+ vehicle = VehicleStatus(
57
+ location="Rue Alphonse Weicker, Luxembourg",
58
+ location_coordinates=(49.505, 6.28111),
59
+ date="2025-05-06",
60
+ time="08:00:00",
61
+ destination="Rue Alphonse Weicker, Luxembourg"
62
+ )
skills/poi.py ADDED
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import requests
3
+ from .common import config, vehicle
4
+
5
+
6
+ # Select coordinates at equal distance, including the last one
7
+ def select_equally_spaced_coordinates(coords, number_of_points=10):
8
+ n = len(coords)
9
+ selected_coords = []
10
+ interval = max((n - 1) / (number_of_points - 1), 1)
11
+ for i in range(number_of_points):
12
+ # Calculate the index, ensuring it doesn't exceed the bounds of the list
13
+ index = int(round(i * interval))
14
+ if index < n:
15
+ selected_coords.append(coords[index])
16
+ return selected_coords
17
+
18
+
19
+ def search_points_of_interests(search_query="french restaurant"):
20
+ """
21
+ Return some of the closest points of interest matching the query.
22
+ :param search_query (string): Required. Describing the type of point of interest depending on what the user wants to do. Make sure to include the type of POI you are looking for. For example italian restaurant, grocery shop, etc.
23
+ """
24
+
25
+ # Extract the latitude and longitude of the vehicle
26
+ vehicle_coordinates = getattr(vehicle, "location_coordinates")
27
+ lat, lon = vehicle_coordinates
28
+ print(f"POI search vehicle's lat: {lat}, lon: {lon}")
29
+
30
+ # https://developer.tomtom.com/search-api/documentation/search-service/search
31
+ r = requests.get(
32
+ f"https://api.tomtom.com/search/2/search/{search_query}.json?key={config.TOMTOM_API_KEY}&lat={lat}&lon={lon}&category&radius=1000&limit=100",
33
+ timeout=5,
34
+ )
35
+
36
+ # Parse JSON from the response
37
+ data = r.json()
38
+ # Extract results
39
+ results = data["results"]
40
+
41
+ # TODO: Handle the no results case.
42
+ if not results:
43
+ return "No results found in the vicinity."
44
+
45
+ # Sort the results based on distance
46
+ results = sorted(results, key=lambda x: x["dist"])
47
+ # print(sorted_results)
48
+
49
+ # Format and limit to top 5 results
50
+ formatted_results = [
51
+ f"{result['poi']['name']}, {int(result['dist'])} meters away"
52
+ for result in results[:3]
53
+ ]
54
+
55
+ output = (
56
+ f"There are {len(results)} options in the vicinity. The most relevant are: "
57
+ )
58
+ return output + ".\n ".join(formatted_results)
59
+
60
+
61
+ def find_points_of_interest(lat="0", lon="0", type_of_poi="restaurant"):
62
+ """
63
+ Return some of the closest points of interest for a specific location and type of point of interest. The more parameters there are, the more precise.
64
+ :param lat (string): latitude
65
+ :param lon (string): longitude
66
+ :param city (string): Required. city
67
+ :param type_of_poi (string): Required. type of point of interest depending on what the user wants to do.
68
+ """
69
+ # https://developer.tomtom.com/search-api/documentation/search-service/points-of-interest-search
70
+ r = requests.get(
71
+ f"https://api.tomtom.com/search/2/search/{type_of_poi}"
72
+ ".json?key={0}&lat={1}&lon={2}&radius=10000&vehicleTypeSet=Car&idxSet=POI&limit=100".format(
73
+ config.TOMTOM_API_KEY, lat, lon
74
+ )
75
+ )
76
+
77
+ # Parse JSON from the response
78
+ data = r.json()
79
+ # print(data)
80
+ # Extract results
81
+ results = data["results"]
82
+
83
+ # Sort the results based on distance
84
+ sorted_results = sorted(results, key=lambda x: x["dist"])
85
+ # print(sorted_results)
86
+
87
+ # Format and limit to top 5 results
88
+ formatted_results = [
89
+ f"The {type_of_poi} {result['poi']['name']}, {int(result['dist'])} meters away"
90
+ for result in sorted_results[:5]
91
+ ]
92
+
93
+ return ". ".join(formatted_results)
94
+
95
+
96
+ def search_along_route_w_coordinates(points: list[tuple[float, float]], query: str):
97
+ """
98
+ Return some of the closest points of interest along the route/way from the depart point, specified by its coordinates.
99
+ :param points (list[tuple(float, float)]): Required. List of tuples of latitude and longitude of the points along the route.
100
+ :param query (string): Required. type of point of interest depending on what the user wants to do.
101
+ """
102
+
103
+ # The API endpoint for searching along a route
104
+ url = f"https://api.tomtom.com/search/2/searchAlongRoute/{query}.json?key={config.TOMTOM_API_KEY}&maxDetourTime=360&limit=20&sortBy=detourTime"
105
+
106
+ points = select_equally_spaced_coordinates(points, number_of_points=20)
107
+
108
+ # The data payload
109
+ payload = {
110
+ "route": {
111
+ "points": [{"lat": pt["latitude"], "lon": pt["longitude"]} for pt in points]
112
+ }
113
+ }
114
+
115
+ # Make the POST request
116
+ response = requests.post(url, json=payload, timeout=5)
117
+
118
+ # Check if the request was successful
119
+ if response.status_code == 200:
120
+ # Parse the JSON response
121
+ data = response.json()
122
+ # print(json.dumps(data, indent=4))
123
+ else:
124
+ print("Failed to retrieve data:", response.status_code)
125
+ return "Failed to retrieve data. Please try again."
126
+ answer = ""
127
+ if not data["results"]:
128
+ return "No results found along the way."
129
+
130
+ if len(data["results"]) == 20:
131
+ answer = "There more than 20 results along the way. Here are the top 3 results:"
132
+ elif len(data["results"]) > 3:
133
+ answer = f"There are {len(data['results'])} results along the way. Here are the top 3 results:"
134
+ for result in data["results"][:3]:
135
+ name = result["poi"]["name"]
136
+ address = result["address"]["freeformAddress"]
137
+ detour_time = result["detourTime"]
138
+ answer = (
139
+ answer
140
+ + f" \n{name} at {address} would require a detour of {int(detour_time/60)} minutes."
141
+ )
142
+
143
+ return answer
skills/routing.py ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from datetime import datetime
2
+ import requests
3
+ from .common import config, vehicle
4
+
5
+
6
+ def find_coordinates(address):
7
+ """
8
+ Find the coordinates of a specific address.
9
+ :param address (string): Required. The address
10
+ """
11
+ # https://developer.tomtom.com/geocoding-api/documentation/geocode
12
+ url = f"https://api.tomtom.com/search/2/geocode/{address}.json?key={config.TOMTOM_API_KEY}"
13
+ response = requests.get(url)
14
+ data = response.json()
15
+ lat = data["results"][0]["position"]["lat"]
16
+ lon = data["results"][0]["position"]["lon"]
17
+ return lat, lon
18
+
19
+
20
+ def plot_route(points):
21
+ import plotly.express as px
22
+
23
+ lats = []
24
+ lons = []
25
+
26
+ for point in points:
27
+ lats.append(point["latitude"])
28
+ lons.append(point["longitude"])
29
+ # fig = px.line_geo(lat=lats, lon=lons)
30
+ # fig.update_geos(fitbounds="locations")
31
+
32
+ fig = px.line_mapbox(
33
+ lat=lats, lon=lons, zoom=12, height=600, color_discrete_sequence=["red"]
34
+ )
35
+
36
+ fig.update_layout(
37
+ mapbox_style="open-street-map",
38
+ # mapbox_zoom=12,
39
+ )
40
+ fig.update_geos(fitbounds="locations")
41
+ fig.update_layout(margin={"r": 20, "t": 20, "l": 20, "b": 20})
42
+ return fig
43
+
44
+
45
+ def calculate_route(origin, destination):
46
+ """This function is called when the origin or destination is updated in the GUI. It calculates the route between the origin and destination."""
47
+ print(f"calculate_route(origin: {origin}, destination: {destination})")
48
+ origin_coords = find_coordinates(origin)
49
+ destination_coords = find_coordinates(destination)
50
+
51
+ orig_coords_str = ",".join(map(str, origin_coords))
52
+ dest_coords_str = ",".join(map(str, destination_coords))
53
+ print(f"origin_coords: {origin_coords}, destination_coords: {destination_coords}")
54
+
55
+ vehicle.destination = destination
56
+ vehicle.location_coordinates = origin_coords
57
+ vehicle.location = origin
58
+
59
+ # origin = "49.631997,6.171029"
60
+ # destination = "49.586745,6.140002"
61
+
62
+ url = f"https://api.tomtom.com/routing/1/calculateRoute/{orig_coords_str}:{dest_coords_str}/json?key={config.TOMTOM_API_KEY}"
63
+ response = requests.get(url)
64
+ data = response.json()
65
+ points = data["routes"][0]["legs"][0]["points"]
66
+
67
+ return plot_route(points), vehicle.model_dump_json(), points
68
+
69
+
70
+ def find_route_tomtom(
71
+ lat_depart="0",
72
+ lon_depart="0",
73
+ lat_dest="0",
74
+ lon_dest="0",
75
+ depart_datetime="",
76
+ **kwargs,
77
+ ):
78
+ """
79
+ Return the distance and the estimated time to go to a specific destination from the current place, at a specified depart time.
80
+ :param lat_depart (string): latitude of depart
81
+ :param lon_depart (string): longitude of depart
82
+ :param lat_dest (string): latitude of destination
83
+ :param lon_dest (string): longitude of destination
84
+ :param depart_time (string): departure hour, in the format '08:00:20'.
85
+ """
86
+ # https://developer.tomtom.com/routing-api/documentation/routing/calculate-route
87
+ # https://developer.tomtom.com/routing-api/documentation/routing/guidance-instructions
88
+ url = f"https://api.tomtom.com/routing/1/calculateRoute/{lat_depart},{lon_depart}:{lat_dest},{lon_dest}/json?key={config.TOMTOM_API_KEY}&departAt={depart_datetime}"
89
+
90
+ print(f"Calling TomTom API: {url}")
91
+ r = requests.get(
92
+ url,
93
+ timeout=5,
94
+ )
95
+
96
+ # Parse JSON from the response
97
+ response = r.json()
98
+
99
+ try:
100
+ result = response["routes"][0]["summary"]
101
+ except KeyError:
102
+ print(f"Failed to find a route: {response}")
103
+ return "Failed to find a route", response
104
+
105
+ distance_m = result["lengthInMeters"]
106
+ duration_s = result["travelTimeInSeconds"]
107
+ arrival_time = result["arrivalTime"]
108
+ # Convert string to datetime object
109
+ arrival_time = datetime.fromisoformat(arrival_time)
110
+
111
+ return {
112
+ "distance_m": distance_m,
113
+ "duration_s": duration_s,
114
+ "arrival_time": arrival_time,
115
+ }, response
116
+
117
+
118
+ def find_route(destination=""):
119
+ """This function finds a route to a destination and returns the distance and the estimated time to go to a specific destination\
120
+ from the current location.
121
+ :param destination (string): Required. The destination
122
+ """
123
+ if not destination:
124
+ destination = vehicle.destination
125
+
126
+ # lat, lon, city = check_city_coordinates(lat_depart,lon_depart,city_depart)
127
+ lat_dest, lon_dest = find_coordinates(destination)
128
+ print(f"lat_dest: {lat_dest}, lon_dest: {lon_dest}")
129
+
130
+ # Extract the latitude and longitude of the vehicle
131
+ vehicle_coordinates = getattr(vehicle, "location_coordinates")
132
+ lat_depart, lon_depart = vehicle_coordinates
133
+ print(f"lat_depart: {lat_depart}, lon_depart: {lon_depart}")
134
+
135
+ date = getattr(vehicle, "date")
136
+ time = getattr(vehicle, "time")
137
+ departure_time = f"{date}T{time}"
138
+
139
+ trip_info, raw_response = find_route_tomtom(
140
+ lat_depart, lon_depart, lat_dest, lon_dest, departure_time
141
+ )
142
+
143
+ distance, duration, arrival_time = (
144
+ trip_info["distance_m"],
145
+ trip_info["duration_s"],
146
+ trip_info["arrival_time"],
147
+ )
148
+
149
+ # Calculate distance in kilometers (1 meter = 0.001 kilometers)
150
+ distance_km = distance * 0.001
151
+ # Calculate travel time in minutes (1 second = 1/60 minutes)
152
+ time_minutes = duration / 60
153
+ if time_minutes < 60:
154
+ time_display = f"{time_minutes:.0f} minutes"
155
+ else:
156
+ hours = int(time_minutes / 60)
157
+ minutes = int(time_minutes % 60)
158
+ time_display = f"{hours} hours" + (
159
+ f" and {minutes} minutes" if minutes > 0 else ""
160
+ )
161
+
162
+ # Extract and display the arrival hour in HH:MM format
163
+ arrival_hour_display = arrival_time.strftime("%H:%M")
164
+
165
+ # return the distance and time
166
+ return f"The route to {destination} is {distance_km:.2f} km which takes {time_display}. Leaving now, the arrival time is estimated at {arrival_hour_display}."
167
+ # raw_response["routes"][0]["legs"][0]["points"]
skills/vehicle.py ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from .common import vehicle
2
+
3
+
4
+ STATUS_TEMPLATE = """
5
+ We are at {location}, coordinates: {lat}, {lon},
6
+ current time: {time}, current date: {date} and our destination is: {destination}.
7
+ """
8
+
9
+
10
+ def vehicle_status() -> tuple[str, dict[str, str]]:
11
+ """Get current vehicle status, which includes, location, date, time, destination.
12
+ Call this to get the current destination or location of the car/vehicle.
13
+ Returns:
14
+ dict[str, str]: The vehicle status. For example:
15
+ {
16
+ "location": "Luxembourg Gare, Luxembourg",
17
+ "lat": 49.6000,
18
+ "lon": 6.1333,
19
+ "date": "2025-03-29",
20
+ "time": "08:00:20",
21
+ "destination": "Kirchberg Campus, Kirchberg"
22
+ }
23
+ """
24
+ # vs = {
25
+ # "location": "Luxembourg Gare, Luxembourg",
26
+ # "lat": 49.6000,
27
+ # "lon": 6.1333,
28
+ # "date": "2025-03-29",
29
+ # "time": "08:00:20",
30
+ # "destination": "Kirchberg Campus, Luxembourg"
31
+ # }
32
+ vs = vehicle.dict()
33
+ vs["lat"] = vs["location_coordinates"][0]
34
+ vs["lon"] = vs["location_coordinates"][1]
35
+ return STATUS_TEMPLATE.format(**vs), vs
skills/weather.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+
3
+ from .common import config, vehicle
4
+
5
+ #current weather API
6
+ def get_weather(location:str= ""):
7
+ """
8
+ Returns the CURRENT weather in a specified location.
9
+ Args:
10
+ location (string) : Required. The name of the location, could be a city or lat/longitude in the following format latitude,longitude (example: 37.7749,-122.4194). If the location is not specified, the function will return the weather in the current location.
11
+ """
12
+
13
+ if location == "":
14
+ print(f"get_weather: location is empty, using the vehicle location. ({vehicle.location})")
15
+ location = vehicle.location
16
+
17
+ # The endpoint URL provided by WeatherAPI
18
+ url = f"http://api.weatherapi.com/v1/current.json?key={config.WEATHER_API_KEY}&q={location}&aqi=no"
19
+ print(url)
20
+
21
+ # Make the API request
22
+ response = requests.get(url)
23
+
24
+ if response.status_code != 200:
25
+ print(f"Failed to get weather data: {response.status_code}, {response.text}")
26
+ return f"Failed to get weather data, try again", response
27
+
28
+ # Parse the JSON response
29
+ weather_data = response.json()
30
+
31
+ # Extracting the necessary pieces of data
32
+ location = weather_data['location']['name']
33
+ region = weather_data['location']['region']
34
+ country = weather_data['location']['country']
35
+ time = weather_data['location']['localtime']
36
+ temperature_c = weather_data['current']['temp_c']
37
+ condition_text = weather_data['current']['condition']['text']
38
+ if 'wind_kph' in weather_data['current']:
39
+ wind_kph = weather_data['current']['wind_kph']
40
+ humidity = weather_data['current']['humidity']
41
+ feelslike_c = weather_data['current']['feelslike_c']
42
+
43
+ # Formulate the sentences - {region}, {country}
44
+ weather_sentences = (
45
+ f"The current weather in {location} is {condition_text} "
46
+ f"with a temperature of {temperature_c}°C that feels like {feelslike_c}°C. "
47
+ # f"Humidity is at {humidity}%. "
48
+ # f"Wind speed is {wind_kph} kph." if 'wind_kph' in weather_data['current'] else ""
49
+ )
50
+ return weather_sentences, weather_data
51
+
52
+ #weather forecast API
53
+ def get_forecast(city_name:str= "", when = 0, **kwargs):
54
+ """
55
+ Returns the weather forecast in a specified number of days for a specified city .
56
+ Args:
57
+ city_name (string) : Required. The name of the city.
58
+ when (int) : Required. in number of days (until the day for which we want to know the forecast) (example: tomorrow is 1, in two days is 2, etc.)
59
+ """
60
+ #print(when)
61
+ when +=1
62
+ # The endpoint URL provided by WeatherAPI
63
+ url = f"http://api.weatherapi.com/v1/forecast.json?key={WEATHER_API_KEY}&q={city_name}&days={str(when)}&aqi=no"
64
+
65
+
66
+ # Make the API request
67
+ response = requests.get(url)
68
+
69
+ if response.status_code == 200:
70
+ # Parse the JSON response
71
+ data = response.json()
72
+
73
+ # Initialize an empty string to hold our result
74
+ forecast_sentences = ""
75
+
76
+ # Extract city information
77
+ location = data.get('location', {})
78
+ city_name = location.get('name', 'the specified location')
79
+
80
+ #print(data)
81
+
82
+
83
+ # Extract the forecast days
84
+ forecast_days = data.get('forecast', {}).get('forecastday', [])[when-1:]
85
+ #number = 0
86
+
87
+ #print (forecast_days)
88
+
89
+ for day in forecast_days:
90
+ date = day.get('date', 'a specific day')
91
+ conditions = day.get('day', {}).get('condition', {}).get('text', 'weather conditions')
92
+ max_temp_c = day.get('day', {}).get('maxtemp_c', 'N/A')
93
+ min_temp_c = day.get('day', {}).get('mintemp_c', 'N/A')
94
+ chance_of_rain = day.get('day', {}).get('daily_chance_of_rain', 'N/A')
95
+
96
+ if when == 1:
97
+ number_str = 'today'
98
+ elif when == 2:
99
+ number_str = 'tomorrow'
100
+ else:
101
+ number_str = f'in {when-1} days'
102
+
103
+ # Generate a sentence for the day's forecast
104
+ forecast_sentence = f"On {date} ({number_str}) in {city_name}, the weather will be {conditions} with a high of {max_temp_c}°C and a low of {min_temp_c}°C. There's a {chance_of_rain}% chance of rain. "
105
+
106
+ #number = number + 1
107
+ # Add the sentence to the result
108
+ forecast_sentences += forecast_sentence
109
+ return forecast_sentences
110
+ else:
111
+ # Handle errors
112
+ print( f"Failed to get weather data: {response.status_code}, {response.text}")
113
+ return f'error {response.status_code}'
stt.ipynb ADDED
@@ -0,0 +1,292 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 25,
6
+ "metadata": {},
7
+ "outputs": [
8
+ {
9
+ "name": "stderr",
10
+ "output_type": "stream",
11
+ "text": [
12
+ "/opt/homebrew/Caskroom/miniconda/base/envs/llm/lib/python3.11/site-packages/transformers/utils/generic.py:441: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
13
+ " _torch_pytree._register_pytree_node(\n",
14
+ "/opt/homebrew/Caskroom/miniconda/base/envs/llm/lib/python3.11/site-packages/transformers/utils/generic.py:309: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
15
+ " _torch_pytree._register_pytree_node(\n",
16
+ "/opt/homebrew/Caskroom/miniconda/base/envs/llm/lib/python3.11/site-packages/transformers/utils/generic.py:309: UserWarning: torch.utils._pytree._register_pytree_node is deprecated. Please use torch.utils._pytree.register_pytree_node instead.\n",
17
+ " _torch_pytree._register_pytree_node(\n"
18
+ ]
19
+ }
20
+ ],
21
+ "source": [
22
+ "#STT (speech to text)\n",
23
+ "from transformers import WhisperProcessor, WhisperForConditionalGeneration\n",
24
+ "from transformers import pipeline\n",
25
+ "import librosa"
26
+ ]
27
+ },
28
+ {
29
+ "cell_type": "code",
30
+ "execution_count": 3,
31
+ "metadata": {},
32
+ "outputs": [
33
+ {
34
+ "name": "stderr",
35
+ "output_type": "stream",
36
+ "text": [
37
+ "Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.\n"
38
+ ]
39
+ }
40
+ ],
41
+ "source": [
42
+ "# load model and processor for speech-to-text\n",
43
+ "processor = WhisperProcessor.from_pretrained(\"openai/whisper-small\")\n",
44
+ "modelw = WhisperForConditionalGeneration.from_pretrained(\"openai/whisper-small\")\n",
45
+ "# modelw.config.forced_decoder_ids = None"
46
+ ]
47
+ },
48
+ {
49
+ "cell_type": "code",
50
+ "execution_count": 51,
51
+ "metadata": {},
52
+ "outputs": [],
53
+ "source": [
54
+ "device = \"cuda\" if torch.cuda.is_available() else \"cpu\""
55
+ ]
56
+ },
57
+ {
58
+ "cell_type": "code",
59
+ "execution_count": 52,
60
+ "metadata": {},
61
+ "outputs": [
62
+ {
63
+ "name": "stderr",
64
+ "output_type": "stream",
65
+ "text": [
66
+ "Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.\n"
67
+ ]
68
+ }
69
+ ],
70
+ "source": [
71
+ "transcriber = pipeline(\"automatic-speech-recognition\", model=\"openai/whisper-base.en\", device=device)"
72
+ ]
73
+ },
74
+ {
75
+ "cell_type": "code",
76
+ "execution_count": 53,
77
+ "metadata": {},
78
+ "outputs": [
79
+ {
80
+ "data": {
81
+ "text/plain": [
82
+ "device(type='cpu')"
83
+ ]
84
+ },
85
+ "execution_count": 53,
86
+ "metadata": {},
87
+ "output_type": "execute_result"
88
+ }
89
+ ],
90
+ "source": [
91
+ "transcriber.device"
92
+ ]
93
+ },
94
+ {
95
+ "cell_type": "code",
96
+ "execution_count": 58,
97
+ "metadata": {},
98
+ "outputs": [],
99
+ "source": [
100
+ "audio = \"/Users/sasan.jafarnejad/dev/uni/talking-car/audio1713724521.779008.wav\"\n",
101
+ "audio = \"/Users/sasan.jafarnejad/dev/uni/talking-car/audio/attenborough/neutral.wav\"\n",
102
+ "if type(audio) == str:\n",
103
+ " link_to_audio = audio\n",
104
+ " audio = torchaudio.load(link_to_audio)\n",
105
+ " audio = audio[0].squeeze().numpy(), sr\n",
106
+ " if len(audio[0].shape) == 2:\n",
107
+ " audio = audio[0].mean(axis=0), audio[1]"
108
+ ]
109
+ },
110
+ {
111
+ "cell_type": "code",
112
+ "execution_count": 59,
113
+ "metadata": {},
114
+ "outputs": [],
115
+ "source": [
116
+ "y, sr = audio"
117
+ ]
118
+ },
119
+ {
120
+ "cell_type": "code",
121
+ "execution_count": 60,
122
+ "metadata": {},
123
+ "outputs": [],
124
+ "source": [
125
+ "out = transcriber({\"sampling_rate\": sr, \"raw\":y})[\"text\"]"
126
+ ]
127
+ },
128
+ {
129
+ "cell_type": "code",
130
+ "execution_count": 61,
131
+ "metadata": {},
132
+ "outputs": [
133
+ {
134
+ "data": {
135
+ "text/plain": [
136
+ "\" If you speed up time, plants begin to reveal their true nature. They're not passive organisms, as you might think, but competitive creatures every bit as aggressive as animals. They're locked in a desperate battle for light and space. They stretch and pulse as they strive to barge their way into pole position. Creepers and vines reach around for the branch or stem of another plant on which to hitch a ride. This is an assassin bug. To us, it's easy enough to spot because it moves. To its prey, that's irrelevant, because it smells like one of their number. The assassin sucks its victims dry and blues their empty husks onto its back. This one is already carrying at least 20 corpses. Its irregular shape makes it hard for other predators to spot it and makes it virtually invisible to its prey, ants. It enters this ant colony unchallenged. Its coat of ant corpses masks its own odour. To the ants, it smells like one of their own, and that's what matters. They'll even run straight over the top of it. The assassin simply takes an ant whenever it feels hungry, and the body of each victim then adds to its disguise. The giants here too. This is the Moala Moala, the sunfish. It's huge, three meters across, and addicted to lying on its side at the surface. It eats vast quantities of jellyfish. And there are not only fish from me in these waters, there are mammals. sea lions, whose ancestors originally came from the coasts of California. The Galapagos plankton is so abundant it attracts some of the biggest of all ocean mammals, humpback whales, and rivalling them in size the biggest of all fish. 20 ton whale shark. Few parts of the world's oceans can equally as Galapagos waters for sheer variety and abundance. That creature was a penguin. Penguins are ocean-goings for moose. But a few thousand years ago some of them got caught in the cold waters of the Humboldt current and were carried northwards up the coast of South America and out to the Galapagos. They could hardly have found anywhere more different from their polar home and in and the response they chain, the Emperor penguin that lives near the South Pole stands over a metre high, Galapagos penguin is now only half, and that helps a lot in the Galapagos. Small animals lose heat much faster than big ones, and the penguins have developed behavioral tricks as well. Bear feet are easily sunburnt, so they do their best to keep them covered, and some parts of the sea around the islands are quite cool. The humbalt current flowing up from the Antarctic and washing around the western parts of the archipelago is still quite chilly. So most of the penguins stay in the channel between the two westernmost island and when things get really hot, they can still cool off with the swim. They're quick to detect the slightest variation in temperature and move around to find places where an eddy might have brought a pleasing chill. The arrival of penguins must be the most unlikely event in the whole story of the colonization of the Galapagos. The existence of creatures like these so far from the nearest continent poses many questions. How, for example, did these enormous beasts get to the islands in the first place? But perhaps the most extraordinary thing about the Galapagos tortoises is that they're not all the same. islands have different kinds. In the heyday there were 15 species. They seem to have appeared in an evolutionary blink of the eye. But will soon become a leaf. It's no ordinary. It has a special altogether more sinister. This is Nepenthes, the pitcher plant. It grows in nutrient poor soils, So has to find nitrogen and minerals in another way. The leaf, just like a flower, attracts insects with a reward. The pitcher is coloured and scented to appeal to flies looking for a meal of rotting flesh. The visitors are rewarded with a greasy substance on the underside of the pitcher's lid. But the plant wants something in return, not pollen, but a meal. The lip of the pitcher is covered in tiny slippery ridges. Wax lubricates the surface further. It's extremely difficult to hold on, even for a fly. Once inside, there's no escape. The leaf holds a pool of digestive liquid. This contains microscopic elastic filaments, which give it the properties of quicksand. The more the insect struggles, the deeper it sinks. Enzymes begin to dissolve the victim's body while it's still alive.\""
137
+ ]
138
+ },
139
+ "execution_count": 61,
140
+ "metadata": {},
141
+ "output_type": "execute_result"
142
+ }
143
+ ],
144
+ "source": [
145
+ "out"
146
+ ]
147
+ },
148
+ {
149
+ "cell_type": "code",
150
+ "execution_count": null,
151
+ "metadata": {},
152
+ "outputs": [],
153
+ "source": [
154
+ "def transcript(audio):\n",
155
+ " if type(audio) == str:\n",
156
+ " link_to_audio = audio\n",
157
+ " audio = torchaudio.load(link_to_audio)\n",
158
+ "\n",
159
+ " # We assume that the audio is the audio tensor\n",
160
+ "\n",
161
+ " # process the audio array\n",
162
+ " input_features = processor(audio_array, sampling_rate, return_tensors=\"pt\").input_features\n",
163
+ " predicted_ids = modelw.generate(input_features)\n",
164
+ "\n",
165
+ " transcription = processor.batch_decode(predicted_ids, skip_special_tokens=True)\n",
166
+ " \n",
167
+ " return audio_path, state['context'], state"
168
+ ]
169
+ },
170
+ {
171
+ "cell_type": "code",
172
+ "execution_count": null,
173
+ "metadata": {},
174
+ "outputs": [],
175
+ "source": [
176
+ "def transcript(general_context, link_to_audio, voice, place, time, delete_history, state):\n",
177
+ " \"\"\"this function manages speech-to-text to input Fnanswer function and text-to-speech with the Fnanswer output\"\"\"\n",
178
+ " # load audio from a specific path\n",
179
+ " audio_path = link_to_audio\n",
180
+ " audio_array, sampling_rate = librosa.load(link_to_audio, sr=16000) # \"sr=16000\" ensures that the sampling rate is as required\n",
181
+ "\n",
182
+ " # process the audio array\n",
183
+ " input_features = processor(audio_array, sampling_rate, return_tensors=\"pt\").input_features\n",
184
+ " predicted_ids = modelw.generate(input_features)\n",
185
+ "\n",
186
+ " transcription = processor.batch_decode(predicted_ids, skip_special_tokens=True)\n",
187
+ " \n",
188
+ "\n",
189
+ "\n",
190
+ " return audio_path, state['context'], state"
191
+ ]
192
+ },
193
+ {
194
+ "cell_type": "code",
195
+ "execution_count": 64,
196
+ "metadata": {},
197
+ "outputs": [
198
+ {
199
+ "name": "stdout",
200
+ "output_type": "stream",
201
+ "text": [
202
+ "Running on local URL: http://127.0.0.1:7875\n",
203
+ "\n",
204
+ "To create a public link, set `share=True` in `launch()`.\n"
205
+ ]
206
+ },
207
+ {
208
+ "data": {
209
+ "text/html": [
210
+ "<div><iframe src=\"http://127.0.0.1:7875/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
211
+ ],
212
+ "text/plain": [
213
+ "<IPython.core.display.HTML object>"
214
+ ]
215
+ },
216
+ "metadata": {},
217
+ "output_type": "display_data"
218
+ },
219
+ {
220
+ "data": {
221
+ "text/plain": []
222
+ },
223
+ "execution_count": 64,
224
+ "metadata": {},
225
+ "output_type": "execute_result"
226
+ }
227
+ ],
228
+ "source": [
229
+ "import numpy as np\n",
230
+ "import gradio as gr\n",
231
+ "import torchaudio\n",
232
+ "import time\n",
233
+ "import torch\n",
234
+ "\n",
235
+ "def save_audio_as_wav(data, sample_rate, file_path):\n",
236
+ " # make a tensor from the numpy array\n",
237
+ " data = torch.tensor(data).reshape(1, -1)\n",
238
+ " torchaudio.save(file_path, data, sample_rate=sample_rate, bits_per_sample=16, encoding=\"PCM_S\")\n",
239
+ "\n",
240
+ "def save_and_transcribe_audio(audio):\n",
241
+ " # capture the audio and save it to a file as wav or mp3\n",
242
+ " # file_name = save(\"audioinput.wav\")\n",
243
+ " sr, y = audio\n",
244
+ " # y = y.astype(np.float32)\n",
245
+ " # y /= np.max(np.abs(y))\n",
246
+ "\n",
247
+ " # add timestamp to file name\n",
248
+ " filename = f\"audio{time.time()}.wav\"\n",
249
+ " save_audio_as_wav(y, sr, filename)\n",
250
+ " \n",
251
+ " sr, y = audio\n",
252
+ " y = y.astype(np.float32)\n",
253
+ " y /= np.max(np.abs(y))\n",
254
+ " text = transcriber({\"sampling_rate\": sr, \"raw\":y})[\"text\"]\n",
255
+ " return text\n",
256
+ "\n",
257
+ "gr.Interface(\n",
258
+ " fn=save_and_transcribe_audio, \n",
259
+ " inputs=gr.Audio(sources=\"microphone\", type=\"numpy\", label=\"Record Audio\"), \n",
260
+ " outputs=\"text\").launch()"
261
+ ]
262
+ },
263
+ {
264
+ "cell_type": "code",
265
+ "execution_count": null,
266
+ "metadata": {},
267
+ "outputs": [],
268
+ "source": []
269
+ }
270
+ ],
271
+ "metadata": {
272
+ "kernelspec": {
273
+ "display_name": "llm",
274
+ "language": "python",
275
+ "name": "python3"
276
+ },
277
+ "language_info": {
278
+ "codemirror_mode": {
279
+ "name": "ipython",
280
+ "version": 3
281
+ },
282
+ "file_extension": ".py",
283
+ "mimetype": "text/x-python",
284
+ "name": "python",
285
+ "nbconvert_exporter": "python",
286
+ "pygments_lexer": "ipython3",
287
+ "version": "3.11.8"
288
+ }
289
+ },
290
+ "nbformat": 4,
291
+ "nbformat_minor": 2
292
+ }
tts.ipynb ADDED
The diff for this file is too large to render. See raw diff