agi-med-common 5.0.16__py3-none-any.whl → 5.0.17__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.
@@ -1,4 +1,4 @@
1
- __version__ = "5.0.16"
1
+ __version__ = "5.0.17"
2
2
 
3
3
  from .models import (
4
4
  MTRSLabelEnum,
@@ -1,6 +1,7 @@
1
1
  from datetime import datetime
2
2
  from typing import Any, List, Dict, Literal
3
3
 
4
+ from agi_med_common.models.chat_item import ChatItem, ReplicaItem, OuterContextItem
4
5
  from agi_med_common.models.widget import Widget
5
6
  from agi_med_common.type_union import TypeUnion
6
7
  from agi_med_common.utils import first_nonnull
@@ -55,12 +56,31 @@ def _get_text(obj: Content) -> str:
55
56
 
56
57
  def _get_resource_id(obj: Content) -> str | None:
57
58
  if isinstance(obj, list):
58
- return first_nonnull(_get_str_field(el, "resource_id") for el in obj)
59
+ return first_nonnull(map(_get_resource_id, obj))
59
60
  if isinstance(obj, dict) and obj.get("type") == "resource_id":
60
61
  return _get_str_field(obj, "resource_id")
61
62
  return None
62
63
 
63
64
 
65
+ def _get_command(obj: Content) -> dict | None:
66
+ if isinstance(obj, list):
67
+ return first_nonnull(map(_get_command, obj))
68
+ if isinstance(obj, dict) and obj.get("type") == "command":
69
+ return _get_str_field(obj, "command")
70
+ return None
71
+
72
+
73
+ def _get_widget(obj: Content) -> Widget | None:
74
+ if isinstance(obj, list):
75
+ return first_nonnull(map(_get_widget, obj))
76
+ if isinstance(obj, Widget):
77
+ return obj
78
+ return None
79
+
80
+
81
+ # todo fix: generalize functions above
82
+
83
+
64
84
  class BaseMessage(_Base):
65
85
  type: str
66
86
  content: Content = Field("", examples=["Привет"])
@@ -75,6 +95,14 @@ class BaseMessage(_Base):
75
95
  def resource_id(self) -> str | None:
76
96
  return _get_resource_id(self.content)
77
97
 
98
+ @property
99
+ def command(self) -> dict | None:
100
+ return _get_command(self.content)
101
+
102
+ @property
103
+ def widget(self) -> Widget | None:
104
+ return _get_widget(self.content)
105
+
78
106
  @staticmethod
79
107
  def DATETIME_FORMAT() -> str:
80
108
  return _DT_FORMAT
@@ -105,3 +133,135 @@ class Chat(_Base):
105
133
 
106
134
  def create_id(self, short: bool = False) -> str:
107
135
  return self.context.create_id(short)
136
+
137
+ @classmethod
138
+ def parse(self, chat_obj: str | dict) -> "Chat":
139
+ return _parse_chat_compat(chat_obj)
140
+
141
+
142
+ def convert_replica_item_to_message(replica: ReplicaItem) -> ChatMessage:
143
+ resource_id = (replica.resource_id or None) and {"type": "resource_id", "resource_id": replica.resource_id}
144
+ body = replica.body
145
+ command = replica.command
146
+ widget = replica.widget
147
+
148
+ content = list(filter(None, [body, resource_id, command, widget]))
149
+ if len(content) == 0:
150
+ content = ""
151
+ elif len(content) == 1:
152
+ content = content[0]
153
+
154
+ kwargs = dict(
155
+ content=content,
156
+ date_time=replica.date_time,
157
+ extra=replica.extra,
158
+ )
159
+ if not replica.role:
160
+ return HumanMessage(**kwargs)
161
+ return AIMessage(
162
+ **kwargs,
163
+ state=replica.state,
164
+ extra=dict(
165
+ action=replica.action,
166
+ moderation=replica.moderation,
167
+ ),
168
+ )
169
+
170
+
171
+ def convert_outer_context_to_context(octx: OuterContextItem) -> Context:
172
+ # legacy: eliminate
173
+ context = Context(
174
+ client_id=octx.client_id,
175
+ user_id=octx.user_id,
176
+ session_id=octx.session_id,
177
+ track_id=octx.track_id,
178
+ extra=dict(
179
+ sex=octx.sex,
180
+ age=octx.age,
181
+ parent_session_id=octx.parent_session_id,
182
+ entrypoint_key=octx.entrypoint_key,
183
+ language_code=octx.language_code,
184
+ ),
185
+ )
186
+ return context
187
+
188
+
189
+ def convert_chat_item_to_chat(chat_item: ChatItem) -> Chat:
190
+ # legacy: eliminate
191
+ context = convert_outer_context_to_context(chat_item.outer_context)
192
+ messages = [convert_replica_item_to_message(replica) for replica in chat_item.inner_context.replicas]
193
+ res = Chat(context=context, messages=messages)
194
+ return res
195
+
196
+
197
+ def convert_context_to_outer_context(context: Context) -> OuterContextItem:
198
+ # legacy: eliminate
199
+ extra = context.extra or {}
200
+ return OuterContextItem(
201
+ client_id=context.client_id,
202
+ user_id=context.user_id,
203
+ session_id=context.session_id,
204
+ track_id=context.track_id,
205
+ sex=extra.get("sex"),
206
+ age=extra.get("age"),
207
+ parent_session_id=extra.get("parent_session_id"),
208
+ entrypoint_key=extra.get("entrypoint_key"),
209
+ language_code=extra.get("language_code"),
210
+ )
211
+
212
+
213
+ def convert_message_to_replica_item(message: ChatMessage) -> ReplicaItem | None:
214
+ # legacy: eliminate
215
+ m_type = message.type
216
+ if m_type in {"ai", "human"}:
217
+ role = m_type == "ai"
218
+ else:
219
+ return None
220
+
221
+ extra = message.extra or {}
222
+ action = extra.pop("action")
223
+ moderation = extra.pop("moderation")
224
+
225
+ kwargs = dict(
226
+ role=role,
227
+ body=message.body,
228
+ resource_id=message.resource_id,
229
+ command=message.command,
230
+ widget=message.widget,
231
+ date_time=message.date_time,
232
+ extra=extra or None,
233
+ state=getattr(message, "state", ""),
234
+ action=action,
235
+ moderation=moderation,
236
+ )
237
+ return ReplicaItem(**kwargs)
238
+
239
+
240
+ def convert_chat_to_chat_item(chat: Chat) -> ChatItem:
241
+ # legacy: eliminate
242
+ return ChatItem(
243
+ outer_context=convert_context_to_outer_context(chat.context),
244
+ inner_context=dict(replicas=list(map(convert_message_to_replica_item, chat.messages))),
245
+ )
246
+
247
+
248
+ def parse_chat_item_as_chat(chat_obj: str | dict) -> Chat:
249
+ # legacy: eliminate
250
+ chat_item = ChatItem.parse(chat_obj)
251
+ res = convert_chat_item_to_chat(chat_item)
252
+ return res
253
+
254
+
255
+ def _parse_chat(chat_obj: str | dict) -> Chat:
256
+ if isinstance(chat_obj, dict):
257
+ return Chat.model_validate(chat_obj)
258
+
259
+ return Chat.model_validate_json(chat_obj)
260
+
261
+
262
+ def _parse_chat_compat(chat_obj: str | dict) -> Chat:
263
+ # legacy: eliminate
264
+ try:
265
+ return _parse_chat(chat_obj)
266
+ except Exception:
267
+ return parse_chat_item_as_chat(chat_obj)
@@ -1,11 +1,8 @@
1
- import json
2
- import warnings
3
1
  from datetime import datetime
4
- from typing import Any, List
2
+ from typing import Annotated, Any, List
5
3
 
6
4
  from agi_med_common.models.widget import Widget
7
- from loguru import logger
8
- from pydantic import Field, ConfigDict, ValidationError
5
+ from pydantic import Field, ConfigDict, BeforeValidator
9
6
 
10
7
  from ._base import _Base
11
8
 
@@ -42,6 +39,24 @@ class OuterContextItem(_Base):
42
39
  def to_dict(self) -> dict[str, Any]:
43
40
  return self.model_dump(by_alias=True)
44
41
 
42
+ LABELS = {
43
+ 0: "OK",
44
+ 1: "NON_MED",
45
+ 2: "CHILD",
46
+ 3: "ABSURD",
47
+ 4: "GREETING",
48
+ 5: "RECEIPT",
49
+ }
50
+
51
+
52
+ def fix_deprecated_moderation(moderation):
53
+ if isinstance(moderation, int):
54
+ return LABELS.get(moderation, "OK")
55
+ elif isinstance(moderation, str):
56
+ return moderation
57
+ else:
58
+ raise ValueError(f"Unsupported moderation: {moderation} :: {type(moderation)}")
59
+
45
60
 
46
61
  class ReplicaItem(_Base):
47
62
  body: str = Field("", alias="Body", examples=["Привет"])
@@ -55,7 +70,9 @@ class ReplicaItem(_Base):
55
70
  state: str = Field("", alias="State", description="chat manager fsm state", examples=["COLLECTION"])
56
71
  action: str = Field("", alias="Action", description="chat manager fsm action", examples=["DIAGNOSIS"])
57
72
  # todo fix: support loading from `moderation: int`
58
- moderation: str = Field("OK", alias="Moderation", description="moderation outcome", examples=["OK"])
73
+ moderation: Annotated[str, BeforeValidator(str)] = Field(
74
+ "OK", alias="Moderation", description="moderation outcome", examples=["OK"]
75
+ )
59
76
  extra: dict | None = Field(None, alias="Extra", examples=[None])
60
77
 
61
78
  def to_dict(self) -> dict[str, Any]:
@@ -99,40 +116,11 @@ class ChatItem(_Base):
99
116
 
100
117
  @classmethod
101
118
  def parse(cls, chat_obj: str | dict) -> "ChatItem":
102
- return _parse_chat(chat_obj)
103
-
119
+ return _parse_chat_item(chat_obj)
104
120
 
105
- def _is_moderation_int_error(err):
106
- # fmt: off
107
- if err['type'] != 'string_type': return False
108
- if err['loc'][0] != 'InnerContext': return False
109
- if err['loc'][1] != 'Replicas': return False
110
- if err['loc'][3] != 'Moderation': return False
111
- if err['msg'] != 'Input should be a valid string': return False
112
- # fmt: on
113
- return True
114
121
 
115
-
116
- def _is_moderation_int_errors(ex):
117
- errs = ex.errors()
118
- return all(map(_is_moderation_int_error, errs))
119
-
120
-
121
- def _parse_chat(chat_obj: str | dict) -> ChatItem:
122
+ def _parse_chat_item(chat_obj: str | dict) -> ChatItem:
122
123
  if isinstance(chat_obj, dict):
123
- return ChatItem(**chat_obj)
124
- try:
125
- return ChatItem.model_validate_json(chat_obj)
126
- except ValidationError as ex:
127
- if _is_moderation_int_errors(ex):
128
- msg = "Failed to parse ChatItem, fallback to old version with `Moderation:int`"
129
- else:
130
- msg = f"Failed to parse: {ex}"
131
- warnings.warn(msg)
132
-
133
- # old version
134
- chat_dict = json.loads(chat_obj)
135
- for rep in chat_dict["InnerContext"]["Replicas"]:
136
- rep["Moderation"] = "OK"
137
-
138
- return ChatItem.model_validate(chat_dict)
124
+ return ChatItem.model_validate(chat_obj)
125
+
126
+ return ChatItem.model_validate_json(chat_obj)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agi_med_common
3
- Version: 5.0.16
3
+ Version: 5.0.17
4
4
  Summary: Сommon for agi-med team
5
5
  Author: AGI-MED-TEAM
6
6
  Requires-Python: >=3.11
@@ -1,4 +1,4 @@
1
- agi_med_common/__init__.py,sha256=trHujI-pVt0AGA2pd1g9XhOIizSfnlvl8v2H3LPTUvQ,1043
1
+ agi_med_common/__init__.py,sha256=CungR6s2AI0QrMzpXdZZqekK2XQt2HpQBS9mBtMHq4E,1043
2
2
  agi_med_common/file_storage.py,sha256=T0Hbs4W-pWO6HdWcmlVqABrQHYdq7lLZH4_Vu-YNVbw,1802
3
3
  agi_med_common/parallel_map.py,sha256=Qx6xe7DqlEUDpSucp5Hm8r9y9Iquwh9JvX7lOqHhnOw,921
4
4
  agi_med_common/type_union.py,sha256=diwmzcnbqkpGFckPHNw9o8zyQ955mOGNvhTlcBJ0RMI,1905
@@ -12,14 +12,14 @@ agi_med_common/api/content_interpreter_remote_api.py,sha256=cGaIKZGuosneLXs0jCM8
12
12
  agi_med_common/api/text_generator_api.py,sha256=c-Yy7KcldyyJtJo6cK6K_qDD1cm8yTMWMiUpzn4l1Kc,171
13
13
  agi_med_common/models/__init__.py,sha256=dqr2kP-RuxFfAZhCr103PQzTVZFKIcdxyzTYiHhdTsE,375
14
14
  agi_med_common/models/_base.py,sha256=qNdH8x3x3mYbo5XgWtR9VpEarxsEvXvzynadUlDvHmU,149
15
- agi_med_common/models/chat.py,sha256=GTdeV3JsubSGoHDJtvbppNhWRMiYhK4i9glxsBzuhrc,2991
16
- agi_med_common/models/chat_item.py,sha256=o3DMYk48otYd__hx8FeQ599gv-FHWl4kUDWI8AdsjYk,5138
15
+ agi_med_common/models/chat.py,sha256=LihfERvN_owk4zsW8eQPKOoXyYCz9lmsD5zw7db6vYk,7583
16
+ agi_med_common/models/chat_item.py,sha256=aF_sUtucV5hXeQ42Z-Jtsz9G1sWtjSFtElRs6hr0AQQ,4648
17
17
  agi_med_common/models/enums.py,sha256=J-GNpql9MCnKnWiV9aJRQGI-pAybvV86923RZs99grA,1006
18
18
  agi_med_common/models/tracks.py,sha256=UP-jeWqDiCK6dyoMDfs7hemgl_xsJKee_DApjBf-XYc,311
19
19
  agi_med_common/models/widget.py,sha256=aJZ2vWx_PTFN02Wz16eokz9IIVrxqNuZYVDqLG36toE,710
20
20
  agi_med_common/models/base_config_models/__init__.py,sha256=KjS_bSCka8BOMsigwcIML-e6eNB2ouMU6gxlhRmzeuY,44
21
21
  agi_med_common/models/base_config_models/gigachat_config.py,sha256=WNSCTO8Fjpxc1v2LRUHfKqo9aeMDpXltTHYBFgTD2N0,422
22
- agi_med_common-5.0.16.dist-info/METADATA,sha256=kEcigFalG-Lm6X0LYnRKbVU5gzem85SZdUwxxZ9jJBc,518
23
- agi_med_common-5.0.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
- agi_med_common-5.0.16.dist-info/top_level.txt,sha256=26o565jF_7wYQj7-YJfTedtT9yDxDcf8RNikOYuPq78,15
25
- agi_med_common-5.0.16.dist-info/RECORD,,
22
+ agi_med_common-5.0.17.dist-info/METADATA,sha256=GdyDBJ7fHIS-qvNIg-BpA_pXKgQ-ir8g7_FYnSnVj8Y,518
23
+ agi_med_common-5.0.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
24
+ agi_med_common-5.0.17.dist-info/top_level.txt,sha256=26o565jF_7wYQj7-YJfTedtT9yDxDcf8RNikOYuPq78,15
25
+ agi_med_common-5.0.17.dist-info/RECORD,,