mirror of
https://github.com/imartinez/privateGPT.git
synced 2026-07-17 01:48:03 +00:00
155 lines
5.6 KiB
Python
155 lines
5.6 KiB
Python
import asyncio
|
|
import inspect
|
|
from collections.abc import AsyncGenerator
|
|
from typing import Any
|
|
|
|
from injector import inject, singleton
|
|
from pydantic import BaseModel
|
|
|
|
from private_gpt.components.streaming.providers.models import StreamStatus
|
|
from private_gpt.components.streaming.stream.event_handler import EventHandler
|
|
from private_gpt.components.streaming.stream_component import StreamComponent
|
|
from private_gpt.components.streaming.tasks.task_manager import TaskManager
|
|
|
|
|
|
@singleton
|
|
class StreamProcessor:
|
|
"""Processes event streams and stores minimal data in Redis."""
|
|
|
|
@inject
|
|
def __init__(
|
|
self,
|
|
stream_component: StreamComponent,
|
|
task_manager: TaskManager,
|
|
):
|
|
self.stream_service = stream_component.stream
|
|
self.task_manager = task_manager
|
|
|
|
async def process_stream(
|
|
self,
|
|
correlation_id: str,
|
|
stream_type: str,
|
|
event_generator: AsyncGenerator[BaseModel, None],
|
|
event_handler: EventHandler,
|
|
metadata: dict[str, Any] | None = None,
|
|
mark_completed: bool = True,
|
|
) -> None:
|
|
"""Process a stream of events and push to Redis."""
|
|
try:
|
|
await self.stream_service.update_stream_status(
|
|
correlation_id,
|
|
StreamStatus.PENDING,
|
|
metadata=metadata,
|
|
)
|
|
|
|
is_processing = False
|
|
async for event in event_generator:
|
|
if self.task_manager.is_cancelled(correlation_id):
|
|
raise asyncio.CancelledError(
|
|
f"Stream processing for {correlation_id} has been cancelled."
|
|
)
|
|
|
|
if not is_processing:
|
|
# Do a lazy initialization of processing status.
|
|
current_status = await event_handler.get_current_status(event)
|
|
if (
|
|
current_status is not None
|
|
and current_status >= StreamStatus.PROCESSING
|
|
):
|
|
# Don't submit current status since
|
|
# the current event has not been processed yet.
|
|
await self.stream_service.update_stream_status(
|
|
correlation_id,
|
|
StreamStatus.PROCESSING,
|
|
)
|
|
is_processing = True
|
|
|
|
event_data = await asyncio.to_thread(event_handler.serialize, event)
|
|
await self.stream_service.push_event(
|
|
correlation_id=correlation_id,
|
|
event_data=event_data,
|
|
)
|
|
|
|
if mark_completed:
|
|
await self.stream_service.update_stream_status(
|
|
correlation_id,
|
|
StreamStatus.COMPLETED,
|
|
)
|
|
|
|
except asyncio.CancelledError:
|
|
await self.stream_service.update_stream_status(
|
|
correlation_id,
|
|
StreamStatus.CANCELLED,
|
|
)
|
|
await self._handle_cancellation(correlation_id, event_generator)
|
|
raise
|
|
except Exception as e:
|
|
if not isinstance(e, asyncio.CancelledError):
|
|
|
|
def create_error_event_data(exception: Exception) -> str:
|
|
error_evt = event_handler.error_event(correlation_id, exception)
|
|
return event_handler.serialize(error_evt)
|
|
|
|
event_data = await asyncio.to_thread(create_error_event_data, e)
|
|
await self.stream_service.push_event(
|
|
correlation_id=correlation_id,
|
|
event_data=event_data,
|
|
)
|
|
await self.stream_service.update_stream_status(
|
|
correlation_id,
|
|
StreamStatus.ERROR,
|
|
error_message=str(e),
|
|
)
|
|
raise
|
|
|
|
async def _handle_cancellation(
|
|
self,
|
|
correlation_id: str,
|
|
event_generator: AsyncGenerator[BaseModel, None],
|
|
) -> None:
|
|
"""Handle cancellation of the stream generator."""
|
|
try:
|
|
cancel = getattr(event_generator, "cancel", None)
|
|
if callable(cancel):
|
|
result = cancel()
|
|
if inspect.isawaitable(result):
|
|
await result
|
|
else:
|
|
await event_generator.aclose()
|
|
except Exception:
|
|
pass
|
|
|
|
async def start_stream_processing(
|
|
self,
|
|
correlation_id: str,
|
|
stream_type: str,
|
|
event_generator: AsyncGenerator[BaseModel, None],
|
|
event_handler: EventHandler,
|
|
metadata: dict[str, Any] | None = None,
|
|
mark_completed: bool = True,
|
|
) -> asyncio.Task[Any]:
|
|
"""Start processing a stream in the background."""
|
|
task: asyncio.Task[Any] = await self.task_manager.create_task(
|
|
correlation_id=correlation_id,
|
|
coro=self.process_stream(
|
|
correlation_id=correlation_id,
|
|
stream_type=stream_type,
|
|
event_generator=event_generator,
|
|
event_handler=event_handler,
|
|
metadata=metadata,
|
|
mark_completed=mark_completed,
|
|
),
|
|
name=f"stream_processor_{correlation_id}",
|
|
)
|
|
return task
|
|
|
|
async def cancel_stream_processing(self, correlation_id: str) -> bool:
|
|
"""Cancel stream processing."""
|
|
success = await self.task_manager.cancel(correlation_id)
|
|
if success:
|
|
await self.stream_service.update_stream_status(
|
|
correlation_id,
|
|
StreamStatus.CANCELLED,
|
|
)
|
|
return success
|