embed-client 2.0.0.0__py3-none-any.whl → 3.1.0.0__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.
@@ -1,71 +1,312 @@
1
1
  """
2
- Example usage of EmbeddingServiceAsyncClient.
2
+ Example usage of EmbeddingServiceAsyncClient with all security modes and ClientFactory.
3
+
4
+ Author: Vasiliy Zdanovskiy
5
+ email: vasilyvz@gmail.com
6
+
7
+ This example demonstrates all 6 security modes supported by the embed-client:
8
+ 1. HTTP (plain HTTP without authentication)
9
+ 2. HTTP + Token (HTTP with API key authentication)
10
+ 3. HTTPS (HTTPS with server certificate verification)
11
+ 4. HTTPS + Token (HTTPS with server certificates + authentication)
12
+ 5. mTLS (mutual TLS with client and server certificates)
13
+ 6. mTLS + Roles (mTLS with role-based access control)
3
14
 
4
15
  USAGE:
16
+ # Basic usage without authentication
5
17
  python embed_client/example_async_usage.py --base-url http://localhost --port 8001
6
- # или
7
- python -m asyncio embed_client/example_async_usage.py --base-url http://localhost --port 8001
8
-
9
- # Можно также использовать переменные окружения:
18
+
19
+ # With API key authentication
20
+ python embed_client/example_async_usage.py --base-url http://localhost --port 8001 --auth-method api_key --api-key your_key
21
+
22
+ # With JWT authentication
23
+ python embed_client/example_async_usage.py --base-url http://localhost --port 8001 --auth-method jwt --jwt-secret secret --jwt-username user
24
+
25
+ # With basic authentication
26
+ python embed_client/example_async_usage.py --base-url http://localhost --port 8001 --auth-method basic --username user --password pass
27
+
28
+ # With configuration file
29
+ python embed_client/example_async_usage.py --config configs/http_token.json
30
+
31
+ # With environment variables
10
32
  export EMBED_CLIENT_BASE_URL=http://localhost
11
33
  export EMBED_CLIENT_PORT=8001
34
+ export EMBED_CLIENT_AUTH_METHOD=api_key
35
+ export EMBED_CLIENT_API_KEY=your_key
12
36
  python embed_client/example_async_usage.py
13
37
 
14
- # ВАЖНО:
15
- # --base-url и --port должны быть отдельными аргументами (через пробел),
16
- # а не через = (НЕ --base_url=...)
17
- # base_url должен содержать http:// или https://
18
-
19
- EXAMPLES:
38
+ SECURITY MODES EXAMPLES:
39
+ # 1. HTTP - plain HTTP without authentication
20
40
  python embed_client/example_async_usage.py --base-url http://localhost --port 8001
21
- python -m asyncio embed_client/example_async_usage.py --base-url http://localhost --port 8001
22
- export EMBED_CLIENT_BASE_URL=http://localhost
23
- export EMBED_CLIENT_PORT=8001
41
+
42
+ # 2. HTTP + Token - HTTP with API key authentication
43
+ python embed_client/example_async_usage.py --base-url http://localhost --port 8001 --auth-method api_key --api-key admin_key_123
44
+
45
+ # 3. HTTPS - HTTPS with server certificate verification
46
+ python embed_client/example_async_usage.py --base-url https://localhost --port 9443
47
+
48
+ # 4. HTTPS + Token - HTTPS with server certificates + authentication
49
+ python embed_client/example_async_usage.py --base-url https://localhost --port 9443 --auth-method jwt --jwt-secret secret --jwt-username admin
50
+
51
+ # 5. mTLS - mutual TLS with client and server certificates
52
+ python embed_client/example_async_usage.py --base-url https://localhost --port 9443 --cert-file certs/client.crt --key-file keys/client.key
53
+
54
+ # 6. mTLS + Roles - mTLS with role-based access control
55
+ python embed_client/example_async_usage.py --base-url https://localhost --port 9443 --cert-file certs/client.crt --key-file keys/client.key --roles admin,user
56
+
57
+ CLIENT FACTORY EXAMPLES:
58
+ # Automatic security mode detection
59
+ python embed_client/example_async_usage.py --factory-mode auto --base-url https://localhost --port 9443 --auth-method api_key --api-key key
60
+
61
+ # Specific security mode creation
62
+ python embed_client/example_async_usage.py --factory-mode https_token --base-url https://localhost --port 9443 --auth-method basic --username user --password pass
63
+
64
+ # mTLS with factory
65
+ python embed_client/example_async_usage.py --factory-mode mtls --base-url https://localhost --port 9443 --cert-file certs/client.crt --key-file keys/client.key
66
+
67
+ SSL/TLS EXAMPLES:
68
+ # HTTPS with SSL verification disabled
69
+ python embed_client/example_async_usage.py --base-url https://localhost --port 9443 --ssl-verify-mode CERT_NONE
70
+
71
+ # mTLS with custom CA certificate
72
+ python embed_client/example_async_usage.py --base-url https://localhost --port 9443 --cert-file certs/client.crt --key-file keys/client.key --ca-cert-file certs/ca.crt
73
+
74
+ # HTTPS with custom SSL settings
75
+ python embed_client/example_async_usage.py --base-url https://localhost --port 9443 --ssl-verify-mode CERT_REQUIRED --ssl-check-hostname --ssl-check-expiry
76
+
77
+ CONFIGURATION EXAMPLES:
78
+ # Using configuration file
79
+ python embed_client/example_async_usage.py --config configs/https_token.json
80
+
81
+ # Using environment variables
82
+ export EMBED_CLIENT_BASE_URL=https://secure.example.com
83
+ export EMBED_CLIENT_PORT=9443
84
+ export EMBED_CLIENT_AUTH_METHOD=api_key
85
+ export EMBED_CLIENT_API_KEY=production_key
24
86
  python embed_client/example_async_usage.py
25
87
 
26
- Explicit session close example:
88
+ DEMO MODE:
89
+ # Show all security modes demonstration
90
+ python embed_client/example_async_usage.py --demo-mode
91
+
92
+ PROGRAMMATIC USAGE EXAMPLES:
27
93
  import asyncio
28
94
  from embed_client.async_client import EmbeddingServiceAsyncClient
95
+ from embed_client.config import ClientConfig
96
+ from embed_client.client_factory import ClientFactory, create_client
97
+
29
98
  async def main():
30
- client = EmbeddingServiceAsyncClient(base_url="http://localhost", port=8001)
31
- # ... use client ...
32
- await client.close() # Explicitly close session
99
+ # Method 1: Direct client creation
100
+ client = EmbeddingServiceAsyncClient('http://localhost', 8001)
101
+ await client.close()
102
+
103
+ # Method 2: Using configuration
104
+ config = ClientConfig()
105
+ config.configure_server('http://localhost', 8001)
106
+ client = EmbeddingServiceAsyncClient.from_config(config)
107
+ await client.close()
108
+
109
+ # Method 3: Using factory with automatic detection
110
+ client = create_client('https://localhost', 9443, auth_method='api_key', api_key='key')
111
+ await client.close()
112
+
113
+ # Method 4: Using specific factory method
114
+ client = ClientFactory.create_https_token_client(
115
+ 'https://localhost', 9443, 'api_key', api_key='key'
116
+ )
117
+ await client.close()
118
+
119
+ # Method 5: Using with_auth method for dynamic authentication
120
+ client = EmbeddingServiceAsyncClient('http://localhost', 8001)
121
+ client = client.with_auth('api_key', api_key='dynamic_key')
122
+ await client.close()
123
+
33
124
  asyncio.run(main())
34
125
  """
