cluebase-backend-sdk 0.0.1__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 (100) hide show
  1. cluebase_backend_sdk/__init__.py +484 -0
  2. cluebase_backend_sdk/_cluebase_authored_attributes.py +37 -0
  3. cluebase_backend_sdk/_generated/__init__.py +11 -0
  4. cluebase_backend_sdk/_generated/correlation_attribute_keys.py +37 -0
  5. cluebase_backend_sdk/_generated/identity_pii_policy.py +62 -0
  6. cluebase_backend_sdk/_generated/ingest_privacy_tables.py +727 -0
  7. cluebase_backend_sdk/_generated/otel_resource_keys.py +62 -0
  8. cluebase_backend_sdk/_generated/sdk_hint_vocabulary.py +54 -0
  9. cluebase_backend_sdk/_generated/usage_governor_tables.py +52 -0
  10. cluebase_backend_sdk/_generated/wire_credential_policy.py +24 -0
  11. cluebase_backend_sdk/_init_options.py +156 -0
  12. cluebase_backend_sdk/_integrations/__init__.py +10 -0
  13. cluebase_backend_sdk/_integrations/ai/__init__.py +10 -0
  14. cluebase_backend_sdk/_integrations/ai/_response_parsing.py +144 -0
  15. cluebase_backend_sdk/_integrations/ai/_span_attributes.py +249 -0
  16. cluebase_backend_sdk/_integrations/ai/_version.py +5 -0
  17. cluebase_backend_sdk/_integrations/ai/instrumentation/__init__.py +1 -0
  18. cluebase_backend_sdk/_integrations/ai/instrumentation/anthropic.py +526 -0
  19. cluebase_backend_sdk/_integrations/ai/instrumentation/base.py +543 -0
  20. cluebase_backend_sdk/_integrations/ai/instrumentation/openai.py +250 -0
  21. cluebase_backend_sdk/_integrations/ai/observer.py +774 -0
  22. cluebase_backend_sdk/_integrations/crewai/__init__.py +29 -0
  23. cluebase_backend_sdk/_integrations/crewai/observer.py +393 -0
  24. cluebase_backend_sdk/_integrations/django/__init__.py +43 -0
  25. cluebase_backend_sdk/_integrations/django/django.py +85 -0
  26. cluebase_backend_sdk/_integrations/django/install.py +154 -0
  27. cluebase_backend_sdk/_integrations/fastapi/__init__.py +35 -0
  28. cluebase_backend_sdk/_integrations/fastapi/fastapi.py +432 -0
  29. cluebase_backend_sdk/_integrations/langchain/__init__.py +28 -0
  30. cluebase_backend_sdk/_integrations/langchain/_run_tracker.py +127 -0
  31. cluebase_backend_sdk/_integrations/langchain/callback.py +505 -0
  32. cluebase_backend_sdk/_integrations/langchain/instrument.py +123 -0
  33. cluebase_backend_sdk/_integrations/llamaindex/__init__.py +28 -0
  34. cluebase_backend_sdk/_integrations/llamaindex/_run_tracker.py +124 -0
  35. cluebase_backend_sdk/_integrations/llamaindex/event_handler.py +320 -0
  36. cluebase_backend_sdk/_integrations/llamaindex/instrument.py +93 -0
  37. cluebase_backend_sdk/_integrations/mcp/__init__.py +14 -0
  38. cluebase_backend_sdk/_integrations/mcp/_method_observations.py +569 -0
  39. cluebase_backend_sdk/_integrations/mcp/_observer_helpers.py +530 -0
  40. cluebase_backend_sdk/_integrations/mcp/_tool_observation.py +346 -0
  41. cluebase_backend_sdk/_integrations/mcp/middleware.py +103 -0
  42. cluebase_backend_sdk/_integrations/mcp/observer.py +660 -0
  43. cluebase_backend_sdk/_integrations/mcp/session.py +105 -0
  44. cluebase_backend_sdk/_internal_log.py +51 -0
  45. cluebase_backend_sdk/_safe_logger.py +32 -0
  46. cluebase_backend_sdk/_version.py +5 -0
  47. cluebase_backend_sdk/context/__init__.py +9 -0
  48. cluebase_backend_sdk/context/parameter_snapshot.py +183 -0
  49. cluebase_backend_sdk/exporter/__init__.py +10 -0
  50. cluebase_backend_sdk/exporter/client.py +574 -0
  51. cluebase_backend_sdk/exporter/collection_gate.py +217 -0
  52. cluebase_backend_sdk/exporter/contracts.py +577 -0
  53. cluebase_backend_sdk/exporter/deadline.py +36 -0
  54. cluebase_backend_sdk/exporter/delivery.py +509 -0
  55. cluebase_backend_sdk/exporter/delivery_runner.py +148 -0
  56. cluebase_backend_sdk/exporter/diagnostics.py +216 -0
  57. cluebase_backend_sdk/exporter/envelope.py +60 -0
  58. cluebase_backend_sdk/exporter/event_id.py +74 -0
  59. cluebase_backend_sdk/exporter/event_size.py +98 -0
  60. cluebase_backend_sdk/exporter/http_transport.py +262 -0
  61. cluebase_backend_sdk/exporter/lifecycle.py +91 -0
  62. cluebase_backend_sdk/exporter/observation_source_event_exporter.py +213 -0
  63. cluebase_backend_sdk/exporter/otel_bridge.py +192 -0
  64. cluebase_backend_sdk/exporter/pending.py +394 -0
  65. cluebase_backend_sdk/exporter/privacy.py +313 -0
  66. cluebase_backend_sdk/exporter/profile.py +149 -0
  67. cluebase_backend_sdk/exporter/sanitize.py +39 -0
  68. cluebase_backend_sdk/exporter/source_signal.py +299 -0
  69. cluebase_backend_sdk/exporter/source_signal_context.py +53 -0
  70. cluebase_backend_sdk/exporter/source_signal_privacy.py +88 -0
  71. cluebase_backend_sdk/exporter/source_signal_record_privacy.py +193 -0
  72. cluebase_backend_sdk/exporter/wire_credentials.py +33 -0
  73. cluebase_backend_sdk/instrumentation/__init__.py +10 -0
  74. cluebase_backend_sdk/instrumentation/_orm_django.py +188 -0
  75. cluebase_backend_sdk/instrumentation/_orm_spans.py +271 -0
  76. cluebase_backend_sdk/instrumentation/_orm_sqlalchemy.py +337 -0
  77. cluebase_backend_sdk/instrumentation/_orm_state.py +100 -0
  78. cluebase_backend_sdk/instrumentation/celery.py +539 -0
  79. cluebase_backend_sdk/instrumentation/http_extraction.py +158 -0
  80. cluebase_backend_sdk/instrumentation/httpx.py +101 -0
  81. cluebase_backend_sdk/instrumentation/orm.py +44 -0
  82. cluebase_backend_sdk/instrumentation/requests.py +254 -0
  83. cluebase_backend_sdk/instrumentation/server_request.py +104 -0
  84. cluebase_backend_sdk/instrumentation/span_attributes.py +71 -0
  85. cluebase_backend_sdk/provider/__init__.py +10 -0
  86. cluebase_backend_sdk/provider/_pipeline.py +286 -0
  87. cluebase_backend_sdk/provider/_runtime_state.py +691 -0
  88. cluebase_backend_sdk/provider/_runtime_telemetry.py +873 -0
  89. cluebase_backend_sdk/provider/bootstrap.py +179 -0
  90. cluebase_backend_sdk/provider/consent_span_processor.py +103 -0
  91. cluebase_backend_sdk/provider/privacy_policy_store.py +115 -0
  92. cluebase_backend_sdk/provider/resources.py +96 -0
  93. cluebase_backend_sdk/provider/runtime.py +258 -0
  94. cluebase_backend_sdk/provider/runtime_context.py +43 -0
  95. cluebase_backend_sdk/provider/runtime_diagnostics.py +66 -0
  96. cluebase_backend_sdk/provider/runtime_lifecycle.py +74 -0
  97. cluebase_backend_sdk-0.0.1.dist-info/METADATA +193 -0
  98. cluebase_backend_sdk-0.0.1.dist-info/RECORD +100 -0
  99. cluebase_backend_sdk-0.0.1.dist-info/WHEEL +5 -0
  100. cluebase_backend_sdk-0.0.1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,484 @@
