agi-med-common 5.0.22__tar.gz → 5.0.23__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.
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/PKG-INFO +1 -1
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/__init__.py +1 -1
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/models/chat.py +82 -43
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/models/chat_item.py +9 -2
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common.egg-info/PKG-INFO +1 -1
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/README.md +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/pyproject.toml +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/setup.cfg +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/api.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/api_v2.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/file_storage.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/models/__init__.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/models/_base.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/models/base_config_models/__init__.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/models/base_config_models/gigachat_config.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/models/enums.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/models/tracks.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/models/widget.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/parallel_map.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/type_union.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/utils.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/validators.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common/xml_parser.py +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common.egg-info/SOURCES.txt +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common.egg-info/dependency_links.txt +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common.egg-info/requires.txt +0 -0
- {agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common.egg-info/top_level.txt +0 -0
@@ -1,11 +1,13 @@
|
|
1
|
+
import warnings
|
2
|
+
from copy import deepcopy
|
1
3
|
from datetime import datetime
|
2
|
-
from typing import Any, List, Dict, Literal
|
4
|
+
from typing import Any, List, Dict, Literal, TypeVar
|
3
5
|
|
4
6
|
from agi_med_common.models.chat_item import ChatItem, ReplicaItem, OuterContextItem
|
5
7
|
from agi_med_common.models.widget import Widget
|
6
8
|
from agi_med_common.type_union import TypeUnion
|
7
9
|
from agi_med_common.utils import first_nonnull
|
8
|
-
from pydantic import Field
|
10
|
+
from pydantic import Field, ValidationError
|
9
11
|
|
10
12
|
from ._base import _Base
|
11
13
|
|
@@ -15,6 +17,7 @@ _EXAMPLE_DT: str = datetime(year=1970, month=1, day=1).strftime(_DT_FORMAT)
|
|
15
17
|
StrDict = Dict[str, Any]
|
16
18
|
ContentBase = str | Widget | StrDict
|
17
19
|
Content = ContentBase | List[ContentBase]
|
20
|
+
T = TypeVar('T')
|
18
21
|
|
19
22
|
|
20
23
|
def now_pretty() -> str:
|
@@ -34,13 +37,32 @@ class Context(_Base):
|
|
34
37
|
return f"{cid}_{uid}_{sid}"
|
35
38
|
return f"client_{cid}_user_{uid}_session_{sid}"
|
36
39
|
|
40
|
+
def _get_deprecated_extra(self, field, default):
|
41
|
+
# legacy: eliminate after migration
|
42
|
+
res = (self.extra or {}).get(field, default)
|
43
|
+
warnings.warn(f"Deprecated property `{field}`, should be eliminated", stacklevel=2)
|
44
|
+
return res
|
37
45
|
|
38
|
-
|
46
|
+
# fmt: off
|
47
|
+
@property
|
48
|
+
def sex(self) -> bool: return self._get_deprecated_extra('sex', True)
|
49
|
+
@property
|
50
|
+
def age(self) -> int: return self._get_deprecated_extra('age', 0)
|
51
|
+
@property
|
52
|
+
def entrypoint_key(self) -> str: return self._get_deprecated_extra('entrypoint_key', '')
|
53
|
+
@property
|
54
|
+
def language_code(self) -> str: return self._get_deprecated_extra('language_code', '')
|
55
|
+
@property
|
56
|
+
def parent_session_id(self) -> str: return self._get_deprecated_extra('parent_session_id', '')
|
57
|
+
# fmt: on
|
58
|
+
|
59
|
+
|
60
|
+
def _get_field(obj: dict, field, val_type: type[T]) -> T | None:
|
39
61
|
if not isinstance(obj, dict):
|
40
62
|
return None
|
41
|
-
|
42
|
-
if
|
43
|
-
return
|
63
|
+
val = obj.get(field)
|
64
|
+
if val is not None and isinstance(val, val_type):
|
65
|
+
return val
|
44
66
|
return None
|
45
67
|
|
46
68
|
|
@@ -50,7 +72,7 @@ def _get_text(obj: Content) -> str:
|
|
50
72
|
if isinstance(obj, list):
|
51
73
|
return "".join(map(_get_text, obj))
|
52
74
|
if isinstance(obj, dict) and obj.get("type") == "text":
|
53
|
-
return
|
75
|
+
return _get_field(obj, "text", str) or ""
|
54
76
|
return ""
|
55
77
|
|
56
78
|
|
@@ -58,7 +80,7 @@ def _get_resource_id(obj: Content) -> str | None:
|
|
58
80
|
if isinstance(obj, list):
|
59
81
|
return first_nonnull(map(_get_resource_id, obj))
|
60
82
|
if isinstance(obj, dict) and obj.get("type") == "resource_id":
|
61
|
-
return
|
83
|
+
return _get_field(obj, "resource_id", str)
|
62
84
|
return None
|
63
85
|
|
64
86
|
|
@@ -66,7 +88,7 @@ def _get_command(obj: Content) -> dict | None:
|
|
66
88
|
if isinstance(obj, list):
|
67
89
|
return first_nonnull(map(_get_command, obj))
|
68
90
|
if isinstance(obj, dict) and obj.get("type") == "command":
|
69
|
-
return
|
91
|
+
return _get_field(obj, "command", dict)
|
70
92
|
return None
|
71
93
|
|
72
94
|
|
@@ -91,6 +113,11 @@ class BaseMessage(_Base):
|
|
91
113
|
def text(self) -> str:
|
92
114
|
return _get_text(self.content)
|
93
115
|
|
116
|
+
@property
|
117
|
+
def body(self) -> str:
|
118
|
+
# legacy: eliminate after migration
|
119
|
+
return self.text
|
120
|
+
|
94
121
|
@property
|
95
122
|
def resource_id(self) -> str | None:
|
96
123
|
return _get_resource_id(self.content)
|
@@ -129,21 +156,26 @@ ChatMessage = TypeUnion[HumanMessage, AIMessage, MiscMessage]
|
|
129
156
|
|
130
157
|
class Chat(_Base):
|
131
158
|
context: Context
|
132
|
-
messages: List[ChatMessage] =
|
159
|
+
messages: List[ChatMessage] = Field(default_factory=list)
|
133
160
|
|
134
161
|
def create_id(self, short: bool = False) -> str:
|
135
162
|
return self.context.create_id(short)
|
136
163
|
|
137
|
-
@
|
138
|
-
def parse(
|
164
|
+
@staticmethod
|
165
|
+
def parse(chat_obj: str | dict | ChatItem) -> "Chat":
|
139
166
|
return _parse_chat_compat(chat_obj)
|
140
167
|
|
168
|
+
def to_chat_item(self) -> ChatItem:
|
169
|
+
return convert_chat_to_chat_item(self)
|
170
|
+
|
141
171
|
|
142
172
|
def convert_replica_item_to_message(replica: ReplicaItem) -> ChatMessage:
|
173
|
+
# legacy: eliminate after migration
|
143
174
|
resource_id = (replica.resource_id or None) and {"type": "resource_id", "resource_id": replica.resource_id}
|
144
175
|
body = replica.body
|
145
176
|
command = replica.command
|
146
177
|
widget = replica.widget
|
178
|
+
date_time = replica.date_time
|
147
179
|
|
148
180
|
content = list(filter(None, [body, resource_id, command, widget]))
|
149
181
|
if len(content) == 0:
|
@@ -151,25 +183,28 @@ def convert_replica_item_to_message(replica: ReplicaItem) -> ChatMessage:
|
|
151
183
|
elif len(content) == 1:
|
152
184
|
content = content[0]
|
153
185
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
)
|
168
|
-
|
186
|
+
is_bot_message = replica.role
|
187
|
+
|
188
|
+
if is_bot_message:
|
189
|
+
kwargs = dict(
|
190
|
+
content=content,
|
191
|
+
date_time=date_time,
|
192
|
+
state=replica.state,
|
193
|
+
extra=dict(
|
194
|
+
**(replica.extra or {}),
|
195
|
+
action=replica.action,
|
196
|
+
moderation=replica.moderation,
|
197
|
+
),
|
198
|
+
)
|
199
|
+
res = AIMessage(**kwargs)
|
200
|
+
else:
|
201
|
+
kwargs = dict(content=content, date_time=date_time)
|
202
|
+
res = HumanMessage(**kwargs)
|
203
|
+
return res
|
169
204
|
|
170
205
|
|
171
206
|
def convert_outer_context_to_context(octx: OuterContextItem) -> Context:
|
172
|
-
# legacy: eliminate
|
207
|
+
# legacy: eliminate after migration
|
173
208
|
context = Context(
|
174
209
|
client_id=octx.client_id,
|
175
210
|
user_id=octx.user_id,
|
@@ -187,15 +222,15 @@ def convert_outer_context_to_context(octx: OuterContextItem) -> Context:
|
|
187
222
|
|
188
223
|
|
189
224
|
def convert_chat_item_to_chat(chat_item: ChatItem) -> Chat:
|
190
|
-
# legacy: eliminate
|
225
|
+
# legacy: eliminate after migration
|
191
226
|
context = convert_outer_context_to_context(chat_item.outer_context)
|
192
|
-
messages =
|
227
|
+
messages = list(map(convert_replica_item_to_message, chat_item.inner_context.replicas))
|
193
228
|
res = Chat(context=context, messages=messages)
|
194
229
|
return res
|
195
230
|
|
196
231
|
|
197
232
|
def convert_context_to_outer_context(context: Context) -> OuterContextItem:
|
198
|
-
# legacy: eliminate
|
233
|
+
# legacy: eliminate after migration
|
199
234
|
extra = context.extra or {}
|
200
235
|
return OuterContextItem(
|
201
236
|
client_id=context.client_id,
|
@@ -211,20 +246,20 @@ def convert_context_to_outer_context(context: Context) -> OuterContextItem:
|
|
211
246
|
|
212
247
|
|
213
248
|
def convert_message_to_replica_item(message: ChatMessage) -> ReplicaItem | None:
|
214
|
-
# legacy: eliminate
|
249
|
+
# legacy: eliminate after migration
|
215
250
|
m_type = message.type
|
216
251
|
if m_type in {"ai", "human"}:
|
217
252
|
role = m_type == "ai"
|
218
253
|
else:
|
219
254
|
return None
|
220
255
|
|
221
|
-
extra = message.extra or {}
|
222
|
-
action = extra.pop("action")
|
223
|
-
moderation = extra.pop("moderation")
|
256
|
+
extra = deepcopy(message.extra) if message.extra or {}
|
257
|
+
action = extra.pop("action", "")
|
258
|
+
moderation = extra.pop("moderation", "OK")
|
224
259
|
|
225
260
|
kwargs = dict(
|
226
261
|
role=role,
|
227
|
-
body=message.
|
262
|
+
body=message.text,
|
228
263
|
resource_id=message.resource_id,
|
229
264
|
command=message.command,
|
230
265
|
widget=message.widget,
|
@@ -238,16 +273,19 @@ def convert_message_to_replica_item(message: ChatMessage) -> ReplicaItem | None:
|
|
238
273
|
|
239
274
|
|
240
275
|
def convert_chat_to_chat_item(chat: Chat) -> ChatItem:
|
241
|
-
# legacy: eliminate
|
276
|
+
# legacy: eliminate after migration
|
242
277
|
return ChatItem(
|
243
278
|
outer_context=convert_context_to_outer_context(chat.context),
|
244
279
|
inner_context=dict(replicas=list(map(convert_message_to_replica_item, chat.messages))),
|
245
280
|
)
|
246
281
|
|
247
282
|
|
248
|
-
def parse_chat_item_as_chat(chat_obj: str | dict) -> Chat:
|
249
|
-
# legacy: eliminate
|
250
|
-
|
283
|
+
def parse_chat_item_as_chat(chat_obj: str | dict | ChatItem) -> Chat:
|
284
|
+
# legacy: eliminate after migration
|
285
|
+
if isinstance(chat_obj, ChatItem):
|
286
|
+
chat_item = chat_obj
|
287
|
+
else:
|
288
|
+
chat_item = ChatItem.parse(chat_obj)
|
251
289
|
res = convert_chat_item_to_chat(chat_item)
|
252
290
|
return res
|
253
291
|
|
@@ -259,9 +297,10 @@ def _parse_chat(chat_obj: str | dict) -> Chat:
|
|
259
297
|
return Chat.model_validate_json(chat_obj)
|
260
298
|
|
261
299
|
|
262
|
-
def _parse_chat_compat(chat_obj: str | dict) -> Chat:
|
263
|
-
# legacy: eliminate
|
300
|
+
def _parse_chat_compat(chat_obj: str | dict | ChatItem) -> Chat:
|
301
|
+
# legacy: eliminate after migration
|
264
302
|
try:
|
265
303
|
return _parse_chat(chat_obj)
|
266
|
-
except
|
304
|
+
except ValidationError as ex:
|
305
|
+
warnings.warn(f"Failed to parse chat: {ex}")
|
267
306
|
return parse_chat_item_as_chat(chat_obj)
|
@@ -2,7 +2,7 @@ from datetime import datetime
|
|
2
2
|
from typing import Annotated, Any, List
|
3
3
|
|
4
4
|
from agi_med_common.models.widget import Widget
|
5
|
-
from pydantic import Field, ConfigDict, BeforeValidator
|
5
|
+
from pydantic import Field, ConfigDict, BeforeValidator, AfterValidator
|
6
6
|
|
7
7
|
from ._base import _Base
|
8
8
|
|
@@ -39,6 +39,7 @@ class OuterContextItem(_Base):
|
|
39
39
|
def to_dict(self) -> dict[str, Any]:
|
40
40
|
return self.model_dump(by_alias=True)
|
41
41
|
|
42
|
+
|
42
43
|
LABELS = {
|
43
44
|
0: "OK",
|
44
45
|
1: "NON_MED",
|
@@ -58,9 +59,15 @@ def fix_deprecated_moderation(moderation):
|
|
58
59
|
raise ValueError(f"Unsupported moderation: {moderation} :: {type(moderation)}")
|
59
60
|
|
60
61
|
|
62
|
+
def nullify_empty(text: str) -> str | None:
|
63
|
+
return text or None
|
64
|
+
|
65
|
+
|
61
66
|
class ReplicaItem(_Base):
|
62
67
|
body: str = Field("", alias="Body", examples=["Привет"])
|
63
|
-
resource_id: str | None = Field(
|
68
|
+
resource_id: Annotated[str | None, AfterValidator(nullify_empty)] = Field(
|
69
|
+
None, alias="ResourceId", examples=["<link-id>"]
|
70
|
+
)
|
64
71
|
widget: Widget | None = Field(None, alias="Widget", examples=[None])
|
65
72
|
command: dict | None = Field(None, alias="Command", examples=[None])
|
66
73
|
role: bool = Field(False, alias="Role", description="True = ai, False = client", examples=[False])
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
{agi_med_common-5.0.22 → agi_med_common-5.0.23}/src/agi_med_common.egg-info/dependency_links.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|