aidial-sdk 0.48.0.dev5__tar.gz → 0.48.0.dev7__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 (80) hide show
  1. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/PKG-INFO +1 -1
  2. aidial_sdk-0.48.0.dev7/aidial_sdk/_constants.py +3 -0
  3. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/application.py +13 -14
  4. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/exceptions.py +6 -2
  5. aidial_sdk-0.48.0.dev7/aidial_sdk/translators/messages_to_chat_completions/backend/client.py +5 -0
  6. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/backend/http.py +16 -6
  7. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/app.py +4 -1
  8. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/streaming.py +9 -2
  9. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/streaming.py +43 -0
  10. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/pyproject.toml +1 -1
  11. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/LICENSE +0 -0
  12. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/README.md +0 -0
  13. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/__init__.py +0 -0
  14. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/_errors.py +0 -0
  15. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/_pydantic/__init__.py +0 -0
  16. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/_pydantic/_compat.py +0 -0
  17. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/_pydantic/_model_config.py +0 -0
  18. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/__init__.py +0 -0
  19. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/_cache.py +0 -0
  20. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/_types.py +0 -0
  21. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/base.py +0 -0
  22. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/choice.py +0 -0
  23. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/choice_base.py +0 -0
  24. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/chunks.py +0 -0
  25. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/enums.py +0 -0
  26. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/form.py +0 -0
  27. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/function_call.py +0 -0
  28. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/function_tool_call.py +0 -0
  29. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/request.py +0 -0
  30. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/response.py +0 -0
  31. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/chat_completion/stage.py +0 -0
  32. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/deployment/__init__.py +0 -0
  33. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/deployment/_headers.py +0 -0
  34. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/deployment/configuration.py +0 -0
  35. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/deployment/from_request_mixin.py +0 -0
  36. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/deployment/rate.py +0 -0
  37. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/deployment/tokenize.py +0 -0
  38. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/deployment/truncate_prompt.py +0 -0
  39. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/embeddings/__init__.py +0 -0
  40. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/embeddings/base.py +0 -0
  41. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/embeddings/request.py +0 -0
  42. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/embeddings/response.py +0 -0
  43. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/header_propagator.py +0 -0
  44. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/py.typed +0 -0
  45. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/pydantic/v2.py +0 -0
  46. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/pydantic_v1/__init__.py +0 -0
  47. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/response_headers.py +0 -0
  48. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/telemetry/__init__.py +0 -0
  49. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/telemetry/_context.py +0 -0
  50. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/telemetry/init.py +0 -0
  51. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/telemetry/types.py +0 -0
  52. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/__init__.py +0 -0
  53. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/_reasoning_content.py +0 -0
  54. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/cache_breakpoints.py +0 -0
  55. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/dial_extensions.py +0 -0
  56. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/from_chat_completions.py +0 -0
  57. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/reasoning.py +0 -0
  58. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/system_turns.py +0 -0
  59. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/chat_completions/to_chat_completions.py +0 -0
  60. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/settings.py +0 -0
  61. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/sse_stream.py +0 -0
  62. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/translators/messages_to_chat_completions/translation_state.py +0 -0
  63. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/__init__.py +0 -0
  64. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/_attachment.py +0 -0
  65. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/_cancel_scope.py +0 -0
  66. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/_content_stream.py +0 -0
  67. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/_disconnect_middleware.py +0 -0
  68. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/_indexed_list.py +0 -0
  69. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/_interpolation.py +0 -0
  70. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/_json_log_formatter.py +0 -0
  71. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/_logging.py +0 -0
  72. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/_logging_middleware.py +0 -0
  73. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/_reflection.py +0 -0
  74. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/env.py +0 -0
  75. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/errors.py +0 -0
  76. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/json.py +0 -0
  77. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/log_config.py +0 -0
  78. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/logging.py +0 -0
  79. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/merge_chunks.py +0 -0
  80. {aidial_sdk-0.48.0.dev5 → aidial_sdk-0.48.0.dev7}/aidial_sdk/utils/pydantic.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aidial-sdk
