dashscope 1.17.1__py3-none-any.whl → 1.18.1__py3-none-any.whl
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.
Potentially problematic release.
This version of dashscope might be problematic. Click here for more details.
- dashscope/aigc/image_synthesis.py +5 -0
- dashscope/api_entities/http_request.py +3 -22
- dashscope/client/base_api.py +15 -3
- dashscope/common/error.py +21 -0
- dashscope/common/utils.py +145 -30
- dashscope/threads/runs/runs.py +82 -6
- dashscope/threads/thread_types.py +85 -5
- dashscope/version.py +1 -1
- {dashscope-1.17.1.dist-info → dashscope-1.18.1.dist-info}/METADATA +1 -1
- {dashscope-1.17.1.dist-info → dashscope-1.18.1.dist-info}/RECORD +14 -14
- {dashscope-1.17.1.dist-info → dashscope-1.18.1.dist-info}/LICENSE +0 -0
- {dashscope-1.17.1.dist-info → dashscope-1.18.1.dist-info}/WHEEL +0 -0
- {dashscope-1.17.1.dist-info → dashscope-1.18.1.dist-info}/entry_points.txt +0 -0
- {dashscope-1.17.1.dist-info → dashscope-1.18.1.dist-info}/top_level.txt +0 -0
|
@@ -24,6 +24,7 @@ class ImageSynthesis(BaseAsyncApi):
|
|
|
24
24
|
images: List[str] = None,
|
|
25
25
|
api_key: str = None,
|
|
26
26
|
sketch_image_url: str = None,
|
|
27
|
+
ref_img: str = None,
|
|
27
28
|
workspace: str = None,
|
|
28
29
|
**kwargs) -> ImageSynthesisResponse:
|
|
29
30
|
"""Call image(s) synthesis service and get result.
|
|
@@ -61,6 +62,7 @@ class ImageSynthesis(BaseAsyncApi):
|
|
|
61
62
|
images,
|
|
62
63
|
api_key=api_key,
|
|
63
64
|
sketch_image_url=sketch_image_url,
|
|
65
|
+
ref_img=ref_img,
|
|
64
66
|
workspace=workspace,
|
|
65
67
|
**kwargs)
|
|
66
68
|
|
|
@@ -72,6 +74,7 @@ class ImageSynthesis(BaseAsyncApi):
|
|
|
72
74
|
images: List[str] = None,
|
|
73
75
|
api_key: str = None,
|
|
74
76
|
sketch_image_url: str = None,
|
|
77
|
+
ref_img: str = None,
|
|
75
78
|
workspace: str = None,
|
|
76
79
|
**kwargs) -> ImageSynthesisResponse:
|
|
77
80
|
"""Create a image(s) synthesis task, and return task information.
|
|
@@ -113,6 +116,8 @@ class ImageSynthesis(BaseAsyncApi):
|
|
|
113
116
|
input[IMAGES] = images
|
|
114
117
|
if sketch_image_url is not None and sketch_image_url:
|
|
115
118
|
input['sketch_image_url'] = sketch_image_url
|
|
119
|
+
if ref_img is not None and ref_img:
|
|
120
|
+
input['ref_img'] == ref_img
|
|
116
121
|
response = super().async_call(model=model,
|
|
117
122
|
task_group=task_group,
|
|
118
123
|
task=ImageSynthesis.task,
|
|
@@ -9,6 +9,7 @@ from dashscope.common.constants import (DEFAULT_REQUEST_TIMEOUT_SECONDS,
|
|
|
9
9
|
SSE_CONTENT_TYPE, HTTPMethod)
|
|
10
10
|
from dashscope.common.error import UnsupportedHTTPMethod
|
|
11
11
|
from dashscope.common.logging import logger
|
|
12
|
+
from dashscope.common.utils import _handle_stream
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
class HttpRequest(BaseRequest):
|
|
@@ -83,34 +84,14 @@ class HttpRequest(BaseRequest):
|
|
|
83
84
|
pass
|
|
84
85
|
return output
|
|
85
86
|
|
|
86
|
-
def _handle_stream(self, response: requests.Response):
|
|
87
|
-
# TODO define done message.
|
|
88
|
-
is_error = False
|
|
89
|
-
status_code = HTTPStatus.INTERNAL_SERVER_ERROR
|
|
90
|
-
for line in response.iter_lines():
|
|
91
|
-
if line:
|
|
92
|
-
line = line.decode('utf8')
|
|
93
|
-
line = line.rstrip('\n').rstrip('\r')
|
|
94
|
-
if line.startswith('event:error'):
|
|
95
|
-
is_error = True
|
|
96
|
-
elif line.startswith('status:'):
|
|
97
|
-
status_code = line[len('status:'):]
|
|
98
|
-
status_code = int(status_code.strip())
|
|
99
|
-
elif line.startswith('data:'):
|
|
100
|
-
line = line[len('data:'):]
|
|
101
|
-
yield (is_error, status_code, line)
|
|
102
|
-
if is_error:
|
|
103
|
-
break
|
|
104
|
-
else:
|
|
105
|
-
continue # ignore heartbeat...
|
|
106
|
-
|
|
107
87
|
def _handle_response(self, response: requests.Response):
|
|
108
88
|
request_id = ''
|
|
109
89
|
if (response.status_code == HTTPStatus.OK and self.stream
|
|
110
90
|
and SSE_CONTENT_TYPE in response.headers.get(
|
|
111
91
|
'content-type', '')):
|
|
112
|
-
for is_error, status_code,
|
|
92
|
+
for is_error, status_code, event in _handle_stream(response):
|
|
113
93
|
try:
|
|
94
|
+
data = event.data
|
|
114
95
|
output = None
|
|
115
96
|
usage = None
|
|
116
97
|
msg = json.loads(data)
|
dashscope/client/base_api.py
CHANGED
|
@@ -15,8 +15,9 @@ from dashscope.common.constants import (DEFAULT_REQUEST_TIMEOUT_SECONDS,
|
|
|
15
15
|
from dashscope.common.error import InvalidParameter, InvalidTask, ModelRequired
|
|
16
16
|
from dashscope.common.logging import logger
|
|
17
17
|
from dashscope.common.utils import (_handle_http_failed_response,
|
|
18
|
-
_handle_http_response,
|
|
19
|
-
|
|
18
|
+
_handle_http_response,
|
|
19
|
+
_handle_http_stream_response,
|
|
20
|
+
default_headers, join_url)
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class BaseAioApi():
|
|
@@ -720,6 +721,7 @@ class CreateMixin():
|
|
|
720
721
|
data: object,
|
|
721
722
|
api_key: str = None,
|
|
722
723
|
path: str = None,
|
|
724
|
+
stream: bool = False,
|
|
723
725
|
workspace: str = None,
|
|
724
726
|
**kwargs) -> Union[DashScopeAPIResponse, Dict]:
|
|
725
727
|
"""Create a object
|
|
@@ -741,6 +743,7 @@ class CreateMixin():
|
|
|
741
743
|
logger.debug('Starting request: %s' % url)
|
|
742
744
|
response = session.post(url,
|
|
743
745
|
json=data,
|
|
746
|
+
stream=stream,
|
|
744
747
|
headers={
|
|
745
748
|
**_workspace_header(workspace),
|
|
746
749
|
**default_headers(api_key),
|
|
@@ -748,7 +751,16 @@ class CreateMixin():
|
|
|
748
751
|
},
|
|
749
752
|
timeout=timeout)
|
|
750
753
|
logger.debug('Starting processing response: %s' % url)
|
|
751
|
-
|
|
754
|
+
response = _handle_http_stream_response(response, flattened_output)
|
|
755
|
+
if stream:
|
|
756
|
+
return (item for item in response)
|
|
757
|
+
else:
|
|
758
|
+
_, output = next(response)
|
|
759
|
+
try:
|
|
760
|
+
next(response)
|
|
761
|
+
except StopIteration:
|
|
762
|
+
pass
|
|
763
|
+
return output
|
|
752
764
|
|
|
753
765
|
|
|
754
766
|
class UpdateMixin():
|
dashscope/common/error.py
CHANGED
|
@@ -58,6 +58,27 @@ class UnsupportedData(DashScopeException):
|
|
|
58
58
|
pass
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
class AssistantError(DashScopeException):
|
|
62
|
+
def __init__(self, **kwargs):
|
|
63
|
+
self.message = None
|
|
64
|
+
self.code = None
|
|
65
|
+
self.request_id = None
|
|
66
|
+
if 'message' in kwargs:
|
|
67
|
+
import json
|
|
68
|
+
msg = json.loads(kwargs['message'])
|
|
69
|
+
if 'request_id' in msg:
|
|
70
|
+
self.request_id = msg['request_id']
|
|
71
|
+
if 'code' in msg:
|
|
72
|
+
self.code = msg['code']
|
|
73
|
+
if 'message' in msg:
|
|
74
|
+
self.message = msg['message']
|
|
75
|
+
|
|
76
|
+
def __str__(self):
|
|
77
|
+
msg = 'Request failed, request_id: %s, code: %s, message: %s' % ( # noqa E501
|
|
78
|
+
self.request_id, self.code, self.message)
|
|
79
|
+
return msg
|
|
80
|
+
|
|
81
|
+
|
|
61
82
|
# for server send generation or inference error.
|
|
62
83
|
class RequestFailure(DashScopeException):
|
|
63
84
|
def __init__(self,
|
dashscope/common/utils.py
CHANGED
|
@@ -2,6 +2,7 @@ import asyncio
|
|
|
2
2
|
import json
|
|
3
3
|
import os
|
|
4
4
|
import platform
|
|
5
|
+
from dataclasses import dataclass
|
|
5
6
|
from http import HTTPStatus
|
|
6
7
|
from typing import Dict
|
|
7
8
|
from urllib.parse import urlparse
|
|
@@ -11,6 +12,8 @@ import requests
|
|
|
11
12
|
|
|
12
13
|
from dashscope.api_entities.dashscope_response import DashScopeAPIResponse
|
|
13
14
|
from dashscope.common.api_key import get_default_api_key
|
|
15
|
+
from dashscope.common.constants import SSE_CONTENT_TYPE
|
|
16
|
+
from dashscope.common.logging import logger
|
|
14
17
|
from dashscope.version import __version__
|
|
15
18
|
|
|
16
19
|
|
|
@@ -163,6 +166,51 @@ async def _handle_aiohttp_response(response: aiohttp.ClientResponse):
|
|
|
163
166
|
message=msg)
|
|
164
167
|
|
|
165
168
|
|
|
169
|
+
@dataclass
|
|
170
|
+
class SSEEvent:
|
|
171
|
+
id: str
|
|
172
|
+
eventType: str
|
|
173
|
+
data: str
|
|
174
|
+
|
|
175
|
+
def __init__(self, id: str, type: str, data: str):
|
|
176
|
+
self.id = id
|
|
177
|
+
self.eventType = type
|
|
178
|
+
self.data = data
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _handle_stream(response: requests.Response):
|
|
182
|
+
# TODO define done message.
|
|
183
|
+
is_error = False
|
|
184
|
+
status_code = HTTPStatus.INTERNAL_SERVER_ERROR
|
|
185
|
+
event = SSEEvent(None, None, None)
|
|
186
|
+
eventType = None
|
|
187
|
+
for line in response.iter_lines():
|
|
188
|
+
if line:
|
|
189
|
+
line = line.decode('utf8')
|
|
190
|
+
line = line.rstrip('\n').rstrip('\r')
|
|
191
|
+
if line.startswith('id:'):
|
|
192
|
+
id = line[len('id:'):]
|
|
193
|
+
event.id = id.strip()
|
|
194
|
+
elif line.startswith('event:'):
|
|
195
|
+
eventType = line[len('event:'):]
|
|
196
|
+
event.eventType = eventType.strip()
|
|
197
|
+
if eventType == 'error':
|
|
198
|
+
is_error = True
|
|
199
|
+
elif line.startswith('status:'):
|
|
200
|
+
status_code = line[len('status:'):]
|
|
201
|
+
status_code = int(status_code.strip())
|
|
202
|
+
elif line.startswith('data:'):
|
|
203
|
+
line = line[len('data:'):]
|
|
204
|
+
event.data = line.strip()
|
|
205
|
+
if eventType is not None and eventType == 'done':
|
|
206
|
+
continue
|
|
207
|
+
yield (is_error, status_code, event)
|
|
208
|
+
if is_error:
|
|
209
|
+
break
|
|
210
|
+
else:
|
|
211
|
+
continue # ignore heartbeat...
|
|
212
|
+
|
|
213
|
+
|
|
166
214
|
def _handle_http_failed_response(
|
|
167
215
|
response: requests.Response,
|
|
168
216
|
flattened_output: bool = False) -> DashScopeAPIResponse:
|
|
@@ -198,38 +246,105 @@ def _handle_http_failed_response(
|
|
|
198
246
|
|
|
199
247
|
def _handle_http_response(response: requests.Response,
|
|
200
248
|
flattened_output: bool = False):
|
|
249
|
+
response = _handle_http_stream_response(response, flattened_output)
|
|
250
|
+
_, output = next(response)
|
|
251
|
+
try:
|
|
252
|
+
next(response)
|
|
253
|
+
except StopIteration:
|
|
254
|
+
pass
|
|
255
|
+
return output
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
def _handle_http_stream_response(response: requests.Response,
|
|
259
|
+
flattened_output: bool = False):
|
|
201
260
|
request_id = ''
|
|
202
|
-
if response.status_code == HTTPStatus.OK
|
|
261
|
+
if (response.status_code == HTTPStatus.OK
|
|
262
|
+
and SSE_CONTENT_TYPE in response.headers.get('content-type', '')):
|
|
263
|
+
for is_error, status_code, event in _handle_stream(response):
|
|
264
|
+
if not is_error:
|
|
265
|
+
try:
|
|
266
|
+
output = None
|
|
267
|
+
usage = None
|
|
268
|
+
msg = json.loads(event.data)
|
|
269
|
+
if flattened_output:
|
|
270
|
+
msg['status_code'] = response.status_code
|
|
271
|
+
yield event.eventType, msg
|
|
272
|
+
else:
|
|
273
|
+
logger.debug('Stream message: %s' % msg)
|
|
274
|
+
if not is_error:
|
|
275
|
+
if 'output' in msg:
|
|
276
|
+
output = msg['output']
|
|
277
|
+
if 'usage' in msg:
|
|
278
|
+
usage = msg['usage']
|
|
279
|
+
if 'request_id' in msg:
|
|
280
|
+
request_id = msg['request_id']
|
|
281
|
+
yield event.eventType, DashScopeAPIResponse(
|
|
282
|
+
request_id=request_id,
|
|
283
|
+
status_code=HTTPStatus.OK,
|
|
284
|
+
output=output,
|
|
285
|
+
usage=usage)
|
|
286
|
+
except json.JSONDecodeError as e:
|
|
287
|
+
if flattened_output:
|
|
288
|
+
yield event.eventType, {
|
|
289
|
+
'status_code': response.status_code,
|
|
290
|
+
'message': e.message
|
|
291
|
+
}
|
|
292
|
+
else:
|
|
293
|
+
yield event.eventType, DashScopeAPIResponse(
|
|
294
|
+
request_id=request_id,
|
|
295
|
+
status_code=HTTPStatus.BAD_REQUEST,
|
|
296
|
+
output=None,
|
|
297
|
+
code='Unknown',
|
|
298
|
+
message=event.data)
|
|
299
|
+
continue
|
|
300
|
+
else:
|
|
301
|
+
if flattened_output:
|
|
302
|
+
yield event.eventType, {
|
|
303
|
+
'status_code': status_code,
|
|
304
|
+
'message': event.data
|
|
305
|
+
}
|
|
306
|
+
else:
|
|
307
|
+
msg = json.loads(event.eventType)
|
|
308
|
+
yield event.eventType, DashScopeAPIResponse(
|
|
309
|
+
request_id=request_id,
|
|
310
|
+
status_code=status_code,
|
|
311
|
+
output=None,
|
|
312
|
+
code=msg['code']
|
|
313
|
+
if 'code' in msg else None, # noqa E501
|
|
314
|
+
message=msg['message']
|
|
315
|
+
if 'message' in msg else None) # noqa E501
|
|
316
|
+
elif response.status_code == HTTPStatus.OK or response.status_code == HTTPStatus.CREATED:
|
|
203
317
|
json_content = response.json()
|
|
204
318
|
if flattened_output:
|
|
205
319
|
json_content['status_code'] = response.status_code
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
code
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
output
|
|
219
|
-
|
|
220
|
-
usage
|
|
221
|
-
|
|
222
|
-
request_id
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
output
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
320
|
+
yield None, json_content
|
|
321
|
+
else:
|
|
322
|
+
output = None
|
|
323
|
+
usage = None
|
|
324
|
+
code = None
|
|
325
|
+
msg = ''
|
|
326
|
+
if 'data' in json_content:
|
|
327
|
+
output = json_content['data']
|
|
328
|
+
if 'code' in json_content:
|
|
329
|
+
code = json_content['code']
|
|
330
|
+
if 'message' in json_content:
|
|
331
|
+
msg = json_content['message']
|
|
332
|
+
if 'output' in json_content:
|
|
333
|
+
output = json_content['output']
|
|
334
|
+
if 'usage' in json_content:
|
|
335
|
+
usage = json_content['usage']
|
|
336
|
+
if 'request_id' in json_content:
|
|
337
|
+
request_id = json_content['request_id']
|
|
338
|
+
json_content.pop('request_id', None)
|
|
339
|
+
|
|
340
|
+
if 'data' not in json_content and 'output' not in json_content:
|
|
341
|
+
output = json_content
|
|
342
|
+
|
|
343
|
+
yield None, DashScopeAPIResponse(request_id=request_id,
|
|
344
|
+
status_code=response.status_code,
|
|
345
|
+
code=code,
|
|
346
|
+
output=output,
|
|
347
|
+
usage=usage,
|
|
348
|
+
message=msg)
|
|
234
349
|
else:
|
|
235
|
-
|
|
350
|
+
yield None, _handle_http_failed_response(response, flattened_output)
|
dashscope/threads/runs/runs.py
CHANGED
|
@@ -5,9 +5,12 @@ from typing import Dict, List, Optional
|
|
|
5
5
|
from dashscope.client.base_api import (CancelMixin, CreateMixin,
|
|
6
6
|
GetStatusMixin, ListObjectMixin,
|
|
7
7
|
UpdateMixin)
|
|
8
|
-
from dashscope.common.error import InputRequired,
|
|
8
|
+
from dashscope.common.error import (AssistantError, InputRequired,
|
|
9
|
+
TimeoutException)
|
|
9
10
|
from dashscope.common.logging import logger
|
|
10
|
-
from dashscope.threads.thread_types import Run, RunList
|
|
11
|
+
from dashscope.threads.thread_types import (Run, RunList, RunStep,
|
|
12
|
+
RunStepDelta, Thread,
|
|
13
|
+
ThreadMessage, ThreadMessageDelta)
|
|
11
14
|
|
|
12
15
|
__all__ = ['Runs']
|
|
13
16
|
|
|
@@ -25,8 +28,10 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
25
28
|
instructions: Optional[str] = None,
|
|
26
29
|
additional_instructions: Optional[str] = None,
|
|
27
30
|
tools: Optional[List[Dict]] = None,
|
|
31
|
+
stream: Optional[bool] = False,
|
|
28
32
|
metadata: Optional[Dict] = None,
|
|
29
33
|
workspace: str = None,
|
|
34
|
+
extra_body: Optional[Dict] = None,
|
|
30
35
|
api_key: str = None,
|
|
31
36
|
**kwargs) -> Run:
|
|
32
37
|
if not assistant_id:
|
|
@@ -44,14 +49,22 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
44
49
|
data['tools'] = tools
|
|
45
50
|
if metadata:
|
|
46
51
|
data['metadata'] = metadata
|
|
52
|
+
data['stream'] = stream
|
|
53
|
+
if extra_body is not None and extra_body:
|
|
54
|
+
data = {**data, **extra_body}
|
|
47
55
|
|
|
48
56
|
response = super().call(data=data,
|
|
49
57
|
path='threads/runs',
|
|
50
58
|
api_key=api_key,
|
|
51
59
|
flattened_output=True,
|
|
60
|
+
stream=stream,
|
|
52
61
|
workspace=workspace,
|
|
53
62
|
**kwargs)
|
|
54
|
-
|
|
63
|
+
if stream:
|
|
64
|
+
return ((event_type, cls.convert_stream_object(event_type, item))
|
|
65
|
+
for event_type, item in response)
|
|
66
|
+
else:
|
|
67
|
+
return Run(**response)
|
|
55
68
|
|
|
56
69
|
@classmethod
|
|
57
70
|
def create(cls,
|
|
@@ -63,7 +76,9 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
63
76
|
additional_instructions: Optional[str] = None,
|
|
64
77
|
tools: Optional[List[Dict]] = None,
|
|
65
78
|
metadata: Optional[Dict] = None,
|
|
79
|
+
stream: Optional[bool] = False,
|
|
66
80
|
workspace: str = None,
|
|
81
|
+
extra_body: Optional[Dict] = None,
|
|
67
82
|
api_key: str = None,
|
|
68
83
|
**kwargs) -> Run:
|
|
69
84
|
"""Create a run.
|
|
@@ -101,14 +116,53 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
101
116
|
data['tools'] = tools
|
|
102
117
|
if metadata:
|
|
103
118
|
data['metadata'] = metadata
|
|
119
|
+
data['stream'] = stream
|
|
120
|
+
if extra_body is not None and extra_body:
|
|
121
|
+
data = {**data, **extra_body}
|
|
104
122
|
|
|
105
123
|
response = super().call(data=data,
|
|
106
124
|
path=f'threads/{thread_id}/runs',
|
|
107
125
|
api_key=api_key,
|
|
108
126
|
flattened_output=True,
|
|
127
|
+
stream=stream,
|
|
109
128
|
workspace=workspace,
|
|
110
129
|
**kwargs)
|
|
111
|
-
|
|
130
|
+
if stream:
|
|
131
|
+
return ((event_type, cls.convert_stream_object(event_type, item))
|
|
132
|
+
for event_type, item in response)
|
|
133
|
+
else:
|
|
134
|
+
return Run(**response)
|
|
135
|
+
|
|
136
|
+
@classmethod
|
|
137
|
+
def convert_stream_object(cls, event, item):
|
|
138
|
+
event_object_map = {
|
|
139
|
+
'thread.created': Thread,
|
|
140
|
+
'thread.run.created': Run,
|
|
141
|
+
'thread.run.queued': Run,
|
|
142
|
+
'thread.run.in_progress': Run,
|
|
143
|
+
'thread.run.requires_action': Run,
|
|
144
|
+
'thread.run.completed': Run,
|
|
145
|
+
'thread.run.failed': Run,
|
|
146
|
+
'thread.run.cancelled': Run,
|
|
147
|
+
'thread.run.expired': Run,
|
|
148
|
+
'thread.run.step.created': RunStep,
|
|
149
|
+
'thread.run.step.in_progress': RunStep,
|
|
150
|
+
'thread.run.step.delta': RunStepDelta,
|
|
151
|
+
'thread.run.step.completed': RunStep,
|
|
152
|
+
'thread.run.step.failed': RunStep,
|
|
153
|
+
'thread.run.step.cancelled': RunStep,
|
|
154
|
+
'thread.run.step.expired': RunStep,
|
|
155
|
+
'thread.message.created': ThreadMessage,
|
|
156
|
+
'thread.message.in_progress': ThreadMessage,
|
|
157
|
+
'thread.message.delta': ThreadMessageDelta,
|
|
158
|
+
'thread.message.completed': ThreadMessage,
|
|
159
|
+
'thread.message.incomplete': ThreadMessage,
|
|
160
|
+
'error': AssistantError,
|
|
161
|
+
}
|
|
162
|
+
if (event in event_object_map):
|
|
163
|
+
return event_object_map[event](**item)
|
|
164
|
+
else:
|
|
165
|
+
return item
|
|
112
166
|
|
|
113
167
|
@classmethod
|
|
114
168
|
def call(cls,
|
|
@@ -119,8 +173,10 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
119
173
|
instructions: Optional[str] = None,
|
|
120
174
|
additional_instructions: Optional[str] = None,
|
|
121
175
|
tools: Optional[List[Dict]] = None,
|
|
176
|
+
stream: Optional[bool] = False,
|
|
122
177
|
metadata: Optional[Dict] = None,
|
|
123
178
|
workspace: str = None,
|
|
179
|
+
extra_body: Optional[Dict] = None,
|
|
124
180
|
api_key: str = None,
|
|
125
181
|
**kwargs) -> Run:
|
|
126
182
|
"""Create a run.
|
|
@@ -151,8 +207,10 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
151
207
|
instructions=instructions,
|
|
152
208
|
additional_instructions=additional_instructions,
|
|
153
209
|
tools=tools,
|
|
210
|
+
stream=stream,
|
|
154
211
|
metadata=metadata,
|
|
155
212
|
workspace=workspace,
|
|
213
|
+
extra_body=extra_body,
|
|
156
214
|
api_key=api_key,
|
|
157
215
|
**kwargs)
|
|
158
216
|
|
|
@@ -254,7 +312,9 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
254
312
|
*,
|
|
255
313
|
thread_id: str,
|
|
256
314
|
tool_outputs: List[Dict],
|
|
315
|
+
stream: Optional[bool] = False,
|
|
257
316
|
workspace: str = None,
|
|
317
|
+
extra_body: Optional[Dict] = None,
|
|
258
318
|
api_key: str = None,
|
|
259
319
|
**kwargs) -> Run:
|
|
260
320
|
"""_summary_
|
|
@@ -278,14 +338,24 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
278
338
|
if not thread_id or not run_id:
|
|
279
339
|
raise InputRequired('thread_id and run_id are required!')
|
|
280
340
|
|
|
341
|
+
data = {'tool_outputs': tool_outputs}
|
|
342
|
+
data['stream'] = stream
|
|
343
|
+
if extra_body is not None and extra_body:
|
|
344
|
+
data = {**data, **extra_body}
|
|
345
|
+
|
|
281
346
|
response = super().call(
|
|
282
|
-
|
|
347
|
+
data,
|
|
283
348
|
path=f'threads/{thread_id}/runs/{run_id}/submit_tool_outputs',
|
|
284
349
|
workspace=workspace,
|
|
285
350
|
api_key=api_key,
|
|
351
|
+
stream=stream,
|
|
286
352
|
flattened_output=True,
|
|
287
353
|
**kwargs)
|
|
288
|
-
|
|
354
|
+
if stream:
|
|
355
|
+
return ((event_type, cls.convert_stream_object(event_type, item))
|
|
356
|
+
for event_type, item in response)
|
|
357
|
+
else:
|
|
358
|
+
return Run(**response)
|
|
289
359
|
|
|
290
360
|
@classmethod
|
|
291
361
|
def wait(cls,
|
|
@@ -316,6 +386,12 @@ class Runs(CreateMixin, CancelMixin, ListObjectMixin, GetStatusMixin,
|
|
|
316
386
|
thread_id=thread_id,
|
|
317
387
|
workspace=workspace,
|
|
318
388
|
api_key=api_key)
|
|
389
|
+
import json
|
|
390
|
+
print(
|
|
391
|
+
json.dumps(run,
|
|
392
|
+
default=lambda o: o.__dict__,
|
|
393
|
+
sort_keys=True,
|
|
394
|
+
indent=4))
|
|
319
395
|
if run.status_code == HTTPStatus.OK:
|
|
320
396
|
if hasattr(run, 'status'):
|
|
321
397
|
if run.status in [
|
|
@@ -85,6 +85,8 @@ class Text(BaseObjectMixin):
|
|
|
85
85
|
self.annotations = []
|
|
86
86
|
for annotation in annotations:
|
|
87
87
|
self.annotations.append(annotation)
|
|
88
|
+
else:
|
|
89
|
+
self.annotations = annotations
|
|
88
90
|
super().__init__(**kwargs)
|
|
89
91
|
|
|
90
92
|
|
|
@@ -108,6 +110,42 @@ MESSAGE_SUPPORT_CONTENT = {
|
|
|
108
110
|
Content = Union[MessageContentImageFile, MessageContentText]
|
|
109
111
|
|
|
110
112
|
|
|
113
|
+
@dataclass(init=False)
|
|
114
|
+
class ThreadMessageDeltaContent(BaseObjectMixin):
|
|
115
|
+
content: Content
|
|
116
|
+
role: str
|
|
117
|
+
|
|
118
|
+
def __init__(self, **kwargs):
|
|
119
|
+
contents = kwargs.pop('content', None)
|
|
120
|
+
if contents:
|
|
121
|
+
for item in contents:
|
|
122
|
+
if item['type'] == 'text':
|
|
123
|
+
self.content = MessageContentText(**item)
|
|
124
|
+
elif item['type'] == 'image_file':
|
|
125
|
+
self.content = MessageContentImageFile(**item)
|
|
126
|
+
else:
|
|
127
|
+
self.content = item
|
|
128
|
+
else:
|
|
129
|
+
self.content = contents
|
|
130
|
+
super().__init__(**kwargs)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass(init=False)
|
|
134
|
+
class ThreadMessageDelta(BaseObjectMixin):
|
|
135
|
+
status_code: int
|
|
136
|
+
id: str
|
|
137
|
+
object: str = 'thread.message.delta'
|
|
138
|
+
delta: ThreadMessageDeltaContent
|
|
139
|
+
|
|
140
|
+
def __init__(self, **kwargs):
|
|
141
|
+
content = kwargs.pop('delta', None)
|
|
142
|
+
if content:
|
|
143
|
+
self.delta = ThreadMessageDeltaContent(**content)
|
|
144
|
+
else:
|
|
145
|
+
self.delta = None
|
|
146
|
+
super().__init__(**kwargs)
|
|
147
|
+
|
|
148
|
+
|
|
111
149
|
@dataclass(init=False)
|
|
112
150
|
class ThreadMessage(BaseObjectMixin):
|
|
113
151
|
status_code: int
|
|
@@ -137,6 +175,8 @@ class ThreadMessage(BaseObjectMixin):
|
|
|
137
175
|
else:
|
|
138
176
|
content_list.append(content)
|
|
139
177
|
self.content = content_list
|
|
178
|
+
else:
|
|
179
|
+
self.content = input_content
|
|
140
180
|
|
|
141
181
|
super().__init__(**kwargs)
|
|
142
182
|
|
|
@@ -203,6 +243,8 @@ class RequiredActionSubmitToolOutputs(BaseObjectMixin):
|
|
|
203
243
|
self.tool_calls = []
|
|
204
244
|
for tc in tcs:
|
|
205
245
|
self.tool_calls.append(RequiredActionFunctionToolCall(**tc))
|
|
246
|
+
else:
|
|
247
|
+
self.tool_calls = tcs
|
|
206
248
|
super().__init__(**kwargs)
|
|
207
249
|
|
|
208
250
|
|
|
@@ -265,6 +307,8 @@ class Run(BaseObjectMixin):
|
|
|
265
307
|
actions = kwargs.pop('required_action', None)
|
|
266
308
|
if actions:
|
|
267
309
|
self.required_action = RequiredAction(**actions)
|
|
310
|
+
else:
|
|
311
|
+
self.required_action = actions
|
|
268
312
|
|
|
269
313
|
super().__init__(**kwargs)
|
|
270
314
|
|
|
@@ -387,10 +431,10 @@ class RetrievalToolCall(BaseObjectMixin):
|
|
|
387
431
|
retrieval: object
|
|
388
432
|
"""For now, this is always going to be an empty object."""
|
|
389
433
|
|
|
390
|
-
type: Literal['
|
|
434
|
+
type: Literal['quark_search']
|
|
391
435
|
"""The type of tool call.
|
|
392
436
|
|
|
393
|
-
This is always going to be `
|
|
437
|
+
This is always going to be `quark_search` for this type of tool call.
|
|
394
438
|
"""
|
|
395
439
|
def __init__(self, **kwargs):
|
|
396
440
|
super().__init__(**kwargs)
|
|
@@ -469,9 +513,34 @@ def convert_step_details_dict_to_objects(step_details):
|
|
|
469
513
|
return step_details
|
|
470
514
|
|
|
471
515
|
|
|
516
|
+
@dataclass(init=False)
|
|
517
|
+
class RunStepDeltaContent(BaseObjectMixin):
|
|
518
|
+
step_details: StepDetails
|
|
519
|
+
|
|
520
|
+
def __init__(self, **kwargs):
|
|
521
|
+
self.step_details = convert_step_details_dict_to_objects(
|
|
522
|
+
kwargs.pop('step_details', {}))
|
|
523
|
+
super().__init__(**kwargs)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
@dataclass(init=False)
|
|
527
|
+
class RunStepDelta(BaseObjectMixin):
|
|
528
|
+
id: str
|
|
529
|
+
object: str = 'thread.run.step.delta'
|
|
530
|
+
delta: RunStepDeltaContent
|
|
531
|
+
|
|
532
|
+
def __init__(self, **kwargs):
|
|
533
|
+
delta = kwargs.pop('delta', None)
|
|
534
|
+
if delta:
|
|
535
|
+
self.delta = RunStepDeltaContent(**delta)
|
|
536
|
+
else:
|
|
537
|
+
self.delta = delta
|
|
538
|
+
super().__init__(**kwargs)
|
|
539
|
+
|
|
540
|
+
|
|
472
541
|
@dataclass(init=False)
|
|
473
542
|
class RunStep(BaseObjectMixin):
|
|
474
|
-
status_code: int
|
|
543
|
+
status_code: int = None
|
|
475
544
|
id: str
|
|
476
545
|
"""The identifier of the run step, which can be referenced in API endpoints."""
|
|
477
546
|
|
|
@@ -547,10 +616,15 @@ class RunStep(BaseObjectMixin):
|
|
|
547
616
|
def __init__(self, **kwargs):
|
|
548
617
|
self.step_details = convert_step_details_dict_to_objects(
|
|
549
618
|
kwargs.pop('step_details', {}))
|
|
550
|
-
|
|
619
|
+
if 'usage' in kwargs and kwargs['usage'] is not None and kwargs['usage']:
|
|
620
|
+
self.usage = Usage(**kwargs.pop('usage', {}))
|
|
621
|
+
else:
|
|
622
|
+
self.usage = None
|
|
551
623
|
last_error = kwargs.pop('last_error', None)
|
|
552
624
|
if last_error:
|
|
553
625
|
self.last_error = LastError(**last_error)
|
|
626
|
+
else:
|
|
627
|
+
last_error = last_error
|
|
554
628
|
super().__init__(**kwargs)
|
|
555
629
|
|
|
556
630
|
|
|
@@ -564,8 +638,14 @@ class RunStepList(BaseList):
|
|
|
564
638
|
first_id: Optional[str] = None,
|
|
565
639
|
data: List[RunStep] = [],
|
|
566
640
|
**kwargs):
|
|
641
|
+
if data:
|
|
642
|
+
steps = []
|
|
643
|
+
for step in data:
|
|
644
|
+
steps.append(RunStep(**step))
|
|
645
|
+
self.data = steps
|
|
646
|
+
else:
|
|
647
|
+
self.data = []
|
|
567
648
|
super().__init__(has_more=has_more,
|
|
568
649
|
last_id=last_id,
|
|
569
650
|
first_id=first_id,
|
|
570
|
-
data=data,
|
|
571
651
|
**kwargs)
|
dashscope/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = '1.
|
|
1
|
+
__version__ = '1.18.1'
|
|
@@ -6,12 +6,12 @@ dashscope/files.py,sha256=QgJjwhtn9F548nCA8jD8OvE6aQEj-20hZqJgYXsUdQU,3930
|
|
|
6
6
|
dashscope/finetune.py,sha256=_tflDUvu0KagSoCzLaf0hofpG_P8NU6PylL8CPjVhrA,6243
|
|
7
7
|
dashscope/model.py,sha256=UPOn1qMYFhX-ovXi3BMxZEBk8qOK7WLJOYHMbPZwYBo,1440
|
|
8
8
|
dashscope/models.py,sha256=UPOn1qMYFhX-ovXi3BMxZEBk8qOK7WLJOYHMbPZwYBo,1440
|
|
9
|
-
dashscope/version.py,sha256=
|
|
9
|
+
dashscope/version.py,sha256=I5ZqRqhAJhmCPjHV4Y-aZSwFiizEYHBBRN0cXvrD1-A,23
|
|
10
10
|
dashscope/aigc/__init__.py,sha256=s-MCA87KYiVumYtKtJi5IMN7xelSF6TqEU3s3_7RF-Y,327
|
|
11
11
|
dashscope/aigc/code_generation.py,sha256=KAJVrGp6tiNFBBg64Ovs9RfcP5SrIhrbW3wdA89NKso,10885
|
|
12
12
|
dashscope/aigc/conversation.py,sha256=xRoJlCR-IXHjSdkDrK74a9ut1FJg0FZhTNXZAJC18MA,14231
|
|
13
13
|
dashscope/aigc/generation.py,sha256=9PMAhSy5Ht5eizUGXnDdk8Jl3yIDM4fR8eSxn1W4c-U,10051
|
|
14
|
-
dashscope/aigc/image_synthesis.py,sha256=
|
|
14
|
+
dashscope/aigc/image_synthesis.py,sha256=7A5txSkKBkg5pN5F7IP5C90277Yk8fkKAWu30YhskdM,9994
|
|
15
15
|
dashscope/aigc/multimodal_conversation.py,sha256=SlNnnsUPV19gdx8fYJAtsMFWPNGY6vhk5IGHZ5ZczpI,5369
|
|
16
16
|
dashscope/api_entities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
dashscope/api_entities/aiohttp_request.py,sha256=aE3AeWba8Ig_xHMYjrAdkq0N61l_L2VFTG6HYh912X0,10229
|
|
@@ -19,7 +19,7 @@ dashscope/api_entities/api_request_data.py,sha256=JUMcfpJjKXEZLCBSFIDpgoaeQYk5uK
|
|
|
19
19
|
dashscope/api_entities/api_request_factory.py,sha256=4p-qxMuvCA0CmUHdH19QaUCaHmlLHAM1X2Jd4YKt5c0,4661
|
|
20
20
|
dashscope/api_entities/base_request.py,sha256=cXUL7xqSV8wBr5d-1kx65AO3IsRR9A_ps6Lok-v-MKM,926
|
|
21
21
|
dashscope/api_entities/dashscope_response.py,sha256=Bp1T7HwVlkOvpMNg-AEjz-BScxhLUXMXlE8ApXTtfhQ,17872
|
|
22
|
-
dashscope/api_entities/http_request.py,sha256=
|
|
22
|
+
dashscope/api_entities/http_request.py,sha256=Y9z4PcRAPlp6Ozfz9P3IVP3y9cufhhIFjXaJOF6xCtQ,9043
|
|
23
23
|
dashscope/api_entities/websocket_request.py,sha256=IaydqRvOSr5IdSSpnX8Vc8rRoVldjfcrLvDxTnPep9g,14676
|
|
24
24
|
dashscope/app/__init__.py,sha256=OOV2rFy0QlA9Gu3XVPtWJoBwK1J11BdGhkEdX_sdYGU,68
|
|
25
25
|
dashscope/app/application.py,sha256=AegGVsk3dDzYACoYRNNjo3eG-2wrDd0dlOjYHpF0r2Y,7949
|
|
@@ -36,16 +36,16 @@ dashscope/audio/asr/transcription.py,sha256=e5O1U51GT-OQPu-wWN2w_T7l6IopWuGMVkhe
|
|
|
36
36
|
dashscope/audio/tts/__init__.py,sha256=fbnieZX9yNFNh5BsxLpLXb63jlxzxrdCJakV3ignjlQ,194
|
|
37
37
|
dashscope/audio/tts/speech_synthesizer.py,sha256=dnKx9FDDdO_ETHAjhK8zaMVaH6SfoTtN5YxXXqgY1JA,7571
|
|
38
38
|
dashscope/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
39
|
-
dashscope/client/base_api.py,sha256=
|
|
39
|
+
dashscope/client/base_api.py,sha256=UDxvwlxiR0KZwj_AI0jACfkLErJ55voure82OmOiDMk,41034
|
|
40
40
|
dashscope/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
41
41
|
dashscope/common/api_key.py,sha256=5Stp0odL5JSuIO3qJBp23QNppuGbqhhvKPS66qbMs0I,1986
|
|
42
42
|
dashscope/common/base_type.py,sha256=wKqLGrr2o9bpI43ws1P0YCuJmoS17J9_Rw_uuYHjIFQ,4531
|
|
43
43
|
dashscope/common/constants.py,sha256=86Zc45f1-OEgNdLTu-4_Pl0U3wRyi5qFt4E7dLlaglU,2327
|
|
44
44
|
dashscope/common/env.py,sha256=oQOZW5JyEeTSde394un2lpDJ5RBh4fMU9hBfbtrKKkc,869
|
|
45
|
-
dashscope/common/error.py,sha256=
|
|
45
|
+
dashscope/common/error.py,sha256=Q7GRhniP-7ap4HBpU69frRdKgKLwmH4ySYxCtupsr60,2638
|
|
46
46
|
dashscope/common/logging.py,sha256=ecGxylG3bWES_Xv5-BD6ep4_0Ciu7F6ZPBjiZtu9Jx4,984
|
|
47
47
|
dashscope/common/message_manager.py,sha256=i5149WzDk6nWmdFaHzYx4USXMBeX18GKSI-F4fLwbN0,1097
|
|
48
|
-
dashscope/common/utils.py,sha256=
|
|
48
|
+
dashscope/common/utils.py,sha256=5jg7q5NE0LE1NigbpfrOkqXjPYqb-3jfFOh7g-nWB1Q,12017
|
|
49
49
|
dashscope/embeddings/__init__.py,sha256=-dxHaoxZZVuP-wAGUIa3sNNh8CQwaeWj2UlqsDy1sV4,240
|
|
50
50
|
dashscope/embeddings/batch_text_embedding.py,sha256=P32LFO9v7ehdJsl0c32In94hUET6K6AaGJ_pDRtFqco,8791
|
|
51
51
|
dashscope/embeddings/batch_text_embedding_response.py,sha256=WziXlQsFIkL1kPc_7lRG1HtqgkO5vVThtnNqExJggNU,2000
|
|
@@ -61,13 +61,13 @@ dashscope/rerank/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
|
|
|
61
61
|
dashscope/rerank/text_rerank.py,sha256=1L-RLUxMCvNhbMud1FUG6YFWT7ZV979fzhMEuVjJ1oI,2398
|
|
62
62
|
dashscope/resources/qwen.tiktoken,sha256=srG437XMXwJLr8NzEhxquj9m-aWgJp4kNHCh3hajMYY,2561218
|
|
63
63
|
dashscope/threads/__init__.py,sha256=md5bsHekHHGOg3uQrBCk8f4BCNnA1AuI_-LDf92pNVU,621
|
|
64
|
-
dashscope/threads/thread_types.py,sha256=
|
|
64
|
+
dashscope/threads/thread_types.py,sha256=SwAJNi-RbqFXlrztDUCt0q0MfgfJLHyYl9qWrQDmQQo,18280
|
|
65
65
|
dashscope/threads/threads.py,sha256=dD72xklN71KFGBVoBVHEbCbZADwLbi9yGS9LbFpnlAI,7665
|
|
66
66
|
dashscope/threads/messages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
67
67
|
dashscope/threads/messages/files.py,sha256=wi0nJ2zsPWOw2Jn-ZkxA3URZBIrkGxqM_uAPfXY1xv0,3820
|
|
68
68
|
dashscope/threads/messages/messages.py,sha256=Zjmyf3rT1XSdn33hPrqOY6DSWUVL7pDEapG03FREPV8,8419
|
|
69
69
|
dashscope/threads/runs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
70
|
-
dashscope/threads/runs/runs.py,sha256=
|
|
70
|
+
dashscope/threads/runs/runs.py,sha256=Cvy5FD0x1Z9c5qayYeNpoL_QIqH4yxgqdGplCk3soRw,18762
|
|
71
71
|
dashscope/threads/runs/steps.py,sha256=pLNR-5g7zvYkvC-p4sZGVgYHd1jqxBerM2WFyB358H8,3638
|
|
72
72
|
dashscope/tokenizers/__init__.py,sha256=Oy5FMT37Non6e1YxdHQ89U93Dy3CG1Ez0gBa771KZo0,200
|
|
73
73
|
dashscope/tokenizers/qwen_tokenizer.py,sha256=dCnT9-9NrqPS85bEhjlPULUfDADVRhlleYwM_ILgCeI,4111
|
|
@@ -76,9 +76,9 @@ dashscope/tokenizers/tokenizer.py,sha256=y6P91qTCYo__pEx_0VHAcj9YECfbUdRqZU1fdGT
|
|
|
76
76
|
dashscope/tokenizers/tokenizer_base.py,sha256=REDhzRyDT13iequ61-a6_KcTy0GFKlihQve5HkyoyRs,656
|
|
77
77
|
dashscope/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
78
78
|
dashscope/utils/oss_utils.py,sha256=fi8-PPsN-iR-iv5k2NS5Z8nlWkpgUhr56FRWm4BDh4A,6984
|
|
79
|
-
dashscope-1.
|
|
80
|
-
dashscope-1.
|
|
81
|
-
dashscope-1.
|
|
82
|
-
dashscope-1.
|
|
83
|
-
dashscope-1.
|
|
84
|
-
dashscope-1.
|
|
79
|
+
dashscope-1.18.1.dist-info/LICENSE,sha256=Izp5L1DF1Mbza6qojkqNNWlE_mYLnr4rmzx2EBF8YFw,11413
|
|
80
|
+
dashscope-1.18.1.dist-info/METADATA,sha256=33M-0CZybEgQltO9zFJSM4xHpCSEzO2IabuoaJJFbdI,6609
|
|
81
|
+
dashscope-1.18.1.dist-info/WHEEL,sha256=G16H4A3IeoQmnOrYV4ueZGKSjhipXx8zc8nu9FGlvMA,92
|
|
82
|
+
dashscope-1.18.1.dist-info/entry_points.txt,sha256=raEp5dOuj8whJ7yqZlDM8WQ5p2RfnGrGNo0QLQEnatY,50
|
|
83
|
+
dashscope-1.18.1.dist-info/top_level.txt,sha256=woqavFJK9zas5xTqynmALqOtlafghjsk63Xk86powTU,10
|
|
84
|
+
dashscope-1.18.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|