embed-client-dev 4.0.78__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.
Files changed (48) hide show
  1. embed_client/__init__.py +36 -0
  2. embed_client/__main__.py +15 -0
  3. embed_client/_version.py +3 -0
  4. embed_client/adapter_config_factory.py +213 -0
  5. embed_client/adapter_config_normalizer.py +135 -0
  6. embed_client/adapter_generator_bridge.py +166 -0
  7. embed_client/auth.py +189 -0
  8. embed_client/cli.py +483 -0
  9. embed_client/cli_core.py +427 -0
  10. embed_client/client_factory.py +515 -0
  11. embed_client/client_factory_examples.py +375 -0
  12. embed_client/client_factory_helpers.py +59 -0
  13. embed_client/config.py +408 -0
  14. embed_client/config_defaults.py +77 -0
  15. embed_client/config_examples.py +228 -0
  16. embed_client/config_generator.py +652 -0
  17. embed_client/config_generator_cli.py +151 -0
  18. embed_client/config_presets.py +245 -0
  19. embed_client/config_validator.py +246 -0
  20. embed_client/config_validator_cli.py +141 -0
  21. embed_client/constants.py +52 -0
  22. embed_client/demo_cli.py +78 -0
  23. embed_client/demo_security_cli.py +206 -0
  24. embed_client/embed_file_output_validate.py +348 -0
  25. embed_client/embed_file_status_store.py +80 -0
  26. embed_client/embedding_client.py +1929 -0
  27. embed_client/embedding_client_helpers.py +105 -0
  28. embed_client/example_async_usage.py +357 -0
  29. embed_client/example_async_usage_demo.py +282 -0
  30. embed_client/example_async_usage_ru.py +340 -0
  31. embed_client/example_async_usage_ru_demo.py +313 -0
  32. embed_client/exceptions.py +122 -0
  33. embed_client/identifier_types.py +56 -0
  34. embed_client/json_embed_file_preflight.py +128 -0
  35. embed_client/local_json_embed_file.py +73 -0
  36. embed_client/md_embed_file_prep.py +197 -0
  37. embed_client/response_normalizer.py +160 -0
  38. embed_client/response_parsers.py +219 -0
  39. embed_client/security_cli.py +200 -0
  40. embed_client/security_cli_core.py +227 -0
  41. embed_client/ssl_examples.py +278 -0
  42. embed_client/ssl_manager.py +141 -0
  43. embed_client_dev-4.0.78.dist-info/METADATA +155 -0
  44. embed_client_dev-4.0.78.dist-info/RECORD +48 -0
  45. embed_client_dev-4.0.78.dist-info/WHEEL +5 -0
  46. embed_client_dev-4.0.78.dist-info/entry_points.txt +4 -0
  47. embed_client_dev-4.0.78.dist-info/licenses/LICENSE +21 -0
  48. embed_client_dev-4.0.78.dist-info/top_level.txt +1 -0
