diff --git a/.github/workflows/license_tests.yml b/.github/workflows/license_tests.yml
new file mode 100644
index 0000000..c37ef9c
--- /dev/null
+++ b/.github/workflows/license_tests.yml
@@ -0,0 +1,12 @@
+name: Run License Tests
+on:
+ push:
+ workflow_dispatch:
+ pull_request:
+ branches:
+ - master
+jobs:
+ license_tests:
+ uses: neongeckocom/.github/.github/workflows/license_tests.yml@master
+ with:
+ packages-exclude: '^(neon-hana|dnspython).*'
diff --git a/.github/workflows/propose_release.yml b/.github/workflows/propose_release.yml
new file mode 100644
index 0000000..6a3614b
--- /dev/null
+++ b/.github/workflows/propose_release.yml
@@ -0,0 +1,28 @@
+name: Propose Stable Release
+on:
+ workflow_dispatch:
+ inputs:
+ release_type:
+ type: choice
+ description: Release Type
+ options:
+ - patch
+ - minor
+ - major
+jobs:
+ update_version:
+ uses: neongeckocom/.github/.github/workflows/propose_semver_release.yml@master
+ with:
+ branch: dev
+ release_type: ${{ inputs.release_type }}
+ update_changelog: True
+ version_file: "neon_hana/version.py"
+ pull_changes:
+ uses: neongeckocom/.github/.github/workflows/pull_master.yml@master
+ needs: update_version
+ with:
+ pr_reviewer: neonreviewers
+ pr_assignee: ${{ github.actor }}
+ pr_draft: false
+ pr_title: ${{ needs.update_version.outputs.version }}
+ pr_body: ${{ needs.update_version.outputs.changelog }}
\ No newline at end of file
diff --git a/.github/workflows/publish_release.yml b/.github/workflows/publish_release.yml
new file mode 100644
index 0000000..bb42ad0
--- /dev/null
+++ b/.github/workflows/publish_release.yml
@@ -0,0 +1,25 @@
+# This workflow will generate a release distribution and upload it to PyPI
+
+name: Publish Build and GitHub Release
+on:
+ push:
+ branches:
+ - master
+
+jobs:
+ tag_release:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Get Version
+ run: |
+ VERSION=$(python setup.py --version)
+ echo "VERSION=${VERSION}" >> $GITHUB_ENV
+ - uses: ncipollo/release-action@v1
+ with:
+ token: ${{secrets.GITHUB_TOKEN}}
+ tag: ${{env.VERSION}}
+ generateReleaseNotes: true
+ build_and_publish_docker:
+ uses: neongeckocom/.github/.github/workflows/publish_docker.yml@master
+ secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/publish_test_build.yml b/.github/workflows/publish_test_build.yml
new file mode 100644
index 0000000..944fd62
--- /dev/null
+++ b/.github/workflows/publish_test_build.yml
@@ -0,0 +1,22 @@
+# This workflow will generate a distribution and upload it to PyPI
+
+name: Publish Alpha Build
+on:
+ push:
+ branches:
+ - dev
+ paths-ignore:
+ - 'neon_hana/version.py'
+
+jobs:
+ publish_alpha_release:
+ uses: neongeckocom/.github/.github/workflows/publish_alpha_release.yml@master
+ secrets: inherit
+ with:
+ version_file: "neon_hana/version.py"
+ publish_prerelease: true
+ publish_pypi: false
+ build_and_publish_docker:
+ needs: publish_alpha_release
+ uses: neongeckocom/.github/.github/workflows/publish_docker.yml@master
+ secrets: inherit
\ No newline at end of file
diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml
new file mode 100644
index 0000000..068ca91
--- /dev/null
+++ b/.github/workflows/unit_tests.yml
@@ -0,0 +1,29 @@
+name: Run Unit Tests
+on:
+ pull_request:
+ workflow_dispatch:
+
+jobs:
+ py_build_tests:
+ uses: neongeckocom/.github/.github/workflows/python_build_tests.yml@master
+ with:
+ python_version: "3.9"
+ unit_tests:
+ strategy:
+ matrix:
+ python-version: [ 3.9, "3.10", "3.11" ]
+ timeout-minutes: 15
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: Set up python ${{ matrix.python-version }}
+ uses: actions/setup-python@v2
+ with:
+ python-version: ${{ matrix.python-version }}
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install . -r requirements/test_requirements.txt
+ - name: Run Tests
+ run: |
+ pytest tests
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..135f22d
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,70 @@
+# Changelog
+
+## [0.0.1a9](https://github.com/NeonGeckoCom/neon-hana/tree/0.0.1a9) (2024-02-26)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-hana/compare/0.0.1a8...0.0.1a9)
+
+**Merged pull requests:**
+
+- Cleanup comments and prep for release [\#13](https://github.com/NeonGeckoCom/neon-hana/pull/13) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.1a8](https://github.com/NeonGeckoCom/neon-hana/tree/0.0.1a8) (2024-01-26)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-hana/compare/0.0.1a7...0.0.1a8)
+
+**Merged pull requests:**
+
+- Update to use client-provided public IP address when available [\#12](https://github.com/NeonGeckoCom/neon-hana/pull/12) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.1a7](https://github.com/NeonGeckoCom/neon-hana/tree/0.0.1a7) (2024-01-26)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-hana/compare/0.0.1a6...0.0.1a7)
+
+**Merged pull requests:**
+
+- Add Node data model and Session support [\#11](https://github.com/NeonGeckoCom/neon-hana/pull/11) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.1a6](https://github.com/NeonGeckoCom/neon-hana/tree/0.0.1a6) (2024-01-26)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-hana/compare/0.0.1a5...0.0.1a6)
+
+**Merged pull requests:**
+
+- Configurable Authorization Request Limits [\#9](https://github.com/NeonGeckoCom/neon-hana/pull/9) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.1a5](https://github.com/NeonGeckoCom/neon-hana/tree/0.0.1a5) (2024-01-23)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-hana/compare/0.0.1a4...0.0.1a5)
+
+**Merged pull requests:**
+
+- JWT server cache fix and client response update [\#8](https://github.com/NeonGeckoCom/neon-hana/pull/8) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.1a4](https://github.com/NeonGeckoCom/neon-hana/tree/0.0.1a4) (2024-01-22)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-hana/compare/0.0.1a3...0.0.1a4)
+
+**Merged pull requests:**
+
+- Default disable email service with note in docs explaining rationale [\#4](https://github.com/NeonGeckoCom/neon-hana/pull/4) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.1a3](https://github.com/NeonGeckoCom/neon-hana/tree/0.0.1a3) (2024-01-22)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-hana/compare/0.0.1a2...0.0.1a3)
+
+**Merged pull requests:**
+
+- Add `assist` route for HTTP requests [\#3](https://github.com/NeonGeckoCom/neon-hana/pull/3) ([NeonDaniel](https://github.com/NeonDaniel))
+
+## [0.0.1a2](https://github.com/NeonGeckoCom/neon-hana/tree/0.0.1a2) (2024-01-19)
+
+[Full Changelog](https://github.com/NeonGeckoCom/neon-hana/compare/885ec6ef0f8ddcaa8127f60730ef7b4011127554...0.0.1a2)
+
+**Merged pull requests:**
+
+- Fix path errors in test build automation [\#2](https://github.com/NeonGeckoCom/neon-hana/pull/2) ([NeonDaniel](https://github.com/NeonDaniel))
+- Initial Implementation [\#1](https://github.com/NeonGeckoCom/neon-hana/pull/1) ([NeonDaniel](https://github.com/NeonDaniel))
+
+
+
+\* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..f8095c0
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,16 @@
+FROM python:3.9-slim
+
+LABEL vendor=neon.ai \
+ ai.neon.name="neon-hana"
+
+ENV OVOS_CONFIG_BASE_FOLDER neon
+ENV OVOS_CONFIG_FILENAME diana.yaml
+ENV XDG_CONFIG_HOME /config
+
+COPY docker_overlay/ /
+
+WORKDIR /app
+COPY . /app
+RUN pip install /app
+
+CMD ["python3", "/app/neon_hana/app/__main__.py"]
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
new file mode 100644
index 0000000..307a15b
--- /dev/null
+++ b/LICENSE.md
@@ -0,0 +1,21 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
+following conditions are met:
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following
+disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
+disclaimer in the documentation and/or other materials provided with the distribution.
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products
+derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..02e3cd1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,47 @@
+# HANA
+HANA (HTTP API for Neon Applications) provides a unified front-end for
+accessing services in a [Neon DIANA](https://github.com/NeonGeckoCom/neon-diana-utils) deployment. This API should generally
+be hosted as part of a Diana deployment to safely expose services to outside
+traffic.
+
+Full API documentation is automatically generated and accessible at `/docs`.
+
+## Configuration
+User configuration belongs in `diana.yaml`, mounted in the container path
+`/config/neon/`. An example user configuration could be:
+```yaml
+MQ:
+ server: mq.mydomain.com
+hana:
+ mq_default_timeout: 10
+ access_token_ttl: 86400 # 1 day
+ refresh_token_ttl: 604800 # 1 week
+ requests_per_minute: 60
+ auth_requests_per_minute: 6 # This counts valid and invalid requests from an IP address
+ access_token_secret: a800445648142061fc238d1f84e96200da87f4f9fa7835cac90db8b4391b117b
+ refresh_token_secret: 833d369ac73d883123743a44b4a7fe21203cffc956f4c8fec712e71aafa8e1aa
+ fastapi_title: "My HANA API Host"
+ fastapi_summary: "Personal HTTP API to access my DIANA backend."
+ disable_auth: True
+ stt_max_length_encoded: 500000 # Arbitrary limit that is larger than any expected voice command
+ tts_max_words: 128 # Arbitrary limit that is longer than any default LLM token limit
+ enable_email: True # Disabled by default; anyone with access to the API will be able to send emails from the configured address
+
+```
+It is recommended to generate unique values for configured tokens, these are 32
+bytes in hexadecimal representation.
+
+## Deployment
+You can build a Docker container from this repository, or pull a built container
+from the GitHub Container Registry. Start Hana via:
+```shell
+docker run -p 8080:8080 -v ~/.config/neon:/config/neon ghcr.io/neongeckocom/neon-hana
+```
+> This assumes you have configuration defined in `~/.config/neon/diana.yaml` and
+ are using the default port 8080
+
+## Usage
+Full API documentation is available at `/docs`. The `/auth/login` endpoint should
+be used to generate a `client_id`, `access_token`, and `refresh_token`. The
+`access_token` should be included in every request and upon expiration of the
+`access_token`, a new token can be obtained from the `auth/refresh` endpoint.
diff --git a/docker_overlay/config/neon/.keep b/docker_overlay/config/neon/.keep
new file mode 100644
index 0000000..e69de29
diff --git a/docker_overlay/etc/neon/diana.yaml b/docker_overlay/etc/neon/diana.yaml
new file mode 100644
index 0000000..c8fa873
--- /dev/null
+++ b/docker_overlay/etc/neon/diana.yaml
@@ -0,0 +1,31 @@
+log_level: INFO
+logs:
+ level_overrides:
+ error:
+ - pika
+ warning:
+ - filelock
+ info:
+ - openai
+ debug: []
+MQ:
+ server: neon-rabbitmq
+ port: 5672
+ users:
+ mq_handler:
+ user: neon_api_utils
+ password: Klatchat2021
+hana:
+ mq_default_timeout: 10
+ access_token_ttl: 86400 # 1 day
+ refresh_token_ttl: 604800 # 1 week
+ requests_per_minute: 60
+ access_token_secret: a800445648142061fc238d1f84e96200da87f4f9f784108ac90db8b4391b117b
+ refresh_token_secret: 833d369ac73d883123743a44b4a7fe21203cffc956f4c8a99be6e71aafa8e1aa
+ server_host: "0.0.0.0"
+ server_port: 8080
+ fastapi_title: "Hana"
+ fastapi_summary: "HANA (HTTP API for Neon Applications) is the HTTP component of the Device Independent API for Neon Applications (DIANA)"
+ stt_max_length_encoded: 500000
+ tts_max_words: 128
+ enable_email: False
\ No newline at end of file
diff --git a/neon_hana/__init__.py b/neon_hana/__init__.py
new file mode 100644
index 0000000..d782cbb
--- /dev/null
+++ b/neon_hana/__init__.py
@@ -0,0 +1,25 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/neon_hana/app/__init__.py b/neon_hana/app/__init__.py
new file mode 100644
index 0000000..5b7123c
--- /dev/null
+++ b/neon_hana/app/__init__.py
@@ -0,0 +1,49 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from fastapi import FastAPI
+
+from neon_hana.app.dependencies import client_manager, jwt_bearer, mq_connector
+from neon_hana.app.routers.api_proxy import proxy_route
+from neon_hana.app.routers.assist import assist_route
+from neon_hana.app.routers.llm import llm_route
+from neon_hana.app.routers.mq_backend import mq_route
+from neon_hana.app.routers.auth import auth_route
+from neon_hana.version import __version__
+
+
+def create_app(config: dict):
+ title = config.get('fastapi_title') or "HANA: HTTP API for Neon Applications"
+ summary = config.get('fastapi_summary') or ""
+ version = __version__
+ app = FastAPI(title=title, summary=summary, version=version)
+ app.include_router(auth_route)
+ app.include_router(assist_route)
+ app.include_router(proxy_route)
+ app.include_router(mq_route)
+ app.include_router(llm_route)
+
+ return app
diff --git a/neon_hana/app/__main__.py b/neon_hana/app/__main__.py
new file mode 100644
index 0000000..a621e21
--- /dev/null
+++ b/neon_hana/app/__main__.py
@@ -0,0 +1,46 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import uvicorn
+from os import environ
+
+environ.setdefault("OVOS_CONFIG_BASE_FOLDER", "neon")
+environ.setdefault("OVOS_CONFIG_FILENAME", "diana.yaml")
+
+from ovos_config.config import Configuration
+
+from neon_hana.app import create_app
+
+
+def main():
+ config = Configuration().get("hana", {})
+ app = create_app(config)
+ uvicorn.run(app, host=config.get('server_host', "0.0.0.0"),
+ port=config.get('port', 8080))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/neon_hana/app/dependencies.py b/neon_hana/app/dependencies.py
new file mode 100644
index 0000000..0c9dcf5
--- /dev/null
+++ b/neon_hana/app/dependencies.py
@@ -0,0 +1,35 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from ovos_config.config import Configuration
+
+from neon_hana.mq_service_api import MQServiceManager
+from neon_hana.auth.client_manager import ClientManager, UserTokenAuth
+
+config = Configuration().get("hana") or dict()
+mq_connector = MQServiceManager(config)
+client_manager = ClientManager(config)
+jwt_bearer = UserTokenAuth(client_manager)
diff --git a/neon_hana/app/routers/__init__.py b/neon_hana/app/routers/__init__.py
new file mode 100644
index 0000000..d782cbb
--- /dev/null
+++ b/neon_hana/app/routers/__init__.py
@@ -0,0 +1,25 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/neon_hana/app/routers/api_proxy.py b/neon_hana/app/routers/api_proxy.py
new file mode 100644
index 0000000..de37194
--- /dev/null
+++ b/neon_hana/app/routers/api_proxy.py
@@ -0,0 +1,67 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from fastapi import APIRouter, Depends
+from neon_hana.schema.api_requests import *
+from neon_hana.schema.api_responses import *
+from neon_hana.app.dependencies import jwt_bearer, mq_connector
+
+
+proxy_route = APIRouter(prefix="/proxy", tags=["backend"],
+ dependencies=[Depends(jwt_bearer)])
+
+
+@proxy_route.post("/weather")
+async def api_proxy_weather(query: WeatherAPIRequest) -> WeatherAPIOnecallResponse:
+ return mq_connector.query_api_proxy("open_weather_map", dict(query))
+
+
+@proxy_route.post("/stock/symbol")
+async def api_proxy_stock_symbol(query: StockAPISymbolRequest) -> StockAPISearchResponse:
+ return mq_connector.query_api_proxy("alpha_vantage",
+ {**dict(query),
+ **{"api": "symbol"}})
+
+
+@proxy_route.post("/stock/quote")
+async def api_proxy_stock_quote(query: StockAPIQuoteRequest) -> StockAPIQuoteResponse:
+ return mq_connector.query_api_proxy("alpha_vantage",
+ {**dict(query), **{"api": "quote"}})
+
+
+@proxy_route.post("/geolocation/geocode")
+async def api_proxy_geolocation(query: GeoAPIRequest) -> GeoAPIGeocodeResponse:
+ return mq_connector.query_api_proxy("map_maker", dict(query))
+
+
+@proxy_route.post("/geolocation/reverse")
+async def api_proxy_geolocation(query: GeoAPIReverseRequest) -> GeoAPIReverseResponse:
+ return mq_connector.query_api_proxy("map_maker", dict(query))
+
+
+@proxy_route.post("/wolframalpha")
+async def api_proxy_wolframalpha(query: WolframAlphaAPIRequest) -> WolframAlphaAPIResponse:
+ return mq_connector.query_api_proxy("wolfram_alpha", dict(query))
\ No newline at end of file
diff --git a/neon_hana/app/routers/assist.py b/neon_hana/app/routers/assist.py
new file mode 100644
index 0000000..52d88f0
--- /dev/null
+++ b/neon_hana/app/routers/assist.py
@@ -0,0 +1,51 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from fastapi import APIRouter, Depends, Request
+from neon_hana.schema.assist_requests import *
+from neon_hana.app.dependencies import jwt_bearer, mq_connector
+
+
+assist_route = APIRouter(prefix="/neon", tags=["assist"],
+ dependencies=[Depends(jwt_bearer)])
+
+
+@assist_route.post("/get_stt")
+async def get_stt(audio_in: STTRequest) -> STTResponse:
+ return mq_connector.get_stt(**dict(audio_in))
+
+
+@assist_route.post("/get_tts")
+async def get_tts(request: TTSRequest) -> TTSResponse:
+ return mq_connector.get_tts(**dict(request))
+
+
+@assist_route.post("/get_response")
+async def get_response(skill_request: SkillRequest,
+ request: Request) -> SkillResponse:
+ if not skill_request.node_data.networking.public_ip:
+ skill_request.node_data.networking.public_ip = request.client.host
+ return mq_connector.get_response(**dict(skill_request))
diff --git a/neon_hana/app/routers/auth.py b/neon_hana/app/routers/auth.py
new file mode 100644
index 0000000..4cf78e2
--- /dev/null
+++ b/neon_hana/app/routers/auth.py
@@ -0,0 +1,44 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from fastapi import APIRouter, Request
+
+from neon_hana.app.dependencies import client_manager
+from neon_hana.schema.auth_requests import *
+
+auth_route = APIRouter(prefix="/auth", tags=["authentication"])
+
+
+@auth_route.post("/login")
+async def check_login(auth_request: AuthenticationRequest,
+ request: Request) -> AuthenticationResponse:
+ return client_manager.check_auth_request(**dict(auth_request),
+ origin_ip=request.client.host)
+
+
+@auth_route.post("/refresh")
+async def check_refresh(request: RefreshRequest) -> AuthenticationResponse:
+ return client_manager.check_refresh_request(**dict(request))
diff --git a/neon_hana/app/routers/llm.py b/neon_hana/app/routers/llm.py
new file mode 100644
index 0000000..a18d86f
--- /dev/null
+++ b/neon_hana/app/routers/llm.py
@@ -0,0 +1,58 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from fastapi import APIRouter, Depends
+from neon_hana.schema.llm_requests import *
+from neon_hana.app.dependencies import jwt_bearer, mq_connector
+
+
+llm_route = APIRouter(prefix="/llm", tags=["backend"],
+ dependencies=[Depends(jwt_bearer)])
+
+
+@llm_route.post("/chatgpt")
+async def llm_ask_chatgpt(query: LLMRequest) -> LLMResponse:
+ return mq_connector.query_llm("chat_gpt", **dict(query))
+
+
+@llm_route.post("/fastchat")
+async def llm_ask_fastchat(query: LLMRequest) -> LLMResponse:
+ return mq_connector.query_llm("fastchat", **dict(query))
+
+
+@llm_route.post("/gemini")
+async def llm_ask_gemini(query: LLMRequest) -> LLMResponse:
+ return mq_connector.query_llm("gemini", **dict(query))
+
+
+@llm_route.post("/claude")
+async def llm_ask_claude(query: LLMRequest) -> LLMResponse:
+ return mq_connector.query_llm("claude", **dict(query))
+
+
+@llm_route.post("/palm")
+async def llm_ask_palm(query: LLMRequest) -> LLMResponse:
+ return mq_connector.query_llm("palm2", **dict(query))
diff --git a/neon_hana/app/routers/mq_backend.py b/neon_hana/app/routers/mq_backend.py
new file mode 100644
index 0000000..6430dff
--- /dev/null
+++ b/neon_hana/app/routers/mq_backend.py
@@ -0,0 +1,53 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from fastapi import APIRouter, Depends
+from neon_hana.schema.api_requests import *
+from neon_hana.schema.api_responses import *
+from neon_hana.app.dependencies import jwt_bearer, mq_connector
+
+
+mq_route = APIRouter(tags=["backend"], dependencies=[Depends(jwt_bearer)])
+
+
+@mq_route.post("/email", dependencies=[Depends(jwt_bearer)])
+async def email_send(request: SendEmailRequest):
+ mq_connector.send_email(**dict(request))
+
+
+@mq_route.post("/metrics/upload", dependencies=[Depends(jwt_bearer)])
+async def upload_metric(metric: UploadMetricRequest):
+ mq_connector.upload_metric(**dict(metric))
+
+
+@mq_route.post("/ccl/parse", dependencies=[Depends(jwt_bearer)])
+async def parse_nct_script(script: ParseScriptRequest) -> ScriptParserResponse:
+ return mq_connector.parse_ccl_script(**dict(script))
+
+
+@mq_route.post("/coupons", dependencies=[Depends(jwt_bearer)])
+async def get_coupons() -> CouponsResponse:
+ return mq_connector.get_coupons()
diff --git a/neon_hana/auth/__init__.py b/neon_hana/auth/__init__.py
new file mode 100644
index 0000000..d782cbb
--- /dev/null
+++ b/neon_hana/auth/__init__.py
@@ -0,0 +1,25 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/neon_hana/auth/client_manager.py b/neon_hana/auth/client_manager.py
new file mode 100644
index 0000000..253b825
--- /dev/null
+++ b/neon_hana/auth/client_manager.py
@@ -0,0 +1,171 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import jwt
+
+from time import time
+from typing import Dict, Optional
+from fastapi import Request, HTTPException
+from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
+from jwt import DecodeError
+from token_throttler import TokenThrottler, TokenBucket
+from token_throttler.storage import RuntimeStorage
+
+
+class ClientManager:
+ def __init__(self, config: dict):
+ self.rate_limiter = TokenThrottler(cost=1, storage=RuntimeStorage())
+
+ self.authorized_clients: Dict[str, dict] = dict()
+ self._access_token_lifetime = config.get("access_token_ttl", 3600 * 24)
+ self._refresh_token_lifetime = config.get("refresh_token_ttl",
+ 3600 * 24 * 7)
+ self._access_secret = config.get("access_token_secret")
+ self._refresh_secret = config.get("refresh_token_secret")
+ self._rpm = config.get("requests_per_minute", 60)
+ self._auth_rpm = config.get("auth_requests_per_minute", 6)
+ self._disable_auth = config.get("disable_auth")
+ self._jwt_algo = "HS256"
+
+ def _create_tokens(self, encode_data: dict) -> dict:
+ token_expiration = encode_data['expire']
+ token = jwt.encode(encode_data, self._access_secret, self._jwt_algo)
+ encode_data['expire'] = time() + self._refresh_token_lifetime
+ encode_data['access_token'] = token
+ refresh = jwt.encode(encode_data, self._refresh_secret, self._jwt_algo)
+ # TODO: Store refresh token on server to allow invalidating clients
+ return {"username": encode_data['username'],
+ "client_id": encode_data['client_id'],
+ "access_token": token,
+ "refresh_token": refresh,
+ "expiration": token_expiration}
+
+ def check_auth_request(self, client_id: str, username: str,
+ password: Optional[str] = None,
+ origin_ip: str = "127.0.0.1"):
+ if client_id in self.authorized_clients:
+ print(f"Using cached client: {self.authorized_clients[client_id]}")
+ return self.authorized_clients[client_id]
+
+ ratelimit_id = f"auth{origin_ip}"
+ if not self.rate_limiter.get_all_buckets(ratelimit_id):
+ self.rate_limiter.add_bucket(ratelimit_id,
+ TokenBucket(replenish_time=60,
+ max_tokens=self._auth_rpm))
+ if not self.rate_limiter.consume(ratelimit_id):
+ bucket = list(self.rate_limiter.get_all_buckets(ratelimit_id).
+ values())[0]
+ replenish_time = bucket.last_replenished + bucket.replenish_time
+ wait_time = round(replenish_time - time())
+ raise HTTPException(status_code=429,
+ detail=f"Too many auth requests from: "
+ f"{origin_ip}. Wait {wait_time}s.")
+
+ if username != "guest":
+ # TODO: Validate password here
+ pass
+ expiration = time() + self._access_token_lifetime
+ encode_data = {"client_id": client_id,
+ "username": username,
+ "password": password,
+ "expire": expiration}
+ auth = self._create_tokens(encode_data)
+ self.authorized_clients[client_id] = auth
+ return auth
+
+ def check_refresh_request(self, access_token: str, refresh_token: str,
+ client_id: str):
+ # Read and validate refresh token
+ try:
+ refresh_data = jwt.decode(refresh_token, self._refresh_secret,
+ self._jwt_algo)
+ except DecodeError:
+ raise HTTPException(status_code=400,
+ detail="Invalid refresh token supplied")
+ if refresh_data['access_token'] != access_token:
+ raise HTTPException(status_code=403,
+ detail="Refresh and access token mismatch")
+ if time() > refresh_data['expire']:
+ raise HTTPException(status_code=401,
+ detail="Refresh token is expired")
+ # Read access token and re-generate a new pair of tokens
+ # This is already known to be a valid token based on the refresh token
+ token_data = jwt.decode(access_token, self._access_secret,
+ self._jwt_algo)
+
+ if token_data['client_id'] != client_id:
+ raise HTTPException(status_code=403,
+ detail="Access token does not match client_id")
+ encode_data = {k: token_data[k] for k in
+ ("client_id", "username", "password")}
+ encode_data["expire"] = time() + self._access_token_lifetime
+ new_auth = self._create_tokens(encode_data)
+ return new_auth
+
+ def validate_auth(self, token: str, origin_ip: str) -> bool:
+ if not self.rate_limiter.get_all_buckets(origin_ip):
+ self.rate_limiter.add_bucket(origin_ip,
+ TokenBucket(replenish_time=60,
+ max_tokens=self._rpm))
+ if not self.rate_limiter.consume(origin_ip) and self._rpm > 0:
+ raise HTTPException(status_code=429,
+ detail=f"Requests limited to {self._rpm}/min"
+ f"per client connection")
+
+ if self._disable_auth:
+ return True
+ try:
+ auth = jwt.decode(token, self._access_secret, self._jwt_algo)
+ if auth['expire'] < time():
+ self.authorized_clients.pop(auth['client_id'], None)
+ return False
+ return True
+ except DecodeError:
+ # Invalid token supplied
+ pass
+ return False
+
+
+class UserTokenAuth(HTTPBearer):
+ def __init__(self, client_manager: ClientManager):
+ HTTPBearer.__init__(self)
+ self.client_manager = client_manager
+
+ async def __call__(self, request: Request):
+ credentials: HTTPAuthorizationCredentials = \
+ await HTTPBearer.__call__(self, request)
+ if credentials:
+ if not credentials.scheme == "Bearer":
+ raise HTTPException(status_code=403,
+ detail="Invalid authentication scheme.")
+ if not self.client_manager.validate_auth(credentials.credentials,
+ request.client.host):
+ raise HTTPException(status_code=403,
+ detail="Invalid or expired token.")
+ return credentials.credentials
+ else:
+ raise HTTPException(status_code=403,
+ detail="Invalid or missing auth credentials.")
diff --git a/neon_hana/mq_service_api.py b/neon_hana/mq_service_api.py
new file mode 100644
index 0000000..c74a1ad
--- /dev/null
+++ b/neon_hana/mq_service_api.py
@@ -0,0 +1,207 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import json
+
+from time import time
+from typing import Optional, Dict, Any, List
+from uuid import uuid4
+from fastapi import HTTPException
+
+from neon_hana.schema.node_model import NodeData
+from neon_hana.schema.user_profile import UserProfile
+from neon_mq_connector.utils.client_utils import send_mq_request
+
+
+class APIError(HTTPException):
+ """
+ Exception class representing errors in getting responses from the MQ API
+ """
+
+
+class MQServiceManager:
+ def __init__(self, config: dict):
+ self.mq_default_timeout = config.get('mq_default_timeout', 10)
+ self.mq_cliend_id = config.get('mq_client_id') or str(uuid4())
+ self.stt_max_length = config.get('stt_max_length_encoded') or 500000
+ self.tts_max_words = config.get('tts_max_words') or 128
+ self.email_enabled = config.get('enable_email')
+ self.sessions_by_id = dict()
+
+ @staticmethod
+ def _validate_api_proxy_response(response: dict):
+ if response['status_code'] == 200:
+ try:
+ resp = json.loads(response['content'])
+ if isinstance(resp, dict):
+ return resp
+ # Reverse Geocode API returns a list; reformat that to a dict
+ if isinstance(resp, list):
+ return {**resp.pop(0),
+ **{"alternate_results": resp}}
+ except json.JSONDecodeError:
+ resp = response['content']
+ # Wolfram Spoken API returns a string; reformat that to a dict
+ if isinstance(resp, str):
+ return {"answer": resp}
+ code = response['status_code'] if response['status_code'] > 200 else 500
+ raise APIError(status_code=code, detail=response['content'])
+
+ def get_session(self, node_data: NodeData) -> dict:
+ """
+ Get a serialized Session object for the specified Node.
+ @param node_data: NodeData received from client
+ @returns: Serialized session, possibly cached from previous a response
+ """
+ session_id = node_data.device_id
+ self.sessions_by_id.setdefault(session_id,
+ {"session_id": session_id,
+ "site_id": node_data.location.site_id})
+ return self.sessions_by_id[session_id]
+
+ def query_api_proxy(self, service_name: str, query_params: dict,
+ timeout: int = 10):
+ query_params['service'] = service_name
+ response = send_mq_request("/neon_api", query_params, "neon_api_input",
+ "neon_api_output", timeout)
+ return self._validate_api_proxy_response(response)
+
+ def query_llm(self, llm_name: str, query: str, history: List[tuple]):
+ response = send_mq_request("/llm", {"query": query,
+ "history": history},
+ f"{llm_name}_input",
+ response_queue=f"{llm_name}_"
+ f"{self.mq_cliend_id}")
+ response = response.get('response') or ""
+ history.append(("user", query))
+ history.append(("llm", response))
+ return {"response": response,
+ "history": history}
+
+ def send_email(self, recipient: str, subject: str, body: str,
+ attachments: Optional[Dict[str, str]]):
+ if not self.email_enabled:
+ raise APIError(status_code=503, detail="Email service disabled")
+ request_data = {"recipient": recipient,
+ "subject": subject,
+ "body": body,
+ "attachments": attachments}
+ response = send_mq_request("/neon_emails", request_data,
+ "neon_emails_input")
+ if not response.get("success"):
+ raise APIError(status_code=500, detail="Email failed to send")
+
+ def upload_metric(self, metric_name: str, timestamp: str,
+ metric_data: Dict[str, Any]):
+ metric_data = {**{"name": metric_name, "timestamp": timestamp},
+ **metric_data}
+ send_mq_request("/neon_metrics", metric_data, "neon_metrics_input",
+ expect_response=False)
+
+ def parse_ccl_script(self, script: str, metadata: Dict[str, Any]):
+ try:
+ response = send_mq_request("/neon_script_parser",
+ {"text": script, "metadata": metadata},
+ "neon_script_parser_input",
+ "neon_script_parser_output",
+ self.mq_default_timeout)
+ return {"ncs": response['parsed_file']}
+ except TimeoutError as e:
+ raise APIError(status_code=500, detail=repr(e))
+
+ def get_coupons(self):
+ try:
+ response = send_mq_request("/neon_coupons", {},
+ "neon_coupons_input",
+ "neon_coupons_output",
+ self.mq_default_timeout)
+ return response
+ except TimeoutError as e:
+ raise APIError(status_code=500, detail=repr(e))
+
+ def get_stt(self, encoded_audio: str, lang_code: str):
+ if 0 < self.stt_max_length < len(encoded_audio):
+ raise APIError(status_code=400,
+ detail=f"Audio exceeds maximum encoded length of "
+ f"{self.stt_max_length}")
+ request_data = {"msg_type": "neon.get_stt",
+ "data": {"audio_data": encoded_audio,
+ "utterances": [""], # TODO: Compat
+ "lang": lang_code},
+ "context": {"source": "hana",
+ "ident": f"{self.mq_cliend_id}"
+ f"{time()}"}}
+ response = send_mq_request("/neon_chat_api", request_data,
+ "neon_chat_api_request",
+ timeout=self.mq_default_timeout)
+ return response['data']
+
+ def get_tts(self, to_speak: str, lang_code: str, gender: str):
+ if 0 < self.tts_max_words < len(to_speak.split()):
+ raise APIError(status_code=400,
+ detail=f"Text exceeds maximum word count of "
+ f"{self.tts_max_words}")
+ request_data = {"msg_type": "neon.get_tts",
+ "data": {"text": to_speak,
+ "utterance": "", # TODO: Compat
+ "speaker": {"name": "Neon",
+ "gender": gender,
+ "lang": lang_code},
+ "lang": lang_code},
+ "context": {"source": "hana",
+ "ident": f"{self.mq_cliend_id}{time()}"}}
+ response = send_mq_request("/neon_chat_api", request_data,
+ "neon_chat_api_request",
+ timeout=self.mq_default_timeout)
+ audio = response['data'][lang_code]['audio'][gender]
+ return {"encoded_audio": audio}
+
+ def get_response(self, utterance: str, lang_code: str,
+ user_profile: UserProfile, node_data: NodeData):
+ session = self.get_session(node_data)
+ user_profile.user.username = (user_profile.user.username or
+ self.mq_cliend_id)
+
+ request_data = {"msg_type": "recognizer_loop:utterance",
+ "data": {"utterances": [utterance],
+ "lang": lang_code},
+ "context": {"username": user_profile.user.username,
+ "user_profiles": [
+ user_profile.model_dump(mode="json")],
+ "source": "hana",
+ "session": session,
+ "node_data": node_data.model_dump(
+ mode="json"),
+ "ident": f"{self.mq_cliend_id}{time()}"}}
+ response = send_mq_request("/neon_chat_api", request_data,
+ "neon_chat_api_request",
+ timeout=self.mq_default_timeout)
+
+ # Update session data for future inputs
+ self.sessions_by_id[session['session_id']] = \
+ response['context']['session']
+ sentence = response['data']['responses'][lang_code]['sentence']
+ return {"answer": sentence, "lang_code": lang_code}
diff --git a/neon_hana/schema/__init__.py b/neon_hana/schema/__init__.py
new file mode 100644
index 0000000..d782cbb
--- /dev/null
+++ b/neon_hana/schema/__init__.py
@@ -0,0 +1,25 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/neon_hana/schema/api_requests.py b/neon_hana/schema/api_requests.py
new file mode 100644
index 0000000..3d652b8
--- /dev/null
+++ b/neon_hana/schema/api_requests.py
@@ -0,0 +1,211 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+from time import time
+from typing import Optional
+
+from pydantic import BaseModel
+
+
+class WeatherAPIRequest(BaseModel):
+ api: str = "onecall"
+ lat: float
+ lon: float
+ unit: str = "metric"
+
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "api": "onecall",
+ "lat": 47.6815,
+ "lon": -122.2087,
+ "unit": "imperial",
+ }, {
+ "api": "onecall",
+ "lat": 47.6815,
+ "lon": -122.2087,
+ "unit": "metric",
+ }]}}
+
+
+class StockAPISymbolRequest(BaseModel):
+ company: Optional[str] = None
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{"company": "microsoft"}]}}
+
+
+class StockAPIQuoteRequest(BaseModel):
+ symbol: Optional[str] = None
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{"symbol": "GOOG"}]}}
+
+
+class GeoAPIRequest(BaseModel):
+ address: str
+
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "address": "1100 Bellevue Way NE Bellevue, WA"
+ }]}}
+
+
+class GeoAPIReverseRequest(BaseModel):
+ lat: float
+ lon: float
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "lat": 47.6815,
+ "lon": -122.2087,
+ }]}}
+
+
+class WolframAlphaAPIRequest(BaseModel):
+ api: str
+ unit: str = "metric"
+ lat: float
+ lon: float
+ query: str
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "api": "spoken",
+ "lat": 47.6815,
+ "lon": -122.2087,
+ "query": "how far away is the moon"
+ }, {
+ "api": "short",
+ "lat": 47.6815,
+ "lon": -122.2087,
+ "query": "how far away is London"
+ }, {
+ "api": "full",
+ "lat": 47.6815,
+ "lon": -122.2087,
+ "query": "what is the derivative of sin(x)"
+ }
+ ]}}
+
+
+class SendEmailRequest(BaseModel):
+ recipient: str
+ subject: str
+ body: str
+ attachments: Optional[dict] = None
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "recipient": "developers@neon.ai",
+ "subject": "API test",
+ "body": "This is a test.\nGenerated from OpenAPI.",
+ "attachments": {"test.txt": "VGhpcyBpcyBhIHRlc3QgZmlsZQo="}
+ }]}}
+
+
+class UploadMetricRequest(BaseModel):
+ metric_name: str
+ timestamp: str
+ metric_data: dict = dict()
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "metric_name": "REST API Test",
+ "timestamp": str(time()),
+ "metric_data": {"test": True, "flag": "demo"}}]}}
+
+
+class ParseScriptRequest(BaseModel):
+ script: str
+ metadata: dict = dict()
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "script": """
+Script: Parser Test Script
+Author: Daniel McKnight
+Description:
+ Just an example description to go with
+ an example script. This will go in meta
+
+# Timeout goto line 18
+Timeout: 10, 18
+# Timeout exit
+Timeout: 20
+
+Synonym: "Test Script"
+ "Tester Script"
+ "Another Synonym"
+Claps: 2 Two clap action
+ 3 3 clap action
+Language: en-us, male
+
+Variable: no_val
+Variable: with_val = "Test Value"
+
+# This is a comment line separating header from execution (kinda)
+Neon speak: inlined speak
+Neon speak:
+ Block speech start
+ ...
+ Block speech end
+@pre-exec
+Execute: hello world
+voice_input(no_val)
+IF no_val == with_val:
+ Goto: pre-exec
+ELSE:
+ Reconvey: pre-exec
+
+If "word" IN "this phrase word is in":
+ Neon speak: "phrase"
+
+Reconvey: pre-exec, file_param
+Name Reconvey: "Someone", "some text", "/path/to/file"
+
+Case {with_val}:
+ "Some value"
+ Neon speak: first
+ "some other value"
+ Neon speak:
+ second
+
+Case(no_val):
+ "no_val_1":
+ Execute: what time is it
+
+Python: 1*2
+
+LOOP check START
+Set: new_val = no_val # This logs an error because it isn't declared
+# The following should warn/error
+dne = "test"
+voice_input(new_val)
+LOOP check END
+Email: "Mail Title", "email body goes here. could be a variable name in most cases"
+Run: script_name_here
+Exit""", "metadata": {"test": True, "context": "demo"}}]}}
diff --git a/neon_hana/schema/api_responses.py b/neon_hana/schema/api_responses.py
new file mode 100644
index 0000000..4b0ca73
--- /dev/null
+++ b/neon_hana/schema/api_responses.py
@@ -0,0 +1,2254 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from typing import Dict, List, Any, Optional
+from pydantic import BaseModel, Field
+
+
+class WeatherAPIOnecallResponse(BaseModel):
+ lat: float
+ lon: float
+ timezone: str
+ timezone_offset: int
+ current: Dict[str, Any]
+ minutely: List[dict]
+ hourly: List[dict]
+ daily: List[dict]
+
+ model_config = {
+ "extra": "allow",
+ "json_schema_extra": {
+ "examples": [
+ {
+ "lat": 47.6815,
+ "lon": -122.2087,
+ "timezone": "America/Los_Angeles",
+ "timezone_offset": -28800,
+ "current": {
+ "dt": 1705080482,
+ "sunrise": 1705074869,
+ "sunset": 1705106347,
+ "temp": 18.36,
+ "feels_like": 9.05,
+ "pressure": 1022,
+ "humidity": 66,
+ "dew_point": 9.93,
+ "uvi": 0.18,
+ "clouds": 75,
+ "visibility": 10000,
+ "wind_speed": 7,
+ "wind_deg": 360,
+ "wind_gust": 14,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04d"
+ }
+ ]
+ },
+ "minutely": [
+ {
+ "dt": 1705080540,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705080600,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705080660,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705080720,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705080780,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705080840,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705080900,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705080960,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081020,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081080,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081140,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081200,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081260,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081320,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081380,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081440,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081500,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081560,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081620,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081680,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081740,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081800,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081860,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081920,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705081980,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082040,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082100,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082160,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082220,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082280,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082340,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082400,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082460,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082520,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082580,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082640,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082700,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082760,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082820,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082880,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705082940,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083000,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083060,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083120,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083180,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083240,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083300,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083360,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083420,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083480,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083540,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083600,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083660,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083720,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083780,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083840,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083900,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705083960,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705084020,
+ "precipitation": 0
+ },
+ {
+ "dt": 1705084080,
+ "precipitation": 0
+ }
+ ],
+ "hourly": [
+ {
+ "dt": 1705078800,
+ "temp": 18.36,
+ "feels_like": 8.4,
+ "pressure": 1022,
+ "humidity": 66,
+ "dew_point": 9.93,
+ "uvi": 0.18,
+ "clouds": 75,
+ "visibility": 10000,
+ "wind_speed": 7.78,
+ "wind_deg": 345,
+ "wind_gust": 11.9,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705082400,
+ "temp": 18.37,
+ "feels_like": 8.19,
+ "pressure": 1022,
+ "humidity": 63,
+ "dew_point": 9.01,
+ "uvi": 0.41,
+ "clouds": 72,
+ "visibility": 10000,
+ "wind_speed": 8.08,
+ "wind_deg": 346,
+ "wind_gust": 10.65,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705086000,
+ "temp": 18.91,
+ "feels_like": 9.01,
+ "pressure": 1023,
+ "humidity": 60,
+ "dew_point": 8.56,
+ "uvi": 0.66,
+ "clouds": 49,
+ "visibility": 10000,
+ "wind_speed": 7.87,
+ "wind_deg": 348,
+ "wind_gust": 9.31,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705089600,
+ "temp": 19.74,
+ "feels_like": 9.99,
+ "pressure": 1024,
+ "humidity": 55,
+ "dew_point": 7.65,
+ "uvi": 0.78,
+ "clouds": 35,
+ "visibility": 10000,
+ "wind_speed": 7.92,
+ "wind_deg": 348,
+ "wind_gust": 8.97,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705093200,
+ "temp": 20.77,
+ "feels_like": 11.23,
+ "pressure": 1024,
+ "humidity": 50,
+ "dew_point": 6.73,
+ "uvi": 0.72,
+ "clouds": 21,
+ "visibility": 10000,
+ "wind_speed": 7.92,
+ "wind_deg": 350,
+ "wind_gust": 8.52,
+ "weather": [
+ {
+ "id": 801,
+ "main": "Clouds",
+ "description": "few clouds",
+ "icon": "02d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705096800,
+ "temp": 21.67,
+ "feels_like": 12.24,
+ "pressure": 1024,
+ "humidity": 47,
+ "dew_point": 3.74,
+ "uvi": 0.52,
+ "clouds": 7,
+ "visibility": 10000,
+ "wind_speed": 8.03,
+ "wind_deg": 353,
+ "wind_gust": 8.97,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705100400,
+ "temp": 21.54,
+ "feels_like": 12.09,
+ "pressure": 1024,
+ "humidity": 48,
+ "dew_point": 3.83,
+ "uvi": 0.26,
+ "clouds": 9,
+ "visibility": 10000,
+ "wind_speed": 8.03,
+ "wind_deg": 352,
+ "wind_gust": 9.19,
+ "weather": [
+ {
+ "id": 800,
+ "main": "Clear",
+ "description": "clear sky",
+ "icon": "01d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705104000,
+ "temp": 20.66,
+ "feels_like": 11.16,
+ "pressure": 1024,
+ "humidity": 51,
+ "dew_point": 4.28,
+ "uvi": 0,
+ "clouds": 13,
+ "visibility": 10000,
+ "wind_speed": 7.85,
+ "wind_deg": 350,
+ "wind_gust": 10.54,
+ "weather": [
+ {
+ "id": 801,
+ "main": "Clouds",
+ "description": "few clouds",
+ "icon": "02d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705107600,
+ "temp": 19.15,
+ "feels_like": 10.02,
+ "pressure": 1024,
+ "humidity": 54,
+ "dew_point": 4.1,
+ "uvi": 0,
+ "clouds": 27,
+ "visibility": 10000,
+ "wind_speed": 6.98,
+ "wind_deg": 351,
+ "wind_gust": 12.08,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705111200,
+ "temp": 18.68,
+ "feels_like": 10.29,
+ "pressure": 1024,
+ "humidity": 55,
+ "dew_point": 4.19,
+ "uvi": 0,
+ "clouds": 33,
+ "visibility": 10000,
+ "wind_speed": 6.08,
+ "wind_deg": 4,
+ "wind_gust": 13.02,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705114800,
+ "temp": 18.39,
+ "feels_like": 11.01,
+ "pressure": 1024,
+ "humidity": 56,
+ "dew_point": 4.15,
+ "uvi": 0,
+ "clouds": 36,
+ "visibility": 10000,
+ "wind_speed": 5.08,
+ "wind_deg": 21,
+ "wind_gust": 11.43,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705118400,
+ "temp": 17.67,
+ "feels_like": 9.88,
+ "pressure": 1024,
+ "humidity": 57,
+ "dew_point": 3.92,
+ "uvi": 0,
+ "clouds": 46,
+ "visibility": 10000,
+ "wind_speed": 5.32,
+ "wind_deg": 52,
+ "wind_gust": 10.56,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705122000,
+ "temp": 16.07,
+ "feels_like": 7.65,
+ "pressure": 1024,
+ "humidity": 57,
+ "dew_point": 2.17,
+ "uvi": 0,
+ "clouds": 56,
+ "visibility": 10000,
+ "wind_speed": 5.64,
+ "wind_deg": 72,
+ "wind_gust": 8.61,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705125600,
+ "temp": 14.31,
+ "feels_like": 4.93,
+ "pressure": 1024,
+ "humidity": 54,
+ "dew_point": -0.89,
+ "uvi": 0,
+ "clouds": 64,
+ "visibility": 10000,
+ "wind_speed": 6.22,
+ "wind_deg": 85,
+ "wind_gust": 9.31,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705129200,
+ "temp": 13.59,
+ "feels_like": 4.37,
+ "pressure": 1024,
+ "humidity": 49,
+ "dew_point": -3.46,
+ "uvi": 0,
+ "clouds": 99,
+ "visibility": 10000,
+ "wind_speed": 5.93,
+ "wind_deg": 83,
+ "wind_gust": 9.01,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705132800,
+ "temp": 13.01,
+ "feels_like": 3.31,
+ "pressure": 1023,
+ "humidity": 46,
+ "dew_point": -5.46,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 6.29,
+ "wind_deg": 83,
+ "wind_gust": 9.71,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705136400,
+ "temp": 12.36,
+ "feels_like": 2.5,
+ "pressure": 1023,
+ "humidity": 45,
+ "dew_point": -6.5,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 6.33,
+ "wind_deg": 77,
+ "wind_gust": 9.84,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705140000,
+ "temp": 11.8,
+ "feels_like": 2.59,
+ "pressure": 1022,
+ "humidity": 45,
+ "dew_point": -7.19,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 5.64,
+ "wind_deg": 75,
+ "wind_gust": 8.79,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705143600,
+ "temp": 11.39,
+ "feels_like": 1.42,
+ "pressure": 1022,
+ "humidity": 45,
+ "dew_point": -7.78,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 6.24,
+ "wind_deg": 75,
+ "wind_gust": 9.78,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705147200,
+ "temp": 10.71,
+ "feels_like": 0.73,
+ "pressure": 1021,
+ "humidity": 45,
+ "dew_point": -8.55,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 6.13,
+ "wind_deg": 77,
+ "wind_gust": 9.84,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705150800,
+ "temp": 10.4,
+ "feels_like": -0.04,
+ "pressure": 1020,
+ "humidity": 44,
+ "dew_point": -9.15,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 6.51,
+ "wind_deg": 77,
+ "wind_gust": 10.33,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705154400,
+ "temp": 9.97,
+ "feels_like": -0.2,
+ "pressure": 1019,
+ "humidity": 44,
+ "dew_point": -9.89,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 6.17,
+ "wind_deg": 73,
+ "wind_gust": 10,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705158000,
+ "temp": 9.88,
+ "feels_like": 0.64,
+ "pressure": 1019,
+ "humidity": 43,
+ "dew_point": -10.3,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 5.35,
+ "wind_deg": 69,
+ "wind_gust": 8.28,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705161600,
+ "temp": 9.39,
+ "feels_like": 0.1,
+ "pressure": 1018,
+ "humidity": 44,
+ "dew_point": -10.37,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 5.32,
+ "wind_deg": 70,
+ "wind_gust": 8.9,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705165200,
+ "temp": 10.35,
+ "feels_like": 1.8,
+ "pressure": 1017,
+ "humidity": 43,
+ "dew_point": -9.99,
+ "uvi": 0.16,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 4.88,
+ "wind_deg": 72,
+ "wind_gust": 8.32,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705168800,
+ "temp": 12.11,
+ "feels_like": 1.4,
+ "pressure": 1016,
+ "humidity": 40,
+ "dew_point": -9.29,
+ "uvi": 0.36,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 7.14,
+ "wind_deg": 61,
+ "wind_gust": 11.32,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705172400,
+ "temp": 14.52,
+ "feels_like": 5.34,
+ "pressure": 1014,
+ "humidity": 38,
+ "dew_point": -8.27,
+ "uvi": 0.5,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 6.06,
+ "wind_deg": 59,
+ "wind_gust": 10.13,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705176000,
+ "temp": 17.11,
+ "feels_like": 9.99,
+ "pressure": 1014,
+ "humidity": 36,
+ "dew_point": -6.72,
+ "uvi": 0.62,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 4.68,
+ "wind_deg": 54,
+ "wind_gust": 7.96,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705179600,
+ "temp": 18.7,
+ "feels_like": 10.42,
+ "pressure": 1012,
+ "humidity": 36,
+ "dew_point": -5.01,
+ "uvi": 0.58,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 5.97,
+ "wind_deg": 53,
+ "wind_gust": 9.91,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705183200,
+ "temp": 19.42,
+ "feels_like": 11.01,
+ "pressure": 1011,
+ "humidity": 37,
+ "dew_point": -3.53,
+ "uvi": 0.47,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 6.24,
+ "wind_deg": 51,
+ "wind_gust": 9.86,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705186800,
+ "temp": 19.15,
+ "feels_like": 10.17,
+ "pressure": 1010,
+ "humidity": 40,
+ "dew_point": -2.43,
+ "uvi": 0.25,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 6.82,
+ "wind_deg": 45,
+ "wind_gust": 11.01,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705190400,
+ "temp": 18.07,
+ "feels_like": 9.61,
+ "pressure": 1010,
+ "humidity": 43,
+ "dew_point": -1.57,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 6.04,
+ "wind_deg": 50,
+ "wind_gust": 10.87,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705194000,
+ "temp": 16.54,
+ "feels_like": 8.38,
+ "pressure": 1011,
+ "humidity": 48,
+ "dew_point": -0.98,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 5.46,
+ "wind_deg": 57,
+ "wind_gust": 9.82,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705197600,
+ "temp": 15.53,
+ "feels_like": 7.2,
+ "pressure": 1011,
+ "humidity": 49,
+ "dew_point": -1.44,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 5.46,
+ "wind_deg": 62,
+ "wind_gust": 10.71,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705201200,
+ "temp": 14.92,
+ "feels_like": 6.87,
+ "pressure": 1012,
+ "humidity": 48,
+ "dew_point": -2.6,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 5.12,
+ "wind_deg": 76,
+ "wind_gust": 9.4,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705204800,
+ "temp": 14.5,
+ "feels_like": 6.87,
+ "pressure": 1013,
+ "humidity": 48,
+ "dew_point": -2.85,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 4.72,
+ "wind_deg": 91,
+ "wind_gust": 8.97,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705208400,
+ "temp": 14.47,
+ "feels_like": 6.42,
+ "pressure": 1014,
+ "humidity": 48,
+ "dew_point": -2.92,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 5.06,
+ "wind_deg": 94,
+ "wind_gust": 9.13,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705212000,
+ "temp": 14.56,
+ "feels_like": 7.95,
+ "pressure": 1014,
+ "humidity": 47,
+ "dew_point": -3.28,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 3.98,
+ "wind_deg": 82,
+ "wind_gust": 7.27,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705215600,
+ "temp": 14.86,
+ "feels_like": 8.28,
+ "pressure": 1015,
+ "humidity": 46,
+ "dew_point": -3.77,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 4,
+ "wind_deg": 82,
+ "wind_gust": 7.18,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705219200,
+ "temp": 15.08,
+ "feels_like": 9.28,
+ "pressure": 1015,
+ "humidity": 45,
+ "dew_point": -3.69,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 3.51,
+ "wind_deg": 90,
+ "wind_gust": 6.53,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705222800,
+ "temp": 15.01,
+ "feels_like": 15.01,
+ "pressure": 1016,
+ "humidity": 47,
+ "dew_point": -3.01,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 2.48,
+ "wind_deg": 82,
+ "wind_gust": 4.59,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705226400,
+ "temp": 15.22,
+ "feels_like": 9.55,
+ "pressure": 1017,
+ "humidity": 48,
+ "dew_point": -2.34,
+ "uvi": 0,
+ "clouds": 100,
+ "visibility": 10000,
+ "wind_speed": 3.44,
+ "wind_deg": 103,
+ "wind_gust": 6.24,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705230000,
+ "temp": 15.21,
+ "feels_like": 15.21,
+ "pressure": 1018,
+ "humidity": 50,
+ "dew_point": -1.41,
+ "uvi": 0,
+ "clouds": 99,
+ "visibility": 10000,
+ "wind_speed": 1.7,
+ "wind_deg": 101,
+ "wind_gust": 2.93,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705233600,
+ "temp": 15.19,
+ "feels_like": 15.19,
+ "pressure": 1019,
+ "humidity": 52,
+ "dew_point": -0.51,
+ "uvi": 0,
+ "clouds": 89,
+ "visibility": 10000,
+ "wind_speed": 2.98,
+ "wind_deg": 105,
+ "wind_gust": 4.88,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705237200,
+ "temp": 15.71,
+ "feels_like": 15.71,
+ "pressure": 1019,
+ "humidity": 53,
+ "dew_point": 0.32,
+ "uvi": 0,
+ "clouds": 96,
+ "visibility": 10000,
+ "wind_speed": 2.13,
+ "wind_deg": 118,
+ "wind_gust": 4.07,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705240800,
+ "temp": 15.26,
+ "feels_like": 10.09,
+ "pressure": 1020,
+ "humidity": 56,
+ "dew_point": 1.04,
+ "uvi": 0,
+ "clouds": 56,
+ "visibility": 10000,
+ "wind_speed": 3.15,
+ "wind_deg": 130,
+ "wind_gust": 5.44,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705244400,
+ "temp": 15.26,
+ "feels_like": 15.26,
+ "pressure": 1021,
+ "humidity": 57,
+ "dew_point": 1.53,
+ "uvi": 0,
+ "clouds": 40,
+ "visibility": 10000,
+ "wind_speed": 2.68,
+ "wind_deg": 122,
+ "wind_gust": 4.05,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03n"
+ }
+ ],
+ "pop": 0
+ },
+ {
+ "dt": 1705248000,
+ "temp": 15.31,
+ "feels_like": 15.31,
+ "pressure": 1022,
+ "humidity": 58,
+ "dew_point": 1.83,
+ "uvi": 0,
+ "clouds": 32,
+ "visibility": 10000,
+ "wind_speed": 2.93,
+ "wind_deg": 129,
+ "wind_gust": 4.32,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ],
+ "pop": 0
+ }
+ ],
+ "daily": [
+ {
+ "dt": 1705089600,
+ "sunrise": 1705074869,
+ "sunset": 1705106347,
+ "moonrise": 1705080180,
+ "moonset": 1705111980,
+ "moon_phase": 0.05,
+ "temp": {
+ "day": 19.74,
+ "min": 13.59,
+ "max": 25.88,
+ "night": 13.59,
+ "eve": 18.68,
+ "morn": 18.7
+ },
+ "feels_like": {
+ "day": 9.99,
+ "night": 4.37,
+ "eve": 10.29,
+ "morn": 8.56
+ },
+ "pressure": 1024,
+ "humidity": 55,
+ "dew_point": 7.65,
+ "wind_speed": 9.62,
+ "wind_deg": 328,
+ "wind_gust": 17.87,
+ "weather": [
+ {
+ "id": 802,
+ "main": "Clouds",
+ "description": "scattered clouds",
+ "icon": "03d"
+ }
+ ],
+ "clouds": 35,
+ "pop": 0.43,
+ "uvi": 0.78
+ },
+ {
+ "dt": 1705176000,
+ "sunrise": 1705161237,
+ "sunset": 1705192824,
+ "moonrise": 1705168260,
+ "moonset": 1705203660,
+ "moon_phase": 0.09,
+ "temp": {
+ "day": 17.11,
+ "min": 9.39,
+ "max": 19.42,
+ "night": 14.86,
+ "eve": 15.53,
+ "morn": 9.97
+ },
+ "feels_like": {
+ "day": 9.99,
+ "night": 8.28,
+ "eve": 7.2,
+ "morn": -0.2
+ },
+ "pressure": 1014,
+ "humidity": 36,
+ "dew_point": -6.72,
+ "wind_speed": 7.14,
+ "wind_deg": 61,
+ "wind_gust": 11.32,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "clouds": 100,
+ "pop": 0,
+ "uvi": 0.62
+ },
+ {
+ "dt": 1705262400,
+ "sunrise": 1705247602,
+ "sunset": 1705279304,
+ "moonrise": 1705255920,
+ "moonset": 1705295220,
+ "moon_phase": 0.13,
+ "temp": {
+ "day": 26.42,
+ "min": 15.01,
+ "max": 27.97,
+ "night": 22.41,
+ "eve": 23.58,
+ "morn": 15.26
+ },
+ "feels_like": {
+ "day": 26.42,
+ "night": 22.41,
+ "eve": 23.58,
+ "morn": 10.09
+ },
+ "pressure": 1023,
+ "humidity": 40,
+ "dew_point": 4.87,
+ "wind_speed": 3.51,
+ "wind_deg": 90,
+ "wind_gust": 6.53,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04d"
+ }
+ ],
+ "clouds": 65,
+ "pop": 0,
+ "uvi": 0.73
+ },
+ {
+ "dt": 1705348800,
+ "sunrise": 1705333965,
+ "sunset": 1705365784,
+ "moonrise": 1705343460,
+ "moonset": 1705386480,
+ "moon_phase": 0.16,
+ "temp": {
+ "day": 32.43,
+ "min": 20.66,
+ "max": 32.43,
+ "night": 24.03,
+ "eve": 25.74,
+ "morn": 20.66
+ },
+ "feels_like": {
+ "day": 32.43,
+ "night": 20.05,
+ "eve": 21.63,
+ "morn": 20.66
+ },
+ "pressure": 1029,
+ "humidity": 41,
+ "dew_point": 11.03,
+ "wind_speed": 3.4,
+ "wind_deg": 80,
+ "wind_gust": 6.11,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "clouds": 98,
+ "pop": 0,
+ "uvi": 0.9
+ },
+ {
+ "dt": 1705435200,
+ "sunrise": 1705420325,
+ "sunset": 1705452267,
+ "moonrise": 1705430880,
+ "moonset": 1705477740,
+ "moon_phase": 0.2,
+ "temp": {
+ "day": 35.67,
+ "min": 23.2,
+ "max": 35.67,
+ "night": 31.51,
+ "eve": 29.66,
+ "morn": 23.4
+ },
+ "feels_like": {
+ "day": 35.67,
+ "night": 28.67,
+ "eve": 26.51,
+ "morn": 23.4
+ },
+ "pressure": 1026,
+ "humidity": 42,
+ "dew_point": 14.59,
+ "wind_speed": 3.11,
+ "wind_deg": 132,
+ "wind_gust": 5.21,
+ "weather": [
+ {
+ "id": 804,
+ "main": "Clouds",
+ "description": "overcast clouds",
+ "icon": "04d"
+ }
+ ],
+ "clouds": 97,
+ "pop": 0,
+ "uvi": 1
+ },
+ {
+ "dt": 1705521600,
+ "sunrise": 1705506683,
+ "sunset": 1705538750,
+ "moonrise": 1705518360,
+ "moonset": 0,
+ "moon_phase": 0.25,
+ "temp": {
+ "day": 36.27,
+ "min": 33.31,
+ "max": 36.27,
+ "night": 36.23,
+ "eve": 36,
+ "morn": 33.44
+ },
+ "feels_like": {
+ "day": 36.27,
+ "night": 36.23,
+ "eve": 36,
+ "morn": 33.44
+ },
+ "pressure": 1024,
+ "humidity": 95,
+ "dew_point": 34.72,
+ "wind_speed": 2.42,
+ "wind_deg": 155,
+ "wind_gust": 2.62,
+ "weather": [
+ {
+ "id": 500,
+ "main": "Rain",
+ "description": "light rain",
+ "icon": "10d"
+ }
+ ],
+ "clouds": 100,
+ "pop": 0.9,
+ "rain": 2.34,
+ "uvi": 1
+ },
+ {
+ "dt": 1705608000,
+ "sunrise": 1705593038,
+ "sunset": 1705625235,
+ "moonrise": 1705605900,
+ "moonset": 1705568880,
+ "moon_phase": 0.27,
+ "temp": {
+ "day": 39.09,
+ "min": 36.5,
+ "max": 40.23,
+ "night": 40.23,
+ "eve": 39.79,
+ "morn": 37.13
+ },
+ "feels_like": {
+ "day": 39.09,
+ "night": 40.23,
+ "eve": 39.79,
+ "morn": 37.13
+ },
+ "pressure": 1024,
+ "humidity": 98,
+ "dew_point": 38.34,
+ "wind_speed": 3.22,
+ "wind_deg": 29,
+ "wind_gust": 3.04,
+ "weather": [
+ {
+ "id": 500,
+ "main": "Rain",
+ "description": "light rain",
+ "icon": "10d"
+ }
+ ],
+ "clouds": 100,
+ "pop": 0.88,
+ "rain": 3.3,
+ "uvi": 1
+ },
+ {
+ "dt": 1705694400,
+ "sunrise": 1705679391,
+ "sunset": 1705711720,
+ "moonrise": 1705693620,
+ "moonset": 1705659960,
+ "moon_phase": 0.31,
+ "temp": {
+ "day": 50.5,
+ "min": 39.88,
+ "max": 50.5,
+ "night": 40.96,
+ "eve": 44.17,
+ "morn": 39.88
+ },
+ "feels_like": {
+ "day": 49.39,
+ "night": 38.59,
+ "eve": 44.17,
+ "morn": 38.16
+ },
+ "pressure": 1022,
+ "humidity": 88,
+ "dew_point": 46.85,
+ "wind_speed": 3.83,
+ "wind_deg": 129,
+ "wind_gust": 4.29,
+ "weather": [
+ {
+ "id": 803,
+ "main": "Clouds",
+ "description": "broken clouds",
+ "icon": "04d"
+ }
+ ],
+ "clouds": 63,
+ "pop": 0,
+ "uvi": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+
+
+class StockAPIQuoteResponse(BaseModel):
+ global_quote: Dict[str, str] = Field(..., alias="Global Quote")
+
+ model_config = {
+ "extra": "allow",
+ "json_schema_extra": {
+ "examples": [
+ {
+ "Global Quote": {
+ "01. symbol": "GOOG",
+ "02. open": "144.8950",
+ "03. high": "146.6600",
+ "04. low": "142.2150",
+ "05. price": "143.6700",
+ "06. volume": "17471130",
+ "07. latest trading day": "2024-01-11",
+ "08. previous close": "143.8000",
+ "09. change": "-0.1300",
+ "10. change percent": "-0.0904%"
+ }
+ }
+ ]}}
+
+
+class StockAPISearchResponse(BaseModel):
+ model_config = {
+ "extra": "allow",
+ "json_schema_extra": {
+ "examples": [
+ {
+ "bestMatches": [
+ {
+ "1. symbol": "MSF0.FRK",
+ "2. name": "MICROSOFT CORP. CDR",
+ "3. type": "Equity",
+ "4. region": "Frankfurt",
+ "5. marketOpen": "08:00",
+ "6. marketClose": "20:00",
+ "7. timezone": "UTC+02",
+ "8. currency": "EUR",
+ "9. matchScore": "0.6429"
+ },
+ {
+ "1. symbol": "MSFT",
+ "2. name": "Microsoft Corporation",
+ "3. type": "Equity",
+ "4. region": "United States",
+ "5. marketOpen": "09:30",
+ "6. marketClose": "16:00",
+ "7. timezone": "UTC-04",
+ "8. currency": "USD",
+ "9. matchScore": "0.6154"
+ },
+ {
+ "1. symbol": "0QYP.LON",
+ "2. name": "Microsoft Corporation",
+ "3. type": "Equity",
+ "4. region": "United Kingdom",
+ "5. marketOpen": "08:00",
+ "6. marketClose": "16:30",
+ "7. timezone": "UTC+01",
+ "8. currency": "USD",
+ "9. matchScore": "0.6000"
+ },
+ {
+ "1. symbol": "MSF.DEX",
+ "2. name": "Microsoft Corporation",
+ "3. type": "Equity",
+ "4. region": "XETRA",
+ "5. marketOpen": "08:00",
+ "6. marketClose": "20:00",
+ "7. timezone": "UTC+02",
+ "8. currency": "EUR",
+ "9. matchScore": "0.6000"
+ },
+ {
+ "1. symbol": "MSF.FRK",
+ "2. name": "Microsoft Corporation",
+ "3. type": "Equity",
+ "4. region": "Frankfurt",
+ "5. marketOpen": "08:00",
+ "6. marketClose": "20:00",
+ "7. timezone": "UTC+02",
+ "8. currency": "EUR",
+ "9. matchScore": "0.6000"
+ },
+ {
+ "1. symbol": "MSFT34.SAO",
+ "2. name": "Microsoft Corporation",
+ "3. type": "Equity",
+ "4. region": "Brazil/Sao Paolo",
+ "5. marketOpen": "10:00",
+ "6. marketClose": "17:30",
+ "7. timezone": "UTC-03",
+ "8. currency": "BRL",
+ "9. matchScore": "0.6000"
+ }
+ ]
+ }]}}
+
+
+class GeoAPIGeocodeResponse(BaseModel):
+ place_id: int
+ licence: str
+ osm_type: str
+ osm_id: int
+ boundingbox: List[str]
+ lat: str
+ lon: str
+ display_name: str
+ class_: Optional[str] = Field(..., alias="class")
+ type_: Optional[str] = Field(..., alias="type")
+ importance: float
+ alternate_results: List[dict]
+
+ model_config = {
+ "extra": "allow",
+ "json_schema_extra": {
+ "examples": [
+ {
+ "place_id": 288749081,
+ "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
+ "osm_type": "node",
+ "osm_id": 9106438617,
+ "boundingbox": [
+ "47.6204274",
+ "47.6205274",
+ "-122.200047",
+ "-122.199947"
+ ],
+ "lat": "47.6204774",
+ "lon": "-122.199997",
+ "display_name": "The UPS Store, 1100, Bellevue Way Northeast, Bellevue, King County, Washington, 98004, United States",
+ "class": "amenity",
+ "type": "post_office",
+ "importance": 0.53001,
+ "alternate_results": [
+ {
+ "place_id": 288749055,
+ "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
+ "osm_type": "node",
+ "osm_id": 1987569546,
+ "boundingbox": [
+ "47.6204239",
+ "47.6205239",
+ "-122.2001916",
+ "-122.2000916"
+ ],
+ "lat": "47.6204739",
+ "lon": "-122.2001416",
+ "display_name": "AAA Cruises and Travel, 1100, Bellevue Way Northeast, Bellevue, King County, Washington, 98004, United States",
+ "class": "club",
+ "type": "automobile",
+ "importance": 0.53001
+ },
+ {
+ "place_id": 288749230,
+ "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
+ "osm_type": "node",
+ "osm_id": 1987569543,
+ "boundingbox": [
+ "47.620366",
+ "47.620466",
+ "-122.201487",
+ "-122.201387"
+ ],
+ "lat": "47.620416",
+ "lon": "-122.201437",
+ "display_name": "Adventure Kids Playcare, 1100, Bellevue Way Northeast, Bellevue, King County, Washington, 98004, United States",
+ "class": "leisure",
+ "type": "playground",
+ "importance": 0.53001
+ }
+ ]
+ }]}}
+
+
+class GeoAPIReverseResponse(BaseModel):
+ place_id: int
+ licence: str
+ osm_type: str
+ osm_id: int
+ lat: str
+ lon: str
+ display_name: str
+ address: Dict[str, str]
+ boundingbox: List[str]
+ model_config = {
+ "extra": "allow",
+ "json_schema_extra": {
+ "examples": [
+ {
+ "place_id": 288417123,
+ "licence": "Data © OpenStreetMap contributors, ODbL 1.0. https://osm.org/copyright",
+ "osm_type": "way",
+ "osm_id": 325822620,
+ "lat": "47.68148615",
+ "lon": "-122.20873015166683",
+ "display_name": "807, 1st Street, Juanita, Kirkland, King County, Washington, 98033, United States",
+ "address": {
+ "house_number": "807",
+ "road": "1st Street",
+ "suburb": "Juanita",
+ "town": "Kirkland",
+ "county": "King County",
+ "state": "Washington",
+ "ISO3166-2-lvl4": "US-WA",
+ "postcode": "98033",
+ "country": "United States",
+ "country_code": "us"
+ },
+ "boundingbox": [
+ "47.6814167",
+ "47.6815308",
+ "-122.2088759",
+ "-122.2086379"
+ ]
+ }]}}
+
+
+class WolframAlphaAPIResponse(BaseModel):
+ answer: str
+ model_config = {
+ "extra": "allow",
+ "json_schema_extra": {
+ "examples": [
+ {
+ "answer": "The distance from Earth to the Moon at 4:29 P.M. Pacific Standard Time, Friday, January 12, 2024 is about 225192 miles"
+ }, {
+ "answer": "about 5378 miles"
+ }, {
+ "answer": "\n\n \n 1000
\n input parameter not present in query\n \n"
+ }]}}
+
+
+class ScriptParserResponse(BaseModel):
+ ncs: str
+ model_config = {
+ "extra": "allow",
+ "json_schema_extra": {
+ "examples": [{
+ "ncs": "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KZRoBksAaAdomGgJXZR1fZQoaANLPWgEjA5MT09QIGNoZWNrIEVORJRoBksAaAdqAgEAAGgJXZR1fZQoaANLPmgEjEwiTWFpbCBUaXRsZSIsICJlbWFpbCBib2R5IGdvZXMgaGVyZS4gY291bGQgYmUgYSB2YXJpYWJsZSBuYW1lIGluIG1vc3QgY2FzZXMilGgGSwBoB4wFZW1haWyUaAldlGgLfZQojAdzdWJqZWN0lIwMIk1haWwgVGl0bGUilIwEYm9keZSMPiJlbWFpbCBib2R5IGdvZXMgaGVyZS4gY291bGQgYmUgYSB2YXJpYWJsZSBuYW1lIGluIG1vc3QgY2FzZXMilHV1fZQoaANLP2gEjBBzY3JpcHRfbmFtZV9oZXJllGgGSwBoB4wDcnVulGgJXZRoC32UjA1zY3JpcHRfdG9fcnVulGomAQAAc3V9lChoA0tAaASMBEV4aXSUaAZLAGgHjARleGl0lGgJXZR1ZX2UKGh7aHxoWWheaFxoXYwNb3ZlcnJpZGVfdXNlcpSIdX2UKGhgTmhuTnV9lIwFY2hlY2uUfZQojAVzdGFydJRLOIwDZW5klEs9dXN9lGiQSx1zSxROXZQoaDxoQmhIZX2UKGhOaFBoVWhWdX2UKIwIY3ZlcnNpb26UjAUwLjUuMJSMCGNvbXBpbGVklEpc6qFljAhjb21waWxlcpSMFU5lb24gQUkgU2NyaXB0IFBhcnNlcpRoDWgFaBBoD2gVjE8KSnVzdCBhbiBleGFtcGxlIGRlc2NyaXB0aW9uIHRvIGdvIHdpdGgKYW4gZXhhbXBsZSBzY3JpcHQuIFRoaXMgd2lsbCBnbyBpbiBtZXRhlIwIcmF3X2ZpbGWUWEAFAAAKU2NyaXB0OiBQYXJzZXIgVGVzdCBTY3JpcHQKQXV0aG9yOiBEYW5pZWwgTWNLbmlnaHQKRGVzY3JpcHRpb246CiAgICBKdXN0IGFuIGV4YW1wbGUgZGVzY3JpcHRpb24gdG8gZ28gd2l0aAogICAgYW4gZXhhbXBsZSBzY3JpcHQuIFRoaXMgd2lsbCBnbyBpbiBtZXRhCgojIFRpbWVvdXQgZ290byBsaW5lIDE4ClRpbWVvdXQ6IDEwLCAxOAojIFRpbWVvdXQgZXhpdApUaW1lb3V0OiAyMAoKU3lub255bTogIlRlc3QgU2NyaXB0IgogICAgIlRlc3RlciBTY3JpcHQiCiAgICAiQW5vdGhlciBTeW5vbnltIgpDbGFwczogMiBUd28gY2xhcCBhY3Rpb24KICAgIDMgMyBjbGFwIGFjdGlvbgpMYW5ndWFnZTogZW4tdXMsIG1hbGUKClZhcmlhYmxlOiBub192YWwKVmFyaWFibGU6IHdpdGhfdmFsID0gIlRlc3QgVmFsdWUiCgojIFRoaXMgaXMgYSBjb21tZW50IGxpbmUgc2VwYXJhdGluZyBoZWFkZXIgZnJvbSBleGVjdXRpb24gKGtpbmRhKQpOZW9uIHNwZWFrOiBpbmxpbmVkIHNwZWFrCk5lb24gc3BlYWs6CiAgICBCbG9jayBzcGVlY2ggc3RhcnQKICAgIC4uLgogICAgQmxvY2sgc3BlZWNoIGVuZApAcHJlLWV4ZWMKRXhlY3V0ZTogaGVsbG8gd29ybGQKdm9pY2VfaW5wdXQobm9fdmFsKQpJRiBub192YWwgPT0gd2l0aF92YWw6CiAgICBHb3RvOiBwcmUtZXhlYwpFTFNFOgogICAgUmVjb252ZXk6IHByZS1leGVjCgpJZiAid29yZCIgSU4gInRoaXMgcGhyYXNlIHdvcmQgaXMgaW4iOgogICAgTmVvbiBzcGVhazogInBocmFzZSIKClJlY29udmV5OiBwcmUtZXhlYywgZmlsZV9wYXJhbQpOYW1lIFJlY29udmV5OiAiU29tZW9uZSIsICJzb21lIHRleHQiLCAiL3BhdGgvdG8vZmlsZSIKCkNhc2Uge3dpdGhfdmFsfToKICAgICJTb21lIHZhbHVlIgogICAgICAgIE5lb24gc3BlYWs6IGZpcnN0CiAgICAic29tZSBvdGhlciB2YWx1ZSIKICAgICAgICBOZW9uIHNwZWFrOgogICAgICAgICAgICBzZWNvbmQKCkNhc2Uobm9fdmFsKToKICAgICJub192YWxfMSI6CiAgICAgICAgRXhlY3V0ZTogd2hhdCB0aW1lIGlzIGl0CgpQeXRob246IDEqMiAgIyBUT0RPOiBzeW50YXggY2hlY2sKCkxPT1AgY2hlY2sgU1RBUlQKU2V0OiBuZXdfdmFsID0gbm9fdmFsICAjIFRoaXMgbG9ncyBhbiBlcnJvciBiZWNhdXNlIGl0IGlzbid0IGRlY2xhcmVkCiMgVE9ETzogVGhlIGZvbGxvd2luZyBzaG91bGQgd2Fybi9lcnJvcgpkbmUgPSAidGVzdCIKdm9pY2VfaW5wdXQobmV3X3ZhbCkKTE9PUCBjaGVjayBFTkQKRW1haWw6ICJNYWlsIFRpdGxlIiwgImVtYWlsIGJvZHkgZ29lcyBoZXJlLiBjb3VsZCBiZSBhIHZhcmlhYmxlIG5hbWUgaW4gbW9zdCBjYXNlcyIKUnVuOiBzY3JpcHRfbmFtZV9oZXJlCkV4aXSUdX2UKGhgaGdobmhwdWUu"
+ }]}}
+
+
+class CouponsResponse(BaseModel):
+ success: bool
+ brands: List[str]
+ coupons: List[str]
+
+ model_config = {
+ "extra": "allow",
+ "json_schema_extra": {
+ "examples": [{
+ "success": True,
+ "brands": [
+ "amazon",
+ "blue apron",
+ "home depot",
+ "old navy",
+ "bed bath and beyond",
+ "sears",
+ "dominos",
+ "budget car rental",
+ "orbitz",
+ "target",
+ "kohls",
+ "nordstrom",
+ "amazon prime now",
+ "apple",
+ "google",
+ "coca cola",
+ "microsoft",
+ "mycroft",
+ "samsung",
+ "ebikes",
+ "kroll maps",
+ "brand",
+ "harrypotter",
+ "conversation processing intelligence",
+ "value added websites",
+ "neon",
+ "steve jones",
+ "alpha",
+ "beta",
+ "gamma",
+ "december",
+ "strata",
+ "intelligent",
+ "flower",
+ "argon",
+ "steve",
+ "elon 5000",
+ "theta",
+ "pool",
+ "josh",
+ "test",
+ "grass",
+ "testing",
+ "demo",
+ "mack",
+ "june",
+ "door dash",
+ "newegg",
+ "amtrak",
+ "toms",
+ "graco",
+ "otterbox",
+ "auto zone",
+ "uber",
+ "papa johns",
+ "bed bath and beyond",
+ "target",
+ "macys",
+ "best buy",
+ "dominos",
+ "office depot",
+ "kohls",
+ "bath and body works",
+ "best buy",
+ "budget rental car",
+ "carters",
+ "dicks sporting goods",
+ "door dash",
+ "enterprise rental car",
+ "famous footware",
+ "fashion nova",
+ "home depot",
+ "hotels.com",
+ "jcpenney",
+ "michaels",
+ "old navy",
+ "oriental trading company",
+ "pizza hut",
+ "post mates",
+ "sephora",
+ "shutterfly",
+ "southwest airlines",
+ "uber eats",
+ "ulta",
+ "victorias secret",
+ "spirit airlines",
+ "rock auto",
+ "vistaprint",
+ "panera bread",
+ "ebay",
+ "walgreens",
+ "revolve",
+ "priceline",
+ "hotels.com",
+ "1800flowers",
+ "airbnb",
+ "staples",
+ "jet.com",
+ "dell.com",
+ "jomashop",
+ "rakuten",
+ "walmart",
+ "groupon",
+ "barnes and noble",
+ "new york times",
+ "old navy",
+ "sprint",
+ "delta",
+ "alaska air",
+ "ford",
+ "mcdonalds",
+ "taco bell",
+ "red lobster",
+ "olive garden",
+ "olive garden",
+ "applebees",
+ "starbucks",
+ "target",
+ "bed bath and beyond",
+ "ticketmaster",
+ "airbnb",
+ "dominos",
+ "papa johns",
+ "door dash",
+ "ashley home store",
+ "august"
+ ],
+ "coupons": [
+ "\"1800flowers\",\"20% Off Flowers And Gifts\",\"SAVETWENTY\"",
+ "\"airbnb\",\"Save 10% on your AirBnB booking\",\"SAVE10\"",
+ "\"airbnb\",\"$40 off your booking with Airbnb\",\"dsenter10\"",
+ "\"alaska air\",\"5% Off Flights For Insider Members\",\"EC6208\"",
+ "\"alpha\",\"Save 10% off with Alpha Brand!\",\"ALF10\"",
+ "\"amazon\",\"NEW CUSTOMERS! $10 OFF YOUR FIRST PRIME NOW ORDER.\",\"10PRIMENOW\"",
+ "\"amazon prime now\",\"Up to $20 Off Your First Orders Through Prime Now Or Whole Foods Market\",\"20PRIMEDAY\"",
+ "\"amtrak\",\"Buy One Ticket, Get One Free When You Share a Bedroom or Roomette\",\"V540\"",
+ "\"apple\",\"$5 Cash Back on $50 for Beats, iPods, and Accessories\",\"ONLINE\"",
+ "\"applebees\",\"$5 Off $25+ Your First Online Order\",\"5OFF25\"",
+ "\"argon\",\"Save 10% on Argon\",\"Argon10off\"",
+ "\"ashley home store\",\"Up to 70% Off + Extra 10% Off + 12 Months Special Financing\",\"POPUP19\"",
+ "\"auto zone\",\"10% on Auto Zone Orders Online or In store\",\"NGZone\"",
+ "\"barnes and noble\",\"25% Off All Eligible NOOK Book Bash Items With Coupon Code\",\"NOOKBASH25\"",
+ "\"bath and body works\",\"20% Off With Promo Code online\",\"TWYSURT\"",
+ "\"bed bath and beyond\",\"20% Off One Item In-Store\",\"20OFFBBB\"",
+ "\"bed bath and beyond\",\"20% Off online orders\",\"20OFF\"",
+ "\"bed bath and beyond\",\"Save 20% off when you sign up for emails.\",\"None needed\"",
+ "\"best buy\",\"Save 20% on regular-priced appliances with promo code\",\"SAVEONSMALLSNOW\"",
+ "\"best buy\",\"20% Off one regular priced item\",\"APPLY20RMNNOW\"",
+ "\"beta\",\"Save 20% off BETA and free shipping\",\"BETA20\"",
+ "\"blue apron\",\"$30 OFF YOUR FIRST DELIVERY!\",\"BA17B25\"",
+ "\"brand\",\"A 1-2-3 Punch of savings\",\"Brand123\"",
+ "\"budget car rental\",\"Up to $25 Off Base Rate on your Car Rental with Minimum Spend\",\"MUWZ092\"",
+ "\"budget rental car\",\"$40 Off Intermediate Or Larger Vehicle Rent\",\"MUGZ025\"",
+ "\"carters\",\"Extra 20% Off Your $50+ In-Store Purchase\",\"CART20\"",
+ "\"coca cola\",\"GET REWARDED WHEN YOU BUY COKE PRODUCTS\",\"REWARDS\"",
+ "\"conversation processing intelligence\",\"Get free conversation transcription\",\"CPI180822\"",
+ "\"december\",\"Get 10% off in December\",\"DECEMBER10\"",
+ "\"dell.com\",\"15% off site wide\",\"SAVE15\"",
+ "\"delta\",\"Up to $250 Off Summer Vacation Bookings To The Caribbean + Earn 2,500 Extra Bonus Miles Per Person\",\"DVSUMMERA\"",
+ "\"demo\",\"Save 10% off demo\",\"Demo10\"",
+ "\"dicks sporting goods\",\"Extra 10% Off Next Purchase With Dick's Sporting Goods Email Sign Up\",\"No Code needed\"",
+ "\"dominos\",\"Carryout Large 3-Topping Pizza for $7.99\",\"9174\"",
+ "\"dominos\",\"30% off Large Traditional & Premium Pizzas, Pick up or Delivered\",\"355852\"",
+ "\"dominos\",\"40% off your order of regular priced pizza\",\"222233\"",
+ "\"door dash\",\"$5 off $10 on Pickup Orders\",\"PICKUPTIME\"",
+ "\"door dash\",\"$5 Off $15\",\"NOVDASH18\"",
+ "\"door dash\",\"$15 off your order\",\"FSt4vk\"",
+ "\"ebay\",\"$5 Off Your Order For New Users\",\"WELCOME5\"",
+ "\"ebikes\",\"10% off ebike kit #1\",\"EB01\"",
+ "\"elon 5000\",\"Brightest mind in the Northwest\",\"XYZ\"",
+ "\"enterprise rental car\",\"10 Off when you book a luxury car\",\"10Off\"",
+ "\"famous footware\",\"$10 Off Your Orders of $50+\",\"ENTR2018\"",
+ "\"fashion nova\",\"30% Off Your Fashion Nova Purchase + Free Shipping Over $75\",\"NOVABABE5-56NS46\"",
+ "\"flower\",\"Save 10% off all flowers!\",\"Flower10\"",
+ "\"ford\",\"10% Off Sitewide\",\"FORDSUMMER14\"",
+ "\"gamma\",\"save on all of your GAMMA needs!\",\"GAMMA30\"",
+ "\"google\",\"$15 off your purchase WITH GOOGLE EXPRESS\",\"KGUCT33MF\"",
+ "\"graco\",\"20% Off Sitewide (Excluding 4Ever)\",\"SUMMEROFGRACO\"",
+ "\"grass\",\"Save 10% off Grass\",\"Grass10\"",
+ "\"groupon\",\"SAVE10\",\"$10 off your order\"",
+ "\"guy's ebikes\",\"20% off\",\"EB1\"",
+ "\"harrypotter\",\"Get a free wizard!\",\"HarryPotterWizard\"",
+ "\"home depot\",\"Additional 10% Off Cutlery Items And Accessories\",\"CHOPUPSAVINGS\"",
+ "\"home depot\",\"$5 Off Coupon With Home Depot Email Signup\",\"No Coupon Code\"",
+ "\"hotels.com\",\"Extra 10% Off Select Hotels\",\"RC10\"",
+ "\"hotels.com\",\"Extra 10% Off Select Hotels\",\"WEDDING10\"",
+ "\"intelligent\",\"SAve 10% off intelligent devices\",\"Intelligent10\"",
+ "\"jcpenney\",\"20% In Store and Online\",\"GIFTDAD\"",
+ "\"jet.com\",\"30% off all Grocery Pup items\",\"GROCERYPUP\"",
+ "\"jomashop\",\"$10 off orders of $150\",\"AD10\"",
+ "\"josh\",\"Josh saves 10%\",\"Josh10\"",
+ "\"june\",\"save 10% on June! Wow!\",\"JUNE10\"",
+ "\"kohls\",\"Save an Extra 20% Off\",\"FIREWORK\"",
+ "\"kohls\",\"15% Off $100 or More + Free Shipping\",\"CATCH15OFF\"",
+ "\"kroll maps\",\"20% off all European Maps!!!\",\"KR01\"",
+ "\"mack\",\"Save 10% off all MAck products\",\"Mack10\"",
+ "\"macys\",\"30% Off Lauren Ralph Lauren\",\"FRIEND\"",
+ "\"mcdonalds\",\"McDonald's Offers, Codes, In-store Coupons, And More\",\"Sign up in App\"",
+ "\"michaels\",\"50% Off One Regular-Priced Item With Michaels Coupon\",\"50HALFBDAY\"",
+ "\"microsoft\",\"10% off + Free shipping for students and parents\",\"10% OFF\"",
+ "\"mycroft\",\"KICKSTARTER - Pledge $299 or more 3-Pack of Mark II devices\",\"MARKII -\"",
+ "\"neon\",\"Save 20% when you buy a NeonX 10 inch audio pc.\",\"NeonXSave20\"",
+ "\"new york times\",\"15% Off Orders Over $50\",\"MOM15\"",
+ "\"newegg\",\"5% Off $50+ on Select CPUs, Input Devices & More\",\"CORNSAVE519\"",
+ "\"nordstrom\",\"Free 21-Piece Gift With Your $75 Beauty Or Fragrance Purchase\",\"TEAL\"",
+ "\"office depot\",\"20% Off Your Qualifying Regular Priced Purchase\",\"DMXP6\"",
+ "\"old navy\",\"OHYES\",\"ohyes\"",
+ "\"old navy\",\"20% Off Sitewide\",\"SWEET\"",
+ "\"old navy\",\"20% Off Your Purchase With Old Navy Email Sign-up\",\"No Code needed\"",
+ "\"olive garden\",\"Free Appetizer Or Dessert With Olive Garden Email Signup\",\"No Code needed\"",
+ "\"olive garden\",\"$2 Off 2 Lunches\",\"2OFF2L\"",
+ "\"orbitz\",\"15% Off Select Hotels\",\"HEATWAVE\"",
+ "\"oriental trading company\",\"Up to $40 Off + Free Shipping on $49\",\"6SAVENOW\"",
+ "\"otterbox\",\"10% off\",\"ULTIMATE10\"",
+ "\"panera bread\",\"50% Off Orders $25+ For Rapid Pick-Up Order\",\"MMDRF\"",
+ "\"papa johns\",\"30% Off Regular Menu-Priced Orders\",\"GET30\"",
+ "\"papa johns\",\"30% Off Regular Menu-Priced Orders with Promo Code!\",\"GET30\"",
+ "\"pizza hut\",\"BF9VY9VE4XE2\",\"BF9VY9VE4XE2\"",
+ "\"pool\",\"save on your pool!\",\"pool10\"",
+ "\"post mates\",\"$100 in delivery credits\",\"FOOD4YOU\"",
+ "\"priceline\",\"8% Off Select Hotels\",\"RMNJUN8\"",
+ "\"rakuten\",\"20% Clothing, Shoes and Accessories\",\"APPAREL20\"",
+ "\"red lobster\",\"10% Off Any To Go Order\",\"LOBSTER75\"",
+ "\"revolve\",\"20% Off Sitewide\",\"REVOLVE4AU\"",
+ "\"rock auto\",\"5% off your order\",\"10703653554843330\"",
+ "\"samsung\",\"$100 Off Samsung POWERbot Robot Vacuum + Additional $50 Off + Free Shipping\",\"PLXGUA9Z7\"",
+ "\"sears\",\"Extra $35 Off $300+ on Home Appliances, Lawn & Garden, Tools, Mattresses & Sporting Goods\",\"SEARS35OFF300\"",
+ "\"sephora\",\"FREE gift with select purchase online only\",\"PICKYOURS\"",
+ "\"shutterfly\",\"30% Off Sitewide\",\"30SAVINGS\"",
+ "\"southwest airlines\",\"Up to 35% Off Base Rate For Hertz Rentals + Up to 2400 Rapid Rewards Points\",\"159062\"",
+ "\"spirit airlines\",\"$50 off bookings\",\"CD50\"",
+ "\"sprint\",\"50% Off When You Upgrade\",\"No Code needed\"",
+ "\"staples\",\"$15 Off Orders of $100+\",\"67914\"",
+ "\"starbucks\",\"$5 Gift With Your Order\",\"wjmnm\"",
+ "\"steve\",\"Test of Steve\",\"test\"",
+ "\"steve jones\",\"Free Call From Steve\",\"steve\"",
+ "\"strata\",\"save 10% off strata\",\"STRATA10\"",
+ "\"taco bell\",\"10% Off Online Order\",\"Save 10% when you order online\"",
+ "\"target\",\"$5 Off $50 Select Items + Free Shipping on Qualifying Purchases\",\"90209\"",
+ "\"target\",\"$5 Off $50 Select Items at Target + Free Shipping\",\"No Code needed\"",
+ "\"target\",\"$5 Off $50 Select Items at Target + Free Shipping\",\"FIVEOFF\"",
+ "\"test\",\"save 10% off test\",\"Test10\"",
+ "\"testing\",\"Save 10% off\",\"Test10\"",
+ "\"theta\",\"saave 10% off Theta\",\"theta10\"",
+ "\"ticketmaster\",\"Save 50% when you buy two tickets\",\"TMN241\"",
+ "\"toms\",\"$10 off any order and free shipping\",\"CHANGE\"",
+ "\"uber\",\"$5 off each of your first 3 trips\",\"NEWRIDER15\"",
+ "\"uber eats\",\"40% Off First Order\",\"SAVE40\"",
+ "\"ulta\",\"401534\",\"401534\"",
+ "\"value added websites\",\"100% off all new websites!\",\"VAW01\"",
+ "\"victorias secret\",\"FREE shipping on orders over $50\",\"SHIP50\"",
+ "\"vistaprint\",\"Up to 50% Off Everything Only at Vistaprint!\",\"SALE50\"",
+ "\"walgreens\",\"50% Off Prints ...\",\"COOLPIX\"",
+ "\"walmart\",\"$10 Off Orders $50+ at Walmart Grocery\",\"LA9ARAAC\""
+ ]}]}}
diff --git a/neon_hana/schema/assist_requests.py b/neon_hana/schema/assist_requests.py
new file mode 100644
index 0000000..7af09b7
--- /dev/null
+++ b/neon_hana/schema/assist_requests.py
@@ -0,0 +1,108 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from typing import List, Optional
+from pydantic import BaseModel
+
+from neon_hana.schema.node_model import NodeData
+from neon_hana.schema.user_profile import UserProfile
+
+
+class STTRequest(BaseModel):
+ encoded_audio: str
+ lang_code: str
+
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "encoded_audio": "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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ "lang_code": "en-us"
+ }]}}
+
+
+class STTResponse(BaseModel):
+ transcripts: List[str]
+ parser_data: dict
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "transcripts": ["hello"],
+ "parser_data": {'client_name': 'ovos_dinkum_listener',
+ 'destination': ['skills'], 'source': 'audio'}
+ }]}}
+
+
+class TTSRequest(BaseModel):
+ to_speak: str
+ lang_code: str
+ gender: str = "female"
+
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "to_speak": "hello",
+ "lang_code": "en-us"
+ }]}}
+
+
+class TTSResponse(BaseModel):
+ encoded_audio: str
+
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "encoded_audio": "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AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ }]}}
+
+
+class SkillRequest(BaseModel):
+ utterance: str
+ lang_code: str
+ user_profile: UserProfile = UserProfile()
+ node_data: Optional[NodeData] = NodeData()
+
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "utterance": "what time is it",
+ "lang_code": "en-us",
+ "user_profile": {"location": {"lat": 40.730610,
+ "lon": -73.935242,
+ "city": "New York",
+ "state": "New York"}}
+ }]}}
+
+
+class SkillResponse(BaseModel):
+ answer: str
+ lang_code: str
+
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "answer": "four forty three.",
+ "lang_code": "en-us"
+ }]}}
diff --git a/neon_hana/schema/auth_requests.py b/neon_hana/schema/auth_requests.py
new file mode 100644
index 0000000..d02724d
--- /dev/null
+++ b/neon_hana/schema/auth_requests.py
@@ -0,0 +1,67 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from typing import Optional
+from uuid import uuid4
+
+from pydantic import BaseModel, Field
+
+
+class AuthenticationRequest(BaseModel):
+ username: str = "guest"
+ password: Optional[str] = None
+ client_id: str = Field(default_factory=lambda: str(uuid4()))
+
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "username": "guest",
+ "password": "password"
+ }]}}
+
+
+class AuthenticationResponse(BaseModel):
+ username: str
+ client_id: str
+ access_token: str
+ refresh_token: str
+ expiration: float
+
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "username": "guest",
+ "client_id": "be84ae66-f61c-4aac-a9af-b0da364b82b6",
+ "access_token": "",
+ "refresh_token": "",
+ "expiration": 1706045776.4168212
+ }]}}
+
+
+class RefreshRequest(BaseModel):
+ access_token: str
+ refresh_token: str
+ client_id: str
diff --git a/neon_hana/schema/llm_requests.py b/neon_hana/schema/llm_requests.py
new file mode 100644
index 0000000..1861f4d
--- /dev/null
+++ b/neon_hana/schema/llm_requests.py
@@ -0,0 +1,54 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from typing import List
+
+from pydantic import BaseModel
+
+
+class LLMRequest(BaseModel):
+ query: str
+ history: List[tuple] = []
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "query": "I am well, how about you?",
+ "history": [("user", "hello"),
+ ("llm", "Hi, how can I help you today?")]}]}}
+
+
+class LLMResponse(BaseModel):
+ response: str
+ history: List[tuple]
+ model_config = {
+ "json_schema_extra": {
+ "examples": [{
+ "query": "I am well, how about you?",
+ "history": [("user", "hello"),
+ ("llm", "Hi, how can I help you today?"),
+ ("user", "I am well, how about you?"),
+ ("llm", "As a large language model, I do not feel")
+ ]}]}}
diff --git a/neon_hana/schema/node_model.py b/neon_hana/schema/node_model.py
new file mode 100644
index 0000000..3fbdc50
--- /dev/null
+++ b/neon_hana/schema/node_model.py
@@ -0,0 +1,57 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+from uuid import uuid4
+
+from pydantic import BaseModel, Field
+from typing import Optional, Dict
+
+
+class NodeSoftware(BaseModel):
+ operating_system: str = ""
+ os_version: str = ""
+ neon_packages: Optional[Dict[str, str]] = None
+
+
+class NodeNetworking(BaseModel):
+ local_ip: str = "127.0.0.1"
+ public_ip: str = ""
+ mac_address: str = ""
+
+
+class NodeLocation(BaseModel):
+ lat: Optional[float] = None
+ lon: Optional[float] = None
+ site_id: Optional[str] = None
+
+
+class NodeData(BaseModel):
+ device_id: str = Field(default_factory=lambda: str(uuid4()))
+ device_name: str = ""
+ device_description: str = ""
+ platform: str = ""
+ networking: NodeNetworking = NodeNetworking()
+ software: NodeSoftware = NodeSoftware()
+ location: NodeLocation = NodeLocation()
diff --git a/neon_hana/schema/user_profile.py b/neon_hana/schema/user_profile.py
new file mode 100644
index 0000000..91a9a05
--- /dev/null
+++ b/neon_hana/schema/user_profile.py
@@ -0,0 +1,104 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from typing import Optional, List
+from pydantic import BaseModel
+
+
+class ProfileUser(BaseModel):
+ first_name: str = ""
+ middle_name: str = ""
+ last_name: str = ""
+ preferred_name: str = ""
+ full_name: str = ""
+ dob: str = "YYYY/MM/DD"
+ age: str = ""
+ email: str = ""
+ username: str = ""
+ password: str = ""
+ picture: str = ""
+ about: str = ""
+ phone: str = ""
+ phone_verified: bool = False
+ email_verified: bool = False
+
+
+class ProfileBrands(BaseModel):
+ ignored_brands: dict = {}
+ favorite_brands: dict = {}
+ specially_requested: dict = {}
+
+
+class ProfileSpeech(BaseModel):
+ stt_language: str = "en-us"
+ alt_languages: List[str] = ['en']
+ tts_language: str = "en-us"
+ tts_gender: str = "female"
+ neon_voice: Optional[str] = ''
+ secondary_tts_language: Optional[str] = ''
+ secondary_tts_gender: str = "male"
+ secondary_neon_voice: str = ''
+ speed_multiplier: float = 1.0
+
+
+class ProfileUnits(BaseModel):
+ time: int = 12
+ # 12, 24
+ date: str = "MDY"
+ # MDY, YMD, YDM
+ measure: str = "imperial"
+ # imperial, metric
+
+
+class ProfileLocation(BaseModel):
+ lat: Optional[float] = None
+ lng: Optional[float] = None
+ city: Optional[str] = None
+ state: Optional[str] = None
+ country: Optional[str] = None
+ tz: Optional[str] = None
+ utc: Optional[float] = None
+
+
+class ProfileResponseMode(BaseModel):
+ speed_mode: str = "quick"
+ hesitation: bool = False
+ limit_dialog: bool = False
+
+
+class ProfilePrivacy(BaseModel):
+ save_audio: bool = False
+ save_text: bool = False
+
+
+class UserProfile(BaseModel):
+ user: ProfileUser = ProfileUser()
+ # brands: ProfileBrands
+ speech: ProfileSpeech = ProfileSpeech()
+ units: ProfileUnits = ProfileUnits()
+ location: ProfileLocation = ProfileLocation()
+ response_mode: ProfileResponseMode = ProfileResponseMode()
+ privacy: ProfilePrivacy = ProfilePrivacy()
diff --git a/neon_hana/version.py b/neon_hana/version.py
new file mode 100644
index 0000000..ef7228e
--- /dev/null
+++ b/neon_hana/version.py
@@ -0,0 +1,29 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2022 Neongecko.com Inc.
+# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds,
+# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo
+# BSD-3 License
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+__version__ = "0.0.1"
diff --git a/requirements/requirements.txt b/requirements/requirements.txt
new file mode 100644
index 0000000..1948175
--- /dev/null
+++ b/requirements/requirements.txt
@@ -0,0 +1,8 @@
+pyyaml>=5.4,<7.0
+fastapi~=0.95
+uvicorn~=0.25
+pydantic~=2.5
+pyjwt~=2.8
+token-throttler~=1.4
+neon-mq-connector~=0.7
+ovos-config~=0.0.12
\ No newline at end of file
diff --git a/requirements/test_requirements.txt b/requirements/test_requirements.txt
new file mode 100644
index 0000000..68e751a
--- /dev/null
+++ b/requirements/test_requirements.txt
@@ -0,0 +1,2 @@
+pytest
+mock
\ No newline at end of file
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..892e74c
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,82 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2022 Neongecko.com Inc.
+# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds,
+# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo
+# BSD-3 License
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+from setuptools import setup, find_packages
+from os import getenv, path
+
+BASE_PATH = path.abspath(path.dirname(__file__))
+
+
+def get_requirements(requirements_filename: str):
+ requirements_file = path.join(BASE_PATH, "requirements", requirements_filename)
+ with open(requirements_file, 'r', encoding='utf-8') as r:
+ requirements = r.readlines()
+ requirements = [r.strip() for r in requirements if r.strip() and not r.strip().startswith("#")]
+
+ for i in range(0, len(requirements)):
+ r = requirements[i]
+ if "@" in r:
+ parts = [p.lower() if p.strip().startswith("git+http") else p for p in r.split('@')]
+ r = "@".join(parts)
+ if getenv("GITHUB_TOKEN"):
+ if "github.com" in r:
+ requirements[i] = r.replace("github.com", f"{getenv('GITHUB_TOKEN')}@github.com")
+ return requirements
+
+
+with open(path.join(BASE_PATH, "README.md"), "r") as f:
+ long_description = f.read()
+
+with open(path.join(BASE_PATH, "neon_hana",
+ "version.py"), "r", encoding="utf-8") as v:
+ for line in v.readlines():
+ if line.startswith("__version__"):
+ if '"' in line:
+ version = line.split('"')[1]
+ else:
+ version = line.split("'")[1]
+
+
+setup(
+ name='neon-hana',
+ version=version,
+ description='HTTP API for Neon Applications',
+ long_description=long_description,
+ long_description_content_type="text/markdown",
+ url='https://github.com/NeonGeckoCom/neon-hana',
+ author='NeonGecko',
+ author_email='developers@neon.ai',
+ license='BSD-3-Clause',
+ packages=find_packages(),
+ install_requires=get_requirements("requirements.txt"),
+ zip_safe=True,
+ classifiers=[
+ 'Intended Audience :: Developers',
+ 'Programming Language :: Python :: 3.6',
+ ]
+)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..d782cbb
--- /dev/null
+++ b/tests/__init__.py
@@ -0,0 +1,25 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tests/test_auth.py b/tests/test_auth.py
new file mode 100644
index 0000000..5bec3d9
--- /dev/null
+++ b/tests/test_auth.py
@@ -0,0 +1,142 @@
+# NEON AI (TM) SOFTWARE, Software Development Kit & Application Development System
+# All trademark and other rights reserved by their respective owners
+# Copyright 2008-2021 Neongecko.com Inc.
+# BSD-3
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+# 3. Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from this
+# software without specific prior written permission.
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import unittest
+from time import time
+from uuid import uuid4
+
+from fastapi import HTTPException
+
+
+class TestClientManager(unittest.TestCase):
+ from neon_hana.auth.client_manager import ClientManager
+ client_manager = ClientManager({"access_token_secret": "a800445648142061fc238d1f84e96200da87f4f9f784108ac90db8b4391b117b",
+ "refresh_token_secret": "a800445648142061fc238d1f84e96200da87f4f9f784108ac90db8b4391b117b",
+ "disable_auth": False})
+
+ def test_check_auth_request(self):
+ client_1 = str(uuid4())
+ client_2 = str(uuid4())
+ request_1 = {"username": "guest", "password": None,
+ "client_id": client_1}
+ request_2 = {"username": "guest", "password": None,
+ "client_id": client_2}
+
+ # Check simple auth
+ auth_resp_1 = self.client_manager.check_auth_request(**request_1)
+ self.assertEqual(self.client_manager.authorized_clients[client_1],
+ auth_resp_1)
+ self.assertEqual(auth_resp_1['username'], 'guest')
+ self.assertEqual(auth_resp_1['client_id'], client_1)
+
+ # Check auth from different client
+ auth_resp_2 = self.client_manager.check_auth_request(**request_2)
+ self.assertNotEquals(auth_resp_1, auth_resp_2)
+ self.assertEqual(self.client_manager.authorized_clients[client_2],
+ auth_resp_2)
+ self.assertEqual(auth_resp_2['username'], 'guest')
+ self.assertEqual(auth_resp_2['client_id'], client_2)
+
+ # Check auth already authorized
+ self.assertEqual(auth_resp_2,
+ self.client_manager.check_auth_request(**request_2))
+
+ def test_validate_auth(self):
+ valid_client = str(uuid4())
+ invalid_client = str(uuid4())
+ auth_response = self.client_manager.check_auth_request(
+ username="valid", client_id=valid_client)['access_token']
+
+ self.assertTrue(self.client_manager.validate_auth(auth_response,
+ "127.0.0.1"))
+ self.assertFalse(self.client_manager.validate_auth(invalid_client,
+ "127.0.0.1"))
+
+ expired_token = self.client_manager._create_tokens(
+ {"client_id": invalid_client, "username": "test",
+ "password": "test", "expire": time()})['access_token']
+ self.assertFalse(self.client_manager.validate_auth(expired_token,
+ "127.0.0.1"))
+
+ self.client_manager._rpm = 1
+ self.assertTrue(self.client_manager.validate_auth(auth_response,
+ "192.168.1.2"))
+ with self.assertRaises(HTTPException) as e:
+ self.client_manager.validate_auth(auth_response, "192.168.1.2")
+ self.assertEqual(e.exception.status_code, 429)
+
+ def test_check_refresh_request(self):
+ valid_client = str(uuid4())
+ tokens = self.client_manager._create_tokens({"client_id": valid_client,
+ "username": "test",
+ "password": "test",
+ "expire": time()})
+ self.assertEqual(tokens['client_id'], valid_client)
+
+ # Test invalid refresh token
+ with self.assertRaises(HTTPException) as e:
+ self.client_manager.check_refresh_request(tokens['access_token'],
+ valid_client,
+ valid_client)
+ self.assertEqual(e.exception.status_code, 400)
+
+ # Test incorrect access token
+ with self.assertRaises(HTTPException) as e:
+ self.client_manager.check_refresh_request(tokens['refresh_token'],
+ tokens['refresh_token'],
+ valid_client)
+ self.assertEqual(e.exception.status_code, 403)
+
+ # Test invalid client_id
+ with self.assertRaises(HTTPException) as e:
+ self.client_manager.check_refresh_request(tokens['access_token'],
+ tokens['refresh_token'],
+ str(uuid4()))
+ self.assertEqual(e.exception.status_code, 403)
+
+ # Test valid refresh
+ valid_refresh = self.client_manager.check_refresh_request(
+ tokens['access_token'], tokens['refresh_token'],
+ tokens['client_id'])
+ self.assertEqual(valid_refresh['client_id'], tokens['client_id'])
+ self.assertNotEqual(valid_refresh['access_token'],
+ tokens['access_token'])
+ self.assertNotEqual(valid_refresh['refresh_token'],
+ tokens['refresh_token'])
+
+ # Test expired refresh token
+ real_refresh = self.client_manager._refresh_token_lifetime
+ self.client_manager._refresh_token_lifetime = 0
+ tokens = self.client_manager._create_tokens({"client_id": valid_client,
+ "username": "test",
+ "password": "test",
+ "expire": time()})
+ with self.assertRaises(HTTPException) as e:
+ self.client_manager.check_refresh_request(tokens['access_token'],
+ tokens['refresh_token'],
+ tokens['client_id'])
+ self.assertEqual(e.exception.status_code, 401)
+ self.client_manager._refresh_token_lifetime = real_refresh