agi-med-common 5.2.1__tar.gz → 5.2.3__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.
Files changed (27) hide show
  1. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/PKG-INFO +1 -1
  2. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/__init__.py +1 -1
  3. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/api.py +5 -7
  4. agi_med_common-5.2.3/src/agi_med_common/file_storage.py +113 -0
  5. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/models/chat.py +7 -6
  6. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/models/chat_item.py +4 -3
  7. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/models/widget.py +3 -3
  8. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/parallel_map.py +3 -1
  9. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/utils.py +2 -2
  10. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common.egg-info/PKG-INFO +1 -1
  11. agi_med_common-5.2.1/src/agi_med_common/file_storage.py +0 -63
  12. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/README.md +0 -0
  13. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/pyproject.toml +0 -0
  14. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/setup.cfg +0 -0
  15. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/models/__init__.py +0 -0
  16. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/models/base.py +0 -0
  17. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/models/base_config_models/__init__.py +0 -0
  18. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/models/base_config_models/gigachat_config.py +0 -0
  19. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/models/enums.py +0 -0
  20. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/models/tracks.py +0 -0
  21. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/type_union.py +0 -0
  22. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/validators.py +0 -0
  23. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common/xml_parser.py +0 -0
  24. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common.egg-info/SOURCES.txt +0 -0
  25. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common.egg-info/dependency_links.txt +0 -0
  26. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common.egg-info/requires.txt +0 -0
  27. {agi_med_common-5.2.1 → agi_med_common-5.2.3}/src/agi_med_common.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agi_med_common
3
- Version: 5.2.1
3
+ Version: 5.2.3
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
- __version__ = "5.2.1"
1
+ __version__ = "5.2.3"
2
2
 
3
3
  from .file_storage import FileStorage, ResourceId
4
4
  from .models.base import Base
@@ -1,5 +1,3 @@
1
- from typing import List, Tuple
2
-
3
1
  from agi_med_common.models.chat import Chat, ChatMessage
4
2
  from agi_med_common.models.tracks import DomainInfo, TrackInfo
5
3
  from pydantic import BaseModel
@@ -11,13 +9,13 @@ ResourceId = str
11
9
 
12
10
 
13
11
  class ChatManagerAPI:
14
- def get_domains(self, *, client_id: str, language_code: str = "ru") -> List[DomainInfo]:
12
+ def get_domains(self, *, client_id: str, language_code: str = "ru") -> list[DomainInfo]:
15
13
  raise NotImplementedError
16
14
 
17
- def get_tracks(self, *, client_id: str, language_code: str = "ru") -> List[TrackInfo]:
15
+ def get_tracks(self, *, client_id: str, language_code: str = "ru") -> list[TrackInfo]:
18
16
  raise NotImplementedError
19
17
 
20
- def get_response(self, *, chat: Chat) -> List[ChatMessage]:
18
+ def get_response(self, *, chat: Chat) -> list[ChatMessage]:
21
19
  raise NotImplementedError
22
20
 
23
21
 
@@ -40,7 +38,7 @@ class ContentInterpreterRemoteAPI:
40
38
 
41
39
 
42
40
  class ClassifierAPI:
43
- def get_values(self) -> List[Value]:
41
+ def get_values(self) -> list[Value]:
44
42
  raise NotImplementedError
45
43
 
46
44
  def evaluate(self, *, chat: Chat) -> Value:
@@ -55,7 +53,7 @@ class CriticAPI:
55
53
  class ContentInterpreterAPI:
56
54
  def interpret(
57
55
  self, *, kind: str, query: str, resource_id: str = "", chat: Chat | None = None
58
- ) -> Tuple[Interpretation, ResourceId | None]:
56
+ ) -> tuple[Interpretation, ResourceId | None]:
59
57
  raise NotImplementedError
60
58
 
61
59
 
