ygg 0.1.53__py3-none-any.whl → 0.1.55__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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ygg
3
- Version: 0.1.53
3
+ Version: 0.1.55
4
4
  Summary: Type-friendly utilities for moving data between Python objects, Arrow, Polars, Pandas, Spark, and Databricks
5
5
  Author: Yggdrasil contributors
6
6
  License: Apache License
@@ -1,7 +1,9 @@
1
- ygg-0.1.53.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
1
+ ygg-0.1.55.dist-info/licenses/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
2
2
  yggdrasil/__init__.py,sha256=4-ghPak2S6zfMqmnlxW2GCgPb5s79znpKa2hGEGXcE4,24
3
- yggdrasil/version.py,sha256=OwqD5Bb-XPoHxyR3pJbfa8OkEcCnfpl_gLnvTYdwfkQ,22
3
+ yggdrasil/version.py,sha256=RJcC5yver_ugtAU_HgwZ-2HWyszBXpsLZyyNma6G4Dc,22
4
4
  yggdrasil/databricks/__init__.py,sha256=skctY2c8W-hI81upx9F_PWRe5ishL3hrdiTuizgDjdw,152
5
+ yggdrasil/databricks/ai/__init__.py,sha256=Mkp70UOVBzDQvdPNsqncHcyzxe5PnSGYE_bHnYxA1eA,21
6
+ yggdrasil/databricks/ai/loki.py,sha256=iyekxctP6393LBN0PJOZgHBxQs9vDyQeRXPrru_krF0,3661
5
7
  yggdrasil/databricks/compute/__init__.py,sha256=NvdzmaJSNYY1uJthv1hHdBuNu3bD_-Z65DWnaJt9yXg,289
6
8
  yggdrasil/databricks/compute/cluster.py,sha256=YomLfvB0oxbgl6WDgBRxI1UXsxwlEbR6gq3FUbPHscY,44199
7
9
  yggdrasil/databricks/compute/execution_context.py,sha256=jIV6uru2NeX3O5lg-3KEqmXtLxxq45CFgkBQgQIIOHQ,23327
@@ -16,11 +18,11 @@ yggdrasil/databricks/sql/types.py,sha256=5G-BM9_eOsRKEMzeDTWUsWW5g4Idvs-czVCpOCr
16
18
  yggdrasil/databricks/sql/warehouse.py,sha256=1J0dyQLJb-OS1_1xU1eAVZ4CoL2-FhFeowKSvU3RzFc,9773
17
19
  yggdrasil/databricks/workspaces/__init__.py,sha256=dv2zotoFVhNFlTCdRq6gwf5bEzeZkOZszoNZMs0k59g,114
18
20
  yggdrasil/databricks/workspaces/filesytem.py,sha256=Z8JXU7_XUEbw9fpTQT1avRQKi-IAP2KemXBMPkUoY4w,9805
19
- yggdrasil/databricks/workspaces/io.py,sha256=4Tyrg2YPlyQMrdzE8Wxx5nTTWnFOmOfA-cFgorB57KA,32998
21
+ yggdrasil/databricks/workspaces/io.py,sha256=PAoxIxYvTC162Dx2qL2hk8oAdt8BnYrQ3jJHcJm4VkA,33116
20
22
  yggdrasil/databricks/workspaces/path.py,sha256=KkvLFHrps3UFr4ogYdESbJHEMfQBcWfWfXjlrv_7rTU,55180
21
23
  yggdrasil/databricks/workspaces/path_kind.py,sha256=rhWe1ky7uPD0du0bZSv2S4fK4C5zWd7zAF3UeS2iiPU,283
22
24
  yggdrasil/databricks/workspaces/volumes_path.py,sha256=s8CA33cG3jpMVJy5MILLlkEBcFg_qInDCF2jozLj1Fg,2431
23
- yggdrasil/databricks/workspaces/workspace.py,sha256=zBlQdYNT_xKwUCYo3O4Q4g-8pfMvff3I26efyCfY_TY,24961
25
+ yggdrasil/databricks/workspaces/workspace.py,sha256=5DCPz5io_rmrpGNi5I6RChmyZ8kjlNUFGQl8mzQJThg,25511
24
26
  yggdrasil/dataclasses/__init__.py,sha256=_RkhfF3KC1eSORby1dzvBXQ0-UGG3u6wyUQWX2jq1Pc,108
25
27
  yggdrasil/dataclasses/dataclass.py,sha256=LxrCjwvmBnb8yRI_N-c31RHHxB4XoJPixmKg9iBIuaI,1148
26
28
  yggdrasil/libs/__init__.py,sha256=zdC9OU0Xy36CLY9mg2drxN6S7isPR8aTLzJA6xVIeLE,91
