banks 2.1.2__py3-none-any.whl → 2.2.0__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.
banks/__about__.py CHANGED
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2023-present Massimiliano Pippi <mpippi@gmail.com>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "2.1.2"
4
+ __version__ = "2.2.0"
banks/env.py CHANGED
@@ -4,7 +4,7 @@
4
4
  from jinja2 import Environment, select_autoescape
5
5
 
6
6
  from .config import config
7
- from .filters import audio, cache_control, image, lemmatize, tool
7
+ from .filters import audio, cache_control, image, lemmatize, tool, xml
8
8
 
9
9
 
10
10
  def _add_extensions(_env):
@@ -38,5 +38,6 @@ env.filters["image"] = image
38
38
  env.filters["lemmatize"] = lemmatize
39
39
  env.filters["tool"] = tool
40
40
  env.filters["audio"] = audio
41
+ env.filters["to_xml"] = xml
41
42
 
42
43
  _add_extensions(env)
banks/filters/__init__.py CHANGED
@@ -6,5 +6,6 @@ from .cache_control import cache_control
6
6
  from .image import image
7
7
  from .lemmatize import lemmatize
8
8
  from .tool import tool
9
+ from .xml import xml
9
10
 
10
- __all__ = ("cache_control", "image", "lemmatize", "tool", "audio")
11
+ __all__ = ("cache_control", "image", "lemmatize", "tool", "audio", "xml")
banks/filters/image.py CHANGED
@@ -1,15 +1,25 @@
1
1
  # SPDX-FileCopyrightText: 2023-present Massimiliano Pippi <mpippi@gmail.com>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
+ import re
4
5
  from pathlib import Path
5
6
  from urllib.parse import urlparse
6
7
 
7
8
  from banks.types import ContentBlock, ImageUrl
8
9
 
10
+ BASE64_PATH_REGEX = re.compile(r"image\/.*;base64,.*")
11
+
9
12
 
10
13
  def _is_url(string: str) -> bool:
11
14
  result = urlparse(string)
12
- return all([result.scheme, result.netloc])
15
+ if not result.scheme:
16
+ return False
17
+
18
+ if not result.netloc:
19
+ # The only valid format when netloc is empty is base64 data urls
20
+ return all([result.scheme == "data", BASE64_PATH_REGEX.match(result.path)])
21
+
22
+ return True
13
23
 
14
24
 
15
25
  def image(value: str) -> str:
banks/filters/xml.py ADDED
@@ -0,0 +1,62 @@
1
+ import json
2
+ import xml.etree.ElementTree as ET
3
+ from typing import Any, Optional, Union
4
+ from xml.dom.minidom import parseString
5
+
6
+ from pydantic import BaseModel
7
+
8
+
9
+ def _deserialize(string: str) -> Optional[dict]:
10
+ try:
11
+ return json.loads(string)
12
+ except json.JSONDecodeError:
13
+ return None
14
+
15
+
16
+ def _prepare_dictionary(value: Union[str, BaseModel, dict[str, Any]]):
17
+ root_tag = "input"
18
+ if isinstance(value, str):
19
+ model: Optional[dict[str, Any]] = _deserialize(value)
20
+ if model is None:
21
+ msg = f"{value} is not deserializable"
22
+ raise ValueError(msg)
23
+ elif isinstance(value, BaseModel):
24
+ model = value.model_dump()
25
+ root_tag = value.__class__.__name__.lower()
26
+ elif isinstance(value, dict):
27
+ model = value.copy()
28
+ for k in value.keys():
29
+ if not isinstance(k, str):
30
+ key = str(k)
31
+ if isinstance(k, (int, float)):
32
+ key = "_" + key
33
+ v = model.pop(k)
34
+ model[key.lower()] = v
35
+ else:
36
+ msg = f"Input can only be of type BaseModel, dictionary or deserializable string. Got {type(value)}"
37
+ raise ValueError(msg)
38
+ return model, root_tag
39
+
40
+
41
+ def xml(value: Union[str, BaseModel, dict[str, Any]]) -> str:
42
+ """
43
+ Convert a Pydantic model, a deserializable string or a dictionary into an XML string.
44
+
45
+ Example:
46
+ ```jinja
47
+ {{'{"username": "user", "email": "example@email.com"}' | to_xml}}
48
+ "
49
+ <input>
50
+ <username>user</username>
51
+ <email>example@email.com</email>
52
+ </input>
53
+ "
54
+ ```
55
+ """
56
+ model, root_tag = _prepare_dictionary(value)
57
+ xml_model = ET.Element(root_tag)
58
+ for k, v in model.items():
59
+ sub = ET.SubElement(xml_model, k)
60
+ sub.text = str(v)
61
+ xml_str = ET.tostring(xml_model, encoding="unicode")
62
+ return parseString(xml_str).toprettyxml().replace('<?xml version="1.0" ?>\n', "") # noqa: S318
@@ -27,7 +27,7 @@ DEFAULT_INDEX_NAME = "index.json"
27
27
  class PromptFile(PromptModel):
