agno 2.3.6__py3-none-any.whl → 2.3.8__py3-none-any.whl

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.
agno/models/base.py CHANGED
@@ -5,7 +5,7 @@ from abc import ABC, abstractmethod
5
5
  from dataclasses import dataclass, field
6
6
  from hashlib import md5
7
7
  from pathlib import Path
8
- from time import time
8
+ from time import sleep, time
9
9
  from types import AsyncGeneratorType, GeneratorType
10
10
  from typing import (
11
11
  Any,
@@ -24,12 +24,13 @@ from uuid import uuid4
24
24
 
25
25
  from pydantic import BaseModel
26
26
 
27
- from agno.exceptions import AgentRunException
27
+ from agno.exceptions import AgentRunException, ModelProviderError
28
28
  from agno.media import Audio, File, Image, Video
29
29
  from agno.models.message import Citations, Message
30
30
  from agno.models.metrics import Metrics
31
31
  from agno.models.response import ModelResponse, ModelResponseEvent, ToolExecution
32
32
  from agno.run.agent import CustomEvent, RunContentEvent, RunOutput, RunOutputEvent
33
+ from agno.run.requirement import RunRequirement
33
34
  from agno.run.team import RunContentEvent as TeamRunContentEvent
34
35
  from agno.run.team import TeamRunOutput, TeamRunOutputEvent
35
36
  from agno.run.workflow import WorkflowRunOutputEvent
@@ -145,10 +146,133 @@ class Model(ABC):
145
146
  cache_ttl: Optional[int] = None
146
147
  cache_dir: Optional[str] = None
147
148
 
149
+ # Retry configuration for model provider errors
150
+ # Number of retries to attempt when a ModelProviderError occurs
151
+ retries: int = 0
152
+ # Delay between retries (in seconds)
153
+ delay_between_retries: int = 1
154
+ # Exponential backoff: if True, the delay between retries is doubled each time
155
+ exponential_backoff: bool = False
156
+
148
157
  def __post_init__(self):
149
158
  if self.provider is None and self.name is not None:
150
159
  self.provider = f"{self.name} ({self.id})"
151
160
 
161
+ def _get_retry_delay(self, attempt: int) -> float:
162
+ """Calculate the delay before the next retry attempt."""
163
+ if self.exponential_backoff:
164
+ return self.delay_between_retries * (2**attempt)
165
+ return self.delay_between_retries
166
+
167
+ def _invoke_with_retry(self, **kwargs) -> ModelResponse:
168
+ """
169
+ Invoke the model with retry logic for ModelProviderError.
170
+
171
+ This method wraps the invoke() call and retries on ModelProviderError
172
+ with optional exponential backoff.
173
+ """
174
+ last_exception: Optional[ModelProviderError] = None
175
+
176
+ for attempt in range(self.retries + 1):
177
+ try:
178
+ return self.invoke(**kwargs)
179
+ except ModelProviderError as e:
180
+ last_exception = e
181
+ if attempt < self.retries:
182
+ delay = self._get_retry_delay(attempt)
183
+ log_warning(
184
+ f"Model provider error (attempt {attempt + 1}/{self.retries + 1}): {e}. Retrying in {delay}s..."
185
+ )
186
+ sleep(delay)
187
+ else:
188
+ log_error(f"Model provider error after {self.retries + 1} attempts: {e}")
189
+
190
+ # If we've exhausted all retries, raise the last exception
191
+ raise last_exception # type: ignore
192
+
193
+ async def _ainvoke_with_retry(self, **kwargs) -> ModelResponse:
194
+ """
195
+ Asynchronously invoke the model with retry logic for ModelProviderError.
196
+
197
+ This method wraps the ainvoke() call and retries on ModelProviderError
198
+ with optional exponential backoff.
199
+ """
200
+ last_exception: Optional[ModelProviderError] = None
201
+
202
+ for attempt in range(self.retries + 1):
203
+ try:
204
+ return await self.ainvoke(**kwargs)
205
+ except ModelProviderError as e:
206
+ last_exception = e
207
+ if attempt < self.retries:
208
+ delay = self._get_retry_delay(attempt)
209
+ log_warning(
210
+ f"Model provider error (attempt {attempt + 1}/{self.retries + 1}): {e}. Retrying in {delay}s..."
211
+ )
212
+ await asyncio.sleep(delay)
213
+ else:
214
+ log_error(f"Model provider error after {self.retries + 1} attempts: {e}")
215
+
216
+ # If we've exhausted all retries, raise the last exception
217
+ raise last_exception # type: ignore
218
+
219
+ def _invoke_stream_with_retry(self, **kwargs) -> Iterator[ModelResponse]:
220
+ """
221
+ Invoke the model stream with retry logic for ModelProviderError.
222
+
223
+ This method wraps the invoke_stream() call and retries on ModelProviderError
224
+ with optional exponential backoff. Note that retries restart the entire stream.
225
+ """
226
+ last_exception: Optional[ModelProviderError] = None
227
+
228
+ for attempt in range(self.retries + 1):
229
+ try:
230
+ yield from self.invoke_stream(**kwargs)
231
+ return # Success, exit the retry loop
232
+ except ModelProviderError as e:
233
+ last_exception = e
234
+ if attempt < self.retries:
235
+ delay = self._get_retry_delay(attempt)
236
+ log_warning(
237
+ f"Model provider error during stream (attempt {attempt + 1}/{self.retries + 1}): {e}. "
238
+ f"Retrying in {delay}s..."
239
+ )
240
+ sleep(delay)
241
+ else:
242
+ log_error(f"Model provider error after {self.retries + 1} attempts: {e}")
243
+
244
+ # If we've exhausted all retries, raise the last exception
245
+ raise last_exception # type: ignore
246
+
247
+ async def _ainvoke_stream_with_retry(self, **kwargs) -> AsyncIterator[ModelResponse]:
248
+ """
249
+ Asynchronously invoke the model stream with retry logic for ModelProviderError.
250
+
251
+ This method wraps the ainvoke_stream() call and retries on ModelProviderError
252
+ with optional exponential backoff. Note that retries restart the entire stream.
253
+ """
254
+ last_exception: Optional[ModelProviderError] = None
255
+
256
+ for attempt in range(self.retries + 1):
257
+ try:
258
+ async for response in self.ainvoke_stream(**kwargs):
259
+ yield response
260
+ return # Success, exit the retry loop
261
+ except ModelProviderError as e:
262
+ last_exception = e
263
+ if attempt < self.retries:
264
+ delay = self._get_retry_delay(attempt)
265
+ log_warning(
266
+ f"Model provider error during stream (attempt {attempt + 1}/{self.retries + 1}): {e}. "
267
+ f"Retrying in {delay}s..."
268
+ )
269
+ await asyncio.sleep(delay)
270
+ else:
271
+ log_error(f"Model provider error after {self.retries + 1} attempts: {e}")
272
+
273
+ # If we've exhausted all retries, raise the last exception
274
+ raise last_exception # type: ignore
275
+
152
276
  def to_dict(self) -> Dict[str, Any]:
153
277
  fields = {"name", "id", "provider"}
154
278
  _dict = {field: getattr(self, field) for field in fields if getattr(self, field) is not None}
@@ -423,10 +547,23 @@ class Model(ABC):
423
547
  ]
