feat(agent): add voice input capability and standardize tool naming
- Add voice recording with transcribe endpoint (ASR) for multimodal input - Android: add RECORD_AUDIO and INTERNET permissions - Refactor tool naming: frontend tools use 'front.' prefix, backend tools use 'back.' - Migrate calendar tools: create_calendar_event -> back.mutate/list/delete events - Add calendar_event_list.v1 and calendar_operation.v1 UI card types - Update all Flutter and Python tests to match new tool naming conventions - Add record package dependency for voice recording
This commit is contained in:
@@ -4,7 +4,7 @@ from datetime import datetime, timezone
|
||||
from typing import TYPE_CHECKING, Protocol
|
||||
from uuid import UUID
|
||||
|
||||
from sqlalchemy import select, update
|
||||
from sqlalchemy import func, select, update
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from core.db.base_repository import BaseRepository
|
||||
@@ -33,6 +33,13 @@ class ScheduleItemRepository(Protocol):
|
||||
async def list_by_date_range(
|
||||
self, owner_id: UUID, start_at: datetime, end_at: datetime
|
||||
) -> list[ScheduleItem]: ...
|
||||
async def list_paginated(
|
||||
self,
|
||||
owner_id: UUID,
|
||||
*,
|
||||
page: int,
|
||||
page_size: int,
|
||||
) -> tuple[list[ScheduleItem], int]: ...
|
||||
async def create_subscription(self, data: dict) -> ScheduleSubscription: ...
|
||||
|
||||
|
||||
@@ -131,11 +138,46 @@ class SQLAlchemyScheduleItemRepository(BaseRepository[ScheduleItem]):
|
||||
logger.exception("Schedule item list failed", owner_id=str(owner_id))
|
||||
raise
|
||||
|
||||
async def list_paginated(
|
||||
self,
|
||||
owner_id: UUID,
|
||||
*,
|
||||
page: int,
|
||||
page_size: int,
|
||||
) -> tuple[list[ScheduleItem], int]:
|
||||
offset = (page - 1) * page_size
|
||||
try:
|
||||
count_stmt = (
|
||||
select(func.count())
|
||||
.select_from(ScheduleItem)
|
||||
.where(ScheduleItem.owner_id == owner_id)
|
||||
.where(ScheduleItem.deleted_at.is_(None))
|
||||
)
|
||||
count_result = await self._session.execute(count_stmt)
|
||||
total = int(count_result.scalar_one() or 0)
|
||||
|
||||
items_stmt = (
|
||||
select(ScheduleItem)
|
||||
.where(ScheduleItem.owner_id == owner_id)
|
||||
.where(ScheduleItem.deleted_at.is_(None))
|
||||
.order_by(ScheduleItem.start_at.asc(), ScheduleItem.id.asc())
|
||||
.offset(offset)
|
||||
.limit(page_size)
|
||||
)
|
||||
items_result = await self._session.execute(items_stmt)
|
||||
items = list(items_result.scalars().all())
|
||||
return items, total
|
||||
except SQLAlchemyError:
|
||||
logger.exception(
|
||||
"Schedule item paginated list failed",
|
||||
owner_id=str(owner_id),
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
)
|
||||
raise
|
||||
|
||||
async def create_subscription(self, data: dict) -> ScheduleSubscription:
|
||||
sub = ScheduleSubscription(**data)
|
||||
self._session.add(sub)
|
||||
await self._session.flush()
|
||||
return sub
|
||||
|
||||
async def get_by_id(self, entity_id: UUID) -> ScheduleItem | None:
|
||||
return await super().get_by_id(entity_id)
|
||||
|
||||
@@ -202,6 +202,34 @@ class ScheduleItemService(BaseService):
|
||||
|
||||
return [self._to_response(item) for item in items]
|
||||
|
||||
async def list_paginated(
|
||||
self,
|
||||
*,
|
||||
page: int,
|
||||
page_size: int,
|
||||
) -> tuple[list[ScheduleItemResponse], int]:
|
||||
user_id = self.require_user_id()
|
||||
if page < 1:
|
||||
raise HTTPException(status_code=400, detail="page must be >= 1")
|
||||
if page_size < 1 or page_size > 100:
|
||||
raise HTTPException(status_code=400, detail="page_size must be 1..100")
|
||||
try:
|
||||
items, total = await self._repository.list_paginated(
|
||||
user_id,
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
)
|
||||
except SQLAlchemyError:
|
||||
logger.exception(
|
||||
"Failed to list schedule items with pagination",
|
||||
page=page,
|
||||
page_size=page_size,
|
||||
)
|
||||
raise HTTPException(
|
||||
status_code=503, detail="Schedule item store unavailable"
|
||||
)
|
||||
return [self._to_response(item) for item in items], total
|
||||
|
||||
async def share(
|
||||
self, item_id: UUID, request: ScheduleItemShareRequest
|
||||
) -> ScheduleItemShareResponse:
|
||||
|
||||
Reference in New Issue
Block a user