@@ -31,14 +33,14 @@ yggdrasil/libs/sparklib.py,sha256=FQ3W1iz2EIpQreorOiQuFt15rdhq2QhGEAWp8Zrbl9A,10
31
33
  yggdrasil/libs/extensions/__init__.py,sha256=mcXW5Li3Cbprbs4Ci-b5A0Ju0wmLcfvEiFusTx6xNjU,117
32
34
  yggdrasil/libs/extensions/polars_extensions.py,sha256=RTkGi8llhPJjX7x9egix7-yXWo2X24zIAPSKXV37SSA,12397
33
35
  yggdrasil/libs/extensions/spark_extensions.py,sha256=E64n-3SFTDgMuXwWitX6vOYP9ln2lpGKb0htoBLEZgc,16745
34
- yggdrasil/pyutils/__init__.py,sha256=tl-LapAc71TV7RMgf2ftKwrzr8iiLOGHeJgA3RvO93w,293
36
+ yggdrasil/pyutils/__init__.py,sha256=a7LKg9xWZGtv2o5jr_7wrSZJ9ZmwolKWc--8iRgKlL4,225
35
37
  yggdrasil/pyutils/callable_serde.py,sha256=1XckmFO-ThP0MedxgXwB71u9jWUuhM1btOzW9gJ8w9g,23117
36
38
  yggdrasil/pyutils/equality.py,sha256=Xyf8D1dLUCm3spDEir8Zyj7O4US_fBJwEylJCfJ9slI,3080
37
39
  yggdrasil/pyutils/exceptions.py,sha256=ssKNm-rjhavHUOZmGA7_1Gq9tSHDrb2EFI-cnBuWgng,3388
38
40
  yggdrasil/pyutils/expiring_dict.py,sha256=pr2u25LGwPVbLfsLptiHGovUtYRRo0AMjaJtCtJl7nQ,8477
39
41
  yggdrasil/pyutils/modules.py,sha256=B7IP99YqUMW6-DIESFzBx8-09V1d0a8qrIJUDFhhL2g,11424
40
42
  yggdrasil/pyutils/parallel.py,sha256=ubuq2m9dJzWYUyKCga4Y_9bpaeMYUrleYxdp49CHr44,6781
41
- yggdrasil/pyutils/python_env.py,sha256=Gh5geFK9ABpyWEfyegGUfIJUoPxKwcH0pqLBiMrW9Rw,51103
43
+ yggdrasil/pyutils/python_env.py,sha256=nmXXcIUSSbpKWHBnYfCY1QIW-LQ8IILV9Ceijufu5Pg,51086
42
44
  yggdrasil/pyutils/retry.py,sha256=gXBtn1DdmIYIUmGKOUr8-SUT7MOu97LykN2YR4uocgc,11917
43
45
  yggdrasil/requests/__init__.py,sha256=dMesyzq97_DmI765x0TwaDPEfsxFtgGNgchk8LvEN-o,103
44
46
  yggdrasil/requests/msal.py,sha256=s2GCyzbgFdgdlJ1JqMrZ4qYVbmoG46-ZOTcaVQhZ-sQ,9220
@@ -57,8 +59,8 @@ yggdrasil/types/cast/registry.py,sha256=OOqIfbIjPH-a3figvu-zTvEtUDTEWhe2xIl3cCA4
57
59
  yggdrasil/types/cast/spark_cast.py,sha256=_KAsl1DqmKMSfWxqhVE7gosjYdgiL1C5bDQv6eP3HtA,24926
58
60
  yggdrasil/types/cast/spark_pandas_cast.py,sha256=BuTiWrdCANZCdD_p2MAytqm74eq-rdRXd-LGojBRrfU,5023
59
61
  yggdrasil/types/cast/spark_polars_cast.py,sha256=btmZNHXn2NSt3fUuB4xg7coaE0RezIBdZD92H8NK0Jw,9073
