aidial-sdk 0.48.0.dev4__tar.gz → 0.48.0.dev5__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 (78) hide show
  1. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/PKG-INFO +1 -1
  2. aidial_sdk-0.48.0.dev5/aidial_sdk/translators/messages_to_chat_completions/chat_completions/_reasoning_content.py +95 -0
  3. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/dial_extensions.py +0 -25
  4. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/from_chat_completions.py +12 -17
  5. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/streaming.py +10 -25
  6. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/pyproject.toml +1 -1
  7. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/LICENSE +0 -0
  8. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/README.md +0 -0
  9. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/__init__.py +0 -0
  10. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/_errors.py +0 -0
  11. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/_pydantic/__init__.py +0 -0
  12. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/_pydantic/_compat.py +0 -0
  13. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/_pydantic/_model_config.py +0 -0
  14. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/application.py +0 -0
  15. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/__init__.py +0 -0
  16. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/_cache.py +0 -0
  17. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/_types.py +0 -0
  18. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/base.py +0 -0
  19. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/choice.py +0 -0
  20. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/choice_base.py +0 -0
  21. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/chunks.py +0 -0
  22. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/enums.py +0 -0
  23. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/form.py +0 -0
  24. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/function_call.py +0 -0
  25. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/function_tool_call.py +0 -0
  26. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/request.py +0 -0
  27. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/response.py +0 -0
  28. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/chat_completion/stage.py +0 -0
  29. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/deployment/__init__.py +0 -0
  30. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/deployment/_headers.py +0 -0
  31. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/deployment/configuration.py +0 -0
  32. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/deployment/from_request_mixin.py +0 -0
  33. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/deployment/rate.py +0 -0
  34. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/deployment/tokenize.py +0 -0
  35. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/deployment/truncate_prompt.py +0 -0
  36. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/embeddings/__init__.py +0 -0
  37. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/embeddings/base.py +0 -0
  38. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/embeddings/request.py +0 -0
  39. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/embeddings/response.py +0 -0
  40. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/exceptions.py +0 -0
  41. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/header_propagator.py +0 -0
  42. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/py.typed +0 -0
  43. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/pydantic/v2.py +0 -0
  44. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/pydantic_v1/__init__.py +0 -0
  45. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/response_headers.py +0 -0
  46. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/telemetry/__init__.py +0 -0
  47. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/telemetry/_context.py +0 -0
  48. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/telemetry/init.py +0 -0
  49. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/telemetry/types.py +0 -0
  50. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/__init__.py +0 -0
  51. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/backend/http.py +0 -0
  52. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/app.py +0 -0
  53. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/cache_breakpoints.py +0 -0
  54. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/reasoning.py +0 -0
  55. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/system_turns.py +0 -0
  56. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/to_chat_completions.py +0 -0
  57. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/settings.py +0 -0
  58. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/sse_stream.py +0 -0
  59. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/translators/messages_to_chat_completions/translation_state.py +0 -0
  60. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/__init__.py +0 -0
  61. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/_attachment.py +0 -0
  62. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/_cancel_scope.py +0 -0
  63. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/_content_stream.py +0 -0
  64. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/_disconnect_middleware.py +0 -0
  65. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/_indexed_list.py +0 -0
  66. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/_interpolation.py +0 -0
  67. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/_json_log_formatter.py +0 -0
  68. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/_logging.py +0 -0
  69. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/_logging_middleware.py +0 -0
  70. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/_reflection.py +0 -0
  71. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/env.py +0 -0
  72. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/errors.py +0 -0
  73. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/json.py +0 -0
  74. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/log_config.py +0 -0
  75. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/logging.py +0 -0
  76. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/merge_chunks.py +0 -0
  77. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/pydantic.py +0 -0
  78. {aidial_sdk-0.48.0.dev4 → aidial_sdk-0.48.0.dev5}/aidial_sdk/utils/streaming.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aidial-sdk
