Skip to content

Commit 771d81e

Browse files
committed
fix: minor fixes
1 parent 5e0b7c7 commit 771d81e

12 files changed

Lines changed: 113 additions & 28 deletions

File tree

.vscode/settings.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

plans/rebase-resolve-message-title-learning-space-conflict.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,6 @@
2020
- [x] `message.py` contains no `<<<<<<<`, `=======`, `>>>>>>>` markers.
2121
- [x] `process_session_pending_message` keeps title generation block and learning-space lookup block.
2222
- [x] `python3 -m py_compile src/server/core/acontext_core/service/controller/message.py` passes.
23+
24+
## validation notes
25+
- `pytest -q src/server/core/tests/service/test_process_session_pending_message.py` could not run in this environment because the local `pytest` installation does not provide `pytest.PytestRemovedIn9Warning`, while the repo warning filter expects it.

src/client/acontext-py/src/acontext/types/session.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ class Session(BaseModel):
177177
disable_task_tracking: bool = Field(
178178
False, description="Whether task tracking is disabled for this session"
179179
)
180+
display_title: str | None = Field(
181+
None, description="Optional generated display title for the session"
182+
)
180183
configs: dict[str, Any] | None = Field(
181184
None, description="Session configuration dictionary"
182185
)

src/client/acontext-py/tests/test_async_client.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,26 @@ async def test_async_sessions_create_with_use_uuid_and_user(
150150
assert kwargs["json_data"]["configs"] == {"agent": "bot1"}
151151

152152

153+
@patch("acontext.async_client.AcontextAsyncClient.request", new_callable=AsyncMock)
154+
@pytest.mark.asyncio
155+
async def test_async_sessions_create_parses_display_title(
156+
mock_request, async_client: AcontextAsyncClient
157+
) -> None:
158+
"""Test that display_title from API is available on Session model."""
159+
mock_request.return_value = {
160+
"id": "session-id",
161+
"project_id": "project-id",
162+
"display_title": "Plan migration rollout",
163+
"configs": {},
164+
"created_at": "2024-01-01T00:00:00Z",
165+
"updated_at": "2024-01-01T00:00:00Z",
166+
}
167+
168+
session = await async_client.sessions.create()
169+
170+
assert session.display_title == "Plan migration rollout"
171+
172+
153173
@patch("acontext.async_client.AcontextAsyncClient.request", new_callable=AsyncMock)
154174
@pytest.mark.asyncio
155175
async def test_async_store_message_with_files_uses_multipart_payload(

src/client/acontext-py/tests/test_client.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,25 @@ def test_sessions_create_with_use_uuid_and_user(
461461
assert kwargs["json_data"]["configs"] == {"agent": "bot1"}
462462

463463

464+
@patch("acontext.client.AcontextClient.request")
465+
def test_sessions_create_parses_display_title(
466+
mock_request, client: AcontextClient
467+
) -> None:
468+
"""Test that display_title from API is available on Session model."""
469+
mock_request.return_value = {
470+
"id": "session-id",
471+
"project_id": "project-id",
472+
"display_title": "Plan migration rollout",
473+
"configs": {},
474+
"created_at": "2024-01-01T00:00:00Z",
475+
"updated_at": "2024-01-01T00:00:00Z",
476+
}
477+
478+
session = client.sessions.create()
479+
480+
assert session.display_title == "Plan migration rollout"
481+
482+
464483
@patch("acontext.client.AcontextClient.request")
465484
def test_sessions_list_filter_by_configs(mock_request, client: AcontextClient) -> None:
466485
"""Test that filter_by_configs is JSON-encoded and sent to API."""

src/client/acontext-ts/src/types/session.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ export const SessionSchema = z.object({
5050
project_id: z.string(),
5151
user_id: z.string().nullable().optional(),
5252
disable_task_tracking: z.boolean(),
53+
display_title: z.string().nullable().optional(),
5354
configs: z.record(z.string(), z.unknown()).nullable(),
5455
created_at: z.string(),
5556
updated_at: z.string(),

src/client/acontext-ts/tests/client.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,16 @@ describe('AcontextClient Unit Tests', () => {
125125
expect(session.id).toBe(customUuid);
126126
});
127127

128+
test('should parse display_title in session response', async () => {
129+
const createdSession = mockSession({
130+
display_title: 'Plan migration rollout',
131+
});
132+
client.mock().onPost('/session', () => createdSession);
133+
134+
const session = await client.sessions.create();
135+
expect(session.display_title).toBe('Plan migration rollout');
136+
});
137+
128138
test('should store a message in acontext format', async () => {
129139
const sessionId = 'test-session-id';
130140
const storedMessage = mockMessage({

src/client/acontext-ts/tests/mocks.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ export function mockSession(overrides?: Partial<{
199199
project_id: string;
200200
user_id: string | null;
201201
disable_task_tracking: boolean;
202+
display_title: string | null;
202203
configs: Record<string, unknown> | null;
203204
created_at: string;
204205
updated_at: string;
@@ -209,6 +210,7 @@ export function mockSession(overrides?: Partial<{
209210
project_id: overrides?.project_id ?? mockId(),
210211
user_id: overrides?.user_id ?? null,
211212
disable_task_tracking: overrides?.disable_task_tracking ?? false,
213+
display_title: overrides?.display_title ?? null,
212214
configs: overrides?.configs ?? {},
213215
created_at: overrides?.created_at ?? now,
214216
updated_at: overrides?.updated_at ?? now,

src/server/core/acontext_core/infra/db.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from ..schema.orm import ORM_BASE
1919
from ..env import LOG as logger
2020
from ..env import DEFAULT_CORE_CONFIG
21+
from .schema_migrations import apply_runtime_schema_patches
2122

2223

2324
class DatabaseClient:
@@ -196,9 +197,16 @@ async def create_tables(self) -> None:
196197
logger.info("pgvector extension init")
197198
async with self.engine.begin() as conn:
198199
await conn.run_sync(ORM_BASE.metadata.create_all)
200+
await self._apply_schema_migrations()
199201

200202
self._table_created = True
201203

204+
async def _apply_schema_migrations(self) -> None:
205+
"""Apply idempotent schema patches for existing deployments."""
206+
async with self.get_session_context() as db_session:
207+
patch_names = await apply_runtime_schema_patches(db_session)
208+
logger.info(f"Schema patches ensured: {', '.join(patch_names)}")
209+
202210
async def drop_tables(self) -> None:
203211
"""Drop all tables defined in the ORM models."""
204212
async with self.engine.begin() as conn:
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from sqlalchemy import text
2+
from sqlalchemy.ext.asyncio import AsyncSession
3+
4+
DISPLAY_TITLE_COLUMN_PATCH_NAME = "sessions.display_title"
5+
DISPLAY_TITLE_COLUMN_PATCH_SQL = text(
6+
"ALTER TABLE sessions ADD COLUMN IF NOT EXISTS display_title TEXT;"
7+
)
8+
9+
10+
async def apply_runtime_schema_patches(db_session: AsyncSession) -> list[str]:
11+
"""Apply idempotent runtime schema patches for existing deployments."""
12+
applied_patch_names: list[str] = []
13+
14+
await db_session.execute(DISPLAY_TITLE_COLUMN_PATCH_SQL)
15+
applied_patch_names.append(DISPLAY_TITLE_COLUMN_PATCH_NAME)
16+
17+
return applied_patch_names

0 commit comments

Comments
 (0)