60
- ygg-0.1.53.dist-info/METADATA,sha256=U_RTDcmrHbLOrFiDxsO0yX4Lgi3qbjd009gh-uuohqY,18528
61
- ygg-0.1.53.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
62
- ygg-0.1.53.dist-info/entry_points.txt,sha256=6q-vpWG3kvw2dhctQ0LALdatoeefkN855Ev02I1dKGY,70
63
- ygg-0.1.53.dist-info/top_level.txt,sha256=iBe9Kk4VIVbLpgv_p8OZUIfxgj4dgJ5wBg6vO3rigso,10
64
- ygg-0.1.53.dist-info/RECORD,,
62
+ ygg-0.1.55.dist-info/METADATA,sha256=F84590C1dKd4ZllEbe1uzAsMuUw9we134qVVQmOcGNI,18528
63
+ ygg-0.1.55.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
64
+ ygg-0.1.55.dist-info/entry_points.txt,sha256=6q-vpWG3kvw2dhctQ0LALdatoeefkN855Ev02I1dKGY,70
65
+ ygg-0.1.55.dist-info/top_level.txt,sha256=iBe9Kk4VIVbLpgv_p8OZUIfxgj4dgJ5wBg6vO3rigso,10
66
+ ygg-0.1.55.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1 @@
1
+ from .loki import *
@@ -0,0 +1,122 @@
1
+ from typing import Optional, Dict, Any, List
2
+ from dataclasses import field, dataclass
3
+
4
+ from ..workspaces.workspace import WorkspaceService
5
+
6
+ try:
7
+ from openai import OpenAI
8
+
9
+ def make_ai_client(
10
+ api_key: str,
11
+ base_url: str
12
+ ):
13
+ return OpenAI(
14
+ api_key=api_key,
15
+ base_url=base_url
16
+ )
17
+ except ImportError:
18
+ class OpenAI:
19
+ pass
20
+
21
+ def make_ai_client(
22
+ api_key: str,
23
+ base_url: str
24
+ ):
25
+ from openai import OpenAI
26
+
27
+ return OpenAI(
28
+ api_key=api_key,
29
+ base_url=base_url
30
+ )
31
+
32
+ __all__ = [
33
+ "Loki"
34
+ ]
35
+
36
+
37
+ @dataclass
38
+ class Loki(WorkspaceService):
39
+ model: str = "databricks-gemini-2-5-flash"
40
+
41
+ _ai_client: Optional[OpenAI] = field(repr=False, hash=False, default=None)
42
+
43
+ @property
44
+ def ai_client(self):
45
+ if self._ai_client is None:
46
+ self._ai_client = self.make_aiclient()
47
+ return self._ai_client
48
+
49
+ def make_aiclient(self):
50
+ return make_ai_client(
51
+ api_key=self.workspace.current_token(),
52
+ base_url=self.workspace.host + "/serving-endpoints"
53
+ )
54
+
55
+ def ask(
56
+ self,
57
+ command: str,
58
+ *,
59
+ system: Optional[str] = None,
60
+ max_tokens: int = 5000,
61
+ temperature: Optional[float] = None,
62
+ extra_messages: Optional[List[Dict[str, str]]] = None,
63
+ **kwargs,
64
+ ) -> str:
65
+ """
66
+ Send a chat prompt to a Databricks Model Serving endpoint using the
67
+ OpenAI-compatible Chat Completions API and return the assistant's text.
68
+
69
+ This is a thin convenience wrapper around:
70
+ self.ai_client.chat.completions.create(...)
71
+
72
+ Parameters
73
+ ----------
74
+ command:
75
+ The user prompt text to send.
76
+ system:
77
+ Optional system instruction (prepended as the first message).
78
+ max_tokens:
79
+ Upper bound on the number of tokens generated in the response.
80
+ temperature:
81
+ Optional sampling temperature. If None, the client/model default is used.
82
+ extra_messages:
83
+ Optional list of additional chat messages to insert before the user prompt.
84
+ Each item should be a dict like {"role": "...", "content": "..."}.
85
+ Useful for few-shot examples or carrying prior context.
86
+ **kwargs:
87
+ Any additional parameters forwarded directly to
88
+ `chat.completions.create(...)` (e.g. top_p, presence_penalty, etc.).
89
+
90
+ Returns
91
+ -------
92
+ str
93
+ The assistant message content (empty string if the API returns no content).
94
+
95
+ Raises
96
+ ------
97
+ Exception
98
+ Propagates any exceptions raised by the OpenAI client (HTTP errors,
99
+ auth errors, invalid request errors, timeouts, etc.).
100
+ """
101
+ messages: List[Dict[str, str]] = []
102
+
103
+ if system:
104
+ messages.append({"role": "system", "content": system})
105
+
106
+ if extra_messages:
107
+ messages.extend(extra_messages)
108
+
109
+ messages.append({"role": "user", "content": command})
110
+
111
+ # Build params cleanly (only include temperature if provided)
112
+ params: Dict[str, Any] = dict(
113
+ model=self.model,
114
+ messages=messages,
115
+ max_tokens=max_tokens,
116
+ **kwargs,
117
+ )
118
+ if temperature is not None:
119
+ params["temperature"] = temperature
120
+
121
+ resp = self.ai_client.chat.completions.create(**params)
122
+ return resp.choices[0].message.content or ""
@@ -12,7 +12,8 @@ import pyarrow.csv as pcsv
12
12
  import pyarrow.parquet as pq
