echo-vector 0.1.2__tar.gz → 1.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- echo_vector-1.0.3/.claude/commands/release.md +15 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/PKG-INFO +1 -1
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/embeddings/clap.py +8 -20
- {echo_vector-0.1.2 → echo_vector-1.0.3}/uv.lock +31 -36
- {echo_vector-0.1.2 → echo_vector-1.0.3}/.claude/settings.json +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/.github/workflows/workflow.yml +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/.gitignore +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/.pre-commit-config.yaml +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/.python-version +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/CLAUDE.md +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/Makefile +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/README.md +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/api/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/api/server.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/audio/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/audio/chunker.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/audio/metadata.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/audio/processor.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/audio/streaming.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/cli/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/cli/main.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/core.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/embeddings/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/embeddings/ast_model.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/embeddings/base.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/embeddings/cache.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/embeddings/factory.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/embeddings/hubert.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/embeddings/local.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/embeddings/wav2vec2.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/embeddings/whisper_enc.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/evaluation/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/evaluation/metrics.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/indexing/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/indexing/base.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/indexing/faiss_index.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/indexing/store.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/search/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/search/engine.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/search/filters.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/search/results.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/utils/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/utils/config.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/echovector/utils/logging.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/pyproject.toml +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/conftest.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/e2e/test_cli.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/fixtures/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/fixtures/audio_generators.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/integration/__init__.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/integration/test_integration.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/test_audio.mp3 +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/unit/test_api.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/unit/test_audio_processor.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/unit/test_chunker.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/unit/test_core.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/unit/test_embeddings.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/unit/test_faiss_index.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/unit/test_results.py +0 -0
- {echo_vector-0.1.2 → echo_vector-1.0.3}/tests/unit/test_search_engine.py +0 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Run the full release pipeline: lint, typecheck, test, commit, tag, and push to trigger CI/CD publish to PyPI.
|
|
2
|
+
|
|
3
|
+
Usage: /release <version> (e.g. /release v0.1.3)
|
|
4
|
+
|
|
5
|
+
Steps:
|
|
6
|
+
1. Verify a version argument was provided (format: vX.Y.Z)
|
|
7
|
+
2. Run `uv run ruff format --check echovector/ tests/` and `uv run ruff check echovector/ tests/`
|
|
8
|
+
3. Run `uv run mypy echovector/`
|
|
9
|
+
4. Run `uv run pytest tests/ -m "not slow and not gpu" -q`
|
|
10
|
+
5. If there are uncommitted changes, stage and commit them with message "release: $ARGUMENTS"
|
|
11
|
+
6. Create git tag `$ARGUMENTS`
|
|
12
|
+
7. Push the branch and the tag: `git push origin master` and `git push origin $ARGUMENTS`
|
|
13
|
+
8. Report the tag that was pushed and remind the user to monitor CI at https://github.com/ahron-maslin/echo_vector/actions
|
|
14
|
+
|
|
15
|
+
If any step fails, stop and report the failure before proceeding.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: echo_vector
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 1.0.3
|
|
4
4
|
Summary: Semantic text search over audio files without full transcription
|
|
5
5
|
Project-URL: Homepage, https://github.com/ahron-maslin/echo_vector
|
|
6
6
|
Project-URL: Documentation, https://github.com/ahron-maslin/echo_vector#readme
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
"""CLAP (Contrastive Language-Audio Pretraining) embedding backend."""
|
|
2
2
|
|
|
3
|
-
from typing import cast
|
|
3
|
+
from typing import Any, cast
|
|
4
4
|
|
|
5
5
|
import librosa
|
|
6
6
|
import numpy as np
|
|
7
7
|
import numpy.typing as npt
|
|
8
|
+
import torch
|
|
9
|
+
from transformers import ClapModel, ClapProcessor
|
|
8
10
|
|
|
9
11
|
from echovector.embeddings.base import EmbeddingBackend
|
|
10
12
|
|
|
11
|
-
|
|
12
|
-
import torch
|
|
13
|
-
from transformers import ClapModel, ClapProcessor
|
|
14
|
-
|
|
15
|
-
_CLAP_AVAILABLE = True
|
|
16
|
-
except ImportError: # pragma: no cover
|
|
17
|
-
_CLAP_AVAILABLE = False
|
|
13
|
+
_CLAP_AVAILABLE = True
|
|
18
14
|
|
|
19
15
|
|
|
20
16
|
class ClapBackend(EmbeddingBackend):
|
|
@@ -33,22 +29,14 @@ class ClapBackend(EmbeddingBackend):
|
|
|
33
29
|
Args:
|
|
34
30
|
model_name: The Hugging Face model identifier.
|
|
35
31
|
device: Device to run the model on (e.g., 'cpu', 'cuda').
|
|
36
|
-
|
|
37
|
-
Raises:
|
|
38
|
-
ImportError: If torch or transformers are not installed.
|
|
39
32
|
"""
|
|
40
|
-
if not _CLAP_AVAILABLE:
|
|
41
|
-
raise ImportError(
|
|
42
|
-
"CLAP backend requires torch and transformers. "
|
|
43
|
-
"Install them with: pip install echo_vector"
|
|
44
|
-
)
|
|
45
33
|
if device is None:
|
|
46
34
|
self.device = "cuda" if torch.cuda.is_available() else "cpu"
|
|
47
35
|
else:
|
|
48
36
|
self.device = device
|
|
49
37
|
|
|
50
|
-
self.processor = ClapProcessor.from_pretrained(model_name)
|
|
51
|
-
self.model = ClapModel.from_pretrained(model_name)
|
|
38
|
+
self.processor: Any = ClapProcessor.from_pretrained(model_name) # pyright: ignore[reportArgumentType]
|
|
39
|
+
self.model: Any = ClapModel.from_pretrained(model_name) # pyright: ignore[reportArgumentType]
|
|
52
40
|
self.model.to(self.device)
|
|
53
41
|
self.model.eval()
|
|
54
42
|
|
|
@@ -81,7 +69,7 @@ class ClapBackend(EmbeddingBackend):
|
|
|
81
69
|
|
|
82
70
|
audios = [self._load_and_resample(path, target_sr) for path in audio_paths]
|
|
83
71
|
|
|
84
|
-
inputs = self.processor(
|
|
72
|
+
inputs = cast("Any", self.processor)(
|
|
85
73
|
audios=audios,
|
|
86
74
|
return_tensors="pt",
|
|
87
75
|
sampling_rate=target_sr,
|
|
@@ -107,7 +95,7 @@ class ClapBackend(EmbeddingBackend):
|
|
|
107
95
|
Returns:
|
|
108
96
|
A numpy array of shape (batch_size, embedding_dim).
|
|
109
97
|
"""
|
|
110
|
-
inputs = self.processor(
|
|
98
|
+
inputs = cast("Any", self.processor)(
|
|
111
99
|
text=texts,
|
|
112
100
|
return_tensors="pt",
|
|
113
101
|
padding=True,
|
|
@@ -477,14 +477,16 @@ dependencies = [
|
|
|
477
477
|
{ name = "pydub" },
|
|
478
478
|
{ name = "rich" },
|
|
479
479
|
{ name = "soundfile" },
|
|
480
|
+
{ name = "torch" },
|
|
480
481
|
{ name = "tqdm" },
|
|
482
|
+
{ name = "transformers" },
|
|
481
483
|
{ name = "typer" },
|
|
482
484
|
]
|
|
483
485
|
|
|
484
486
|
[package.optional-dependencies]
|
|
485
487
|
all = [
|
|
486
488
|
{ name = "fastapi" },
|
|
487
|
-
{ name = "
|
|
489
|
+
{ name = "httpx2" },
|
|
488
490
|
{ name = "hypothesis" },
|
|
489
491
|
{ name = "mkdocs-gen-files" },
|
|
490
492
|
{ name = "mkdocs-literate-nav" },
|
|
@@ -498,20 +500,14 @@ all = [
|
|
|
498
500
|
{ name = "pytest-cov" },
|
|
499
501
|
{ name = "pytest-xdist" },
|
|
500
502
|
{ name = "ruff" },
|
|
501
|
-
{ name = "torch" },
|
|
502
|
-
{ name = "transformers" },
|
|
503
503
|
{ name = "uvicorn", extra = ["standard"] },
|
|
504
504
|
]
|
|
505
505
|
api = [
|
|
506
506
|
{ name = "fastapi" },
|
|
507
507
|
{ name = "uvicorn", extra = ["standard"] },
|
|
508
508
|
]
|
|
509
|
-
clap = [
|
|
510
|
-
{ name = "torch" },
|
|
511
|
-
{ name = "transformers" },
|
|
512
|
-
]
|
|
513
509
|
dev = [
|
|
514
|
-
{ name = "
|
|
510
|
+
{ name = "httpx2" },
|
|
515
511
|
{ name = "hypothesis" },
|
|
516
512
|
{ name = "mutmut" },
|
|
517
513
|
{ name = "mypy" },
|
|
@@ -529,18 +525,13 @@ docs = [
|
|
|
529
525
|
{ name = "mkdocstrings", extra = ["python"] },
|
|
530
526
|
]
|
|
531
527
|
|
|
532
|
-
[package.dev-dependencies]
|
|
533
|
-
dev = [
|
|
534
|
-
{ name = "httpx" },
|
|
535
|
-
]
|
|
536
|
-
|
|
537
528
|
[package.metadata]
|
|
538
529
|
requires-dist = [
|
|
539
530
|
{ name = "faiss-cpu", specifier = ">=1.7,<2" },
|
|
540
531
|
{ name = "fastapi", marker = "extra == 'all'", specifier = ">=0.109,<1" },
|
|
541
532
|
{ name = "fastapi", marker = "extra == 'api'", specifier = ">=0.109,<1" },
|
|
542
|
-
{ name = "
|
|
543
|
-
{ name = "
|
|
533
|
+
{ name = "httpx2", marker = "extra == 'all'", specifier = ">=2.0,<3" },
|
|
534
|
+
{ name = "httpx2", marker = "extra == 'dev'", specifier = ">=2.0,<3" },
|
|
544
535
|
{ name = "hypothesis", marker = "extra == 'all'", specifier = ">=6.92,<7" },
|
|
545
536
|
{ name = "hypothesis", marker = "extra == 'dev'", specifier = ">=6.92,<7" },
|
|
546
537
|
{ name = "librosa", specifier = ">=0.10,<1" },
|
|
@@ -570,22 +561,17 @@ requires-dist = [
|
|
|
570
561
|
{ name = "pytest-xdist", marker = "extra == 'all'", specifier = ">=3.5,<4" },
|
|
571
562
|
{ name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.5,<4" },
|
|
572
563
|
{ name = "rich", specifier = ">=13.7,<14" },
|
|
573
|
-
{ name = "ruff", marker = "extra == 'all'", specifier = ">=0.
|
|
574
|
-
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.
|
|
564
|
+
{ name = "ruff", marker = "extra == 'all'", specifier = ">=0.15,<1" },
|
|
565
|
+
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15,<1" },
|
|
575
566
|
{ name = "soundfile", specifier = ">=0.12,<1" },
|
|
576
|
-
{ name = "torch",
|
|
577
|
-
{ name = "torch", marker = "extra == 'clap'", specifier = ">=2.1,<3" },
|
|
567
|
+
{ name = "torch", specifier = ">=2.1,<3" },
|
|
578
568
|
{ name = "tqdm", specifier = ">=4.66,<5" },
|
|
579
|
-
{ name = "transformers",
|
|
580
|
-
{ name = "transformers", marker = "extra == 'clap'", specifier = ">=4.36,<5" },
|
|
569
|
+
{ name = "transformers", specifier = ">=4.36,<5" },
|
|
581
570
|
{ name = "typer", extras = ["all"], specifier = ">=0.12,<1" },
|
|
582
571
|
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'all'", specifier = ">=0.27,<1" },
|
|
583
572
|
{ name = "uvicorn", extras = ["standard"], marker = "extra == 'api'", specifier = ">=0.27,<1" },
|
|
584
573
|
]
|
|
585
|
-
provides-extras = ["all", "api", "
|
|
586
|
-
|
|
587
|
-
[package.metadata.requires-dev]
|
|
588
|
-
dev = [{ name = "httpx", specifier = ">=0.28.1" }]
|
|
574
|
+
provides-extras = ["all", "api", "dev", "docs"]
|
|
589
575
|
|
|
590
576
|
[[package]]
|
|
591
577
|
name = "execnet"
|
|
@@ -717,16 +703,16 @@ wheels = [
|
|
|
717
703
|
]
|
|
718
704
|
|
|
719
705
|
[[package]]
|
|
720
|
-
name = "
|
|
721
|
-
version = "
|
|
706
|
+
name = "httpcore2"
|
|
707
|
+
version = "2.3.0"
|
|
722
708
|
source = { registry = "https://pypi.org/simple" }
|
|
723
709
|
dependencies = [
|
|
724
|
-
{ name = "certifi" },
|
|
725
710
|
{ name = "h11" },
|
|
711
|
+
{ name = "truststore" },
|
|
726
712
|
]
|
|
727
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
713
|
+
sdist = { url = "https://files.pythonhosted.org/packages/e6/34/18f1c596e677962f040284246f393b10a1f8ce440b3a7e69c637d0f1c7ad/httpcore2-2.3.0.tar.gz", hash = "sha256:07327e251560960eea8e969d92d4c6a325feb13cca39e25340731336c3baf924", size = 64300, upload-time = "2026-06-01T13:15:02.998Z" }
|
|
728
714
|
wheels = [
|
|
729
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
715
|
+
{ url = "https://files.pythonhosted.org/packages/c2/dd/3357218c69360d1cecc196c230c9a1d5c9afd5dba362056e23e60a5e64e5/httpcore2-2.3.0-py3-none-any.whl", hash = "sha256:477e9e334f74e5240dcac002e890580f36a57d40ff0fb14cc9655731d23b8415", size = 80024, upload-time = "2026-06-01T13:15:00.001Z" },
|
|
730
716
|
]
|
|
731
717
|
|
|
732
718
|
[[package]]
|
|
@@ -759,18 +745,18 @@ wheels = [
|
|
|
759
745
|
]
|
|
760
746
|
|
|
761
747
|
[[package]]
|
|
762
|
-
name = "
|
|
763
|
-
version = "
|
|
748
|
+
name = "httpx2"
|
|
749
|
+
version = "2.3.0"
|
|
764
750
|
source = { registry = "https://pypi.org/simple" }
|
|
765
751
|
dependencies = [
|
|
766
752
|
{ name = "anyio" },
|
|
767
|
-
{ name = "
|
|
768
|
-
{ name = "httpcore" },
|
|
753
|
+
{ name = "httpcore2" },
|
|
769
754
|
{ name = "idna" },
|
|
755
|
+
{ name = "truststore" },
|
|
770
756
|
]
|
|
771
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
757
|
+
sdist = { url = "https://files.pythonhosted.org/packages/9f/9a/cca0b9145f13d8ae34b885ae28d403a1469a433abc78e0f94f4ce94e650b/httpx2-2.3.0.tar.gz", hash = "sha256:227e7c41d95a76d4077a52640564132777215fc3394e07b66a3116c33d668fa9", size = 81115, upload-time = "2026-06-01T13:15:04.324Z" }
|
|
772
758
|
wheels = [
|
|
773
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
759
|
+
{ url = "https://files.pythonhosted.org/packages/87/ce/ae2911859847f9ba1d6b23027e53481cbeb50b93234f355a968d300ca2cb/httpx2-2.3.0-py3-none-any.whl", hash = "sha256:6f393663bdf6dbe7fe90118e3eb5b2bd024a675cae0390ac08cec9198812d8b7", size = 74538, upload-time = "2026-06-01T13:15:01.566Z" },
|
|
774
760
|
]
|
|
775
761
|
|
|
776
762
|
[[package]]
|
|
@@ -2544,6 +2530,15 @@ wheels = [
|
|
|
2544
2530
|
{ url = "https://files.pythonhosted.org/packages/c1/68/fa86e5a39608000f645535b2c124920126327ab731f8c4fafd5b07ff8d4b/triton-3.7.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ce061073102714b725f3660ec6939d94a1da7984b3aa99c921417cae273672f5", size = 201546766, upload-time = "2026-05-07T18:46:42.088Z" },
|
|
2545
2531
|
]
|
|
2546
2532
|
|
|
2533
|
+
[[package]]
|
|
2534
|
+
name = "truststore"
|
|
2535
|
+
version = "0.10.4"
|
|
2536
|
+
source = { registry = "https://pypi.org/simple" }
|
|
2537
|
+
sdist = { url = "https://files.pythonhosted.org/packages/53/a3/1585216310e344e8102c22482f6060c7a6ea0322b63e026372e6dcefcfd6/truststore-0.10.4.tar.gz", hash = "sha256:9d91bd436463ad5e4ee4aba766628dd6cd7010cf3e2461756b3303710eebc301", size = 26169, upload-time = "2025-08-12T18:49:02.73Z" }
|
|
2538
|
+
wheels = [
|
|
2539
|
+
{ url = "https://files.pythonhosted.org/packages/19/97/56608b2249fe206a67cd573bc93cd9896e1efb9e98bce9c163bcdc704b88/truststore-0.10.4-py3-none-any.whl", hash = "sha256:adaeaecf1cbb5f4de3b1959b42d41f6fab57b2b1666adb59e89cb0b53361d981", size = 18660, upload-time = "2025-08-12T18:49:01.46Z" },
|
|
2540
|
+
]
|
|
2541
|
+
|
|
2547
2542
|
[[package]]
|
|
2548
2543
|
name = "typer"
|
|
2549
2544
|
version = "0.25.1"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|