mmar-mapi 1.0.15__tar.gz → 1.0.16__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.

Potentially problematic release.


This version of mmar-mapi might be problematic. Click here for more details.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mmar-mapi
3
- Version: 1.0.15
3
+ Version: 1.0.16
4
4
  Summary: Common pure/IO utilities for multi-modal architectures team
5
5
  Keywords:
6
6
  Author: Eugene Tagin
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "mmar-mapi"
3
3
  # dynamic version is not supported yet on uv_build
4
- version = "1.0.15"
4
+ version = "1.0.16"
5
5
  description = "Common pure/IO utilities for multi-modal architectures team"
6
6
  authors = [{name = "Eugene Tagin", email = "tagin@airi.net"}]
7
7
  license = "MIT"
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
  from zipfile import ZipFile, is_zipfile
7
7
 
8
8
  ResourceId = str
9
- ASCII_DIGITS_SPECIAL = set(string.ascii_lowercase + string.digits + '-')
9
+ ASCII_DIGITS_SPECIAL = set(string.ascii_lowercase + string.digits + "-")
10
10
  SUFFIX_DIR = ".dir"
11
11
  SUFFIX_METADATA = ".metadata"
12
12
 
@@ -61,8 +61,11 @@ class FileStorage:
61
61
 
62
62
  return str(fpath)
63
63
 
64
- def get_metadata_path(self, resource_id: ResourceId) -> ResourceId:
65
- return Path(resource_id).with_suffix(SUFFIX_METADATA).as_posix()
64
+ def get_metadata(self, resource_id: ResourceId) -> dict | None:
65
+ metadata_path = Path(resource_id).with_suffix(SUFFIX_METADATA)
66
+ if not metadata_path.exists():
67
+ return None
68
+ return json.loads(metadata_path.read_text())
66
69
 
67
70
  async def upload_async(self, content: bytes | str, fname: str) -> ResourceId:
68
71
  return self.upload(content, fname)
@@ -4,14 +4,14 @@ from copy import deepcopy
4
4
  from datetime import datetime
5
5
  from typing import Any, Literal, TypeVar
6
6
 
7
- from mmar_mapi.models.chat_item import ChatItem, ReplicaItem, OuterContextItem
7
+ from pydantic import Field, ValidationError
8
+
9
+ from mmar_mapi.models.chat_item import ChatItem, OuterContextItem, ReplicaItem
8
10
  from mmar_mapi.models.widget import Widget
9
11
  from mmar_mapi.type_union import TypeUnion
10
- from pydantic import Field, ValidationError
11
12
 
12
13
  from .base import Base
13
14
 
14
-
15
15
  _DT_FORMAT: str = "%Y-%m-%d-%H-%M-%S"
16
16
  _EXAMPLE_DT: str = datetime(year=1970, month=1, day=1).strftime(_DT_FORMAT)
17
17
  StrDict = dict[str, Any]
@@ -96,6 +96,22 @@ def _get_resource_id(obj: Content) -> str | None:
96
96
  return None
97
97
 
98
98
 
99
+ def _get_resource_name(obj: Content) -> str | None:
100
+ if isinstance(obj, list):
101
+ return next((el for el in map(_get_resource_name, obj) if el), None)
102
+ if isinstance(obj, dict) and obj.get("type") == "resource_id":
103
+ return _get_field(obj, "resource_name", str)
104
+ return None
105
+
106
+
107
+ def _get_resource(obj: Content) -> str | None:
108
+ if isinstance(obj, list):
109
+ return next((el for el in map(_get_resource_id, obj) if el), None)
110
+ if isinstance(obj, dict) and obj.get("type") == "resource_id":
111
+ return obj
112
+ return None
113
+
114
+
99
115
  def _get_command(obj: Content) -> dict | None:
100
116
  if isinstance(obj, list):
101
117
  return next((el for el in map(_get_command, obj) if el), None)
@@ -138,6 +154,15 @@ class BaseMessage(Base):
138
154
  def resource_id(self) -> str | None:
139
155
  return _get_resource_id(self.content)
140
156
 
157
+ @property
158
+ def resource_name(self) -> str | None:
159
+ res = _get_resource_name(self.content)
160
+ return res
161
+
162
+ @property
163
+ def resource(self) -> dict | None:
164
+ return _get_resource(self.content)
165
+
141
166
  @property
142
167
  def command(self) -> dict | None:
143
168
  return _get_command(self.content)
@@ -162,7 +187,7 @@ class BaseMessage(Base):
162
187
  return _DT_FORMAT
163
188
 
164
189
  @staticmethod
165
- def find_resource_id(msg: "BaseMessage", ext: str | None = None, type: str=None) -> str | None:
190
+ def find_resource_id(msg: "BaseMessage", ext: str | None = None, type: str = None) -> str | None:
166
191
  resource_id = msg.resource_id
167
192
  if type and type != msg.type:
168
193
  return None
@@ -188,6 +213,7 @@ class AIMessage(BaseMessage):
188
213
  def with_state(self, state: str) -> "AIMessage":
189
214
  return self.model_copy(update=dict(state=state))
190
215
 
216
+
191
217
  class MiscMessage(BaseMessage):
192
218
  type: Literal["misc"] = "misc"
193
219
 
