nvidia-nat-redis 1.2.0rc5__py3-none-any.whl

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.
aiq/meta/pypi.md ADDED
@@ -0,0 +1,23 @@
1
+ <!--
2
+ SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
3
+ SPDX-License-Identifier: Apache-2.0
4
+
5
+ Licensed under the Apache License, Version 2.0 (the "License");
6
+ you may not use this file except in compliance with the License.
7
+ You may obtain a copy of the License at
8
+
9
+ http://www.apache.org/licenses/LICENSE-2.0
10
+
11
+ Unless required by applicable law or agreed to in writing, software
12
+ distributed under the License is distributed on an "AS IS" BASIS,
13
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ See the License for the specific language governing permissions and
15
+ limitations under the License.
16
+ -->
17
+
18
+ ![NVIDIA NeMo Agent Toolkit](https://media.githubusercontent.com/media/NVIDIA/NeMo-Agent-Toolkit/refs/heads/main/docs/source/_static/aiqtoolkit_banner.png "NeMo Agent toolkit banner image")
19
+
20
+ # NVIDIA NeMo Agent Toolkit Subpackage
21
+ This is a subpackage for Redis memory integration in NeMo Agent toolkit.
22
+
23
+ For more information about NeMo Agent toolkit, please visit the [NeMo Agent toolkit package](https://pypi.org/project/aiqtoolkit/).
File without changes
@@ -0,0 +1,57 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import redis.asyncio as redis
17
+ from pydantic import Field
18
+
19
+ from aiq.builder.builder import Builder
20
+ from aiq.builder.framework_enum import LLMFrameworkEnum
21
+ from aiq.cli.register_workflow import register_memory
22
+ from aiq.data_models.component_ref import EmbedderRef
23
+ from aiq.data_models.memory import MemoryBaseConfig
24
+
25
+
26
+ class RedisMemoryClientConfig(MemoryBaseConfig, name="redis_memory"):
27
+ host: str | None = Field(default="localhost", description="Redis server host")
28
+ db: str | None = Field(default="0", description="Redis DB")
29
+ port: str | None = Field(default="6379", description="Redis server port")
30
+ key_prefix: str | None = Field(default="aiq", description="Key prefix to use for redis keys")
31
+ embedder: EmbedderRef = Field(description=("Instance name of the memory client instance from the workflow "
32
+ "configuration object."))
33
+
34
+
35
+ @register_memory(config_type=RedisMemoryClientConfig)
36
+ async def redis_memory_client(config: RedisMemoryClientConfig, builder: Builder):
37
+
38
+ from aiq.plugins.redis.redis_editor import RedisEditor
39
+
40
+ from .schema import ensure_index_exists
41
+
42
+ redis_client = redis.Redis(host=config.host,
43
+ port=config.port,
44
+ db=config.db,
45
+ decode_responses=True,
46
+ socket_timeout=5.0,
47
+ socket_connect_timeout=5.0)
48
+
49
+ embedder = await builder.get_embedder(config.embedder, wrapper_type=LLMFrameworkEnum.LANGCHAIN)
50
+
51
+ test_embedding = await embedder.aembed_query("test")
52
+ embedding_dim = len(test_embedding)
53
+ await ensure_index_exists(client=redis_client, key_prefix=config.key_prefix, embedding_dim=embedding_dim)
54
+
55
+ memory_editor = RedisEditor(redis_client=redis_client, key_prefix=config.key_prefix, embedder=embedder)
56
+
57
+ yield memory_editor
@@ -0,0 +1,233 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+ import secrets
18
+
19
+ import numpy as np
20
+ import redis.asyncio as redis
21
+ import redis.exceptions as redis_exceptions
22
+ from langchain_core.embeddings import Embeddings
23
+ from redis.commands.search.query import Query
24
+
25
+ from aiq.memory.interfaces import MemoryEditor
26
+ from aiq.memory.models import MemoryItem
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+ INDEX_NAME = "memory_idx"
31
+
32
+
33
+ class RedisEditor(MemoryEditor):
34
+ """
35
+ Wrapper class that implements AIQ Toolkit Interfaces for Redis memory storage.
36
+ """
37
+
38
+ def __init__(self, redis_client: redis.Redis, key_prefix: str, embedder: Embeddings):
39
+ """
40
+ Initialize Redis client for memory storage.
41
+
42
+ Args:
43
+ redis_client: (redis.Redis) Redis client
44
+ key_prefix: (str) Redis key prefix
45
+ embedder: (Embeddings) Embedder for semantic search functionality
46
+ """
47
+
48
+ self._client: redis.Redis = redis_client
49
+ self._key_prefix: str = key_prefix
50
+ self._embedder: Embeddings = embedder
51
+
52
+ async def add_items(self, items: list[MemoryItem]) -> None:
53
+ """
54
+ Insert Multiple MemoryItems into Redis.
55
+ Each MemoryItem is stored with its metadata and tags.
56
+ """
57
+ logger.debug(f"Attempting to add {len(items)} items to Redis")
58
+
59
+ for memory_item in items:
60
+ item_meta = memory_item.metadata
61
+ conversation = memory_item.conversation
62
+ user_id = memory_item.user_id
63
+ tags = memory_item.tags
64
+ memory_id = secrets.token_hex(4) # e.g. 02ba3fe9
65
+
66
+ # Create a unique key for this memory item
67
+ memory_key = f"{self._key_prefix}:memory:{memory_id}"
68
+ logger.debug(f"Generated memory key: {memory_key}")
69
+
70
+ # Prepare memory data
71
+ memory_data = {
72
+ "conversation": conversation,
73
+ "user_id": user_id,
74
+ "tags": tags,
75
+ "metadata": item_meta,
76
+ "memory": memory_item.memory or ""
77
+ }
78
+ logger.debug(f"Prepared memory data for key {memory_key}")
79
+
80
+ # If we have memory, compute and store the embedding
81
+ if memory_item.memory:
82
+ logger.debug("Computing embedding for memory text")
83
+ search_vector = await self._embedder.aembed_query(memory_item.memory)
84
+ logger.debug(f"Generated embedding vector of length: {len(search_vector)}")
85
+ memory_data["embedding"] = search_vector
86
+
87
+ try:
88
+ # Store as JSON in Redis
89
+ logger.debug(f"Attempting to store memory data in Redis for key: {memory_key}")
90
+ await self._client.json().set(memory_key, "$", memory_data)
91
+ logger.debug(f"Successfully stored memory data for key: {memory_key}")
92
+
93
+ # Verify the data was stored
94
+ stored_data = await self._client.json().get(memory_key)
95
+ logger.debug(f"Verified data storage for key {memory_key}: {bool(stored_data)}")
96
+
97
+ except redis_exceptions.ResponseError as e:
98
+ logger.error(f"Failed to store memory item: {str(e)}")
99
+ raise
100
+ except redis_exceptions.ConnectionError as e:
101
+ logger.error(f"Redis connection error while storing memory item: {str(e)}")
102
+ raise
103
+
104
+ async def search(self, query: str, top_k: int = 5, **kwargs) -> list[MemoryItem]:
105
+ """
106
+ Retrieve items relevant to the given query.
107
+
108
+ Args:
109
+ query (str): The query string to match.
110
+ top_k (int): Maximum number of items to return.
111
+ kwargs (dict): Keyword arguments to pass to the search method.
112
+
113
+ Returns:
114
+ list[MemoryItem]: The most relevant MemoryItems for the given query.
115
+ """
116
+ logger.debug(f"Search called with query: {query}, top_k: {top_k}, kwargs: {kwargs}")
117
+
118
+ user_id = kwargs.get("user_id", "redis") # TODO: remove this fallback username
119
+ logger.debug(f"Using user_id: {user_id}")
120
+
121
+ # Perform vector search using Redis search
122
+ logger.debug("Using embedder for vector search")
123
+ try:
124
+ logger.debug(f"Generating embedding for query: '{query}'")
125
+ query_vector = await self._embedder.aembed_query(query)
126
+ logger.debug(f"Generated embedding vector of length: {len(query_vector)}")
127
+ except Exception as e:
128
+ logger.error(f"Failed to generate embedding: {str(e)}")
129
+ raise
130
+
131
+ # Create vector search query
132
+ search_query = (
133
+ Query(f"(@user_id:{user_id})=>[KNN {top_k} @embedding $vec AS score]").sort_by("score").return_fields(
134
+ "conversation", "user_id", "tags", "metadata", "memory", "score").dialect(2))
135
+ logger.debug(f"Created search query: {search_query}")
136
+ logger.debug(f"Query string: {search_query.query_string()}")
137
+
138
+ # Convert query vector to bytes
139
+ try:
140
+ logger.debug("Converting query vector to bytes")
141
+ query_vector_bytes = np.array(query_vector, dtype=np.float32).tobytes()
142
+ logger.debug(f"Converted vector to bytes of length: {len(query_vector_bytes)}")
143
+ except Exception as e:
144
+ logger.error(f"Failed to convert vector to bytes: {str(e)}")
145
+ raise
146
+
147
+ try:
148
+ # Execute search with vector parameters
149
+ logger.debug("Executing Redis search with vector parameters")
150
+ logger.debug(f"Search query parameters: vec length={len(query_vector_bytes)}")
151
+
152
+ # Log the actual query being executed
153
+ logger.debug(f"Full search query: {search_query.query_string()}")
154
+
155
+ # Check if there are any documents in the index
156
+ try:
157
+ total_docs = await self._client.ft(INDEX_NAME).info()
158
+ logger.debug(f"Total documents in index: {total_docs.get('num_docs', 0)}")
159
+ except Exception as e:
160
+ logger.error(f"Failed to get index info: {str(e)}")
161
+
162
+ # Execute the search
163
+ results = await self._client.ft(INDEX_NAME).search(search_query, query_params={"vec": query_vector_bytes})
164
+
165
+ # Log detailed results information
166
+ logger.debug(f"Search returned {len(results.docs)} results")
167
+ logger.debug(f"Total results found: {results.total}")
168
+
169
+ # Convert results to MemoryItems
170
+ memories = []
171
+ for i, doc in enumerate(results.docs):
172
+ try:
173
+ logger.debug(f"Processing result {i+1}/{len(results.docs)}")
174
+ # Get the document data from the correct attribute
175
+ memory_data = {
176
+ "conversation": getattr(doc, 'conversation', []),
177
+ "user_id": getattr(doc, 'user_id', user_id),
178
+ "tags": getattr(doc, 'tags', []),
179
+ "metadata": getattr(doc, 'metadata', {}),
180
+ "memory": getattr(doc, 'memory', "")
181
+ }
182
+ logger.debug(f"Similarity score: {getattr(doc, 'score', 0)}")
183
+ logger.debug(f"Extracted data for result {i+1}: {memory_data}")
184
+ memory_item = self._create_memory_item(memory_data, user_id)
185
+ memories.append(memory_item)
186
+ logger.debug(f"Successfully created MemoryItem for result {i+1}")
187
+ except Exception as e:
188
+ logger.error(f"Failed to process result {i+1}: {str(e)}")
189
+ raise
190
+
191
+ logger.debug(f"Successfully processed all {len(memories)} results")
192
+ return memories
193
+ except redis_exceptions.ResponseError as e:
194
+ logger.error(f"Search failed with ResponseError: {str(e)}")
195
+ raise
196
+ except redis_exceptions.ConnectionError as e:
197
+ logger.error(f"Search failed with ConnectionError: {str(e)}")
198
+ raise
199
+ except Exception as e:
200
+ logger.error(f"Unexpected error during search: {str(e)}")
201
+ raise
202
+
203
+ def _create_memory_item(self, memory_data: dict, user_id: str) -> MemoryItem:
204
+ """Helper method to create a MemoryItem from Redis data."""
205
+ # Ensure tags is always a list
206
+ tags = memory_data.get("tags", [])
207
+ # Not sure why but sometimes the tags are retrieved as a string
208
+ if isinstance(tags, str):
209
+ tags = [tags]
210
+ elif not isinstance(tags, list):
211
+ tags = []
212
+
213
+ return MemoryItem(conversation=memory_data.get("conversation", []),
214
+ user_id=user_id,
215
+ memory=memory_data.get("memory", ""),
216
+ tags=tags,
217
+ metadata=memory_data.get("metadata", {}))
218
+
219
+ async def remove_items(self, **kwargs):
220
+ """
221
+ Remove memory items based on provided criteria.
222
+ """
223
+ try:
224
+ pattern = f"{self._key_prefix}:memory:*"
225
+ keys = await self._client.keys(pattern)
226
+ if keys:
227
+ await self._client.delete(*keys)
228
+ except redis_exceptions.ResponseError as e:
229
+ logger.error(f"Failed to remove items: {str(e)}")
230
+ raise
231
+ except redis_exceptions.ConnectionError as e:
232
+ logger.error(f"Redis connection error while removing items: {str(e)}")
233
+ raise
@@ -0,0 +1,22 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ # pylint: disable=unused-import
17
+ # flake8: noqa
18
+ # isort:skip_file
19
+
20
+ # Import any providers which need to be automatically registered here
21
+
22
+ from . import memory
@@ -0,0 +1,135 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: Apache-2.0
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ import logging
17
+
18
+ import redis.asyncio as redis
19
+ import redis.exceptions as redis_exceptions
20
+ from redis.commands.search.field import TagField
21
+ from redis.commands.search.field import TextField
22
+ from redis.commands.search.field import VectorField
23
+ from redis.commands.search.indexDefinition import IndexDefinition
24
+ from redis.commands.search.indexDefinition import IndexType
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+ INDEX_NAME = "memory_idx"
29
+ DEFAULT_DIM = 384 # Default embedding dimension
30
+
31
+
32
+ def create_schema(embedding_dim: int = DEFAULT_DIM):
33
+ """
34
+ Create the Redis search schema for redis_memory.
35
+
36
+ Args:
37
+ embedding_dim (int): Dimension of the embedding vectors
38
+
39
+ Returns:
40
+ tuple: Schema definition for Redis search
41
+ """
42
+ logger.info(f"Creating schema with embedding dimension: {embedding_dim}")
43
+
44
+ embedding_field = VectorField("$.embedding",
45
+ "HNSW",
46
+ {
47
+ "TYPE": "FLOAT32",
48
+ "DIM": embedding_dim,
49
+ "DISTANCE_METRIC": "L2",
50
+ "INITIAL_CAP": 100,
51
+ "M": 16,
52
+ "EF_CONSTRUCTION": 200,
53
+ "EF_RUNTIME": 10
54
+ },
55
+ as_name="embedding")
56
+ logger.info(f"Created embedding field with dimension {embedding_dim}")
57
+
58
+ schema = (
59
+ TextField("$.user_id", as_name="user_id"),
60
+ TagField("$.tags[*]", as_name="tags"),
61
+ TextField("$.memory", as_name="memory"),
62
+ # TextField("$.conversations[*]", as_name="conversations"), # TODO: figure out if/how this should be done
63
+ embedding_field)
64
+
65
+ # Log the schema details
66
+ logger.info("Schema fields:")
67
+ for field in schema:
68
+ logger.info(f" - {field.name}: {type(field).__name__}")
69
+
70
+ return schema
71
+
72
+
73
+ async def ensure_index_exists(client: redis.Redis, key_prefix: str, embedding_dim: int | None) -> None:
74
+ """
75
+ Ensure the Redis search index exists, creating it if necessary.
76
+
77
+ Args:
78
+ client (redis.Redis): Redis client instance
79
+ key_prefix (str): Prefix for keys to be indexed
80
+ embedding_dim (Optional[int]): Dimension of embedding vectors. If None, uses default.
81
+ """
82
+ try:
83
+ # Check if index exists
84
+ logger.info(f"Checking if index '{INDEX_NAME}' exists...")
85
+ info = await client.ft(INDEX_NAME).info()
86
+ logger.info(f"Redis search index '{INDEX_NAME}' exists.")
87
+
88
+ # Verify the schema
89
+ schema = info.get('attributes', [])
90
+
91
+ return
92
+ except redis_exceptions.ResponseError as e:
93
+ error_msg = str(e)
94
+ if "no such index" not in error_msg.lower() and "Index needs recreation" not in error_msg:
95
+ logger.error(f"Unexpected Redis error: {error_msg}")
96
+ raise
97
+
98
+ # Index doesn't exist or needs recreation
99
+ logger.info(f"Creating Redis search index '{INDEX_NAME}' with prefix '{key_prefix}'")
100
+
101
+ # Drop any existing index
102
+ try:
103
+ logger.info(f"Attempting to drop existing index '{INDEX_NAME}' if it exists")
104
+ await client.ft(INDEX_NAME).dropindex()
105
+ logger.info(f"Successfully dropped existing index '{INDEX_NAME}'")
106
+ except redis_exceptions.ResponseError as e:
107
+ if "no such index" not in str(e).lower():
108
+ logger.warning(f"Error while dropping index: {str(e)}")
109
+
110
+ # Create new schema and index
111
+ schema = create_schema(embedding_dim or DEFAULT_DIM)
112
+ logger.info(f"Created schema with embedding dimension: {embedding_dim or DEFAULT_DIM}")
113
+
114
+ try:
115
+ # Create the index
116
+ logger.info(f"Creating new index '{INDEX_NAME}' with schema")
117
+ await client.ft(INDEX_NAME).create_index(schema,
118
+ definition=IndexDefinition(prefix=[f"{key_prefix}:"],
119
+ index_type=IndexType.JSON))
120
+
121
+ # Verify index was created
122
+ info = await client.ft(INDEX_NAME).info()
123
+ logger.info(f"Successfully created Redis search index '{INDEX_NAME}'")
124
+ logger.debug(f"Redis search index info: {info}")
125
+
126
+ # Verify the schema
127
+ schema = info.get('attributes', [])
128
+ logger.debug(f"New index schema: {schema}")
129
+
130
+ except redis_exceptions.ResponseError as e:
131
+ logger.error(f"Failed to create index: {str(e)}")
132
+ raise
133
+ except redis_exceptions.ConnectionError as e:
134
+ logger.error(f"Redis connection error while creating index: {str(e)}")
135
+ raise
@@ -0,0 +1,34 @@
1
+ Metadata-Version: 2.4
2
+ Name: nvidia-nat-redis
3
+ Version: 1.2.0rc5
4
+ Summary: Subpackage for Redis memory integration in AIQtoolkit
5
+ Keywords: ai,agents,memory
6
+ Classifier: Programming Language :: Python
7
+ Requires-Python: <3.13,>=3.11
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: nvidia-nat~=1.2
10
+ Requires-Dist: redis~=4.3.4
11
+
12
+ <!--
13
+ SPDX-FileCopyrightText: Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
14
+ SPDX-License-Identifier: Apache-2.0
15
+
16
+ Licensed under the Apache License, Version 2.0 (the "License");
17
+ you may not use this file except in compliance with the License.
18
+ You may obtain a copy of the License at
19
+
20
+ http://www.apache.org/licenses/LICENSE-2.0
21
+
22
+ Unless required by applicable law or agreed to in writing, software
23
+ distributed under the License is distributed on an "AS IS" BASIS,
24
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25
+ See the License for the specific language governing permissions and
26
+ limitations under the License.
27
+ -->
28
+
29
+ ![NVIDIA NeMo Agent Toolkit](https://media.githubusercontent.com/media/NVIDIA/NeMo-Agent-Toolkit/refs/heads/main/docs/source/_static/aiqtoolkit_banner.png "NeMo Agent toolkit banner image")
30
+
31
+ # NVIDIA NeMo Agent Toolkit Subpackage
32
+ This is a subpackage for Redis memory integration in NeMo Agent toolkit.
33
+
34
+ For more information about NeMo Agent toolkit, please visit the [NeMo Agent toolkit package](https://pypi.org/project/aiqtoolkit/).
@@ -0,0 +1,11 @@
1
+ aiq/meta/pypi.md,sha256=tfhFKDhfJtxBVT6J5pnvrIdaBN_Uy5Dy6in6CCZpYpU,1101
2
+ aiq/plugins/redis/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ aiq/plugins/redis/memory.py,sha256=rz-cbcSOe1knd795783leY4vm0n1C4UtUJtZOHmINcU,2569
4
+ aiq/plugins/redis/redis_editor.py,sha256=2hb5BGSTjWId5sb6dNAA3MLzO6WSVAU-Px-iv7Fzy2Q,10088
5
+ aiq/plugins/redis/register.py,sha256=_ffKNKnMfkB2HzX4Nk_9EW0pwebg3GuzAE-iB-CoC3E,839
6
+ aiq/plugins/redis/schema.py,sha256=lcazZKzWEoJEJmGEt6rk135zJ6kGqw6v2b1B5l1xg7o,5494
7
+ nvidia_nat_redis-1.2.0rc5.dist-info/METADATA,sha256=PrUfA8u5_hdV53lCkhonCJtaZILr-LCmAsxw7ywx5i4,1427
8
+ nvidia_nat_redis-1.2.0rc5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
+ nvidia_nat_redis-1.2.0rc5.dist-info/entry_points.txt,sha256=JyY7zDx7SZTvETPEEZuTgDVhe3EN23s1Glyp8RHIoWY,56
10
+ nvidia_nat_redis-1.2.0rc5.dist-info/top_level.txt,sha256=fo7AzYcNhZ_tRWrhGumtxwnxMew4xrT1iwouDy_f0Kc,4
11
+ nvidia_nat_redis-1.2.0rc5.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [aiq.components]
2
+ aiq_redis = aiq.plugins.redis.register