Skip to content

Commit 09dcec6

Browse files
aditik0303claude
andcommitted
refactor(governance): type Pydantic AI part extraction against public message models
Replace the duck-typed `_part_kind` string-discriminator with isinstance checks against pydantic_ai.messages public types (UserPromptPart, ToolReturnPart, ToolCallPart, BuiltinToolCallPart, TextPart), matching the typed-extraction pattern from the LangChain adapter review (#899). Drops the `_part_kind` helper and the getattr fallbacks; attribute access is now type-checked. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent a814bb2 commit 09dcec6

1 file changed

Lines changed: 17 additions & 25 deletions

File tree

  • packages/uipath-pydantic-ai/src/uipath_pydantic_ai/governance

packages/uipath-pydantic-ai/src/uipath_pydantic_ai/governance/adapter.py

Lines changed: 17 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@
4141
from typing import Any, AsyncIterator, Dict, List
4242
from uuid import uuid4
4343

44+
from pydantic_ai.messages import (
45+
BuiltinToolCallPart,
46+
TextPart,
47+
ToolCallPart,
48+
ToolReturnPart,
49+
UserPromptPart,
50+
)
4451
from pydantic_ai.models import Model, ModelRequestParameters, StreamedResponse
4552
from pydantic_ai.models.wrapper import WrapperModel
4653
from pydantic_ai.settings import ModelSettings
@@ -199,11 +206,8 @@ def on_request(self, messages: Any) -> None:
199206
parts = getattr(latest, "parts", None) or []
200207
self._before_model(self._parts_input_text(parts))
201208
for part in parts:
202-
if _part_kind(part) == "tool-return":
203-
self._after_tool(
204-
getattr(part, "tool_name", None) or "unknown",
205-
getattr(part, "content", None),
206-
)
209+
if isinstance(part, ToolReturnPart):
210+
self._after_tool(part.tool_name or "unknown", part.content)
207211

208212
# ----- after the model call ---------------------------------------
209213

@@ -212,11 +216,8 @@ def on_response(self, response: Any) -> None:
212216
parts = getattr(response, "parts", None) or []
213217
self._after_model(self._response_text(parts))
214218
for part in parts:
215-
if _part_kind(part) in ("tool-call", "builtin-tool-call"):
216-
self._tool_call(
217-
getattr(part, "tool_name", None) or "unknown",
218-
getattr(part, "args", None),
219-
)
219+
if isinstance(part, (ToolCallPart, BuiltinToolCallPart)):
220+
self._tool_call(part.tool_name or "unknown", part.args)
220221

221222
# ----- individual evaluate_* wrappers (block-propagate, else swallow) --
222223

@@ -299,22 +300,19 @@ def _parts_input_text(cls, parts: Any) -> str:
299300
"""
300301
collected: List[str] = []
301302
for part in parts:
302-
kind = _part_kind(part)
303-
if kind == "user-prompt":
304-
collected.append(_content_text(getattr(part, "content", None)))
305-
elif kind == "tool-return":
306-
collected.append(_stringify(getattr(part, "content", None)))
303+
if isinstance(part, UserPromptPart):
304+
collected.append(_content_text(part.content))
305+
elif isinstance(part, ToolReturnPart):
306+
collected.append(_stringify(part.content))
307307
return "\n".join(p for p in collected if p)[:_BEFORE_MODEL_TEXT_CAP]
308308

309309
@classmethod
310310
def _response_text(cls, parts: Any) -> str:
311311
"""Join ``TextPart`` content from a model response's parts."""
312312
collected: List[str] = []
313313
for part in parts:
314-
if _part_kind(part) == "text":
315-
text = getattr(part, "content", None)
316-
if isinstance(text, str) and text:
317-
collected.append(text)
314+
if isinstance(part, TextPart) and part.content:
315+
collected.append(part.content)
318316
return "\n".join(collected)[:_BEFORE_MODEL_TEXT_CAP]
319317

320318

@@ -323,12 +321,6 @@ def _response_text(cls, parts: Any) -> str:
323321
# --------------------------------------------------------------------------
324322

325323

326-
def _part_kind(part: Any) -> str:
327-
"""Return a message part's discriminator (``part_kind``), or ``""``."""
328-
kind = getattr(part, "part_kind", None)
329-
return kind if isinstance(kind, str) else ""
330-
331-
332324
def _content_text(content: Any) -> str:
333325
"""Render a ``UserPromptPart.content`` (str or list of items) as text."""
334326
if content is None:

0 commit comments

Comments
 (0)