13
13
  from pyarrow.dataset import (
14
14
  FileFormat,
15
- ParquetFileFormat, CsvFileFormat,
15
+ ParquetFileFormat,
16
+ CsvFileFormat,
16
17
  )
17
18
 
18
19
  from .path_kind import DatabricksPathKind
@@ -71,10 +72,11 @@ class DatabricksIO(ABC, IO):
71
72
  self.close()
72
73
 
73
74
  def __del__(self):
74
- try:
75
- Thread(target=self.close).start()
76
- except BaseException:
77
- pass
75
+ if self._need_flush():
76
+ try:
77
+ Thread(target=self.close).start()
78
+ except BaseException:
79
+ pass
78
80
 
79
81
  def __next__(self):
80
82
  """Iterate over lines in the file."""
@@ -533,13 +535,16 @@ class DatabricksIO(ABC, IO):
533
535
 
534
536
  return size
535
537
 
538
+ def _need_flush(self):
539
+ return self._write_flag and self._buffer is not None
540
+
536
541
  def flush(self):
537
542
  """Flush buffered data to the remote path.
538
543
 
539
544
  Returns:
540
545
  None.
541
546
  """
542
- if self._write_flag and self._buffer is not None:
547
+ if self._need_flush():
543
548
  self.write_all_bytes(data=self._buffer.getvalue())
544
549
  self._write_flag = False
545
550
 
@@ -645,15 +645,16 @@ class Workspace:
645
645
 
646
646
  def clusters(
647
647
  self,
648
+ workspace: Optional["Workspace"] = None,
648
649
  cluster_id: Optional[str] = None,
649
650
  cluster_name: Optional[str] = None,
650
651
  ) -> "Cluster":
651
652
  """Return a Cluster helper bound to this workspace.
652
653
 
653
654
  Args:
655
+ workspace: Optional workspace override.
654
656
  cluster_id: Optional cluster id.
655
657
  cluster_name: Optional cluster name.
656
- **kwargs: Additional Cluster parameters.
657
658
 
658
659
  Returns:
659
660
  A Cluster instance.
@@ -661,11 +662,30 @@ class Workspace:
661
662
  from ..compute.cluster import Cluster
662
663
 
663
664
  return Cluster(
664
- workspace=self,
665
+ workspace=self if workspace is None else workspace,
665
666
  cluster_id=cluster_id,
666
667
  cluster_name=cluster_name,
667
668
  )
668
669
 
670
+ def loki(
671
+ self,
672
+ workspace: Optional["Workspace"] = None,
673
+ ):
674
+ """
675
+ Return a Cluster helper bound to this workspace.
676
+
677
+ Args:
678
+ workspace: Optional workspace override.
679
+
680
+ Returns:
681
+ A Loki AI instance.
682
+ """
683
+ from ..ai.loki import Loki
684
+
685
+ return Loki(
686
+ workspace=self if workspace is None else workspace,
687
+ )
688
+
669
689
  # ---------------------------------------------------------------------------
670
690
  # Workspace-bound base class
671
691
  # ---------------------------------------------------------------------------
@@ -717,13 +737,13 @@ class WorkspaceService(ABC):
717
737
  """
718
738
  return self.workspace.is_in_databricks_environment()
719
739
 
720
- def connect(self):
740
+ def connect(self, clone: bool = False):
721
741
  """Connect the underlying workspace.
722
742
 
723
743
  Returns:
724
744
  The current WorkspaceService instance.
725
745
  """
726
- self.workspace = self.workspace.connect()
746
+ self.workspace = self.workspace.connect(clone=clone)
727
747
  return self
728
748
 
729
749
  def dbfs_path(
@@ -4,5 +4,3 @@ from .retry import retry
4
4
  from .parallel import parallelize
5
5
  from .python_env import PythonEnv
6
6
  from .callable_serde import CallableSerde
7
-
8
- __all__ = ["retry", "parallelize", "PythonEnv", "CallableSerde"]
@@ -20,7 +20,7 @@ from dataclasses import dataclass, field
20
20
  from pathlib import Path
21
21
  from typing import Any, Iterable, Iterator, Mapping, MutableMapping, Optional, Union, List, Tuple
22
22
 
23
- from yggdrasil.pyutils.modules import PipIndexSettings
23
+ from .modules import PipIndexSettings
24
24
 
25
25
  log = logging.getLogger(__name__)
26
26
 
yggdrasil/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.53"
1
+ __version__ = "0.1.55"