Skip to content

Commit

Permalink
Slim dependencies (#1)
Browse files Browse the repository at this point in the history
* Moving dependencies for specific nodes to optional

* add github actions

* update mypy to include all extras

* fix toml read bug
  • Loading branch information
ProKil authored Sep 8, 2024
1 parent 4e650ff commit fd89553
Show file tree
Hide file tree
Showing 20 changed files with 210 additions and 77 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/mypy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Mypy
on: [push]

jobs:
Static-Type-Checking:
runs-on: ubuntu-latest
strategy:
max-parallel: 5
matrix:
python-version: ["3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install system packages
run: sudo apt-get install -y portaudio19-dev
- name: Display Python version
run: python -c "import sys; print(sys.version)"
- name: Install dependencies
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Type-checking package with mypy
run: |
uv run --all-extras mypy --strict .
17 changes: 17 additions & 0 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: pre-commit

on:
pull_request:
push:
branches: [main]

jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.11
uses: actions/setup-python@v4
with:
python-version: 3.11.2
- uses: pre-commit/[email protected]
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,4 @@ cython_debug/
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
#.idea/
30 changes: 30 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
exclude: '^(body|open_gopro)/'

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v3.2.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.1 # Use the sha / tag you want to point at
hooks:
- id: prettier
types_or: [html]
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.3.5
hooks:
# Run the linter.
- id: ruff
types_or: [ python, pyi, jupyter ]
args: [ --fix ]
# Run the formatter.
- id: ruff-format
types_or: [ python, pyi, jupyter ]
- repo: https://github.com/kynan/nbstripout
rev: 0.6.0
hooks:
- id: nbstripout
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,4 @@ You will see a tick printed every second.
pip install aact[audio]
docker run -d --name redis-stack -p 6379:6379 -p 8001:8001 redis/redis-stack:latest
aact run-dataflow examples/speaker_listener.toml
```
```
2 changes: 1 addition & 1 deletion examples/example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ node_class = "print"

[[nodes]]
node_name = "tick"
node_class = "tick"
node_class = "tick"
2 changes: 1 addition & 1 deletion examples/speaker_listener.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ node_class = "listener"


[nodes.node_args]
output_channel = "audio_input"
output_channel = "audio_input"
23 changes: 15 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,34 +1,41 @@
[project]
name = "aact"
version = "0.0.2"
description = "An actor model library for multi-agent/environment interaction in Python based on Redis."
readme = "README.md"
requires-python = ">=3.10"
dependencies = [
"pydantic>=2.8.2",
"redis>=5.0.8",
"types-requests>=2.32.0.20240712",
"requests>=2.32.3",
"toml>=0.10.2",
"types-toml>=0.10.8.20240310",
"aiofiles>=24.1.0",
"types-aiofiles>=24.1.0.20240626",
"aiostream>=0.6.2",
"google-cloud-speech>=2.27.0",
"google-cloud-texttospeech>=2.17.2",
"rq>=1.16.2",
"typer>=0.12.5",
"numpy>=2.1.0",
"tomlkit>=0.13.0; python_version <= '3.10'",
]
dynamic = ["version"]

[tool.hatch]
version = {path = "src/aact/__about__.py"}

[project.optional-dependencies]
typing = [
"mypy>=0.910",
"types-requests>=2.32.0.20240712",
"types-aiofiles>=24.1.0.20240626",
]
vision = [
"opencv-python"
]
audio = [
"pyaudio >= 0.2.14",
"types-pyaudio >= 0.2.16.20240516",
]
google = [
"google-cloud-speech>=2.27.0",
"google-cloud-texttospeech>=2.17.2",
]

[build-system]
requires = ["hatchling"]
Expand All @@ -46,4 +53,4 @@ plugins = [
]

[project.scripts]
aact = 'aact.cli:app'
aact = 'aact.cli:app'
1 change: 1 addition & 0 deletions src/aact/__about__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "0.0.3"
29 changes: 19 additions & 10 deletions src/aact/cli/launch/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import logging
import os
import signal
import sys
import time
from typing import Annotated, Any, Optional, TypeVar
from ..app import app
Expand All @@ -12,7 +13,10 @@

from subprocess import Popen

import toml
if sys.version_info >= (3, 11):
import tomllib
else:
import tomlkit as tomllib
from rq import Queue
from rq.exceptions import InvalidJobOperation
from rq.job import Job
Expand Down Expand Up @@ -59,7 +63,7 @@ def run_node(
redis_url: str = typer.Option(),
) -> None:
logger = logging.getLogger(__name__)
config = Config.model_validate(toml.load(dataflow_toml))
config = Config.model_validate(tomllib.load(open(dataflow_toml, "rb")))
logger.info(f"Starting dataflow with config {config}")
# dynamically import extra modules
for module in config.extra_modules:
Expand All @@ -86,11 +90,10 @@ def run_dataflow(
),
) -> None:
logger = logging.getLogger(__name__)
config = Config.model_validate(toml.load(dataflow_toml))
config = Config.model_validate(tomllib.load(open(dataflow_toml, "rb")))
logger.info(f"Starting dataflow with config {config}")

if with_rq:

redis = Redis.from_url(config.redis_url)
queue = Queue(connection=redis)
job_ids: list[str] = []
Expand All @@ -100,22 +103,28 @@ def run_dataflow(

try:
# Wait for all jobs to finish
while not all(Job.fetch(job_id, connection=redis).get_status() == "finished" for job_id in job_ids):
while not all(
Job.fetch(job_id, connection=redis).get_status() == "finished"
for job_id in job_ids
):
time.sleep(1)
except KeyboardInterrupt:
logger.warning("Terminating RQ jobs.")
for job_id in job_ids:
logger.info(f"Terminating job {job_id}")
try:
send_stop_job_command(redis, job_id) # stop the job if it's running
send_stop_job_command(redis, job_id) # stop the job if it's running
except InvalidJobOperation:
logger.info(f"Job {job_id} is not currently executing. Trying to delete it from queue.")
logger.info(
f"Job {job_id} is not currently executing. Trying to delete it from queue."
)
job = Job.fetch(job_id, connection=redis)
job.delete() # remove job from redis
logger.info(f"Job {job_id} has been terminated. Job status: {job.get_status()}")
job.delete() # remove job from redis
logger.info(
f"Job {job_id} has been terminated. Job status: {job.get_status()}"
)
finally:
return


subprocesses: list[Popen[bytes]] = []

Expand Down
9 changes: 7 additions & 2 deletions src/aact/cli/reader/dataflow_reader.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import base64
from collections import defaultdict
import logging
import toml
import sys

if sys.version_info >= (3, 11):
import tomllib
else:
import tomlkit as tomllib
from pydantic import BaseModel, ConfigDict, Field

import requests
Expand Down Expand Up @@ -36,7 +41,7 @@ def get_dataflow_config(dataflow_toml: str) -> Config:
"""
logger = logging.getLogger(__name__)
config = Config.model_validate(toml.load(dataflow_toml))
config = Config.model_validate(tomllib.load(open(dataflow_toml, "rb")))
logger.info(f"Starting dataflow with config {config}")

return config
Expand Down
1 change: 1 addition & 0 deletions src/aact/nodes/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import sys

if sys.version_info >= (3, 11):
from typing import Self
else:
Expand Down
19 changes: 11 additions & 8 deletions src/aact/nodes/listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@
# Runtime import
try:
import pyaudio

PYAUDIO_AVAILABLE = True
except ImportError:
PYAUDIO_AVAILABLE = False


@NodeFactory.register("listener")
class ListenerNode(Node[Zero, Audio]):
def __init__(self, output_channel: str, redis_url: str):
if not PYAUDIO_AVAILABLE:
raise ImportError(
"PyAudio is not available. Please install it to use the ListenerNode."
"PyAudio is not available."
"Please install aact with `pip install aact[audio]` to use the ListenerNode."
)

super().__init__(
Expand All @@ -37,14 +40,14 @@ def __init__(self, output_channel: str, redis_url: str):
redis_url=redis_url,
)
self.output_channel = output_channel
self.audio: 'pyaudio.PyAudio' = pyaudio.PyAudio()
self.stream: Optional['pyaudio.Stream'] = None
self.audio: "pyaudio.PyAudio" = pyaudio.PyAudio()
self.stream: Optional["pyaudio.Stream"] = None
self.queue: asyncio.Queue[bytes] = asyncio.Queue()
self.task: Optional[asyncio.Task[None]] = None

async def __aenter__(self) -> Self:
if PYAUDIO_AVAILABLE:
self.stream = self.audio.open(
self.stream = self.audio.open(
format=pyaudio.paInt16,
channels=1,
rate=44100,
Expand All @@ -57,10 +60,10 @@ async def __aenter__(self) -> Self:

async def __aexit__(self, _: Any, __: Any, ___: Any) -> None:
if self.stream:
self.stream.stop_stream()
self.stream.close()
self.stream.stop_stream()
self.stream.close()
if PYAUDIO_AVAILABLE:
self.audio.terminate()
self.audio.terminate()
if self.task:
self.task.cancel()
try:
Expand Down Expand Up @@ -92,4 +95,4 @@ async def event_handler(
self, _: str, __: Message[Zero]
) -> AsyncIterator[tuple[str, Message[Audio]]]:
raise NotImplementedError("ListenerNode does not have an event handler.")
yield "", Message[DataModel](data=Zero())
yield "", Message[DataModel](data=Zero())
1 change: 1 addition & 0 deletions src/aact/nodes/print.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import asyncio
import sys

if sys.version_info >= (3, 11):
from typing import Self
else:
Expand Down
1 change: 1 addition & 0 deletions src/aact/nodes/record.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import asyncio
from datetime import datetime
import sys

if sys.version_info >= (3, 11):
from typing import Self
else:
Expand Down
Loading

0 comments on commit fd89553

Please sign in to comment.