embed-client 3.1.0.1__py3-none-any.whl → 3.1.0.3__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.
embed_client/__init__.py CHANGED
@@ -1 +1,21 @@
1
-
1
+ """
2
+ embed-client: Async client for Embedding Service API with comprehensive authentication, SSL/TLS, and mTLS support
3
+
4
+ Author: Vasiliy Zdanovskiy
5
+ email: vasilyvz@gmail.com
6
+ """
7
+
8
+ from .async_client import EmbeddingServiceAsyncClient
9
+ from .config import ClientConfig
10
+ from .auth import AuthManager
11
+ from .ssl_manager import ClientSSLManager
12
+ from .client_factory import ClientFactory
13
+
14
+ __version__ = "3.1.0.2"
15
+ __all__ = [
16
+ "EmbeddingServiceAsyncClient",
17
+ "ClientConfig",
18
+ "AuthManager",
19
+ "ClientSSLManager",
20
+ "ClientFactory"
21
+ ]
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Main entry point for embed-client CLI.
4
+
5
+ Author: Vasiliy Zdanovskiy
6
+ email: vasilyvz@gmail.com
7
+ """
8
+
9
+ import asyncio
10
+ import sys
11
+ from .cli import main
12
+
13
+ if __name__ == "__main__":
14
+ sys.exit(asyncio.run(main()))
embed_client/cli.py ADDED
@@ -0,0 +1,277 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ CLI Application for Text Vectorization
4
+ Command-line interface for embedding text using embed-client.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ import argparse
11
+ import asyncio
12
+ import json
13
+ import sys
14
+ from pathlib import Path
15
+ from typing import List, Optional, Dict, Any
16
+
17
+ from embed_client.async_client import EmbeddingServiceAsyncClient
18
+ from embed_client.client_factory import ClientFactory
19
+
20
+
21
+ class VectorizationCLI:
22
+ """CLI application for text vectorization."""
23
+
24
+ def __init__(self):
25
+ self.client = None
26
+
27
+ async def connect(self, config: Dict[str, Any]):
28
+ """Connect to embedding service."""
29
+ try:
30
+ self.client = EmbeddingServiceAsyncClient(config_dict=config)
31
+ await self.client.__aenter__()
32
+ return True
33
+ except Exception as e:
34
+ print(f"❌ Failed to connect: {e}")
35
+ return False
36
+
37
+ async def disconnect(self):
38
+ """Disconnect from embedding service."""
39
+ if self.client:
40
+ await self.client.close()
41
+
42
+ async def health_check(self) -> bool:
43
+ """Check service health."""
44
+ try:
45
+ result = await self.client.health()
46
+ print(f"✅ Service health: {result.get('status', 'unknown')}")
47
+ return True
48
+ except Exception as e:
49
+ print(f"❌ Health check failed: {e}")
50
+ return False
51
+
52
+ async def vectorize_texts(self, texts: List[str], output_format: str = "json") -> Optional[List[List[float]]]:
53
+ """Vectorize texts."""
54
+ try:
55
+ params = {"texts": texts}
56
+ result = await self.client.cmd("embed", params=params)
57
+
58
+ # Extract embeddings from result
59
+ embeddings = self._extract_embeddings(result)
60
+
61
+ if output_format == "json":
62
+ print(json.dumps(embeddings, indent=2))
63
+ elif output_format == "csv":
64
+ self._print_csv(embeddings)
65
+ elif output_format == "vectors":
66
+ self._print_vectors(embeddings)
67
+
68
+ return embeddings
69
+
70
+ except Exception as e:
71
+ print(f"❌ Vectorization failed: {e}")
72
+ return None
73
+
74
+ def _extract_embeddings(self, result: Dict[str, Any]) -> List[List[float]]:
75
+ """Extract embeddings from API response."""
76
+ # Handle different response formats
77
+ if "embeddings" in result:
78
+ return result["embeddings"]
79
+
80
+ if "result" in result:
81
+ res = result["result"]
82
+
83
+ if isinstance(res, list):
84
+ return res
85
+
86
+ if isinstance(res, dict):
87
+ if "embeddings" in res:
88
+ return res["embeddings"]
89
+
90
+ if "data" in res and isinstance(res["data"], list):
91
+ embeddings = []
92
+ for item in res["data"]:
93
+ if isinstance(item, dict) and "embedding" in item:
94
+ embeddings.append(item["embedding"])
95
+ else:
96
+ embeddings.append(item)
97
+ return embeddings
98
+
99
+ raise ValueError(f"Cannot extract embeddings from response: {result}")
100
+
101
+ def _print_csv(self, embeddings: List[List[float]]):
102
+ """Print embeddings in CSV format."""
103
+ for i, embedding in enumerate(embeddings):
104
+ print(f"text_{i}," + ",".join(map(str, embedding)))
105
+
106
+ def _print_vectors(self, embeddings: List[List[float]]):
107
+ """Print embeddings as vectors."""
108
+ for i, embedding in enumerate(embeddings):
109
+ print(f"Text {i}: [{', '.join(map(str, embedding))}]")
110
+
111
+ async def get_help(self, command: Optional[str] = None):
112
+ """Get help information."""
113
+ try:
114
+ if command:
115
+ result = await self.client.cmd("help", params={"command": command})
116
+ else:
117
+ result = await self.client.cmd("help")
118
+
119
+ print(json.dumps(result, indent=2))
120
+
121
+ except Exception as e:
122
+ print(f"❌ Help request failed: {e}")
123
+
124
+ async def get_commands(self):
125
+ """Get available commands."""
126
+ try:
127
+ result = await self.client.get_commands()
128
+ print(json.dumps(result, indent=2))
129
+
130
+ except Exception as e:
131
+ print(f"❌ Commands request failed: {e}")
132
+
133
+
134
+ def create_config_from_args(args) -> Dict[str, Any]:
135
+ """Create configuration from command line arguments."""
136
+ config = {
137
+ "server": {
138
+ "host": args.host,
139
+ "port": args.port
140
+ },
141
+ "auth": {"method": "none"},
142
+ "ssl": {"enabled": False},
143
+ "security": {"enabled": False}
144
+ }
145
+
146
+ # Add authentication if specified
147
+ if args.api_key:
148
+ config["auth"]["method"] = "api_key"
149
+ config["security"] = {
150
+ "enabled": True,
151
+ "tokens": {"user": args.api_key}
152
+ }
153
+
154
+ # Add SSL if specified
155
+ if args.ssl:
156
+ config["ssl"]["enabled"] = True
157
+ if args.cert_file:
158
+ config["ssl"]["cert_file"] = args.cert_file
159
+ if args.key_file:
160
+ config["ssl"]["key_file"] = args.key_file
161
+ if args.ca_cert_file:
162
+ config["ssl"]["ca_cert_file"] = args.ca_cert_file
163
+
164
+ return config
165
+
166
+
167
+ async def main():
168
+ """Main CLI function."""
169
+ parser = argparse.ArgumentParser(
170
+ description="CLI Application for Text Vectorization",
171
+ formatter_class=argparse.RawDescriptionHelpFormatter,
172
+ epilog="""
173
+ Examples:
174
+ # Vectorize text from command line
175
+ python -m embed_client.cli vectorize "hello world" "test text"
176
+
177
+ # Vectorize text from file
178
+ python -m embed_client.cli vectorize --file texts.txt
179
+
180
+ # Use HTTPS with authentication
181
+ python -m embed_client.cli vectorize "hello world" --host https://localhost --port 8443 --api-key your-key
182
+
183
+ # Use mTLS
184
+ python -m embed_client.cli vectorize "hello world" --ssl --cert-file client.crt --key-file client.key
185
+
186
+ # Get help from service
187
+ python -m embed_client.cli help
188
+
189
+ # Check service health
190
+ python -m embed_client.cli health
191
+ """
192
+ )
193
+
194
+ # Connection options
195
+ parser.add_argument("--host", default="http://localhost", help="Server host (default: http://localhost)")
196
+ parser.add_argument("--port", type=int, default=8001, help="Server port (default: 8001)")
197
+ parser.add_argument("--api-key", help="API key for authentication")
198
+ parser.add_argument("--ssl", action="store_true", help="Enable SSL/TLS")
199
+ parser.add_argument("--cert-file", help="Client certificate file")
200
+ parser.add_argument("--key-file", help="Client private key file")
201
+ parser.add_argument("--ca-cert-file", help="CA certificate file")
202
+
203
+ # Output options
204
+ parser.add_argument("--format", choices=["json", "csv", "vectors"], default="json", help="Output format")
205
+ parser.add_argument("--output", "-o", help="Output file (default: stdout)")
206
+
207
+ # Commands
208
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
209
+
210
+ # Vectorize command
211
+ vectorize_parser = subparsers.add_parser("vectorize", help="Vectorize text")
212
+ vectorize_parser.add_argument("texts", nargs="*", help="Texts to vectorize")
213
+ vectorize_parser.add_argument("--file", "-f", help="File containing texts (one per line)")
214
+
215
+ # Health command
216
+ health_parser = subparsers.add_parser("health", help="Check service health")
217
+
218
+ # Help command
219
+ help_parser = subparsers.add_parser("help", help="Get help from service")
220
+ help_parser.add_argument("--command", help="Specific command to get help for")
221
+
222
+ # Commands command
223
+ commands_parser = subparsers.add_parser("commands", help="Get available commands")
224
+
225
+ args = parser.parse_args()
226
+
227
+ if not args.command:
228
+ parser.print_help()
229
+ return 1
230
+
231
+ # Create configuration
232
+ config = create_config_from_args(args)
233
+
234
+ # Create CLI instance
235
+ cli = VectorizationCLI()
236
+
237
+ try:
238
+ # Connect to service
239
+ if not await cli.connect(config):
240
+ return 1
241
+
242
+ # Execute command
243
+ if args.command == "vectorize":
244
+ texts = args.texts
245
+ if args.file:
246
+ with open(args.file, 'r') as f:
247
+ texts.extend(line.strip() for line in f if line.strip())
248
+
249
+ if not texts:
250
+ print("❌ No texts provided")
251
+ return 1
252
+
253
+ await cli.vectorize_texts(texts, args.format)
254
+
255
+ elif args.command == "health":
256
+ await cli.health_check()
257
+
258
+ elif args.command == "help":
259
+ await cli.get_help(args.command)
260
+
261
+ elif args.command == "commands":
262
+ await cli.get_commands()
263
+
264
+ return 0
265
+
266
+ except KeyboardInterrupt:
267
+ print("\n🛑 Interrupted by user")
268
+ return 1
269
+ except Exception as e:
270
+ print(f"❌ Error: {e}")
271
+ return 1
272
+ finally:
273
+ await cli.disconnect()
274
+
275
+
276
+ if __name__ == "__main__":
277
+ sys.exit(asyncio.run(main()))
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Demo CLI Application for Text Vectorization
4
+ Simple demonstration of the CLI functionality.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ import asyncio
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ # Add the project root to the path
15
+ sys.path.insert(0, str(Path(__file__).parent.parent))
16
+
17
+ from embed_client.cli import VectorizationCLI
18
+
19
+
20
+ async def demo_vectorization():
21
+ """Demonstrate vectorization functionality."""
22
+ print("🚀 Embed-Client CLI Demo")
23
+ print("=" * 40)
24
+
25
+ # Configuration for demo
26
+ config = {
27
+ "server": {"host": "http://localhost", "port": 8001},
28
+ "auth": {"method": "none"},
29
+ "ssl": {"enabled": False},
30
+ "security": {"enabled": False}
31
+ }
32
+
33
+ cli = VectorizationCLI()
34
+
35
+ try:
36
+ # Connect to service
37
+ print("🔌 Connecting to embedding service...")
38
+ if not await cli.connect(config):
39
+ print("❌ Failed to connect to service")
40
+ return
41
+
42
+ # Health check
43
+ print("\n🏥 Checking service health...")
44
+ await cli.health_check()
45
+
46
+ # Get help
47
+ print("\n❓ Getting help from service...")
48
+ await cli.get_help()
49
+
50
+ # Get commands
51
+ print("\n📋 Getting available commands...")
52
+ await cli.get_commands()
53
+
54
+ # Vectorize texts
55
+ print("\n🔤 Vectorizing texts...")
56
+ texts = [
57
+ "Hello world",
58
+ "This is a test",
59
+ "Machine learning is awesome"
60
+ ]
61
+
62
+ print(f"📝 Vectorizing {len(texts)} texts:")
63
+ for i, text in enumerate(texts):
64
+ print(f" {i+1}. {text}")
65
+
66
+ await cli.vectorize_texts(texts, "json")
67
+
68
+ print("\n✅ Demo completed successfully!")
69
+
70
+ except Exception as e:
71
+ print(f"❌ Demo failed: {e}")
72
+ finally:
73
+ await cli.disconnect()
74
+
75
+
76
+ async def main():
77
+ """Main demo function."""
78
+ await demo_vectorization()
79
+
80
+
81
+ if __name__ == "__main__":
82
+ asyncio.run(main())
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Demo Security CLI Application
4
+ Demonstrates all 8 security modes using the security CLI.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ import asyncio
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ # Add the project root to the path
15
+ sys.path.insert(0, str(Path(__file__).parent.parent))
16
+
17
+ from embed_client.security_cli import SecurityCLI, create_config_from_security_mode
18
+
19
+
20
+ async def demo_security_mode(mode: str, host: str, port: int, **kwargs):
21
+ """Demonstrate a specific security mode."""
22
+ print(f"\n🔐 Demonstrating {mode} mode...")
23
+ print("=" * 50)
24
+
25
+ # Create configuration
26
+ config = create_config_from_security_mode(mode, host, port, **kwargs)
27
+
28
+ cli = SecurityCLI()
29
+
30
+ try:
31
+ # Connect to service
32
+ print(f"🔌 Connecting using {mode} mode...")
33
+ if not await cli.connect(config):
34
+ print(f"❌ Failed to connect using {mode} mode")
35
+ return False
36
+
37
+ # Health check
38
+ print(f"🏥 Checking service health...")
39
+ health_ok = await cli.health_check()
40
+ if not health_ok:
41
+ print(f"❌ Health check failed for {mode} mode")
42
+ return False
43
+
44
+ # Get help
45
+ print(f"❓ Getting help from service...")
46
+ await cli.get_help()
47
+
48
+ # Get commands
49
+ print(f"📋 Getting available commands...")
50
+ await cli.get_commands()
51
+
52
+ # Vectorize texts
53
+ texts = [
54
+ f"Hello from {mode} mode",
55
+ f"Security test for {mode}",
56
+ f"Vectorization using {mode}"
57
+ ]
58
+
59
+ print(f"🔤 Vectorizing {len(texts)} texts using {mode} mode...")
60
+ for i, text in enumerate(texts):
61
+ print(f" {i+1}. {text}")
62
+
63
+ # Test different output formats
64
+ print(f"\n📊 Testing JSON format:")
65
+ await cli.vectorize_texts(texts, "json")
66
+
67
+ print(f"\n📊 Testing CSV format:")
68
+ await cli.vectorize_texts(texts, "csv")
69
+
70
+ print(f"\n📊 Testing vectors format:")
71
+ await cli.vectorize_texts(texts, "vectors")
72
+
73
+ print(f"✅ {mode} mode demonstration completed successfully!")
74
+ return True
75
+
76
+ except Exception as e:
77
+ print(f"❌ {mode} mode demonstration failed: {e}")
78
+ return False
79
+ finally:
80
+ await cli.disconnect()
81
+
82
+
83
+ async def demo_all_security_modes():
84
+ """Demonstrate all 8 security modes."""
85
+ print("🚀 Security CLI Demo - All 8 Security Modes")
86
+ print("=" * 60)
87
+
88
+ # Demo configurations for each mode
89
+ demos = [
90
+ {
91
+ "mode": "http",
92
+ "host": "localhost",
93
+ "port": 10001,
94
+ "description": "Plain HTTP without authentication"
95
+ },
96
+ {
97
+ "mode": "http_token",
98
+ "host": "localhost",
99
+ "port": 10002,
100
+ "api_key": "admin-secret-key",
101
+ "description": "HTTP with API Key authentication"
102
+ },
103
+ {
104
+ "mode": "http_token_roles",
105
+ "host": "localhost",
106
+ "port": 10003,
107
+ "api_key": "admin-secret-key",
108
+ "description": "HTTP with API Key and role-based access control"
109
+ },
110
+ {
111
+ "mode": "https",
112
+ "host": "localhost",
113
+ "port": 10011,
114
+ "cert_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/server/embedding-service.crt",
115
+ "key_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/server/embedding-service.key",
116
+ "description": "HTTPS with server certificate verification"
117
+ },
118
+ {
119
+ "mode": "https_token",
120
+ "host": "localhost",
121
+ "port": 10012,
122
+ "api_key": "admin-secret-key",
123
+ "cert_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/server/embedding-service.crt",
124
+ "key_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/server/embedding-service.key",
125
+ "description": "HTTPS with server certificates + authentication"
126
+ },
127
+ {
128
+ "mode": "https_token_roles",
129
+ "host": "localhost",
130
+ "port": 10013,
131
+ "api_key": "admin-secret-key",
132
+ "cert_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/server/embedding-service.crt",
133
+ "key_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/server/embedding-service.key",
134
+ "description": "HTTPS with server certificates + authentication + roles"
135
+ },
136
+ {
137
+ "mode": "mtls",
138
+ "host": "localhost",
139
+ "port": 10021,
140
+ "cert_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/client/embedding-service.crt",
141
+ "key_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/client/embedding-service.key",
142
+ "ca_cert_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/ca/ca.crt",
143
+ "description": "Mutual TLS with client and server certificates"
144
+ },
145
+ {
146
+ "mode": "mtls_roles",
147
+ "host": "localhost",
148
+ "port": 10022,
149
+ "cert_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/client/embedding-service.crt",
150
+ "key_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/client/embedding-service.key",
151
+ "ca_cert_file": "/home/vasilyvz/projects/embed-client/mtls_certificates/ca/ca.crt",
152
+ "description": "mTLS with role-based access control"
153
+ }
154
+ ]
155
+
156
+ results = {}
157
+
158
+ for demo in demos:
159
+ mode = demo["mode"]
160
+ description = demo["description"]
161
+
162
+ print(f"\n🔐 {mode.upper()} - {description}")
163
+ print("-" * 50)
164
+
165
+ # Extract parameters for the demo
166
+ demo_params = {k: v for k, v in demo.items() if k not in ["mode", "host", "port", "description"]}
167
+
168
+ success = await demo_security_mode(
169
+ demo["mode"],
170
+ demo["host"],
171
+ demo["port"],
172
+ **demo_params
173
+ )
174
+
175
+ results[mode] = success
176
+
177
+ # Print summary
178
+ print("\n📊 Demo Results Summary:")
179
+ print("=" * 50)
180
+ for mode, success in results.items():
181
+ status = "✅ SUCCESS" if success else "❌ FAILED"
182
+ print(f"{mode:20} {status}")
183
+
184
+ total_demos = len(results)
185
+ successful_demos = sum(1 for success in results.values() if success)
186
+ failed_demos = total_demos - successful_demos
187
+
188
+ print(f"\n🎉 Demo completed!")
189
+ print(f"📊 Results: {successful_demos}/{total_demos} demos successful")
190
+ if failed_demos > 0:
191
+ print(f"❌ {failed_demos} demos failed")
192
+ else:
193
+ print("✅ All demos successful!")
194
+
195
+
196
+ async def main():
197
+ """Main demo function."""
198
+ await demo_all_security_modes()
199
+
200
+
201
+ if __name__ == "__main__":
202
+ asyncio.run(main())
@@ -0,0 +1,409 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Security CLI Application for Text Vectorization
4
+ Command-line interface supporting all 8 security modes.
5
+
6
+ Author: Vasiliy Zdanovskiy
7
+ email: vasilyvz@gmail.com
8
+ """
9
+
10
+ import argparse
11
+ import asyncio
12
+ import json
13
+ import sys
14
+ from pathlib import Path
15
+ from typing import List, Optional, Dict, Any
16
+
17
+ from embed_client.async_client import EmbeddingServiceAsyncClient
18
+ from embed_client.client_factory import ClientFactory
19
+
20
+
21
+ class SecurityCLI:
22
+ """CLI application supporting all 8 security modes."""
23
+
24
+ def __init__(self):
25
+ self.client = None
26
+
27
+ async def connect(self, config: Dict[str, Any]):
28
+ """Connect to embedding service."""
29
+ try:
30
+ # Создаем клиент, но не входим в контекст пока
31
+ self.client = EmbeddingServiceAsyncClient(config_dict=config)
32
+ return True
33
+ except Exception as e:
34
+ print(f"❌ Failed to connect: {e}")
35
+ return False
36
+
37
+ async def disconnect(self):
38
+ """Disconnect from embedding service."""
39
+ if self.client:
40
+ await self.client.close()
41
+
42
+ async def health_check(self) -> bool:
43
+ """Check service health."""
44
+ try:
45
+ async with self.client as client:
46
+ result = await client.health()
47
+ print(f"✅ Service health: {result.get('status', 'unknown')}")
48
+ return True
49
+ except Exception as e:
50
+ print(f"❌ Health check failed: {e}")
51
+ return False
52
+
53
+ async def vectorize_texts(self, texts: List[str], output_format: str = "json") -> Optional[List[List[float]]]:
54
+ """Vectorize texts."""
55
+ try:
56
+ params = {"texts": texts}
57
+ result = await self.client.cmd("embed", params=params)
58
+
59
+ # Extract embeddings from result
60
+ embeddings = self._extract_embeddings(result)
61
+
62
+ if output_format == "json":
63
+ print(json.dumps(embeddings, indent=2))
64
+ elif output_format == "csv":
65
+ self._print_csv(embeddings)
66
+ elif output_format == "vectors":
67
+ self._print_vectors(embeddings)
68
+
69
+ return embeddings
70
+
71
+ except Exception as e:
72
+ print(f"❌ Vectorization failed: {e}")
73
+ return None
74
+
75
+ def _extract_embeddings(self, result: Dict[str, Any]) -> List[List[float]]:
76
+ """Extract embeddings from API response."""
77
+ # Handle different response formats
78
+ if "embeddings" in result:
79
+ return result["embeddings"]
80
+
81
+ if "result" in result:
82
+ res = result["result"]
83
+
84
+ if isinstance(res, list):
85
+ return res
86
+
87
+ if isinstance(res, dict):
88
+ if "embeddings" in res:
89
+ return res["embeddings"]
90
+
91
+ if "data" in res and isinstance(res["data"], list):
92
+ embeddings = []
93
+ for item in res["data"]:
94
+ if isinstance(item, dict) and "embedding" in item:
95
+ embeddings.append(item["embedding"])
96
+ else:
97
+ embeddings.append(item)
98
+ return embeddings
99
+
100
+ raise ValueError(f"Cannot extract embeddings from response: {result}")
101
+
102
+ def _print_csv(self, embeddings: List[List[float]]):
103
+ """Print embeddings in CSV format."""
104
+ for i, embedding in enumerate(embeddings):
105
+ print(f"text_{i}," + ",".join(map(str, embedding)))
106
+
107
+ def _print_vectors(self, embeddings: List[List[float]]):
108
+ """Print embeddings as vectors."""
109
+ for i, embedding in enumerate(embeddings):
110
+ print(f"Text {i}: [{', '.join(map(str, embedding))}]")
111
+
112
+ async def get_help(self, command: Optional[str] = None):
113
+ """Get help information."""
114
+ try:
115
+ if command:
116
+ result = await self.client.cmd("help", params={"command": command})
117
+ else:
118
+ result = await self.client.cmd("help")
119
+
120
+ print(json.dumps(result, indent=2))
121
+
122
+ except Exception as e:
123
+ print(f"❌ Help request failed: {e}")
124
+
125
+ async def get_commands(self):
126
+ """Get available commands."""
127
+ try:
128
+ result = await self.client.get_commands()
129
+ print(json.dumps(result, indent=2))
130
+
131
+ except Exception as e:
132
+ print(f"❌ Commands request failed: {e}")
133
+
134
+
135
+ def create_config_from_security_mode(security_mode: str, host: str, port: int, **kwargs) -> Dict[str, Any]:
136
+ """Create configuration based on security mode."""
137
+
138
+ # Base configuration
139
+ config = {
140
+ "server": {"host": host, "port": port},
141
+ "auth": {"method": "none"},
142
+ "ssl": {"enabled": False},
143
+ "security": {"enabled": False}
144
+ }
145
+
146
+ if security_mode == "http":
147
+ # HTTP - plain HTTP without authentication
148
+ config["server"]["host"] = f"http://{host.split('://')[-1]}"
149
+
150
+ elif security_mode == "http_token":
151
+ # HTTP + Token - HTTP with API Key authentication
152
+ config["server"]["host"] = f"http://{host.split('://')[-1]}"
153
+ config["auth"]["method"] = "api_key"
154
+ config["security"] = {
155
+ "enabled": True,
156
+ "tokens": {"user": kwargs.get("api_key", "admin-secret-key")}
157
+ }
158
+
159
+ elif security_mode == "http_token_roles":
160
+ # HTTP + Token + Roles - HTTP with API Key and role-based access control
161
+ config["server"]["host"] = f"http://{host.split('://')[-1]}"
162
+ config["auth"]["method"] = "api_key"
163
+ config["security"] = {
164
+ "enabled": True,
165
+ "tokens": {"user": kwargs.get("api_key", "admin-secret-key")},
166
+ "roles": {
167
+ "admin": ["read", "write", "delete", "admin"],
168
+ "user": ["read", "write"],
169
+ "readonly": ["read"]
170
+ }
171
+ }
172
+
173
+ elif security_mode == "https":
174
+ # HTTPS - HTTPS with server certificate verification
175
+ config["server"]["host"] = f"https://{host.split('://')[-1]}"
176
+ config["ssl"] = {
177
+ "enabled": True,
178
+ "verify": False, # Отключаем проверку для самоподписанных сертификатов
179
+ "cert_file": kwargs.get("cert_file"),
180
+ "key_file": kwargs.get("key_file"),
181
+ "ca_cert_file": kwargs.get("ca_cert_file")
182
+ }
183
+
184
+ elif security_mode == "https_token":
185
+ # HTTPS + Token - HTTPS with server certificates + authentication
186
+ config["server"]["host"] = f"https://{host.split('://')[-1]}"
187
+ config["auth"]["method"] = "api_key"
188
+ config["ssl"] = {
189
+ "enabled": True,
190
+ "cert_file": kwargs.get("cert_file"),
191
+ "key_file": kwargs.get("key_file")
192
+ }
193
+ config["security"] = {
194
+ "enabled": True,
195
+ "tokens": {"user": kwargs.get("api_key", "admin-secret-key")}
196
+ }
197
+
198
+ elif security_mode == "https_token_roles":
199
+ # HTTPS + Token + Roles - HTTPS with server certificates + authentication + roles
200
+ config["server"]["host"] = f"https://{host.split('://')[-1]}"
201
+ config["auth"]["method"] = "api_key"
202
+ config["ssl"] = {
203
+ "enabled": True,
204
+ "cert_file": kwargs.get("cert_file"),
205
+ "key_file": kwargs.get("key_file")
206
+ }
207
+ config["security"] = {
208
+ "enabled": True,
209
+ "tokens": {"user": kwargs.get("api_key", "admin-secret-key")},
210
+ "roles": {
211
+ "admin": ["read", "write", "delete", "admin"],
212
+ "user": ["read", "write"],
213
+ "readonly": ["read"]
214
+ }
215
+ }
216
+
217
+ elif security_mode == "mtls":
218
+ # mTLS - mutual TLS with client and server certificates
219
+ config["server"]["host"] = f"https://{host.split('://')[-1]}"
220
+ config["auth"]["method"] = "certificate"
221
+ config["ssl"] = {
222
+ "enabled": True,
223
+ "cert_file": kwargs.get("cert_file"),
224
+ "key_file": kwargs.get("key_file"),
225
+ "ca_cert_file": kwargs.get("ca_cert_file")
226
+ }
227
+
228
+ elif security_mode == "mtls_roles":
229
+ # mTLS + Roles - mTLS with role-based access control
230
+ config["server"]["host"] = f"https://{host.split('://')[-1]}"
231
+ config["auth"]["method"] = "certificate"
232
+ config["ssl"] = {
233
+ "enabled": True,
234
+ "cert_file": kwargs.get("cert_file"),
235
+ "key_file": kwargs.get("key_file"),
236
+ "ca_cert_file": kwargs.get("ca_cert_file")
237
+ }
238
+ config["security"] = {
239
+ "enabled": True,
240
+ "roles": {
241
+ "admin": ["read", "write", "delete", "admin"],
242
+ "user": ["read", "write"],
243
+ "readonly": ["read"]
244
+ }
245
+ }
246
+
247
+ return config
248
+
249
+
250
+ async def main():
251
+ """Main CLI function."""
252
+ parser = argparse.ArgumentParser(
253
+ description="Security CLI Application for Text Vectorization",
254
+ formatter_class=argparse.RawDescriptionHelpFormatter,
255
+ epilog="""
256
+ Security Modes:
257
+ http - Plain HTTP without authentication
258
+ http_token - HTTP with API Key authentication
259
+ http_token_roles - HTTP with API Key and role-based access control
260
+ https - HTTPS with server certificate verification
261
+ https_token - HTTPS with server certificates + authentication
262
+ https_token_roles - HTTPS with server certificates + authentication + roles
263
+ mtls - Mutual TLS with client and server certificates
264
+ mtls_roles - mTLS with role-based access control
265
+
266
+ Examples:
267
+ # HTTP without authentication
268
+ python -m embed_client.security_cli vectorize --mode http "hello world"
269
+
270
+ # HTTP with token authentication
271
+ python -m embed_client.security_cli vectorize --mode http_token --api-key your-key "hello world"
272
+
273
+ # HTTPS with server certificates
274
+ python -m embed_client.security_cli vectorize --mode https --cert-file server.crt --key-file server.key "hello world"
275
+
276
+ # mTLS with client certificates
277
+ python -m embed_client.security_cli vectorize --mode mtls --cert-file client.crt --key-file client.key --ca-cert-file ca.crt "hello world"
278
+
279
+ # mTLS with roles
280
+ python -m embed_client.security_cli vectorize --mode mtls_roles --cert-file client.crt --key-file client.key --ca-cert-file ca.crt "hello world"
281
+ """
282
+ )
283
+
284
+ # Security mode selection
285
+ parser.add_argument(
286
+ "--mode",
287
+ choices=[
288
+ "http", "http_token", "http_token_roles",
289
+ "https", "https_token", "https_token_roles",
290
+ "mtls", "mtls_roles"
291
+ ],
292
+ required=True,
293
+ help="Security mode to use"
294
+ )
295
+
296
+ # Connection options
297
+ parser.add_argument("--host", default="localhost", help="Server host (default: localhost)")
298
+ parser.add_argument("--port", type=int, default=8001, help="Server port (default: 8001)")
299
+
300
+ # Authentication options
301
+ parser.add_argument("--api-key", help="API key for authentication (required for token modes)")
302
+
303
+ # SSL/TLS options
304
+ parser.add_argument("--cert-file", help="Certificate file (required for HTTPS/mTLS modes)")
305
+ parser.add_argument("--key-file", help="Private key file (required for HTTPS/mTLS modes)")
306
+ parser.add_argument("--ca-cert-file", help="CA certificate file (required for mTLS modes)")
307
+
308
+ # Output options
309
+ parser.add_argument("--format", choices=["json", "csv", "vectors"], default="json", help="Output format")
310
+ parser.add_argument("--output", "-o", help="Output file (default: stdout)")
311
+
312
+ # Commands
313
+ subparsers = parser.add_subparsers(dest="command", help="Available commands")
314
+
315
+ # Vectorize command
316
+ vectorize_parser = subparsers.add_parser("vectorize", help="Vectorize text")
317
+ vectorize_parser.add_argument("texts", nargs="*", help="Texts to vectorize")
318
+ vectorize_parser.add_argument("--file", "-f", help="File containing texts (one per line)")
319
+
320
+ # Health command
321
+ health_parser = subparsers.add_parser("health", help="Check service health")
322
+
323
+ # Help command
324
+ help_parser = subparsers.add_parser("help", help="Get help from service")
325
+ help_parser.add_argument("--command", help="Specific command to get help for")
326
+
327
+ # Commands command
328
+ commands_parser = subparsers.add_parser("commands", help="Get available commands")
329
+
330
+ args = parser.parse_args()
331
+
332
+ if not args.command:
333
+ parser.print_help()
334
+ return 1
335
+
336
+ # Validate required arguments based on security mode
337
+ if "token" in args.mode and not args.api_key:
338
+ print("❌ API key required for token-based authentication modes")
339
+ return 1
340
+
341
+ if "https" in args.mode or "mtls" in args.mode:
342
+ if not args.cert_file or not args.key_file:
343
+ print("❌ Certificate and key files required for HTTPS/mTLS modes")
344
+ return 1
345
+
346
+ if "mtls" in args.mode and not args.ca_cert_file:
347
+ print("❌ CA certificate file required for mTLS modes")
348
+ return 1
349
+
350
+ # Create configuration
351
+ config = create_config_from_security_mode(
352
+ args.mode,
353
+ args.host,
354
+ args.port,
355
+ api_key=args.api_key,
356
+ cert_file=args.cert_file,
357
+ key_file=args.key_file,
358
+ ca_cert_file=args.ca_cert_file
359
+ )
360
+
361
+ # Create CLI instance
362
+ cli = SecurityCLI()
363
+
364
+ try:
365
+ # Connect to service
366
+ print(f"🔌 Connecting using {args.mode} mode...")
367
+ if not await cli.connect(config):
368
+ return 1
369
+
370
+ # Execute command
371
+ if args.command == "vectorize":
372
+ texts = args.texts
373
+ if args.file:
374
+ with open(args.file, 'r') as f:
375
+ texts.extend(line.strip() for line in f if line.strip())
376
+
377
+ if not texts:
378
+ print("❌ No texts provided")
379
+ return 1
380
+
381
+ print(f"🔤 Vectorizing {len(texts)} texts using {args.mode} mode...")
382
+ await cli.vectorize_texts(texts, args.format)
383
+
384
+ elif args.command == "health":
385
+ print(f"🏥 Checking service health using {args.mode} mode...")
386
+ await cli.health_check()
387
+
388
+ elif args.command == "help":
389
+ print(f"❓ Getting help using {args.mode} mode...")
390
+ await cli.get_help(args.command)
391
+
392
+ elif args.command == "commands":
393
+ print(f"📋 Getting commands using {args.mode} mode...")
394
+ await cli.get_commands()
395
+
396
+ return 0
397
+
398
+ except KeyboardInterrupt:
399
+ print("\n🛑 Interrupted by user")
400
+ return 1
401
+ except Exception as e:
402
+ print(f"❌ Error: {e}")
403
+ return 1
404
+ finally:
405
+ await cli.disconnect()
406
+
407
+
408
+ if __name__ == "__main__":
409
+ sys.exit(asyncio.run(main()))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: embed-client
3
- Version: 3.1.0.1
3
+ Version: 3.1.0.3
4
4
  Summary: Async client for Embedding Service API with comprehensive authentication, SSL/TLS, and mTLS support
