Skip to content

Commit

Permalink
Improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
gregorybchris committed Apr 18, 2024
1 parent 0da9df1 commit fa9e8e3
Show file tree
Hide file tree
Showing 25 changed files with 1,024 additions and 429 deletions.
48 changes: 45 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ jobs:
env:
HUME_DEV_API_KEY: ${{ secrets.HUME_DEV_API_KEY }}
run: |
poetry run pytest tests --cov=hume --cov-report=html --cov-report=xml --cov-branch -m "not service"
poetry run pytest tests --cov=hume --cov-report=html --cov-report=xml --cov-branch -m "(not service) and (not playback)"
- name: Run pydocstyle
shell: bash
Expand All @@ -84,13 +84,18 @@ jobs:
build-service:
runs-on: ubuntu-latest

strategy:
matrix:
python-version:
- "3.10"

steps:
- uses: actions/checkout@v2

- name: Python 3.10 Setup
- name: Python ${{ matrix.python-version }} Setup
uses: actions/setup-python@v2
with:
python-version: "3.10"
python-version: ${{ matrix.python-version }}

- name: Install Python dependencies
shell: bash
Expand Down Expand Up @@ -124,3 +129,40 @@ jobs:
shell: bash
run: |
poetry run covcheck coverage.xml --config pyproject.toml --group service
build-playback:
runs-on: ubuntu-latest

strategy:
matrix:
python-version:
- "3.10"

steps:
- uses: actions/checkout@v2

- name: Python ${{ matrix.python-version }} Setup
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

- name: Install system dependencies
shell: bash
run: |
sudo apt-get --yes update
sudo apt-get --yes install libasound2-dev libportaudio2
- name: Install Python dependencies
shell: bash
run: |
pip install poetry
if [ -d /poetryenvs ]; then rm -rf ~/poetryenvs; fi
poetry config virtualenvs.path ~/poetryenvs
poetry install -E playback
- name: Run pytest
shell: bash
env:
HUME_DEV_API_KEY: ${{ secrets.HUME_DEV_API_KEY }}
run: |
poetry run pytest tests --cov=hume --cov-report=html --cov-report=xml --cov-branch -m playback
96 changes: 96 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,106 @@ Basic installation:
pip install hume
```

## Requirements

To use the basic functionality of `HumeVoiceClient`, `HumeBatchClient` or `HumeStreamClient` there are no additional system dependencies, however using the audio playback functionality of the EVI `MicrophoneInterface` may require a few extra dependencies depending on your operating system.

### Linux

- `libasound2-dev`
- `libportaudio2`

You can install these dependencies with:

```bash
sudo apt-get --yes update
sudo apt-get --yes install libasound2-dev libportaudio2
```

## Basic Usage

Jupyter example notebooks can be found in the [Python SDK GitHub repo](https://github.com/HumeAI/hume-python-sdk/tree/main/examples/README.md).

### Stream an EVI chat session

Start a new session using your device's microphone:

> Note: to use
```python
import asyncio

from hume import HumeVoiceClient, MicrophoneInterface

async def main() -> None:
client = HumeVoiceClient("<your-api-key>")

async with client.connect() as socket:
await MicrophoneInterface.start(socket)

asyncio.run(main())
```

Using a custom voice config:

```py
import asyncio

from hume import HumeVoiceClient, MicrophoneInterface

async def main() -> None:
client = HumeVoiceClient("<YOUR API KEY>")

async with client.connect(config_id="<YOUR CONFIG ID>") as socket:
await MicrophoneInterface.start(socket)

asyncio.run(main())
```

### Managing voice configs

Create a new config:

```py
from hume import HumeVoiceClient, VoiceConfig

client = HumeVoiceClient("<YOUR API KEY">)
config: VoiceConfig = client.create_config(
name=f"silly-poet",
prompt="you are a silly poet",
)
print("Created config: ", config.id)
```

Get an existing config:

```py
from hume import HumeVoiceClient