3
- Version: 0.48.0.dev4
3
+ Version: 0.48.0.dev5
4
4
  Summary: Framework to create applications and model adapters for AI DIAL
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE
@@ -0,0 +1,95 @@
1
+ import re
2
+ from dataclasses import dataclass
3
+ from typing import Literal
4
+
5
+ from openai.types.chat import ChatCompletionMessage
6
+ from openai.types.chat.chat_completion_chunk import ChoiceDelta
7
+
8
+ from aidial_sdk.translators.messages_to_chat_completions.chat_completions.dial_extensions import (
9
+ CustomContent,
10
+ parse_extras,
11
+ )
12
+
13
+ _Source = Literal["azure", "claude", "dial"]
14
+
15
+ _REASONING_STAGE = re.compile(r"think|thought|reason", re.IGNORECASE)
16
+
17
+
18
+ @dataclass
19
+ class ReasoningContent:
20
+ content: str | None
21
+ signature: str | None
22
+
23
+
24
+ class ReasoningContentProvider:
25
+ """
26
+ Reads the reasoning of a message, or of a stream of message chunks, from
27
+ `reasoning_content`, the Claude state and the DIAL reasoning stages. They
28
+ carry the same text, chunked differently: only the text none of them has
29
+ delivered before is returned.
30
+ """
31
+
32
+ def __init__(self) -> None:
33
+ # Every source is a prefix of the same text, so offsets suffice.
34
+ self._returned = 0
35
+ self._received: dict[_Source, int] = {
36
+ "azure": 0,
37
+ "claude": 0,
38
+ "dial": 0,
39
+ }
40
+ self._reasoning_stages: set[int | None] = set()
41
+
42
+ def get_reasoning_content(
43
+ self, message: ChatCompletionMessage | ChoiceDelta
44
+ ) -> ReasoningContent:
45
+ custom_content = parse_extras(message.model_extra).custom_content
46
+ thinking, signature = _get_claude_reasoning_content(custom_content)
47
+ content = (
48
+ self._unseen("azure", _get_azure_reasoning_content(message))
49
+ + self._unseen("claude", thinking)
50
+ + self._unseen("dial", self._dial_reasoning_content(custom_content))
51
+ )
52
+ return ReasoningContent(
53
+ content=content or None,
54
+ signature=signature or None,
55
+ )
56
+
57
+ def _unseen(self, source: _Source, text: str | None) -> str:
58
+ text = text or ""
59
+ start = self._received[source]
60
+ self._received[source] += len(text)
61
+ unseen = text[self._returned - start :]
62
+ self._returned = max(self._returned, self._received[source])
63
+ return unseen
64
+
65
+ def _dial_reasoning_content(
66
+ self, custom_content: CustomContent | None
67
+ ) -> str:
68
+ text = ""
69
+ for stage in (custom_content.stages if custom_content else None) or []:
70
+ # In a stream, only the first chunk of a stage carries its name.
71
+ if _is_reasoning_stage(stage.name):
72
+ self._reasoning_stages.add(stage.index)
73
+ if stage.index in self._reasoning_stages:
74
+ text += stage.content or ""
75
+ return text
76
+
77
+
78
+ def _get_azure_reasoning_content(
79
+ message: ChatCompletionMessage | ChoiceDelta,
80
+ ) -> str | None:
81
+ return (message.model_extra or {}).get("reasoning_content")
82
+
83
+
84
+ def _get_claude_reasoning_content(
85
+ custom_content: CustomContent | None,
86
+ ) -> tuple[str | None, str | None]:
87
+ state = custom_content.state if custom_content else None
88
+ for block in (state.claude_message_content if state else None) or []:
89
+ if block.type == "thinking":
90
+ return block.thinking, block.signature
91
+ return None, None
92
+
93
+
94
+ def _is_reasoning_stage(name: str | None) -> bool:
95
+ return name is not None and bool(_REASONING_STAGE.search(name))
@@ -1,4 +1,3 @@
1
- import re
2
1
  from collections.abc import Mapping
3
2
 
4
3
  from openai.types.chat.chat_completion_message import Annotation
@@ -7,8 +6,6 @@ from pydantic import ValidationError
7
6
  from aidial_sdk.utils.logging import logger as log
8
7
  from aidial_sdk.utils.pydantic import ExtraAllowModel
9
8
 
10
- _REASONING_STAGE = re.compile(r"think|thought|reason", re.IGNORECASE)
11
-
12
9
 
13
10
  class ClaudeMessageBlock(ExtraAllowModel):
14
11
  type: str | None = None
@@ -42,25 +39,3 @@ def parse_extras(model_extra: Mapping[str, object] | None) -> DialExtras:
42
39
  except ValidationError as e:
43
40
  log.error(f"Failed to parse DIAL extension fields: {e}")
44
41
  return DialExtras()
45
-
46
-
47
- def is_reasoning_stage(name: str | None) -> bool:
48
- return name is not None and bool(_REASONING_STAGE.search(name))
49
-
50
-
51
- def get_claude_thinking_block(
52
- custom_content: CustomContent | None,
53
- ) -> ClaudeMessageBlock | None:
54
- state = custom_content.state if custom_content else None
55
- for block in (state.claude_message_content if state else None) or []:
56
- if block.type == "thinking" and block.thinking:
57
- return block
58
- return None
59
-
60
-
61
- def get_dial_reasoning_content(custom_content: CustomContent | None) -> str:
62
- return "".join(
63
- stage.content or ""
64
- for stage in (custom_content.stages if custom_content else None) or []
65
- if is_reasoning_stage(stage.name)
66
- )
@@ -25,11 +25,8 @@ from openai.types.chat.chat_completion_message_tool_call import (
25
25
  )
26
26
  from openai.types.completion_usage import CompletionUsage, PromptTokensDetails
27
27
 
