bluejay-sdk 0.1.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.
@@ -0,0 +1,3 @@
1
+ from bluejay_sdk.tracing import BluejayTracing
2
+
3
+ __all__ = ["BluejayTracing"]
bluejay_sdk/tracing.py ADDED
@@ -0,0 +1,135 @@
1
+ import logging
2
+ import os
3
+
4
+ import httpx
5
+ import opentelemetry.trace as otel_trace
6
+ from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
7
+ from opentelemetry.sdk import trace as trace_sdk
8
+ from opentelemetry.sdk.resources import SERVICE_NAME, Resource
9
+ from opentelemetry.sdk.trace.export import SimpleSpanProcessor
10
+
11
+ logger = logging.getLogger("bluejay_sdk")
12
+
13
+ DEFAULT_OTLP_ENDPOINT = "https://otlp.getbluejay.ai/v1/traces"
14
+ DEFAULT_API_URL = "https://api.getbluejay.ai/v1"
15
+
16
+
17
+ class BluejayTracing:
18
+ def __init__(
19
+ self,
20
+ api_key: str,
21
+ *,
22
+ service_name: str = "bluejay-agent",
23
+ otlp_endpoint: str = DEFAULT_OTLP_ENDPOINT,
24
+ api_url: str = DEFAULT_API_URL,
25
+ ):
26
+ self.api_key = api_key
27
+ self.api_url = api_url
28
+ self.simulation_result_id: str | None = None
29
+ self.trace_id: str | None = None
30
+
31
+ resource = Resource.create({SERVICE_NAME: service_name})
32
+ self.tracer_provider = trace_sdk.TracerProvider(resource=resource)
33
+ self.tracer_provider.add_span_processor(
34
+ SimpleSpanProcessor(
35
+ OTLPSpanExporter(otlp_endpoint, headers={"X-API-KEY": api_key})
36
+ )
37
+ )
38
+
39
+ try:
40
+ from livekit.agents.telemetry import set_tracer_provider
41
+
42
+ set_tracer_provider(self.tracer_provider)
43
+ except ImportError:
44
+ pass
45
+
46
+ otel_trace.set_tracer_provider(self.tracer_provider)
47
+
48
+ @classmethod
49
+ def init(
50
+ cls,
51
+ api_key: str | None = None,
52
+ *,
53
+ service_name: str | None = None,
54
+ otlp_endpoint: str | None = None,
55
+ api_url: str | None = None,
56
+ ) -> "BluejayTracing":
57
+ key = api_key or os.getenv("BLUEJAY_API_KEY")
58
+ if not key:
59
+ raise ValueError("BLUEJAY_API_KEY not set and no api_key provided")
60
+
61
+ return cls(
62
+ api_key=key,
63
+ service_name=service_name or os.getenv("BLUEJAY_SERVICE_NAME", "bluejay-agent"),
64
+ otlp_endpoint=otlp_endpoint or os.getenv("BLUEJAY_OTLP_ENDPOINT", DEFAULT_OTLP_ENDPOINT),
65
+ api_url=api_url or os.getenv("BLUEJAY_API_URL", DEFAULT_API_URL),
66
+ )
67
+
68
+ def link_to_session(self, ctx) -> None:
69
+ """Register participant listener and shutdown callback on a LiveKit JobContext."""
70
+ from livekit import rtc
71
+
72
+ @ctx.room.on("participant_connected")
73
+ def on_participant_connected(participant: rtc.RemoteParticipant):
74
+ if self.simulation_result_id:
75
+ return
76
+ result_id = (participant.attributes or {}).get("X-Simulation-Result-Id")
77
+ if result_id:
78
+ self.simulation_result_id = str(result_id)
79
+ logger.info(f"captured simulation_result_id={result_id}")
80
+
81
+ for p in ctx.room.remote_participants.values():
82
+ result_id = (p.attributes or {}).get("X-Simulation-Result-Id")
83
+ if result_id:
84
+ self.simulation_result_id = str(result_id)
85
+ break
86
+
87
+ ctx.add_shutdown_callback(self._on_shutdown)
88
+
89
+ def capture_trace(self) -> str | None:
90
+ """Capture trace_id from the current OTel span (call after session.start())."""
91
+ span = otel_trace.get_current_span()
92
+ span_ctx = span.get_span_context()
93
+ if span_ctx.is_valid:
94
+ self.trace_id = format(span_ctx.trace_id, "032x")
95
+ logger.info(f"agent_session trace_id={self.trace_id}")
96
+ return self.trace_id
97
+
98
+ async def _on_shutdown(self) -> None:
99
+ logger.info(f"shutdown — simulation_result_id={self.simulation_result_id} trace_id={self.trace_id}")
100
+
101
+ try:
102
+ self.tracer_provider.force_flush()
103
+ except Exception as e:
104
+ logger.error(f"force_flush failed: {e}")
105
+
106
+ if self.simulation_result_id and self.trace_id:
107
+ await self._post_result()
108
+ else:
109
+ logger.warning(f"skipping update-simulation-result — simulation_result_id={self.simulation_result_id} trace_id={self.trace_id}")
110
+
111
+ try:
112
+ self.tracer_provider.shutdown()
113
+ except Exception as e:
114
+ logger.error(f"tracer shutdown failed: {e}")
115
+
116
+ async def _post_result(self) -> None:
117
+ payload = {
118
+ "simulation_result_id": self.simulation_result_id,
119
+ "trace_ids": [self.trace_id],
120
+ }
121
+ headers = {"X-API-Key": self.api_key, "Content-Type": "application/json"}
122
+
123
+ async with httpx.AsyncClient(timeout=10) as client:
124
+ try:
125
+ resp = await client.post(
126
+ f"{self.api_url}/update-simulation-result",
127
+ json=payload,
128
+ headers=headers,
129
+ )
130
+ resp.raise_for_status()
131
+ logger.info("update-simulation-result SUCCESS")
132
+ except httpx.HTTPStatusError as e:
133
+ logger.error(f"update-simulation-result FAILED: {e.response.status_code} {e.response.text}")
134
+ except Exception as e:
135
+ logger.error(f"update-simulation-result FAILED: {e}")
@@ -0,0 +1,10 @@
1
+ Metadata-Version: 2.4
2
+ Name: bluejay-sdk
3
+ Version: 0.1.0
4
+ Summary: Bluejay SDK for LiveKit voice agents
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: httpx>=0.25
7
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http
8
+ Requires-Dist: opentelemetry-sdk
9
+ Provides-Extra: livekit
10
+ Requires-Dist: livekit-agents>=1.0; extra == 'livekit'
@@ -0,0 +1,5 @@
1
+ bluejay_sdk/__init__.py,sha256=vNAhrGWEJmZsCscA6NrWdS7U5JInuChqUxDDuFlTT8Y,77
2
+ bluejay_sdk/tracing.py,sha256=zPc-PZPjUOV0OYx5TGLaV3eOx91-FXOE36e2_ZhmFLc,5070
3
+ bluejay_sdk-0.1.0.dist-info/METADATA,sha256=E7IRPCdXebHtC_aMI8kWlamim1hwLuVl4T6UY0drTAo,318
4
+ bluejay_sdk-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
5
+ bluejay_sdk-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any