Files
MoFin/venv/lib/python3.12/site-packages/litellm/files/streaming.py
T
知微 fa45d8aa5f fix: 小果地址统一node122(兼容LAN+EasyTier)
- health_checklist.json: 192.168.1.122→node122
- ocr_client.py: docstring IP→node122
- docs/market-data-requirements.md: IP→node122
- 所有API调用通过ProxyHandler({})绕过系统代理
  Privoxy对node122:18003返回500,直连正常
2026-06-30 02:56:35 +08:00

251 lines
8.8 KiB
Python

import datetime
import traceback
from typing import (
TYPE_CHECKING,
Any,
AsyncIterator,
Dict,
Iterator,
Optional,
Union,
cast,
)
import anyio
from litellm.files.types import FileContentProvider
if TYPE_CHECKING:
from litellm.litellm_core_utils.litellm_logging import (
Logging as LiteLLMLoggingObj,
)
from litellm.types.utils import StandardLoggingHiddenParams, StandardLoggingPayload
class FileContentStreamingResponse:
"""
Iterator wrapper for file content streaming that carries LiteLLM metadata
and emits success/failure callbacks once the stream finishes.
"""
def __init__(
self,
stream_iterator: Union[Iterator[bytes], AsyncIterator[bytes]],
file_id: str,
model: Optional[str],
custom_llm_provider: Optional[Union[FileContentProvider, str]],
logging_obj: Optional["LiteLLMLoggingObj"],
) -> None:
self.stream_iterator = stream_iterator
self.file_id = file_id
self.model = model
self.custom_llm_provider = custom_llm_provider
self.logging_obj = logging_obj
self.standard_logging_object: Optional["StandardLoggingPayload"] = None
self._hidden_params: Dict[str, Any] = {}
self._logging_completed = False
self._close_completed = False
self._start_time = (
logging_obj.start_time
if logging_obj is not None and getattr(logging_obj, "start_time", None)
else datetime.datetime.now()
)
self._sync_hidden_params()
def __iter__(self) -> "FileContentStreamingResponse":
if not hasattr(self.stream_iterator, "__next__"):
raise TypeError("File content stream does not support sync iteration")
return self
def __next__(self) -> bytes:
if not hasattr(self.stream_iterator, "__next__"):
raise TypeError("File content stream does not support sync iteration")
try:
return next(cast(Iterator[bytes], self.stream_iterator))
except StopIteration:
self._log_success_sync()
raise
except Exception as e:
self._log_failure_sync(e)
raise
def __aiter__(self) -> "FileContentStreamingResponse":
if not hasattr(self.stream_iterator, "__anext__"):
raise TypeError("File content stream does not support async iteration")
return self
async def __anext__(self) -> bytes:
if not hasattr(self.stream_iterator, "__anext__"):
raise TypeError("File content stream does not support async iteration")
try:
return await cast(AsyncIterator[bytes], self.stream_iterator).__anext__()
except StopAsyncIteration:
await self._log_success_async()
raise
except Exception as e:
await self._log_failure_async(e)
raise
async def aclose(self) -> None:
if self._close_completed:
return
self._close_completed = True
self._logging_completed = True
stream_to_close = self.stream_iterator
self.stream_iterator = cast(
Union[Iterator[bytes], AsyncIterator[bytes]], iter(())
)
# Shield cleanup from request cancellation so upstream HTTP connections
# are released promptly on client disconnects.
with anyio.CancelScope(shield=True):
if hasattr(stream_to_close, "aclose"):
await cast(AsyncIterator[bytes], stream_to_close).aclose() # type: ignore[attr-defined]
elif hasattr(stream_to_close, "close"):
result = cast(Iterator[bytes], stream_to_close).close() # type: ignore[attr-defined]
if result is not None:
await result
def close(self) -> None:
if self._close_completed:
return
self._close_completed = True
self._logging_completed = True
stream_to_close = self.stream_iterator
self.stream_iterator = cast(
Union[Iterator[bytes], AsyncIterator[bytes]], iter(())
)
if hasattr(stream_to_close, "close"):
cast(Iterator[bytes], stream_to_close).close() # type: ignore[attr-defined]
def _build_logging_response(self) -> Dict[str, str]:
response = {
"id": self.file_id,
"object": "file.content",
}
if self.model:
response["model"] = self.model
return response
def _sync_hidden_params(self) -> None:
litellm_params: dict[str, Any] = {}
if self.logging_obj is not None:
litellm_params = (
self.logging_obj.model_call_details.get("litellm_params", {}) or {}
)
if "api_base" not in self._hidden_params and litellm_params.get("api_base"):
self._hidden_params["api_base"] = litellm_params["api_base"]
# The generic client decorator infers `model` from the first positional arg,
# which is `file_id` for this API. Correct it before logging callbacks run.
self._hidden_params["litellm_model_name"] = self.model
if "response_cost" not in self._hidden_params:
self._hidden_params["response_cost"] = None
def _build_standard_logging_object(
self,
end_time: datetime.datetime,
) -> Optional["StandardLoggingPayload"]:
if self.standard_logging_object is not None:
return self.standard_logging_object
if self.logging_obj is None:
return None
from litellm.litellm_core_utils.litellm_logging import (
get_standard_logging_object_payload,
)
self._sync_hidden_params()
payload = get_standard_logging_object_payload(
kwargs=self.logging_obj.model_call_details,
init_response_obj=self._build_logging_response(),
start_time=self._start_time,
end_time=end_time,
logging_obj=self.logging_obj,
status="success",
)
if payload is None:
return None
merged_hidden_params = cast(
"StandardLoggingHiddenParams",
{
**cast(Dict[str, Any], payload.get("hidden_params") or {}),
**self._hidden_params,
},
)
payload["hidden_params"] = merged_hidden_params
payload["response"] = self._build_logging_response()
if self.custom_llm_provider is not None:
payload["custom_llm_provider"] = self.custom_llm_provider
if self.model is not None:
payload["model"] = self.model
if self._hidden_params.get("api_base"):
payload["api_base"] = cast(str, self._hidden_params["api_base"])
self.standard_logging_object = payload
return payload
async def _log_success_async(self) -> None:
if self._logging_completed or self.logging_obj is None:
return
self._logging_completed = True
end_time = datetime.datetime.now()
standard_logging_object = self._build_standard_logging_object(end_time=end_time)
await self.logging_obj.async_success_handler(
result=self._build_logging_response(),
start_time=self._start_time,
end_time=end_time,
standard_logging_object=standard_logging_object,
)
self.logging_obj.handle_sync_success_callbacks_for_async_calls(
result=self._build_logging_response(),
start_time=self._start_time,
end_time=end_time,
)
def _log_success_sync(self) -> None:
if self._logging_completed or self.logging_obj is None:
return
self._logging_completed = True
end_time = datetime.datetime.now()
standard_logging_object = self._build_standard_logging_object(end_time=end_time)
self.logging_obj.success_handler(
result=self._build_logging_response(),
start_time=self._start_time,
end_time=end_time,
standard_logging_object=standard_logging_object,
)
async def _log_failure_async(self, error: Exception) -> None:
if self._logging_completed or self.logging_obj is None:
return
self._logging_completed = True
end_time = datetime.datetime.now()
traceback_str = traceback.format_exc()
self.logging_obj.failure_handler(
error, traceback_str, self._start_time, end_time
)
await self.logging_obj.async_failure_handler(
error, traceback_str, self._start_time, end_time
)
def _log_failure_sync(self, error: Exception) -> None:
if self._logging_completed or self.logging_obj is None:
return
self._logging_completed = True
end_time = datetime.datetime.now()
self.logging_obj.failure_handler(
error, traceback.format_exc(), self._start_time, end_time
)