@@ -0,0 +1,113 @@
1
+ import string
2
+ from hashlib import md5
3
+ from pathlib import Path
4
+ from zipfile import ZipFile, is_zipfile
5
+
6
+
7
+ ResourceId = str
8
+ ASCII_DIGITS = set(string.ascii_lowercase + string.digits)
9
+ SUFFIX_DIR = ".dir"
10
+
11
+
12
+ def _validate_exist(files_dir):
13
+ if not files_dir.exists():
14
+ err = f"Failed to access file-storage directory: {files_dir}"
15
+ raise OSError(err)
16
+
17
+
18
+ def _validate_dtype(dtype: str):
19
+ if all(map(ASCII_DIGITS.__contains__, dtype)):
20
+ return
21
+ raise ValueError(f"Bad dtype: {dtype}")
22
+
23
+
24
+ def generate_fname(content, dtype):
25
+ fname_hash = md5(content).hexdigest()
26
+ fname = f"{fname_hash}.{dtype}"
27
+ return fname
28
+
29
+
30
+ class FileStorage:
31
+ def __init__(self, files_dir):
32
+ self.files_dir = Path(files_dir)
33
+ self.files_dir.mkdir(exist_ok=True, parents=True)
34
+ _validate_exist(self.files_dir)
35
+
36
+ def _generate_fname_path(self, content: bytes, dtype: str):
37
+ fpath = self.files_dir / generate_fname(content, dtype)
38
+ return fpath
39
+
40
+ def upload_maybe(self, content: bytes | None, dtype: str) -> ResourceId | None:
41
+ if not content:
42
+ return None
43
+ resource_id = self.upload(content, dtype)
44
+ return resource_id
45
+
46
+ def upload(self, content: bytes | str, dtype: str) -> ResourceId:
47
+ _validate_dtype(dtype)
48
+ if isinstance(content, str):
49
+ content = content.encode()
50
+ fpath = self._generate_fname_path(content, dtype)
51
+ fpath.write_bytes(content)
52
+ return str(fpath)
53
+
54
+ def upload_dir(self, resource_ids: list[ResourceId]) -> ResourceId:
55
+ content = "\n".join(resource_ids)
56
+ res = self.upload(content, "dir")
57
+ return res
58
+
59
+ def download(self, resource_id: ResourceId) -> bytes:
60
+ return Path(resource_id).read_bytes()
61
+
62
+ def download_text(self, resource_id: ResourceId) -> str:
63
+ return Path(resource_id).read_text(encoding="utf-8")
64
+
65
+ def read_dir_or_none(self, resource_id: ResourceId) -> list[ResourceId] | None:
66
+ if not self.is_dir(resource_id):
67
+ return None
68
+ res = self.download_text(resource_id).split("\n")
69
+ return res
70
+
71
+ def _get_path(self, resource_id: ResourceId | None) -> Path | None:
72
+ if not resource_id:
73
+ return None
74
+ path = Path(resource_id)
75
+ return path if (path.exists() and path.is_file()) else None
76
+
77
+ def is_valid(self, resource_id: ResourceId | None) -> bool:
78
+ path = self._get_path(resource_id)
79
+ return path is not None
80
+
81
+ def is_file(self, resource_id: ResourceId | None) -> bool:
82
+ path = self._get_path(resource_id)
83
+ return path and path.suffix != SUFFIX_DIR
84
+
85
+ def is_dir(self, resource_id: ResourceId | None) -> bool:
86
+ path = self._get_path(resource_id)
87
+ return path and path.suffix == SUFFIX_DIR
88
+
89
+ def get_dtype(self, resource_id: ResourceId | None) -> str | None:
90
+ return resource_id and resource_id.rsplit(".")[-1]
91
+
92
+ def unzip_file(self, resource_id: str) -> ResourceId:
93
+ """ takes resource_id which refer to zip-archive, unpacks it and returns directory ResourceId with content of zip-archive """
94
+ path = self._get_path(resource_id)
95
+ if not path:
96
+ raise ValueError(f"Not found path: {resource_id}")
97
+ if not is_zipfile(resource_id):
98
+ raise ValueError(f"Expected zip archive but found: {resource_id}")
99
+
100
+ resource_ids = []
101
+
102
+ with ZipFile(path, mode='r') as zip_file:
103
+ files_info = zip_file.filelist
104
+
105
+ for file_info in zip_file.filelist:
106
+ file_dtype = file_info.filename.rsplit('.')[-1]
107
+ file_bytes = zip_file.read(file_info)
108
+ rid = self.upload(file_bytes, file_dtype)
109
+ resource_ids.append(rid)
110
+
111
+ res = self.upload_dir(resource_ids)
112
+ return res
113
+
@@ -1,7 +1,8 @@
1
1
  import warnings