28
- from aidial_sdk.translators.messages_to_chat_completions.chat_completions.dial_extensions import (
29
- CustomContent,
30
- get_claude_thinking_block,
31
- get_dial_reasoning_content,
32
- parse_extras,
28
+ from aidial_sdk.translators.messages_to_chat_completions.chat_completions._reasoning_content import (
29
+ ReasoningContentProvider,
33
30
  )
34
31
  from aidial_sdk.translators.messages_to_chat_completions.translation_state import (
35
32
  TranslationState,
@@ -76,8 +73,7 @@ def _to_anthropic_content_blocks(
76
73
 
77
74
  content: list[AnthropicContentBlock] = []
78
75
 
79
- extras = parse_extras(message.model_extra)
80
- if thinking := to_anthropic_thinking_block(extras.custom_content):
76
+ if thinking := to_anthropic_thinking_block(message):
81
77
  content.append(thinking)
82
78
 
83
79
  content.extend(
@@ -100,17 +96,16 @@ def _text_block(text: str) -> TextBlock:
100
96
 
101
97
 
102
98
  def to_anthropic_thinking_block(
103
- custom_content: CustomContent | None,
99
+ message: ChatCompletionMessage,
104
100
  ) -> ThinkingBlock | None:
105
- if block := get_claude_thinking_block(custom_content):
106
- return ThinkingBlock(
107
- type="thinking",
108
- thinking=block.thinking or "",
109
- signature=block.signature or "",
110
- )
111
- if text := get_dial_reasoning_content(custom_content):
112
- return ThinkingBlock(type="thinking", thinking=text, signature="")
113
- return None
101
+ reasoning = ReasoningContentProvider().get_reasoning_content(message)
102
+ if reasoning.content is None:
103
+ return None
104
+ return ThinkingBlock(
105
+ type="thinking",
106
+ thinking=reasoning.content,
107
+ signature=reasoning.signature or "",
108
+ )
114
109
 
115
110
 
116
111
  def _citations(annotations: list[Annotation] | None) -> list[tuple[str, str]]:
@@ -16,9 +16,10 @@ from openai.types.chat import ChatCompletionChunk
16
16
  from openai.types.chat.chat_completion_chunk import Choice, ChoiceDeltaToolCall
17
17
  from openai.types.completion_usage import CompletionUsage
18
18
 
19
+ from aidial_sdk.translators.messages_to_chat_completions.chat_completions._reasoning_content import (
20
+ ReasoningContentProvider,
21
+ )
19
22
  from aidial_sdk.translators.messages_to_chat_completions.chat_completions.dial_extensions import (
20
- CustomContent,
21
- is_reasoning_stage,
22
23
  parse_extras,
23
24
  )
24
25
  from aidial_sdk.translators.messages_to_chat_completions.chat_completions.from_chat_completions import (
@@ -55,9 +56,8 @@ class ChatCompletionsToAnthropicStream(AnthropicStreamState):
55
56
  self.is_refusal = False
56
57
  self.has_tool_call = False
57
58
  self._translation_state = translation_state
58
- self._reasoning_stages: set[int | None] = set()
59
+ self._reasoning = ReasoningContentProvider()
59
60
  self._seen_citations: set[str] = set()
60
- self._signed = False
61
61
  self._tools: dict[int, int] = {}
62
62
 
63
63
  def handle(self, chunk: ChatCompletionChunk) -> Iterator[bytes]:
@@ -84,7 +84,12 @@ class ChatCompletionsToAnthropicStream(AnthropicStreamState):
84
84
 
85
85
  if choice.finish_reason:
86
86
  self.finish_reason = choice.finish_reason
87
- yield from self._on_custom_content(extras.custom_content)
87
+
88
+ reasoning = self._reasoning.get_reasoning_content(delta)
89
+ if reasoning.content:
90
+ yield from self._thinking_delta(reasoning.content)
91
+ if reasoning.signature:
92
+ yield from self._signature_delta(reasoning.signature)
88
93
 
89
94
  if text := delta.content:
90
95
  yield from self._emit_text(text)
@@ -106,26 +111,6 @@ class ChatCompletionsToAnthropicStream(AnthropicStreamState):
106
111
  for call in delta.tool_calls or []:
107
112
  yield from self._on_tool_call_delta(call)
108
113
 
109
- def _on_custom_content(
110
- self, custom_content: CustomContent | None
111
- ) -> Iterator[bytes]:
112
- if custom_content is None:
113
- return
114
-
115
- for stage in custom_content.stages or []:
116
- if is_reasoning_stage(stage.name):
117
- self._reasoning_stages.add(stage.index)
118
- if stage.index in self._reasoning_stages and stage.content:
119
- yield from self._thinking_delta(stage.content)
120
-
121
- if self._signed or custom_content.state is None:
122
- return
123
- for block in custom_content.state.claude_message_content or []:
124
- if block.type == "thinking" and block.signature:
125
- self._signed = True
126
- yield from self._signature_delta(block.signature)
127
- break
128
-
129
114
  def _thinking_delta(self, text: str) -> Iterator[bytes]:
130
115
  yield from self._close_tools()
131
116
  if not self.is_open(_THINKING_KEY):
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aidial-sdk"
3
- version = "0.48.0.dev4"
3
+ version = "0.48.0.dev5"
4
4
  description = "Framework to create applications and model adapters for AI DIAL"
5
5
  authors = [{ name = "EPAM RAIL", email = "SpecialEPM-DIALDevTeam@epam.com" }]
6
6
  license = "Apache-2.0"