langchain-deepseek 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- langchain_deepseek-0.0.1/PKG-INFO +73 -0
- langchain_deepseek-0.0.1/README.md +56 -0
- langchain_deepseek-0.0.1/langchain_deepseek/__init__.py +6 -0
- langchain_deepseek-0.0.1/langchain_deepseek/api.py +77 -0
- langchain_deepseek-0.0.1/langchain_deepseek/base.py +199 -0
- langchain_deepseek-0.0.1/langchain_deepseek/const.py +3 -0
- langchain_deepseek-0.0.1/langchain_deepseek/exceptions.py +62 -0
- langchain_deepseek-0.0.1/langchain_deepseek/tools.py +1 -0
- langchain_deepseek-0.0.1/pyproject.toml +20 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: langchain-deepseek
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary:
|
|
5
|
+
Author: SyJarvis
|
|
6
|
+
Author-email: 1755115828@qq.com
|
|
7
|
+
Requires-Python: >=3.9,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Requires-Dist: langchain (>=0.2.6)
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
<center><h2>🚀 Langchain-Deepseek: Using the deepseek model in langchain</h2></center>
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
* Install from source (Recommend)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
cd langchain-deepseek
|
|
24
|
+
pip install -e .
|
|
25
|
+
```
|
|
26
|
+
* Install from PyPI
|
|
27
|
+
```bash
|
|
28
|
+
pip install langchain-deepseek
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
* All the code can be found in the `examples`
|
|
33
|
+
* Set DeepSeek API key in environment if using DeepSeek models: `export DEEPSEEK_API_KEY="sk-...".`
|
|
34
|
+
* Maybe you can try loading environment variables like this. Create a new `.env` file
|
|
35
|
+
```
|
|
36
|
+
DEEPSEEK_API_KEY="sk-..."
|
|
37
|
+
```
|
|
38
|
+
```python
|
|
39
|
+
from dotenv import load_dotenv, find_dotenv
|
|
40
|
+
load_dotenv(find_dotenv(), override=True)
|
|
41
|
+
```
|
|
42
|
+
##### It works with the Langchain library
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
from langchain_deepseek import ChatDeepSeekAI
|
|
46
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
47
|
+
|
|
48
|
+
llm = ChatDeepSeekAI(
|
|
49
|
+
model="deepseek-chat",
|
|
50
|
+
api_key="sk-...",
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
output_parser = StrOutputParser()
|
|
54
|
+
chain = llm | output_parser
|
|
55
|
+
response = chain.invoke("太阳系有几大行星?")
|
|
56
|
+
print(response)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
## 🌟Citation
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
@article{guo2025langchain-deepseek,
|
|
65
|
+
title={langchain-deepseek: Using the deepseek model in langchain},
|
|
66
|
+
author={Runke Zhong},
|
|
67
|
+
year={2025}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
**保持热爱,奔赴星海!**
|
|
71
|
+
|
|
72
|
+
*这个世界上唯有两样东西能让我们的心灵感到深深的震撼:一是我们头上灿烂的星空,一是我们内心崇高的道德法则*
|
|
73
|
+
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
<center><h2>🚀 Langchain-Deepseek: Using the deepseek model in langchain</h2></center>
|
|
2
|
+
## Install
|
|
3
|
+
|
|
4
|
+
* Install from source (Recommend)
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
cd langchain-deepseek
|
|
8
|
+
pip install -e .
|
|
9
|
+
```
|
|
10
|
+
* Install from PyPI
|
|
11
|
+
```bash
|
|
12
|
+
pip install langchain-deepseek
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
* All the code can be found in the `examples`
|
|
17
|
+
* Set DeepSeek API key in environment if using DeepSeek models: `export DEEPSEEK_API_KEY="sk-...".`
|
|
18
|
+
* Maybe you can try loading environment variables like this. Create a new `.env` file
|
|
19
|
+
```
|
|
20
|
+
DEEPSEEK_API_KEY="sk-..."
|
|
21
|
+
```
|
|
22
|
+
```python
|
|
23
|
+
from dotenv import load_dotenv, find_dotenv
|
|
24
|
+
load_dotenv(find_dotenv(), override=True)
|
|
25
|
+
```
|
|
26
|
+
##### It works with the Langchain library
|
|
27
|
+
|
|
28
|
+
```python
|
|
29
|
+
from langchain_deepseek import ChatDeepSeekAI
|
|
30
|
+
from langchain_core.output_parsers import StrOutputParser
|
|
31
|
+
|
|
32
|
+
llm = ChatDeepSeekAI(
|
|
33
|
+
model="deepseek-chat",
|
|
34
|
+
api_key="sk-...",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
output_parser = StrOutputParser()
|
|
38
|
+
chain = llm | output_parser
|
|
39
|
+
response = chain.invoke("太阳系有几大行星?")
|
|
40
|
+
print(response)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
## 🌟Citation
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
@article{guo2025langchain-deepseek,
|
|
49
|
+
title={langchain-deepseek: Using the deepseek model in langchain},
|
|
50
|
+
author={Runke Zhong},
|
|
51
|
+
year={2025}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
**保持热爱,奔赴星海!**
|
|
55
|
+
|
|
56
|
+
*这个世界上唯有两样东西能让我们的心灵感到深深的震撼:一是我们头上灿烂的星空,一是我们内心崇高的道德法则*
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field
|
|
2
|
+
import os
|
|
3
|
+
import requests
|
|
4
|
+
import json
|
|
5
|
+
from .const import DEFAULT_BASE_URL
|
|
6
|
+
from .exceptions import AuthenticationError, BadRequestError, APITimeoutError
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RestAPI:
|
|
10
|
+
"""
|
|
11
|
+
访问大模型的REST API
|
|
12
|
+
"""
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
base_url: str = None,
|
|
16
|
+
api_key: str = None
|
|
17
|
+
):
|
|
18
|
+
env_base_url = os.getenv("DEEPSEEK_BASE_URL")
|
|
19
|
+
env_api_key = os.getenv("DEEPSEEK_API_KEY")
|
|
20
|
+
if base_url is None:
|
|
21
|
+
base_url = env_base_url if env_base_url else DEFAULT_BASE_URL
|
|
22
|
+
if api_key is None:
|
|
23
|
+
if env_api_key is None:
|
|
24
|
+
raise ValueError("`DEEPSEEK_API_KEY` is required")
|
|
25
|
+
else:
|
|
26
|
+
api_key = env_api_key
|
|
27
|
+
self.base_url = base_url
|
|
28
|
+
self.api_key = api_key
|
|
29
|
+
self.session = requests.Session()
|
|
30
|
+
|
|
31
|
+
def action_post(self, request_path: str, datas: str=None, **kwargs):
|
|
32
|
+
"""POST"""
|
|
33
|
+
url = self.base_url + "/" + request_path
|
|
34
|
+
headers = self._generate_headers()
|
|
35
|
+
payload = datas if datas else json.dumps(kwargs)
|
|
36
|
+
for _ in range(3):
|
|
37
|
+
try:
|
|
38
|
+
response = self.session.post(url, headers=headers, data=payload)
|
|
39
|
+
except Exception as e:
|
|
40
|
+
print("Retry for HTTP Error ...")
|
|
41
|
+
continue
|
|
42
|
+
else:
|
|
43
|
+
if response.status_code == 401:
|
|
44
|
+
raise AuthenticationError(
|
|
45
|
+
"认证失败:无效的API密钥或未授权访问",
|
|
46
|
+
response=response,
|
|
47
|
+
body=response.text
|
|
48
|
+
)
|
|
49
|
+
elif response.status_code == 400:
|
|
50
|
+
raise BadRequestError(
|
|
51
|
+
"请求参数错误",
|
|
52
|
+
response=response,
|
|
53
|
+
body=response.text
|
|
54
|
+
)
|
|
55
|
+
elif response.status_code == 200:
|
|
56
|
+
break
|
|
57
|
+
else:
|
|
58
|
+
raise APITimeoutError("连接超时")
|
|
59
|
+
|
|
60
|
+
if response.text:
|
|
61
|
+
resp = response.json()
|
|
62
|
+
return resp
|
|
63
|
+
else:
|
|
64
|
+
return {}
|
|
65
|
+
|
|
66
|
+
def close(self):
|
|
67
|
+
"""关闭会话"""
|
|
68
|
+
self.session.close()
|
|
69
|
+
|
|
70
|
+
def _generate_headers(self, token=None) -> dict:
|
|
71
|
+
if token is None:
|
|
72
|
+
token = self.api_key
|
|
73
|
+
return {
|
|
74
|
+
"Content-Type": 'application/json',
|
|
75
|
+
'Accept': 'application/json',
|
|
76
|
+
'Authorization': f'Bearer {token}'
|
|
77
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from typing import (
|
|
3
|
+
Dict,
|
|
4
|
+
List,
|
|
5
|
+
Any,
|
|
6
|
+
Optional,
|
|
7
|
+
Mapping,
|
|
8
|
+
Tuple
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from langchain_core.language_models.chat_models import (
|
|
12
|
+
BaseChatModel,
|
|
13
|
+
generate_from_stream
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
from langchain_core.messages import (
|
|
17
|
+
BaseMessage
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from langchain_core.outputs import (
|
|
21
|
+
ChatResult,
|
|
22
|
+
ChatGeneration
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from langchain_core.callbacks import (
|
|
26
|
+
CallbackManagerForLLMRun
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from langchain_community.adapters.openai import (
|
|
30
|
+
convert_message_to_dict,
|
|
31
|
+
convert_dict_to_message
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
from langchain_core.pydantic_v1 import BaseModel, Field, root_validator
|
|
35
|
+
|
|
36
|
+
from .api import RestAPI
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ChatDeepSeekAI(BaseChatModel):
|
|
40
|
+
"""`DeepSeek` Chat large language models API.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def lc_secrets(self) -> Dict[str, str]:
|
|
46
|
+
return {"deepseek_api_key": "DEEPSEEK_API_KEY"}
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def get_lc_namespace(cls) -> List[str]:
|
|
50
|
+
"""Return"""
|
|
51
|
+
return ["langchain", "chat_models", "DeepSeek"]
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def lc_attributes(self) -> Dict[str, Any]:
|
|
55
|
+
attributes: Dict[str, Any] = {}
|
|
56
|
+
return attributes
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def _llm_type(self) -> str:
|
|
60
|
+
"""Return the type of chat model."""
|
|
61
|
+
return "DeepSeekAI"
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def _identifying_params(self) -> Mapping[str, Any]:
|
|
65
|
+
return {**{"model_name": self.model}, **self._default_params}
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def _default_params(self) -> Dict[str, Any]:
|
|
69
|
+
params = {
|
|
70
|
+
"model": self.model,
|
|
71
|
+
"stream": self.streaming,
|
|
72
|
+
"n": self.n,
|
|
73
|
+
"temperature": self.temperature,
|
|
74
|
+
# **self.model_kwargs
|
|
75
|
+
}
|
|
76
|
+
if self.max_tokens is not None:
|
|
77
|
+
params["max_tokens"] = self.max_tokens
|
|
78
|
+
return params
|
|
79
|
+
|
|
80
|
+
"""deepseek client"""
|
|
81
|
+
client: Any = Field(default=None, exclude=True)
|
|
82
|
+
"""model name use"""
|
|
83
|
+
model: str = Field(default="deepseek-chat")
|
|
84
|
+
api_key: Optional[str] = Field(default=None, exclude=True)
|
|
85
|
+
base_url: Optional[str] = Field(default=None)
|
|
86
|
+
temperature: Optional[float] = Field(default=1)
|
|
87
|
+
top_p: Optional[float] = Field(default=1)
|
|
88
|
+
request_id: Optional[str] = Field(default=None)
|
|
89
|
+
max_tokens: Optional[int] = Field(default=2048)
|
|
90
|
+
streaming: Optional[bool] = Field(default=False)
|
|
91
|
+
n: Optional[int] = Field(default=1)
|
|
92
|
+
response_format: Dict[str, str] = Field(default={"type": "text"})
|
|
93
|
+
frequency_penalty: Optional[int] = Field(default=0)
|
|
94
|
+
presence_penalty: Optional[int] = Field(default=0)
|
|
95
|
+
tools: Any = Field(default=None)
|
|
96
|
+
tool_choice: Optional[str] = Field(default="none")
|
|
97
|
+
logprobs: Optional[bool] = Field(default=False)
|
|
98
|
+
top_logprobs: Optional[int] = Field(default=None)
|
|
99
|
+
|
|
100
|
+
@classmethod
|
|
101
|
+
def filter_model_kwargs(cls):
|
|
102
|
+
"""
|
|
103
|
+
"""
|
|
104
|
+
return [
|
|
105
|
+
"model",
|
|
106
|
+
"frequency_penalty",
|
|
107
|
+
"max_tokens",
|
|
108
|
+
"presence_penalty",
|
|
109
|
+
"response_format",
|
|
110
|
+
"stop",
|
|
111
|
+
"stream",
|
|
112
|
+
"temperature",
|
|
113
|
+
"top_p",
|
|
114
|
+
"tools",
|
|
115
|
+
"tool_choice",
|
|
116
|
+
"logprobs",
|
|
117
|
+
"top_logprobs",
|
|
118
|
+
"request_id"
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
def _generate(
|
|
122
|
+
self,
|
|
123
|
+
messages: List[BaseMessage],
|
|
124
|
+
stop: Optional[List[str]] = None,
|
|
125
|
+
run_manager: Optional[CallbackManagerForLLMRun] = None,
|
|
126
|
+
stream: Optional[bool] = None,
|
|
127
|
+
**kwargs: Any
|
|
128
|
+
) -> ChatResult:
|
|
129
|
+
should_stream = stream if stream is not None else self.streaming
|
|
130
|
+
if should_stream:
|
|
131
|
+
stream_iter = self._stream(
|
|
132
|
+
messages, stop=stop, run_manager=run_manager, **kwargs
|
|
133
|
+
)
|
|
134
|
+
return generate_from_stream(stream_iter)
|
|
135
|
+
message_dict, params = self._create_message_dicts(messages, stop)
|
|
136
|
+
response = self.completion_with_retry(
|
|
137
|
+
message_dict=message_dict,
|
|
138
|
+
run_manager=run_manager,
|
|
139
|
+
params=params
|
|
140
|
+
)
|
|
141
|
+
return self._create_chat_result(response)
|
|
142
|
+
|
|
143
|
+
def completion_with_retry(
|
|
144
|
+
self, message_dict=None, run_manager: Optional[CallbackManagerForLLMRun] = None, **kwargs
|
|
145
|
+
):
|
|
146
|
+
params = kwargs["params"]
|
|
147
|
+
params.update({"messages": message_dict})
|
|
148
|
+
try:
|
|
149
|
+
self.client = RestAPI(base_url=self.base_url, api_key=self.api_key)
|
|
150
|
+
reply = self.client.action_post(request_path=f"chat/completions", **params)
|
|
151
|
+
except Exception as e:
|
|
152
|
+
raise e
|
|
153
|
+
return reply
|
|
154
|
+
|
|
155
|
+
def _create_chat_result(self, response):
|
|
156
|
+
generations = []
|
|
157
|
+
id = response.get("id")
|
|
158
|
+
if not isinstance(response, dict):
|
|
159
|
+
response = response.dict()
|
|
160
|
+
for res in response["choices"]:
|
|
161
|
+
message_dict = res["message"]
|
|
162
|
+
message = convert_dict_to_message(message_dict)
|
|
163
|
+
generation_info = dict(finish_reason=res.get("finish_reason"))
|
|
164
|
+
gen = ChatGeneration(
|
|
165
|
+
message=message,
|
|
166
|
+
generation_info=generation_info,
|
|
167
|
+
)
|
|
168
|
+
generations.append(gen)
|
|
169
|
+
token_usage = response.get("usage", {})
|
|
170
|
+
llm_output = {
|
|
171
|
+
"id": id,
|
|
172
|
+
"created": response.get("created"),
|
|
173
|
+
"token_usage": token_usage,
|
|
174
|
+
"model_name": self.model,
|
|
175
|
+
}
|
|
176
|
+
return ChatResult(generations=generations, llm_output=llm_output)
|
|
177
|
+
|
|
178
|
+
def _create_message_dicts(
|
|
179
|
+
self, messages: List[BaseMessage], stop: Optional[List[str]]
|
|
180
|
+
) -> Tuple[List[Dict[str, Any]], Dict[str, Any]]:
|
|
181
|
+
params = self.get_model_kwargs()
|
|
182
|
+
params.update({"stream": False})
|
|
183
|
+
if stop is not None:
|
|
184
|
+
if "stop" in params:
|
|
185
|
+
raise ValueError("`stop` found in both the input and default params.")
|
|
186
|
+
params.update({"stop": stop})
|
|
187
|
+
message_dicts = [convert_message_to_dict(message) for message in messages]
|
|
188
|
+
# 传递prompt
|
|
189
|
+
params.update({"messages": message_dicts})
|
|
190
|
+
return message_dicts, params
|
|
191
|
+
|
|
192
|
+
def get_model_kwargs(self):
|
|
193
|
+
attrs = {}
|
|
194
|
+
for cls in inspect.getmro(self.__class__):
|
|
195
|
+
attrs.update(vars(cls))
|
|
196
|
+
attrs.update((vars(self)))
|
|
197
|
+
return {
|
|
198
|
+
attr: value for attr, value in attrs.items() if attr in self.__class__.filter_model_kwargs() and value is not None
|
|
199
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
import requests
|
|
7
|
+
from typing import Any, Optional, Literal
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"AuthenticationError",
|
|
11
|
+
"BadRequestError",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class DeepSeekError(Exception):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class APIError(DeepSeekError):
|
|
20
|
+
message: str = None
|
|
21
|
+
request: Optional[httpx.Request] = None
|
|
22
|
+
body: Any = None
|
|
23
|
+
code: Optional[str] = None
|
|
24
|
+
param: Optional[str] = None
|
|
25
|
+
type: Optional[str]
|
|
26
|
+
|
|
27
|
+
def __init__(self, message: str, request: httpx.Request, *, body: object | None) -> None:
|
|
28
|
+
self.message = message
|
|
29
|
+
self.request = request
|
|
30
|
+
self.body = body
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class APIStatusError(APIError):
|
|
34
|
+
"""Raised when an API response has a status code of 4xx or 5xx."""
|
|
35
|
+
|
|
36
|
+
response: httpx.Response
|
|
37
|
+
status_code: int
|
|
38
|
+
request_id: str | None
|
|
39
|
+
|
|
40
|
+
def __init__(self, message: str, *, response: httpx.Response, body: object | None) -> None:
|
|
41
|
+
super().__init__(message, response.request, body=body)
|
|
42
|
+
self.response = response
|
|
43
|
+
self.status_code = response.status_code
|
|
44
|
+
self.request_id = response.headers.get("x-request-id")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class APIConnectionError(APIError):
|
|
48
|
+
def __init__(self, *, message: str = "Connection error.", request: httpx.Request) -> None:
|
|
49
|
+
super().__init__(message, request, body=None)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class APITimeoutError(APIConnectionError):
|
|
53
|
+
def __init__(self, request: httpx.Request) -> None:
|
|
54
|
+
super().__init__(message="Request timed out.", request=request)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class AuthenticationError(APIStatusError):
|
|
58
|
+
status_code: Literal[401] = 401
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class BadRequestError(APIStatusError):
|
|
62
|
+
status_code: Literal[400] = 400
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "langchain-deepseek"
|
|
3
|
+
version = "0.0.1"
|
|
4
|
+
description = ""
|
|
5
|
+
authors = [
|
|
6
|
+
{name = "SyJarvis",email = "1755115828@qq.com"}
|
|
7
|
+
]
|
|
8
|
+
readme = "README.md"
|
|
9
|
+
requires-python = ">=3.9,<4.0"
|
|
10
|
+
dependencies = [
|
|
11
|
+
"langchain (>=0.2.6)"
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
[[tool.poetry.source]]
|
|
15
|
+
name = "aliyun"
|
|
16
|
+
url = "https://mirrors.aliyun.com/pypi/simple/"
|
|
17
|
+
|
|
18
|
+
[build-system]
|
|
19
|
+
requires = ["poetry-core>=2.0.0,<3.0.0"]
|
|
20
|
+
build-backend = "poetry.core.masonry.api"
|