1
+ """Cluebase Backend SDK (Python) public API.
2
+
3
+ The package exposes a single ``cluebase`` namespace object with ten methods:
4
+ ``init`` / ``identify`` / ``group`` / ``reset`` / ``track`` / ``set_consent`` /
5
+ ``link`` / ``flush`` / ``close`` / ``get_diagnostics``. Python options use
6
+ snake_case names.
7
+
8
+ Idiomatic usage::
9
+
10
+ from cluebase_backend_sdk import cluebase
11
+
12
+ cluebase.init({
13
+ "endpoint": "https://api.example.com",
14
+ "project_key": "pk_prod_...",
15
+ "api_key": "cluebasesk_...",
16
+ "service_key": "backend-api",
17
+ })
18
+ cluebase.identify("user_123", {"name": "Alice", "email": "alice@example.com"})
19
+ cluebase.group("organization", "org_42", {"name": "Acme"})
20
+ cluebase.track("checkout_completed", {"plan": "pro"})
21
+ cluebase.set_consent("granted")
22
+ await cluebase.flush()
23
+ await cluebase.close()
24
+
25
+ Framework integrations import runtime helpers from their owning modules; the
26
+ package root is the customer-facing facade plus stable setup helpers.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ import asyncio
32
+ from collections.abc import Awaitable, Callable, Mapping
33
+ from typing import Literal, TypeAlias, TypedDict
34
+
35
+ from ._init_options import (
36
+ CluebaseInitInput,
37
+ CluebaseInitOptions,
38
+ CluebaseSdkCollectionMode,
39
+ _resolve_init_options,
40
+ _validate_init_options,
41
+ )
42
+ from ._internal_log import get_logger
43
+ from ._version import __version__
44
+ from .exporter.client import CluebasePythonClient
45
+ from .exporter.diagnostics import DiagnosticSnapshot
46
+ from .exporter.privacy import (
47
+ DEFAULT_DENIED_KEYS,
48
+ JsonValue,
49
+ build_mask_token,
50
+ safe_sanitize_object,
51
+ )
52
+ from .instrumentation.celery import instrument_celery_signals
53
+ from .instrumentation.httpx import instrument_httpx, reset_httpx_instrumentation
54
+ from .instrumentation.orm import instrument_django_orm, instrument_sqlalchemy_orm
55
+ from .instrumentation.requests import (
56
+ instrument_requests,
57
+ reset_requests_instrumentation,
58
+ )
59
+ from .provider.bootstrap import (
60
+ CluebasePythonBootstrapConfig,
61
+ build_otel_environment,
62
+ configure_opentelemetry_environment,
63
+ default_traces_endpoint_from_ingest_endpoint,
64
+ initialize_opentelemetry,
65
+ load_bootstrap_config,
66
+ require_opentelemetry,
67
+ )
68
+ from .provider.resources import (
69
+ CluebasePythonResourceConfig,
70
+ build_resource_attributes,
71
+ serialize_resource_attributes,
72
+ )
73
+ from .provider.runtime import (
74
+ CluebaseCommand,
75
+ CluebasePythonSettings,
76
+ CluebaseStateTransition,
77
+ CluebaseSetOperationDiscriminator,
78
+ CluebaseToolCall,
79
+ annotate_current_otel_span,
80
+ build_settings,
81
+ clear_configured_settings,
82
+ close_shared_clients,
83
+ configure_settings,
84
+ create_client,
85
+ create_request_context,
86
+ emit_sdk_initialized,
87
+ enqueue_client_events,
88
+ flush_shared_clients,
89
+ get_current_client,
90
+ get_current_consent,
91
+ get_current_context,
92
+ load_settings,
93
+ reset_current_state,
94
+ set_current_state,
95
+ )
96
+ from .provider.runtime import (
97
+ CluebaseIdentify as _internal_identify,
98
+ )
99
+ from .provider.runtime import (
100
+ CluebaseLink as _internal_link,
101
+ )
102
+ from .provider.runtime import (
103
+ CluebaseLogout as _internal_logout,
104
+ )
105
+ from .provider.runtime import (
106
+ CluebaseSetOrganization as _internal_set_organization,
107
+ )
108
+ from .provider.runtime import (
109
+ CluebaseTrack as _internal_track,
110
+ )
111
+ from .provider.runtime import (
112
+ clear_current_state as _clear_current_state,
113
+ )
114
+ from .provider.runtime import (
115
+ get_diagnostics as _get_runtime_diagnostics,
116
+ )
117
+ from .provider.runtime import (
118
+ set_consent as _set_runtime_consent,
119
+ )
120
+
121
+ _logger = get_logger("public")
122
+
123
+
124
+ # ---------------------------------------------------------------------------
125
+ # Public namespace types (= mirror the Frontend SDK's `CluebaseGroupType` /
126
+ # `CluebaseGroupTraits` / `CluebaseConsentState`).
127
+ # ---------------------------------------------------------------------------
128
+
129
+
130
+ CluebaseGroupType = Literal["organization"]
131
+ """MVP group taxonomy. Customer company grouping is represented as
132
+ ``organization``. ``account`` / ``workspace`` are future concepts and are not
133
+ public ``cluebase.group`` inputs for MVP."""
134
+
135
+
136
+ CluebaseConsentState = Literal["granted", "denied", "unknown"]
137
+ """Process-wide collection consent state."""
138
+
139
+ """SDK collection mode accepted by ``cluebase.init``."""
140
+
141
+ CluebasePublicTraits: TypeAlias = Mapping[str, object]
142
+ """Free-form customer traits accepted by ``cluebase.identify`` and ``cluebase.group``."""
143
+
144
+ CluebasePublicProperties: TypeAlias = Mapping[str, object]
145
+ """Free-form customer event properties accepted by ``cluebase.track``."""
146
+
147
+ CluebasePublicMetrics: TypeAlias = Mapping[str, object]
148
+ """Free-form customer event metrics accepted by ``cluebase.track``."""
149
+
150
+
151
+ class CluebaseGroupTraits(TypedDict, total=False):
152
+ """Optional traits attached to ``cluebase.group(...)``. ``name`` is recommended
153
+ (human-readable label used downstream by analysis surfaces); other keys are
154
+ free-form and forwarded as event properties subject to ``denied_keys``."""
155
+
156
+ name: str
157
+
158
+
159
+ class CluebaseLinkSubject(TypedDict):
160
+ type: CluebaseGroupType
161
+ id: str
162
+
163
+
164
+ class CluebaseExternalIdentity(TypedDict):
165
+ namespace: str
166
+ id: str
167
+
168
+
169
+ class CluebaseLinkInput(TypedDict):
170
+ subject: CluebaseLinkSubject
171
+ external: CluebaseExternalIdentity
172
+
173
+
174
+ class CluebaseSubjectTraits(TypedDict, total=False):
175
+ """Optional traits attached to ``cluebase.identify(user_id, traits)``. The
176
+ ``name`` field is *strongly* recommended because analysis surfaces must not
177
+ display the raw user_id as a person label. ``email`` is converted to
178
+ privacy-safe contact-derived fields before transport.
179
+
180
+ Field naming exception: keys are **camelCase** even on the Python SDK
181
+ (``avatarUrl``), not the usual Python ``snake_case``.
182
+ This honours the cross-SDK customer-facing contract documented in
183
+ ``docs/contracts/sdk/public-api.md`` §1.6 — every SDK
184
+ (Browser / Node / Python) accepts the same trait keys so customers can
185
+ copy idiomatic examples between languages without renaming fields.
186
+ The ingest core normalizes these values after receiving SDK source signals."""
187
+
188
+ name: str
189
+ email: str
190
+ emailAddress: str
191
+ displayName: str
192
+ avatarUrl: str
193
+
194
+
195
+
196
+
197
+ """Runtime input accepted by ``cluebase.init``; mapping form preserves public SDK flexibility."""
198
+
199
+
200
+ # ---------------------------------------------------------------------------
201
+ # Safe-call helpers — the SDK guarantees never to crash the host application,
202
+ # even if a hot path raises. We log the swallowed exception via the SDK's
203
+ # internal diagnostic logger so operators can opt-in to debug output.
204
+ # ---------------------------------------------------------------------------
205
+
206
+
207
+ def _report_public_sdk_error(operation: str, error: BaseException) -> None:
208
+ _ = error
209
+ # `_logger` は `SafeLogger` で、記録に失敗しても自分で飲み込みます
210
+ # (`_safe_logger.py`)。ここで受け口を重ねても、通ることがありません
211
+ _logger.warning(
212
+ "cluebase.sdk public operation failed operation=%s code=public_operation_failed",
213
+ operation,
214
+ )
215
+
216
+
217
+ def _safe_call(operation: str, callback: Callable[[], object]) -> None:
218
+ try:
219
+ callback()
220
+ except BaseException as exc: # noqa: BLE001 — last-resort host protection
221
+ _report_public_sdk_error(operation, exc)
222
+
223
+
224
+ async def _safe_async_call(
225
+ operation: str, callback: Callable[[], Awaitable[object]]
226
+ ) -> None:
227
+ try:
228
+ await callback()
229
+ except BaseException as exc: # noqa: BLE001 — last-resort host protection
230
+ _report_public_sdk_error(operation, exc)
231
+
232
+
233
+ # ---------------------------------------------------------------------------
234
+ # `cluebase.init` maps Python setup options onto runtime bootstrap settings.
235
+ # ---------------------------------------------------------------------------
236
+
237
+
238
+
239
+
240
+
241
+
242
+
243
+
244
+
245
+
246
+ # ``allowed_value_paths`` used to let the caller name the request/response
247
+ # fields that may leave the process in clear text. That choice now belongs to
248
+ # the Cluebase privacy screen and reaches the SDK through the delivered privacy
249
+ # policy, so the option has nothing left to apply to.
250
+
251
+
252
+
253
+
254
+ # ---------------------------------------------------------------------------
255
+ # `cluebase` namespace — module-level singleton facade. The object is module-level
256
+ # so multiple imports in the same process share state, exactly like the
257
+ # Frontend SDK's ``globalThis.__cluebase_frontend_sdk_singleton__`` pattern.
258
+ # ---------------------------------------------------------------------------
259
+
260
+
261
+ class _Cluebase:
262
+ """Cluebase Backend SDK public namespace.
263
+
264
+ Ten methods mirror the cross-SDK contract documented in
265
+ ``docs/contracts/sdk/public-api.md`` §1.6. Every method
266
+ swallows exceptions and reports them via the SDK internal logger; the SDK
267
+ is contractually forbidden from crashing the host application.
268
+ """
269
+
270
+ __slots__ = ()
271
+
272
+ def _allows_collection(self) -> bool:
273
+ return get_current_consent() != "denied"
274
+
275
+ def init(self, options: CluebaseInitInput) -> None:
276
+ """Initialise the SDK. ``endpoint``, ``project_key``, ``api_key``, and
277
+ ``service_key`` are required; a missing one raises ``ValueError`` rather
278
+ than leaving the SDK collecting nothing. Calling ``init`` twice is a
279
+ no-op (= the bootstrap layer guards against duplicate TracerProvider
280
+ wiring)."""
281
+
282
+ # Raised before ``_safe_call`` so the caller actually sees it; see
283
+ # ``_validate_init_options``.
284
+ _validate_init_options(options)
285
+
286
+ def _do_init() -> None:
287
+ bootstrap_config, settings = _resolve_init_options(options)
288
+ require_opentelemetry(bootstrap_config)
289
+ configure_settings(settings)
290
+ emit_sdk_initialized(settings)
291
+
292
+ _safe_call("init", _do_init)
293
+
294
+ def identify(
295
+ self,
296
+ user_id: str,
297
+ traits: CluebasePublicTraits | None = None,
298
+ ) -> None:
299
+ """Bind the caller-supplied subject identity to subsequent events.
300
+ ``traits['name']`` is strongly recommended because analysis surfaces must
301
+ not display the raw user_id as a person label. ``traits['email']`` is
302
+ converted to privacy-safe contact-derived fields before transport."""
303
+
304
+ if not self._allows_collection():
305
+ return
306
+
307
+ _safe_call("identify", lambda: _internal_identify(user_id, traits or {}))
308
+
309
+ def group(
310
+ self,
311
+ group_type: str,
312
+ group_key: str,
313
+ traits: CluebasePublicTraits | None = None,
314
+ ) -> None:
315
+ """Associate the caller with a customer company."""
316
+
317
+ def _dispatch() -> None:
318
+ resolved_traits = traits or {}
319
+ if group_type != "organization":
320
+ raise ValueError(
321
+ f"cluebase.group: unsupported group_type {group_type!r}; "
322
+ "expected 'organization'"
323
+ )
324
+ if not self._allows_collection():
325
+ return
326
+
327
+ _internal_set_organization(group_key, resolved_traits)
328
+
329
+ _safe_call("group", _dispatch)
330
+
331
+ def reset(self) -> None:
332
+ """Clear identity state. ``logout`` and the in-memory reset are wrapped
333
+ in independent ``_safe_call`` invocations so one failing does not
334
+ short-circuit the other (= state must be cleared as best-effort)."""
335
+
336
+ if self._allows_collection():
337
+ _safe_call("reset", lambda: _internal_logout())
338
+ _safe_call("reset", _clear_current_state)
339
+
340
+ def link(self, link_input: CluebaseLinkInput | Mapping[str, object]) -> None:
341
+ """Record a canonical organization to external identifier fact."""
342
+
343
+ if not self._allows_collection():
344
+ return
345
+ _safe_call("link", lambda: _internal_link(link_input))
346
+
347
+ def track(
348
+ self,
349
+ event_name: str,
350
+ properties: CluebasePublicProperties | None = None,
351
+ metrics: CluebasePublicMetrics | None = None,
352
+ ) -> None:
353
+ """Emit a custom event with the same
354
+ ``track(event_name, properties=None, metrics=None)`` public contract as
355
+ the Browser and Node SDKs."""
356
+
357
+ if not self._allows_collection():
358
+ return
359
+
360
+ _safe_call("track", lambda: _internal_track(event_name, properties, metrics))
361
+
362
+ def set_operation_discriminator(self, value: str) -> None:
363
+ """Annotate the active backend operation with a mechanical branch key."""
364
+
365
+ _safe_call(
366
+ "set_operation_discriminator",
367
+ lambda: CluebaseSetOperationDiscriminator(value),
368
+ )
369
+
370
+ def set_consent(self, consent: str) -> None:
371
+ """Record the caller's analytics consent state. Accepted values
372
+ (Literal): ``"granted"`` / ``"denied"`` / ``"unknown"``. ``denied``
373
+ makes later collection calls no-op; changing back to ``granted`` or
374
+ ``unknown`` resumes collection."""
375
+
376
+ def _set_state() -> None:
377
+ if consent not in ("granted", "denied", "unknown"):
378
+ raise ValueError(
379
+ f"cluebase.set_consent: unsupported value {consent!r}; "
380
+ "expected one of 'granted' / 'denied' / 'unknown'"
381
+ )
382
+ _set_runtime_consent(consent)
383
+
384
+ _safe_call("set_consent", _set_state)
385
+
386
+ async def flush(self) -> None:
387
+ """Force-flush every buffered shared client. Awaited so callers can
388
+ ``await cluebase.flush()`` before shutdown — the underlying flush is
389
+ synchronous but we expose it as a coroutine for cross-SDK signature
390
+ parity (= Frontend SDK is async)."""
391
+
392
+ async def _run() -> None:
393
+ await asyncio.to_thread(flush_shared_clients)
394
+
395
+ await _safe_async_call("flush", _run)
396
+
397
+ async def close(self) -> None:
398
+ """Stop capture and close the canonical delivery lifecycle."""
399
+
400
+ async def _run() -> None:
401
+ await asyncio.to_thread(close_shared_clients)
402
+
403
+ await _safe_async_call("close", _run)
404
+
405
+ def get_diagnostics(self) -> DiagnosticSnapshot:
406
+ """Return a defensive, privacy-safe runtime diagnostics snapshot."""
407
+
408
+ return _get_runtime_diagnostics()
409
+
410
+
411
+ cluebase = _Cluebase()
412
+ """Module-level singleton. Import as ``from cluebase_backend_sdk import cluebase``."""
413
+
414
+ CluebaseIdentify = _internal_identify
415
+ CluebaseLink = _internal_link
416
+ CluebaseLogout = _internal_logout
417
+ CluebaseSetOrganization = _internal_set_organization
418
+ CluebaseTrack = _internal_track
419
+
420
+
421
+ # ---------------------------------------------------------------------------
422
+ # Helper exports for framework integrations.
423
+ __all__ = [
424
+ "DEFAULT_DENIED_KEYS",
425
+ "CluebaseCommand",
426
+ "CluebaseConsentState",
427
+ "CluebaseGroupTraits",
428
+ "CluebaseGroupType",
429
+ "CluebaseIdentify",
430
+ "CluebaseExternalIdentity",
431
+ "CluebaseLink",
432
+ "CluebaseInitInput",
433
+ "CluebaseInitOptions",
434
+ "CluebaseLinkInput",
435
+ "CluebaseLinkSubject",
436
+ "CluebaseLogout",
437
+ "CluebasePublicMetrics",
438
+ "CluebasePublicProperties",
439
+ "CluebasePublicTraits",
440
+ "CluebasePythonBootstrapConfig",
441
+ "CluebasePythonClient",
442
+ "CluebasePythonResourceConfig",
443
+ "CluebasePythonSettings",
444
+ "CluebaseSdkCollectionMode",
445
+ "CluebaseSetOrganization",
446
+ "CluebaseSetOperationDiscriminator",
447
+ "CluebaseStateTransition",
448
+ "CluebaseSubjectTraits",
449
+ "CluebaseToolCall",
450
+ "CluebaseTrack",
451
+ "JsonValue",
452
+ "__version__",
453
+ "annotate_current_otel_span",
454
+ "build_mask_token",
455
+ "build_otel_environment",
456
+ "build_resource_attributes",
457
+ "build_settings",
458
+ "clear_configured_settings",
459
+ "cluebase",
460
+ "configure_opentelemetry_environment",
461
+ "configure_settings",
462
+ "create_client",
463
+ "create_request_context",
464
+ "default_traces_endpoint_from_ingest_endpoint",
465
+ "emit_sdk_initialized",
466
+ "enqueue_client_events",
467
+ "flush_shared_clients",
468
+ "get_current_client",
469
+ "get_current_context",
470
+ "initialize_opentelemetry",
471
+ "instrument_celery_signals",
472
+ "instrument_django_orm",
473
+ "instrument_httpx",
474
+ "instrument_requests",
475
+ "instrument_sqlalchemy_orm",
476
+ "load_bootstrap_config",
477
+ "load_settings",
478
+ "reset_current_state",
479
+ "reset_httpx_instrumentation",
480
+ "reset_requests_instrumentation",
481
+ "safe_sanitize_object",
482
+ "serialize_resource_attributes",
483
+ "set_current_state",
484
+ ]
@@ -0,0 +1,37 @@
1
+ """配る部品**自身が測った値**を封筒に載せる時の、属性の名前。
2
+
3
+ 顧客が ``track`` で付けた項目は ``cluebase.properties.<綴り>`` /
4
+ ``cluebase.metrics.<綴り>`` という封筒で運ばれます。**配る部品も同じ封筒を使う**
5
+ ため、封筒を開けた受け口から見ると、顧客が名付けた業務項目と、部品が測った値の
6
+ 区別が付きません。
7
+
8
+ 実際にそれが起きました。分析が ``duration_ms`` を顧客の業務項目だと解釈し、
9
+ 利用者へ**「duration_ms を測りました」**と答えました(実測 2026-08-15)。
10
+
11
+ そこで、部品が測った値は封筒の中で**予約された名前**(``cluebase.`` で始まる)を
12
+ 名乗ります。顧客の項目がこの接頭辞を持つことはないため、封筒を開けた時点で
13
+ 混ざりません。**顧客が偶然 ``queue_name`` という業務項目を送ってきても、それは
14
+ 顧客のものとして扱われます。**
15
+
16
+ **顧客が渡した項目には使いません。** 顧客の綴りをここへ通すと、その項目が
17
+ 仕組みのものとして扱われ、分析に出なくなります。
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ RESERVED_FIELD_KEY_PREFIX = "cluebase."
23
+
24
+ _PROPERTY_ENVELOPE_PREFIX = "cluebase.properties."
25
+ _METRIC_ENVELOPE_PREFIX = "cluebase.metrics."
26
+
27
+
28
+ def cluebase_authored_property_attribute(name: str) -> str:
29
+ """部品自身が測った値(文字列など)の属性名。"""
30
+
31
+ return f"{_PROPERTY_ENVELOPE_PREFIX}{RESERVED_FIELD_KEY_PREFIX}{name}"
32
+
33
+
34
+ def cluebase_authored_metric_attribute(name: str) -> str:
35
+ """部品自身が測った値(数値)の属性名。"""
36
+
37
+ return f"{_METRIC_ENVELOPE_PREFIX}{RESERVED_FIELD_KEY_PREFIX}{name}"
@@ -0,0 +1,11 @@
1
+ """自動生成です。**手で編集しないでください。**
2
+
3
+ 正本: packages/cluebase-shared/src/ingest/ingest-privacy-policy.v1.json
4
+ 出し直す: node scripts/generate-shared-contract-tables.mjs
5
+
6
+ 正本を直したらこの file も一緒に commit してください。CI が
7
+ ``node scripts/generate-shared-contract-tables.mjs --check`` で照合し、ずれていれば落とします。
8
+ """
9
+
10
+ #: 生成物を出し直す命令。**この綴りを他所へ写さないでください。**
11
+ REGENERATE_COMMAND = "node scripts/generate-shared-contract-tables.mjs"
@@ -0,0 +1,37 @@
1
+ """自動生成です。**手で編集しないでください。**
2
+
3
+ 正本: packages/cluebase-backend-contract/src/delivery/correlation-attribute-keys.v1.json
4
+ 出し直す: node scripts/generate-shared-contract-tables.mjs
5
+
6
+ 正本を直したらこの file も一緒に commit してください。CI が
7
+ ``node scripts/generate-shared-contract-tables.mjs --check`` で照合し、ずれていれば落とします。
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ #: 伏せる処理を通しても残す、相関のための属性名。
13
+ CLUEBASE_CORRELATION_ATTRIBUTE_KEYS: frozenset[str] = frozenset((
14
+ "cluebase.actor.anonymous_id",
15
+ "cluebase.actor.user_id",
16
+ "cluebase.interaction.id",
17
+ "cluebase.interaction_id",
18
+ "cluebase.organization.id",
19
+ "cluebase.organization_id",
20
+ "cluebase.origin_frontend_interaction.id",
21
+ "cluebase.parent_request_span_id",
22
+ "cluebase.parent_span_id",
23
+ "cluebase.request.id",
24
+ "cluebase.request_id",
25
+ "cluebase.request_span_id",
26
+ "cluebase.session.id",
27
+ "cluebase.session_id",
28
+ "cluebase.span_id",
29
+ "cluebase.subject.organization_id",
30
+ "cluebase.tab.id",
31
+ "cluebase.tab_id",
32
+ "cluebase.trace_id",
33
+ "enduser.id",
34
+ "mcp.client.name",
35
+ "mcp.client.version",
36
+ "mcp.session.id",
37
+ ))
@@ -0,0 +1,62 @@
1
+ """自動生成です。**手で編集しないでください。**
2
+
3
+ 正本: packages/cluebase-shared/src/ingest/identity-pii-policy.v1.json
4
+ 出し直す: node scripts/generate-shared-contract-tables.mjs
5
+
6
+ 正本を直したらこの file も一緒に commit してください。CI が
7
+ ``node scripts/generate-shared-contract-tables.mjs --check`` で照合し、ずれていれば落とします。
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ #: 見る順序。先に当たった種類を返します。
13
+ PII_CATEGORY_ORDER: tuple[str, ...] = (
14
+ "email",
15
+ "ssn",
16
+ "phone",
17
+ "credit_card",
18
+ )
19
+
20
+ #: 電子メールの形。
21
+ PII_EMAIL_PATTERN = "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$"
22
+
23
+ #: 社会保障番号の形。
24
+ PII_SSN_PATTERN = "^\\d{3}-\\d{2}-\\d{4}$"
25
+
26
+ #: 電話とみなす桁数の範囲と、見た目・求める区切り。
27
+ PII_PHONE_MIN_DIGITS = 7
28
+ PII_PHONE_MAX_DIGITS = 15
29
+ PII_PHONE_SHAPE_PATTERN = "^[+()0-9.\\-\\s]+$"
30
+ PII_PHONE_SEPARATOR_PATTERN = "[+().\\-\\s]"
31
+
32
+ #: 支払い用の番号とみなす桁数の範囲と見た目。Luhn の検算を必ず通します。
33
+ PII_CREDIT_CARD_MIN_DIGITS = 13
34
+ PII_CREDIT_CARD_MAX_DIGITS = 19
35
+ PII_CREDIT_CARD_SHAPE_PATTERN = "^[0-9\\s\\-]+$"
36
+
37
+ #: 伏せた値の形。同じ入力なら配る部品 3 つとも同じ値になります。
38
+ PII_HASH_PREFIX = "cluebase_pii_hashed:"
39
+ PII_HASH_HEX_LENGTH = 32
40
+
41
+ #: どの面の規則か(読み手が取り違えないため)。
42
+ PII_SURFACE = "whole_value_is_pii"
43
+
44
+
45
+ def passes_luhn(digits: str) -> bool:
46
+ """Luhn の検算。支払い用の番号らしさを、無作為な数字の並びと分けます。
47
+
48
+ **手順もここから出します。** 桁数と綴りだけを正本化して手順を手書きで
49
+ 残すと、同じ算法が 4 か所に残ります。実際に残っていました。
50
+ """
51
+
52
+ total = 0
53
+ alt = False
54
+ for ch in reversed(digits):
55
+ n = int(ch)
56
+ if alt:
57
+ n *= 2
58
+ if n > 9:
59
+ n -= 9
60
+ total += n
61
+ alt = not alt
62
+ return total % 10 == 0