warm-sdk 0.1.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.
- warm_sdk-0.1.3/.gitignore +85 -0
- warm_sdk-0.1.3/PKG-INFO +207 -0
- warm_sdk-0.1.3/README.md +115 -0
- warm_sdk-0.1.3/benchmarks/client_overhead.py +233 -0
- warm_sdk-0.1.3/pyproject.toml +118 -0
- warm_sdk-0.1.3/tests/importers/__init__.py +0 -0
- warm_sdk-0.1.3/tests/importers/conftest.py +143 -0
- warm_sdk-0.1.3/tests/importers/helpers_embedder.py +22 -0
- warm_sdk-0.1.3/tests/importers/mockserver.py +161 -0
- warm_sdk-0.1.3/tests/importers/test_cloud_b2.py +588 -0
- warm_sdk-0.1.3/tests/importers/test_core.py +282 -0
- warm_sdk-0.1.3/tests/importers/test_files.py +19 -0
- warm_sdk-0.1.3/tests/importers/test_identity.py +294 -0
- warm_sdk-0.1.3/tests/importers/test_indexes_a2.py +329 -0
- warm_sdk-0.1.3/tests/importers/test_markdown.py +86 -0
- warm_sdk-0.1.3/tests/importers/test_memory_c1.py +591 -0
- warm_sdk-0.1.3/tests/importers/test_openai_vs.py +127 -0
- warm_sdk-0.1.3/tests/importers/test_servers_b1.py +395 -0
- warm_sdk-0.1.3/tests/importers/test_sink_retries.py +52 -0
- warm_sdk-0.1.3/tests/importers/test_stores_c2.py +151 -0
- warm_sdk-0.1.3/tests/importers/test_vectordbs_a1.py +329 -0
- warm_sdk-0.1.3/tests/test_client.py +198 -0
- warm_sdk-0.1.3/tests/test_evidence.py +227 -0
- warm_sdk-0.1.3/tests/test_importer_metadata_validation.py +38 -0
- warm_sdk-0.1.3/tests/test_live_server.py +164 -0
- warm_sdk-0.1.3/tests/test_memory.py +260 -0
- warm_sdk-0.1.3/tests/test_transport.py +100 -0
- warm_sdk-0.1.3/uv.lock +15355 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/__init__.py +21 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/_transport.py +118 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/client.py +281 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/crewai/__init__.py +5 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/crewai/tools.py +44 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/dspy/__init__.py +5 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/dspy/retriever.py +43 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/embedchain/__init__.py +5 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/embedchain/vectordb.py +59 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/evidence.py +225 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/haystack/__init__.py +5 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/haystack/document_store.py +44 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/__init__.py +38 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/checkpoint.py +96 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/cli.py +189 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/common.py +214 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/inventory.py +103 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/mapping.py +364 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/model.py +79 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/options.py +114 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/pipeline.py +355 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/report.py +182 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sink.py +254 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/__init__.py +61 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/_fields.py +117 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/_sidecar.py +108 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/annoy.py +102 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/base.py +169 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/chroma.py +122 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/elastic.py +276 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/faiss.py +153 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/files.py +338 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/hnswlib.py +127 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/lancedb.py +117 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/langchain.py +207 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/letta.py +148 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/llamaindex.py +128 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/markdown.py +202 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/mem0.py +303 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/milvus.py +191 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/mongodb.py +162 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/openai_vs.py +110 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/pgvector.py +140 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/pinecone.py +181 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/qdrant.py +170 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/redis.py +223 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/sqlite_vec.py +149 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/vespa.py +233 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/weaviate.py +186 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/sources/zep.py +144 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/importers/verify.py +257 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/langchain/__init__.py +5 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/langchain/vectorstores.py +65 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/llamaindex/__init__.py +5 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/llamaindex/vectorstores.py +68 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/memory.py +756 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/memory.pyi +139 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/phidata/__init__.py +5 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/phidata/vectordb.py +61 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/py.typed +0 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/pydantic_ai/__init__.py +7 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/pydantic_ai/tools.py +39 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/semantic_kernel/__init__.py +2 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/semantic_kernel/memory_store.py +95 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/smolagents/__init__.py +5 -0
- warm_sdk-0.1.3/writerslogic_warm_sdk/smolagents/tools.py +51 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Rust
|
|
2
|
+
/target
|
|
3
|
+
/fuzz/Cargo.lock
|
|
4
|
+
/bindings/wasm/Cargo.lock
|
|
5
|
+
/fuzz/target
|
|
6
|
+
/fuzz/corpus
|
|
7
|
+
/fuzz/artifacts
|
|
8
|
+
|
|
9
|
+
# Node.js
|
|
10
|
+
/node_modules
|
|
11
|
+
package-lock.json
|
|
12
|
+
!interop/w3c/package-lock.json
|
|
13
|
+
!integrations/mcp/tests/package-lock.json
|
|
14
|
+
!integrations/js/package-lock.json
|
|
15
|
+
interop/w3c/node_modules/
|
|
16
|
+
*.node
|
|
17
|
+
!index.js
|
|
18
|
+
|
|
19
|
+
# OS
|
|
20
|
+
.DS_Store
|
|
21
|
+
Thumbs.db
|
|
22
|
+
|
|
23
|
+
# Databases (Local dev)
|
|
24
|
+
memory_db/
|
|
25
|
+
*.db
|
|
26
|
+
*.redb
|
|
27
|
+
vectors_data.bin/
|
|
28
|
+
|
|
29
|
+
# Environment / Private
|
|
30
|
+
.cargo_home/
|
|
31
|
+
.env
|
|
32
|
+
.env.*
|
|
33
|
+
!.env.example
|
|
34
|
+
!.env.sample
|
|
35
|
+
|
|
36
|
+
# Python
|
|
37
|
+
__pycache__/
|
|
38
|
+
.venv/
|
|
39
|
+
|
|
40
|
+
# uv script lockfiles written beside the benchmark scripts
|
|
41
|
+
benchmarks/public/*.py.lock
|
|
42
|
+
|
|
43
|
+
# A build driver that joins an absolute path under the cwd recreates this tree at the repo root
|
|
44
|
+
/Volumes/
|
|
45
|
+
|
|
46
|
+
# Evidence kept off git: benchmarks/evidence/MANIFEST.json lists the release archives and
|
|
47
|
+
# scripts/fetch_evidence.py restores them to these paths.
|
|
48
|
+
/benchmarks/results/superiority_v2/
|
|
49
|
+
/benchmarks/results/superiority_v3/
|
|
50
|
+
/benchmarks/results/native_harness_v1/
|
|
51
|
+
/benchmarks/results/native_harness_gce_v1/
|
|
52
|
+
/benchmarks/results/ann_extended_diagnostic_v1/
|
|
53
|
+
/benchmarks/results/ann_minimal_diagnostic_v1/
|
|
54
|
+
/benchmarks/results/algebraic_codebook_synthetic.json
|
|
55
|
+
/benchmarks/results/longmemeval_dev_answers_v2.json
|
|
56
|
+
/benchmarks/results/longmemeval_dev_answers_v2b.json
|
|
57
|
+
|
|
58
|
+
# Golden stores are committed test data.
|
|
59
|
+
!tests/golden/**/vectors_data.bin/
|
|
60
|
+
|
|
61
|
+
# Documentation build
|
|
62
|
+
/.docs-gen/
|
|
63
|
+
/scripts/docs-api/node_modules/
|
|
64
|
+
/scripts/docs-qa/node_modules/
|
|
65
|
+
/_site/
|
|
66
|
+
!/scripts/docs-qa/package-lock.json
|
|
67
|
+
!/scripts/docs-api/package-lock.json
|
|
68
|
+
# Local embedder model packs are build products (see bindings/embed-models)
|
|
69
|
+
*.hmsemb
|
|
70
|
+
bindings/embed-models/**/packs/
|
|
71
|
+
.warm-local/
|
|
72
|
+
benchmarks/results/research/ann/*.graph
|
|
73
|
+
|
|
74
|
+
# Historical benchmark inputs are restored locally from benchmarks/evidence/retired-inputs.json.
|
|
75
|
+
/benchmarks/results/metal_sdpa_draft_2026-10-09/isolated/
|
|
76
|
+
/benchmarks/results/metal_sdpa_draft_2026-10-09/dependencies/
|
|
77
|
+
/benchmarks/results/query_aware_graph_v1/sources/
|
|
78
|
+
/benchmarks/results/query_aware_graph_v4/runs/llama/snapshots/
|
|
79
|
+
/benchmarks/results/query_aware_graph_v4/runs/yi/snapshots/
|
|
80
|
+
/benchmarks/results/longmemeval_dev_answers_v1.json
|
|
81
|
+
/benchmarks/results/longmemeval_dev_answers_v3.json
|
|
82
|
+
/benchmarks/results/longmemeval_dev_answers_local30b_v1.json
|
|
83
|
+
|
|
84
|
+
/_legacy-docs/
|
|
85
|
+
/.hms-local/
|
warm_sdk-0.1.3/PKG-INFO
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: warm-sdk
|
|
3
|
+
Version: 0.1.3
|
|
4
|
+
Summary: HTTP client and agent-framework adapters for a WritersLogic Adaptive Retention & Memory service
|
|
5
|
+
Project-URL: Homepage, https://writerslogic.com/warm/
|
|
6
|
+
Project-URL: Documentation, https://writerslogic.com/warm/docs/
|
|
7
|
+
Project-URL: Repository, https://github.com/writerslogic/warm
|
|
8
|
+
Author-email: WritersLogic <david@writerslogic.com>
|
|
9
|
+
License: Apache-2.0
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Requires-Dist: httpx>=0.27.0
|
|
12
|
+
Requires-Dist: sniffio>=1.3
|
|
13
|
+
Provides-Extra: all
|
|
14
|
+
Requires-Dist: crewai>=0.28.0; extra == 'all'
|
|
15
|
+
Requires-Dist: dspy-ai>=2.4.0; extra == 'all'
|
|
16
|
+
Requires-Dist: embedchain>=0.1.114; extra == 'all'
|
|
17
|
+
Requires-Dist: haystack-ai>=2.0.0; extra == 'all'
|
|
18
|
+
Requires-Dist: langchain-core>=0.1.0; extra == 'all'
|
|
19
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == 'all'
|
|
20
|
+
Requires-Dist: phidata>=2.4.0; extra == 'all'
|
|
21
|
+
Requires-Dist: pydantic-ai>=0.0.14; extra == 'all'
|
|
22
|
+
Requires-Dist: semantic-kernel>=1.39.4; extra == 'all'
|
|
23
|
+
Requires-Dist: smolagents>=1.0.0; extra == 'all'
|
|
24
|
+
Provides-Extra: crewai
|
|
25
|
+
Requires-Dist: crewai>=0.28.0; extra == 'crewai'
|
|
26
|
+
Provides-Extra: dspy
|
|
27
|
+
Requires-Dist: dspy-ai>=2.4.0; extra == 'dspy'
|
|
28
|
+
Provides-Extra: embedchain
|
|
29
|
+
Requires-Dist: embedchain>=0.1.114; extra == 'embedchain'
|
|
30
|
+
Provides-Extra: fast
|
|
31
|
+
Requires-Dist: orjson>=3.9; extra == 'fast'
|
|
32
|
+
Provides-Extra: haystack
|
|
33
|
+
Requires-Dist: haystack-ai>=2.0.0; extra == 'haystack'
|
|
34
|
+
Provides-Extra: import-annoy
|
|
35
|
+
Requires-Dist: annoy>=1.17; extra == 'import-annoy'
|
|
36
|
+
Requires-Dist: numpy>=1.26; extra == 'import-annoy'
|
|
37
|
+
Provides-Extra: import-chroma
|
|
38
|
+
Requires-Dist: chromadb>=0.5; extra == 'import-chroma'
|
|
39
|
+
Provides-Extra: import-faiss
|
|
40
|
+
Requires-Dist: faiss-cpu>=1.8; extra == 'import-faiss'
|
|
41
|
+
Requires-Dist: numpy>=1.26; extra == 'import-faiss'
|
|
42
|
+
Provides-Extra: import-files
|
|
43
|
+
Requires-Dist: duckdb>=1.0; extra == 'import-files'
|
|
44
|
+
Requires-Dist: numpy>=1.26; extra == 'import-files'
|
|
45
|
+
Requires-Dist: pyarrow>=15; extra == 'import-files'
|
|
46
|
+
Provides-Extra: import-hnswlib
|
|
47
|
+
Requires-Dist: hnswlib>=0.8; extra == 'import-hnswlib'
|
|
48
|
+
Requires-Dist: numpy>=1.26; extra == 'import-hnswlib'
|
|
49
|
+
Provides-Extra: import-lancedb
|
|
50
|
+
Requires-Dist: lancedb>=0.9; extra == 'import-lancedb'
|
|
51
|
+
Requires-Dist: pyarrow>=15; extra == 'import-lancedb'
|
|
52
|
+
Provides-Extra: import-langchain
|
|
53
|
+
Requires-Dist: faiss-cpu>=1.8; extra == 'import-langchain'
|
|
54
|
+
Requires-Dist: langchain-core>=0.2; extra == 'import-langchain'
|
|
55
|
+
Provides-Extra: import-letta
|
|
56
|
+
Requires-Dist: letta-client>=0.1; extra == 'import-letta'
|
|
57
|
+
Provides-Extra: import-llamaindex
|
|
58
|
+
Requires-Dist: llama-index-core>=0.10; extra == 'import-llamaindex'
|
|
59
|
+
Provides-Extra: import-mem0
|
|
60
|
+
Requires-Dist: mem0ai<0.2,>=0.1; extra == 'import-mem0'
|
|
61
|
+
Provides-Extra: import-milvus
|
|
62
|
+
Requires-Dist: pymilvus>=2.4; extra == 'import-milvus'
|
|
63
|
+
Provides-Extra: import-mongodb
|
|
64
|
+
Requires-Dist: pymongo>=4.6; extra == 'import-mongodb'
|
|
65
|
+
Provides-Extra: import-openai
|
|
66
|
+
Requires-Dist: openai>=1.40; extra == 'import-openai'
|
|
67
|
+
Provides-Extra: import-pgvector
|
|
68
|
+
Requires-Dist: psycopg[binary]>=3.1; extra == 'import-pgvector'
|
|
69
|
+
Provides-Extra: import-pinecone
|
|
70
|
+
Requires-Dist: pinecone>=5; extra == 'import-pinecone'
|
|
71
|
+
Provides-Extra: import-qdrant
|
|
72
|
+
Requires-Dist: qdrant-client>=1.9; extra == 'import-qdrant'
|
|
73
|
+
Provides-Extra: import-redis
|
|
74
|
+
Requires-Dist: redis>=5; extra == 'import-redis'
|
|
75
|
+
Provides-Extra: import-sqlite-vec
|
|
76
|
+
Requires-Dist: sqlite-vec>=0.1.1; extra == 'import-sqlite-vec'
|
|
77
|
+
Provides-Extra: import-zep
|
|
78
|
+
Requires-Dist: zep-cloud>=2; extra == 'import-zep'
|
|
79
|
+
Provides-Extra: langchain
|
|
80
|
+
Requires-Dist: langchain-core>=0.1.0; extra == 'langchain'
|
|
81
|
+
Provides-Extra: llamaindex
|
|
82
|
+
Requires-Dist: llama-index-core>=0.10.0; extra == 'llamaindex'
|
|
83
|
+
Provides-Extra: phidata
|
|
84
|
+
Requires-Dist: phidata>=2.4.0; extra == 'phidata'
|
|
85
|
+
Provides-Extra: pydantic-ai
|
|
86
|
+
Requires-Dist: pydantic-ai>=0.0.14; extra == 'pydantic-ai'
|
|
87
|
+
Provides-Extra: semantic-kernel
|
|
88
|
+
Requires-Dist: semantic-kernel>=1.39.4; extra == 'semantic-kernel'
|
|
89
|
+
Provides-Extra: smolagents
|
|
90
|
+
Requires-Dist: smolagents>=1.0.0; extra == 'smolagents'
|
|
91
|
+
Description-Content-Type: text/markdown
|
|
92
|
+
|
|
93
|
+
<a href="https://writerslogic.com/warm/"><img src="https://raw.githubusercontent.com/writerslogic/warm/main/assets/crystal-halo/crystal-detailed-static.png" width="96" alt="WARM logo" align="left"></a>
|
|
94
|
+
|
|
95
|
+
# WARM SDK
|
|
96
|
+
|
|
97
|
+
Python HTTP client and framework adapters for a WARM service.
|
|
98
|
+
|
|
99
|
+
[Website](https://writerslogic.com/warm/) · [Documentation](https://writerslogic.com/warm/docs/) · [Source](https://github.com/writerslogic/warm) · [License](https://github.com/writerslogic/warm/blob/main/LICENSE)
|
|
100
|
+
|
|
101
|
+
<br clear="left">
|
|
102
|
+
|
|
103
|
+
The client talks to `warm-server`, which ships in the WARM repository. The server
|
|
104
|
+
stores vectors you supply and exposes document and query endpoints. See the
|
|
105
|
+
[server guide](https://writerslogic.com/warm/docs/latest/SERVER/) to run it.
|
|
106
|
+
|
|
107
|
+
## Installation
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
uv add warm-sdk
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Framework adapters are optional extras. Install one, or all of them:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
uv add "warm-sdk[langchain]"
|
|
117
|
+
uv add "warm-sdk[all]"
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The earlier standalone `langchain-holographic` distribution is retired. For LangChain, install
|
|
121
|
+
`warm-sdk[langchain]` and import `WarmVectorStore` from
|
|
122
|
+
`writerslogic_warm_sdk.langchain`. This adapter connects to `warm-server`; update the old
|
|
123
|
+
`langchain_holographic` import and its local-store configuration when migrating.
|
|
124
|
+
|
|
125
|
+
The `fast` extra adds `orjson` for request encoding, which cuts the per-query cost of sending an embedding:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
uv add "warm-sdk[fast]"
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Requires Python 3.10 or newer.
|
|
132
|
+
|
|
133
|
+
## Quick Start
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from writerslogic_warm_sdk import WarmClient
|
|
137
|
+
|
|
138
|
+
client = WarmClient(url="http://localhost:8080", api_key="...")
|
|
139
|
+
|
|
140
|
+
client.add_documents([
|
|
141
|
+
{"id": "doc-1", "text": "Paris is the capital of France.", "vector": [0.12, -0.03, 0.88], "metadata": {"lang": "en"}},
|
|
142
|
+
])
|
|
143
|
+
|
|
144
|
+
response = client.query(query_vector=[0.10, -0.01, 0.90], top_k=3, filter={"lang": "en"})
|
|
145
|
+
print(response["matches"])
|
|
146
|
+
client.delete_document("doc-1")
|
|
147
|
+
client.close()
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
`AsyncWarmClient` has the same methods as coroutines; close it with `await client.aclose()`. Embeddings come from your own model: the SDK sends vectors, it does not compute them.
|
|
151
|
+
|
|
152
|
+
After bulk ingestion, `client.maintain_document_index()` explicitly builds the server's
|
|
153
|
+
exact dense document index and returns `{"documentIndexTrained": true}` when the
|
|
154
|
+
store is eligible. Smaller or unsupported stores return `false` and use exact scanning.
|
|
155
|
+
Use `await client.maintain_document_index()` with the async client. Rebuild after updates
|
|
156
|
+
or restart; the index is a runtime cache. Searches preserve the existing cosine results,
|
|
157
|
+
and metadata filters continue to apply before ranking. This method requires an
|
|
158
|
+
`warm-server` version exposing `POST /api/v1/index/maintain`.
|
|
159
|
+
|
|
160
|
+
## Source Evidence
|
|
161
|
+
|
|
162
|
+
`complete_evidence` packs complete source observations in your retrieval order. Each observation contains a turn handle, session handle, observation time, role, and verbatim text. Supply opaque caller IDs as lowercase SHA-256 hex handles, and keep their association with your original records.
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
from hashlib import sha256
|
|
166
|
+
from writerslogic_warm_sdk import complete_evidence, decode_source_packet
|
|
167
|
+
|
|
168
|
+
def handle(identity):
|
|
169
|
+
return sha256(identity.encode("utf-8")).hexdigest()
|
|
170
|
+
|
|
171
|
+
session = handle("session-7")
|
|
172
|
+
candidates = [
|
|
173
|
+
[(handle("turn-1"), session, "2026-10-13T10:00:00Z", "user",
|
|
174
|
+
"I have two aquarium tanks.")],
|
|
175
|
+
[(handle("turn-2"), session, "2026-10-13T10:00:00Z", "assistant",
|
|
176
|
+
"You mentioned two aquarium tanks.")],
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
selection = complete_evidence(candidates, selected=[0], budget_bytes=2048)
|
|
180
|
+
observations = decode_source_packet(selection.packet)
|
|
181
|
+
assert observations == [candidates[index] for index in selection.selected]
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
The indices in `selected` are mandatory; packing raises `ValueError` if they exceed the budget. Additional complete groups fit in candidate order, with shared text and session metadata stored once. Decoding preserves every occurrence, role, observation time, and source text. The budget counts UTF-8 packet bytes; measure reader tokens separately. Packets contain source observations and no inferred answer or event date. These helpers run locally and make no service calls.
|
|
185
|
+
|
|
186
|
+
## Integrations
|
|
187
|
+
|
|
188
|
+
| Framework | Extra | Import |
|
|
189
|
+
|-----------|-------|--------|
|
|
190
|
+
| LangChain | `langchain` | `from writerslogic_warm_sdk.langchain import WarmVectorStore` |
|
|
191
|
+
| LlamaIndex | `llamaindex` | `from writerslogic_warm_sdk.llamaindex import WarmVectorStore` |
|
|
192
|
+
| Haystack | `haystack` | `from writerslogic_warm_sdk.haystack import WarmDocumentStore` |
|
|
193
|
+
| Semantic Kernel | `semantic-kernel` | `from writerslogic_warm_sdk.semantic_kernel import WarmMemoryStore` |
|
|
194
|
+
| DSPy | `dspy` | `from writerslogic_warm_sdk.dspy import WarmRM` |
|
|
195
|
+
| CrewAI | `crewai` | `from writerslogic_warm_sdk.crewai import WarmSearchTool` |
|
|
196
|
+
| Embedchain | `embedchain` | `from writerslogic_warm_sdk.embedchain import WarmDB` |
|
|
197
|
+
| Phidata | `phidata` | `from writerslogic_warm_sdk.phidata import WarmVectorDb` |
|
|
198
|
+
| Pydantic AI | `pydantic-ai` | `from writerslogic_warm_sdk.pydantic_ai import get_warm_tool` |
|
|
199
|
+
| SmolAgents | `smolagents` | `from writerslogic_warm_sdk.smolagents import WarmSearchTool` |
|
|
200
|
+
|
|
201
|
+
## Vector Masking
|
|
202
|
+
|
|
203
|
+
Passing `zero_trust_key` masks every embedding client-side with a keyed signed permutation (`VectorMask`, scrypt-derived). The server can still rank vectors because inner products and cosine similarity are preserved exactly. This is obfuscation, **not encryption**: the server learns all pairwise similarities, and known plain/masked pairs reveal the key for the coordinates they cover. Pass a per-collection `mask_salt` (at least 16 bytes) shared by all clients of that collection. Masks produced by 0.1.0/0.1.1 are not compatible and were not secure.
|
|
204
|
+
|
|
205
|
+
## License
|
|
206
|
+
|
|
207
|
+
Apache-2.0. See the [LICENSE](https://github.com/writerslogic/warm/blob/main/LICENSE).
|
warm_sdk-0.1.3/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
<a href="https://writerslogic.com/warm/"><img src="https://raw.githubusercontent.com/writerslogic/warm/main/assets/crystal-halo/crystal-detailed-static.png" width="96" alt="WARM logo" align="left"></a>
|
|
2
|
+
|
|
3
|
+
# WARM SDK
|
|
4
|
+
|
|
5
|
+
Python HTTP client and framework adapters for a WARM service.
|
|
6
|
+
|
|
7
|
+
[Website](https://writerslogic.com/warm/) · [Documentation](https://writerslogic.com/warm/docs/) · [Source](https://github.com/writerslogic/warm) · [License](https://github.com/writerslogic/warm/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
<br clear="left">
|
|
10
|
+
|
|
11
|
+
The client talks to `warm-server`, which ships in the WARM repository. The server
|
|
12
|
+
stores vectors you supply and exposes document and query endpoints. See the
|
|
13
|
+
[server guide](https://writerslogic.com/warm/docs/latest/SERVER/) to run it.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
uv add warm-sdk
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Framework adapters are optional extras. Install one, or all of them:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
uv add "warm-sdk[langchain]"
|
|
25
|
+
uv add "warm-sdk[all]"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The earlier standalone `langchain-holographic` distribution is retired. For LangChain, install
|
|
29
|
+
`warm-sdk[langchain]` and import `WarmVectorStore` from
|
|
30
|
+
`writerslogic_warm_sdk.langchain`. This adapter connects to `warm-server`; update the old
|
|
31
|
+
`langchain_holographic` import and its local-store configuration when migrating.
|
|
32
|
+
|
|
33
|
+
The `fast` extra adds `orjson` for request encoding, which cuts the per-query cost of sending an embedding:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
uv add "warm-sdk[fast]"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Requires Python 3.10 or newer.
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from writerslogic_warm_sdk import WarmClient
|
|
45
|
+
|
|
46
|
+
client = WarmClient(url="http://localhost:8080", api_key="...")
|
|
47
|
+
|
|
48
|
+
client.add_documents([
|
|
49
|
+
{"id": "doc-1", "text": "Paris is the capital of France.", "vector": [0.12, -0.03, 0.88], "metadata": {"lang": "en"}},
|
|
50
|
+
])
|
|
51
|
+
|
|
52
|
+
response = client.query(query_vector=[0.10, -0.01, 0.90], top_k=3, filter={"lang": "en"})
|
|
53
|
+
print(response["matches"])
|
|
54
|
+
client.delete_document("doc-1")
|
|
55
|
+
client.close()
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`AsyncWarmClient` has the same methods as coroutines; close it with `await client.aclose()`. Embeddings come from your own model: the SDK sends vectors, it does not compute them.
|
|
59
|
+
|
|
60
|
+
After bulk ingestion, `client.maintain_document_index()` explicitly builds the server's
|
|
61
|
+
exact dense document index and returns `{"documentIndexTrained": true}` when the
|
|
62
|
+
store is eligible. Smaller or unsupported stores return `false` and use exact scanning.
|
|
63
|
+
Use `await client.maintain_document_index()` with the async client. Rebuild after updates
|
|
64
|
+
or restart; the index is a runtime cache. Searches preserve the existing cosine results,
|
|
65
|
+
and metadata filters continue to apply before ranking. This method requires an
|
|
66
|
+
`warm-server` version exposing `POST /api/v1/index/maintain`.
|
|
67
|
+
|
|
68
|
+
## Source Evidence
|
|
69
|
+
|
|
70
|
+
`complete_evidence` packs complete source observations in your retrieval order. Each observation contains a turn handle, session handle, observation time, role, and verbatim text. Supply opaque caller IDs as lowercase SHA-256 hex handles, and keep their association with your original records.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from hashlib import sha256
|
|
74
|
+
from writerslogic_warm_sdk import complete_evidence, decode_source_packet
|
|
75
|
+
|
|
76
|
+
def handle(identity):
|
|
77
|
+
return sha256(identity.encode("utf-8")).hexdigest()
|
|
78
|
+
|
|
79
|
+
session = handle("session-7")
|
|
80
|
+
candidates = [
|
|
81
|
+
[(handle("turn-1"), session, "2026-10-13T10:00:00Z", "user",
|
|
82
|
+
"I have two aquarium tanks.")],
|
|
83
|
+
[(handle("turn-2"), session, "2026-10-13T10:00:00Z", "assistant",
|
|
84
|
+
"You mentioned two aquarium tanks.")],
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
selection = complete_evidence(candidates, selected=[0], budget_bytes=2048)
|
|
88
|
+
observations = decode_source_packet(selection.packet)
|
|
89
|
+
assert observations == [candidates[index] for index in selection.selected]
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The indices in `selected` are mandatory; packing raises `ValueError` if they exceed the budget. Additional complete groups fit in candidate order, with shared text and session metadata stored once. Decoding preserves every occurrence, role, observation time, and source text. The budget counts UTF-8 packet bytes; measure reader tokens separately. Packets contain source observations and no inferred answer or event date. These helpers run locally and make no service calls.
|
|
93
|
+
|
|
94
|
+
## Integrations
|
|
95
|
+
|
|
96
|
+
| Framework | Extra | Import |
|
|
97
|
+
|-----------|-------|--------|
|
|
98
|
+
| LangChain | `langchain` | `from writerslogic_warm_sdk.langchain import WarmVectorStore` |
|
|
99
|
+
| LlamaIndex | `llamaindex` | `from writerslogic_warm_sdk.llamaindex import WarmVectorStore` |
|
|
100
|
+
| Haystack | `haystack` | `from writerslogic_warm_sdk.haystack import WarmDocumentStore` |
|
|
101
|
+
| Semantic Kernel | `semantic-kernel` | `from writerslogic_warm_sdk.semantic_kernel import WarmMemoryStore` |
|
|
102
|
+
| DSPy | `dspy` | `from writerslogic_warm_sdk.dspy import WarmRM` |
|
|
103
|
+
| CrewAI | `crewai` | `from writerslogic_warm_sdk.crewai import WarmSearchTool` |
|
|
104
|
+
| Embedchain | `embedchain` | `from writerslogic_warm_sdk.embedchain import WarmDB` |
|
|
105
|
+
| Phidata | `phidata` | `from writerslogic_warm_sdk.phidata import WarmVectorDb` |
|
|
106
|
+
| Pydantic AI | `pydantic-ai` | `from writerslogic_warm_sdk.pydantic_ai import get_warm_tool` |
|
|
107
|
+
| SmolAgents | `smolagents` | `from writerslogic_warm_sdk.smolagents import WarmSearchTool` |
|
|
108
|
+
|
|
109
|
+
## Vector Masking
|
|
110
|
+
|
|
111
|
+
Passing `zero_trust_key` masks every embedding client-side with a keyed signed permutation (`VectorMask`, scrypt-derived). The server can still rank vectors because inner products and cosine similarity are preserved exactly. This is obfuscation, **not encryption**: the server learns all pairwise similarities, and known plain/masked pairs reveal the key for the coordinates they cover. Pass a per-collection `mask_salt` (at least 16 bytes) shared by all clients of that collection. Masks produced by 0.1.0/0.1.1 are not compatible and were not secure.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
Apache-2.0. See the [LICENSE](https://github.com/writerslogic/warm/blob/main/LICENSE).
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""Per-call client overhead of the SDK against a local warm-server, paired with raw http.client.
|
|
2
|
+
|
|
3
|
+
Starts the server binary given by ``--server``, loads ``--rows`` documents, then runs
|
|
4
|
+
``--rounds`` interleaved rounds. Each round times every variant on query, add, replace and
|
|
5
|
+
delete, rotating the variant order between rounds. Variants:
|
|
6
|
+
|
|
7
|
+
- ``raw``: one persistent ``http.client.HTTPConnection`` with a pre-encoded JSON body.
|
|
8
|
+
- ``sdk``: ``WarmClient`` from the installed package (sync).
|
|
9
|
+
- ``sdk-async``: ``AsyncWarmClient`` awaited sequentially in one event loop.
|
|
10
|
+
|
|
11
|
+
The SDK is whichever ``writerslogic_warm_sdk`` is importable, so a before/after pair runs this script
|
|
12
|
+
once per SDK build (each in its own environment), interleaving the processes.
|
|
13
|
+
|
|
14
|
+
Writes a JSON summary with p50/p99 per variant and operation to ``--out``.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
import argparse
|
|
18
|
+
import asyncio
|
|
19
|
+
import http.client
|
|
20
|
+
import importlib.metadata
|
|
21
|
+
import json
|
|
22
|
+
import os
|
|
23
|
+
import platform
|
|
24
|
+
import random
|
|
25
|
+
import statistics
|
|
26
|
+
import subprocess
|
|
27
|
+
import sys
|
|
28
|
+
import tempfile
|
|
29
|
+
import time
|
|
30
|
+
|
|
31
|
+
API_KEY = "bench-key"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def vec(rng, dim):
|
|
35
|
+
return [rng.uniform(-1.0, 1.0) for _ in range(dim)]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def pct(samples, q):
|
|
39
|
+
s = sorted(samples)
|
|
40
|
+
return s[min(len(s) - 1, int(q * len(s)))]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Raw:
|
|
44
|
+
def __init__(self, url):
|
|
45
|
+
host, port = url.removeprefix("http://").split(":")
|
|
46
|
+
self.conn = http.client.HTTPConnection(host, int(port))
|
|
47
|
+
self.headers = {"Content-Type": "application/json", "Authorization": f"Bearer {API_KEY}"}
|
|
48
|
+
|
|
49
|
+
def call(self, method, path, body=None):
|
|
50
|
+
self.conn.request(method, path, body=body, headers=self.headers)
|
|
51
|
+
res = self.conn.getresponse()
|
|
52
|
+
data = res.read()
|
|
53
|
+
if res.status >= 300:
|
|
54
|
+
raise RuntimeError(f"{res.status} {data[:200]!r}")
|
|
55
|
+
return data
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def installed(name):
|
|
59
|
+
try:
|
|
60
|
+
return importlib.metadata.version(name)
|
|
61
|
+
except importlib.metadata.PackageNotFoundError:
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def make_ops(rng, dim, n, tag):
|
|
66
|
+
"""Per-variant inputs: queries, fresh docs to add, the same ids to replace, then delete."""
|
|
67
|
+
queries = [vec(rng, dim) for _ in range(n["query"])]
|
|
68
|
+
ids = [f"{tag}-{i}" for i in range(n["write"])]
|
|
69
|
+
adds = [{"id": i, "text": i, "vector": vec(rng, dim)} for i in ids]
|
|
70
|
+
reps = [{"id": i, "text": i, "vector": vec(rng, dim)} for i in ids]
|
|
71
|
+
return queries, adds, reps, ids
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def time_sync(variant, client, ops, top_k):
|
|
75
|
+
queries, adds, reps, ids = ops
|
|
76
|
+
out = {k: [] for k in ("query", "add", "replace", "delete")}
|
|
77
|
+
clock = time.perf_counter_ns
|
|
78
|
+
if variant == "raw":
|
|
79
|
+
for q in queries:
|
|
80
|
+
t = clock()
|
|
81
|
+
json.loads(client.call("POST", "/api/v1/query", json.dumps({"query_vector": q, "top_k": top_k})))
|
|
82
|
+
out["query"].append(clock() - t)
|
|
83
|
+
for key, docs in (("add", adds), ("replace", reps)):
|
|
84
|
+
for d in docs:
|
|
85
|
+
t = clock()
|
|
86
|
+
client.call("POST", "/api/v1/documents/batch", json.dumps([d]))
|
|
87
|
+
out[key].append(clock() - t)
|
|
88
|
+
for i in ids:
|
|
89
|
+
t = clock()
|
|
90
|
+
client.call("DELETE", f"/api/v1/documents/{i}")
|
|
91
|
+
out["delete"].append(clock() - t)
|
|
92
|
+
return out
|
|
93
|
+
for q in queries:
|
|
94
|
+
t = clock()
|
|
95
|
+
client.query(q, top_k=top_k)
|
|
96
|
+
out["query"].append(clock() - t)
|
|
97
|
+
for key, docs in (("add", adds), ("replace", reps)):
|
|
98
|
+
for d in docs:
|
|
99
|
+
t = clock()
|
|
100
|
+
client.add_documents([d])
|
|
101
|
+
out[key].append(clock() - t)
|
|
102
|
+
for i in ids:
|
|
103
|
+
t = clock()
|
|
104
|
+
client.delete_document(i)
|
|
105
|
+
out["delete"].append(clock() - t)
|
|
106
|
+
return out
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
async def time_async(client, ops, top_k):
|
|
110
|
+
queries, adds, reps, ids = ops
|
|
111
|
+
out = {k: [] for k in ("query", "add", "replace", "delete")}
|
|
112
|
+
clock = time.perf_counter_ns
|
|
113
|
+
for q in queries:
|
|
114
|
+
t = clock()
|
|
115
|
+
await client.query(q, top_k=top_k)
|
|
116
|
+
out["query"].append(clock() - t)
|
|
117
|
+
for key, docs in (("add", adds), ("replace", reps)):
|
|
118
|
+
for d in docs:
|
|
119
|
+
t = clock()
|
|
120
|
+
await client.add_documents([d])
|
|
121
|
+
out[key].append(clock() - t)
|
|
122
|
+
for i in ids:
|
|
123
|
+
t = clock()
|
|
124
|
+
await client.delete_document(i)
|
|
125
|
+
out["delete"].append(clock() - t)
|
|
126
|
+
return out
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def start_server(binary, dim, data_dir):
|
|
130
|
+
env = {**os.environ, "WARM_API_KEY": API_KEY}
|
|
131
|
+
proc = subprocess.Popen(
|
|
132
|
+
[binary, "--bind", "127.0.0.1:0", "--input-dim", str(dim), "--data-dir", data_dir],
|
|
133
|
+
stdout=subprocess.PIPE,
|
|
134
|
+
stderr=subprocess.DEVNULL,
|
|
135
|
+
text=True,
|
|
136
|
+
env=env,
|
|
137
|
+
)
|
|
138
|
+
line = proc.stdout.readline().strip()
|
|
139
|
+
if not line.startswith("warm-server listening on "):
|
|
140
|
+
proc.terminate()
|
|
141
|
+
raise RuntimeError(line)
|
|
142
|
+
return proc, f"http://{line.rsplit(' ', 1)[1]}"
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def main():
|
|
146
|
+
ap = argparse.ArgumentParser()
|
|
147
|
+
ap.add_argument("--server", required=True)
|
|
148
|
+
ap.add_argument("--label", required=True)
|
|
149
|
+
ap.add_argument("--rows", type=int, default=10_000)
|
|
150
|
+
ap.add_argument("--dim", type=int, default=384)
|
|
151
|
+
ap.add_argument("--top-k", type=int, default=10)
|
|
152
|
+
ap.add_argument("--queries", type=int, default=2000)
|
|
153
|
+
ap.add_argument("--writes", type=int, default=500)
|
|
154
|
+
ap.add_argument("--rounds", type=int, default=3)
|
|
155
|
+
ap.add_argument("--out", required=True)
|
|
156
|
+
args = ap.parse_args()
|
|
157
|
+
|
|
158
|
+
from writerslogic_warm_sdk import AsyncWarmClient, WarmClient
|
|
159
|
+
|
|
160
|
+
impls = {"sdk": (WarmClient, AsyncWarmClient)}
|
|
161
|
+
|
|
162
|
+
rng = random.Random(7)
|
|
163
|
+
n = {"query": args.queries, "write": args.writes}
|
|
164
|
+
with tempfile.TemporaryDirectory() as data_dir:
|
|
165
|
+
proc, url = start_server(args.server, args.dim, data_dir)
|
|
166
|
+
try:
|
|
167
|
+
raw = Raw(url)
|
|
168
|
+
for start in range(0, args.rows, 1000):
|
|
169
|
+
docs = [{"id": f"row-{i}", "text": f"row {i}", "vector": vec(rng, args.dim)} for i in range(start, min(args.rows, start + 1000))]
|
|
170
|
+
raw.call("POST", "/api/v1/documents/batch", json.dumps(docs))
|
|
171
|
+
raw.call("POST", "/api/v1/index/maintain", "{}")
|
|
172
|
+
|
|
173
|
+
variants = ["raw"] + [v for name in impls for v in (name, f"{name}-async")]
|
|
174
|
+
samples = {v: {k: [] for k in ("query", "add", "replace", "delete")} for v in variants}
|
|
175
|
+
clients = {"raw": raw}
|
|
176
|
+
for name, (sync_cls, _) in impls.items():
|
|
177
|
+
clients[name] = sync_cls(url, api_key=API_KEY)
|
|
178
|
+
loop = asyncio.new_event_loop()
|
|
179
|
+
for name, (_, async_cls) in impls.items():
|
|
180
|
+
clients[f"{name}-async"] = async_cls(url, api_key=API_KEY)
|
|
181
|
+
|
|
182
|
+
# Warm every connection before the first timed call.
|
|
183
|
+
warm = make_ops(rng, args.dim, {"query": 50, "write": 20}, "warm")
|
|
184
|
+
for v in variants:
|
|
185
|
+
if v.endswith("-async"):
|
|
186
|
+
loop.run_until_complete(time_async(clients[v], warm, args.top_k))
|
|
187
|
+
else:
|
|
188
|
+
time_sync(v, clients[v], warm, args.top_k)
|
|
189
|
+
warm = make_ops(rng, args.dim, {"query": 50, "write": 20}, f"warm-{v}")
|
|
190
|
+
|
|
191
|
+
# Variants alternate in small blocks with a rotating order, so background load on a
|
|
192
|
+
# shared machine lands on raw and SDK calls alike and the per-call difference stays paired.
|
|
193
|
+
blocks = 20
|
|
194
|
+
block = {"query": max(1, n["query"] // blocks), "write": max(1, n["write"] // blocks)}
|
|
195
|
+
for r in range(args.rounds):
|
|
196
|
+
for b in range(blocks):
|
|
197
|
+
s = (r + b) % len(variants)
|
|
198
|
+
for v in variants[s:] + variants[:s]:
|
|
199
|
+
ops = make_ops(rng, args.dim, block, f"r{r}-b{b}-{v}")
|
|
200
|
+
if v.endswith("-async"):
|
|
201
|
+
got = loop.run_until_complete(time_async(clients[v], ops, args.top_k))
|
|
202
|
+
else:
|
|
203
|
+
got = time_sync(v, clients[v], ops, args.top_k)
|
|
204
|
+
for k, xs in got.items():
|
|
205
|
+
samples[v][k].extend(xs)
|
|
206
|
+
finally:
|
|
207
|
+
proc.terminate()
|
|
208
|
+
proc.wait(timeout=30)
|
|
209
|
+
|
|
210
|
+
summary = {}
|
|
211
|
+
for v, ops in samples.items():
|
|
212
|
+
summary[v] = {}
|
|
213
|
+
for k, xs in ops.items():
|
|
214
|
+
p50 = pct(xs, 0.5) / 1e3
|
|
215
|
+
p99 = pct(xs, 0.99) / 1e3
|
|
216
|
+
raw50 = pct(samples["raw"][k], 0.5) / 1e3
|
|
217
|
+
summary[v][k] = {"n": len(xs), "p50_us": round(p50, 1), "p99_us": round(p99, 1), "mean_us": round(statistics.fmean(xs) / 1e3, 1), "p50_over_raw_us": round(p50 - raw50, 1)}
|
|
218
|
+
result = {
|
|
219
|
+
"label": args.label,
|
|
220
|
+
"config": {k: v for k, v in vars(args).items() if k not in ("server", "out", "label")},
|
|
221
|
+
"python": sys.version.split()[0],
|
|
222
|
+
"packages": {p: installed(p) for p in ("httpx", "httpcore", "sniffio", "orjson")},
|
|
223
|
+
"machine": platform.machine(),
|
|
224
|
+
"load_average": os.getloadavg(),
|
|
225
|
+
"results": summary,
|
|
226
|
+
}
|
|
227
|
+
with open(args.out, "w") as f:
|
|
228
|
+
json.dump(result, f, indent=2)
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
if __name__ == "__main__":
|
|
233
|
+
main()
|