SQLAlchemySession on PostgreSQL returns items out of order and pops the wrong item when created_at is not monotonic

未关闭 适合新手
#5,459 4 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

维护者通常 1 天内回复

还没有人认领这个 Issue。

评估

难度
2/5
预计耗时
1-3 小时
新手友好度
85/100
Issue 类型
缺陷
描述清晰度
描述清楚
活跃度
活跃
技术栈
postgresql, python, sqlalchemy
领域
databases

调研方向

从 src/agents/extensions/memory/sqlalchemy_session.py 开始,其中 get_items 和 pop_item 按 created_at, id 排序。阅读 created_at 的 Column 定义以及同步和异步方法中的 order_by 调用。将排序改为仅按 id,或将时间戳存储为 UTC,然后运行 issue 中的复现脚本,确认 turn 按追加顺序返回。完成意味着 get_items 和 pop_item 与 SQLiteSession 的行为一致。

由索引模型根据 Issue 内容生成。

描述

Please read this first
  • Have you read the docs? Agents SDK docs
    Yes
  • Have you searched for related issues? Others may have faced similar issues.
    Yes. #1900 fixed ties between equal timestamps by adding id as a tie-breaker. This report is about timestamps that go backwards.
Describe the bug

Currently, SQLAlchemySession.get_items and pop_item order rows by created_at first and id second. The column is TIMESTAMP(timezone=False) with server_default=CURRENT_TIMESTAMP. On PostgreSQL that value is the transaction start time, converted to the wall clock of the connection's TimeZone setting. It does not follow the order in which rows were appended:

  • writers whose connections use different TimeZone settings write timestamps that differ by the UTC offset;
  • on a server whose time zone observes daylight saving time, the wall clock repeats an hour when the clocks fall back, so rows written during the repeated hour can sort before rows written in the hour just before it.

In each case get_items() returns turns out of order, get_items(limit=1) returns an item that is not the latest, and pop_item() deletes and returns an item that is not the most recent. #1900 shows what such a reordering does to the next run: a reasoning item that sorts after its message makes the Responses API reject the input with Item 'msg_...' of type 'message' was provided without its required 'reasoning' item. SQLiteSession and AsyncSQLiteSession order by id and return the append order for the same calls. The Session docstrings say get_items returns items "in chronological order" and pop_item removes "the most recent item".

The same ordering is on main (src/agents/extensions/memory/sqlalchemy_session.py):

            Column(
                "created_at",
                TIMESTAMP(timezone=False),
                server_default=sql_text("CURRENT_TIMESTAMP"),
                nullable=False,
            ),
                .order_by(
                    self._messages.c.created_at.desc(),
                    self._messages.c.id.desc(),
                )
Debug information
  • Agents SDK version: 0.24.0
  • Related library versions (optional, e.g. any-llm, litellm, or pydantic): SQLAlchemy 2.1.6, asyncpg 0.32.0, PostgreSQL 17.11
  • Python version: 3.12.13
  • Operating system: macOS 26.6.2, arm64 (Darwin 25.6.0)
  • Model and model provider: none (session API only)
  • Does the issue reproduce with the latest Agents SDK release? Yes, 0.24.0; main has the same code.
  • Does the issue occur consistently or intermittently? Consistently with the script below.
Repro steps
import asyncio
from sqlalchemy.ext.asyncio import create_async_engine
from agents.extensions.memory import SQLAlchemySession

URL = "postgresql+asyncpg://postgres@localhost:5432/postgres"


async def main():
    # two app instances whose connections use different TimeZone settings
    utc = create_async_engine(URL, connect_args={"server_settings": {"timezone": "UTC"}})
    toronto = create_async_engine(URL, connect_args={"server_settings": {"timezone": "America/Toronto"}})
    a = SQLAlchemySession("conv", engine=utc, create_tables=True)
    b = SQLAlchemySession("conv", engine=toronto, create_tables=True)
    await a.clear_session()

    await a.add_items([{"role": "user", "content": "turn 1"}])
    await a.add_items([{"role": "user", "content": "turn 2"}])
    await b.add_items([{"role": "user", "content": "turn 3"}])

    print([i["content"] for i in await a.get_items()])
    # actual:   ['turn 3', 'turn 1', 'turn 2']
    # expected: ['turn 1', 'turn 2', 'turn 3']
    print([i["content"] for i in await a.get_items(limit=1)])
    # actual:   ['turn 2']
    # expected: ['turn 3']
    print((await a.pop_item())["content"])
    # actual:   turn 2
    # expected: turn 3


asyncio.run(main())

With both engines set to the same TimeZone, the output is the expected one. A server whose own time zone observes daylight saving time behaves like the second engine after the clocks fall back. With TimeZone set to America/Toronto, SELECT '2026-11-01 05:59:00+00'::TIMESTAMPTZ::TIMESTAMP, '2026-11-01 06:00:00+00'::TIMESTAMPTZ::TIMESTAMP returns 2026-11-01 01:59:00 and 2026-11-01 01:00:00: a row written one minute later gets a created_at 59 minutes earlier.

Expected behavior

get_items returns items in the order they were added, and pop_item removes the last item added, as SQLiteSession does for the same calls. Storing created_at as TIMESTAMP WITH TIME ZONE (or always in UTC) keeps the current created_at, id ordering correct; ordering by id alone also gives that order.

主要语言
Python
星标
30.4k
派生
4.9k
平均合并
16 小时 32 分钟
30 天内合并 PR
85

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

openai/openai-agents-python 的其他 Issue

查看 openai/openai-agents-python 的全部 Issue

相似的 Issue

更多 Python Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。