5
5
  Author-email: Vasiliy Zdanovskiy <vasilyvz@gmail.com>
6
6
  License-Expression: MIT
@@ -29,6 +29,7 @@ Requires-Dist: aiohttp>=3.8.0
29
29
  Requires-Dist: PyJWT>=2.0.0
30
30
  Requires-Dist: cryptography>=3.0.0
31
31
  Requires-Dist: pydantic>=2.0.0
32
+ Requires-Dist: mcp-security-framework>=1.2.3
32
33
  Provides-Extra: test
33
34
  Requires-Dist: pytest; extra == "test"
34
35
  Requires-Dist: pytest-asyncio; extra == "test"
@@ -1,17 +1,22 @@
1
- embed_client/__init__.py,sha256=Nqnn8clbgv-5l0PgxcTOldg8mkMKrFn4TvPL-rYUUGg,1
1
+ embed_client/__init__.py,sha256=12iI-f075Gmv8IyQ9L7-SH8eNND4qpF6Fjqz1-P4d1Y,535
2
+ embed_client/__main__.py,sha256=oURC6FcGQxbMzz07-RxZUILIPjjIhMMWyPWEPnfbU4s,235
2
3
  embed_client/async_client.py,sha256=Zf08g-i_2ylDj4tt9E1TO9swi-zcozFDr4mJdlfiGSI,40222
