Skip to content
Open
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
30 changes: 29 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -138,4 +138,32 @@ cython_debug/

# Exception for voice_samples
!voice_samples/*.mp3
!voice_samples/*.wav
!voice_samples/*.wav

# Claude settings
.claude/*

# Additional testing artifacts
test-results/
.benchmarks/
.mutmut-cache

# IDE files
*.swp
*.swo
*~
.project
.classpath
.settings/
*.sublime-*

# OS files
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
*.lnk
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
# CLAUDE.md - Project Context for AI Assistant

## Project Overview
JARVIS Voice Assistant - A modular voice assistant application with support for multiple state-of-the-art models.

## Key Commands
- `poetry run test` - Run all tests
- `poetry run tests` - Alternative test command
- `poetry run pytest --cov` - Run tests with coverage
- `./scripts/test.sh` - Run tests without coverage
- `./scripts/test.sh coverage` - Run tests with coverage

## Important Notes
- Testing framework: pytest with pytest-cov and pytest-mock
- Package manager: Poetry
- Coverage threshold: 80% (when running with coverage)
- Test markers: unit, integration, slow
- PyAudio dependency removed due to system requirements

5,725 changes: 5,725 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
[tool.poetry]
name = "jarvis-voice-assistant"
version = "0.1.0"
description = "A modular voice assistant application with support for multiple state-of-the-art models."
authors = ["Prompt <engineerprompt@gmail.com>"]
readme = "README.md"
homepage = "https://github.com/PromtEngineer/JARVIS-VoiceAssistant"
packages = [{include = "*", from = "."}]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Scientific/Engineering :: Artificial Intelligence"
]

[tool.poetry.dependencies]
python = "^3.10"
annotated-types = "^0.6.0"
anyio = "^4.3.0"
certifi = "^2024.2.2"
charset-normalizer = "^3.3.2"
distro = "^1.9.0"
h11 = "^0.14.0"
httpcore = "^1.0.5"
httpx = "^0.27.0"
idna = "^3.7"
openai = "^1.30.1"
pydantic = "^2.7.1"
pydantic-core = "^2.18.2"
pygame = "^2.5.2"
requests = "^2.31.0"
setuptools = "^69.5.1"
sniffio = "^1.3.1"
SpeechRecognition = "^3.10.4"
tqdm = "^4.66.4"
typing-extensions = "^4.11.0"
urllib3 = "^2.2.1"
colorama = "*"
deepgram-sdk = "*"
groq = "*"
python-dotenv = "*"
keyboard = "*"
elevenlabs = "*"
fastapi = "*"
uvicorn = "*"
numpy = "*"
sounddevice = "*"
cartesia = "*"
soundfile = "*"
ollama = "*"
pydub = "*"

[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"

[tool.poetry.scripts]
jarvis = "run_voice_assistant:main"

[tool.pytest.ini_options]
minversion = "8.0"
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
"-vv",
]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests",
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning",
]

[tool.coverage.run]
source = ["tests"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/env/*",
"*/.venv/*",
"*/.env/*",
"*/virtualenv/*",
"*/setup.py",
"*/conftest.py",
"*/.pytest_cache/*",
"*/htmlcov/*",
"*/dist/*",
"*/build/*",
"*.egg-info/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
precision = 2
show_missing = true
skip_covered = true
skip_empty = true

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
9 changes: 9 additions & 0 deletions scripts/test.sh
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# Script to run tests with Poetry

# Run tests with the desired options
if [ "$1" == "coverage" ]; then
poetry run pytest --cov=. --cov-branch --cov-report=term-missing:skip-covered --cov-report=html:htmlcov --cov-report=xml:coverage.xml --cov-fail-under=80 "${@:2}"
else
poetry run pytest "$@"
fi
Empty file addedtests/__init__.py
Empty file.
171 changes: 171 additions & 0 deletions tests/conftest.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
import os
import sys
import tempfile
import shutil
from pathlib import Path
from typing import Generator, Dict, Any
import pytest
from unittest.mock import Mock, MagicMock


sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for test files."""
temp_path = tempfile.mkdtemp()
yield Path(temp_path)
shutil.rmtree(temp_path)


@pytest.fixture
def temp_file(temp_dir: Path) -> Generator[Path, None, None]:
"""Create a temporary file within the temporary directory."""
temp_file_path = temp_dir / "test_file.txt"
temp_file_path.write_text("Test content")
yield temp_file_path


@pytest.fixture
def mock_config() -> Dict[str, Any]:
"""Provide a mock configuration dictionary."""
return {
"api_key": "test-api-key",
"model": "test-model",
"temperature": 0.7,
"max_tokens": 100,
"debug": True,
"log_level": "DEBUG",
"timeout": 30,
}


@pytest.fixture
def mock_env_vars(monkeypatch) -> Dict[str, str]:
"""Set up mock environment variables."""
env_vars = {
"OPENAI_API_KEY": "test-openai-key",
"GROQ_API_KEY": "test-groq-key",
"DEEPGRAM_API_KEY": "test-deepgram-key",
"ELEVENLABS_API_KEY": "test-elevenlabs-key",
"DEBUG": "true",
}
for key, value in env_vars.items():
monkeypatch.setenv(key, value)
return env_vars


@pytest.fixture
def mock_api_response() -> Dict[str, Any]:
"""Provide a mock API response structure."""
return {
"id": "test-response-id",
"object": "chat.completion",
"created": 1234567890,
"model": "test-model",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Test response content"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"total_tokens": 30
}
}


@pytest.fixture
def mock_audio_data() -> bytes:
"""Provide mock audio data for testing."""
return b'\x00\x01' * 1000


@pytest.fixture
def mock_llm_client():
"""Create a mock LLM client."""
client = Mock()
client.generate_response = MagicMock(return_value="Test LLM response")
client.is_connected = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_tts_client():
"""Create a mock TTS client."""
client = Mock()
client.synthesize = MagicMock(return_value=b'\x00\x01' * 1000)
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_stt_client():
"""Create a mock STT client."""
client = Mock()
client.transcribe = MagicMock(return_value="Test transcription")
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture(autouse=True)
def reset_singleton_instances():
"""Reset any singleton instances between tests."""
yield


@pytest.fixture
def capture_logs(caplog):
"""Capture log messages during tests."""
with caplog.at_level("DEBUG"):
yield caplog


@pytest.fixture
def mock_file_system(tmp_path):
"""Create a mock file system structure for testing."""
dirs = ["config", "data", "logs", "models"]
for dir_name in dirs:
(tmp_path / dir_name).mkdir()

config_file = tmp_path / "config" / "config.json"
config_file.write_text('{"test": true}')

return tmp_path


@pytest.fixture
def performance_timer():
"""Measure test performance."""
import time
start_time = time.time()
yield
end_time = time.time()
print(f"\nTest execution time: {end_time - start_time:.3f} seconds")


@pytest.fixture(scope="session")
def test_data_dir() -> Path:
"""Provide path to test data directory."""
return Path(__file__).parent / "test_data"


def pytest_configure(config):
"""Configure pytest with custom markers."""
config.addinivalue_line("markers", "unit: Mark test as a unit test")
config.addinivalue_line("markers", "integration: Mark test as an integration test")
config.addinivalue_line("markers", "slow: Mark test as slow running")


def pytest_collection_modifyitems(config, items):
"""Modify test collection to add markers based on test location."""
for item in items:
if "unit" in str(item.fspath):
item.add_marker(pytest.mark.unit)
elif "integration" in str(item.fspath):
item.add_marker(pytest.mark.integration)
Empty file.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
feat: Set up comprehensive Python testing infrastructure with Poetry by llbbl · Pull Request #34 · PromtEngineer/Verbi · GitHub
Skip to content
Open
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
30 changes: 29 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -138,4 +138,32 @@ cython_debug/

# Exception for voice_samples
!voice_samples/*.mp3
!voice_samples/*.wav
!voice_samples/*.wav

# Claude settings
.claude/*

# Additional testing artifacts
test-results/
.benchmarks/
.mutmut-cache

# IDE files
*.swp
*.swo
*~
.project
.classpath
.settings/
*.sublime-*

# OS files
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
*.lnk
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
# CLAUDE.md - Project Context for AI Assistant

## Project Overview
JARVIS Voice Assistant - A modular voice assistant application with support for multiple state-of-the-art models.

## Key Commands
- `poetry run test` - Run all tests
- `poetry run tests` - Alternative test command
- `poetry run pytest --cov` - Run tests with coverage
- `./scripts/test.sh` - Run tests without coverage
- `./scripts/test.sh coverage` - Run tests with coverage

## Important Notes
- Testing framework: pytest with pytest-cov and pytest-mock
- Package manager: Poetry
- Coverage threshold: 80% (when running with coverage)
- Test markers: unit, integration, slow
- PyAudio dependency removed due to system requirements

5,725 changes: 5,725 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
[tool.poetry]
name = "jarvis-voice-assistant"
version = "0.1.0"
description = "A modular voice assistant application with support for multiple state-of-the-art models."
authors = ["Prompt <engineerprompt@gmail.com>"]
readme = "README.md"
homepage = "https://github.com/PromtEngineer/JARVIS-VoiceAssistant"
packages = [{include = "*", from = "."}]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Scientific/Engineering :: Artificial Intelligence"
]

[tool.poetry.dependencies]
python = "^3.10"
annotated-types = "^0.6.0"
anyio = "^4.3.0"
certifi = "^2024.2.2"
charset-normalizer = "^3.3.2"
distro = "^1.9.0"
h11 = "^0.14.0"
httpcore = "^1.0.5"
httpx = "^0.27.0"
idna = "^3.7"
openai = "^1.30.1"
pydantic = "^2.7.1"
pydantic-core = "^2.18.2"
pygame = "^2.5.2"
requests = "^2.31.0"
setuptools = "^69.5.1"
sniffio = "^1.3.1"
SpeechRecognition = "^3.10.4"
tqdm = "^4.66.4"
typing-extensions = "^4.11.0"
urllib3 = "^2.2.1"
colorama = "*"
deepgram-sdk = "*"
groq = "*"
python-dotenv = "*"
keyboard = "*"
elevenlabs = "*"
fastapi = "*"
uvicorn = "*"
numpy = "*"
sounddevice = "*"
cartesia = "*"
soundfile = "*"
ollama = "*"
pydub = "*"

[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"

[tool.poetry.scripts]
jarvis = "run_voice_assistant:main"

[tool.pytest.ini_options]
minversion = "8.0"
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
"-vv",
]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests",
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning",
]

[tool.coverage.run]
source = ["tests"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/env/*",
"*/.venv/*",
"*/.env/*",
"*/virtualenv/*",
"*/setup.py",
"*/conftest.py",
"*/.pytest_cache/*",
"*/htmlcov/*",
"*/dist/*",
"*/build/*",
"*.egg-info/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
precision = 2
show_missing = true
skip_covered = true
skip_empty = true

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
9 changes: 9 additions & 0 deletions scripts/test.sh
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# Script to run tests with Poetry

# Run tests with the desired options
if [ "$1" == "coverage" ]; then
poetry run pytest --cov=. --cov-branch --cov-report=term-missing:skip-covered --cov-report=html:htmlcov --cov-report=xml:coverage.xml --cov-fail-under=80 "${@:2}"
else
poetry run pytest "$@"
fi
Empty file addedtests/__init__.py
Empty file.
171 changes: 171 additions & 0 deletions tests/conftest.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
import os
import sys
import tempfile
import shutil
from pathlib import Path
from typing import Generator, Dict, Any
import pytest
from unittest.mock import Mock, MagicMock


sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for test files."""
temp_path = tempfile.mkdtemp()
yield Path(temp_path)
shutil.rmtree(temp_path)


@pytest.fixture
def temp_file(temp_dir: Path) -> Generator[Path, None, None]:
"""Create a temporary file within the temporary directory."""
temp_file_path = temp_dir / "test_file.txt"
temp_file_path.write_text("Test content")
yield temp_file_path


@pytest.fixture
def mock_config() -> Dict[str, Any]:
"""Provide a mock configuration dictionary."""
return {
"api_key": "test-api-key",
"model": "test-model",
"temperature": 0.7,
"max_tokens": 100,
"debug": True,
"log_level": "DEBUG",
"timeout": 30,
}


@pytest.fixture
def mock_env_vars(monkeypatch) -> Dict[str, str]:
"""Set up mock environment variables."""
env_vars = {
"OPENAI_API_KEY": "test-openai-key",
"GROQ_API_KEY": "test-groq-key",
"DEEPGRAM_API_KEY": "test-deepgram-key",
"ELEVENLABS_API_KEY": "test-elevenlabs-key",
"DEBUG": "true",
}
for key, value in env_vars.items():
monkeypatch.setenv(key, value)
return env_vars


@pytest.fixture
def mock_api_response() -> Dict[str, Any]:
"""Provide a mock API response structure."""
return {
"id": "test-response-id",
"object": "chat.completion",
"created": 1234567890,
"model": "test-model",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Test response content"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"total_tokens": 30
}
}


@pytest.fixture
def mock_audio_data() -> bytes:
"""Provide mock audio data for testing."""
return b'\x00\x01' * 1000


@pytest.fixture
def mock_llm_client():
"""Create a mock LLM client."""
client = Mock()
client.generate_response = MagicMock(return_value="Test LLM response")
client.is_connected = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_tts_client():
"""Create a mock TTS client."""
client = Mock()
client.synthesize = MagicMock(return_value=b'\x00\x01' * 1000)
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_stt_client():
"""Create a mock STT client."""
client = Mock()
client.transcribe = MagicMock(return_value="Test transcription")
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture(autouse=True)
def reset_singleton_instances():
"""Reset any singleton instances between tests."""
yield


@pytest.fixture
def capture_logs(caplog):
"""Capture log messages during tests."""
with caplog.at_level("DEBUG"):
yield caplog


@pytest.fixture
def mock_file_system(tmp_path):
"""Create a mock file system structure for testing."""
dirs = ["config", "data", "logs", "models"]
for dir_name in dirs:
(tmp_path / dir_name).mkdir()

config_file = tmp_path / "config" / "config.json"
config_file.write_text('{"test": true}')

return tmp_path


@pytest.fixture
def performance_timer():
"""Measure test performance."""
import time
start_time = time.time()
yield
end_time = time.time()
print(f"\nTest execution time: {end_time - start_time:.3f} seconds")


@pytest.fixture(scope="session")
def test_data_dir() -> Path:
"""Provide path to test data directory."""
return Path(__file__).parent / "test_data"


def pytest_configure(config):
"""Configure pytest with custom markers."""
config.addinivalue_line("markers", "unit: Mark test as a unit test")
config.addinivalue_line("markers", "integration: Mark test as an integration test")
config.addinivalue_line("markers", "slow: Mark test as slow running")


def pytest_collection_modifyitems(config, items):
"""Modify test collection to add markers based on test location."""
for item in items:
if "unit" in str(item.fspath):
item.add_marker(pytest.mark.unit)
elif "integration" in str(item.fspath):
item.add_marker(pytest.mark.integration)
Empty file.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat: Set up comprehensive Python testing infrastructure with Poetry by llbbl · Pull Request #34 · PromtEngineer/Verbi · GitHub
Skip to content
Open
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
30 changes: 29 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -138,4 +138,32 @@ cython_debug/