3
- Version: 0.48.0.dev5
3
+ Version: 0.48.0.dev7
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,3 @@
1
+ from aidial_sdk.utils.env import env_float
2
+
3
+ DIAL_SDK_SSE_HEARTBEAT_INTERVAL = env_float("DIAL_SDK_SSE_HEARTBEAT_INTERVAL")
@@ -9,6 +9,7 @@ from fastapi import FastAPI, HTTPException, Request
9
9
  from fastapi.responses import JSONResponse, Response, StreamingResponse
10
10
  from starlette.types import Receive, Scope, Send
11
11
 
12
+ from aidial_sdk._constants import DIAL_SDK_SSE_HEARTBEAT_INTERVAL
12
13
  from aidial_sdk._errors import (
13
14
  Handler,
14
15
  dial_exception_handler,
@@ -45,7 +46,7 @@ from aidial_sdk.utils._interpolation import (
45
46
  )
46
47
  from aidial_sdk.utils._logging_middleware import LoggingMiddleware
47
48
  from aidial_sdk.utils._reflection import get_method_implementation
48
- from aidial_sdk.utils.env import env_float, env_var_list
49
+ from aidial_sdk.utils.env import env_var_list
49
50
  from aidial_sdk.utils.log_config import configure_sdk_logger
50
51
  from aidial_sdk.utils.logging import (
51
52
  reset_log_context,
@@ -53,7 +54,7 @@ from aidial_sdk.utils.logging import (
53
54
  )
54
55
  from aidial_sdk.utils.pydantic import model_validate_extra_fields
55
56
  from aidial_sdk.utils.streaming import (
56
- add_heartbeat,
57
+ start_streaming,
57
58
  to_block_response,
58
59
  to_streaming_response,
59
60
  )
@@ -203,6 +204,10 @@ class DIALApp(FastAPI):
203
204
  *,
204
205
  heartbeat_interval: float | None = None,
205
206
  ) -> "DIALApp":
207
+ heartbeat_interval = (
208
+ heartbeat_interval or DIAL_SDK_SSE_HEARTBEAT_INTERVAL
209
+ )
210
+
206
211
  for endpoint, handler, methods in self._chat_completion_handlers(
207
212
  deployment_name, impl, heartbeat_interval
208
213
  ):
@@ -394,10 +399,6 @@ class DIALApp(FastAPI):
394
399
  *,
395
400
  heartbeat_interval: float | None,
396
401
  ) -> Handler:
397
- heartbeat_interval = heartbeat_interval or env_float(
398
- "DIAL_SDK_SSE_HEARTBEAT_INTERVAL"
399
- )
400
-
401
402
  async def _handler(original_request: Request) -> Response:
