Skip to content

Commit b1b0189

Browse files
authored
Merge branch 'main' into fix/serializer-depth-limit
2 parents ab2fa6f + 840cf2a commit b1b0189

8 files changed

Lines changed: 79 additions & 23 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ jobs:
7878
- uses: actions/checkout@v3
7979
- uses: pnpm/action-setup@v3
8080
with:
81-
version: 9.5.0
81+
version: 10.33.0
8282

8383
- name: Clone langfuse server
8484
run: |

CLAUDE.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This is the Langfuse Python SDK, a client library for accessing the Langfuse obs
99
## Development Commands
1010

1111
### Setup
12+
1213
```bash
1314
# Install Poetry plugins (one-time setup)
1415
poetry self add poetry-dotenv-plugin
@@ -21,6 +22,7 @@ poetry run pre-commit install
2122
```
2223

2324
### Testing
25+
2426
```bash
2527
# Run all tests with verbose output
2628
poetry run pytest -s -v --log-cli-level=INFO
@@ -33,6 +35,7 @@ poetry run pytest -s -v --log-cli-level=INFO -n auto
3335
```
3436

3537
### Code Quality
38+
3639
```bash
3740
# Format code with Ruff
3841
poetry run ruff format .
@@ -48,6 +51,7 @@ poetry run pre-commit run --all-files
4851
```
4952

5053
### Building and Releasing
54+
5155
```bash
5256
# Build the package locally (for testing)
5357
poetry build
@@ -57,6 +61,7 @@ poetry run pdoc -o docs/ --docformat google --logo "https://langfuse.com/langfus
5761
```
5862

5963
Releases are automated via GitHub Actions. To release:
64+
6065
1. Go to Actions > "Release Python SDK" workflow
6166
2. Click "Run workflow"
6267
3. Select version bump type (patch/minor/major/prerelease)
@@ -89,6 +94,7 @@ The workflow handles versioning, building, PyPI publishing (via OIDC), and GitHu
8994
### Key Design Patterns
9095

9196
The SDK is built on OpenTelemetry for observability, using:
97+
9298
- Spans for tracing LLM operations
9399
- Attributes for metadata (see `LangfuseOtelSpanAttributes`)
94100
- Resource management for efficient batching and flushing
@@ -98,6 +104,7 @@ The client follows an async-first design with automatic batching of events and b
98104
## Configuration
99105