2
2
  from copy import deepcopy
3
3
  from datetime import datetime
4
- from typing import Any, List, Dict, Literal, TypeVar, Callable
4
+ from typing import Any, Literal, TypeVar
5
+ from collections.abc import Callable
5
6
 
6
7
  from agi_med_common.models.chat_item import ChatItem, ReplicaItem, OuterContextItem
7
8
  from agi_med_common.models.widget import Widget
@@ -14,9 +15,9 @@ from .base import Base
14
15
 
15
16
  _DT_FORMAT: str = "%Y-%m-%d-%H-%M-%S"
16
17
  _EXAMPLE_DT: str = datetime(year=1970, month=1, day=1).strftime(_DT_FORMAT)
17
- StrDict = Dict[str, Any]
18
+ StrDict = dict[str, Any]
18
19
  ContentBase = str | Widget | StrDict
19
- Content = ContentBase | List[ContentBase]
20
+ Content = ContentBase | list[ContentBase]
20
21
  T = TypeVar("T")
21
22
 
22
23
 
@@ -184,7 +185,7 @@ ChatMessage = TypeUnion[HumanMessage, AIMessage, MiscMessage]
184
185
 
185
186
  class Chat(Base):
186
187
  context: Context = Field(default_factory=Context)
187
- messages: List[ChatMessage] = Field(default_factory=list)
188
+ messages: list[ChatMessage] = Field(default_factory=list)
188
189
 
189
190
  class Config:
190
191
  extra = Extra.ignore
@@ -208,11 +209,11 @@ class Chat(Base):
208
209
  def add_message(self, message: ChatMessage):
209
210
  self.messages.append(message)
210
211
 
211
- def add_messages(self, messages: List[ChatMessage]):
212
+ def add_messages(self, messages: list[ChatMessage]):
212
213
  for message in messages:
213
214
  self.messages.append(message)
214
215
 
215
- def replace_messages(self, messages: List[ChatMessage]):
216
+ def replace_messages(self, messages: list[ChatMessage]):
216
217
  return self.model_copy(update=dict(messages=messages))
217
218
 
218
219
  def get_last_state(self, default: str = "empty") -> str:
@@ -1,5 +1,6 @@
1
1
  from datetime import datetime
2
- from typing import Annotated, Any, List, Callable
2
+ from typing import Annotated, Any
3
+ from collections.abc import Callable
3
4
 
4
5
  from agi_med_common.models.widget import Widget
5
6
  from pydantic import Field, ConfigDict, BeforeValidator, AfterValidator
@@ -126,11 +127,11 @@ class ChatItem(Base):
126
127
  def add_replica(self, replica: ReplicaItem):
127
128
  self.inner_context.replicas.append(replica)
128
129
 
129
- def add_replicas(self, replicas: List[ReplicaItem]):
130
+ def add_replicas(self, replicas: list[ReplicaItem]):
130
131
  for replica in replicas:
131
132
  self.inner_context.replicas.append(replica)
132
133
 
133
- def replace_replicas(self, replicas: List[ReplicaItem]):
134
+ def replace_replicas(self, replicas: list[ReplicaItem]):
134
135
  return self.model_copy(update=dict(inner_context=InnerContextItem(replicas=replicas)))
135
136
 
136
137
  def get_last_state(self, default: str = "empty") -> str:
@@ -1,12 +1,12 @@
1
- from typing import List, Self, Literal
1
+ from typing import Self, Literal
2
2
 
