Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slim dependencies #1

Merged
merged 4 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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/action@v3.0.0
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
Loading