3
4
  embed_client/auth.py,sha256=MMk5XqFIY5htf3Bx0JA_8mJaAiLslpLUIVUaiO84XBw,18310
4
5
  embed_client/auth_examples.py,sha256=QX_QWaaeyBAqvSs9uOP1ZVdAe72gJ91e8AP1e7R-yzQ,7361
6
+ embed_client/cli.py,sha256=xu8sM1UhnduJFt8Oe-LLxuaCWo6x-ZsYBmZNzP1Fax0,9266
5
7
  embed_client/client_factory.py,sha256=-W2nvzMuyyQNu3_nNmALKHT9uL0Z9nMYTxyQ4bgVOFQ,14279
6
8
  embed_client/client_factory_examples.py,sha256=7A2JWkyGEJeOB7p84ijklh8rTyeRUeg2lCIPTfAtahc,11823
7
9
  embed_client/config.py,sha256=CzVmduJjuN82uiaxDyMyXJLS2tNOOlDBGwcO76SWjXg,19922
8
10
  embed_client/config_examples.py,sha256=9aEF8X4h9nbfSV_CORZLrlWM5KEwuwqW6AcQDPguQ7g,6175
11
+ embed_client/demo_cli.py,sha256=20MEcpyNLCYIoqSvR1jTROuop3LAQzMdlT-EYGqq2RQ,2052
12
+ embed_client/demo_security_cli.py,sha256=gwuKzI5R4SzkkMPtkmwIYupHFDec6-yGcgGPYcPBCGI,6932
9
13
  embed_client/example_async_usage.py,sha256=dndf79MIqZaHvWzbtKrZVwU7G4a5yNL1JPMnMBEdLBg,29803
