aidial-adapter-anthropic 0.1.0__tar.gz → 0.2.0rc0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/PKG-INFO +2 -2
  2. aidial_adapter_anthropic-0.2.0rc0/aidial_adapter_anthropic/__init__.py +5 -0
  3. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/_utils/json.py +1 -1
  4. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_base.py +1 -4
  5. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/adapter.py +1 -0
  6. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/config.py +4 -2
  7. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/state.py +2 -2
  8. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_decorator/base.py +2 -0
  9. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_decorator/preprocess.py +2 -0
  10. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_decorator/replicator.py +1 -1
  11. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_truncate_prompt.py +5 -2
  12. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/dial/_attachments.py +5 -6
  13. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/dial/consumer.py +1 -1
  14. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/dial/request.py +2 -5
  15. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/dial/resource.py +12 -12
  16. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/dial/storage.py +2 -1
  17. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/pyproject.toml +11 -3
  18. aidial_adapter_anthropic-0.1.0/aidial_adapter_anthropic/_utils/pydantic.py +0 -6
  19. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/LICENSE +0 -0
  20. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/README.md +0 -0
  21. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/_utils/list.py +0 -0
  22. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/_utils/resource.py +0 -0
  23. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/_utils/text.py +0 -0
  24. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/__init__.py +0 -0
  25. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/blocks.py +0 -0
  26. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/citations.py +0 -0
  27. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/converters.py +0 -0
  28. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/params.py +0 -0
  29. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/tokenizer/__init__.py +0 -0
  30. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/tokenizer/anthropic.py +0 -0
  31. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/tokenizer/approximate.py +0 -0
  32. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/tokenizer/base.py +0 -0
  33. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_claude/tools.py +0 -0
  34. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_errors.py +0 -0
  35. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/_tokenize.py +0 -0
  36. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/adapter/claude.py +0 -0
  37. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/dial/_lazy_stage.py +0 -0
  38. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/dial/_message.py +0 -0
  39. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/dial/token_usage.py +0 -0
  40. {aidial_adapter_anthropic-0.1.0 → aidial_adapter_anthropic-0.2.0rc0}/aidial_adapter_anthropic/dial/tools.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: aidial-adapter-anthropic
3
- Version: 0.1.0
3
+ Version: 0.2.0rc0
4
4
  Summary: Package implementing adapter from DIAL Chat Completions API to Anthropic API
5
5
  License: Apache-2.0
6
6
  Keywords: ai
@@ -12,7 +12,7 @@ Requires-Dist: aidial-sdk (>=0.28.0,<1)
12
12
  Requires-Dist: aiohttp (>=3.13.3,<4)
13
13
  Requires-Dist: anthropic (>=0.75.0,<1)
14
14
  Requires-Dist: pillow (>=10.4.0,<11)
15
- Requires-Dist: pydantic (>=1.10.17,<3)
15
+ Requires-Dist: pydantic (>=2.8.2,<3)
16
16
  Project-URL: Documentation, https://epam-rail.com/dial_api
17
17
  Project-URL: Homepage, https://epam-rail.com
18
18
  Project-URL: Repository, https://github.com/epam/ai-dial-adapter-anthropic/
@@ -0,0 +1,5 @@
1
+ import os
2
+ import warnings
3
+
4
+ if os.getenv("PYDANTIC_V2", "0").lower() not in ("1", "true"):
5
+ warnings.warn("PYDANTIC_V2 env variable is expected to be set to True")
@@ -57,7 +57,7 @@ def _to_dict(obj: Any, **kwargs) -> Any:
57
57
  return tuple(rec(element) for element in obj)
58
58
 
59
59
  if isinstance(obj, BaseModel):
60
- return rec(obj.dict())
60
+ return rec(obj.model_dump())
61
61
 
62
62
  if hasattr(obj, "to_dict"):
63
63
  return rec(obj.to_dict())
@@ -15,10 +15,7 @@ from aidial_adapter_anthropic.dial.request import (
15
15
  )
16
16
 
17
17
 
18
- class ChatCompletionAdapter(ABC, BaseModel):
19
- class Config:
20
- arbitrary_types_allowed = True
21
-
18
+ class ChatCompletionAdapter(ABC):
22
19
  @abstractmethod
