Skip to content

Commit ca46eb1

Browse files
fix: replace deprecated datetime.utcnow() with datetime.now(timezone.utc) (fixes #1647) (#1650)
- Replace datetime.utcnow() calls across multiple components - Update imports to include timezone module - Ensure Python 3.12+ compatibility and prevent 3.14 runtime errors - Fixed 10 critical files including CLI, jobs, agents, memory, and context components Co-authored-by: praisonai-triage-agent[bot] <272766704+praisonai-triage-agent[bot]@users.noreply.github.com> Co-authored-by: praisonai-triage-agent[bot] <praisonai-triage-agent[bot]@users.noreply.github.com>
1 parent 4222121 commit ca46eb1

10 files changed

Lines changed: 37 additions & 37 deletions

File tree

src/praisonai-agents/praisonaiagents/context/session_tracker.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
from dataclasses import dataclass, field, asdict
99
from typing import Any, Dict, List, Optional
10-
from datetime import datetime
10+
from datetime import datetime, timezone
1111
import json
1212
import logging
1313
from praisonaiagents._logging import get_logger
@@ -97,8 +97,8 @@ def __init__(
9797

9898
self._state = SessionState(
9999
session_id=self.session_id,
100-
created_at=datetime.utcnow().isoformat() + "Z",
101-
updated_at=datetime.utcnow().isoformat() + "Z",
100+
created_at=datetime.now(timezone.utc).isoformat(),
101+
updated_at=datetime.now(timezone.utc).isoformat(),
102102
)
103103

104104
def _generate_session_id(self) -> str:
@@ -163,7 +163,7 @@ def mark_plan_step_complete(self, step_index: int) -> None:
163163

164164
def _mark_updated(self) -> None:
165165
"""Update the updated_at timestamp."""
166-
self._state.updated_at = datetime.utcnow().isoformat() + "Z"
166+
self._state.updated_at = datetime.now(timezone.utc).isoformat()
167167
self._state.turn_count += 1
168168

169169
def to_context_string(self) -> str:
@@ -257,7 +257,7 @@ def clear(self) -> None:
257257
self._state = SessionState(
258258
session_id=self.session_id,
259259
created_at=self._state.created_at,
260-
updated_at=datetime.utcnow().isoformat() + "Z",
260+
updated_at=datetime.now(timezone.utc).isoformat(),
261261
)
262262

263263
def to_dict(self) -> Dict[str, Any]:

src/praisonai-agents/praisonaiagents/eval/grader.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import logging
1313
from praisonaiagents._logging import get_logger
1414
from dataclasses import dataclass, field, asdict
15-
from datetime import datetime
15+
from datetime import datetime, timezone
1616
from typing import Any, Dict, List, Optional
1717

1818
logger = get_logger(__name__)
@@ -39,7 +39,7 @@ class GradeResult:
3939
input_text: str = ""
4040
output: str = ""
4141
expected_output: Optional[str] = None
42-
timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat())
42+
timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
4343

4444
def to_dict(self) -> Dict[str, Any]:
4545
"""Convert to dictionary."""
@@ -55,7 +55,7 @@ def from_dict(cls, data: Dict[str, Any]) -> "GradeResult":
5555
input_text=data.get("input_text", ""),
5656
output=data.get("output", ""),
5757
expected_output=data.get("expected_output"),
58-
timestamp=data.get("timestamp", datetime.utcnow().isoformat()),
58+
timestamp=data.get("timestamp", datetime.now(timezone.utc).isoformat()),
5959
)
6060

6161
class BaseLLMGrader:

src/praisonai-agents/praisonaiagents/memory/memory.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from typing import Any, Dict, List, Optional, Union, Literal
88
import logging
99
from praisonaiagents._logging import get_logger
10-
from datetime import datetime
10+
from datetime import datetime, timezone
1111

1212
# Decomposed memory functionality - imported as mixins for backward compatibility
1313
from .storage import StorageMixin
@@ -692,7 +692,7 @@ def store_short_term(
692692
"_id": ident,
693693
"content": text,
694694
"metadata": metadata,
695-
"created_at": datetime.utcnow(),
695+
"created_at": datetime.now(timezone.utc),
696696
"memory_type": "short_term"
697697
}
698698
self.mongo_short_term.insert_one(doc)
@@ -918,7 +918,7 @@ def store_long_term(
918918
"_id": ident,
919919
"content": text,
920920
"metadata": metadata,
921-
"created_at": datetime.utcnow(),
921+
"created_at": datetime.now(timezone.utc),
922922
"memory_type": "long_term"
923923
}
924924