10
14
  embed_client/example_async_usage_ru.py,sha256=3HeP5YoyGd49dRlxNC-2TAh_GbwSW4jJmoLXMpMIfFs,29899
15
+ embed_client/security_cli.py,sha256=YV_IgT7btVSTMOHPrCnKNCU5NAINfQ5z_eWJSMX0A5I,15114
11
16
  embed_client/ssl_examples.py,sha256=22lTGhK2bqGJ44uUCpc7v2egY4AP3_ar8RMaTb4KBwU,9346
12
17
  embed_client/ssl_manager.py,sha256=Ts6-hKxUUNHdW3yldnoykaDblxWtGbIwKdnzPw__30U,17247
13
- embed_client-3.1.0.1.dist-info/licenses/LICENSE,sha256=6KdtUcTwmTRbJrAmYjVn7e6S-V42ubeDJ-AiVEzZ510,1075
14
- embed_client-3.1.0.1.dist-info/METADATA,sha256=mvhUNvCDzryNQh68sFeSyGx9ZG9gyypnkPWrNYqxth4,8633
15
- embed_client-3.1.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- embed_client-3.1.0.1.dist-info/top_level.txt,sha256=uG00A4d9o9DFrhiN7goObpeig72Pniby0E7UpDRgyXY,13
17
- embed_client-3.1.0.1.dist-info/RECORD,,
18
+ embed_client-3.1.0.3.dist-info/licenses/LICENSE,sha256=6KdtUcTwmTRbJrAmYjVn7e6S-V42ubeDJ-AiVEzZ510,1075
19
+ embed_client-3.1.0.3.dist-info/METADATA,sha256=15-eyREORjE5HLHyoTcQcs3JYHHEZkIk8veXaXGxE14,8678
20
+ embed_client-3.1.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
21
+ embed_client-3.1.0.3.dist-info/top_level.txt,sha256=uG00A4d9o9DFrhiN7goObpeig72Pniby0E7UpDRgyXY,13
22
+ embed_client-3.1.0.3.dist-info/RECORD,,