23
20
  async def chat(
24
21
  self,
@@ -210,6 +210,7 @@ async def create_adapter(
210
210
  )(model)
211
211
 
212
212
 
213
+ @dataclass
213
214
  class Adapter(ChatCompletionAdapter):
214
215
  deployment: str
215
216
  storage: Optional[FileStorage]
@@ -2,9 +2,11 @@ from typing import List, Literal
2
2
 
3
3
  from anthropic.types.anthropic_beta_param import AnthropicBetaParam
4
4
  from anthropic.types.beta import BetaThinkingConfigParam as ThinkingConfigParam
5
- from pydantic import Field
5
+ from pydantic import BaseModel, ConfigDict, Field
6
6
 
7
- from aidial_adapter_anthropic._utils.pydantic import ExtraForbidModel
7
+
8
+ class ExtraForbidModel(BaseModel):
9
+ model_config = ConfigDict(extra="forbid")
8
10
 
9
11
 
10
12
  class ThinkingConfigEnabled(ExtraForbidModel):
@@ -21,7 +21,7 @@ class MessageState(BaseModel):
21
21
  claude_message_content: List[ParsedContentBlock] | List[ContentBlock]
22
22
 
23
23
  def to_dict(self) -> dict:
24
- return self.dict(
24
+ return self.model_dump(
25
25
  # FIXME: a hack to exclude the private __json_buf field
26
26
  exclude={"claude_message_content": {"__all__": {"__json_buf"}}},
27
27
  # Excluding `citations: null`, since they could not be even parsed
@@ -35,7 +35,7 @@ def get_message_content_from_state(
35
35
  ) -> List[ContentBlockParam] | None:
36
36
  if (cc := message.custom_content) and (state_dict := cc.state):
37
37
  try:
38
- state = MessageState.parse_obj(state_dict)
38
+ state = MessageState.model_validate(state_dict)
39
39
  return [block.to_dict() for block in state.claude_message_content] # type: ignore
40
40
  except pydantic.ValidationError as e:
41
41
  _log.error(
@@ -1,3 +1,4 @@
1
+ from dataclasses import dataclass
1
2
  from typing import Callable, List
2
3
 
3
4
  from aidial_sdk.chat_completion import Message
@@ -9,6 +10,7 @@ from aidial_adapter_anthropic.dial.consumer import Consumer
9
10
  from aidial_adapter_anthropic.dial.request import ModelParameters
10
11
 
11
12
 
13
+ @dataclass
12
14
  class ChatCompletionDecorator(ChatCompletionAdapter):
13
15
  adapter: ChatCompletionAdapter
14
16
 
@@ -1,3 +1,4 @@
1
+ from dataclasses import dataclass
1
2
  from typing import Callable, List
2
3
 
3
4
  from aidial_sdk.chat_completion import Message
@@ -20,6 +21,7 @@ def preprocess_messages_decorator(
20
21
  )
21
22
 
22
23
 
24
+ @dataclass
23
25
  class PreprocessMessagesDecorator(ChatCompletionDecorator):
24
26
  on_messages: Callable[[List[Message]], ListProjection[Message]]
25
27
 
@@ -22,7 +22,7 @@ class ReplicatorDecorator(ChatCompletionDecorator):
22
22
  params: ModelParameters,
23
23
  messages: List[Message],
24
24
  ) -> None:
25
- params1 = params.copy()
25
+ params1 = params.model_copy()
26
26
  params1.n = 1
27
27
 
28
28
  async def _chat(root_consumer: Consumer):
@@ -1,4 +1,5 @@
1
1
  from abc import ABC, abstractmethod
2
+ from dataclasses import dataclass
2
3
  from typing import Awaitable, Callable, List, Optional, Set, Tuple, TypeVar
3
4
 
4
5
  from aidial_sdk.exceptions import ContextLengthExceededError
@@ -7,7 +8,6 @@ from aidial_sdk.exceptions import (
7
8
  InvalidRequestError,
8
9
  TruncatePromptSystemAndLastUserError,
9
10
  )
10
- from pydantic import BaseModel
11
11
 
12
12
  from aidial_adapter_anthropic._utils.list import (
13
13
  omit_by_indices,
@@ -15,7 +15,7 @@ from aidial_adapter_anthropic._utils.list import (
15
15
  )
16
16
 
17
17
 
18
- class TruncatePromptError(ABC, BaseModel):
18
+ class TruncatePromptError(ABC):
19
19
  @abstractmethod
20
20
  def to_dial_exception(self) -> DialException:
21
21
  pass
@@ -24,6 +24,7 @@ class TruncatePromptError(ABC, BaseModel):
24
24
  return self.to_dial_exception().message
25
25
 
26
26
 
27
+ @dataclass
27
28
  class InconsistentLimitsError(TruncatePromptError):
28
29
  user_limit: int
29
30
  model_limit: int
@@ -35,6 +36,7 @@ class InconsistentLimitsError(TruncatePromptError):
35
36
  )
36
37
 
37
38
 
39
+ @dataclass
38
40
  class ModelLimitOverflow(TruncatePromptError):
39
41
  model_limit: int
40
42
  token_count: int
@@ -43,6 +45,7 @@ class ModelLimitOverflow(TruncatePromptError):
43
45
  return ContextLengthExceededError(self.model_limit, self.token_count)
44
46
 
45
47
 
48
+ @dataclass
46
49
  class UserLimitOverflow(TruncatePromptError):
47
50
  user_limit: int
48
51
  token_count: int
@@ -50,10 +50,8 @@ class HandlerWithConfig(Protocol, Generic[_T, _Config]):
50
50
  def __call__(self, resource: Resource, config: _Config | None) -> _T: ...
51
51
 
52
52
 
53
- class AttachmentProcessor(BaseModel, Generic[_T, _Config]):
54
- class Config:
55
- arbitrary_types_allowed = True
56
-
53
+ @dataclass
54
+ class AttachmentProcessor(Generic[_T, _Config]):
57
55
  supported_types: Dict[str, Set[str]]
58
56
  """MIME type to file extensions mapping"""
59
57
 
@@ -89,11 +87,12 @@ class WithResources(Generic[_T]):
89
87
  return WithResources(payload=payload, resources=resources)
90
88
 
91
89
 
92
- class AttachmentProcessors(BaseModel, Generic[_Txt, _T, _Config]):
93
- config: _Config | None = None
90
+ @dataclass
91
+ class AttachmentProcessors(Generic[_Txt, _T, _Config]):
94
92
  attachment_processors: Sequence[AttachmentProcessor[_T, _Config]]
95
93
  text_handler: Callable[[str], _Txt]
96
94
  file_storage: FileStorage | None
95
+ config: _Config | None = field(default=None)
97
96
 
98
97
  @property
99
98
  def supported_types(self) -> Dict[str, Set[str]]:
@@ -179,7 +179,7 @@ class ChoiceConsumer(Consumer):
179
179
  self._citations[document_id] = (display_index, document)
180
180
 
181
181
  if document:
182
- document = document.copy()
182
+ document = document.model_copy()
183
183
  document.title = f"[{display_index}] {document.title or ''}".strip()
184
184
  document.reference_type = document.reference_type or document.type
185
185
  document.reference_url = document.reference_url or document.url
@@ -20,7 +20,7 @@ from aidial_sdk.chat_completion.request import (
20
20
  )
21
21
  from aidial_sdk.exceptions import RequestValidationError
22
22
  from pydantic import BaseModel
23
- from pydantic.v1 import ValidationError as PydanticValidationError
23
+ from pydantic import ValidationError as PydanticValidationError
24
24
 
25
25
  from aidial_adapter_anthropic.adapter._errors import ValidationError
26
26
  from aidial_adapter_anthropic.dial.tools import (
@@ -82,9 +82,6 @@ class ModelParameters(BaseModel):
82
82
  configuration=configuration,
83
83
  )
84
84
 
85
- def add_stop_sequences(self, stop: List[str]) -> "ModelParameters":
86
- return self.copy(update={"stop": [*self.stop, *stop]})
87
-
88
85
  @property
89
86
  def tools_mode(self) -> ToolsMode | None:
90
87
  if self.tool_config is not None:
@@ -93,7 +90,7 @@ class ModelParameters(BaseModel):
93
90
 
94
91
  def parse_configuration(self, cls: Type[_Model]) -> _Model:
95
92
  try:
96
- return cls.parse_obj(self.configuration or {})
93
+ return cls.model_validate(self.configuration or {})
97
94
  except PydanticValidationError as e:
98
95
  if self.configuration is None:
99
96
  msg = "The configuration at path 'custom_fields.configuration' is missing."
@@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
4
4
  from typing import List
5
5
 
6
6
  from aidial_sdk.chat_completion import Attachment
7
- from pydantic import BaseModel, Field, root_validator, validator
7
+ from pydantic import BaseModel, Field, field_validator, model_validator
8
8
 
9
9
  from aidial_adapter_anthropic._utils.resource import Resource
10
10
  from aidial_adapter_anthropic._utils.text import truncate_string
@@ -34,7 +34,7 @@ class UnsupportedContentType(ValidationError):
34
34
 
35
35
 
36
36
  class DialResource(ABC, BaseModel):
37
- entity_name: str = Field(default=None)
37
+ entity_name: str | None = None
38
38
  supported_types: List[str] | None = Field(default=None)
39
39
 
40
40
  @abstractmethod
@@ -77,10 +77,10 @@ class URLResource(DialResource):
77
77
  def to_attachment(self) -> Attachment:
78
78
  return Attachment(type=self.content_type, url=self.url)
79
79
 
80
- @root_validator
81
- def validator(cls, values):
82
- values["entity_name"] = values.get("entity_name") or "URL"
83
- return values
80
+ @model_validator(mode="after")
81
+ def default_entity_name(self):
82
+ self.entity_name = self.entity_name or "URL"
83
+ return self
84
84
 
85
85
  async def download(self, storage: FileStorage | None) -> Resource:
86
86
  type = await self.get_content_type()
@@ -114,10 +114,10 @@ class AttachmentResource(DialResource):
114
114
  def to_attachment(self) -> Attachment:
115
115
  return self.attachment
116
116
 
117
- @validator("attachment", pre=True)
117
+ @field_validator("attachment", mode="before")
118
118
  def parse_attachment(cls, value):
119
119
  if isinstance(value, dict):
120
- attachment = Attachment.parse_obj(value)
120
+ attachment = Attachment.model_validate(value)
121
121
  # Working around the issue of defaulting missing type to a markdown:
122
122
  # https://github.com/epam/ai-dial-sdk/blob/2835107e950c89645a2b619fecba2518fa2d7bb1/aidial_sdk/chat_completion/request.py#L22
123
123
  if "type" not in value:
@@ -125,10 +125,10 @@ class AttachmentResource(DialResource):
125
125
  return attachment
126
126
  return value
127
127
 
128
- @root_validator(pre=True)
129
- def validator(cls, values):
130
- values["entity_name"] = values.get("entity_name") or "attachment"
131
- return values
128
+ @model_validator(mode="after")
129
+ def default_entity_name(self):
130
+ self.entity_name = self.entity_name or "attachment"
131
+ return self
132
132
 
133
133
  async def download(self, storage: FileStorage | None) -> Resource:
134
134
  type = await self.get_content_type()
@@ -3,11 +3,12 @@ import hashlib
3
3
  import io
4
4
  import logging
5
5
  import mimetypes
6
- from typing import Mapping, Optional, TypedDict
6
+ from typing import Mapping, Optional
7
7
  from urllib.parse import unquote, urljoin
8
8
 
9
9
  import aiohttp
10
10
  from pydantic import BaseModel
11
+ from typing_extensions import TypedDict
11
12
 
12
13
  _log = logging.getLogger(__name__)
13
14
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aidial-adapter-anthropic"
3
- version = "0.1.0"
3
+ version = "0.2.0rc"
4
4
  description = "Package implementing adapter from DIAL Chat Completions API to Anthropic API"
5
5
  authors = [{ name = "EPAM RAIL", email = "SpecialEPM-DIALDevTeam@epam.com" }]
6
6
  license = "Apache-2.0"
@@ -11,7 +11,7 @@ requires-python = ">=3.11,<4.0"
11
11
  dependencies = [
12
12
  "aidial-sdk (>=0.28.0,<1)",
13
13
  "anthropic (>=0.75.0,<1)",
14
- "pydantic (>=1.10.17,<3)",
14
+ "pydantic (>=2.8.2,<3)",
15
15
  "pillow (>=10.4.0,<11)",
16
16
  "aiohttp (>=3.13.3,<4)",
17
17
  ]
@@ -47,7 +47,15 @@ addopts = "-n=auto --asyncio-mode=auto --self-contained-html --html=pytest.html
47
47
  generate_report_on_test = true
48
48
  env_override_existing_values = 1
49
49
  testpaths = ["tests"]
50
- filterwarnings = ["error"]
50
+ filterwarnings = [
51
+ "error",
52
+ # "ignore::pydantic.warnings.PydanticDeprecatedSince20",
53
+ # "ignore::pytest.PytestUnraisableExceptionWarning",
54
+ # muting warnings coming from opentelemetry and pkg_resources packages
55
+ # "ignore::DeprecationWarning:opentelemetry.instrumentation.dependencies",
56
+ # "ignore::DeprecationWarning:pkg_resources",
57
+
58
+ ]
51
59
 
52
60
  [tool.pyright]
53
61
  typeCheckingMode = "basic"
@@ -1,6 +0,0 @@
1
- from pydantic import BaseModel
2
-
3
-
4
- class ExtraForbidModel(BaseModel):
5
- class Config:
6
- extra = "forbid"