client = HumeVoiceClient("<YOUR API KEY">)
config = client.get_config("<YOUR CONFIG ID>")
print("Fetched config: ", config.name)
```

List all your configs:

```py
from hume import HumeVoiceClient

client = HumeVoiceClient("<YOUR API KEY">)
for config in client.iter_configs():
print(f"- {config.name} ({config.id})")
```

Delete a config:

```py
from hume import HumeVoiceClient

client = HumeVoiceClient("<YOUR API KEY">)
client.delete_config("<YOUR CONFIG ID>")
```

### Submit a new batch job

> Note: Your personal API key can be found in the profile section of [beta.hume.ai](https://beta.hume.ai)
Expand Down
137 changes: 137 additions & 0 deletions examples/evi-using-configs/evi-using-configs.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Using configs with EVI\n",
"\n",
"This notebook uses the Empathic Voice API to configure voices."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from hume import HumeVoiceClient, VoiceConfig"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"client = HumeVoiceClient(\"<your-api-key>\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Create a new config"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from uuid import uuid4\n",
"\n",
"name = f\"poet-{uuid4()}\"\n",
"prompt = \"you are a silly poet\"\n",
"config: VoiceConfig = client.create_config(\n",
" name=name,\n",
" prompt=prompt,\n",
")\n",
"print(\"Created config: \", config.id)\n",
"config_id = config.id"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Get an existing config"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"config = client.get_config(config_id)\n",
"print(\"Fetched config: \", config.name)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### List all your configs"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for config in client.iter_configs():\n",
" print(f\"- {config.name} ({config.id})\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Delete a config"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"client.delete_config(config_id)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"for config in client.iter_configs():\n",
" print(f\"- {config.name} ({config.id})\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "hume",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.8"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
2 changes: 1 addition & 1 deletion hume/_voice/hume_voice_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@


class HumeVoiceClient(ChatMixin, ChatsMixin, ConfigsMixin):
pass
"""Empathic Voice Interface client."""
12 changes: 12 additions & 0 deletions hume/_voice/microphone/asyncio_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,31 @@
# NOTE: asyncio.Queue isn't itself async iterable
@dataclasses.dataclass
class Stream(Generic[T]):
"""Async iterable stream."""

queue: BaseQueue
agen: AsyncGenerator[T, None]

@classmethod
def new(cls) -> "Stream[T]":
"""Create a new async iterable stream."""
return cls.from_queue(BaseQueue())

@classmethod
def from_queue(cls, queue: BaseQueue) -> "Stream[T]":
"""Create a new async iterable stream from a queue.
Args:
queue (asyncio.Queue): Queue to use for the stream.
"""
return cls(queue=queue, agen=cls._agen(queue=queue))

def __aiter__(self) -> AsyncIterator[T]:
"""Iterate over stream items."""
return self.agen

async def __anext__(self) -> T:
"""Get the next item in the stream."""
return await self.agen.__anext__()

@staticmethod
Expand All @@ -31,7 +41,9 @@ async def _agen(*, queue: BaseQueue) -> AsyncGenerator[T, None]:
yield await queue.get()

async def put(self, item: T) -> None:
"""Put an item into the stream."""
await self.queue.put(item)

async def aclose(self) -> None:
"""Close the stream."""
await self.agen.aclose()
7 changes: 7 additions & 0 deletions hume/_voice/microphone/audio_utilities.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"""Utilities for audio playback."""

import asyncio
from io import BytesIO

Expand All @@ -12,5 +14,10 @@
# - [https://github.com/jiaaro/pydub#playback]
# - [https://github.com/jiaaro/pydub/blob/master/pydub/playback.py]
async def play_audio(byte_str: bytes) -> None:
"""Play a byte string of audio data with the system audio output device.
Args:
byte_str (bytes): Byte string of audio data.
"""
segment = AudioSegment.from_file(BytesIO(byte_str))
await asyncio.to_thread(pydub.playback.play, segment)
Loading

0 comments on commit fa9e8e3

Please sign in to comment.