@@ -259,20 +285,37 @@ def make_content(
259
285
  text: str | None = None,
260
286
  *,
261
287
  resource_id: str | None = None,
288
+ resource: dict | None = None,
262
289
  command: dict | None = None,
263
290
  widget: Widget | None = None,
264
291
  content: Content | None = None,
265
292
  ) -> Content:
266
- resource_id = (resource_id or None) and {"type": "resource_id", "resource_id": resource_id}
293
+ if resource and resource_id:
294
+ raise ValueError("Cannot pass both 'resource' and 'resource_id'")
295
+
296
+ if resource_id:
297
+ resource = {"type": "resource_id", "resource_id": resource_id}
298
+ elif resource:
299
+ if not isinstance(resource, dict):
300
+ raise TypeError("'resource' must be a dict")
301
+ resource_id = resource.get("resource_id")
302
+ if not resource_id:
303
+ raise ValueError("'resource' must contain 'resource_id'")
304
+ resource_name = resource.get("resource_name")
305
+ resource = {"type": "resource_id", "resource_id": resource_id}
306
+ if resource_name:
307
+ resource["resource_name"] = resource_name
308
+ else:
309
+ resource = None
310
+
267
311
  command = (command or None) and {"type": "command", "command": command}
268
312
 
269
313
  content = content if isinstance(content, list) else [content] if content else []
270
- content += list(filter(None, [text, resource_id, command, widget]))
314
+ content += list(filter(None, [text, resource, command, widget]))
271
315
  if len(content) == 0:
272
316
  content = ""
273
317
  elif len(content) == 1:
274
318
  content = content[0]
275
-
276
319
  return content
277
320
 
278
321
 
@@ -285,13 +328,19 @@ def convert_replica_item_to_message(replica: ReplicaItem) -> ChatMessage:
285
328
  widget=replica.widget,
286
329
  )
287
330
  # legacy: eliminate after migration
288
- resource_id = (replica.resource_id or None) and {"type": "resource_id", "resource_id": replica.resource_id}
331
+ if resource_id := replica.resource_id:
332
+ resource = {"type": "resource_id", "resource_id": resource_id}
333
+ resource_name = replica.resource_name
334
+ if resource_name:
335
+ resource["resource_name"] = resource_name
336
+ else:
337
+ resource = None
289
338
  body = replica.body
290
339
  command = (replica.command or None) and {"type": "command", "command": replica.command}
291
340
  widget = replica.widget
292
341
  date_time = replica.date_time
293
342
 
294
- content = list(filter(None, [body, resource_id, command, widget]))
343
+ content = list(filter(None, [body, resource, command, widget]))
295
344
  if len(content) == 0:
296
345
  content = ""
297
346
  elif len(content) == 1:
@@ -375,6 +424,7 @@ def convert_message_to_replica_item(message: ChatMessage) -> ReplicaItem | None:
375
424
  role=role,
376
425
  body=message.text,
377
426
  resource_id=message.resource_id,
427
+ resource_name=message.resource_name,
378
428
  command=message.command,
379
429
  widget=message.widget,
380
430
  date_time=message.date_time,
@@ -388,10 +438,11 @@ def convert_message_to_replica_item(message: ChatMessage) -> ReplicaItem | None:
388
438
 
389
439
  def convert_chat_to_chat_item(chat: Chat) -> ChatItem:
390
440
  # legacy: eliminate after migration
391
- return ChatItem(
441
+ res = ChatItem(
392
442
  outer_context=convert_context_to_outer_context(chat.context),
393
443
  inner_context=dict(replicas=list(map(convert_message_to_replica_item, chat.messages))),
394
444
  )
445
+ return res
395
446
 
396
447
 
397
448
  def parse_chat_item_as_chat(chat_obj: str | dict | ChatItem) -> Chat:
@@ -69,6 +69,9 @@ class ReplicaItem(Base):
69
69
  resource_id: Annotated[str | None, AfterValidator(nullify_empty)] = Field(
70
70
  None, alias="ResourceId", examples=["<link-id>"]
71
71
  )
72
+ resource_name: Annotated[str | None, AfterValidator(nullify_empty)] = Field(
73
+ None, alias="ResourceName", examples=["filename"]
74
+ )
72
75
  widget: Widget | None = Field(None, alias="Widget", examples=[None])
73
76
  command: dict | None = Field(None, alias="Command", examples=[None])
74
77
  role: bool = Field(False, alias="Role", description="True = ai, False = client", examples=[False])
@@ -6,7 +6,7 @@ from datetime import datetime
6
6
  def make_session_id(with_millis=False) -> str:
7
7
  dt = datetime.now()
8
8
  fmt = "%Y-%m-%d--%H-%M-%S-%f" if with_millis else "%Y-%m-%d--%H-%M-%S"
9
- return datetime.now().strftime(fmt)
9
+ return dt.strftime(fmt)
10
10
 
11
11
 
12
12
  def chunked(items: Iterable, n) -> list:
@@ -4,8 +4,9 @@ from types import ModuleType
4
4
 
5
5
  from loguru import logger
6
6
 
7
+
7
8
  def convert_snake_to_pascal(name: str) -> str:
8
- """ snake_to_pascal -> SnakeToPascal """
9
+ """snake_to_pascal -> SnakeToPascal"""
9
10
  return "".join(map(str.capitalize, name.split("_")))
10
11
 
11
12
 
File without changes
File without changes