3
3
  from pydantic import BaseModel, model_validator
4
4
 
5
5
 
6
6
  class Widget(BaseModel):
7
7
  type: Literal["widget"] = "widget"
8
- buttons: List[List[str]] | None = None
9
- ibuttons: List[List[str]] | None = None
8
+ buttons: list[list[str]] | None = None
9
+ ibuttons: list[list[str]] | None = None
10
10
 
11
11
  @model_validator(mode="after")
12
12
  def check(self) -> Self:
@@ -1,4 +1,6 @@
1
- from typing import Any, Callable, TypeVar, Iterable
1
+ from typing import Any, TypeVar
2
+ from collections.abc import Callable
3
+ from collections.abc import Iterable
2
4
  import concurrent.futures
3
5
  from tqdm import tqdm
4
6
 
@@ -1,13 +1,13 @@
1
1
  import codecs
2
2
  import json
3
3
  import os
4
+ from collections.abc import Iterable
4
5
  from datetime import datetime
5
6
  from pathlib import Path
6
- from typing import Iterable
7
7
 
8
8
 
9
9
  def make_session_id() -> str:
10
- return f"{datetime.now():%y%m%d%H%M%S}"
10
+ return f"{datetime.now():%Y-%m-%d--%H-%M-%S}"
11
11
 
12
12
 
13
13
  def read_json(path: Path | os.PathLike[str] | str) -> list | dict:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agi_med_common
3
- Version: 5.2.1
3
+ Version: 5.2.3
4
4
  Summary: Сommon for agi-med team
5
5
  Author: AGI-MED-TEAM
6
6
  Requires-Python: >=3.11
@@ -1,63 +0,0 @@
1
- import string
2
- from hashlib import md5
3
- from pathlib import Path
4
-
5
- ResourceId = str
6
- ASCII_DIGITS = set(string.ascii_lowercase + string.digits)
7
-
8
-
9
- def _validate_exist(files_dir):
10
- if not files_dir.exists():
11
- err = f"Failed to access file-storage directory: {files_dir}"
12
- raise OSError(err)
13
-
14
-
15
- def _validate_dtype(dtype: str):
16
- if all(map(ASCII_DIGITS.__contains__, dtype)):
17
- return
18
- raise ValueError(f"Bad dtype: {dtype}")
19
-
20
-
21
- def generate_fname(content, dtype):
22
- fname_hash = md5(content).hexdigest()
23
- fname = f"{fname_hash}.{dtype}"
24
- return fname
25
-
26
-
27
- class FileStorage:
28
- def __init__(self, files_dir):
29
- self.files_dir = Path(files_dir)
30
- self.files_dir.mkdir(exist_ok=True, parents=True)
31
- _validate_exist(self.files_dir)
32
-
33
- def _generate_fname_path(self, content, dtype):
34
- fpath = self.files_dir / generate_fname(content, dtype)
35
- return fpath
36
-
37
- def upload_maybe(
38
- self,
39
- content: bytes | None,
40
- dtype: str,
41
- ) -> ResourceId | None:
42
- if not content:
43
- return None
44
- resource_id = self.upload(content, dtype)
45
- return resource_id
46
-
47
- def upload(self, content: bytes, dtype: str) -> ResourceId | None:
48
- _validate_dtype(dtype)
49
- fpath = self._generate_fname_path(content, dtype)
50
- fpath.write_bytes(content)
51
- return str(fpath)
52
-
53
- def download(self, rid: ResourceId) -> bytes:
54
- return Path(rid).read_bytes()
55
-
56
- def download_text(self, rid: ResourceId) -> str:
57
- return Path(rid).read_text(encoding="utf-8")
58
-
59
- def is_valid(self, rid: ResourceId | None) -> bool:
60
- return rid and Path(rid).exists() and Path(rid).is_file()
61
-
62
- def get_dtype(self, rid: ResourceId | None) -> str | None:
63
- return rid and rid.rsplit(".")[-1]
File without changes
File without changes