28
28
  """Model representing a prompt file stored on disk."""
29
29
 
30
- path: Path = Field(exclude=True)
30
+ path: Path | None = Field(default=None, exclude=True)
31
31
 
32
32
  @classmethod
33
33
  def from_prompt_path(cls: type[Self], prompt: Prompt, path: Path) -> Self:
@@ -101,7 +101,7 @@ class DirectoryPromptRegistry:
101
101
  """
102
102
  version = version or DEFAULT_VERSION
103
103
  for pf in self._index.files:
104
- if pf.name == name and pf.version == version and pf.path.exists():
104
+ if pf.name == name and pf.version == version and pf.path and pf.path.exists():
105
105
  return Prompt(**pf.model_dump())
106
106
  raise PromptNotFoundError
107
107
 
@@ -135,6 +135,10 @@ class DirectoryPromptRegistry:
135
135
  def _load(self):
136
136
  """Load the prompt index from disk."""
137
137
  self._index = PromptFileIndex.model_validate_json(self._index_path.read_text())
138
+ # Reconstruct the file paths since they're excluded from serialization
139
+ for pf in self._index.files:
140
+ if pf.path is None:
141
+ pf.path = self._path / f"{pf.name}.{pf.version}.jinja"
138
142
 
139
143
  def _save(self):
140
144
  """Save the prompt index to disk."""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: banks
3
- Version: 2.1.2
3
+ Version: 2.2.0
4
4
  Summary: A prompt programming language
5
5
  Project-URL: Documentation, https://github.com/masci/banks#readme
6
6
  Project-URL: Issues, https://github.com/masci/banks/issues
@@ -1,8 +1,8 @@
1
- banks/__about__.py,sha256=FgQqeZsldfp1gHFQOfAIpO9mYK5Yl83xo-6r31g2O-0,132
1
+ banks/__about__.py,sha256=77xoXWQ484-a4X30IBRswGGTfRx8t_BwItzAm7NTXwI,132
2
2
  banks/__init__.py,sha256=4IBopxXstFZliCvSjOuTurSQb32Vy26EXOPhmNZ4Hus,334
3
3
  banks/cache.py,sha256=uUGAu82-mfrscc2q24x19ZMZBkoQzf3hh7_V300J-Ik,1069
4
4
  banks/config.py,sha256=c6B1cXUZ-NN0XmJvfezXeHPXHP7knk8TfbmcZL7gCzk,1082
5
- banks/env.py,sha256=PBSz-iH_E3_KYofAfsQZLrYMZXVLNJLxgSFD7GB1Xd0,1233
5
+ banks/env.py,sha256=XOSz6QGNSRaqnIdKWhH5U-ci8Tfi1mDyJHit_aE27Ro,1266
6
6
  banks/errors.py,sha256=I5cgsa7wtolRVKBSq_aH5xs27yVcErBlMyUswCnM-es,580
7
7
  banks/prompt.py,sha256=RhPq3wpE-AiCfCftZpPFj2HXGdazwYD502Pr1e-j7FY,8162
8
8
  banks/types.py,sha256=03x7E7FPVfuN39xY--c0fKumnyVUVzNrq9pgG5R-pAU,5520
@@ -11,17 +11,18 @@ banks/extensions/__init__.py,sha256=Lx4UrOzywYQY7a8qvIqvc3ql54nwK0lNP7x3jYdbREY,
11
11
  banks/extensions/chat.py,sha256=VV6UV1wQZcJ0KbIFHSFmDeptWtww4o2IXF5pXB6TpTM,2478
12
12
  banks/extensions/completion.py,sha256=kF55PiNxjqpslUTAd46H4jOy0eFiLLm5hEcwxS4_oxs,7356
13
13
  banks/extensions/docs.py,sha256=vWOZvu2JoS4LwUG-BR3jPqThirYvu3Fdba331UxooYM,1098
14
- banks/filters/__init__.py,sha256=LV4mtTYkmLPo0OST8PMhJJmyC8azZJgU-ZKtpAVU1_0,325
14
+ banks/filters/__init__.py,sha256=MMNxopwecFHW4LA76NwL2JQkdddIAGbKOaHUHG1JQs8,353
15
15
  banks/filters/audio.py,sha256=2vTPdpDo8FVQsl0WiPlXskwMCGnF8zKwWXfq1fYQzws,726
16
16
  banks/filters/cache_control.py,sha256=aOGOIzuqasV_TcuFaaXbaoGhA2W9YTFuz7wkatyjXRU,962
17
- banks/filters/image.py,sha256=cxp_Tlqy-DQ1y3I6IyO6TwM7KkdP8aL_xEKcSqeWX1w,1140
17
+ banks/filters/image.py,sha256=Ls1fWCgRx0YLGIFx7hdKtR1skY575jDWlCESP0zV1Bs,1407
18
18
  banks/filters/lemmatize.py,sha256=Yvp8M4HCx6C0nrcu3UEMtjJUwsyVYI6GQDYOG4S6EEw,887
19
19
  banks/filters/tool.py,sha256=i8ukSDYw54ksShVJ2abfRQAiKzKrqUtmgBB1H04cig0,475
20
+ banks/filters/xml.py,sha256=uQ_2zfCf8NhpdbF8F5HS7URXvDzsxfg-TEIVGufZbM0,1991
20
21
  banks/registries/__init__.py,sha256=iRK-8420cKBckOTd5KcIFQyV66EsF0Mc7UHCkzf8qZU,255
21
- banks/registries/directory.py,sha256=WXVL8gIrH2OqDYcC89TuTW-iXSwT9WqJq_OWe_kYC2Q,5803
22
+ banks/registries/directory.py,sha256=gRFO7fl9yXHt2NJ1pDA2wPSQtlORhSw1GKWxSTyFzE8,6055
22
23
  banks/registries/file.py,sha256=8ayvFrcM8Tk0DWgGXmKD2DRBfGXr5CmgtdQaQ5cXhow,4054
23
24
  banks/registries/redis.py,sha256=eBL92URJa-NegOxRLS4b2xrDRDxz6iiaz_7Ddi32Rtc,2756
24
- banks-2.1.2.dist-info/METADATA,sha256=o-tu_uFkiR7T3pkakUGoIZq4iOWIy7bkTay_9vX36lw,12098
25
- banks-2.1.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
- banks-2.1.2.dist-info/licenses/LICENSE.txt,sha256=NZJne_JTwMFwq_g-kq-sm4PuaeVOgu1l3NUGOgBHX-g,1102
27
- banks-2.1.2.dist-info/RECORD,,
25
+ banks-2.2.0.dist-info/METADATA,sha256=m2W5swzWGUFGr0uNCFAgOqFtxv4A7fMyjsC34GDBTsk,12098
26
+ banks-2.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
+ banks-2.2.0.dist-info/licenses/LICENSE.txt,sha256=NZJne_JTwMFwq_g-kq-sm4PuaeVOgu1l3NUGOgBHX-g,1102
28
+ banks-2.2.0.dist-info/RECORD,,
File without changes