fa45d8aa5f
- 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,直连正常
251 lines
8.8 KiB
Python
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
|
|
)
|