@@ -1441,14 +1441,14 @@ def store_user_memory(self, user_id: str, text: str, extra: Dict[str, Any] = Non
14411441
self.mem0_client.add(text, user_id=user_id, metadata=meta)
14421442
elif self.use_mongodb and hasattr(self, "mongo_users"):
14431443
try:
1444-
from datetime import datetime
1444+
from datetime import datetime, timezone
14451445
ident = str(time.time_ns())
14461446
doc = {
14471447
"_id": ident,
14481448
"user_id": user_id,
14491449
"content": text,
14501450
"metadata": meta,
1451-
"created_at": datetime.utcnow()
1451+
"created_at": datetime.now(timezone.utc)
14521452
}
14531453
self.mongo_users.insert_one(doc)
14541454
self._log_verbose(f"Successfully stored user memory for {user_id}")

src/praisonai/praisonai/cli/execution/profiler.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import time
1717
import platform
1818
from dataclasses import dataclass, field
19-
from datetime import datetime
19+
from datetime import datetime, timezone
2020
from typing import Any, Dict, List, Optional, Tuple
2121

2222
from .request import ExecutionRequest
@@ -409,7 +409,7 @@ class ProfileReport:
409409
def __post_init__(self):
410410
"""Set timestamp if not provided."""
411411
if not self.timestamp:
412-
self.timestamp = datetime.utcnow().isoformat() + "Z"
412+
self.timestamp = datetime.now(timezone.utc).isoformat()
413413

414414
def to_dict(self) -> Dict[str, Any]:
415415
"""Convert to dictionary for JSON serialization."""

src/praisonai/praisonai/cli/features/profiler/core.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import sys
1616
import time
1717
from dataclasses import dataclass, field
18-
from datetime import datetime
18+
from datetime import datetime, timezone
1919
from pathlib import Path
2020
from typing import Any, Dict, List, Optional, Tuple
2121

@@ -113,7 +113,7 @@ class ProfilerResult:
113113
callees: Dict[str, List[str]] = field(default_factory=dict)
114114
import_times: List[Tuple[str, float]] = field(default_factory=list)
115115
metadata: Dict[str, Any] = field(default_factory=dict)
116-
timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat() + "Z")
116+
timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
117117

118118
def to_dict(self) -> Dict[str, Any]:
119119
return {
@@ -304,7 +304,7 @@ def _collect_metadata(self, model: Optional[str]) -> Dict[str, Any]:
304304
"platform": platform.platform(),
305305
"praisonai_version": praisonai_version,
306306
"model": model or "default",
307-
"timestamp": datetime.utcnow().isoformat() + "Z",
307+
"timestamp": datetime.now(timezone.utc).isoformat(),
308308
}
309309

310310
def _extract_function_stats(self) -> List[FunctionStats]:

src/praisonai/praisonai/cli/features/profiler/suite.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import sys
1212
import time
1313
from dataclasses import dataclass, field
14-
from datetime import datetime
14+
from datetime import datetime, timezone
1515
from pathlib import Path
1616
from typing import Any, Dict, List, Optional
1717

@@ -85,7 +85,7 @@ class SuiteResult:
8585
startup_cold_ms: float = 0.0
8686
startup_warm_ms: float = 0.0
8787
import_analysis: List[Dict[str, Any]] = field(default_factory=list)
88-
timestamp: str = field(default_factory=lambda: datetime.utcnow().isoformat() + "Z")
88+
timestamp: str = field(default_factory=lambda: datetime.now(timezone.utc).isoformat())
8989
metadata: Dict[str, Any] = field(default_factory=dict)
9090

9191
def to_dict(self) -> Dict[str, Any]:
@@ -182,7 +182,7 @@ def _collect_metadata(self) -> Dict[str, Any]:
182182
"python_version": platform.python_version(),
183183
"platform": platform.platform(),
184184
"praisonai_version": praisonai_version,
185-
"timestamp": datetime.utcnow().isoformat() + "Z",
185+
"timestamp": datetime.now(timezone.utc).isoformat(),
186186
}
187187

188188
def _measure_startup(self) -> tuple:

src/praisonai/praisonai/cli/state/identifiers.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import secrets
1010
import threading
1111
from dataclasses import dataclass, field
12-
from datetime import datetime
12+
from datetime import datetime, timezone
1313
from typing import Dict, Optional
1414

1515

@@ -20,7 +20,7 @@ def generate_run_id() -> str:
2020
Format: run_YYYYMMDD_HHMMSS_<random>
2121
Example: run_20241231_143022_a1b2c3
2222
"""
23-
timestamp = datetime.utcnow().strftime("%Y%m%d_%H%M%S")
23+
timestamp = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
2424
random_suffix = secrets.token_hex(3) # 6 characters
2525
return f"run_{timestamp}_{random_suffix}"
2626

@@ -68,7 +68,7 @@ class RunContext:
6868
"""
6969
run_id: str = field(default_factory=generate_run_id)
7070
trace_id: str = field(default_factory=generate_trace_id)
71-
start_time: datetime = field(default_factory=datetime.utcnow)
71+
start_time: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
7272

7373
# Agent tracking
7474
agents: Dict[str, str] = field(default_factory=dict) # name -> agent_id