# Exception for voice_samples
!voice_samples/*.mp3
!voice_samples/*.wav
!voice_samples/*.wav

# Claude settings
.claude/*

# Additional testing artifacts
test-results/
.benchmarks/
.mutmut-cache

# IDE files
*.swp
*.swo
*~
.project
.classpath
.settings/
*.sublime-*

# OS files
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
*.lnk
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
# CLAUDE.md - Project Context for AI Assistant

## Project Overview
JARVIS Voice Assistant - A modular voice assistant application with support for multiple state-of-the-art models.

## Key Commands
- `poetry run test` - Run all tests
- `poetry run tests` - Alternative test command
- `poetry run pytest --cov` - Run tests with coverage
- `./scripts/test.sh` - Run tests without coverage
- `./scripts/test.sh coverage` - Run tests with coverage

## Important Notes
- Testing framework: pytest with pytest-cov and pytest-mock
- Package manager: Poetry
- Coverage threshold: 80% (when running with coverage)
- Test markers: unit, integration, slow
- PyAudio dependency removed due to system requirements

5,725 changes: 5,725 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
[tool.poetry]
name = "jarvis-voice-assistant"
version = "0.1.0"
description = "A modular voice assistant application with support for multiple state-of-the-art models."
authors = ["Prompt <engineerprompt@gmail.com>"]
readme = "README.md"
homepage = "https://github.com/PromtEngineer/JARVIS-VoiceAssistant"
packages = [{include = "*", from = "."}]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Scientific/Engineering :: Artificial Intelligence"
]

[tool.poetry.dependencies]
python = "^3.10"
annotated-types = "^0.6.0"
anyio = "^4.3.0"
certifi = "^2024.2.2"
charset-normalizer = "^3.3.2"
distro = "^1.9.0"
h11 = "^0.14.0"
httpcore = "^1.0.5"
httpx = "^0.27.0"
idna = "^3.7"
openai = "^1.30.1"
pydantic = "^2.7.1"
pydantic-core = "^2.18.2"
pygame = "^2.5.2"
requests = "^2.31.0"
setuptools = "^69.5.1"
sniffio = "^1.3.1"
SpeechRecognition = "^3.10.4"
tqdm = "^4.66.4"
typing-extensions = "^4.11.0"
urllib3 = "^2.2.1"
colorama = "*"
deepgram-sdk = "*"
groq = "*"
python-dotenv = "*"
keyboard = "*"
elevenlabs = "*"
fastapi = "*"
uvicorn = "*"
numpy = "*"
sounddevice = "*"
cartesia = "*"
soundfile = "*"
ollama = "*"
pydub = "*"

[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"

[tool.poetry.scripts]
jarvis = "run_voice_assistant:main"

[tool.pytest.ini_options]
minversion = "8.0"
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
"-vv",
]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests",
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning",
]

[tool.coverage.run]
source = ["tests"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/env/*",
"*/.venv/*",
"*/.env/*",
"*/virtualenv/*",
"*/setup.py",
"*/conftest.py",
"*/.pytest_cache/*",
"*/htmlcov/*",
"*/dist/*",
"*/build/*",
"*.egg-info/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
precision = 2
show_missing = true
skip_covered = true
skip_empty = true

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
9 changes: 9 additions & 0 deletions scripts/test.sh
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# Script to run tests with Poetry

# Run tests with the desired options
if [ "$1" == "coverage" ]; then
poetry run pytest --cov=. --cov-branch --cov-report=term-missing:skip-covered --cov-report=html:htmlcov --cov-report=xml:coverage.xml --cov-fail-under=80 "${@:2}"
else
poetry run pytest "$@"
fi
Empty file addedtests/__init__.py
Empty file.
171 changes: 171 additions & 0 deletions tests/conftest.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
import os
import sys
import tempfile
import shutil
from pathlib import Path
from typing import Generator, Dict, Any
import pytest
from unittest.mock import Mock, MagicMock


sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for test files."""
temp_path = tempfile.mkdtemp()
yield Path(temp_path)
shutil.rmtree(temp_path)


@pytest.fixture
def temp_file(temp_dir: Path) -> Generator[Path, None, None]:
"""Create a temporary file within the temporary directory."""
temp_file_path = temp_dir / "test_file.txt"
temp_file_path.write_text("Test content")
yield temp_file_path


@pytest.fixture
def mock_config() -> Dict[str, Any]:
"""Provide a mock configuration dictionary."""
return {
"api_key": "test-api-key",
"model": "test-model",
"temperature": 0.7,
"max_tokens": 100,
"debug": True,
"log_level": "DEBUG",
"timeout": 30,
}


@pytest.fixture
def mock_env_vars(monkeypatch) -> Dict[str, str]:
"""Set up mock environment variables."""
env_vars = {
"OPENAI_API_KEY": "test-openai-key",
"GROQ_API_KEY": "test-groq-key",
"DEEPGRAM_API_KEY": "test-deepgram-key",
"ELEVENLABS_API_KEY": "test-elevenlabs-key",
"DEBUG": "true",
}
for key, value in env_vars.items():
monkeypatch.setenv(key, value)
return env_vars


@pytest.fixture
def mock_api_response() -> Dict[str, Any]:
"""Provide a mock API response structure."""
return {
"id": "test-response-id",
"object": "chat.completion",
"created": 1234567890,
"model": "test-model",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Test response content"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"total_tokens": 30
}
}


@pytest.fixture
def mock_audio_data() -> bytes:
"""Provide mock audio data for testing."""
return b'\x00\x01' * 1000


@pytest.fixture
def mock_llm_client():
"""Create a mock LLM client."""
client = Mock()
client.generate_response = MagicMock(return_value="Test LLM response")
client.is_connected = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_tts_client():
"""Create a mock TTS client."""
client = Mock()
client.synthesize = MagicMock(return_value=b'\x00\x01' * 1000)
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_stt_client():
"""Create a mock STT client."""
client = Mock()
client.transcribe = MagicMock(return_value="Test transcription")
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture(autouse=True)
def reset_singleton_instances():
"""Reset any singleton instances between tests."""
yield


@pytest.fixture
def capture_logs(caplog):
"""Capture log messages during tests."""
with caplog.at_level("DEBUG"):
yield caplog


@pytest.fixture
def mock_file_system(tmp_path):
"""Create a mock file system structure for testing."""
dirs = ["config", "data", "logs", "models"]
for dir_name in dirs:
(tmp_path / dir_name).mkdir()

config_file = tmp_path / "config" / "config.json"
config_file.write_text('{"test": true}')

return tmp_path


@pytest.fixture
def performance_timer():
"""Measure test performance."""
import time
start_time = time.time()
yield
end_time = time.time()
print(f"\nTest execution time: {end_time - start_time:.3f} seconds")


@pytest.fixture(scope="session")
def test_data_dir() -> Path:
"""Provide path to test data directory."""
return Path(__file__).parent / "test_data"


def pytest_configure(config):
"""Configure pytest with custom markers."""
config.addinivalue_line("markers", "unit: Mark test as a unit test")
config.addinivalue_line("markers", "integration: Mark test as an integration test")
config.addinivalue_line("markers", "slow: Mark test as slow running")


def pytest_collection_modifyitems(config, items):
"""Modify test collection to add markers based on test location."""
for item in items:
if "unit" in str(item.fspath):
item.add_marker(pytest.mark.unit)
elif "integration" in str(item.fspath):
item.add_marker(pytest.mark.integration)
Empty file.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat: Set up comprehensive Python testing infrastructure with Poetry by llbbl · Pull Request #34 · PromtEngineer/Verbi · GitHub
Skip to content
Open
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
30 changes: 29 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -138,4 +138,32 @@ cython_debug/

# Exception for voice_samples
!voice_samples/*.mp3
!voice_samples/*.wav
!voice_samples/*.wav

# Claude settings
.claude/*

# Additional testing artifacts
test-results/
.benchmarks/
.mutmut-cache

# IDE files
*.swp
*.swo
*~
.project
.classpath
.settings/
*.sublime-*

# OS files
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
*.lnk
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
# CLAUDE.md - Project Context for AI Assistant

## Project Overview
JARVIS Voice Assistant - A modular voice assistant application with support for multiple state-of-the-art models.

## Key Commands
- `poetry run test` - Run all tests
- `poetry run tests` - Alternative test command
- `poetry run pytest --cov` - Run tests with coverage
- `./scripts/test.sh` - Run tests without coverage
- `./scripts/test.sh coverage` - Run tests with coverage

## Important Notes
- Testing framework: pytest with pytest-cov and pytest-mock
- Package manager: Poetry
- Coverage threshold: 80% (when running with coverage)
- Test markers: unit, integration, slow
- PyAudio dependency removed due to system requirements

5,725 changes: 5,725 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
[tool.poetry]
name = "jarvis-voice-assistant"
version = "0.1.0"
description = "A modular voice assistant application with support for multiple state-of-the-art models."
authors = ["Prompt <engineerprompt@gmail.com>"]
readme = "README.md"
homepage = "https://github.com/PromtEngineer/JARVIS-VoiceAssistant"
packages = [{include = "*", from = "."}]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Scientific/Engineering :: Artificial Intelligence"
]

[tool.poetry.dependencies]
python = "^3.10"
annotated-types = "^0.6.0"
anyio = "^4.3.0"
certifi = "^2024.2.2"
charset-normalizer = "^3.3.2"
distro = "^1.9.0"
h11 = "^0.14.0"
httpcore = "^1.0.5"
httpx = "^0.27.0"
idna = "^3.7"
openai = "^1.30.1"
pydantic = "^2.7.1"
pydantic-core = "^2.18.2"
pygame = "^2.5.2"
requests = "^2.31.0"
setuptools = "^69.5.1"
sniffio = "^1.3.1"
SpeechRecognition = "^3.10.4"
tqdm = "^4.66.4"
typing-extensions = "^4.11.0"
urllib3 = "^2.2.1"
colorama = "*"
deepgram-sdk = "*"
groq = "*"
python-dotenv = "*"
keyboard = "*"
elevenlabs = "*"
fastapi = "*"
uvicorn = "*"
numpy = "*"
sounddevice = "*"
cartesia = "*"
soundfile = "*"
ollama = "*"
pydub = "*"

[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"

[tool.poetry.scripts]
jarvis = "run_voice_assistant:main"

[tool.pytest.ini_options]
minversion = "8.0"
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
"-vv",
]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests",
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning",
]

[tool.coverage.run]
source = ["tests"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/env/*",
"*/.venv/*",
"*/.env/*",
"*/virtualenv/*",
"*/setup.py",
"*/conftest.py",
"*/.pytest_cache/*",
"*/htmlcov/*",
"*/dist/*",
"*/build/*",
"*.egg-info/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
precision = 2
show_missing = true
skip_covered = true
skip_empty = true

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
9 changes: 9 additions & 0 deletions scripts/test.sh
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# Script to run tests with Poetry

# Run tests with the desired options
if [ "$1" == "coverage" ]; then
poetry run pytest --cov=. --cov-branch --cov-report=term-missing:skip-covered --cov-report=html:htmlcov --cov-report=xml:coverage.xml --cov-fail-under=80 "${@:2}"
else
poetry run pytest "$@"
fi
Empty file addedtests/__init__.py
Empty file.
171 changes: 171 additions & 0 deletions tests/conftest.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
import os
import sys
import tempfile
import shutil
from pathlib import Path
from typing import Generator, Dict, Any
import pytest
from unittest.mock import Mock, MagicMock


sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for test files."""
temp_path = tempfile.mkdtemp()
yield Path(temp_path)
shutil.rmtree(temp_path)


