Files
EvoScientist-Multi/EvoScientist/middleware/runtime_context.py
T
2026-06-02 18:47:26 +01:00

87 lines
2.7 KiB
Python

"""Runtime context middleware for EvoScientist."""
from __future__ import annotations
from collections.abc import Awaitable, Callable
from datetime import datetime
from langchain.agents.middleware.types import (
AgentMiddleware,
ModelRequest,
ModelResponse,
)
RUNTIME_CONTEXT_TEMPLATE = """<runtime_context>
Current date: {date}
Local timezone: {timezone}
Use this context to resolve relative time references like today, yesterday, and
next week.
</runtime_context>"""
def _format_timezone(now: datetime) -> str:
"""Return a compact local timezone label for prompt injection."""
offset = now.utcoffset()
if offset is None:
return "local"
total_seconds = int(offset.total_seconds())
sign = "+" if total_seconds >= 0 else "-"
total_seconds = abs(total_seconds)
hours, remainder = divmod(total_seconds, 3600)
minutes = remainder // 60
offset_text = f"UTC{sign}{hours:02d}:{minutes:02d}"
name = now.tzname()
if name and name != offset_text:
return f"{name} ({offset_text})"
return offset_text
class RuntimeContextMiddleware(AgentMiddleware):
"""Inject per-turn runtime context into model calls."""
def __init__(self, *, now_fn: Callable[[], datetime] | None = None) -> None:
self._now_fn = now_fn or (lambda: datetime.now().astimezone())
def _runtime_context(self) -> str:
now = self._now_fn()
return RUNTIME_CONTEXT_TEMPLATE.format(
date=now.strftime("%Y-%m-%d"),
timezone=_format_timezone(now),
)
def modify_request(self, request: ModelRequest) -> ModelRequest:
"""Append runtime context to the system prompt."""
from deepagents.middleware._utils import append_to_system_message
new_system = append_to_system_message(
request.system_message,
self._runtime_context(),
)
return request.override(system_message=new_system)
def wrap_model_call(
self,
request: ModelRequest,
handler: Callable[[ModelRequest], ModelResponse],
) -> ModelResponse:
"""Inject runtime context before the sync model handler."""
return handler(self.modify_request(request))
async def awrap_model_call(
self,
request: ModelRequest,
handler: Callable[[ModelRequest], Awaitable[ModelResponse]],
) -> ModelResponse:
"""Inject runtime context before the async model handler."""
return await handler(self.modify_request(request))
def create_runtime_context_middleware(
*, now_fn: Callable[[], datetime] | None = None
) -> RuntimeContextMiddleware:
"""Build runtime-context middleware."""
return RuntimeContextMiddleware(now_fn=now_fn)