9201ef759e
Harness Compat / harness compat (push) Failing after 0s
CI / test on 3.12 (standard) (push) Has been cancelled
CI / test on 3.13 (standard) (push) Has been cancelled
CI / test on 3.14 (standard) (push) Has been cancelled
CI / test on 3.10 (all-extras) (push) Has been cancelled
CI / test on 3.11 (all-extras) (push) Has been cancelled
CI / test on 3.12 (all-extras) (push) Has been cancelled
CI / test on 3.14 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.10 (pydantic-evals) (push) Has been cancelled
CI / test on 3.11 (pydantic-evals) (push) Has been cancelled
CI / test on 3.12 (pydantic-evals) (push) Has been cancelled
CI / deploy-docs-preview (push) Has been cancelled
CI / build release artifacts (push) Has been cancelled
CI / publish to PyPI (push) Has been cancelled
CI / Send tweet (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / mypy (push) Has been cancelled
CI / docs (push) Has been cancelled
CI / test on 3.10 (standard) (push) Has been cancelled
CI / test on 3.11 (standard) (push) Has been cancelled
CI / test on 3.13 (all-extras) (push) Has been cancelled
CI / test on 3.14 (all-extras) (push) Has been cancelled
CI / test on 3.10 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.11 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.12 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.13 (pydantic-ai-slim) (push) Has been cancelled
CI / test on 3.13 (pydantic-evals) (push) Has been cancelled
CI / test on 3.14 (pydantic-evals) (push) Has been cancelled
CI / test on 3.10 (lowest-versions) (push) Has been cancelled
CI / test on 3.11 (lowest-versions) (push) Has been cancelled
CI / test on 3.12 (lowest-versions) (push) Has been cancelled
CI / test on 3.13 (lowest-versions) (push) Has been cancelled
CI / test on 3.14 (lowest-versions) (push) Has been cancelled
CI / test examples on 3.11 (push) Has been cancelled
CI / test examples on 3.12 (push) Has been cancelled
CI / test examples on 3.13 (push) Has been cancelled
CI / test examples on 3.14 (push) Has been cancelled
CI / coverage (push) Has been cancelled
CI / check (push) Has been cancelled
CI / deploy-docs (push) Has been cancelled
118 lines
4.2 KiB
Python
118 lines
4.2 KiB
Python
from __future__ import annotations as _annotations
|
|
|
|
import pytest
|
|
|
|
from pydantic_ai import ModelResponsePart, TextPart, ThinkingPart, ThinkingPartDelta
|
|
from pydantic_ai._thinking_part import split_content_into_text_and_thinking
|
|
|
|
from ._inline_snapshot import snapshot
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'thinking_tags, content, parts',
|
|
[
|
|
# default <think>…</think> cases
|
|
(
|
|
('<think>', '</think>'),
|
|
'foo bar',
|
|
[TextPart(content='foo bar')],
|
|
),
|
|
(
|
|
('<think>', '</think>'),
|
|
'foo bar<think>thinking</think>',
|
|
[TextPart(content='foo bar'), ThinkingPart(content='thinking')],
|
|
),
|
|
(
|
|
('<think>', '</think>'),
|
|
'foo bar<think>thinking</think>baz',
|
|
[
|
|
TextPart(content='foo bar'),
|
|
ThinkingPart(content='thinking'),
|
|
TextPart(content='baz'),
|
|
],
|
|
),
|
|
(
|
|
('<think>', '</think>'),
|
|
'foo bar<think>thinking',
|
|
[TextPart(content='foo bar'), TextPart(content='thinking')],
|
|
),
|
|
(
|
|
('<think>', '</think>'),
|
|
'foo bar<custom>thinking</custom>baz',
|
|
[TextPart(content='foo bar<custom>thinking</custom>baz')],
|
|
),
|
|
# custom <custom>…</custom> cases
|
|
(
|
|
('<custom>', '</custom>'),
|
|
'foo bar',
|
|
[TextPart(content='foo bar')],
|
|
),
|
|
(
|
|
('<custom>', '</custom>'),
|
|
'foo bar<custom>thinking</custom>',
|
|
[TextPart(content='foo bar'), ThinkingPart(content='thinking')],
|
|
),
|
|
(
|
|
('<custom>', '</custom>'),
|
|
'foo bar<custom>thinking</custom>baz',
|
|
[
|
|
TextPart(content='foo bar'),
|
|
ThinkingPart(content='thinking'),
|
|
TextPart(content='baz'),
|
|
],
|
|
),
|
|
(
|
|
('<custom>', '</custom>'),
|
|
'foo bar<custom>thinking',
|
|
[TextPart(content='foo bar'), TextPart(content='thinking')],
|
|
),
|
|
(
|
|
('<custom>', '</custom>'),
|
|
'foo bar<think>thinking</think>baz',
|
|
[TextPart(content='foo bar<think>thinking</think>baz')],
|
|
),
|
|
],
|
|
)
|
|
def test_split_content(thinking_tags: tuple[str, str], content: str, parts: list[ModelResponsePart]):
|
|
assert split_content_into_text_and_thinking(content, thinking_tags) == parts
|
|
|
|
|
|
def test_thinking_part_delta_applies_both_content_and_signature():
|
|
thinking_part = ThinkingPart(content='Initial content', signature='initial_sig')
|
|
delta = ThinkingPartDelta(content_delta=' added', signature_delta='new_sig')
|
|
|
|
result = delta.apply(thinking_part)
|
|
|
|
# The content is appended, and the signature is updated.
|
|
assert result == snapshot(ThinkingPart(content='Initial content added', signature='new_sig'))
|
|
|
|
|
|
def test_thinking_part_delta_applies_signature_only():
|
|
thinking_part = ThinkingPart(content='Initial content', signature='initial_sig')
|
|
delta_sig_only = ThinkingPartDelta(content_delta=None, signature_delta='sig_only')
|
|
|
|
result_sig_only = delta_sig_only.apply(thinking_part)
|
|
|
|
# The content is unchanged, and the signature is updated.
|
|
assert result_sig_only == snapshot(ThinkingPart(content='Initial content', signature='sig_only'))
|
|
|
|
|
|
def test_thinking_part_delta_applies_content_only_preserves_signature():
|
|
thinking_part = ThinkingPart(content='Initial content', signature='initial_sig')
|
|
delta_content_only = ThinkingPartDelta(content_delta=' more', signature_delta=None)
|
|
|
|
result_content_only = delta_content_only.apply(thinking_part)
|
|
|
|
# The content is appended, and the signature is preserved.
|
|
assert result_content_only == snapshot(ThinkingPart(content='Initial content more', signature='initial_sig'))
|
|
|
|
|
|
def test_thinking_part_delta_applies_to_part_with_none_signature():
|
|
thinking_part_no_sig = ThinkingPart(content='No sig content', signature=None)
|
|
delta_to_none_sig = ThinkingPartDelta(content_delta=' extra', signature_delta='added_sig')
|
|
|
|
result_none_sig = delta_to_none_sig.apply(thinking_part_no_sig)
|
|
|
|
# The content is appended, and the signature is updated.
|
|
assert result_none_sig == snapshot(ThinkingPart(content='No sig content extra', signature='added_sig'))
|