35
126
 
127
+ import argparse
36
128
  import asyncio
37
- import sys
129
+ import json
38
130
  import os
39
- from embed_client.async_client import (
40
- EmbeddingServiceAsyncClient,
41
- EmbeddingServiceError,
42
- EmbeddingServiceAPIError,
43
- EmbeddingServiceHTTPError,
44
- EmbeddingServiceConnectionError,
45
- EmbeddingServiceTimeoutError,
46
- EmbeddingServiceJSONError,
47
- EmbeddingServiceConfigError
131
+ import sys
132
+ from typing import Dict, Any, Optional, Union
133
+
134
+ from embed_client.async_client import EmbeddingServiceAsyncClient, EmbeddingServiceError, EmbeddingServiceConfigError
135
+ from embed_client.config import ClientConfig
136
+ from embed_client.client_factory import (
137
+ ClientFactory, SecurityMode, create_client, create_client_from_config,
138
+ create_client_from_env, detect_security_mode
48
139
  )
49
140
 
141
+
50
142
  def get_params():
51
- base_url = None
52
- port = None
53
- for i, arg in enumerate(sys.argv):
54
- if arg in ("--base-url", "-b") and i + 1 < len(sys.argv):
55
- base_url = sys.argv[i + 1]
56
- if arg in ("--port", "-p") and i + 1 < len(sys.argv):
57
- port = sys.argv[i + 1]
58
- if not base_url:
59
- base_url = os.environ.get("EMBED_CLIENT_BASE_URL")
60
- if not port:
61
- port = os.environ.get("EMBED_CLIENT_PORT")
143
+ """Parse command line arguments and environment variables for client configuration."""
144
+ parser = argparse.ArgumentParser(description="Embedding Service Async Client Example - All Security Modes")
145
+
146
+ # Basic connection parameters
147
+ parser.add_argument("--base-url", "-b", help="Base URL of the embedding service")
148
+ parser.add_argument("--port", "-p", type=int, help="Port of the embedding service")
149
+ parser.add_argument("--config", "-c", help="Path to configuration file")
150
+
151
+ # Client factory mode
152
+ parser.add_argument("--factory-mode", choices=["auto", "http", "http_token", "https", "https_token", "mtls", "mtls_roles"],
153
+ default="auto", help="Client factory mode (auto for automatic detection)")
154
+
155
+ # Authentication parameters
156
+ parser.add_argument("--auth-method", choices=["none", "api_key", "jwt", "basic", "certificate"],
157
+ default="none", help="Authentication method")
158
+ parser.add_argument("--api-key", help="API key for api_key authentication")
159
+ parser.add_argument("--jwt-secret", help="JWT secret for jwt authentication")
160
+ parser.add_argument("--jwt-username", help="JWT username for jwt authentication")
161
+ parser.add_argument("--jwt-password", help="JWT password for jwt authentication")
162
+ parser.add_argument("--username", help="Username for basic authentication")
163
+ parser.add_argument("--password", help="Password for basic authentication")
164
+ parser.add_argument("--cert-file", help="Certificate file for certificate authentication")
165
+ parser.add_argument("--key-file", help="Key file for certificate authentication")
166
+
167
+ # SSL/TLS parameters
168
+ parser.add_argument("--ssl-verify-mode", choices=["CERT_NONE", "CERT_OPTIONAL", "CERT_REQUIRED"],
169
+ default="CERT_REQUIRED", help="SSL certificate verification mode")
170
+ parser.add_argument("--ssl-check-hostname", action="store_true", default=True,
171
+ help="Enable SSL hostname checking")
172
+ parser.add_argument("--ssl-check-expiry", action="store_true", default=True,
173
+ help="Enable SSL certificate expiry checking")
174
+ parser.add_argument("--ca-cert-file", help="CA certificate file for SSL verification")
175
+
176
+ # Role-based access control (for mTLS + Roles)
177
+ parser.add_argument("--roles", help="Comma-separated list of roles for mTLS + Roles mode")
178
+ parser.add_argument("--role-attributes", help="JSON string of role attributes for mTLS + Roles mode")
179
+
180
+ # Additional parameters
181
+ parser.add_argument("--timeout", type=float, default=30.0, help="Request timeout in seconds")
182
+ parser.add_argument("--demo-mode", action="store_true", help="Run in demo mode (show all security modes)")
183
+
184
+ args = parser.parse_args()
185
+
186
+ # Store demo_mode in args for later use
187
+ args.demo_mode = args.demo_mode
188
+
189
+ # If demo mode is requested, return args directly
190
+ if args.demo_mode:
191
+ return args
192
+
193
+ # If config file is provided, load it
194
+ if args.config:
195
+ try:
196
+ config = ClientConfig()
197
+ config.load_from_file(args.config)
198
+ return config
199
+ except Exception as e:
200
+ print(f"Error loading config file {args.config}: {e}")
201
+ sys.exit(1)
202
+
203
+ # Otherwise, build config from arguments and environment variables
204
+ base_url = args.base_url or os.environ.get("EMBED_CLIENT_BASE_URL", "http://localhost")
205
+ port = args.port or int(os.environ.get("EMBED_CLIENT_PORT", "8001"))
206
+
62
207
  if not base_url or not port:
63
208
  print("Error: base_url and port must be provided via --base-url/--port arguments or EMBED_CLIENT_BASE_URL/EMBED_CLIENT_PORT environment variables.")
64
209
  sys.exit(1)
65
- return None, None
66
- return base_url, int(port)
210
+
211
+ # Build configuration dictionary
212
+ config_dict = {
213
+ "server": {
214
+ "host": base_url,
215
+ "port": port
216
+ },
217
+ "client": {
218
+ "timeout": args.timeout
219
+ },
220
+ "auth": {
221
+ "method": args.auth_method
222
+ }
223
+ }
224
+
225
+ # Add authentication configuration
226
+ if args.auth_method == "api_key":
227
+ api_key = args.api_key or os.environ.get("EMBED_CLIENT_API_KEY")
228
+ if api_key:
229
+ config_dict["auth"]["api_keys"] = {"user": api_key}
230
+ else:
231
+ print("Warning: API key not provided for api_key authentication")
232
+
233
+ elif args.auth_method == "jwt":
234
+ jwt_secret = args.jwt_secret or os.environ.get("EMBED_CLIENT_JWT_SECRET")
235
+ jwt_username = args.jwt_username or os.environ.get("EMBED_CLIENT_JWT_USERNAME")
236
+ jwt_password = args.jwt_password or os.environ.get("EMBED_CLIENT_JWT_PASSWORD")
237
+
238
+ if jwt_secret and jwt_username and jwt_password:
239
+ config_dict["auth"]["jwt"] = {
240
+ "secret": jwt_secret,
241
+ "username": jwt_username,
242
+ "password": jwt_password
243
+ }
244
+ else:
245
+ print("Warning: JWT credentials not fully provided")
246
+
247
+ elif args.auth_method == "basic":
248
+ username = args.username or os.environ.get("EMBED_CLIENT_USERNAME")
249
+ password = args.password or os.environ.get("EMBED_CLIENT_PASSWORD")
250
+
251
+ if username and password:
252
+ config_dict["auth"]["basic"] = {
253
+ "username": username,
254
+ "password": password
255
+ }
256
+ else:
257
+ print("Warning: Basic auth credentials not fully provided")
258
+
259
+ elif args.auth_method == "certificate":
260
+ cert_file = args.cert_file or os.environ.get("EMBED_CLIENT_CERT_FILE")
261
+ key_file = args.key_file or os.environ.get("EMBED_CLIENT_KEY_FILE")
262
+
263
+ if cert_file and key_file:
264
+ config_dict["auth"]["certificate"] = {
265
+ "cert_file": cert_file,
266
+ "key_file": key_file
267
+ }
268
+ else:
269
+ print("Warning: Certificate files not fully provided")
270
+
271
+ # Add SSL configuration if HTTPS is used or SSL parameters are provided
272
+ if base_url.startswith("https://") or args.ssl_verify_mode != "CERT_REQUIRED" or args.ca_cert_file:
273
+ # Force check_hostname=False for CERT_NONE mode
274
+ check_hostname = args.ssl_check_hostname
275
+ if args.ssl_verify_mode == "CERT_NONE":
276
+ check_hostname = False
277
+
278
+ config_dict["ssl"] = {
279
+ "enabled": True,
280
+ "verify_mode": args.ssl_verify_mode,
281
+ "check_hostname": check_hostname,
282
+ "check_expiry": args.ssl_check_expiry
283
+ }
284
+
285
+ if args.ca_cert_file:
286
+ config_dict["ssl"]["ca_cert_file"] = args.ca_cert_file
287
+
288
+ # Add client certificates for mTLS
289
+ if args.cert_file:
290
+ config_dict["ssl"]["cert_file"] = args.cert_file
291
+ if args.key_file:
292
+ config_dict["ssl"]["key_file"] = args.key_file
293
+
294
+ # Add role-based access control for mTLS + Roles
295
+ if args.roles:
296
+ roles = [role.strip() for role in args.roles.split(",")]
297
+ config_dict["roles"] = roles
298
+
299
+ if args.role_attributes:
300
+ try:
301
+ role_attributes = json.loads(args.role_attributes)
302
+ config_dict["role_attributes"] = role_attributes
303
+ except json.JSONDecodeError:
304
+ print("Warning: Invalid JSON in role_attributes")
305
+
306
+ return config_dict
67
307
 
68
- def extract_vectors(result):
308
+
309
+ def extract_embeddings(result):
69
310
  """Extract embeddings from the API response, supporting both old and new formats."""
70
311
  # Handle direct embeddings field (old format compatibility)
71
312
  if "embeddings" in result:
@@ -100,59 +341,306 @@ def extract_vectors(result):
100
341
 
101
342
  raise ValueError(f"Cannot extract embeddings from response: {result}")
102
343
 
344
+
345
+ async def run_client_examples(client):
346
+ """Run example operations with the client."""
347
+ # Check health
348
+ try:
349
+ health = await client.health()
350
+ print("Service health:", health)
351
+ except EmbeddingServiceError as e:
352
+ print(f"Error during health check: {e}")
353
+ return
354
+
355
+ # Get OpenAPI schema
356
+ try:
357
+ schema = await client.get_openapi_schema()
358
+ print(f"OpenAPI schema version: {schema.get('info', {}).get('version', 'unknown')}")
359
+ except EmbeddingServiceError as e:
360
+ print(f"Error getting OpenAPI schema: {e}")
361
+
362
+ # Get available commands
363
+ try:
364
+ commands = await client.get_commands()
365
+ print(f"Available commands: {commands}")
366
+ except EmbeddingServiceError as e:
367
+ print(f"Error getting commands: {e}")
368
+
369
+ # Test embedding generation
370
+ try:
371
+ texts = ["Hello, world!", "This is a test sentence.", "Embedding service is working!"]
372
+ result = await client.cmd("embed", {"texts": texts})
373
+
374
+ if result.get("success"):
375
+ embeddings = extract_embeddings(result)
376
+ print(f"Generated {len(embeddings)} embeddings")
377
+ print(f"First embedding dimension: {len(embeddings[0]) if embeddings else 0}")
378
+ else:
379
+ print(f"Embedding generation failed: {result.get('error', 'Unknown error')}")
380
+ except EmbeddingServiceError as e:
381
+ print(f"Error during embedding generation: {e}")
382
+
383
+
384
+ async def demonstrate_security_modes():
385
+ """Demonstrate all security modes using ClientFactory."""
386
+ print("=== Security Modes Demonstration ===")
387
+ print("This demonstration shows how to create clients for all 6 security modes.")
388
+ print("Note: These examples create client configurations but don't connect to actual servers.")
389
+
390
+ # 1. HTTP mode
391
+ print("\n1. HTTP Mode (no authentication, no SSL):")
392
+ print(" Use case: Development, internal networks, trusted environments")
393
+ try:
394
+ client = ClientFactory.create_http_client("http://localhost", 8001)
395
+ print(f" ✓ Created HTTP client: {client.base_url}:{client.port}")
396
+ print(f" ✓ SSL enabled: {client.is_ssl_enabled()}")
397
+ print(f" ✓ Authenticated: {client.is_authenticated()}")
398
+ print(f" ✓ Auth method: {client.get_auth_method()}")
399
+ await client.close()
400
+ except Exception as e:
401
+ print(f" ✗ Error: {e}")
402
+
403
+ # 2. HTTP + Token mode
404
+ print("\n2. HTTP + Token Mode (HTTP with API key):")
405
+ print(" Use case: API access control, simple authentication")
406
+ try:
407
+ client = ClientFactory.create_http_token_client(
408
+ "http://localhost", 8001, "api_key", api_key="demo_key"
409
+ )
410
+ print(f" ✓ Created HTTP + Token client: {client.base_url}:{client.port}")
411
+ print(f" ✓ SSL enabled: {client.is_ssl_enabled()}")
412
+ print(f" ✓ Authenticated: {client.is_authenticated()}")
413
+ print(f" ✓ Auth method: {client.get_auth_method()}")
414
+ if client.is_authenticated():
415
+ headers = client.get_auth_headers()
416
+ print(f" ✓ Auth headers: {headers}")
417
+ await client.close()
418
+ except Exception as e:
419
+ print(f" ✗ Error: {e}")
420
+
421
+ # 3. HTTPS mode
422
+ print("\n3. HTTPS Mode (HTTPS with server certificates):")
423
+ print(" Use case: Secure communication, public networks")
424
+ try:
425
+ client = ClientFactory.create_https_client("https://localhost", 9443)
426
+ print(f" ✓ Created HTTPS client: {client.base_url}:{client.port}")
427
+ print(f" ✓ SSL enabled: {client.is_ssl_enabled()}")
428
+ print(f" ✓ Authenticated: {client.is_authenticated()}")
429
+ if client.is_ssl_enabled():
430
+ ssl_config = client.get_ssl_config()
431
+ print(f" ✓ SSL config: {ssl_config}")
432
+ protocols = client.get_supported_ssl_protocols()
433
+ print(f" ✓ Supported SSL protocols: {protocols}")
434
+ await client.close()
435
+ except Exception as e:
436
+ print(f" ✗ Error: {e}")
437
+
438
+ # 4. HTTPS + Token mode
439
+ print("\n4. HTTPS + Token Mode (HTTPS with server certificates + authentication):")
440
+ print(" Use case: Secure API access, production environments")
441
+ try:
442
+ client = ClientFactory.create_https_token_client(
443
+ "https://localhost", 9443, "basic", username="admin", password="secret"
444
+ )
445
+ print(f" ✓ Created HTTPS + Token client: {client.base_url}:{client.port}")
446
+ print(f" ✓ SSL enabled: {client.is_ssl_enabled()}")
447
+ print(f" ✓ Authenticated: {client.is_authenticated()}")
448
+ print(f" ✓ Auth method: {client.get_auth_method()}")
449
+ if client.is_authenticated():
450
+ headers = client.get_auth_headers()
451
+ print(f" ✓ Auth headers: {headers}")
452
+ await client.close()
453
+ except Exception as e:
454
+ print(f" ✗ Error: {e}")
455
+
456
+ # 5. mTLS mode
457
+ print("\n5. mTLS Mode (mutual TLS with client and server certificates):")
458
+ print(" Use case: High security, client certificate authentication")
459
+ try:
460
+ client = ClientFactory.create_mtls_client(
461
+ "https://localhost", "client_cert.pem", "client_key.pem", 9443
462
+ )
463
+ print(f" ✓ Created mTLS client: {client.base_url}:{client.port}")
464
+ print(f" ✓ SSL enabled: {client.is_ssl_enabled()}")
465
+ print(f" ✓ mTLS enabled: {client.is_mtls_enabled()}")
466
+ print(f" ✓ Authenticated: {client.is_authenticated()}")
467
+ if client.is_ssl_enabled():
468
+ ssl_config = client.get_ssl_config()
469
+ print(f" ✓ SSL config: {ssl_config}")
470
+ await client.close()
471
+ except Exception as e:
472
+ print(f" ✗ Error: {e}")
473
+
474
+ # 6. mTLS + Roles mode
475
+ print("\n6. mTLS + Roles Mode (mTLS with role-based access control):")
476
+ print(" Use case: Enterprise security, role-based permissions")
477
+ try:
478
+ client = ClientFactory.create_mtls_roles_client(
479
+ "https://localhost", "client_cert.pem", "client_key.pem", 9443,
480
+ roles=["admin", "user"], role_attributes={"department": "IT"}
481
+ )
482
+ print(f" ✓ Created mTLS + Roles client: {client.base_url}:{client.port}")
483
+ print(f" ✓ SSL enabled: {client.is_ssl_enabled()}")
484
+ print(f" ✓ mTLS enabled: {client.is_mtls_enabled()}")
485
+ print(f" ✓ Authenticated: {client.is_authenticated()}")
486
+ if client.is_authenticated():
487
+ headers = client.get_auth_headers()
488
+ print(f" ✓ Auth headers: {headers}")
489
+ await client.close()
490
+ except Exception as e:
491
+ print(f" ✗ Error: {e}")
492
+
493
+ print("\n=== Security Mode Summary ===")
494
+ print("1. HTTP: Basic connectivity, no security")
495
+ print("2. HTTP + Token: API key authentication over HTTP")
496
+ print("3. HTTPS: Encrypted communication with server certificates")
497
+ print("4. HTTPS + Token: Encrypted communication + authentication")
498
+ print("5. mTLS: Mutual certificate authentication")
499
+ print("6. mTLS + Roles: Mutual certificates + role-based access control")
500
+
501
+
502
+ async def demonstrate_automatic_detection():
503
+ """Demonstrate automatic security mode detection."""
504
+ print("\n=== Automatic Security Mode Detection ===")
505
+ print("This shows how the client automatically detects the appropriate security mode.")
506
+
507
+ test_cases = [
508
+ ("http://localhost", None, None, None, None, "HTTP"),
509
+ ("http://localhost", "api_key", None, None, None, "HTTP + Token"),
510
+ ("https://localhost", None, None, None, None, "HTTPS"),
511
+ ("https://localhost", "api_key", None, None, None, "HTTPS + Token"),
512
+ ("https://localhost", None, None, "cert.pem", "key.pem", "mTLS"),
513
+ ("https://localhost", None, None, "cert.pem", "key.pem", "mTLS + Roles", {"roles": ["admin"]}),
514
+ ]
515
+
516
+ for case in test_cases:
517
+ if len(case) == 6:
518
+ base_url, auth_method, ssl_enabled, cert_file, key_file, expected = case
519
+ kwargs = {}
520
+ else:
521
+ base_url, auth_method, ssl_enabled, cert_file, key_file, expected, kwargs = case
522
+
523
+ try:
524
+ mode = detect_security_mode(base_url, auth_method, ssl_enabled, cert_file, key_file, **kwargs)
525
+ print(f" ✓ {base_url} + {auth_method or 'none'} + {cert_file or 'no cert'} -> {mode} ({expected})")
526
+ except Exception as e:
527
+ print(f" ✗ Error detecting mode for {base_url}: {e}")
528
+
529
+
530
+ async def demonstrate_with_auth_method():
531
+ """Demonstrate the with_auth method for dynamic authentication."""
532
+ print("\n=== Dynamic Authentication with with_auth() Method ===")
533
+ print("This shows how to create clients with different authentication methods using the with_auth class method.")
534
+
535
+ # Demonstrate different authentication methods
536
+ auth_examples = [
537
+ ("api_key", {"api_key": "dynamic_api_key"}, "API Key Authentication"),
538
+ ("jwt", {"secret": "secret", "username": "user", "password": "pass"}, "JWT Authentication"),
539
+ ("basic", {"username": "admin", "password": "secret"}, "Basic Authentication"),
540
+ ("certificate", {"cert_file": "client.crt", "key_file": "client.key"}, "Certificate Authentication"),
541
+ ]
542
+
543
+ for auth_method, kwargs, description in auth_examples:
544
+ try:
545
+ print(f"\n{description}:")
546
+ auth_client = EmbeddingServiceAsyncClient.with_auth("http://localhost", 8001, auth_method, **kwargs)
547
+ print(f" ✓ Auth method: {auth_client.get_auth_method()}")
548
+ print(f" ✓ Authenticated: {auth_client.is_authenticated()}")
549
+ if auth_client.is_authenticated():
550
+ headers = auth_client.get_auth_headers()
551
+ print(f" ✓ Auth headers: {headers}")
552
+ await auth_client.close()
553
+ except Exception as e:
554
+ print(f" ✗ Error with {auth_method}: {e}")
555
+
556
+ print("\n✓ Dynamic authentication demonstration completed.")
557
+
558
+
103
559
  async def main():
104
560
  try:
105
- base_url, port = get_params()
561
+ config = get_params()
562
+
563
+ # Check if demo mode is requested
564
+ if hasattr(config, 'demo_mode') and config.demo_mode:
565
+ await demonstrate_security_modes()
566
+ await demonstrate_automatic_detection()
567
+ await demonstrate_with_auth_method()
568
+ return
569
+
570
+ # Create client based on factory mode
571
+ if isinstance(config, ClientConfig):
572
+ # Using configuration object
573
+ client = EmbeddingServiceAsyncClient.from_config(config)
574
+ else:
575
+ # Using configuration dictionary
576
+ factory_mode = getattr(config, 'factory_mode', 'auto')
577
+
578
+ if factory_mode == "auto":
579
+ # Automatic detection
580
+ client = create_client(
581
+ config["server"]["host"],
582
+ config["server"]["port"],
583
+ auth_method=config["auth"]["method"],
584
+ **{k: v for k, v in config.items() if k not in ["server", "auth", "ssl", "client"]}
585
+ )
586
+ else:
587
+ # Specific factory method
588
+ base_url = config["server"]["host"]
589
+ port = config["server"]["port"]
590
+ auth_method = config["auth"]["method"]
591
+
592
+ if factory_mode == "http":
593
+ client = ClientFactory.create_http_client(base_url, port)
594
+ elif factory_mode == "http_token":
595
+ client = ClientFactory.create_http_token_client(base_url, port, auth_method, **config.get("auth", {}))
596
+ elif factory_mode == "https":
597
+ client = ClientFactory.create_https_client(base_url, port)
598
+ elif factory_mode == "https_token":
599
+ client = ClientFactory.create_https_token_client(base_url, port, auth_method, **config.get("auth", {}))
600
+ elif factory_mode == "mtls":
601
+ cert_file = config.get("ssl", {}).get("cert_file", "client_cert.pem")
602
+ key_file = config.get("ssl", {}).get("key_file", "client_key.pem")
603
+ client = ClientFactory.create_mtls_client(base_url, cert_file, key_file, port)
604
+ elif factory_mode == "mtls_roles":
605
+ cert_file = config.get("ssl", {}).get("cert_file", "client_cert.pem")
606
+ key_file = config.get("ssl", {}).get("key_file", "client_key.pem")
607
+ roles = config.get("roles", ["admin"])
608
+ role_attributes = config.get("role_attributes", {})
609
+ client = ClientFactory.create_mtls_roles_client(
610
+ base_url, cert_file, key_file, port, roles, role_attributes
611
+ )
612
+ else:
613
+ client = EmbeddingServiceAsyncClient(config_dict=config)
614
+
615
+ print(f"Client configuration:")
616
+ print(f" Base URL: {client.base_url}")
617
+ print(f" Port: {client.port}")
618
+ print(f" Authentication: {client.get_auth_method()}")
619
+ print(f" Authenticated: {client.is_authenticated()}")
620
+ if client.is_authenticated():
621
+ headers = client.get_auth_headers()
622
+ print(f" Auth headers: {headers}")
623
+ print(f" SSL enabled: {client.is_ssl_enabled()}")
624
+ print(f" mTLS enabled: {client.is_mtls_enabled()}")
625
+ if client.is_ssl_enabled():
626
+ ssl_config = client.get_ssl_config()
627
+ print(f" SSL config: {ssl_config}")
628
+ protocols = client.get_supported_ssl_protocols()
629
+ print(f" Supported SSL protocols: {protocols}")
630
+ print()
631
+
106
632
  # Explicit open/close example
107
- client = EmbeddingServiceAsyncClient(base_url=base_url, port=port)
108
633
  print("Explicit session open/close example:")
109
634
  await client.close()
110
635
  print("Session closed explicitly (manual close example).\n")
111
- async with EmbeddingServiceAsyncClient(base_url=base_url, port=port) as client:
112
- # Check health
113
- try:
114
- health = await client.health()
115
- print("Service health:", health)
116
- except EmbeddingServiceConnectionError as e:
117
- print(f"Connection error during health check: {e}")
118
- return
119
- except EmbeddingServiceTimeoutError as e:
120
- print(f"Timeout error during health check: {e}")
121
- except EmbeddingServiceError as e:
122
- print(f"Error during health check: {e}")
123
-
124
- # Request embeddings for a list of texts
125
- texts = ["hello world", "test embedding"]
126
- try:
127
- result = await client.cmd("embed", params={"texts": texts})
128
- vectors = extract_vectors(result)
129
- print(f"Embeddings for {len(texts)} texts:")
130
- for i, vec in enumerate(vectors):
131
- print(f" Text: {texts[i]!r}\n Vector: {vec[:5]}... (total {len(vec)} dims)")
132
- except EmbeddingServiceAPIError as e:
133
- print(f"API error during embedding: {e}")
134
- except EmbeddingServiceConnectionError as e:
135
- print(f"Connection error during embedding: {e}")
136
- except EmbeddingServiceTimeoutError as e:
137
- print(f"Timeout error during embedding: {e}")
138
- except EmbeddingServiceError as e:
139
- print(f"Error during embedding: {e}")
140
-
141
- # Example: health check via cmd
142
- try:
143
- result = await client.cmd("health")
144
- print("Health check result:", result)
145
- except EmbeddingServiceError as e:
146
- print(f"Error during health command: {e}")
147
-
148
- # Example: error handling for empty command
149
- try:
150
- result = await client.cmd("")
151
- print("Empty command result:", result)
152
- except EmbeddingServiceAPIError as e:
153
- print(f"Expected error for empty command: {e}")
154
- except EmbeddingServiceError as e:
155
- print(f"Error for empty command: {e}")
636
+
637
+ # Use context manager
638
+ if isinstance(config, ClientConfig):
639
+ async with EmbeddingServiceAsyncClient.from_config(config) as client:
640
+ await run_client_examples(client)
641
+ else:
642
+ async with EmbeddingServiceAsyncClient(config_dict=config) as client:
643
+ await run_client_examples(client)
156
644
 
157
645
  except EmbeddingServiceConfigError as e:
158
646
  print(f"Configuration error: {e}")
@@ -162,4 +650,4 @@ async def main():
162
650
  sys.exit(1)
163
651
 
164
652
  if __name__ == "__main__":
165
- asyncio.run(main())
653
+ asyncio.run(main())