100106
Environment variables (defined in `_client/environment_variables.py`):
107+
101108
- `LANGFUSE_PUBLIC_KEY` / `LANGFUSE_SECRET_KEY`: API credentials
102109
- `LANGFUSE_HOST`: API endpoint (defaults to https://cloud.langfuse.com)
103110
- `LANGFUSE_DEBUG`: Enable debug logging
@@ -127,9 +134,11 @@ The `langfuse/api/` directory is auto-generated from the Langfuse OpenAPI specif
127134
## Testing Guidelines
128135

129136
### Approach to Test Changes
137+
130138
- Don't remove functionality from existing unit tests just to make tests pass. Only change the test, if underlying code changes warrant a test change.
131139

132140
## Python Code Rules
133141

134142
### Exception Handling
143+
135144
- Exception must not use an f-string literal, assign to variable first

langfuse/_client/client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1818,7 +1818,7 @@ def create_score(
18181818
try:
18191819
new_body = ScoreBody(
18201820
id=score_id,
1821-
session_id=session_id,
1821+
sessionId=session_id,
18221822
datasetRunId=dataset_run_id,
18231823
traceId=trace_id,
18241824
observationId=observation_id,

langfuse/_utils/request.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,8 @@ def generate_headers(self) -> dict:
4848

4949
def batch_post(self, **kwargs: Any) -> httpx.Response:
5050
"""Post the `kwargs` to the batch API endpoint for events"""
51-
logger.debug("uploading data: %s", kwargs)
52-
5351
res = self.post(**kwargs)
52+
5453
return self._process_response(
5554
res, success_message="data uploaded successfully", return_json=False
5655
)

langfuse/langchain/CallbackHandler.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,10 +1057,10 @@ def _convert_message_to_dict(self, message: BaseMessage) -> Dict[str, Any]:
10571057
and len(message.tool_calls) > 0
10581058
):
10591059
message_dict["tool_calls"] = message.tool_calls
1060-
1060+
10611061
if (
1062-
hasattr(message, "invalid_tool_calls")
1063-
and message.invalid_tool_calls is not None
1062+
hasattr(message, "invalid_tool_calls")
1063+
and message.invalid_tool_calls is not None
10641064
and len(message.invalid_tool_calls) > 0
10651065
):
10661066
message_dict["invalid_tool_calls"] = message.invalid_tool_calls

poetry.lock

Lines changed: 11 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/test_core_sdk.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,49 @@ def test_invalid_score_data_does_not_raise_exception():
118118
# We can't assert queue size in OTEL implementation, but we can verify it completes without exception
119119

120120

121+
def test_create_session_score():
122+
langfuse = Langfuse()
123+
124+
session_id = "my-session"
125+
126+
# Create a span and set trace properties
127+
with langfuse.start_as_current_observation(name="test-span"):
128+
with propagate_attributes(
129+
trace_name="this-is-so-great-new",
130+
user_id="test",
131+
metadata={"test": "test"},
132+
session_id=session_id,
133+
):
134+
pass
135+
136+
# Ensure data is sent
137+
langfuse.flush()
138+
sleep(2)
139+
140+
# Create a numeric score
141+
score_id = create_uuid()
142+
143+
langfuse.create_score(
144+
score_id=score_id,
145+
session_id=session_id,
146+
name="this-is-a-score",
147+
value=1,
148+
)
149+
150+
# Ensure data is sent
151+
langfuse.flush()
152+
sleep(2)
153+
154+
# Retrieve and verify
155+
score = langfuse.api.scores.get_by_id(score_id)
156+
157+
# find the score by name (server may transform the id format)
158+
assert score is not None
159+
assert score.value == 1
160+
assert score.data_type == "NUMERIC"
161+
assert score.session_id == session_id
162+
163+
121164
def test_create_numeric_score():
122165
langfuse = Langfuse()
123166
api_wrapper = LangfuseAPI()

tests/test_langchain.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def test_callback_generated_from_trace_chat():
5151

5252
assert trace.id == trace_id
5353

54-
assert len(trace.observations) == 2
54+
assert len(trace.observations) == 3
5555

5656
langchain_generation_span = list(
5757
filter(
@@ -286,7 +286,7 @@ def test_openai_instruct_usage():
286286
observations = get_api().trace.get(trace_id).observations
287287

288288
# Add 1 to account for the wrapping span
289-
assert len(observations) == 3
289+
assert len(observations) == 4
290290

291291
for observation in observations:
292292
if observation.type == "GENERATION":
@@ -391,6 +391,7 @@ def test_get_langchain_chat_prompt():
391391
)
392392

393393

394+
@pytest.mark.skip("Flaky")
394395
def test_link_langfuse_prompts_invoke():
395396
langfuse = Langfuse()
396397
trace_name = "test_link_langfuse_prompts_invoke"
@@ -463,7 +464,7 @@ def test_link_langfuse_prompts_invoke():
463464
key=lambda x: x.start_time,
464465
)
465466

466-
assert len(generations) == 2
467+
# assert len(generations) == 4
467468
assert generations[0].input == "Tell me a joke involving the animal dog"
468469
assert "Explain the joke to me like I'm a 5 year old" in generations[1].input
469470

@@ -474,6 +475,7 @@ def test_link_langfuse_prompts_invoke():
474475
assert generations[1].prompt_version == langfuse_explain_prompt.version
475476

476477

478+
@pytest.mark.skip("Flaky")
477479
def test_link_langfuse_prompts_stream():
478480
langfuse = Langfuse()
479481
trace_name = "test_link_langfuse_prompts_stream"
@@ -550,7 +552,7 @@ def test_link_langfuse_prompts_stream():
550552
key=lambda x: x.start_time,
551553
)
552554

553-
assert len(generations) == 2
555+
assert len(generations) == 4
554556
assert generations[0].input == "Tell me a joke involving the animal dog"
555557
assert "Explain the joke to me like I'm a 5 year old" in generations[1].input
556558

@@ -564,6 +566,7 @@ def test_link_langfuse_prompts_stream():
564566
assert generations[1].time_to_first_token is not None
565567

566568

569+
@pytest.mark.skip("Flaky")
567570
def test_link_langfuse_prompts_batch():
568571
langfuse = Langfuse()
569572
trace_name = "test_link_langfuse_prompts_batch_" + create_uuid()[:8]
@@ -639,7 +642,7 @@ def test_link_langfuse_prompts_batch():
639642
key=lambda x: x.start_time,
640643
)
641644

642-
assert len(generations) == 6
645+
assert len(generations) == 10
643646

644647
assert generations[0].prompt_name == joke_prompt_name
645648
assert generations[1].prompt_name == joke_prompt_name
@@ -710,6 +713,7 @@ def test_get_langchain_chat_prompt_with_precompiled_prompt():
710713
assert user_message.content == "This is a langchain chain."
711714

712715

716+
@pytest.mark.skip("Flaky")
713717
def test_callback_openai_functions_with_tools():
714718
handler = CallbackHandler()
715719

@@ -856,7 +860,7 @@ def test_multimodal():
856860

857861
trace = get_api().trace.get(trace_id=trace_id)
858862

859-
assert len(trace.observations) == 2
863+
assert len(trace.observations) == 3
860864
# Filter for the observation with type GENERATION
861865
generation_observation = next(
862866
(obs for obs in trace.observations if obs.type == "GENERATION"), None

0 commit comments

Comments
 (0)