feat: 增强日历功能并集成 AgentScope 代理服务
This commit is contained in:
@@ -2,21 +2,20 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import Depends
|
||||
from redis.asyncio import Redis
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from core.agent.infrastructure.events.redis_stream import RedisStreamEventStore
|
||||
from core.agent.infrastructure.storage.tool_result_storage import (
|
||||
create_tool_result_storage,
|
||||
)
|
||||
from core.agent.infrastructure.queue.tasks import (
|
||||
from core.agentscope.events import RedisStreamBus
|
||||
from core.agentscope.runtime.tasks import (
|
||||
run_command_task,
|
||||
run_command_task_bulk,
|
||||
run_command_task_critical,
|
||||
)
|
||||
from core.agent.infrastructure.storage.tool_result_storage import (
|
||||
create_tool_result_storage,
|
||||
)
|
||||
from core.config.settings import config
|
||||
from core.db import get_db
|
||||
from services.base.redis import get_or_init_redis_client
|
||||
@@ -84,18 +83,18 @@ class TaskiqQueueClient:
|
||||
|
||||
class RedisEventStream:
|
||||
def __init__(self) -> None:
|
||||
self._store: RedisStreamEventStore | None = None
|
||||
self._bus: RedisStreamBus | None = None
|
||||
|
||||
async def _get_store(self) -> RedisStreamEventStore:
|
||||
if self._store is None:
|
||||
async def _get_bus(self) -> RedisStreamBus:
|
||||
if self._bus is None:
|
||||
client = await get_or_init_redis_client()
|
||||
self._store = RedisStreamEventStore(
|
||||
self._bus = RedisStreamBus(
|
||||
client=client,
|
||||
stream_prefix=config.agent_runtime.redis_stream_prefix,
|
||||
read_count=config.agent_runtime.redis_stream_read_count,
|
||||
block_ms=config.agent_runtime.redis_stream_block_ms,
|
||||
)
|
||||
return self._store
|
||||
return self._bus
|
||||
|
||||
async def read(
|
||||
self,
|
||||
@@ -103,12 +102,9 @@ class RedisEventStream:
|
||||
session_id: str,
|
||||
last_event_id: str | None,
|
||||
) -> list[dict[str, Any]]:
|
||||
store = await self._get_store()
|
||||
rows = await store.read_events(
|
||||
session_id=UUID(session_id),
|
||||
last_event_id=last_event_id,
|
||||
)
|
||||
return [{**row, "cursor": last_event_id} for row in rows]
|
||||
bus = await self._get_bus()
|
||||
rows = await bus.read(session_id=session_id, last_event_id=last_event_id)
|
||||
return [{**row, "cursor": row.get("id")} for row in rows]
|
||||
|
||||
|
||||
def get_agent_service(session: AsyncSession = Depends(get_db)) -> AgentService:
|
||||
|
||||
@@ -14,7 +14,7 @@ from fastapi import APIRouter, Depends, Header, Query, Request, status, UploadFi
|
||||
from fastapi import HTTPException
|
||||
from fastapi.responses import JSONResponse, StreamingResponse
|
||||
|
||||
from core.agent.infrastructure.agui.stream import to_sse_event
|
||||
from core.agentscope.events import to_sse_event
|
||||
from core.agent.domain.agui_input import (
|
||||
parse_run_input,
|
||||
validate_run_request_messages_contract,
|
||||
|
||||
@@ -18,6 +18,17 @@ from core.logging import get_logger
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
def _extract_user_token_from_run_input(run_input: RunAgentInput) -> str | None:
|
||||
forwarded = run_input.forwarded_props
|
||||
if not isinstance(forwarded, dict):
|
||||
return None
|
||||
for key in ("accessToken", "userToken", "token"):
|
||||
value = forwarded.get(key)
|
||||
if isinstance(value, str) and value.strip():
|
||||
return value.strip()
|
||||
return None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class TaskAccepted:
|
||||
task_id: str
|
||||
@@ -65,6 +76,10 @@ def ensure_session_owner(*, owner_id: str, current_user: CurrentUser) -> None:
|
||||
|
||||
|
||||
class AgentService:
|
||||
_repository: AgentRepositoryLike
|
||||
_queue: QueueClientLike
|
||||
_stream: EventStreamLike
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
@@ -107,6 +122,8 @@ class AgentService:
|
||||
task_id = await self._queue.enqueue(
|
||||
command={
|
||||
"command": "run",
|
||||
"owner_id": str(current_user.id),
|
||||
"user_token": _extract_user_token_from_run_input(run_input),
|
||||
"run_input": run_input.model_dump(mode="json", by_alias=True),
|
||||
},
|
||||
dedup_key=None,
|
||||
@@ -132,6 +149,8 @@ class AgentService:
|
||||
task_id = await self._queue.enqueue(
|
||||
command={
|
||||
"command": "resume",
|
||||
"owner_id": str(current_user.id),
|
||||
"user_token": _extract_user_token_from_run_input(run_input),
|
||||
"run_input": run_input.model_dump(mode="json", by_alias=True),
|
||||
},
|
||||
dedup_key=dedup_key,
|
||||
|
||||
@@ -32,6 +32,7 @@ class ScheduleItemMetadata(BaseModel):
|
||||
location: str | None = None
|
||||
notes: str | None = None
|
||||
attachments: list[ScheduleItemMetadataAttachment] = Field(default_factory=list)
|
||||
reminder_minutes: int | None = Field(default=None, ge=0, le=10080)
|
||||
version: Literal[1] = 1
|
||||
|
||||
|
||||
|
||||
@@ -135,14 +135,13 @@ class ScheduleItemService(BaseService):
|
||||
update_data = request.model_dump(exclude_unset=True)
|
||||
|
||||
# Handle metadata separately (model_dump returns dict)
|
||||
if "metadata" in update_data and update_data["metadata"] is not None:
|
||||
metadata_value = update_data["metadata"]
|
||||
if "metadata" in update_data:
|
||||
metadata_value = update_data.pop("metadata")
|
||||
update_data["extra_metadata"] = (
|
||||
metadata_value.model_dump()
|
||||
if hasattr(metadata_value, "model_dump")
|
||||
else metadata_value
|
||||
)
|
||||
del update_data["metadata"]
|
||||
|
||||
# Validate time range
|
||||
next_start = update_data.get("start_at", existing.start_at)
|
||||
|
||||
Reference in New Issue
Block a user