@pytest.fixture
def temp_file(temp_dir: Path) -> Generator[Path, None, None]:
"""Create a temporary file within the temporary directory."""
temp_file_path = temp_dir / "test_file.txt"
temp_file_path.write_text("Test content")
yield temp_file_path


@pytest.fixture
def mock_config() -> Dict[str, Any]:
"""Provide a mock configuration dictionary."""
return {
"api_key": "test-api-key",
"model": "test-model",
"temperature": 0.7,
"max_tokens": 100,
"debug": True,
"log_level": "DEBUG",
"timeout": 30,
}


@pytest.fixture
def mock_env_vars(monkeypatch) -> Dict[str, str]:
"""Set up mock environment variables."""
env_vars = {
"OPENAI_API_KEY": "test-openai-key",
"GROQ_API_KEY": "test-groq-key",
"DEEPGRAM_API_KEY": "test-deepgram-key",
"ELEVENLABS_API_KEY": "test-elevenlabs-key",
"DEBUG": "true",
}
for key, value in env_vars.items():
monkeypatch.setenv(key, value)
return env_vars


@pytest.fixture
def mock_api_response() -> Dict[str, Any]:
"""Provide a mock API response structure."""
return {
"id": "test-response-id",
"object": "chat.completion",
"created": 1234567890,
"model": "test-model",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Test response content"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"total_tokens": 30
}
}


@pytest.fixture
def mock_audio_data() -> bytes:
"""Provide mock audio data for testing."""
return b'\x00\x01' * 1000


@pytest.fixture
def mock_llm_client():
"""Create a mock LLM client."""
client = Mock()
client.generate_response = MagicMock(return_value="Test LLM response")
client.is_connected = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_tts_client():
"""Create a mock TTS client."""
client = Mock()
client.synthesize = MagicMock(return_value=b'\x00\x01' * 1000)
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_stt_client():
"""Create a mock STT client."""
client = Mock()
client.transcribe = MagicMock(return_value="Test transcription")
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture(autouse=True)
def reset_singleton_instances():
"""Reset any singleton instances between tests."""
yield


@pytest.fixture
def capture_logs(caplog):
"""Capture log messages during tests."""
with caplog.at_level("DEBUG"):
yield caplog


@pytest.fixture
def mock_file_system(tmp_path):
"""Create a mock file system structure for testing."""
dirs = ["config", "data", "logs", "models"]
for dir_name in dirs:
(tmp_path / dir_name).mkdir()

config_file = tmp_path / "config" / "config.json"
config_file.write_text('{"test": true}')

return tmp_path


@pytest.fixture
def performance_timer():
"""Measure test performance."""
import time
start_time = time.time()
yield
end_time = time.time()
print(f"\nTest execution time: {end_time - start_time:.3f} seconds")


@pytest.fixture(scope="session")
def test_data_dir() -> Path:
"""Provide path to test data directory."""
return Path(__file__).parent / "test_data"


def pytest_configure(config):
"""Configure pytest with custom markers."""
config.addinivalue_line("markers", "unit: Mark test as a unit test")
config.addinivalue_line("markers", "integration: Mark test as an integration test")
config.addinivalue_line("markers", "slow: Mark test as slow running")


def pytest_collection_modifyitems(config, items):
"""Modify test collection to add markers based on test location."""
for item in items:
if "unit" in str(item.fspath):
item.add_marker(pytest.mark.unit)
elif "integration" in str(item.fspath):
item.add_marker(pytest.mark.integration)
Empty file.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ' feat: Set up comprehensive Python testing infrastructure with Poetry by llbbl · Pull Request #34 · PromtEngineer/Verbi · GitHub
Skip to content
Open
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
30 changes: 29 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -138,4 +138,32 @@ cython_debug/