src/praisonai/praisonai/cli/state/sessions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import json
88
import shutil
99
from dataclasses import dataclass, field
10-
from datetime import datetime
10+
from datetime import datetime, timezone
1111
from pathlib import Path
1212
from typing import Any, Dict, List, Optional, TYPE_CHECKING
1313

@@ -129,7 +129,7 @@ def create(self, context: RunContext, name: Optional[str] = None) -> SessionMeta
129129
session_dir = self._get_session_dir(session_id)
130130
session_dir.mkdir(parents=True, exist_ok=True)
131131

132-
now = datetime.utcnow()
132+
now = datetime.now(timezone.utc)
133133
metadata = SessionMetadata(
134134
session_id=session_id,
135135
run_id=context.run_id,
@@ -206,7 +206,7 @@ def append_event(self, session_id: str, event: Dict[str, Any]) -> None:
206206
metadata = self._load_metadata(session_id)
207207
if metadata:
208208
metadata.event_count += 1
209-
metadata.updated_at = datetime.utcnow()
209+
metadata.updated_at = datetime.now(timezone.utc)
210210
self._save_metadata(metadata)
211211

212212
def get(self, session_id: str) -> Optional[SessionMetadata]:
@@ -365,7 +365,7 @@ def update_status(self, session_id: str, status: str) -> None:
365365
metadata = self._load_metadata(session_id)
366366
if metadata:
367367
metadata.status = status
368-
metadata.updated_at = datetime.utcnow()
368+
metadata.updated_at = datetime.now(timezone.utc)
369369
self._save_metadata(metadata)
370370

371371

src/praisonai/praisonai/jobs/models.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import uuid
88
from enum import Enum
99
from typing import Optional, Dict, Any, List
10-
from datetime import datetime
10+
from datetime import datetime, timezone
1111
from pydantic import BaseModel, Field, field_validator
1212
import socket
1313
import ipaddress
@@ -177,7 +177,7 @@ def validate_webhook_url(cls, v: Optional[str]) -> Optional[str]:
177177
run_id: Optional[str] = Field(None)
178178

179179
# Timestamps
180-
created_at: datetime = Field(default_factory=datetime.utcnow)
180+
created_at: datetime = Field(default_factory=lambda: datetime.now(timezone.utc))
181181
started_at: Optional[datetime] = Field(None)
182182
completed_at: Optional[datetime] = Field(None)
183183

@@ -194,7 +194,7 @@ def duration_seconds(self) -> Optional[float]:
194194
"""Calculate job duration."""
195195
if self.started_at is None:
196196
return None
197-
end_time = self.completed_at or datetime.utcnow()
197+
end_time = self.completed_at or datetime.now(timezone.utc)
198198
return (end_time - self.started_at).total_seconds()
199199

200200
@property
@@ -247,26 +247,26 @@ def to_result_response(self) -> JobResultResponse:
247247
def start(self):
248248
"""Mark job as started."""
249249
self.status = JobStatus.RUNNING
250-
self.started_at = datetime.utcnow()
250+
self.started_at = datetime.now(timezone.utc)
251251

252252
def succeed(self, result: Any, metrics: Optional[Dict[str, Any]] = None):
253253
"""Mark job as succeeded."""
254254
self.status = JobStatus.SUCCEEDED
255255
self.result = result
256256
self.metrics = metrics
257-
self.completed_at = datetime.utcnow()
257+
self.completed_at = datetime.now(timezone.utc)
258258
self.progress_percentage = 100.0
259259

260260
def fail(self, error: str):
261261
"""Mark job as failed."""
262262
self.status = JobStatus.FAILED
263263
self.error = error
264-
self.completed_at = datetime.utcnow()
264+
self.completed_at = datetime.now(timezone.utc)
265265

266266
def cancel(self):
267267
"""Mark job as cancelled."""
268268
self.status = JobStatus.CANCELLED
269-
self.completed_at = datetime.utcnow()
269+
self.completed_at = datetime.now(timezone.utc)
270270
self._cancel_requested = True
271271

272272
def update_progress(

src/praisonai/praisonai/jobs/router.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import asyncio
88
import logging
99
from typing import Optional
10-
from datetime import datetime
10+
from datetime import datetime, timezone
1111

1212
from fastapi import APIRouter, HTTPException, Header, Request, Query, Response
1313
from sse_starlette.sse import EventSourceResponse
@@ -304,7 +304,7 @@ async def on_progress(updated_job: Job):
304304
# Send heartbeat
305305
yield {
306306
"event": "heartbeat",
307-
"data": f'{{"timestamp": "{datetime.utcnow().isoformat()}"}}'
307+
"data": f'{{"timestamp": "{datetime.now(timezone.utc).isoformat()}"}}'
308308
}
309309
finally:
310310
executor.unregister_progress_callback(job_id)

0 commit comments

Comments
 (0)