@@ -0,0 +1,515 @@
1
+ """
2
+ Client Factory for Embedding Service
3
+
4
+ Author: Vasiliy Zdanovskiy
5
+ email: vasilyvz@gmail.com
6
+
7
+ """
8
+
9
+ import os
10
+ from typing import Optional, Dict, Any
11
+ from urllib.parse import urlparse
12
+
13
+ from embed_client.embedding_client import (
14
+ EmbeddingClient,
15
+ embedding_client_from_config_dict,
16
+ )
17
+ from embed_client.config import ClientConfig
18
+
19
+
20
+ class SecurityMode:
21
+ """Security mode constants."""
22
+
23
+ HTTP = "http"
24
+ HTTP_TOKEN = "http_token"
25
+ HTTPS = "https"
26
+ HTTPS_TOKEN = "https_token"
27
+ MTLS = "mtls"
28
+ MTLS_ROLES = "mtls_roles"
29
+
30
+
31
+ class ClientFactory:
32
+ """
33
+ Factory for creating EmbeddingClient instances with automatic
34
+ security mode detection and configuration.
35
+
36
+ Supports all 6 security modes:
37
+ 1. HTTP - plain HTTP without authentication
38
+ 2. HTTP + Token - HTTP with API Key, JWT, or Basic authentication
39
+ 3. HTTPS - HTTPS with server certificate verification
40
+ 4. HTTPS + Token - HTTPS with server certificates + authentication
41
+ 5. mTLS - mutual TLS with client and server certificates
42
+ 6. mTLS + Roles - mTLS with role-based access control
43
+
44
+ All clients created by this factory use adapter-only transport (AdapterTransport).
45
+ Authentication, TLS/mTLS operations, and protocol selection are handled exclusively
46
+ by the mcp-proxy-adapter client facilities. This factory only creates configuration
47
+ dictionaries that are passed to EmbeddingClient via
48
+ ``embedding_client_from_config_dict`` (AdapterConfigFactory → JsonRpcClient).
49
+ """
50
+
51
+ @staticmethod
52
+ def detect_security_mode(
53
+ base_url: str,
54
+ auth_method: Optional[str] = None,
55
+ ssl_enabled: Optional[bool] = None,
56
+ cert_file: Optional[str] = None,
57
+ key_file: Optional[str] = None,
58
+ **kwargs,
59
+ ) -> str:
60
+ """
61
+ Automatically detect security mode based on provided parameters.
62
+
63
+ Args:
64
+ base_url: Server base URL
65
+ auth_method: Authentication method (none, api_key, jwt, basic, certificate)
66
+ ssl_enabled: Whether SSL is enabled
67
+ cert_file: Client certificate file path
68
+ key_file: Client private key file path
69
+ **kwargs: Additional parameters
70
+
71
+ Returns:
72
+ Detected security mode string
73
+
74
+ Note:
75
+ This method only determines the security mode string based on parameters.
76
+ It does NOT execute authentication or TLS operations. All actual auth/TLS
77
+ execution is handled by the adapter transport layer.
78
+ """
79
+ # Parse URL to determine protocol
80
+ parsed_url = urlparse(base_url)
81
+ is_https = parsed_url.scheme.lower() == "https"
82
+
83
+ # Determine SSL status
84
+ if ssl_enabled is None:
85
+ ssl_enabled = is_https
86
+ else:
87
+ ssl_enabled = bool(ssl_enabled)
88
+
89
+ # Check for mTLS (client certificates)
90
+ has_client_cert = bool(cert_file and key_file)
91
+
92
+ # Check for authentication
93
+ has_auth = auth_method and auth_method != "none"
94
+
95
+ # Determine security mode
96
+ if ssl_enabled and has_client_cert:
97
+ # Check for role-based access (additional certificate attributes)
98
+ if kwargs.get("roles") or kwargs.get("role_attributes"):
99
+ return SecurityMode.MTLS_ROLES
100
+ else:
101
+ return SecurityMode.MTLS
102
+ elif ssl_enabled and has_auth:
103
+ return SecurityMode.HTTPS_TOKEN
104
+ elif ssl_enabled:
105
+ return SecurityMode.HTTPS
106
+ elif has_auth:
107
+ return SecurityMode.HTTP_TOKEN
108
+ else:
109
+ return SecurityMode.HTTP
110
+
111
+ @staticmethod
112
+ def create_client(
113
+ base_url: str,
114
+ port: int = 8001,
115
+ auth_method: Optional[str] = None,
116
+ ssl_enabled: Optional[bool] = None,
117
+ **kwargs,
118
+ ) -> EmbeddingClient:
119
+ """
120
+ Create a client with automatic security mode detection.
121
+
122
+ Args:
123
+ base_url: Server base URL
124
+ port: Server port
125
+ auth_method: Authentication method
126
+ ssl_enabled: Whether SSL is enabled
127
+ **kwargs: Additional configuration parameters
128
+
129
+ Returns:
130
+ Configured EmbeddingClient instance. The client uses
131
+ JsonRpcClient for all network operations. This factory method only
132
+ creates configuration dictionaries - no authentication or TLS operations
133
+ are executed here.
134
+ """
135
+ # Detect security mode
136
+ security_mode = ClientFactory.detect_security_mode(
137
+ base_url, auth_method, ssl_enabled, **kwargs
138
+ )
139
+
140
+ # Create configuration based on detected mode
141
+ config_dict = ClientFactory._create_config_for_mode(
142
+ security_mode, base_url, port, auth_method, ssl_enabled, **kwargs
143
+ )
144
+
145
+ # Create and return client
146
+ return embedding_client_from_config_dict(config_dict)
147
+
148
+ @staticmethod
149
+ def _create_config_for_mode(
150
+ security_mode: str,
151
+ base_url: str,
152
+ port: int,
153
+ auth_method: Optional[str],
154
+ ssl_enabled: Optional[bool],
155
+ **kwargs,
156
+ ) -> Dict[str, Any]:
157
+ """
158
+ Create configuration dictionary for specific security mode.
159
+
160
+ Args:
161
+ security_mode: Detected security mode
162
+ base_url: Server base URL
163
+ port: Server port
164
+ auth_method: Authentication method
165
+ ssl_enabled: Whether SSL is enabled
166
+ **kwargs: Additional parameters
167
+
168
+ Returns:
169
+ Configuration dictionary
170
+
171
+ Note:
172
+ This method only creates a configuration dictionary structure. It does
173
+ NOT execute authentication or TLS operations. All actual auth/TLS execution
174
+ is handled by AdapterTransport after the config is converted to adapter
175
+ parameters via AdapterConfigFactory.
176
+ """
177
+ config_dict = {
178
+ "server": {"host": base_url, "port": port},
179
+ "client": {"timeout": kwargs.get("timeout", 30.0)},
180
+ }
181
+
182
+ # Configure authentication
183
+ if security_mode in [SecurityMode.HTTP_TOKEN, SecurityMode.HTTPS_TOKEN]:
184
+ config_dict["auth"] = ClientFactory._create_auth_config(
185
+ auth_method, **kwargs
186
+ )
187
+ elif (
188
+ security_mode in [SecurityMode.MTLS, SecurityMode.MTLS_ROLES]
189
+ and auth_method
190
+ and auth_method != "none"
191
+ ):
192
+ config_dict["auth"] = ClientFactory._create_auth_config(
193
+ auth_method, **kwargs
194
+ )
195
+
196
+ # Configure SSL/TLS
197
+ if security_mode in [
198
+ SecurityMode.HTTPS,
199
+ SecurityMode.HTTPS_TOKEN,
200
+ SecurityMode.MTLS,
201
+ SecurityMode.MTLS_ROLES,
202
+ ]:
203
+ config_dict["ssl"] = ClientFactory._create_ssl_config(
204
+ security_mode, ssl_enabled, **kwargs
205
+ )
206
+
207
+ return config_dict
208
+
209
+ @staticmethod
210
+ def _create_auth_config(auth_method: Optional[str], **kwargs) -> Dict[str, Any]:
211
+ """
212
+ Create authentication configuration dictionary.
213
+
214
+ This method only creates a configuration dictionary structure for authentication.
215
+ It does NOT execute authentication operations. All actual authentication is
216
+ handled by AdapterTransport after the config is converted to adapter parameters
217
+ via AdapterConfigFactory.
218
+
219
+ Args:
220
+ auth_method: Authentication method (none, api_key, jwt, basic, certificate)
221
+ **kwargs: Additional authentication parameters
222
+
223
+ Returns:
224
+ Authentication configuration dictionary
225
+ """
226
+ if not auth_method or auth_method == "none":
227
+ return {"method": "none"}
228
+
229
+ auth_config: Dict[str, Any] = {"method": auth_method}
230
+
231
+ if auth_method == "api_key":
232
+ api_key = kwargs.get("api_key") or os.environ.get("EMBED_CLIENT_API_KEY")
233
+ if api_key:
234
+ auth_config["api_keys"] = {"user": api_key}
235
+ if kwargs.get("api_key_header"):
236
+ auth_config["api_key_header"] = kwargs["api_key_header"]
237
+
238
+ elif auth_method == "jwt":
239
+ jwt_secret = kwargs.get("jwt_secret") or os.environ.get(
240
+ "EMBED_CLIENT_JWT_SECRET"
241
+ )
242
+ jwt_username = kwargs.get("jwt_username") or os.environ.get(
243
+ "EMBED_CLIENT_JWT_USERNAME"
244
+ )
245
+ jwt_password = kwargs.get("jwt_password") or os.environ.get(
246
+ "EMBED_CLIENT_JWT_PASSWORD"
247
+ )
248
+
249
+ if jwt_secret and jwt_username and jwt_password:
250
+ auth_config["jwt"] = {
251
+ "secret": jwt_secret,
252
+ "username": jwt_username,
253
+ "password": jwt_password,
254
+ }
255
+ if kwargs.get("jwt_expiry"):
256
+ auth_config["jwt"]["expiry"] = kwargs["jwt_expiry"]
257
+
258
+ elif auth_method == "basic":
259
+ username = kwargs.get("username") or os.environ.get("EMBED_CLIENT_USERNAME")
260
+ password = kwargs.get("password") or os.environ.get("EMBED_CLIENT_PASSWORD")
261
+
262
+ if username and password:
263
+ auth_config["basic"] = {"username": username, "password": password}
264
+
265
+ elif auth_method == "certificate":
266
+ cert_file = kwargs.get("cert_file") or os.environ.get(
267
+ "EMBED_CLIENT_CERT_FILE"
268
+ )
269
+ key_file = kwargs.get("key_file") or os.environ.get("EMBED_CLIENT_KEY_FILE")
270
+
271
+ if cert_file and key_file:
272
+ auth_config["certificate"] = {
273
+ "cert_file": cert_file,
274
+ "key_file": key_file,
275
+ }
276
+
277
+ return auth_config
278
+
279
+ @staticmethod
280
+ def _create_ssl_config(
281
+ security_mode: str, ssl_enabled: Optional[bool], **kwargs
282
+ ) -> Dict[str, Any]:
283
+ """
284
+ Create SSL/TLS configuration dictionary.
285
+
286
+ This method only creates a configuration dictionary structure for SSL/TLS.
287
+ It does NOT execute TLS operations or certificate validation. All actual TLS
288
+ operations are handled by AdapterTransport after the config is converted to
289
+ adapter parameters via AdapterConfigFactory.
290
+
291
+ Args:
292
+ security_mode: Security mode string
293
+ ssl_enabled: Whether SSL is enabled
294
+ **kwargs: Additional SSL/TLS parameters
295
+
296
+ Returns:
297
+ SSL/TLS configuration dictionary
298
+ """
299
+ enabled = bool(ssl_enabled)
300
+ ssl_config: Dict[str, Any] = {
301
+ "enabled": enabled,
302
+ "verify_mode": kwargs.get("verify_mode", "CERT_REQUIRED"),
303
+ "check_hostname": kwargs.get("check_hostname", True),
304
+ "check_expiry": kwargs.get("check_expiry", True),
305
+ }
306
+
307
+ # Add CA certificate if provided
308
+ ca_cert_file = kwargs.get("ca_cert_file") or os.environ.get(
309
+ "EMBED_CLIENT_CA_CERT_FILE"
310
+ )
311
+ if ca_cert_file:
312
+ ssl_config["ca_cert_file"] = ca_cert_file
313
+
314
+ # Add client certificates for mTLS
315
+ if security_mode in [SecurityMode.MTLS, SecurityMode.MTLS_ROLES]:
316
+ cert_file = kwargs.get("cert_file") or os.environ.get(
317
+ "EMBED_CLIENT_CERT_FILE"
318
+ )
319
+ key_file = kwargs.get("key_file") or os.environ.get("EMBED_CLIENT_KEY_FILE")
320
+
321
+ if cert_file:
322
+ ssl_config["cert_file"] = cert_file
323
+ if key_file:
324
+ ssl_config["key_file"] = key_file
325
+
326
+ return ssl_config
327
+
328
+ @staticmethod
329
+ def create_http_client(
330
+ base_url: str, port: int = 8001, **kwargs
331
+ ) -> EmbeddingClient:
332
+ """
333
+ Create HTTP client (no authentication, no SSL).
334
+
335
+ Note:
336
+ The created client uses AdapterTransport for all network operations.
337
+ All authentication and TLS operations are handled by the adapter.
338
+ """
339
+ return ClientFactory.create_client(
340
+ base_url, port, auth_method="none", ssl_enabled=False, **kwargs
341
+ )
342
+
343
+ @staticmethod
344
+ def create_http_token_client(
345
+ base_url: str, port: int = 8001, auth_method: str = "api_key", **kwargs
346
+ ) -> EmbeddingClient:
347
+ """
348
+ Create HTTP client with authentication.
349
+
350
+ Note:
351
+ The created client uses AdapterTransport for all network operations.
352
+ All authentication and TLS operations are handled by the adapter.
353
+ """
354
+ return ClientFactory.create_client(
355
+ base_url, port, auth_method=auth_method, ssl_enabled=False, **kwargs
356
+ )
357
+
358
+ @staticmethod
359
+ def create_https_client(
360
+ base_url: str, port: int = 8001, **kwargs
361
+ ) -> EmbeddingClient:
362
+ """
363
+ Create HTTPS client (no authentication, with SSL).
364
+
365
+ Note:
366
+ The created client uses AdapterTransport for all network operations.
367
+ All authentication and TLS operations are handled by the adapter.
368
+ """
369
+ return ClientFactory.create_client(
370
+ base_url, port, auth_method="none", ssl_enabled=True, **kwargs
371
+ )
372
+
373
+ @staticmethod
374
+ def create_https_token_client(
375
+ base_url: str, port: int = 8001, auth_method: str = "api_key", **kwargs
376
+ ) -> EmbeddingClient:
377
+ """
378
+ Create HTTPS client with authentication.
379
+
380
+ Note:
381
+ The created client uses AdapterTransport for all network operations.
382
+ All authentication and TLS operations are handled by the adapter.
383
+ """
384
+ return ClientFactory.create_client(
385
+ base_url, port, auth_method=auth_method, ssl_enabled=True, **kwargs
386
+ )
387
+
388
+ @staticmethod
389
+ def create_mtls_client(
390
+ base_url: str,
391
+ cert_file: str,
392
+ key_file: str,
393
+ port: int = 8001,
394
+ auth_method: Optional[str] = None,
395
+ **kwargs,
396
+ ) -> EmbeddingClient:
397
+ """
398
+ Create mTLS client with client certificates.
399
+
400
+ Note:
401
+ The created client uses AdapterTransport for all network operations.
402
+ All authentication and TLS operations are handled by the adapter.
403
+ """
404
+ return ClientFactory.create_client(
405
+ base_url,
406
+ port,
407
+ auth_method=auth_method,
408
+ ssl_enabled=True,
409
+ cert_file=cert_file,
410
+ key_file=key_file,
411
+ **kwargs,
412
+ )
413
+
414
+ @staticmethod
415
+ def create_mtls_roles_client(
416
+ base_url: str,
417
+ cert_file: str,
418
+ key_file: str,
419
+ port: int = 8001,
420
+ roles: Optional[list] = None,
421
+ role_attributes: Optional[dict] = None,
422
+ auth_method: Optional[str] = None,
423
+ **kwargs,
424
+ ) -> EmbeddingClient:
425
+ """
426
+ Create mTLS client with role-based access control.
427
+
428
+ Note:
429
+ The created client uses AdapterTransport for all network operations.
430
+ All authentication and TLS operations are handled by the adapter.
431
+ """
432
+ return ClientFactory.create_client(
433
+ base_url,
434
+ port,
435
+ auth_method=auth_method,
436
+ ssl_enabled=True,
437
+ cert_file=cert_file,
438
+ key_file=key_file,
439
+ roles=roles,
440
+ role_attributes=role_attributes,
441
+ **kwargs,
442
+ )
443
+
444
+ @staticmethod
445
+ def from_config_file(config_path: str) -> EmbeddingClient:
446
+ """
447
+ Create client from configuration file.
448
+
449
+ Note:
450
+ The created client uses AdapterTransport for all network operations.
451
+ All authentication and TLS operations are handled by the adapter.
452
+ """
453
+ config = ClientConfig()
454
+ config.load_config_file(config_path)
455
+ return EmbeddingClient.from_config(config)
456
+
457
+ @staticmethod
458
+ def from_environment() -> EmbeddingClient:
459
+ """
460
+ Create client from environment variables.
461
+
462
+ Note:
463
+ The created client uses AdapterTransport for all network operations.
464
+ All authentication and TLS operations are handled by the adapter.
465
+ """
466
+ base_url = os.environ.get("EMBED_CLIENT_BASE_URL", "http://localhost")
467
+ port = int(os.environ.get("EMBED_CLIENT_PORT", "8001"))
468
+ auth_method = os.environ.get("EMBED_CLIENT_AUTH_METHOD", "none")
469
+
470
+ # Check if SSL should be enabled
471
+ ssl_enabled = os.environ.get("EMBED_CLIENT_SSL_ENABLED", "").lower() in [
472
+ "true",
473
+ "1",
474
+ "yes",
475
+ ]
476
+ if not ssl_enabled and base_url.startswith("https://"):
477
+ ssl_enabled = True
478
+
479
+ # Paths for mTLS / server trust (same names as CONFIGURATION.md); required
480
+ # when SSL is on so EmbeddingClient/SSLConfig match e.g. mTLS localhost.
481
+ env_ssl_kwargs: Dict[str, Any] = {}
482
+ cert_file = os.environ.get("EMBED_CLIENT_CERT_FILE", "").strip()
483
+ key_file = os.environ.get("EMBED_CLIENT_KEY_FILE", "").strip()
484
+ ca_cert_file = os.environ.get("EMBED_CLIENT_CA_CERT_FILE", "").strip()
485
+ if cert_file:
486
+ env_ssl_kwargs["cert_file"] = cert_file
487
+ if key_file:
488
+ env_ssl_kwargs["key_file"] = key_file
489
+ if ca_cert_file:
490
+ env_ssl_kwargs["ca_cert_file"] = ca_cert_file
491
+
492
+ return ClientFactory.create_client(
493
+ base_url,
494
+ port,
495
+ auth_method=auth_method,
496
+ ssl_enabled=ssl_enabled,
497
+ **env_ssl_kwargs,
498
+ )
499
+
500
+
501
+ from embed_client.client_factory_helpers import ( # noqa: E402 # lazy import to avoid circular dependency
502
+ create_client,
503
+ create_client_from_config,
504
+ create_client_from_env,
505
+ detect_security_mode,
506
+ )
507
+
508
+ __all__ = [
509
+ "ClientFactory",
510
+ "SecurityMode",
511
+ "create_client",
512
+ "create_client_from_config",
513
+ "create_client_from_env",
514
+ "detect_security_mode",
515
+ ]