# Exception for voice_samples
!voice_samples/*.mp3
!voice_samples/*.wav
!voice_samples/*.wav

# Claude settings
.claude/*

# Additional testing artifacts
test-results/
.benchmarks/
.mutmut-cache

# IDE files
*.swp
*.swo
*~
.project
.classpath
.settings/
*.sublime-*

# OS files
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
*.lnk
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
# CLAUDE.md - Project Context for AI Assistant

## Project Overview
JARVIS Voice Assistant - A modular voice assistant application with support for multiple state-of-the-art models.

## Key Commands
- `poetry run test` - Run all tests
- `poetry run tests` - Alternative test command
- `poetry run pytest --cov` - Run tests with coverage
- `./scripts/test.sh` - Run tests without coverage
- `./scripts/test.sh coverage` - Run tests with coverage

## Important Notes
- Testing framework: pytest with pytest-cov and pytest-mock
- Package manager: Poetry
- Coverage threshold: 80% (when running with coverage)
- Test markers: unit, integration, slow
- PyAudio dependency removed due to system requirements

5,725 changes: 5,725 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
[tool.poetry]
name = "jarvis-voice-assistant"
version = "0.1.0"
description = "A modular voice assistant application with support for multiple state-of-the-art models."
authors = ["Prompt <engineerprompt@gmail.com>"]
readme = "README.md"
homepage = "https://github.com/PromtEngineer/JARVIS-VoiceAssistant"
packages = [{include = "*", from = "."}]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Scientific/Engineering :: Artificial Intelligence"
]

[tool.poetry.dependencies]
python = "^3.10"
annotated-types = "^0.6.0"
anyio = "^4.3.0"
certifi = "^2024.2.2"
charset-normalizer = "^3.3.2"
distro = "^1.9.0"
h11 = "^0.14.0"
httpcore = "^1.0.5"
httpx = "^0.27.0"
idna = "^3.7"
openai = "^1.30.1"
pydantic = "^2.7.1"
pydantic-core = "^2.18.2"
pygame = "^2.5.2"
requests = "^2.31.0"
setuptools = "^69.5.1"
sniffio = "^1.3.1"
SpeechRecognition = "^3.10.4"
tqdm = "^4.66.4"
typing-extensions = "^4.11.0"
urllib3 = "^2.2.1"
colorama = "*"
deepgram-sdk = "*"
groq = "*"
python-dotenv = "*"
keyboard = "*"
elevenlabs = "*"
fastapi = "*"
uvicorn = "*"
numpy = "*"
sounddevice = "*"
cartesia = "*"
soundfile = "*"
ollama = "*"
pydub = "*"

[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"

[tool.poetry.scripts]
jarvis = "run_voice_assistant:main"

[tool.pytest.ini_options]
minversion = "8.0"
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
"-vv",
]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests",
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning",
]

[tool.coverage.run]
source = ["tests"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/env/*",
"*/.venv/*",
"*/.env/*",
"*/virtualenv/*",
"*/setup.py",
"*/conftest.py",
"*/.pytest_cache/*",
"*/htmlcov/*",
"*/dist/*",
"*/build/*",
"*.egg-info/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
precision = 2
show_missing = true
skip_covered = true
skip_empty = true

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
9 changes: 9 additions & 0 deletions scripts/test.sh
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# Script to run tests with Poetry

# Run tests with the desired options
if [ "$1" == "coverage" ]; then
poetry run pytest --cov=. --cov-branch --cov-report=term-missing:skip-covered --cov-report=html:htmlcov --cov-report=xml:coverage.xml --cov-fail-under=80 "${@:2}"
else
poetry run pytest "$@"
fi
Empty file addedtests/__init__.py
Empty file.
171 changes: 171 additions & 0 deletions tests/conftest.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
import os
import sys
import tempfile
import shutil
from pathlib import Path
from typing import Generator, Dict, Any
import pytest
from unittest.mock import Mock, MagicMock


sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for test files."""
temp_path = tempfile.mkdtemp()
yield Path(temp_path)
shutil.rmtree(temp_path)


@pytest.fixture
def temp_file(temp_dir: Path) -> Generator[Path, None, None]:
"""Create a temporary file within the temporary directory."""
temp_file_path = temp_dir / "test_file.txt"
temp_file_path.write_text("Test content")
yield temp_file_path


@pytest.fixture
def mock_config() -> Dict[str, Any]:
"""Provide a mock configuration dictionary."""
return {
"api_key": "test-api-key",
"model": "test-model",
"temperature": 0.7,
"max_tokens": 100,
"debug": True,
"log_level": "DEBUG",
"timeout": 30,
}


@pytest.fixture
def mock_env_vars(monkeypatch) -> Dict[str, str]:
"""Set up mock environment variables."""
env_vars = {
"OPENAI_API_KEY": "test-openai-key",
"GROQ_API_KEY": "test-groq-key",
"DEEPGRAM_API_KEY": "test-deepgram-key",
"ELEVENLABS_API_KEY": "test-elevenlabs-key",
"DEBUG": "true",
}
for key, value in env_vars.items():
monkeypatch.setenv(key, value)
return env_vars


@pytest.fixture
def mock_api_response() -> Dict[str, Any]:
"""Provide a mock API response structure."""
return {
"id": "test-response-id",
"object": "chat.completion",
"created": 1234567890,
"model": "test-model",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Test response content"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"total_tokens": 30
}
}


@pytest.fixture
def mock_audio_data() -> bytes:
"""Provide mock audio data for testing."""
return b'\x00\x01' * 1000


@pytest.fixture
def mock_llm_client():
"""Create a mock LLM client."""
client = Mock()
client.generate_response = MagicMock(return_value="Test LLM response")
client.is_connected = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_tts_client():
"""Create a mock TTS client."""
client = Mock()
client.synthesize = MagicMock(return_value=b'\x00\x01' * 1000)
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_stt_client():
"""Create a mock STT client."""
client = Mock()
client.transcribe = MagicMock(return_value="Test transcription")
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture(autouse=True)
def reset_singleton_instances():
"""Reset any singleton instances between tests."""
yield


@pytest.fixture
def capture_logs(caplog):
"""Capture log messages during tests."""
with caplog.at_level("DEBUG"):
yield caplog


@pytest.fixture
def mock_file_system(tmp_path):
"""Create a mock file system structure for testing."""
dirs = ["config", "data", "logs", "models"]
for dir_name in dirs:
(tmp_path / dir_name).mkdir()

config_file = tmp_path / "config" / "config.json"
config_file.write_text('{"test": true}')

return tmp_path


@pytest.fixture
def performance_timer():
"""Measure test performance."""
import time
start_time = time.time()
yield
end_time = time.time()
print(f"\nTest execution time: {end_time - start_time:.3f} seconds")


@pytest.fixture(scope="session")
def test_data_dir() -> Path:
"""Provide path to test data directory."""
return Path(__file__).parent / "test_data"


def pytest_configure(config):
"""Configure pytest with custom markers."""
config.addinivalue_line("markers", "unit: Mark test as a unit test")
config.addinivalue_line("markers", "integration: Mark test as an integration test")
config.addinivalue_line("markers", "slow: Mark test as slow running")


def pytest_collection_modifyitems(config, items):
"""Modify test collection to add markers based on test location."""
for item in items:
if "unit" in str(item.fspath):
item.add_marker(pytest.mark.unit)
elif "integration" in str(item.fspath):
item.add_marker(pytest.mark.integration)
Empty file.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat: Set up comprehensive Python testing infrastructure with Poetry by llbbl · Pull Request #34 · PromtEngineer/Verbi · GitHub
Skip to content
Open
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
30 changes: 29 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -138,4 +138,32 @@ cython_debug/

# Exception for voice_samples
!voice_samples/*.mp3
!voice_samples/*.wav
!voice_samples/*.wav

# Claude settings
.claude/*

# Additional testing artifacts
test-results/
.benchmarks/
.mutmut-cache

# IDE files
*.swp
*.swo
*~
.project
.classpath
.settings/
*.sublime-*

# OS files
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
*.lnk
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
# CLAUDE.md - Project Context for AI Assistant

## Project Overview
JARVIS Voice Assistant - A modular voice assistant application with support for multiple state-of-the-art models.

## Key Commands
- `poetry run test` - Run all tests
- `poetry run tests` - Alternative test command
- `poetry run pytest --cov` - Run tests with coverage
- `./scripts/test.sh` - Run tests without coverage
- `./scripts/test.sh coverage` - Run tests with coverage

## Important Notes
- Testing framework: pytest with pytest-cov and pytest-mock
- Package manager: Poetry
- Coverage threshold: 80% (when running with coverage)
- Test markers: unit, integration, slow
- PyAudio dependency removed due to system requirements

5,725 changes: 5,725 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
[tool.poetry]
name = "jarvis-voice-assistant"
version = "0.1.0"
description = "A modular voice assistant application with support for multiple state-of-the-art models."
authors = ["Prompt <engineerprompt@gmail.com>"]
readme = "README.md"
homepage = "https://github.com/PromtEngineer/JARVIS-VoiceAssistant"
packages = [{include = "*", from = "."}]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Scientific/Engineering :: Artificial Intelligence"
]

[tool.poetry.dependencies]
python = "^3.10"
annotated-types = "^0.6.0"
anyio = "^4.3.0"
certifi = "^2024.2.2"
charset-normalizer = "^3.3.2"
distro = "^1.9.0"
h11 = "^0.14.0"
httpcore = "^1.0.5"
httpx = "^0.27.0"
idna = "^3.7"
openai = "^1.30.1"
pydantic = "^2.7.1"
pydantic-core = "^2.18.2"
pygame = "^2.5.2"
requests = "^2.31.0"
setuptools = "^69.5.1"
sniffio = "^1.3.1"
SpeechRecognition = "^3.10.4"
tqdm = "^4.66.4"
typing-extensions = "^4.11.0"
urllib3 = "^2.2.1"
colorama = "*"
deepgram-sdk = "*"
groq = "*"
python-dotenv = "*"
keyboard = "*"
elevenlabs = "*"
fastapi = "*"
uvicorn = "*"
numpy = "*"
sounddevice = "*"
cartesia = "*"
soundfile = "*"
ollama = "*"
pydub = "*"

[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"

[tool.poetry.scripts]
jarvis = "run_voice_assistant:main"

[tool.pytest.ini_options]
minversion = "8.0"
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
"-vv",
]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests",
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning",
]

[tool.coverage.run]
source = ["tests"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/env/*",
"*/.venv/*",
"*/.env/*",
"*/virtualenv/*",
"*/setup.py",
"*/conftest.py",
"*/.pytest_cache/*",
"*/htmlcov/*",
"*/dist/*",
"*/build/*",
"*.egg-info/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
precision = 2
show_missing = true
skip_covered = true
skip_empty = true

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
9 changes: 9 additions & 0 deletions scripts/test.sh
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# Script to run tests with Poetry

# Run tests with the desired options
if [ "$1" == "coverage" ]; then
poetry run pytest --cov=. --cov-branch --cov-report=term-missing:skip-covered --cov-report=html:htmlcov --cov-report=xml:coverage.xml --cov-fail-under=80 "${@:2}"
else
poetry run pytest "$@"
fi
Empty file addedtests/__init__.py
Empty file.
171 changes: 171 additions & 0 deletions tests/conftest.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
import os
import sys
import tempfile
import shutil
from pathlib import Path
from typing import Generator, Dict, Any
import pytest
from unittest.mock import Mock, MagicMock


sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for test files."""
temp_path = tempfile.mkdtemp()
yield Path(temp_path)
shutil.rmtree(temp_path)


