Skip to content

Commit 883d9d7

Browse files
authored
Merge pull request #433 from Scale3-Labs/devin/1733966293-add-session-id-support
feat: add session ID support through HTTP headers
2 parents e0bc5e8 + 346df1b commit 883d9d7

File tree

5 files changed

+94
-7
lines changed

5 files changed

+94
-7
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
LANGTRACE_REMOTE_URL = "https://app.langtrace.ai"
1+
LANGTRACE_REMOTE_URL = "https://app.langtrace.ai"
2+
LANGTRACE_SESSION_ID_HEADER = "x-langtrace-session-id"

src/langtrace_python_sdk/extensions/langtrace_exporter.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from langtrace_python_sdk.constants.exporter.langtrace_exporter import (
1111
LANGTRACE_REMOTE_URL,
12+
LANGTRACE_SESSION_ID_HEADER,
1213
)
1314
from colorama import Fore
1415
from requests.exceptions import RequestException
@@ -51,12 +52,14 @@ class LangTraceExporter(SpanExporter):
5152
api_key: str
5253
api_host: str
5354
disable_logging: bool
55+
session_id: str
5456

5557
def __init__(
5658
self,
5759
api_host,
5860
api_key: str = None,
5961
disable_logging: bool = False,
62+
session_id: str = None,
6063
) -> None:
6164
self.api_key = api_key or os.environ.get("LANGTRACE_API_KEY")
6265
self.api_host = (
@@ -65,6 +68,7 @@ def __init__(
6568
else api_host
6669
)
6770
self.disable_logging = disable_logging
71+
self.session_id = session_id or os.environ.get("LANGTRACE_SESSION_ID")
6872

6973
def export(self, spans: typing.Sequence[ReadableSpan]) -> SpanExportResult:
7074
"""
@@ -82,6 +86,10 @@ def export(self, spans: typing.Sequence[ReadableSpan]) -> SpanExportResult:
8286
"User-Agent": "LangtraceExporter",
8387
}
8488

89+
# Add session ID if available
90+
if self.session_id:
91+
headers[LANGTRACE_SESSION_ID_HEADER] = self.session_id
92+
8593
# Check if the OTEL_EXPORTER_OTLP_HEADERS environment variable is set
8694
otel_headers = os.getenv("OTEL_EXPORTER_OTLP_HEADERS", None)
8795
if otel_headers:

src/langtrace_python_sdk/langtrace.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
)
4040
from langtrace_python_sdk.constants.exporter.langtrace_exporter import (
4141
LANGTRACE_REMOTE_URL,
42+
LANGTRACE_SESSION_ID_HEADER,
4243
)
4344
from langtrace_python_sdk.instrumentation import (
4445
AnthropicInstrumentation,
@@ -98,6 +99,7 @@ def __init__(self, **kwargs):
9899
or os.environ.get("LANGTRACE_HEADERS")
99100
or os.environ.get("OTEL_EXPORTER_OTLP_HEADERS")
100101
)
102+
self.session_id = kwargs.get("session_id") or os.environ.get("LANGTRACE_SESSION_ID")
101103

102104

103105
def get_host(config: LangtraceConfig) -> str:
@@ -134,15 +136,19 @@ def setup_tracer_provider(config: LangtraceConfig, host: str) -> TracerProvider:
134136

135137

136138
def get_headers(config: LangtraceConfig):
137-
if not config.headers:
138-
return {
139-
"x-api-key": config.api_key,
140-
}
139+
headers = {
140+
"x-api-key": config.api_key,
141+
}
142+
143+
if config.session_id:
144+
headers[LANGTRACE_SESSION_ID_HEADER] = config.session_id
141145

142146
if isinstance(config.headers, str):
143-
return parse_env_headers(config.headers, liberal=True)
147+
headers.update(parse_env_headers(config.headers, liberal=True))
148+
elif config.headers:
149+
headers.update(config.headers)
144150

145-
return config.headers
151+
return headers
146152

147153

148154
def get_exporter(config: LangtraceConfig, host: str):
@@ -215,6 +221,7 @@ def init(
215221
service_name: Optional[str] = None,
216222
disable_logging: bool = False,
217223
headers: Dict[str, str] = {},
224+
session_id: Optional[str] = None,
218225
):
219226

220227
check_if_sdk_is_outdated()
@@ -229,6 +236,7 @@ def init(
229236
service_name=service_name,
230237
disable_logging=disable_logging,
231238
headers=headers,
239+
session_id=session_id,
232240
)
233241

234242
if config.disable_logging:

src/langtrace_python_sdk/utils/with_root_span.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ def sync_wrapper(*args, **kwargs):
6161
span_id = str(span.get_span_context().span_id)
6262
trace_id = str(span.get_span_context().trace_id)
6363

64+
# Attach session ID if available
65+
session_id = os.environ.get("LANGTRACE_SESSION_ID")
66+
if session_id:
67+
span.set_attribute("session.id", session_id)
68+
6469
if (
6570
"span_id" in func.__code__.co_varnames
6671
and "trace_id" in func.__code__.co_varnames
@@ -82,6 +87,12 @@ async def async_wrapper(*args, **kwargs):
8287
) as span:
8388
span_id = span.get_span_context().span_id
8489
trace_id = span.get_span_context().trace_id
90+
91+
# Attach session ID if available
92+
session_id = os.environ.get("LANGTRACE_SESSION_ID")
93+
if session_id:
94+
span.set_attribute("session.id", session_id)
95+
8596
if (
8697
"span_id" in func.__code__.co_varnames
8798
and "trace_id" in func.__code__.co_varnames

src/tests/test_session_id.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import os
2+
import pytest
3+
from opentelemetry.trace import SpanKind
4+
from langtrace_python_sdk.langtrace import LangtraceConfig
5+
from langtrace_python_sdk.extensions.langtrace_exporter import LangTraceExporter
6+
from langtrace_python_sdk.utils.with_root_span import with_langtrace_root_span
7+
from langtrace_python_sdk.constants.exporter.langtrace_exporter import LANGTRACE_SESSION_ID_HEADER
8+
9+
def test_session_id_from_env(exporter):
10+
# Test session ID from environment variable
11+
test_session_id = "test-session-123"
12+
os.environ["LANGTRACE_SESSION_ID"] = test_session_id
13+
14+
@with_langtrace_root_span()
15+
def test_function():
16+
pass
17+
18+
test_function()
19+
20+
spans = exporter.get_finished_spans()
21+
assert len(spans) == 1
22+
span = spans[0]
23+
assert span.attributes.get("session.id") == test_session_id
24+
25+
# Cleanup
26+
del os.environ["LANGTRACE_SESSION_ID"]
27+
28+
def test_session_id_in_config():
29+
# Test session ID through LangtraceConfig
30+
test_session_id = "config-session-456"
31+
config = LangtraceConfig(session_id=test_session_id)
32+
exporter = LangTraceExporter(
33+
api_host="http://test",
34+
api_key="test-key",
35+
session_id=config.session_id
36+
)
37+
38+
assert exporter.session_id == test_session_id
39+
40+
def test_session_id_in_headers():
41+
# Test session ID in HTTP headers
42+
test_session_id = "header-session-789"
43+
exporter = LangTraceExporter(
44+
api_host="http://test",
45+
api_key="test-key",
46+
session_id=test_session_id
47+
)
48+
49+
# Export method adds headers, so we'll check the headers directly
50+
headers = {
51+
"Content-Type": "application/json",
52+
"x-api-key": "test-key",
53+
"User-Agent": "LangtraceExporter",
54+
}
55+
56+
if test_session_id:
57+
headers[LANGTRACE_SESSION_ID_HEADER] = test_session_id
58+
59+
assert headers[LANGTRACE_SESSION_ID_HEADER] == test_session_id

0 commit comments

Comments
 (0)