mirror of
https://github.com/RYDE-WORK/Langchain-Chatchat.git
synced 2026-02-07 07:23:29 +08:00
增加使用说明
This commit is contained in:
parent
4ce7ce0709
commit
d245e20c1d
@ -42,3 +42,39 @@ make format
|
|||||||
make format_diff
|
make format_diff
|
||||||
```
|
```
|
||||||
当你对项目的一部分进行了更改,并希望确保更改的部分格式正确,而不影响代码库的其他部分时,这个命令特别有用。
|
当你对项目的一部分进行了更改,并希望确保更改的部分格式正确,而不影响代码库的其他部分时,这个命令特别有用。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
### 开始使用
|
||||||
|
|
||||||
|
当项目安装完成,配置这个`model_providers.yaml`文件,即可完成平台加载
|
||||||
|
> 注意: 在您配置平台之前,请确认平台依赖完整,例如智谱平台,您需要安装智谱sdk `pip install zhipuai`
|
||||||
|
|
||||||
|
model_providers包含了不同平台提供的 全局配置`provider_credential`,和模型配置`model_credential`
|
||||||
|
不同平台所加载的配置有所不同,关于如何配置这个文件
|
||||||
|
|
||||||
|
请查看包`model_providers.core.model_runtime.model_providers`下方的平台 `yaml`文件
|
||||||
|
例如`zhipuai.yaml`,这里给出了`provider_credential_schema`,其中包含了一个变量`api_key`
|
||||||
|
|
||||||
|
要加载智谱平台,操作如下
|
||||||
|
|
||||||
|
- 安装sdk
|
||||||
|
```shell
|
||||||
|
$ pip install zhipuai
|
||||||
|
```
|
||||||
|
|
||||||
|
- 编辑`model_providers.yaml`
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
|
||||||
|
zhipuai:
|
||||||
|
|
||||||
|
provider_credential:
|
||||||
|
api_key: 'd4fa0690b6dfa205204cae2e12aa6fb6.2'
|
||||||
|
```
|
||||||
|
|
||||||
|
- `model-providers`可以运行pytest 测试
|
||||||
|
```shell
|
||||||
|
poetry run pytest tests/server_unit_test/test_init_server.py
|
||||||
|
|
||||||
|
```
|
||||||
@ -24,13 +24,13 @@ from model_providers.core.model_runtime.model_providers.__base.large_language_mo
|
|||||||
from model_providers.core.model_runtime.model_providers.zhipuai._common import (
|
from model_providers.core.model_runtime.model_providers.zhipuai._common import (
|
||||||
_CommonZhipuaiAI,
|
_CommonZhipuaiAI,
|
||||||
)
|
)
|
||||||
from model_providers.core.model_runtime.model_providers.zhipuai.zhipuai_sdk._client import (
|
from zhipuai import (
|
||||||
ZhipuAI,
|
ZhipuAI,
|
||||||
)
|
)
|
||||||
from model_providers.core.model_runtime.model_providers.zhipuai.zhipuai_sdk.types.chat.chat_completion import (
|
from zhipuai.types.chat.chat_completion import (
|
||||||
Completion,
|
Completion,
|
||||||
)
|
)
|
||||||
from model_providers.core.model_runtime.model_providers.zhipuai.zhipuai_sdk.types.chat.chat_completion_chunk import (
|
from zhipuai.types.chat.chat_completion_chunk import (
|
||||||
ChatCompletionChunk,
|
ChatCompletionChunk,
|
||||||
)
|
)
|
||||||
from model_providers.core.model_runtime.utils import helper
|
from model_providers.core.model_runtime.utils import helper
|
||||||
|
|||||||
@ -15,7 +15,7 @@ from model_providers.core.model_runtime.model_providers.__base.text_embedding_mo
|
|||||||
from model_providers.core.model_runtime.model_providers.zhipuai._common import (
|
from model_providers.core.model_runtime.model_providers.zhipuai._common import (
|
||||||
_CommonZhipuaiAI,
|
_CommonZhipuaiAI,
|
||||||
)
|
)
|
||||||
from model_providers.core.model_runtime.model_providers.zhipuai.zhipuai_sdk._client import (
|
from zhipuai import (
|
||||||
ZhipuAI,
|
ZhipuAI,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
from .__version__ import __version__
|
|
||||||
from ._client import ZhipuAI
|
|
||||||
from .core._errors import (
|
|
||||||
APIAuthenticationError,
|
|
||||||
APIInternalError,
|
|
||||||
APIReachLimitError,
|
|
||||||
APIRequestFailedError,
|
|
||||||
APIResponseError,
|
|
||||||
APIResponseValidationError,
|
|
||||||
APIServerFlowExceedError,
|
|
||||||
APIStatusError,
|
|
||||||
APITimeoutError,
|
|
||||||
ZhipuAIError,
|
|
||||||
)
|
|
||||||
@ -1 +0,0 @@
|
|||||||
__version__ = "v2.0.1"
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
from collections.abc import Mapping
|
|
||||||
from typing import Union
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
from httpx import Timeout
|
|
||||||
from typing_extensions import override
|
|
||||||
|
|
||||||
from . import api_resource
|
|
||||||
from .core import _jwt_token
|
|
||||||
from .core._base_type import NOT_GIVEN, NotGiven
|
|
||||||
from .core._errors import ZhipuAIError
|
|
||||||
from .core._http_client import ZHIPUAI_DEFAULT_MAX_RETRIES, HttpClient
|
|
||||||
|
|
||||||
|
|
||||||
class ZhipuAI(HttpClient):
|
|
||||||
chat: api_resource.chat
|
|
||||||
api_key: str
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
api_key: str | None = None,
|
|
||||||
base_url: str | httpx.URL | None = None,
|
|
||||||
timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
|
|
||||||
max_retries: int = ZHIPUAI_DEFAULT_MAX_RETRIES,
|
|
||||||
http_client: httpx.Client | None = None,
|
|
||||||
custom_headers: Mapping[str, str] | None = None,
|
|
||||||
) -> None:
|
|
||||||
# if api_key is None:
|
|
||||||
# api_key = os.environ.get("ZHIPUAI_API_KEY")
|
|
||||||
if api_key is None:
|
|
||||||
raise ZhipuAIError("未提供api_key,请通过参数或环境变量提供")
|
|
||||||
self.api_key = api_key
|
|
||||||
|
|
||||||
if base_url is None:
|
|
||||||
base_url = os.environ.get("ZHIPUAI_BASE_URL")
|
|
||||||
if base_url is None:
|
|
||||||
base_url = "https://open.bigmodel.cn/api/paas/v4"
|
|
||||||
from .__version__ import __version__
|
|
||||||
|
|
||||||
super().__init__(
|
|
||||||
version=__version__,
|
|
||||||
base_url=base_url,
|
|
||||||
timeout=timeout,
|
|
||||||
custom_httpx_client=http_client,
|
|
||||||
custom_headers=custom_headers,
|
|
||||||
)
|
|
||||||
self.chat = api_resource.chat.Chat(self)
|
|
||||||
self.images = api_resource.images.Images(self)
|
|
||||||
self.embeddings = api_resource.embeddings.Embeddings(self)
|
|
||||||
self.files = api_resource.files.Files(self)
|
|
||||||
self.fine_tuning = api_resource.fine_tuning.FineTuning(self)
|
|
||||||
|
|
||||||
@property
|
|
||||||
@override
|
|
||||||
def _auth_headers(self) -> dict[str, str]:
|
|
||||||
api_key = self.api_key
|
|
||||||
return {"Authorization": f"{_jwt_token.generate_token(api_key)}"}
|
|
||||||
|
|
||||||
def __del__(self) -> None:
|
|
||||||
if (
|
|
||||||
not hasattr(self, "_has_custom_http_client")
|
|
||||||
or not hasattr(self, "close")
|
|
||||||
or not hasattr(self, "_client")
|
|
||||||
):
|
|
||||||
# if the '__init__' method raised an error, self would not have client attr
|
|
||||||
return
|
|
||||||
|
|
||||||
if self._has_custom_http_client:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.close()
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
from .chat import chat
|
|
||||||
from .embeddings import Embeddings
|
|
||||||
from .files import Files
|
|
||||||
from .fine_tuning import fine_tuning
|
|
||||||
from .images import Images
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Literal, Optional, Union
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from ...core._base_api import BaseAPI
|
|
||||||
from ...core._base_type import NOT_GIVEN, Headers, NotGiven
|
|
||||||
from ...core._http_client import make_user_request_input
|
|
||||||
from ...types.chat.async_chat_completion import AsyncCompletion, AsyncTaskStatus
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ..._client import ZhipuAI
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncCompletions(BaseAPI):
|
|
||||||
def __init__(self, client: ZhipuAI) -> None:
|
|
||||||
super().__init__(client)
|
|
||||||
|
|
||||||
def create(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
model: str,
|
|
||||||
request_id: Optional[str] | NotGiven = NOT_GIVEN,
|
|
||||||
do_sample: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,
|
|
||||||
temperature: Optional[float] | NotGiven = NOT_GIVEN,
|
|
||||||
top_p: Optional[float] | NotGiven = NOT_GIVEN,
|
|
||||||
max_tokens: int | NotGiven = NOT_GIVEN,
|
|
||||||
seed: int | NotGiven = NOT_GIVEN,
|
|
||||||
messages: Union[str, list[str], list[int], list[list[int]], None],
|
|
||||||
stop: Optional[Union[str, list[str], None]] | NotGiven = NOT_GIVEN,
|
|
||||||
sensitive_word_check: Optional[object] | NotGiven = NOT_GIVEN,
|
|
||||||
tools: Optional[object] | NotGiven = NOT_GIVEN,
|
|
||||||
tool_choice: str | NotGiven = NOT_GIVEN,
|
|
||||||
extra_headers: Headers | None = None,
|
|
||||||
disable_strict_validation: Optional[bool] | None = None,
|
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
) -> AsyncTaskStatus:
|
|
||||||
_cast_type = AsyncTaskStatus
|
|
||||||
|
|
||||||
if disable_strict_validation:
|
|
||||||
_cast_type = object
|
|
||||||
return self._post(
|
|
||||||
"/async/chat/completions",
|
|
||||||
body={
|
|
||||||
"model": model,
|
|
||||||
"request_id": request_id,
|
|
||||||
"temperature": temperature,
|
|
||||||
"top_p": top_p,
|
|
||||||
"do_sample": do_sample,
|
|
||||||
"max_tokens": max_tokens,
|
|
||||||
"seed": seed,
|
|
||||||
"messages": messages,
|
|
||||||
"stop": stop,
|
|
||||||
"sensitive_word_check": sensitive_word_check,
|
|
||||||
"tools": tools,
|
|
||||||
"tool_choice": tool_choice,
|
|
||||||
},
|
|
||||||
options=make_user_request_input(
|
|
||||||
extra_headers=extra_headers, timeout=timeout
|
|
||||||
),
|
|
||||||
cast_type=_cast_type,
|
|
||||||
enable_stream=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
def retrieve_completion_result(
|
|
||||||
self,
|
|
||||||
id: str,
|
|
||||||
extra_headers: Headers | None = None,
|
|
||||||
disable_strict_validation: Optional[bool] | None = None,
|
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
) -> Union[AsyncCompletion, AsyncTaskStatus]:
|
|
||||||
_cast_type = Union[AsyncCompletion, AsyncTaskStatus]
|
|
||||||
if disable_strict_validation:
|
|
||||||
_cast_type = object
|
|
||||||
return self._get(
|
|
||||||
path=f"/async-result/{id}",
|
|
||||||
cast_type=_cast_type,
|
|
||||||
options=make_user_request_input(
|
|
||||||
extra_headers=extra_headers, timeout=timeout
|
|
||||||
),
|
|
||||||
)
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from ...core._base_api import BaseAPI
|
|
||||||
from .async_completions import AsyncCompletions
|
|
||||||
from .completions import Completions
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ..._client import ZhipuAI
|
|
||||||
|
|
||||||
|
|
||||||
class Chat(BaseAPI):
|
|
||||||
completions: Completions
|
|
||||||
|
|
||||||
def __init__(self, client: "ZhipuAI") -> None:
|
|
||||||
super().__init__(client)
|
|
||||||
self.completions = Completions(client)
|
|
||||||
self.asyncCompletions = AsyncCompletions(client)
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Literal, Optional, Union
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from ...core._base_api import BaseAPI
|
|
||||||
from ...core._base_type import NOT_GIVEN, Headers, NotGiven
|
|
||||||
from ...core._http_client import make_user_request_input
|
|
||||||
from ...core._sse_client import StreamResponse
|
|
||||||
from ...types.chat.chat_completion import Completion
|
|
||||||
from ...types.chat.chat_completion_chunk import ChatCompletionChunk
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ..._client import ZhipuAI
|
|
||||||
|
|
||||||
|
|
||||||
class Completions(BaseAPI):
|
|
||||||
def __init__(self, client: ZhipuAI) -> None:
|
|
||||||
super().__init__(client)
|
|
||||||
|
|
||||||
def create(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
model: str,
|
|
||||||
request_id: Optional[str] | NotGiven = NOT_GIVEN,
|
|
||||||
do_sample: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,
|
|
||||||
stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,
|
|
||||||
temperature: Optional[float] | NotGiven = NOT_GIVEN,
|
|
||||||
top_p: Optional[float] | NotGiven = NOT_GIVEN,
|
|
||||||
max_tokens: int | NotGiven = NOT_GIVEN,
|
|
||||||
seed: int | NotGiven = NOT_GIVEN,
|
|
||||||
messages: Union[str, list[str], list[int], object, None],
|
|
||||||
stop: Optional[Union[str, list[str], None]] | NotGiven = NOT_GIVEN,
|
|
||||||
sensitive_word_check: Optional[object] | NotGiven = NOT_GIVEN,
|
|
||||||
tools: Optional[object] | NotGiven = NOT_GIVEN,
|
|
||||||
tool_choice: str | NotGiven = NOT_GIVEN,
|
|
||||||
extra_headers: Headers | None = None,
|
|
||||||
disable_strict_validation: Optional[bool] | None = None,
|
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
) -> Completion | StreamResponse[ChatCompletionChunk]:
|
|
||||||
_cast_type = Completion
|
|
||||||
_stream_cls = StreamResponse[ChatCompletionChunk]
|
|
||||||
if disable_strict_validation:
|
|
||||||
_cast_type = object
|
|
||||||
_stream_cls = StreamResponse[object]
|
|
||||||
return self._post(
|
|
||||||
"/chat/completions",
|
|
||||||
body={
|
|
||||||
"model": model,
|
|
||||||
"request_id": request_id,
|
|
||||||
"temperature": temperature,
|
|
||||||
"top_p": top_p,
|
|
||||||
"do_sample": do_sample,
|
|
||||||
"max_tokens": max_tokens,
|
|
||||||
"seed": seed,
|
|
||||||
"messages": messages,
|
|
||||||
"stop": stop,
|
|
||||||
"sensitive_word_check": sensitive_word_check,
|
|
||||||
"stream": stream,
|
|
||||||
"tools": tools,
|
|
||||||
"tool_choice": tool_choice,
|
|
||||||
},
|
|
||||||
options=make_user_request_input(
|
|
||||||
extra_headers=extra_headers,
|
|
||||||
),
|
|
||||||
cast_type=_cast_type,
|
|
||||||
enable_stream=stream or False,
|
|
||||||
stream_cls=_stream_cls,
|
|
||||||
)
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Optional, Union
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from ..core._base_api import BaseAPI
|
|
||||||
from ..core._base_type import NOT_GIVEN, Headers, NotGiven
|
|
||||||
from ..core._http_client import make_user_request_input
|
|
||||||
from ..types.embeddings import EmbeddingsResponded
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .._client import ZhipuAI
|
|
||||||
|
|
||||||
|
|
||||||
class Embeddings(BaseAPI):
|
|
||||||
def __init__(self, client: ZhipuAI) -> None:
|
|
||||||
super().__init__(client)
|
|
||||||
|
|
||||||
def create(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
input: Union[str, list[str], list[int], list[list[int]]],
|
|
||||||
model: Union[str],
|
|
||||||
encoding_format: str | NotGiven = NOT_GIVEN,
|
|
||||||
user: str | NotGiven = NOT_GIVEN,
|
|
||||||
sensitive_word_check: Optional[object] | NotGiven = NOT_GIVEN,
|
|
||||||
extra_headers: Headers | None = None,
|
|
||||||
disable_strict_validation: Optional[bool] | None = None,
|
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
) -> EmbeddingsResponded:
|
|
||||||
_cast_type = EmbeddingsResponded
|
|
||||||
if disable_strict_validation:
|
|
||||||
_cast_type = object
|
|
||||||
return self._post(
|
|
||||||
"/embeddings",
|
|
||||||
body={
|
|
||||||
"input": input,
|
|
||||||
"model": model,
|
|
||||||
"encoding_format": encoding_format,
|
|
||||||
"user": user,
|
|
||||||
"sensitive_word_check": sensitive_word_check,
|
|
||||||
},
|
|
||||||
options=make_user_request_input(
|
|
||||||
extra_headers=extra_headers, timeout=timeout
|
|
||||||
),
|
|
||||||
cast_type=_cast_type,
|
|
||||||
enable_stream=False,
|
|
||||||
)
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from ..core._base_api import BaseAPI
|
|
||||||
from ..core._base_type import NOT_GIVEN, FileTypes, Headers, NotGiven
|
|
||||||
from ..core._files import is_file_content
|
|
||||||
from ..core._http_client import make_user_request_input
|
|
||||||
from ..types.file_object import FileObject, ListOfFileObject
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .._client import ZhipuAI
|
|
||||||
|
|
||||||
__all__ = ["Files"]
|
|
||||||
|
|
||||||
|
|
||||||
class Files(BaseAPI):
|
|
||||||
def __init__(self, client: ZhipuAI) -> None:
|
|
||||||
super().__init__(client)
|
|
||||||
|
|
||||||
def create(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
file: FileTypes,
|
|
||||||
purpose: str,
|
|
||||||
extra_headers: Headers | None = None,
|
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
) -> FileObject:
|
|
||||||
if not is_file_content(file):
|
|
||||||
prefix = f"Expected file input `{file!r}`"
|
|
||||||
raise RuntimeError(
|
|
||||||
f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(file)} instead."
|
|
||||||
) from None
|
|
||||||
files = [("file", file)]
|
|
||||||
|
|
||||||
extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
|
|
||||||
|
|
||||||
return self._post(
|
|
||||||
"/files",
|
|
||||||
body={
|
|
||||||
"purpose": purpose,
|
|
||||||
},
|
|
||||||
files=files,
|
|
||||||
options=make_user_request_input(
|
|
||||||
extra_headers=extra_headers, timeout=timeout
|
|
||||||
),
|
|
||||||
cast_type=FileObject,
|
|
||||||
)
|
|
||||||
|
|
||||||
def list(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
purpose: str | NotGiven = NOT_GIVEN,
|
|
||||||
limit: int | NotGiven = NOT_GIVEN,
|
|
||||||
after: str | NotGiven = NOT_GIVEN,
|
|
||||||
order: str | NotGiven = NOT_GIVEN,
|
|
||||||
extra_headers: Headers | None = None,
|
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
) -> ListOfFileObject:
|
|
||||||
return self._get(
|
|
||||||
"/files",
|
|
||||||
cast_type=ListOfFileObject,
|
|
||||||
options=make_user_request_input(
|
|
||||||
extra_headers=extra_headers,
|
|
||||||
timeout=timeout,
|
|
||||||
query={
|
|
||||||
"purpose": purpose,
|
|
||||||
"limit": limit,
|
|
||||||
"after": after,
|
|
||||||
"order": order,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
from ...core._base_api import BaseAPI
|
|
||||||
from .jobs import Jobs
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ..._client import ZhipuAI
|
|
||||||
|
|
||||||
|
|
||||||
class FineTuning(BaseAPI):
|
|
||||||
jobs: Jobs
|
|
||||||
|
|
||||||
def __init__(self, client: "ZhipuAI") -> None:
|
|
||||||
super().__init__(client)
|
|
||||||
self.jobs = Jobs(client)
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Optional
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from ...core._base_api import BaseAPI
|
|
||||||
from ...core._base_type import NOT_GIVEN, Headers, NotGiven
|
|
||||||
from ...core._http_client import make_user_request_input
|
|
||||||
from ...types.fine_tuning import (
|
|
||||||
FineTuningJob,
|
|
||||||
FineTuningJobEvent,
|
|
||||||
ListOfFineTuningJob,
|
|
||||||
job_create_params,
|
|
||||||
)
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ..._client import ZhipuAI
|
|
||||||
|
|
||||||
__all__ = ["Jobs"]
|
|
||||||
|
|
||||||
|
|
||||||
class Jobs(BaseAPI):
|
|
||||||
def __init__(self, client: ZhipuAI) -> None:
|
|
||||||
super().__init__(client)
|
|
||||||
|
|
||||||
def create(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
model: str,
|
|
||||||
training_file: str,
|
|
||||||
hyperparameters: job_create_params.Hyperparameters | NotGiven = NOT_GIVEN,
|
|
||||||
suffix: Optional[str] | NotGiven = NOT_GIVEN,
|
|
||||||
request_id: Optional[str] | NotGiven = NOT_GIVEN,
|
|
||||||
validation_file: Optional[str] | NotGiven = NOT_GIVEN,
|
|
||||||
extra_headers: Headers | None = None,
|
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
) -> FineTuningJob:
|
|
||||||
return self._post(
|
|
||||||
"/fine_tuning/jobs",
|
|
||||||
body={
|
|
||||||
"model": model,
|
|
||||||
"training_file": training_file,
|
|
||||||
"hyperparameters": hyperparameters,
|
|
||||||
"suffix": suffix,
|
|
||||||
"validation_file": validation_file,
|
|
||||||
"request_id": request_id,
|
|
||||||
},
|
|
||||||
options=make_user_request_input(
|
|
||||||
extra_headers=extra_headers, timeout=timeout
|
|
||||||
),
|
|
||||||
cast_type=FineTuningJob,
|
|
||||||
)
|
|
||||||
|
|
||||||
def retrieve(
|
|
||||||
self,
|
|
||||||
fine_tuning_job_id: str,
|
|
||||||
*,
|
|
||||||
extra_headers: Headers | None = None,
|
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
) -> FineTuningJob:
|
|
||||||
return self._get(
|
|
||||||
f"/fine_tuning/jobs/{fine_tuning_job_id}",
|
|
||||||
options=make_user_request_input(
|
|
||||||
extra_headers=extra_headers, timeout=timeout
|
|
||||||
),
|
|
||||||
cast_type=FineTuningJob,
|
|
||||||
)
|
|
||||||
|
|
||||||
def list(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
after: str | NotGiven = NOT_GIVEN,
|
|
||||||
limit: int | NotGiven = NOT_GIVEN,
|
|
||||||
extra_headers: Headers | None = None,
|
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
) -> ListOfFineTuningJob:
|
|
||||||
return self._get(
|
|
||||||
"/fine_tuning/jobs",
|
|
||||||
cast_type=ListOfFineTuningJob,
|
|
||||||
options=make_user_request_input(
|
|
||||||
extra_headers=extra_headers,
|
|
||||||
timeout=timeout,
|
|
||||||
query={
|
|
||||||
"after": after,
|
|
||||||
"limit": limit,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
def list_events(
|
|
||||||
self,
|
|
||||||
fine_tuning_job_id: str,
|
|
||||||
*,
|
|
||||||
after: str | NotGiven = NOT_GIVEN,
|
|
||||||
limit: int | NotGiven = NOT_GIVEN,
|
|
||||||
extra_headers: Headers | None = None,
|
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
) -> FineTuningJobEvent:
|
|
||||||
return self._get(
|
|
||||||
f"/fine_tuning/jobs/{fine_tuning_job_id}/events",
|
|
||||||
cast_type=FineTuningJobEvent,
|
|
||||||
options=make_user_request_input(
|
|
||||||
extra_headers=extra_headers,
|
|
||||||
timeout=timeout,
|
|
||||||
query={
|
|
||||||
"after": after,
|
|
||||||
"limit": limit,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING, Optional
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from ..core._base_api import BaseAPI
|
|
||||||
from ..core._base_type import NOT_GIVEN, Headers, NotGiven
|
|
||||||
from ..core._http_client import make_user_request_input
|
|
||||||
from ..types.image import ImagesResponded
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .._client import ZhipuAI
|
|
||||||
|
|
||||||
|
|
||||||
class Images(BaseAPI):
|
|
||||||
def __init__(self, client: ZhipuAI) -> None:
|
|
||||||
super().__init__(client)
|
|
||||||
|
|
||||||
def generations(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
prompt: str,
|
|
||||||
model: str | NotGiven = NOT_GIVEN,
|
|
||||||
n: Optional[int] | NotGiven = NOT_GIVEN,
|
|
||||||
quality: Optional[str] | NotGiven = NOT_GIVEN,
|
|
||||||
response_format: Optional[str] | NotGiven = NOT_GIVEN,
|
|
||||||
size: Optional[str] | NotGiven = NOT_GIVEN,
|
|
||||||
style: Optional[str] | NotGiven = NOT_GIVEN,
|
|
||||||
user: str | NotGiven = NOT_GIVEN,
|
|
||||||
extra_headers: Headers | None = None,
|
|
||||||
disable_strict_validation: Optional[bool] | None = None,
|
|
||||||
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
) -> ImagesResponded:
|
|
||||||
_cast_type = ImagesResponded
|
|
||||||
if disable_strict_validation:
|
|
||||||
_cast_type = object
|
|
||||||
return self._post(
|
|
||||||
"/images/generations",
|
|
||||||
body={
|
|
||||||
"prompt": prompt,
|
|
||||||
"model": model,
|
|
||||||
"n": n,
|
|
||||||
"quality": quality,
|
|
||||||
"response_format": response_format,
|
|
||||||
"size": size,
|
|
||||||
"style": style,
|
|
||||||
"user": user,
|
|
||||||
},
|
|
||||||
options=make_user_request_input(
|
|
||||||
extra_headers=extra_headers, timeout=timeout
|
|
||||||
),
|
|
||||||
cast_type=_cast_type,
|
|
||||||
enable_stream=False,
|
|
||||||
)
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import TYPE_CHECKING
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from .._client import ZhipuAI
|
|
||||||
|
|
||||||
|
|
||||||
class BaseAPI:
|
|
||||||
_client: ZhipuAI
|
|
||||||
|
|
||||||
def __init__(self, client: ZhipuAI) -> None:
|
|
||||||
self._client = client
|
|
||||||
self._delete = client.delete
|
|
||||||
self._get = client.get
|
|
||||||
self._post = client.post
|
|
||||||
self._put = client.put
|
|
||||||
self._patch = client.patch
|
|
||||||
@ -1,112 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from collections.abc import Mapping, Sequence
|
|
||||||
from os import PathLike
|
|
||||||
from typing import IO, TYPE_CHECKING, Any, Literal, TypeVar, Union
|
|
||||||
|
|
||||||
import pydantic
|
|
||||||
from typing_extensions import override
|
|
||||||
|
|
||||||
Query = Mapping[str, object]
|
|
||||||
Body = object
|
|
||||||
AnyMapping = Mapping[str, object]
|
|
||||||
PrimitiveData = Union[str, int, float, bool, None]
|
|
||||||
Data = Union[PrimitiveData, list[Any], tuple[Any], "Mapping[str, Any]"]
|
|
||||||
ModelT = TypeVar("ModelT", bound=pydantic.BaseModel)
|
|
||||||
_T = TypeVar("_T")
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
NoneType: type[None]
|
|
||||||
else:
|
|
||||||
NoneType = type(None)
|
|
||||||
|
|
||||||
|
|
||||||
# Sentinel class used until PEP 0661 is accepted
|
|
||||||
class NotGiven(pydantic.BaseModel):
|
|
||||||
"""
|
|
||||||
A sentinel singleton class used to distinguish omitted keyword arguments
|
|
||||||
from those passed in with the value None (which may have different behavior).
|
|
||||||
|
|
||||||
For example:
|
|
||||||
|
|
||||||
```py
|
|
||||||
def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ...
|
|
||||||
|
|
||||||
get(timeout=1) # 1s timeout
|
|
||||||
get(timeout=None) # No timeout
|
|
||||||
get() # Default timeout behavior, which may not be statically known at the method definition.
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __bool__(self) -> Literal[False]:
|
|
||||||
return False
|
|
||||||
|
|
||||||
@override
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return "NOT_GIVEN"
|
|
||||||
|
|
||||||
|
|
||||||
NotGivenOr = Union[_T, NotGiven]
|
|
||||||
NOT_GIVEN = NotGiven()
|
|
||||||
|
|
||||||
|
|
||||||
class Omit(pydantic.BaseModel):
|
|
||||||
"""In certain situations you need to be able to represent a case where a default value has
|
|
||||||
to be explicitly removed and `None` is not an appropriate substitute, for example:
|
|
||||||
|
|
||||||
```py
|
|
||||||
# as the default `Content-Type` header is `application/json` that will be sent
|
|
||||||
client.post('/upload/files', files={'file': b'my raw file content'})
|
|
||||||
|
|
||||||
# you can't explicitly override the header as it has to be dynamically generated
|
|
||||||
# to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983'
|
|
||||||
client.post(..., headers={'Content-Type': 'multipart/form-data'})
|
|
||||||
|
|
||||||
# instead you can remove the default `application/json` header by passing Omit
|
|
||||||
client.post(..., headers={'Content-Type': Omit()})
|
|
||||||
```
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __bool__(self) -> Literal[False]:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
Headers = Mapping[str, Union[str, Omit]]
|
|
||||||
|
|
||||||
ResponseT = TypeVar(
|
|
||||||
"ResponseT",
|
|
||||||
bound="Union[str, None, BaseModel, list[Any], Dict[str, Any], Response, UnknownResponse, ModelBuilderProtocol, BinaryResponseContent]",
|
|
||||||
)
|
|
||||||
|
|
||||||
# for user input files
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
FileContent = Union[IO[bytes], bytes, PathLike[str]]
|
|
||||||
else:
|
|
||||||
FileContent = Union[IO[bytes], bytes, PathLike]
|
|
||||||
|
|
||||||
FileTypes = Union[
|
|
||||||
FileContent, # file content
|
|
||||||
tuple[str, FileContent], # (filename, file)
|
|
||||||
tuple[str, FileContent, str], # (filename, file , content_type)
|
|
||||||
tuple[
|
|
||||||
str, FileContent, str, Mapping[str, str]
|
|
||||||
], # (filename, file , content_type, headers)
|
|
||||||
]
|
|
||||||
|
|
||||||
RequestFiles = Union[Mapping[str, FileTypes], Sequence[tuple[str, FileTypes]]]
|
|
||||||
|
|
||||||
# for httpx client supported files
|
|
||||||
|
|
||||||
HttpxFileContent = Union[bytes, IO[bytes]]
|
|
||||||
HttpxFileTypes = Union[
|
|
||||||
FileContent, # file content
|
|
||||||
tuple[str, HttpxFileContent], # (filename, file)
|
|
||||||
tuple[str, HttpxFileContent, str], # (filename, file , content_type)
|
|
||||||
tuple[
|
|
||||||
str, HttpxFileContent, str, Mapping[str, str]
|
|
||||||
], # (filename, file , content_type, headers)
|
|
||||||
]
|
|
||||||
|
|
||||||
HttpxRequestFiles = Union[
|
|
||||||
Mapping[str, HttpxFileTypes], Sequence[tuple[str, HttpxFileTypes]]
|
|
||||||
]
|
|
||||||
@ -1,94 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"ZhipuAIError",
|
|
||||||
"APIStatusError",
|
|
||||||
"APIRequestFailedError",
|
|
||||||
"APIAuthenticationError",
|
|
||||||
"APIReachLimitError",
|
|
||||||
"APIInternalError",
|
|
||||||
"APIServerFlowExceedError",
|
|
||||||
"APIResponseError",
|
|
||||||
"APIResponseValidationError",
|
|
||||||
"APITimeoutError",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ZhipuAIError(Exception):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
message: str,
|
|
||||||
) -> None:
|
|
||||||
super().__init__(message)
|
|
||||||
|
|
||||||
|
|
||||||
class APIStatusError(Exception):
|
|
||||||
response: httpx.Response
|
|
||||||
status_code: int
|
|
||||||
|
|
||||||
def __init__(self, message: str, *, response: httpx.Response) -> None:
|
|
||||||
super().__init__(message)
|
|
||||||
self.response = response
|
|
||||||
self.status_code = response.status_code
|
|
||||||
|
|
||||||
|
|
||||||
class APIRequestFailedError(APIStatusError):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class APIAuthenticationError(APIStatusError):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class APIReachLimitError(APIStatusError):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class APIInternalError(APIStatusError):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class APIServerFlowExceedError(APIStatusError):
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
class APIResponseError(Exception):
|
|
||||||
message: str
|
|
||||||
request: httpx.Request
|
|
||||||
json_data: object
|
|
||||||
|
|
||||||
def __init__(self, message: str, request: httpx.Request, json_data: object):
|
|
||||||
self.message = message
|
|
||||||
self.request = request
|
|
||||||
self.json_data = json_data
|
|
||||||
super().__init__(message)
|
|
||||||
|
|
||||||
|
|
||||||
class APIResponseValidationError(APIResponseError):
|
|
||||||
status_code: int
|
|
||||||
response: httpx.Response
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
response: httpx.Response,
|
|
||||||
json_data: object | None,
|
|
||||||
*,
|
|
||||||
message: str | None = None,
|
|
||||||
) -> None:
|
|
||||||
super().__init__(
|
|
||||||
message=message or "Data returned by API invalid for expected schema.",
|
|
||||||
request=response.request,
|
|
||||||
json_data=json_data,
|
|
||||||
)
|
|
||||||
self.response = response
|
|
||||||
self.status_code = response.status_code
|
|
||||||
|
|
||||||
|
|
||||||
class APITimeoutError(Exception):
|
|
||||||
request: httpx.Request
|
|
||||||
|
|
||||||
def __init__(self, request: httpx.Request):
|
|
||||||
self.request = request
|
|
||||||
super().__init__("Request Timeout")
|
|
||||||
@ -1,45 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import io
|
|
||||||
import os
|
|
||||||
from collections.abc import Mapping, Sequence
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from ._base_type import FileTypes, HttpxFileTypes, HttpxRequestFiles, RequestFiles
|
|
||||||
|
|
||||||
|
|
||||||
def is_file_content(obj: object) -> bool:
|
|
||||||
return isinstance(obj, bytes | tuple | io.IOBase | os.PathLike)
|
|
||||||
|
|
||||||
|
|
||||||
def _transform_file(file: FileTypes) -> HttpxFileTypes:
|
|
||||||
if is_file_content(file):
|
|
||||||
if isinstance(file, os.PathLike):
|
|
||||||
path = Path(file)
|
|
||||||
return path.name, path.read_bytes()
|
|
||||||
else:
|
|
||||||
return file
|
|
||||||
if isinstance(file, tuple):
|
|
||||||
if isinstance(file[1], os.PathLike):
|
|
||||||
return (file[0], Path(file[1]).read_bytes(), *file[2:])
|
|
||||||
else:
|
|
||||||
return (file[0], file[1], *file[2:])
|
|
||||||
else:
|
|
||||||
raise TypeError(
|
|
||||||
f"Unexpected input file with type {type(file)},Expected FileContent type or tuple type"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def make_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles | None:
|
|
||||||
if files is None:
|
|
||||||
return None
|
|
||||||
|
|
||||||
if isinstance(files, Mapping):
|
|
||||||
files = {key: _transform_file(file) for key, file in files.items()}
|
|
||||||
elif isinstance(files, Sequence):
|
|
||||||
files = [(key, _transform_file(file)) for key, file in files]
|
|
||||||
else:
|
|
||||||
raise TypeError(
|
|
||||||
f"Unexpected input file with type {type(files)}, excepted Mapping or Sequence"
|
|
||||||
)
|
|
||||||
return files
|
|
||||||
@ -1,401 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import inspect
|
|
||||||
from collections.abc import Mapping
|
|
||||||
from typing import Any, Union, cast
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
import pydantic
|
|
||||||
from httpx import URL, Timeout
|
|
||||||
|
|
||||||
from . import _errors
|
|
||||||
from ._base_type import (
|
|
||||||
NOT_GIVEN,
|
|
||||||
Body,
|
|
||||||
Data,
|
|
||||||
Headers,
|
|
||||||
NotGiven,
|
|
||||||
Query,
|
|
||||||
RequestFiles,
|
|
||||||
ResponseT,
|
|
||||||
)
|
|
||||||
from ._errors import APIResponseValidationError, APIStatusError, APITimeoutError
|
|
||||||
from ._files import make_httpx_files
|
|
||||||
from ._request_opt import ClientRequestParam, UserRequestInput
|
|
||||||
from ._response import HttpResponse
|
|
||||||
from ._sse_client import StreamResponse
|
|
||||||
from ._utils import flatten
|
|
||||||
|
|
||||||
headers = {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Content-Type": "application/json; charset=UTF-8",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def _merge_map(map1: Mapping, map2: Mapping) -> Mapping:
|
|
||||||
merged = {**map1, **map2}
|
|
||||||
return {key: val for key, val in merged.items() if val is not None}
|
|
||||||
|
|
||||||
|
|
||||||
from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT
|
|
||||||
|
|
||||||
ZHIPUAI_DEFAULT_TIMEOUT = httpx.Timeout(timeout=300.0, connect=8.0)
|
|
||||||
ZHIPUAI_DEFAULT_MAX_RETRIES = 3
|
|
||||||
ZHIPUAI_DEFAULT_LIMITS = httpx.Limits(max_connections=5, max_keepalive_connections=5)
|
|
||||||
|
|
||||||
|
|
||||||
class HttpClient:
|
|
||||||
_client: httpx.Client
|
|
||||||
_version: str
|
|
||||||
_base_url: URL
|
|
||||||
|
|
||||||
timeout: Union[float, Timeout, None]
|
|
||||||
_limits: httpx.Limits
|
|
||||||
_has_custom_http_client: bool
|
|
||||||
_default_stream_cls: type[StreamResponse[Any]] | None = None
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
version: str,
|
|
||||||
base_url: URL,
|
|
||||||
timeout: Union[float, Timeout, None],
|
|
||||||
custom_httpx_client: httpx.Client | None = None,
|
|
||||||
custom_headers: Mapping[str, str] | None = None,
|
|
||||||
) -> None:
|
|
||||||
if timeout is None or isinstance(timeout, NotGiven):
|
|
||||||
if (
|
|
||||||
custom_httpx_client
|
|
||||||
and custom_httpx_client.timeout != HTTPX_DEFAULT_TIMEOUT
|
|
||||||
):
|
|
||||||
timeout = custom_httpx_client.timeout
|
|
||||||
else:
|
|
||||||
timeout = ZHIPUAI_DEFAULT_TIMEOUT
|
|
||||||
self.timeout = cast(Timeout, timeout)
|
|
||||||
self._has_custom_http_client = bool(custom_httpx_client)
|
|
||||||
self._client = custom_httpx_client or httpx.Client(
|
|
||||||
base_url=base_url,
|
|
||||||
timeout=self.timeout,
|
|
||||||
limits=ZHIPUAI_DEFAULT_LIMITS,
|
|
||||||
)
|
|
||||||
self._version = version
|
|
||||||
url = URL(url=base_url)
|
|
||||||
if not url.raw_path.endswith(b"/"):
|
|
||||||
url = url.copy_with(raw_path=url.raw_path + b"/")
|
|
||||||
self._base_url = url
|
|
||||||
self._custom_headers = custom_headers or {}
|
|
||||||
|
|
||||||
def _prepare_url(self, url: str) -> URL:
|
|
||||||
sub_url = URL(url)
|
|
||||||
if sub_url.is_relative_url:
|
|
||||||
request_raw_url = self._base_url.raw_path + sub_url.raw_path.lstrip(b"/")
|
|
||||||
return self._base_url.copy_with(raw_path=request_raw_url)
|
|
||||||
|
|
||||||
return sub_url
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _default_headers(self):
|
|
||||||
return {
|
|
||||||
"Accept": "application/json",
|
|
||||||
"Content-Type": "application/json; charset=UTF-8",
|
|
||||||
"ZhipuAI-SDK-Ver": self._version,
|
|
||||||
"source_type": "zhipu-sdk-python",
|
|
||||||
"x-request-sdk": "zhipu-sdk-python",
|
|
||||||
**self._auth_headers,
|
|
||||||
**self._custom_headers,
|
|
||||||
}
|
|
||||||
|
|
||||||
@property
|
|
||||||
def _auth_headers(self):
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def _prepare_headers(self, request_param: ClientRequestParam) -> httpx.Headers:
|
|
||||||
custom_headers = request_param.headers or {}
|
|
||||||
headers_dict = _merge_map(self._default_headers, custom_headers)
|
|
||||||
|
|
||||||
httpx_headers = httpx.Headers(headers_dict)
|
|
||||||
|
|
||||||
return httpx_headers
|
|
||||||
|
|
||||||
def _prepare_request(self, request_param: ClientRequestParam) -> httpx.Request:
|
|
||||||
kwargs: dict[str, Any] = {}
|
|
||||||
json_data = request_param.json_data
|
|
||||||
headers = self._prepare_headers(request_param)
|
|
||||||
url = self._prepare_url(request_param.url)
|
|
||||||
json_data = request_param.json_data
|
|
||||||
if headers.get("Content-Type") == "multipart/form-data":
|
|
||||||
headers.pop("Content-Type")
|
|
||||||
|
|
||||||
if json_data:
|
|
||||||
kwargs["data"] = self._make_multipartform(json_data)
|
|
||||||
|
|
||||||
return self._client.build_request(
|
|
||||||
headers=headers,
|
|
||||||
timeout=self.timeout
|
|
||||||
if isinstance(request_param.timeout, NotGiven)
|
|
||||||
else request_param.timeout,
|
|
||||||
method=request_param.method,
|
|
||||||
url=url,
|
|
||||||
json=json_data,
|
|
||||||
files=request_param.files,
|
|
||||||
params=request_param.params,
|
|
||||||
**kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _object_to_formfata(
|
|
||||||
self, key: str, value: Data | Mapping[object, object]
|
|
||||||
) -> list[tuple[str, str]]:
|
|
||||||
items = []
|
|
||||||
|
|
||||||
if isinstance(value, Mapping):
|
|
||||||
for k, v in value.items():
|
|
||||||
items.extend(self._object_to_formfata(f"{key}[{k}]", v))
|
|
||||||
return items
|
|
||||||
if isinstance(value, list | tuple):
|
|
||||||
for v in value:
|
|
||||||
items.extend(self._object_to_formfata(key + "[]", v))
|
|
||||||
return items
|
|
||||||
|
|
||||||
def _primitive_value_to_str(val) -> str:
|
|
||||||
# copied from httpx
|
|
||||||
if val is True:
|
|
||||||
return "true"
|
|
||||||
elif val is False:
|
|
||||||
return "false"
|
|
||||||
elif val is None:
|
|
||||||
return ""
|
|
||||||
return str(val)
|
|
||||||
|
|
||||||
str_data = _primitive_value_to_str(value)
|
|
||||||
|
|
||||||
if not str_data:
|
|
||||||
return []
|
|
||||||
return [(key, str_data)]
|
|
||||||
|
|
||||||
def _make_multipartform(self, data: Mapping[object, object]) -> dict[str, object]:
|
|
||||||
items = flatten([self._object_to_formfata(k, v) for k, v in data.items()])
|
|
||||||
|
|
||||||
serialized: dict[str, object] = {}
|
|
||||||
for key, value in items:
|
|
||||||
if key in serialized:
|
|
||||||
raise ValueError(f"存在重复的键: {key};")
|
|
||||||
serialized[key] = value
|
|
||||||
return serialized
|
|
||||||
|
|
||||||
def _parse_response(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
cast_type: type[ResponseT],
|
|
||||||
response: httpx.Response,
|
|
||||||
enable_stream: bool,
|
|
||||||
request_param: ClientRequestParam,
|
|
||||||
stream_cls: type[StreamResponse[Any]] | None = None,
|
|
||||||
) -> HttpResponse:
|
|
||||||
http_response = HttpResponse(
|
|
||||||
raw_response=response,
|
|
||||||
cast_type=cast_type,
|
|
||||||
client=self,
|
|
||||||
enable_stream=enable_stream,
|
|
||||||
stream_cls=stream_cls,
|
|
||||||
)
|
|
||||||
return http_response.parse()
|
|
||||||
|
|
||||||
def _process_response_data(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
data: object,
|
|
||||||
cast_type: type[ResponseT],
|
|
||||||
response: httpx.Response,
|
|
||||||
) -> ResponseT:
|
|
||||||
if data is None:
|
|
||||||
return cast(ResponseT, None)
|
|
||||||
|
|
||||||
try:
|
|
||||||
if inspect.isclass(cast_type) and issubclass(cast_type, pydantic.BaseModel):
|
|
||||||
return cast(ResponseT, cast_type.validate(data))
|
|
||||||
|
|
||||||
return cast(
|
|
||||||
ResponseT, pydantic.TypeAdapter(cast_type).validate_python(data)
|
|
||||||
)
|
|
||||||
except pydantic.ValidationError as err:
|
|
||||||
raise APIResponseValidationError(response=response, json_data=data) from err
|
|
||||||
|
|
||||||
def is_closed(self) -> bool:
|
|
||||||
return self._client.is_closed
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
self._client.close()
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
||||||
self.close()
|
|
||||||
|
|
||||||
def request(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
cast_type: type[ResponseT],
|
|
||||||
params: ClientRequestParam,
|
|
||||||
enable_stream: bool = False,
|
|
||||||
stream_cls: type[StreamResponse[Any]] | None = None,
|
|
||||||
) -> ResponseT | StreamResponse:
|
|
||||||
request = self._prepare_request(params)
|
|
||||||
|
|
||||||
try:
|
|
||||||
response = self._client.send(
|
|
||||||
request,
|
|
||||||
stream=enable_stream,
|
|
||||||
)
|
|
||||||
response.raise_for_status()
|
|
||||||
except httpx.TimeoutException as err:
|
|
||||||
raise APITimeoutError(request=request) from err
|
|
||||||
except httpx.HTTPStatusError as err:
|
|
||||||
err.response.read()
|
|
||||||
# raise err
|
|
||||||
raise self._make_status_error(err.response) from None
|
|
||||||
|
|
||||||
except Exception as err:
|
|
||||||
raise err
|
|
||||||
|
|
||||||
return self._parse_response(
|
|
||||||
cast_type=cast_type,
|
|
||||||
request_param=params,
|
|
||||||
response=response,
|
|
||||||
enable_stream=enable_stream,
|
|
||||||
stream_cls=stream_cls,
|
|
||||||
)
|
|
||||||
|
|
||||||
def get(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
*,
|
|
||||||
cast_type: type[ResponseT],
|
|
||||||
options: UserRequestInput = {},
|
|
||||||
enable_stream: bool = False,
|
|
||||||
) -> ResponseT | StreamResponse:
|
|
||||||
opts = ClientRequestParam.construct(method="get", url=path, **options)
|
|
||||||
return self.request(
|
|
||||||
cast_type=cast_type, params=opts, enable_stream=enable_stream
|
|
||||||
)
|
|
||||||
|
|
||||||
def post(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
*,
|
|
||||||
body: Body | None = None,
|
|
||||||
cast_type: type[ResponseT],
|
|
||||||
options: UserRequestInput = {},
|
|
||||||
files: RequestFiles | None = None,
|
|
||||||
enable_stream: bool = False,
|
|
||||||
stream_cls: type[StreamResponse[Any]] | None = None,
|
|
||||||
) -> ResponseT | StreamResponse:
|
|
||||||
opts = ClientRequestParam.construct(
|
|
||||||
method="post",
|
|
||||||
json_data=body,
|
|
||||||
files=make_httpx_files(files),
|
|
||||||
url=path,
|
|
||||||
**options,
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.request(
|
|
||||||
cast_type=cast_type,
|
|
||||||
params=opts,
|
|
||||||
enable_stream=enable_stream,
|
|
||||||
stream_cls=stream_cls,
|
|
||||||
)
|
|
||||||
|
|
||||||
def patch(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
*,
|
|
||||||
body: Body | None = None,
|
|
||||||
cast_type: type[ResponseT],
|
|
||||||
options: UserRequestInput = {},
|
|
||||||
) -> ResponseT:
|
|
||||||
opts = ClientRequestParam.construct(
|
|
||||||
method="patch", url=path, json_data=body, **options
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.request(
|
|
||||||
cast_type=cast_type,
|
|
||||||
params=opts,
|
|
||||||
)
|
|
||||||
|
|
||||||
def put(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
*,
|
|
||||||
body: Body | None = None,
|
|
||||||
cast_type: type[ResponseT],
|
|
||||||
options: UserRequestInput = {},
|
|
||||||
files: RequestFiles | None = None,
|
|
||||||
) -> ResponseT | StreamResponse:
|
|
||||||
opts = ClientRequestParam.construct(
|
|
||||||
method="put",
|
|
||||||
url=path,
|
|
||||||
json_data=body,
|
|
||||||
files=make_httpx_files(files),
|
|
||||||
**options,
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.request(
|
|
||||||
cast_type=cast_type,
|
|
||||||
params=opts,
|
|
||||||
)
|
|
||||||
|
|
||||||
def delete(
|
|
||||||
self,
|
|
||||||
path: str,
|
|
||||||
*,
|
|
||||||
body: Body | None = None,
|
|
||||||
cast_type: type[ResponseT],
|
|
||||||
options: UserRequestInput = {},
|
|
||||||
) -> ResponseT | StreamResponse:
|
|
||||||
opts = ClientRequestParam.construct(
|
|
||||||
method="delete", url=path, json_data=body, **options
|
|
||||||
)
|
|
||||||
|
|
||||||
return self.request(
|
|
||||||
cast_type=cast_type,
|
|
||||||
params=opts,
|
|
||||||
)
|
|
||||||
|
|
||||||
def _make_status_error(self, response) -> APIStatusError:
|
|
||||||
response_text = response.text.strip()
|
|
||||||
status_code = response.status_code
|
|
||||||
error_msg = f"Error code: {status_code}, with error text {response_text}"
|
|
||||||
|
|
||||||
if status_code == 400:
|
|
||||||
return _errors.APIRequestFailedError(message=error_msg, response=response)
|
|
||||||
elif status_code == 401:
|
|
||||||
return _errors.APIAuthenticationError(message=error_msg, response=response)
|
|
||||||
elif status_code == 429:
|
|
||||||
return _errors.APIReachLimitError(message=error_msg, response=response)
|
|
||||||
elif status_code == 500:
|
|
||||||
return _errors.APIInternalError(message=error_msg, response=response)
|
|
||||||
elif status_code == 503:
|
|
||||||
return _errors.APIServerFlowExceedError(
|
|
||||||
message=error_msg, response=response
|
|
||||||
)
|
|
||||||
return APIStatusError(message=error_msg, response=response)
|
|
||||||
|
|
||||||
|
|
||||||
def make_user_request_input(
|
|
||||||
max_retries: int | None = None,
|
|
||||||
timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
|
|
||||||
extra_headers: Headers = None,
|
|
||||||
query: Query | None = None,
|
|
||||||
) -> UserRequestInput:
|
|
||||||
options: UserRequestInput = {}
|
|
||||||
|
|
||||||
if extra_headers is not None:
|
|
||||||
options["headers"] = extra_headers
|
|
||||||
if max_retries is not None:
|
|
||||||
options["max_retries"] = max_retries
|
|
||||||
if not isinstance(timeout, NotGiven):
|
|
||||||
options["timeout"] = timeout
|
|
||||||
if query is not None:
|
|
||||||
options["params"] = query
|
|
||||||
|
|
||||||
return options
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
import time
|
|
||||||
|
|
||||||
import cachetools.func
|
|
||||||
import jwt
|
|
||||||
|
|
||||||
API_TOKEN_TTL_SECONDS = 3 * 60
|
|
||||||
|
|
||||||
CACHE_TTL_SECONDS = API_TOKEN_TTL_SECONDS - 30
|
|
||||||
|
|
||||||
|
|
||||||
@cachetools.func.ttl_cache(maxsize=10, ttl=CACHE_TTL_SECONDS)
|
|
||||||
def generate_token(apikey: str):
|
|
||||||
try:
|
|
||||||
api_key, secret = apikey.split(".")
|
|
||||||
except Exception as e:
|
|
||||||
raise Exception("invalid api_key", e)
|
|
||||||
|
|
||||||
payload = {
|
|
||||||
"api_key": api_key,
|
|
||||||
"exp": int(round(time.time() * 1000)) + API_TOKEN_TTL_SECONDS * 1000,
|
|
||||||
"timestamp": int(round(time.time() * 1000)),
|
|
||||||
}
|
|
||||||
ret = jwt.encode(
|
|
||||||
payload,
|
|
||||||
secret,
|
|
||||||
algorithm="HS256",
|
|
||||||
headers={"alg": "HS256", "sign_type": "SIGN"},
|
|
||||||
)
|
|
||||||
return ret
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Any, ClassVar, Union
|
|
||||||
|
|
||||||
from httpx import Timeout
|
|
||||||
from pydantic import ConfigDict
|
|
||||||
from typing_extensions import TypedDict, Unpack
|
|
||||||
|
|
||||||
from ._base_type import Body, Headers, HttpxRequestFiles, NotGiven, Query
|
|
||||||
from ._utils import remove_notgiven_indict
|
|
||||||
|
|
||||||
|
|
||||||
class UserRequestInput(TypedDict, total=False):
|
|
||||||
max_retries: int
|
|
||||||
timeout: float | Timeout | None
|
|
||||||
headers: Headers
|
|
||||||
params: Query | None
|
|
||||||
|
|
||||||
|
|
||||||
class ClientRequestParam:
|
|
||||||
method: str
|
|
||||||
url: str
|
|
||||||
max_retries: Union[int, NotGiven] = NotGiven()
|
|
||||||
timeout: Union[float, NotGiven] = NotGiven()
|
|
||||||
headers: Union[Headers, NotGiven] = NotGiven()
|
|
||||||
json_data: Union[Body, None] = None
|
|
||||||
files: Union[HttpxRequestFiles, None] = None
|
|
||||||
params: Query = {}
|
|
||||||
model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
|
|
||||||
|
|
||||||
def get_max_retries(self, max_retries) -> int:
|
|
||||||
if isinstance(self.max_retries, NotGiven):
|
|
||||||
return max_retries
|
|
||||||
return self.max_retries
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def construct( # type: ignore
|
|
||||||
cls,
|
|
||||||
_fields_set: set[str] | None = None,
|
|
||||||
**values: Unpack[UserRequestInput],
|
|
||||||
) -> ClientRequestParam:
|
|
||||||
kwargs: dict[str, Any] = {
|
|
||||||
key: remove_notgiven_indict(value) for key, value in values.items()
|
|
||||||
}
|
|
||||||
client = cls()
|
|
||||||
client.__dict__.update(kwargs)
|
|
||||||
|
|
||||||
return client
|
|
||||||
|
|
||||||
model_construct = construct
|
|
||||||
@ -1,123 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import datetime
|
|
||||||
from typing import TYPE_CHECKING, Any, Generic, TypeVar, cast, get_args, get_origin
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
import pydantic
|
|
||||||
from typing_extensions import ParamSpec
|
|
||||||
|
|
||||||
from ._base_type import NoneType
|
|
||||||
from ._sse_client import StreamResponse
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ._http_client import HttpClient
|
|
||||||
|
|
||||||
P = ParamSpec("P")
|
|
||||||
R = TypeVar("R")
|
|
||||||
|
|
||||||
|
|
||||||
class HttpResponse(Generic[R]):
|
|
||||||
_cast_type: type[R]
|
|
||||||
_client: HttpClient
|
|
||||||
_parsed: R | None
|
|
||||||
_enable_stream: bool
|
|
||||||
_stream_cls: type[StreamResponse[Any]]
|
|
||||||
http_response: httpx.Response
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
raw_response: httpx.Response,
|
|
||||||
cast_type: type[R],
|
|
||||||
client: HttpClient,
|
|
||||||
enable_stream: bool = False,
|
|
||||||
stream_cls: type[StreamResponse[Any]] | None = None,
|
|
||||||
) -> None:
|
|
||||||
self._cast_type = cast_type
|
|
||||||
self._client = client
|
|
||||||
self._parsed = None
|
|
||||||
self._stream_cls = stream_cls
|
|
||||||
self._enable_stream = enable_stream
|
|
||||||
self.http_response = raw_response
|
|
||||||
|
|
||||||
def parse(self) -> R:
|
|
||||||
self._parsed = self._parse()
|
|
||||||
return self._parsed
|
|
||||||
|
|
||||||
def _parse(self) -> R:
|
|
||||||
if self._enable_stream:
|
|
||||||
self._parsed = cast(
|
|
||||||
R,
|
|
||||||
self._stream_cls(
|
|
||||||
cast_type=cast(type, get_args(self._stream_cls)[0]),
|
|
||||||
response=self.http_response,
|
|
||||||
client=self._client,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
return self._parsed
|
|
||||||
cast_type = self._cast_type
|
|
||||||
if cast_type is NoneType:
|
|
||||||
return cast(R, None)
|
|
||||||
http_response = self.http_response
|
|
||||||
if cast_type == str:
|
|
||||||
return cast(R, http_response.text)
|
|
||||||
|
|
||||||
content_type, *_ = http_response.headers.get(
|
|
||||||
"content-type", "application/json"
|
|
||||||
).split(";")
|
|
||||||
origin = get_origin(cast_type) or cast_type
|
|
||||||
if content_type != "application/json":
|
|
||||||
if issubclass(origin, pydantic.BaseModel):
|
|
||||||
data = http_response.json()
|
|
||||||
return self._client._process_response_data(
|
|
||||||
data=data,
|
|
||||||
cast_type=cast_type, # type: ignore
|
|
||||||
response=http_response,
|
|
||||||
)
|
|
||||||
|
|
||||||
return http_response.text
|
|
||||||
|
|
||||||
data = http_response.json()
|
|
||||||
|
|
||||||
return self._client._process_response_data(
|
|
||||||
data=data,
|
|
||||||
cast_type=cast_type, # type: ignore
|
|
||||||
response=http_response,
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def headers(self) -> httpx.Headers:
|
|
||||||
return self.http_response.headers
|
|
||||||
|
|
||||||
@property
|
|
||||||
def http_request(self) -> httpx.Request:
|
|
||||||
return self.http_response.request
|
|
||||||
|
|
||||||
@property
|
|
||||||
def status_code(self) -> int:
|
|
||||||
return self.http_response.status_code
|
|
||||||
|
|
||||||
@property
|
|
||||||
def url(self) -> httpx.URL:
|
|
||||||
return self.http_response.url
|
|
||||||
|
|
||||||
@property
|
|
||||||
def method(self) -> str:
|
|
||||||
return self.http_request.method
|
|
||||||
|
|
||||||
@property
|
|
||||||
def content(self) -> bytes:
|
|
||||||
return self.http_response.content
|
|
||||||
|
|
||||||
@property
|
|
||||||
def text(self) -> str:
|
|
||||||
return self.http_response.text
|
|
||||||
|
|
||||||
@property
|
|
||||||
def http_version(self) -> str:
|
|
||||||
return self.http_response.http_version
|
|
||||||
|
|
||||||
@property
|
|
||||||
def elapsed(self) -> datetime.timedelta:
|
|
||||||
return self.http_response.elapsed
|
|
||||||
@ -1,155 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import json
|
|
||||||
from collections.abc import Iterator, Mapping
|
|
||||||
from typing import TYPE_CHECKING, Generic
|
|
||||||
|
|
||||||
import httpx
|
|
||||||
|
|
||||||
from ._base_type import ResponseT
|
|
||||||
from ._errors import APIResponseError
|
|
||||||
|
|
||||||
_FIELD_SEPARATOR = ":"
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ._http_client import HttpClient
|
|
||||||
|
|
||||||
|
|
||||||
class StreamResponse(Generic[ResponseT]):
|
|
||||||
response: httpx.Response
|
|
||||||
_cast_type: type[ResponseT]
|
|
||||||
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
*,
|
|
||||||
cast_type: type[ResponseT],
|
|
||||||
response: httpx.Response,
|
|
||||||
client: HttpClient,
|
|
||||||
) -> None:
|
|
||||||
self.response = response
|
|
||||||
self._cast_type = cast_type
|
|
||||||
self._data_process_func = client._process_response_data
|
|
||||||
self._stream_chunks = self.__stream__()
|
|
||||||
|
|
||||||
def __next__(self) -> ResponseT:
|
|
||||||
return self._stream_chunks.__next__()
|
|
||||||
|
|
||||||
def __iter__(self) -> Iterator[ResponseT]:
|
|
||||||
yield from self._stream_chunks
|
|
||||||
|
|
||||||
def __stream__(self) -> Iterator[ResponseT]:
|
|
||||||
sse_line_parser = SSELineParser()
|
|
||||||
iterator = sse_line_parser.iter_lines(self.response.iter_lines())
|
|
||||||
|
|
||||||
for sse in iterator:
|
|
||||||
if sse.data.startswith("[DONE]"):
|
|
||||||
break
|
|
||||||
|
|
||||||
if sse.event is None:
|
|
||||||
data = sse.json_data()
|
|
||||||
if isinstance(data, Mapping) and data.get("error"):
|
|
||||||
raise APIResponseError(
|
|
||||||
message="An error occurred during streaming",
|
|
||||||
request=self.response.request,
|
|
||||||
json_data=data["error"],
|
|
||||||
)
|
|
||||||
|
|
||||||
yield self._data_process_func(
|
|
||||||
data=data, cast_type=self._cast_type, response=self.response
|
|
||||||
)
|
|
||||||
for sse in iterator:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Event:
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
event: str | None = None,
|
|
||||||
data: str | None = None,
|
|
||||||
id: str | None = None,
|
|
||||||
retry: int | None = None,
|
|
||||||
):
|
|
||||||
self._event = event
|
|
||||||
self._data = data
|
|
||||||
self._id = id
|
|
||||||
self._retry = retry
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
data_len = len(self._data) if self._data else 0
|
|
||||||
return f"Event(event={self._event}, data={self._data} ,data_length={data_len}, id={self._id}, retry={self._retry}"
|
|
||||||
|
|
||||||
@property
|
|
||||||
def event(self):
|
|
||||||
return self._event
|
|
||||||
|
|
||||||
@property
|
|
||||||
def data(self):
|
|
||||||
return self._data
|
|
||||||
|
|
||||||
def json_data(self):
|
|
||||||
return json.loads(self._data)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def id(self):
|
|
||||||
return self._id
|
|
||||||
|
|
||||||
@property
|
|
||||||
def retry(self):
|
|
||||||
return self._retry
|
|
||||||
|
|
||||||
|
|
||||||
class SSELineParser:
|
|
||||||
_data: list[str]
|
|
||||||
_event: str | None
|
|
||||||
_retry: int | None
|
|
||||||
_id: str | None
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self._event = None
|
|
||||||
self._data = []
|
|
||||||
self._id = None
|
|
||||||
self._retry = None
|
|
||||||
|
|
||||||
def iter_lines(self, lines: Iterator[str]) -> Iterator[Event]:
|
|
||||||
for line in lines:
|
|
||||||
line = line.rstrip("\n")
|
|
||||||
if not line:
|
|
||||||
if (
|
|
||||||
self._event is None
|
|
||||||
and not self._data
|
|
||||||
and self._id is None
|
|
||||||
and self._retry is None
|
|
||||||
):
|
|
||||||
continue
|
|
||||||
sse_event = Event(
|
|
||||||
event=self._event,
|
|
||||||
data="\n".join(self._data),
|
|
||||||
id=self._id,
|
|
||||||
retry=self._retry,
|
|
||||||
)
|
|
||||||
self._event = None
|
|
||||||
self._data = []
|
|
||||||
self._id = None
|
|
||||||
self._retry = None
|
|
||||||
|
|
||||||
yield sse_event
|
|
||||||
self.decode_line(line)
|
|
||||||
|
|
||||||
def decode_line(self, line: str):
|
|
||||||
if line.startswith(":") or not line:
|
|
||||||
return
|
|
||||||
|
|
||||||
field, _p, value = line.partition(":")
|
|
||||||
|
|
||||||
if value.startswith(" "):
|
|
||||||
value = value[1:]
|
|
||||||
if field == "data":
|
|
||||||
self._data.append(value)
|
|
||||||
elif field == "event":
|
|
||||||
self._event = value
|
|
||||||
elif field == "retry":
|
|
||||||
try:
|
|
||||||
self._retry = int(value)
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
pass
|
|
||||||
return
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from collections.abc import Iterable, Mapping
|
|
||||||
from typing import TypeVar
|
|
||||||
|
|
||||||
from ._base_type import NotGiven
|
|
||||||
|
|
||||||
|
|
||||||
def remove_notgiven_indict(obj):
|
|
||||||
if obj is None or (not isinstance(obj, Mapping)):
|
|
||||||
return obj
|
|
||||||
return {key: value for key, value in obj.items() if not isinstance(value, NotGiven)}
|
|
||||||
|
|
||||||
|
|
||||||
_T = TypeVar("_T")
|
|
||||||
|
|
||||||
|
|
||||||
def flatten(t: Iterable[Iterable[_T]]) -> list[_T]:
|
|
||||||
return [item for sublist in t for item in sublist]
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
from .chat_completion import CompletionChoice, CompletionUsage
|
|
||||||
|
|
||||||
__all__ = ["AsyncTaskStatus"]
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncTaskStatus(BaseModel):
|
|
||||||
id: Optional[str] = None
|
|
||||||
request_id: Optional[str] = None
|
|
||||||
model: Optional[str] = None
|
|
||||||
task_status: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class AsyncCompletion(BaseModel):
|
|
||||||
id: Optional[str] = None
|
|
||||||
request_id: Optional[str] = None
|
|
||||||
model: Optional[str] = None
|
|
||||||
task_status: str
|
|
||||||
choices: list[CompletionChoice]
|
|
||||||
usage: CompletionUsage
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
__all__ = ["Completion", "CompletionUsage"]
|
|
||||||
|
|
||||||
|
|
||||||
class Function(BaseModel):
|
|
||||||
arguments: str
|
|
||||||
name: str
|
|
||||||
|
|
||||||
|
|
||||||
class CompletionMessageToolCall(BaseModel):
|
|
||||||
id: str
|
|
||||||
function: Function
|
|
||||||
type: str
|
|
||||||
|
|
||||||
|
|
||||||
class CompletionMessage(BaseModel):
|
|
||||||
content: Optional[str] = None
|
|
||||||
role: str
|
|
||||||
tool_calls: Optional[list[CompletionMessageToolCall]] = None
|
|
||||||
|
|
||||||
|
|
||||||
class CompletionUsage(BaseModel):
|
|
||||||
prompt_tokens: int
|
|
||||||
completion_tokens: int
|
|
||||||
total_tokens: int
|
|
||||||
|
|
||||||
|
|
||||||
class CompletionChoice(BaseModel):
|
|
||||||
index: int
|
|
||||||
finish_reason: str
|
|
||||||
message: CompletionMessage
|
|
||||||
|
|
||||||
|
|
||||||
class Completion(BaseModel):
|
|
||||||
model: Optional[str] = None
|
|
||||||
created: Optional[int] = None
|
|
||||||
choices: list[CompletionChoice]
|
|
||||||
request_id: Optional[str] = None
|
|
||||||
id: Optional[str] = None
|
|
||||||
usage: CompletionUsage
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
__all__ = [
|
|
||||||
"ChatCompletionChunk",
|
|
||||||
"Choice",
|
|
||||||
"ChoiceDelta",
|
|
||||||
"ChoiceDeltaFunctionCall",
|
|
||||||
"ChoiceDeltaToolCall",
|
|
||||||
"ChoiceDeltaToolCallFunction",
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ChoiceDeltaFunctionCall(BaseModel):
|
|
||||||
arguments: Optional[str] = None
|
|
||||||
name: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class ChoiceDeltaToolCallFunction(BaseModel):
|
|
||||||
arguments: Optional[str] = None
|
|
||||||
name: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class ChoiceDeltaToolCall(BaseModel):
|
|
||||||
index: int
|
|
||||||
id: Optional[str] = None
|
|
||||||
function: Optional[ChoiceDeltaToolCallFunction] = None
|
|
||||||
type: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class ChoiceDelta(BaseModel):
|
|
||||||
content: Optional[str] = None
|
|
||||||
role: Optional[str] = None
|
|
||||||
tool_calls: Optional[list[ChoiceDeltaToolCall]] = None
|
|
||||||
|
|
||||||
|
|
||||||
class Choice(BaseModel):
|
|
||||||
delta: ChoiceDelta
|
|
||||||
finish_reason: Optional[str] = None
|
|
||||||
index: int
|
|
||||||
|
|
||||||
|
|
||||||
class CompletionUsage(BaseModel):
|
|
||||||
prompt_tokens: int
|
|
||||||
completion_tokens: int
|
|
||||||
total_tokens: int
|
|
||||||
|
|
||||||
|
|
||||||
class ChatCompletionChunk(BaseModel):
|
|
||||||
id: Optional[str] = None
|
|
||||||
choices: list[Choice]
|
|
||||||
created: Optional[int] = None
|
|
||||||
model: Optional[str] = None
|
|
||||||
usage: Optional[CompletionUsage] = None
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
from typing_extensions import TypedDict
|
|
||||||
|
|
||||||
|
|
||||||
class Reference(TypedDict, total=False):
|
|
||||||
enable: Optional[bool]
|
|
||||||
search_query: Optional[str]
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
from .chat.chat_completion import CompletionUsage
|
|
||||||
|
|
||||||
__all__ = ["Embedding", "EmbeddingsResponded"]
|
|
||||||
|
|
||||||
|
|
||||||
class Embedding(BaseModel):
|
|
||||||
object: str
|
|
||||||
index: Optional[int] = None
|
|
||||||
embedding: list[float]
|
|
||||||
|
|
||||||
|
|
||||||
class EmbeddingsResponded(BaseModel):
|
|
||||||
object: str
|
|
||||||
data: list[Embedding]
|
|
||||||
model: str
|
|
||||||
usage: CompletionUsage
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
__all__ = ["FileObject"]
|
|
||||||
|
|
||||||
|
|
||||||
class FileObject(BaseModel):
|
|
||||||
id: Optional[str] = None
|
|
||||||
bytes: Optional[int] = None
|
|
||||||
created_at: Optional[int] = None
|
|
||||||
filename: Optional[str] = None
|
|
||||||
object: Optional[str] = None
|
|
||||||
purpose: Optional[str] = None
|
|
||||||
status: Optional[str] = None
|
|
||||||
status_details: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class ListOfFileObject(BaseModel):
|
|
||||||
object: Optional[str] = None
|
|
||||||
data: list[FileObject]
|
|
||||||
has_more: Optional[bool] = None
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from .fine_tuning_job import FineTuningJob as FineTuningJob
|
|
||||||
from .fine_tuning_job import ListOfFineTuningJob as ListOfFineTuningJob
|
|
||||||
from .fine_tuning_job_event import FineTuningJobEvent as FineTuningJobEvent
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
from typing import Optional, Union
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
__all__ = ["FineTuningJob", "Error", "Hyperparameters", "ListOfFineTuningJob"]
|
|
||||||
|
|
||||||
|
|
||||||
class Error(BaseModel):
|
|
||||||
code: str
|
|
||||||
message: str
|
|
||||||
param: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class Hyperparameters(BaseModel):
|
|
||||||
n_epochs: Union[str, int, None] = None
|
|
||||||
|
|
||||||
|
|
||||||
class FineTuningJob(BaseModel):
|
|
||||||
id: Optional[str] = None
|
|
||||||
|
|
||||||
request_id: Optional[str] = None
|
|
||||||
|
|
||||||
created_at: Optional[int] = None
|
|
||||||
|
|
||||||
error: Optional[Error] = None
|
|
||||||
|
|
||||||
fine_tuned_model: Optional[str] = None
|
|
||||||
|
|
||||||
finished_at: Optional[int] = None
|
|
||||||
|
|
||||||
hyperparameters: Optional[Hyperparameters] = None
|
|
||||||
|
|
||||||
model: Optional[str] = None
|
|
||||||
|
|
||||||
object: Optional[str] = None
|
|
||||||
|
|
||||||
result_files: list[str]
|
|
||||||
|
|
||||||
status: str
|
|
||||||
|
|
||||||
trained_tokens: Optional[int] = None
|
|
||||||
|
|
||||||
training_file: str
|
|
||||||
|
|
||||||
validation_file: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class ListOfFineTuningJob(BaseModel):
|
|
||||||
object: Optional[str] = None
|
|
||||||
data: list[FineTuningJob]
|
|
||||||
has_more: Optional[bool] = None
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
from typing import Optional, Union
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
__all__ = ["FineTuningJobEvent", "Metric", "JobEvent"]
|
|
||||||
|
|
||||||
|
|
||||||
class Metric(BaseModel):
|
|
||||||
epoch: Optional[Union[str, int, float]] = None
|
|
||||||
current_steps: Optional[int] = None
|
|
||||||
total_steps: Optional[int] = None
|
|
||||||
elapsed_time: Optional[str] = None
|
|
||||||
remaining_time: Optional[str] = None
|
|
||||||
trained_tokens: Optional[int] = None
|
|
||||||
loss: Optional[Union[str, int, float]] = None
|
|
||||||
eval_loss: Optional[Union[str, int, float]] = None
|
|
||||||
acc: Optional[Union[str, int, float]] = None
|
|
||||||
eval_acc: Optional[Union[str, int, float]] = None
|
|
||||||
learning_rate: Optional[Union[str, int, float]] = None
|
|
||||||
|
|
||||||
|
|
||||||
class JobEvent(BaseModel):
|
|
||||||
object: Optional[str] = None
|
|
||||||
id: Optional[str] = None
|
|
||||||
type: Optional[str] = None
|
|
||||||
created_at: Optional[int] = None
|
|
||||||
level: Optional[str] = None
|
|
||||||
message: Optional[str] = None
|
|
||||||
data: Optional[Metric] = None
|
|
||||||
|
|
||||||
|
|
||||||
class FineTuningJobEvent(BaseModel):
|
|
||||||
object: Optional[str] = None
|
|
||||||
data: list[JobEvent]
|
|
||||||
has_more: Optional[bool] = None
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Literal, Union
|
|
||||||
|
|
||||||
from typing_extensions import TypedDict
|
|
||||||
|
|
||||||
__all__ = ["Hyperparameters"]
|
|
||||||
|
|
||||||
|
|
||||||
class Hyperparameters(TypedDict, total=False):
|
|
||||||
batch_size: Union[Literal["auto"], int]
|
|
||||||
|
|
||||||
learning_rate_multiplier: Union[Literal["auto"], float]
|
|
||||||
|
|
||||||
n_epochs: Union[Literal["auto"], int]
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from typing import Optional
|
|
||||||
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
__all__ = ["GeneratedImage", "ImagesResponded"]
|
|
||||||
|
|
||||||
|
|
||||||
class GeneratedImage(BaseModel):
|
|
||||||
b64_json: Optional[str] = None
|
|
||||||
url: Optional[str] = None
|
|
||||||
revised_prompt: Optional[str] = None
|
|
||||||
|
|
||||||
|
|
||||||
class ImagesResponded(BaseModel):
|
|
||||||
created: int
|
|
||||||
data: list[GeneratedImage]
|
|
||||||
Loading…
x
Reference in New Issue
Block a user