@pytest.fixture
def temp_file(temp_dir: Path) -> Generator[Path, None, None]:
"""Create a temporary file within the temporary directory."""
temp_file_path = temp_dir / "test_file.txt"
temp_file_path.write_text("Test content")
yield temp_file_path


@pytest.fixture
def mock_config() -> Dict[str, Any]:
"""Provide a mock configuration dictionary."""
return {
"api_key": "test-api-key",
"model": "test-model",
"temperature": 0.7,
"max_tokens": 100,
"debug": True,
"log_level": "DEBUG",
"timeout": 30,
}


@pytest.fixture
def mock_env_vars(monkeypatch) -> Dict[str, str]:
"""Set up mock environment variables."""
env_vars = {
"OPENAI_API_KEY": "test-openai-key",
"GROQ_API_KEY": "test-groq-key",
"DEEPGRAM_API_KEY": "test-deepgram-key",
"ELEVENLABS_API_KEY": "test-elevenlabs-key",
"DEBUG": "true",
}
for key, value in env_vars.items():
monkeypatch.setenv(key, value)
return env_vars


@pytest.fixture
def mock_api_response() -> Dict[str, Any]:
"""Provide a mock API response structure."""
return {
"id": "test-response-id",
"object": "chat.completion",
"created": 1234567890,
"model": "test-model",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Test response content"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"total_tokens": 30
}
}


@pytest.fixture
def mock_audio_data() -> bytes:
"""Provide mock audio data for testing."""
return b'\x00\x01' * 1000


@pytest.fixture
def mock_llm_client():
"""Create a mock LLM client."""
client = Mock()
client.generate_response = MagicMock(return_value="Test LLM response")
client.is_connected = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_tts_client():
"""Create a mock TTS client."""
client = Mock()
client.synthesize = MagicMock(return_value=b'\x00\x01' * 1000)
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_stt_client():
"""Create a mock STT client."""
client = Mock()
client.transcribe = MagicMock(return_value="Test transcription")
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture(autouse=True)
def reset_singleton_instances():
"""Reset any singleton instances between tests."""
yield


@pytest.fixture
def capture_logs(caplog):
"""Capture log messages during tests."""
with caplog.at_level("DEBUG"):
yield caplog


@pytest.fixture
def mock_file_system(tmp_path):
"""Create a mock file system structure for testing."""
dirs = ["config", "data", "logs", "models"]
for dir_name in dirs:
(tmp_path / dir_name).mkdir()

config_file = tmp_path / "config" / "config.json"
config_file.write_text('{"test": true}')

return tmp_path


@pytest.fixture
def performance_timer():
"""Measure test performance."""
import time
start_time = time.time()
yield
end_time = time.time()
print(f"\nTest execution time: {end_time - start_time:.3f} seconds")


@pytest.fixture(scope="session")
def test_data_dir() -> Path:
"""Provide path to test data directory."""
return Path(__file__).parent / "test_data"


def pytest_configure(config):
"""Configure pytest with custom markers."""
config.addinivalue_line("markers", "unit: Mark test as a unit test")
config.addinivalue_line("markers", "integration: Mark test as an integration test")
config.addinivalue_line("markers", "slow: Mark test as slow running")


def pytest_collection_modifyitems(config, items):
"""Modify test collection to add markers based on test location."""
for item in items:
if "unit" in str(item.fspath):
item.add_marker(pytest.mark.unit)
elif "integration" in str(item.fspath):
item.add_marker(pytest.mark.integration)
Empty file.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ' feat: Set up comprehensive Python testing infrastructure with Poetry by llbbl · Pull Request #34 · PromtEngineer/Verbi · GitHub
Skip to content
Open
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
30 changes: 29 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -138,4 +138,32 @@ cython_debug/