402
403
  request = await self._parse_request(
403
404
  ChatCompletionRequest, original_request, deployment_id
@@ -407,15 +408,13 @@ class DIALApp(FastAPI):
407
408
  stream = response._generate_stream(impl.chat_completion)
408
409
 
409
410
  if request.stream:
410
- if heartbeat_interval:
411
- stream = add_heartbeat(
412
- stream,
413
- heartbeat_interval=heartbeat_interval,
414
- heartbeat_object=": heartbeat\n\n",
415
- )
416
-
411
+ sse_stream = await start_streaming(
412
+ stream,
413
+ heartbeat_interval=heartbeat_interval,
414
+ heartbeat=": heartbeat\n\n",
415
+ )
417
416
  resp = StreamingResponse(
418
- await to_streaming_response(stream),
417
+ await to_streaming_response(sse_stream),
419
418
  media_type="text/event-stream",
420
419
  )
421
420
  else:
@@ -13,13 +13,16 @@ ExceptionType = Literal["openai", "anthropic"]
13
13
 
14
14
 
15
15
  class _AnthropicErrorType(Enum):
16
- INVALID_REQUEST = "invalid_request_error", 400, 422
16
+ INVALID_REQUEST = "invalid_request_error", 400
17
17
  AUTHENTICATION = "authentication_error", 401
18
+ BILLING = "billing_error", 402
18
19
  PERMISSION = "permission_error", 403
19
20
  NOT_FOUND = "not_found_error", 404
21
+ CONFLICT = "conflict_error", 409
20
22
  REQUEST_TOO_LARGE = "request_too_large", 413
21
23
  RATE_LIMIT = "rate_limit_error", 429
22
24
  API = "api_error", 500, 502
25
+ TIMEOUT = "timeout_error", 504
23
26
  OVERLOADED = "overloaded_error", 529, 503
24
27
 
25
28
  def __init__(self, code: str, *status_codes: int) -> None:
@@ -31,7 +34,8 @@ class _AnthropicErrorType(Enum):
31
34
  for error_type in cls:
32
35
  if status_code in error_type.status_codes:
33
36
  return error_type
34
- return cls.API
37
+ # Anthropic answers the other 4xx statuses with invalid_request_error
38
+ return cls.INVALID_REQUEST if 400 <= status_code < 500 else cls.API
35
39
 
36
40
 
37
41
  class HTTPException(Exception):
@@ -0,0 +1,5 @@
1
+ class Heartbeat:
2
+ """Yielded by a chat stream for each heartbeat interval it was idle."""
3
+
4
+
5
+ HEARTBEAT = Heartbeat()
@@ -10,10 +10,15 @@ from openai import AsyncOpenAI, AsyncStream, Omit, omit
10
10
  from openai.types.chat import ChatCompletion, ChatCompletionChunk
11
11
 
12
12
  from aidial_sdk.exceptions import HTTPException as DIALException
13
+ from aidial_sdk.translators.messages_to_chat_completions.backend.client import (
14
+ HEARTBEAT,
15
+ Heartbeat,
16
+ )
13
17
  from aidial_sdk.translators.messages_to_chat_completions.settings import (
14
18
  get_dial_api_version,
15
19
  )
16
20
  from aidial_sdk.utils.logging import logger as log
21
+ from aidial_sdk.utils.streaming import with_heartbeat
17
22
 
18
23
  # The headers of the inbound request that DIAL Core reads when the translator
19
24
  # calls it back. Any other header is not forwarded.
@@ -122,12 +127,16 @@ def _core_errors() -> Iterator[None]:
122
127
  raise _from_core_error(e) from e
123
128
 
124
129
 
125
- async def _chunks(
126
- stream: AsyncStream[ChatCompletionChunk],
127
- ) -> AsyncGenerator[ChatCompletionChunk, None]:
130
+ async def _with_heartbeat(
131
+ stream: AsyncStream[ChatCompletionChunk], heartbeat_interval: float | None
132
+ ) -> AsyncGenerator[ChatCompletionChunk | Heartbeat, None]:
128
133
  with _core_errors():
129
134
  async with stream:
130
- async for item in stream:
135
+ async for item in with_heartbeat(
136
+ aiter(stream),
137
+ heartbeat_interval=heartbeat_interval,
138
+ heartbeat=HEARTBEAT,
139
+ ):
131
140
  yield item
132
141
 
133
142
 
@@ -135,10 +144,11 @@ async def _chunks(
135
144
  class ChatClientHttp:
136
145
  client: AsyncOpenAI
137
146
  core_headers: dict[str, str | Omit]
147
+ heartbeat_interval: float | None
138
148
 
139
149
  async def create_stream(
140
150
  self, body: dict
141
- ) -> AsyncGenerator[ChatCompletionChunk, None]:
151
+ ) -> AsyncGenerator[ChatCompletionChunk | Heartbeat, None]:
142
152
  with _core_errors():
143
153
  stream = await self.client.post(
144
154
  "/chat/completions",
@@ -148,7 +158,7 @@ class ChatClientHttp:
148
158
  stream=True,
149
159
  stream_cls=AsyncStream[ChatCompletionChunk],
150
160
  )
151
- return _chunks(stream)
161
+ return _with_heartbeat(stream, self.heartbeat_interval)
152
162
 
153
163
  async def create_block(self, body: dict) -> ChatCompletion:
154
164
  with _core_errors():
@@ -8,6 +8,7 @@ from anthropic.types.beta.message_create_params import MessageCreateParams
8
8
  from fastapi import FastAPI, Request
9
9
  from fastapi.responses import JSONResponse, Response, StreamingResponse
10
10
 
11
+ from aidial_sdk._constants import DIAL_SDK_SSE_HEARTBEAT_INTERVAL
11
12
  from aidial_sdk._errors import error_handler
12
13
  from aidial_sdk.deployment.from_request_mixin import get_client_deployment_id
13
14
  from aidial_sdk.exceptions import InvalidRequestError, ResourceNotFoundError
@@ -94,7 +95,9 @@ def create_anthropic_api_app(
94
95
  else:
95
96
  client = core_v1_client(dial_url, http_client)
96
97
 
97
- return ChatClientHttp(client, core_headers(request))
98
+ return ChatClientHttp(
99
+ client, core_headers(request), DIAL_SDK_SSE_HEARTBEAT_INTERVAL
100
+ )
98
101
 
99
102
  @app.post("/anthropic/v1/messages")
100
103
  @error_handler("anthropic")
@@ -16,6 +16,9 @@ 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.backend.client import (
20
+ Heartbeat,
21
+ )
19
22
  from aidial_sdk.translators.messages_to_chat_completions.chat_completions._reasoning_content import (
20
23
  ReasoningContentProvider,
21
24
  )
@@ -60,10 +63,14 @@ class ChatCompletionsToAnthropicStream(AnthropicStreamState):
60
63
  self._seen_citations: set[str] = set()
61
64
  self._tools: dict[int, int] = {}
62
65
 
63
- def handle(self, chunk: ChatCompletionChunk) -> Iterator[bytes]:
66
+ def handle(self, chunk: ChatCompletionChunk | Heartbeat) -> Iterator[bytes]:
64
67
  if self.terminated:
65
68
  return
66
69
 
70
+ if isinstance(chunk, Heartbeat):
71
+ yield format_sse({"type": "ping"})
72
+ return
73
+
67
74
  if not self.started:
68
75
  self.message_id = chunk.id
69
76
  yield from self._start()
@@ -213,7 +220,7 @@ def _merge_usage(
213
220
 
214
221
 
215
222
  async def translate_stream(
216
- stream: AsyncGenerator[ChatCompletionChunk, None],
223
+ stream: AsyncGenerator[ChatCompletionChunk | Heartbeat, None],
217
224
  requested_model: str,
218
225
  translation_state: TranslationState,
219
226
  ) -> AsyncIterator[bytes]:
@@ -151,3 +151,46 @@ async def add_heartbeat(
151
151
 
152
152
  if heartbeat_callback is not None:
153
153
  await _call_heartbeat_callback(heartbeat_callback)
154
+
155
+
156
+ def with_heartbeat(
157
+ stream: AsyncIterator[_T],
158
+ *,
159
+ heartbeat_interval: float | None,
160
+ heartbeat: _T,
161
+ ) -> AsyncIterator[_T]:
162
+ if not heartbeat_interval:
163
+ return stream
164
+ return add_heartbeat(
165
+ stream,
166
+ heartbeat_interval=heartbeat_interval,
167
+ heartbeat_object=heartbeat,
168
+ )
169
+
170
+
171
+ async def start_streaming(
172
+ stream: AsyncIterator[_T],
173
+ *,
174
+ heartbeat_interval: float | None,
175
+ heartbeat: _T,
176
+ ) -> AsyncIterator[_T]:
177
+ """
178
+ Waits for the first item of the stream or, while it is idle, the first
179
+ heartbeat. A DIALException as the first item is raised, so that it becomes
180
+ an HTTP error, while a later one is left in the stream.
181
+ """
182
+
183
+ stream = with_heartbeat(
184
+ stream, heartbeat_interval=heartbeat_interval, heartbeat=heartbeat
185
+ )
186
+
187
+ first_item = await anext(stream)
188
+ if isinstance(first_item, DIALException):
189
+ raise first_item
190
+
191
+ async def _stream() -> AsyncIterator[_T]:
192
+ yield first_item
193
+ async for item in stream:
194
+ yield item
195
+
196
+ return _stream()
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aidial-sdk"
3
- version = "0.48.0.dev5"
3
+ version = "0.48.0.dev7"
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"