Skip to content

Commit 5fae762

Browse files
committed
- fix coverage
- simplify tests (-> parametrized cases) - next: investigating potential dead tests and dead code
1 parent ac03e38 commit 5fae762

File tree

4 files changed

+183
-441
lines changed

4 files changed

+183
-441
lines changed

pydantic_ai_slim/pydantic_ai/_parts_manager.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
from pydantic_ai.exceptions import UnexpectedModelBehavior
2121
from pydantic_ai.messages import (
2222
BuiltinToolCallPart,
23+
BuiltinToolReturnPart,
24+
FilePart,
2325
ModelResponsePart,
2426
ModelResponseStreamEvent,
2527
PartDeltaEvent,
@@ -344,6 +346,10 @@ def _handle_text_delta_with_thinking_tags(
344346
part_index = self._vendor_id_to_part_index.get(vendor_part_id)
345347
existing_part = self._parts[part_index] if part_index is not None else None
346348

349+
# Strip leading whitespace if enabled and no existing part
350+
if ignore_leading_whitespace and not buffered and not existing_part:
351+
content = content.lstrip()
352+
347353
# If a TextPart has already been created for this vendor_part_id, disable thinking tag detection
348354
if existing_part is not None and isinstance(existing_part, TextPart):
349355
combined_content = buffered + content
@@ -367,12 +373,11 @@ def _handle_text_delta_with_thinking_tags(
367373
)
368374

369375
# Check for text before thinking tag - if so, treat entire combined content as text
376+
# this covers cases like `pre<think>` or `pre<thi`
370377
if segments and segments[0][0] == 'text':
371378
text_content = segments[0][1]
372-
if ignore_leading_whitespace and text_content.isspace():
373-
text_content = ''
374379

375-
if text_content:
380+
if text_content: # praga: no cover - line was always true
376381
combined_content = buffered + content
377382
self._thinking_tag_buffer.pop(vendor_part_id, None)
378383
yield from self._emit_text_part(
@@ -392,7 +397,7 @@ def _handle_text_delta_with_thinking_tags(
392397
and i + 1 < len(segments)
393398
and segments[i + 1][0] == 'start_tag'
394399
)
395-
if not skip_whitespace_before_tag:
400+
if not skip_whitespace_before_tag: # praga: no cover - line was always true (this is probably dead code, will remove after double checking)
396401
yield from self._emit_text_part(
397402
vendor_part_id=vendor_part_id,
398403
content=segment_content,
@@ -445,6 +450,7 @@ def _emit_text_part(
445450
latest_part = self._parts[part_index]
446451
if isinstance(latest_part, TextPart):
447452
existing_text_part_and_index = latest_part, part_index
453+
# else: existing_text_part_and_index remains None
448454
else:
449455
part_index = self._vendor_id_to_part_index.get(vendor_part_id)
450456
if part_index is not None:
@@ -453,6 +459,7 @@ def _emit_text_part(
453459
existing_text_part_and_index = existing_part, part_index
454460
else:
455461
raise UnexpectedModelBehavior(f'Cannot apply a text delta to {existing_part=}')
462+
# else: existing_text_part_and_index remains None
456463

457464
if existing_text_part_and_index is None:
458465
new_part_index = len(self._parts)
@@ -467,7 +474,9 @@ def _emit_text_part(
467474
part_delta = TextPartDelta(content_delta=content)
468475
updated_text_part = part_delta.apply(existing_text_part)
469476
self._parts[part_index] = updated_text_part
470-
if part_index not in self._started_part_indices:
477+
if (
478+
part_index not in self._started_part_indices
479+
): # pragma: no cover - TextPart should have already emitted PartStartEvent when created
471480
self._started_part_indices.add(part_index)
472481
yield PartStartEvent(index=part_index, part=updated_text_part)
473482
else:
@@ -516,6 +525,8 @@ def handle_thinking_delta(
516525
raise UnexpectedModelBehavior(
517526
'Cannot create ThinkingPart after TextPart: thinking must come before text in response'
518527
)
528+
else: # pragma: no cover - `handle_thinking_delta` should never be called when vendor_part_id is None the latest part is not a ThinkingPart or TextPart
529+
raise UnexpectedModelBehavior(f'Cannot apply a thinking delta to {latest_part=}')
519530
else:
520531
# Otherwise, attempt to look up an existing ThinkingPart by vendor_part_id
521532
part_index = self._vendor_id_to_part_index.get(vendor_part_id)
@@ -684,10 +695,7 @@ def handle_tool_call_part(
684695
return PartStartEvent(index=new_part_index, part=new_part)
685696

686697
def handle_part(
687-
self,
688-
*,
689-
vendor_part_id: Hashable | None,
690-
part: ModelResponsePart,
698+
self, *, vendor_part_id: Hashable | None, part: BuiltinToolCallPart | BuiltinToolReturnPart | FilePart
691699
) -> ModelResponseStreamEvent:
692700
"""Create or overwrite a ModelResponsePart.
693701

pydantic_ai_slim/pydantic_ai/models/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -582,7 +582,7 @@ def part_end_event(next_part: ModelResponsePart | None = None) -> PartEndEvent |
582582
yield event
583583

584584
# Flush any buffered content and stream finalize events
585-
for finalize_event in self._parts_manager.finalize():
585+
for finalize_event in self._parts_manager.finalize(): # pragma: no cover
586586
if isinstance(finalize_event, PartStartEvent):
587587
if last_start_event:
588588
end_event = part_end_event(finalize_event.part)

0 commit comments

Comments
 (0)