# Exception for voice_samples
!voice_samples/*.mp3
!voice_samples/*.wav
!voice_samples/*.wav

# Claude settings
.claude/*

# Additional testing artifacts
test-results/
.benchmarks/
.mutmut-cache

# IDE files
*.swp
*.swo
*~
.project
.classpath
.settings/
*.sublime-*

# OS files
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
*.lnk
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
# CLAUDE.md - Project Context for AI Assistant

## Project Overview
JARVIS Voice Assistant - A modular voice assistant application with support for multiple state-of-the-art models.

## Key Commands
- `poetry run test` - Run all tests
- `poetry run tests` - Alternative test command
- `poetry run pytest --cov` - Run tests with coverage
- `./scripts/test.sh` - Run tests without coverage
- `./scripts/test.sh coverage` - Run tests with coverage

## Important Notes
- Testing framework: pytest with pytest-cov and pytest-mock
- Package manager: Poetry
- Coverage threshold: 80% (when running with coverage)
- Test markers: unit, integration, slow
- PyAudio dependency removed due to system requirements

5,725 changes: 5,725 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
[tool.poetry]
name = "jarvis-voice-assistant"
version = "0.1.0"
description = "A modular voice assistant application with support for multiple state-of-the-art models."
authors = ["Prompt <engineerprompt@gmail.com>"]
readme = "README.md"
homepage = "https://github.com/PromtEngineer/JARVIS-VoiceAssistant"
packages = [{include = "*", from = "."}]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Scientific/Engineering :: Artificial Intelligence"
]

[tool.poetry.dependencies]
python = "^3.10"
annotated-types = "^0.6.0"
anyio = "^4.3.0"
certifi = "^2024.2.2"
charset-normalizer = "^3.3.2"
distro = "^1.9.0"
h11 = "^0.14.0"
httpcore = "^1.0.5"
httpx = "^0.27.0"
idna = "^3.7"
openai = "^1.30.1"
pydantic = "^2.7.1"
pydantic-core = "^2.18.2"
pygame = "^2.5.2"
requests = "^2.31.0"
setuptools = "^69.5.1"
sniffio = "^1.3.1"
SpeechRecognition = "^3.10.4"
tqdm = "^4.66.4"
typing-extensions = "^4.11.0"
urllib3 = "^2.2.1"
colorama = "*"
deepgram-sdk = "*"
groq = "*"
python-dotenv = "*"
keyboard = "*"
elevenlabs = "*"
fastapi = "*"
uvicorn = "*"
numpy = "*"
sounddevice = "*"
cartesia = "*"
soundfile = "*"
ollama = "*"
pydub = "*"

[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"

[tool.poetry.scripts]
jarvis = "run_voice_assistant:main"

[tool.pytest.ini_options]
minversion = "8.0"
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
"-vv",
]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests",
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning",
]

[tool.coverage.run]
source = ["tests"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/env/*",
"*/.venv/*",
"*/.env/*",
"*/virtualenv/*",
"*/setup.py",
"*/conftest.py",
"*/.pytest_cache/*",
"*/htmlcov/*",
"*/dist/*",
"*/build/*",
"*.egg-info/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
precision = 2
show_missing = true
skip_covered = true
skip_empty = true

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
9 changes: 9 additions & 0 deletions scripts/test.sh
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# Script to run tests with Poetry

# Run tests with the desired options
if [ "$1" == "coverage" ]; then
poetry run pytest --cov=. --cov-branch --cov-report=term-missing:skip-covered --cov-report=html:htmlcov --cov-report=xml:coverage.xml --cov-fail-under=80 "${@:2}"
else
poetry run pytest "$@"
fi
Empty file addedtests/__init__.py
Empty file.
171 changes: 171 additions & 0 deletions tests/conftest.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
import os
import sys
import tempfile
import shutil
from pathlib import Path
from typing import Generator, Dict, Any
import pytest
from unittest.mock import Mock, MagicMock


sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for test files."""
temp_path = tempfile.mkdtemp()
yield Path(temp_path)
shutil.rmtree(temp_path)


@pytest.fixture
def temp_file(temp_dir: Path) -> Generator[Path, None, None]:
"""Create a temporary file within the temporary directory."""
temp_file_path = temp_dir / "test_file.txt"
temp_file_path.write_text("Test content")
yield temp_file_path


@pytest.fixture
def mock_config() -> Dict[str, Any]:
"""Provide a mock configuration dictionary."""
return {
"api_key": "test-api-key",
"model": "test-model",
"temperature": 0.7,
"max_tokens": 100,
"debug": True,
"log_level": "DEBUG",
"timeout": 30,
}


@pytest.fixture
def mock_env_vars(monkeypatch) -> Dict[str, str]:
"""Set up mock environment variables."""
env_vars = {
"OPENAI_API_KEY": "test-openai-key",
"GROQ_API_KEY": "test-groq-key",
"DEEPGRAM_API_KEY": "test-deepgram-key",
"ELEVENLABS_API_KEY": "test-elevenlabs-key",
"DEBUG": "true",
}
for key, value in env_vars.items():
monkeypatch.setenv(key, value)
return env_vars


@pytest.fixture
def mock_api_response() -> Dict[str, Any]:
"""Provide a mock API response structure."""
return {
"id": "test-response-id",
"object": "chat.completion",
"created": 1234567890,
"model": "test-model",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Test response content"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"total_tokens": 30
}
}


@pytest.fixture
def mock_audio_data() -> bytes:
"""Provide mock audio data for testing."""
return b'\x00\x01' * 1000


@pytest.fixture
def mock_llm_client():
"""Create a mock LLM client."""
client = Mock()
client.generate_response = MagicMock(return_value="Test LLM response")
client.is_connected = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_tts_client():
"""Create a mock TTS client."""
client = Mock()
client.synthesize = MagicMock(return_value=b'\x00\x01' * 1000)
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_stt_client():
"""Create a mock STT client."""
client = Mock()
client.transcribe = MagicMock(return_value="Test transcription")
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture(autouse=True)
def reset_singleton_instances():
"""Reset any singleton instances between tests."""
yield


@pytest.fixture
def capture_logs(caplog):
"""Capture log messages during tests."""
with caplog.at_level("DEBUG"):
yield caplog


@pytest.fixture
def mock_file_system(tmp_path):
"""Create a mock file system structure for testing."""
dirs = ["config", "data", "logs", "models"]
for dir_name in dirs:
(tmp_path / dir_name).mkdir()

config_file = tmp_path / "config" / "config.json"
config_file.write_text('{"test": true}')

return tmp_path


@pytest.fixture
def performance_timer():
"""Measure test performance."""
import time
start_time = time.time()
yield
end_time = time.time()
print(f"\nTest execution time: {end_time - start_time:.3f} seconds")


@pytest.fixture(scope="session")
def test_data_dir() -> Path:
"""Provide path to test data directory."""
return Path(__file__).parent / "test_data"


def pytest_configure(config):
"""Configure pytest with custom markers."""
config.addinivalue_line("markers", "unit: Mark test as a unit test")
config.addinivalue_line("markers", "integration: Mark test as an integration test")
config.addinivalue_line("markers", "slow: Mark test as slow running")


def pytest_collection_modifyitems(config, items):
"""Modify test collection to add markers based on test location."""
for item in items:
if "unit" in str(item.fspath):
item.add_marker(pytest.mark.unit)
elif "integration" in str(item.fspath):
item.add_marker(pytest.mark.integration)
Empty file.
Loading
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })(); feat: Set up comprehensive Python testing infrastructure with Poetry by llbbl · Pull Request #34 · PromtEngineer/Verbi · GitHub
Skip to content
Open
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
30 changes: 29 additions & 1 deletion .gitignore
Original file line numberDiff line numberDiff line change
Expand Up@@ -138,4 +138,32 @@ cython_debug/

# Exception for voice_samples
!voice_samples/*.mp3
!voice_samples/*.wav
!voice_samples/*.wav

# Claude settings
.claude/*

# Additional testing artifacts
test-results/
.benchmarks/
.mutmut-cache

# IDE files
*.swp
*.swo
*~
.project
.classpath
.settings/
*.sublime-*

# OS files
Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp
*.lnk
19 changes: 19 additions & 0 deletions CLAUDE.md
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
# CLAUDE.md - Project Context for AI Assistant

## Project Overview
JARVIS Voice Assistant - A modular voice assistant application with support for multiple state-of-the-art models.

## Key Commands
- `poetry run test` - Run all tests
- `poetry run tests` - Alternative test command
- `poetry run pytest --cov` - Run tests with coverage
- `./scripts/test.sh` - Run tests without coverage
- `./scripts/test.sh coverage` - Run tests with coverage

## Important Notes
- Testing framework: pytest with pytest-cov and pytest-mock
- Package manager: Poetry
- Coverage threshold: 80% (when running with coverage)
- Test markers: unit, integration, slow
- PyAudio dependency removed due to system requirements

5,725 changes: 5,725 additions & 0 deletions poetry.lock

Large diffs are not rendered by default.

137 changes: 137 additions & 0 deletions pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
[tool.poetry]
name = "jarvis-voice-assistant"
version = "0.1.0"
description = "A modular voice assistant application with support for multiple state-of-the-art models."
authors = ["Prompt <engineerprompt@gmail.com>"]
readme = "README.md"
homepage = "https://github.com/PromtEngineer/JARVIS-VoiceAssistant"
packages = [{include = "*", from = "."}]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Topic :: Software Development :: Libraries",
"Topic :: Software Development :: Libraries :: Python Modules",
"Topic :: Scientific/Engineering :: Artificial Intelligence"
]

[tool.poetry.dependencies]
python = "^3.10"
annotated-types = "^0.6.0"
anyio = "^4.3.0"
certifi = "^2024.2.2"
charset-normalizer = "^3.3.2"
distro = "^1.9.0"
h11 = "^0.14.0"
httpcore = "^1.0.5"
httpx = "^0.27.0"
idna = "^3.7"
openai = "^1.30.1"
pydantic = "^2.7.1"
pydantic-core = "^2.18.2"
pygame = "^2.5.2"
requests = "^2.31.0"
setuptools = "^69.5.1"
sniffio = "^1.3.1"
SpeechRecognition = "^3.10.4"
tqdm = "^4.66.4"
typing-extensions = "^4.11.0"
urllib3 = "^2.2.1"
colorama = "*"
deepgram-sdk = "*"
groq = "*"
python-dotenv = "*"
keyboard = "*"
elevenlabs = "*"
fastapi = "*"
uvicorn = "*"
numpy = "*"
sounddevice = "*"
cartesia = "*"
soundfile = "*"
ollama = "*"
pydub = "*"

[tool.poetry.group.dev.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
pytest-mock = "^3.14.0"

[tool.poetry.scripts]
jarvis = "run_voice_assistant:main"

[tool.pytest.ini_options]
minversion = "8.0"
addopts = [
"-ra",
"--strict-markers",
"--strict-config",
"-vv",
]
testpaths = ["tests"]
python_files = ["test_*.py", "*_test.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
markers = [
"unit: Unit tests",
"integration: Integration tests",
"slow: Slow running tests",
]
filterwarnings = [
"error",
"ignore::UserWarning",
"ignore::DeprecationWarning",
]

[tool.coverage.run]
source = ["tests"]
omit = [
"*/tests/*",
"*/test_*",
"*/__pycache__/*",
"*/venv/*",
"*/env/*",
"*/.venv/*",
"*/.env/*",
"*/virtualenv/*",
"*/setup.py",
"*/conftest.py",
"*/.pytest_cache/*",
"*/htmlcov/*",
"*/dist/*",
"*/build/*",
"*.egg-info/*",
]

[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"def __repr__",
"if self.debug:",
"if settings.DEBUG",
"raise AssertionError",
"raise NotImplementedError",
"if 0:",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"class .*\\bProtocol\\):",
"@(abc\\.)?abstractmethod",
]
precision = 2
show_missing = true
skip_covered = true
skip_empty = true

[tool.coverage.html]
directory = "htmlcov"

[tool.coverage.xml]
output = "coverage.xml"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
9 changes: 9 additions & 0 deletions scripts/test.sh
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
#!/bin/bash
# Script to run tests with Poetry

# Run tests with the desired options
if [ "$1" == "coverage" ]; then
poetry run pytest --cov=. --cov-branch --cov-report=term-missing:skip-covered --cov-report=html:htmlcov --cov-report=xml:coverage.xml --cov-fail-under=80 "${@:2}"
else
poetry run pytest "$@"
fi
Empty file addedtests/__init__.py
Empty file.
171 changes: 171 additions & 0 deletions tests/conftest.py
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
import os
import sys
import tempfile
import shutil
from pathlib import Path
from typing import Generator, Dict, Any
import pytest
from unittest.mock import Mock, MagicMock


sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))


@pytest.fixture
def temp_dir() -> Generator[Path, None, None]:
"""Create a temporary directory for test files."""
temp_path = tempfile.mkdtemp()
yield Path(temp_path)
shutil.rmtree(temp_path)


@pytest.fixture
def temp_file(temp_dir: Path) -> Generator[Path, None, None]:
"""Create a temporary file within the temporary directory."""
temp_file_path = temp_dir / "test_file.txt"
temp_file_path.write_text("Test content")
yield temp_file_path


@pytest.fixture
def mock_config() -> Dict[str, Any]:
"""Provide a mock configuration dictionary."""
return {
"api_key": "test-api-key",
"model": "test-model",
"temperature": 0.7,
"max_tokens": 100,
"debug": True,
"log_level": "DEBUG",
"timeout": 30,
}


@pytest.fixture
def mock_env_vars(monkeypatch) -> Dict[str, str]:
"""Set up mock environment variables."""
env_vars = {
"OPENAI_API_KEY": "test-openai-key",
"GROQ_API_KEY": "test-groq-key",
"DEEPGRAM_API_KEY": "test-deepgram-key",
"ELEVENLABS_API_KEY": "test-elevenlabs-key",
"DEBUG": "true",
}
for key, value in env_vars.items():
monkeypatch.setenv(key, value)
return env_vars


@pytest.fixture
def mock_api_response() -> Dict[str, Any]:
"""Provide a mock API response structure."""
return {
"id": "test-response-id",
"object": "chat.completion",
"created": 1234567890,
"model": "test-model",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "Test response content"
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 20,
"total_tokens": 30
}
}


@pytest.fixture
def mock_audio_data() -> bytes:
"""Provide mock audio data for testing."""
return b'\x00\x01' * 1000


@pytest.fixture
def mock_llm_client():
"""Create a mock LLM client."""
client = Mock()
client.generate_response = MagicMock(return_value="Test LLM response")
client.is_connected = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_tts_client():
"""Create a mock TTS client."""
client = Mock()
client.synthesize = MagicMock(return_value=b'\x00\x01' * 1000)
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture
def mock_stt_client():
"""Create a mock STT client."""
client = Mock()
client.transcribe = MagicMock(return_value="Test transcription")
client.is_available = MagicMock(return_value=True)
return client


@pytest.fixture(autouse=True)
def reset_singleton_instances():
"""Reset any singleton instances between tests."""
yield


@pytest.fixture
def capture_logs(caplog):
"""Capture log messages during tests."""
with caplog.at_level("DEBUG"):
yield caplog


@pytest.fixture
def mock_file_system(tmp_path):
"""Create a mock file system structure for testing."""
dirs = ["config", "data", "logs", "models"]
for dir_name in dirs:
(tmp_path / dir_name).mkdir()

config_file = tmp_path / "config" / "config.json"
config_file.write_text('{"test": true}')

return tmp_path


@pytest.fixture
def performance_timer():
"""Measure test performance."""
import time
start_time = time.time()
yield
end_time = time.time()
print(f"\nTest execution time: {end_time - start_time:.3f} seconds")


@pytest.fixture(scope="session")
def test_data_dir() -> Path:
"""Provide path to test data directory."""
return Path(__file__).parent / "test_data"


def pytest_configure(config):
"""Configure pytest with custom markers."""
config.addinivalue_line("markers", "unit: Mark test as a unit test")
config.addinivalue_line("markers", "integration: Mark test as an integration test")
config.addinivalue_line("markers", "slow: Mark test as slow running")


def pytest_collection_modifyitems(config, items):
"""Modify test collection to add markers based on test location."""
for item in items:
if "unit" in str(item.fspath):
item.add_marker(pytest.mark.unit)
elif "integration" in str(item.fspath):
item.add_marker(pytest.mark.integration)
Empty file.
Loading