424
548
  and function_call_response.tool_executions is not None
425
549
  ):
550
+ # Record the tool execution in the model response
426
551
  if model_response.tool_executions is None:
427
552
  model_response.tool_executions = []
428
553
  model_response.tool_executions.extend(function_call_response.tool_executions)
429
554
 
555
+ # If the tool is currently paused (HITL flow), add the requirement to the run response
556
+ if (
557
+ function_call_response.event == ModelResponseEvent.tool_call_paused.value
558
+ and run_response is not None
559
+ ):
560
+ current_tool_execution = function_call_response.tool_executions[-1]
561
+ if run_response.requirements is None:
562
+ run_response.requirements = []
563
+ run_response.requirements.append(
564
+ RunRequirement(tool_execution=current_tool_execution)
565
+ )
566
+
430
567
  elif function_call_response.event not in [
431
568
  ModelResponseEvent.tool_call_started.value,
432
569
  ModelResponseEvent.tool_call_completed.value,
@@ -615,6 +752,19 @@ class Model(ABC):
615
752
  if model_response.tool_executions is None:
616
753
  model_response.tool_executions = []
617
754
  model_response.tool_executions.extend(function_call_response.tool_executions)
755
+
756
+ # If the tool is currently paused (HITL flow), add the requirement to the run response
757
+ if (
758
+ function_call_response.event == ModelResponseEvent.tool_call_paused.value
759
+ and run_response is not None
760
+ ):
761
+ current_tool_execution = function_call_response.tool_executions[-1]
762
+ if run_response.requirements is None:
763
+ run_response.requirements = []
764
+ run_response.requirements.append(
765
+ RunRequirement(tool_execution=current_tool_execution)
766
+ )
767
+
618
768
  elif function_call_response.event not in [
619
769
  ModelResponseEvent.tool_call_started.value,
620
770
  ModelResponseEvent.tool_call_completed.value,
@@ -707,8 +857,8 @@ class Model(ABC):
707
857
  Returns:
708
858
  Tuple[Message, bool]: (assistant_message, should_continue)
709
859
  """
710
- # Generate response
711
- provider_response = self.invoke(
860
+ # Generate response with retry logic for ModelProviderError
861
+ provider_response = self._invoke_with_retry(
712
862
  assistant_message=assistant_message,
713
863
  messages=messages,
714
864
  response_format=response_format,
@@ -764,8 +914,8 @@ class Model(ABC):
764
914
  Returns:
765
915
  Tuple[Message, bool]: (assistant_message, should_continue)
766
916
  """
767
- # Generate response
768
- provider_response = await self.ainvoke(
917
+ # Generate response with retry logic for ModelProviderError
918
+ provider_response = await self._ainvoke_with_retry(
769
919
  messages=messages,
770
920
  response_format=response_format,
771
921
  tools=tools,
@@ -886,10 +1036,10 @@ class Model(ABC):
886
1036
  compress_tool_results: bool = False,
887
1037
  ) -> Iterator[ModelResponse]:
888
1038
  """
889
- Process a streaming response from the model.
1039
+ Process a streaming response from the model with retry logic for ModelProviderError.
890
1040
  """
891
1041
 
892
- for response_delta in self.invoke_stream(
1042
+ for response_delta in self._invoke_stream_with_retry(
893
1043
  messages=messages,
894
1044
  assistant_message=assistant_message,
895
1045
  response_format=response_format,
@@ -1105,9 +1255,9 @@ class Model(ABC):
1105
1255
  compress_tool_results: bool = False,
1106
1256
  ) -> AsyncIterator[ModelResponse]:
1107
1257
  """
1108
- Process a streaming response from the model.
1258
+ Process a streaming response from the model with retry logic for ModelProviderError.
1109
1259
  """
1110
- async for response_delta in self.ainvoke_stream(
1260
+ async for response_delta in self._ainvoke_stream_with_retry(
1111
1261
  messages=messages,
1112
1262
  assistant_message=assistant_message,
1113
1263
  response_format=response_format,
@@ -1115,7 +1265,7 @@ class Model(ABC):
1115
1265
  tool_choice=tool_choice or self._tool_choice,
1116
1266
  run_response=run_response,
1117
1267
  compress_tool_results=compress_tool_results,
1118
- ): # type: ignore
1268
+ ):
1119
1269
  for model_response_delta in self._populate_stream_data(
1120
1270
  stream_data=stream_data,
1121
1271
  model_response_delta=response_delta,
@@ -1706,7 +1856,7 @@ class Model(ABC):
1706
1856
 
1707
1857
  paused_tool_executions = []
1708
1858
 
1709
- # The function cannot be executed without user confirmation
1859
+ # The function requires user confirmation (HITL)
1710
1860
  if fc.function.requires_confirmation:
1711
1861
  paused_tool_executions.append(
1712
1862
  ToolExecution(
@@ -1716,7 +1866,8 @@ class Model(ABC):
1716
1866
  requires_confirmation=True,
1717
1867
  )
1718
1868
  )
1719
- # If the function requires user input, we yield a message to the user
1869
+
1870
+ # The function requires user input (HITL)
1720
1871
  if fc.function.requires_user_input:
1721
1872
  user_input_schema = fc.function.user_input_schema
1722
1873
  if fc.arguments and user_input_schema:
@@ -1734,7 +1885,8 @@ class Model(ABC):
1734
1885
  user_input_schema=user_input_schema,
1735
1886
  )
1736
1887
  )
1737
- # If the function is from the user control flow tools, we handle it here
1888
+
1889
+ # If the function is from the user control flow (HITL) tools, we handle it here
1738
1890
  if fc.function.name == "get_user_input" and fc.arguments and fc.arguments.get("user_input_fields"):
1739
1891
  user_input_schema = []
1740
1892
  for input_field in fc.arguments.get("user_input_fields", []):
@@ -1760,7 +1912,8 @@ class Model(ABC):
1760
1912
  user_input_schema=user_input_schema,
1761
1913
  )
1762
1914
  )
1763
- # If the function requires external execution, we yield a message to the user
1915
+
1916
+ # The function requires external execution (HITL)
1764
1917
  if fc.function.external_execution:
1765
1918
  paused_tool_executions.append(
1766
1919
  ToolExecution(
@@ -307,6 +307,8 @@ class OpenAIResponses(Model):
307
307
 
308
308
  def _upload_file(self, file: File) -> Optional[str]:
309
309
  """Upload a file to the OpenAI vector database."""
310
+ from pathlib import Path
311
+ from urllib.parse import urlparse
310
312
 
311
313
  if file.url is not None:
312
314
  file_content_tuple = file.file_url_content
@@ -314,13 +316,12 @@ class OpenAIResponses(Model):
314
316
  file_content = file_content_tuple[0]
315
317
  else:
316
318
  return None
317
- file_name = file.url.split("/")[-1]
319
+ file_name = Path(urlparse(file.url).path).name or "file"
318
320
  file_tuple = (file_name, file_content)
319
321
  result = self.get_client().files.create(file=file_tuple, purpose="assistants")
320
322
  return result.id
321
323
  elif file.filepath is not None:
322
324
  import mimetypes
323
- from pathlib import Path
324
325
 
325
326
  file_path = file.filepath if isinstance(file.filepath, Path) else Path(file.filepath)
326
327
  if file_path.exists() and file_path.is_file():
agno/models/response.py CHANGED
@@ -37,7 +37,7 @@ class ToolExecution:
37
37
 
38
38
  created_at: int = field(default_factory=lambda: int(time()))
39
39
 
40
- # User control flow requirements
40
+ # User control flow (HITL) fields
41
41
  requires_confirmation: Optional[bool] = None
42
42
  confirmed: Optional[bool] = None
43
43
  confirmation_note: Optional[str] = None
@@ -110,7 +110,7 @@ async def map_a2a_request_to_run_input(request_body: dict, stream: bool = True)
110
110
 
111
111
  Returns:
112
112
  RunInput: The Agno RunInput
113
- stream: Wheter we are in stream mode
113
+ stream: Whether we are in stream mode
114
114
  """
115
115
 
116
116
  # 1. Validate the request
agno/os/middleware/jwt.py CHANGED
@@ -188,18 +188,20 @@ class JWTMiddleware(BaseHTTPMiddleware):
188
188
 
189
189
  # Extract dependency claims
190
190
  dependencies = {}
191
- for claim in self.dependencies_claims:
192
- if claim in payload:
193
- dependencies[claim] = payload[claim]
191
+ if self.dependencies_claims:
192
+ for claim in self.dependencies_claims:
193
+ if claim in payload:
194
+ dependencies[claim] = payload[claim]
194
195
 
195
196
  if dependencies:
196
197
  request.state.dependencies = dependencies
197
198
 
198
199
  # Extract session state claims
199
200
  session_state = {}
200
- for claim in self.session_state_claims:
201
- if claim in payload:
202
- session_state[claim] = payload[claim]
201
+ if self.session_state_claims:
202
+ for claim in self.session_state_claims:
203
+ if claim in payload:
204
+ session_state[claim] = payload[claim]
203
205
 
204
206
  if session_state:
205
207
  request.state.session_state = session_state
@@ -36,7 +36,10 @@ async def run_accuracy_eval(
36
36
  model=default_model,
37
37
  )
38
38
 
39
- result = accuracy_eval.run(print_results=False, print_summary=False)
39
+ if isinstance(db, AsyncBaseDb):
40
+ result = await accuracy_eval.arun(print_results=False, print_summary=False)
41
+ else:
42
+ result = accuracy_eval.run(print_results=False, print_summary=False)
40
43
  if not result:
41
44
  raise HTTPException(status_code=500, detail="Failed to run accuracy evaluation")
42
45
 
@@ -86,7 +89,11 @@ async def run_performance_eval(
86
89
  model_id=model_id,
87
90
  model_provider=model_provider,
88
91
  )
89
- result = performance_eval.run(print_results=False, print_summary=False)
92
+
93
+ if isinstance(db, AsyncBaseDb):
94
+ result = await performance_eval.arun(print_results=False, print_summary=False)
95
+ else:
96
+ result = performance_eval.run(print_results=False, print_summary=False)
90
97
  if not result:
91
98
  raise HTTPException(status_code=500, detail="Failed to run performance evaluation")
92
99
 
@@ -141,7 +148,10 @@ async def run_reliability_eval(
141
148
  model_id = team.model.id if team and team.model else None
142
149
  model_provider = team.model.provider if team and team.model else None
143
150
 
144
- result = reliability_eval.run(print_results=False)
151
+ if isinstance(db, AsyncBaseDb):
152
+ result = await reliability_eval.arun(print_results=False)
153
+ else:
154
+ result = reliability_eval.run(print_results=False)
145
155
  if not result:
146
156
  raise HTTPException(status_code=500, detail="Failed to run reliability evaluation")
147
157
 
agno/run/agent.py CHANGED
@@ -11,6 +11,7 @@ from agno.models.metrics import Metrics
11
11
  from agno.models.response import ToolExecution
12
12
  from agno.reasoning.step import ReasoningStep
13
13
  from agno.run.base import BaseRunOutputEvent, MessageReferences, RunStatus
14
+ from agno.run.requirement import RunRequirement
14
15
  from agno.utils.log import logger
15
16
  from agno.utils.media import (
16
17
  reconstruct_audio_list,
@@ -273,11 +274,18 @@ class RunCompletedEvent(BaseAgentRunEvent):
273
274
  class RunPausedEvent(BaseAgentRunEvent):
274
275
  event: str = RunEvent.run_paused.value
275
276
  tools: Optional[List[ToolExecution]] = None
277
+ requirements: Optional[List[RunRequirement]] = None
276
278
 
277
279
  @property
278
280
  def is_paused(self):
279
281
  return True
280
282
 
283
+ @property
284
+ def active_requirements(self) -> List[RunRequirement]:
285
+ if not self.requirements:
286
+ return []
287
+ return [requirement for requirement in self.requirements if not requirement.is_resolved()]
288
+
281
289
 
282
290
  @dataclass
283
291
  class RunContinuedEvent(BaseAgentRunEvent):
@@ -539,11 +547,20 @@ class RunOutput:
539
547
 
540
548
  status: RunStatus = RunStatus.running
541
549
 
550
+ # User control flow (HITL) requirements to continue a run when paused, in order of arrival
551
+ requirements: Optional[list[RunRequirement]] = None
552
+
542
553
  # === FOREIGN KEY RELATIONSHIPS ===
543
554
  # These fields establish relationships to parent workflow/step structures
544
555
  # and should be treated as foreign keys for data integrity
545
556
  workflow_step_id: Optional[str] = None # FK: Points to StepOutput.step_id
546
557
 
558
+ @property
559
+ def active_requirements(self) -> list[RunRequirement]:
560
+ if not self.requirements:
561
+ return []
562
+ return [requirement for requirement in self.requirements if not requirement.is_resolved()]
563
+
547
564
  @property
548
565
  def is_paused(self):
549
566
  return self.status == RunStatus.paused
@@ -0,0 +1,98 @@
1
+ from dataclasses import dataclass
2
+ from datetime import datetime, timezone
3
+ from typing import TYPE_CHECKING, List, Optional
4
+ from uuid import uuid4
5
+
6
+ from agno.models.response import ToolExecution, UserInputField
7
+
8
+ if TYPE_CHECKING:
9
+ pass
10
+
11
+
12
+ @dataclass
13
+ class RunRequirement:
14
+ """Requirement to complete a paused run (used in HITL flows)"""
15
+
16
+ tool_execution: Optional[ToolExecution] = None
17
+ created_at: datetime = datetime.now(timezone.utc)
18
+
19
+ # User confirmation
20
+ confirmation: Optional[bool] = None
21
+ confirmation_note: Optional[str] = None
22
+
23
+ # User input
24
+ user_input_schema: Optional[List[UserInputField]] = None
25
+
26
+ # External execution
27
+ external_execution_result: Optional[str] = None
28
+
29
+ def __init__(self, tool_execution: ToolExecution):
30
+ self.id = str(uuid4())
31
+ self.tool_execution = tool_execution
32
+ self.user_input_schema = tool_execution.user_input_schema
33
+
34
+ @property
35
+ def needs_confirmation(self) -> bool:
36
+ if self.confirmation is not None:
37
+ return False
38
+ if not self.tool_execution:
39
+ return False
40
+ if self.tool_execution.confirmed is True:
41
+ return True
42
+
43
+ return self.tool_execution.requires_confirmation or False
44
+
45
+ @property
46
+ def needs_user_input(self) -> bool:
47
+ if not self.tool_execution:
48
+ return False
49
+ if self.tool_execution.answered is True:
50
+ return False
51
+ if self.user_input_schema and not all(field.value is not None for field in self.user_input_schema):
52
+ return True
53
+
54
+ return self.tool_execution.requires_user_input or False
55
+
56
+ @property
57
+ def needs_external_execution(self) -> bool:
58
+ if not self.tool_execution:
59
+ return False
60
+ if self.external_execution_result is not None:
61
+ return True
62
+
63
+ return self.tool_execution.external_execution_required or False
64
+
65
+ def confirm(self):
66
+ if not self.needs_confirmation:
67
+ raise ValueError("This requirement does not require confirmation")
68
+ self.confirmation = True
69
+ if self.tool_execution:
70
+ self.tool_execution.confirmed = True
71
+
72
+ def reject(self):
73
+ if not self.needs_confirmation:
74
+ raise ValueError("This requirement does not require confirmation")
75
+ self.confirmation = False
76
+ if self.tool_execution:
77
+ self.tool_execution.confirmed = False
78
+
79
+ def set_external_execution_result(self, result: str):
80
+ if not self.needs_external_execution:
81
+ raise ValueError("This requirement does not require external execution")
82
+ self.external_execution_result = result
83
+ if self.tool_execution:
84
+ self.tool_execution.result = result
85
+
86
+ def update_tool(self):
87
+ if not self.tool_execution:
88
+ return
89
+ if self.confirmation is True:
90
+ self.tool_execution.confirmed = True
91
+ elif self.confirmation is False:
92
+ self.tool_execution.confirmed = False
93
+ else:
94
+ raise ValueError("This requirement does not require confirmation or user input")
95
+
96
+ def is_resolved(self) -> bool:
97
+ """Return True if the requirement has been resolved"""
98
+ return not self.needs_confirmation and not self.needs_user_input and not self.needs_external_execution
agno/run/team.py CHANGED
@@ -12,6 +12,7 @@ from agno.models.response import ToolExecution
12
12
  from agno.reasoning.step import ReasoningStep
13
13
  from agno.run.agent import RunEvent, RunOutput, RunOutputEvent, run_output_event_from_dict
14
14
  from agno.run.base import BaseRunOutputEvent, MessageReferences, RunStatus
15
+ from agno.run.requirement import RunRequirement
15
16
  from agno.utils.log import log_error
16
17
  from agno.utils.media import (
17
18
  reconstruct_audio_list,
@@ -515,11 +516,20 @@ class TeamRunOutput:
515
516
 
516
517
  status: RunStatus = RunStatus.running
517
518
 
519
+ # User control flow (HITL) requirements to continue a run when paused, in order of arrival
520
+ requirements: Optional[list[RunRequirement]] = None
521
+
518
522
  # === FOREIGN KEY RELATIONSHIPS ===
519
523
  # These fields establish relationships to parent workflow/step structures
520
524
  # and should be treated as foreign keys for data integrity
521
525
  workflow_step_id: Optional[str] = None # FK: Points to StepOutput.step_id
522
526
 
527
+ @property
528
+ def active_requirements(self) -> list[RunRequirement]:
529
+ if not self.requirements:
530
+ return []
531
+ return [requirement for requirement in self.requirements if not requirement.is_resolved()]
532
+
523
533
  @property
524
534
  def is_paused(self):
525
535
  return self.status == RunStatus.paused