kiarina-llm 1.1.1__py3-none-any.whl → 1.3.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.
@@ -0,0 +1,37 @@
1
+ import logging
2
+ from importlib import import_module
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ._models.content_limits import ContentLimits
7
+ from ._models.content_metrics import ContentMetrics
8
+ from ._models.content_scale import ContentScale
9
+ from ._operations.calculate_overflow import calculate_overflow
10
+
11
+ __all__ = [
12
+ # .models
13
+ "ContentLimits",
14
+ "ContentMetrics",
15
+ "ContentScale",
16
+ # .operations
17
+ "calculate_overflow",
18
+ ]
19
+
20
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
21
+
22
+
23
+ def __getattr__(name: str) -> object:
24
+ if name not in __all__:
25
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
26
+
27
+ module_map = {
28
+ # .models
29
+ "ContentLimits": "._models.content_limits",
30
+ "ContentMetrics": "._models.content_metrics",
31
+ "ContentScale": "._models.content_scale",
32
+ # .operations
33
+ "calculate_overflow": "._operations.calculate_overflow",
34
+ }
35
+
36
+ globals()[name] = getattr(import_module(module_map[name], __name__), name)
37
+ return globals()[name]
File without changes
@@ -0,0 +1,69 @@
1
+ from pydantic import BaseModel
2
+
3
+
4
+ class ContentLimits(BaseModel):
5
+ """
6
+ Content Limits
7
+ """
8
+
9
+ token_input_limit: int = -1
10
+ """Maximum token count input limit (-1 indicates unlimited)"""
11
+
12
+ file_size_limit: int = -1
13
+ """Maximum total file size limit (in bytes; -1 indicates unlimited)"""
14
+
15
+ # --------------------------------------------------
16
+ # Text Files
17
+ # --------------------------------------------------
18
+
19
+ text_file_limit: int = -1
20
+ """Maximum number of text files (-1 indicates unlimited)"""
21
+
22
+ # --------------------------------------------------
23
+ # Image Files
24
+ # --------------------------------------------------
25
+
26
+ image_input_enabled: bool = True
27
+ """Whether image input is enabled"""
28
+
29
+ image_file_limit: int = -1
30
+ """Maximum number of image files (-1 indicates unlimited)"""
31
+
32
+ # --------------------------------------------------
33
+ # Audio Files
34
+ # --------------------------------------------------
35
+
36
+ audio_input_enabled: bool = True
37
+ """Whether audio input is enabled"""
38
+
39
+ audio_duration_limit: float = 60 * 60 * 9.5
40
+ """Maximum audio duration limit (in seconds; Gemini's limit is 9.5h)"""
41
+
42
+ audio_file_limit: int = -1
43
+ """Maximum number of audio files (-1 indicates unlimited)"""
44
+
45
+ # --------------------------------------------------
46
+ # Video Files
47
+ # --------------------------------------------------
48
+
49
+ video_input_enabled: bool = True
50
+ """Whether video input is enabled"""
51
+
52
+ video_duration_limit: float = 60 * 60 * 1.0
53
+ """Maximum video duration limit (in seconds; Gemini's limit is 1h)"""
54
+
55
+ video_file_limit: int = -1
56
+ """Maximum number of video files (-1 indicates unlimited)"""
57
+
58
+ # --------------------------------------------------
59
+ # PDF Files
60
+ # --------------------------------------------------
61
+
62
+ pdf_input_enabled: bool = True
63
+ """Whether PDF input is enabled"""
64
+
65
+ pdf_page_limit: int = 100
66
+ """Maximum number of PDF pages (default is 100)"""
67
+
68
+ pdf_file_limit: int = -1
69
+ """Maximum number of PDF files (-1 indicates unlimited)"""
@@ -0,0 +1,115 @@
1
+ from typing import Self
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class ContentMetrics(BaseModel):
7
+ """
8
+ Content Metrics
9
+ """
10
+
11
+ # --------------------------------------------------
12
+ # Properties
13
+ # --------------------------------------------------
14
+
15
+ token_count: int = 0
16
+ """Token count"""
17
+
18
+ file_size: int = 0
19
+ """Total file size (bytes)"""
20
+
21
+ text_file_count: int = 0
22
+ """Number of text files"""
23
+
24
+ image_file_count: int = 0
25
+ """Number of image files"""
26
+
27
+ audio_duration: float = 0.0
28
+ """Audio duration (seconds)"""
29
+
30
+ audio_file_count: int = 0
31
+ """Number of audio files"""
32
+
33
+ video_duration: float = 0.0
34
+ """Video duration (seconds)"""
35
+
36
+ video_file_count: int = 0
37
+ """Number of video files"""
38
+
39
+ pdf_page_count: int = 0
40
+ """Number of PDF pages"""
41
+
42
+ pdf_file_count: int = 0
43
+ """Number of PDF files"""
44
+
45
+ # --------------------------------------------------
46
+ # Properties for Meta Information
47
+ # --------------------------------------------------
48
+
49
+ text_token_count: int = 0
50
+ """Text token count"""
51
+
52
+ image_token_count: int = 0
53
+ """Image token count"""
54
+
55
+ audio_token_count: int = 0
56
+ """Audio token count"""
57
+
58
+ video_token_count: int = 0
59
+ """Video token count"""
60
+
61
+ pdf_token_count: int = 0
62
+ """PDF token count"""
63
+
64
+ # --------------------------------------------------
65
+ # Methods
66
+ # --------------------------------------------------
67
+
68
+ def add_text_token_count(self, count: int) -> None:
69
+ self.text_token_count += count
70
+ self.token_count += count
71
+
72
+ def add_image_token_count(self, count: int) -> None:
73
+ self.image_token_count += count
74
+ self.token_count += count
75
+
76
+ def add_audio_token_count(self, count: int) -> None:
77
+ self.audio_token_count += count
78
+ self.token_count += count
79
+
80
+ def add_video_token_count(self, count: int) -> None:
81
+ self.video_token_count += count
82
+ self.token_count += count
83
+
84
+ def add_pdf_token_count(self, count: int) -> None:
85
+ self.pdf_token_count += count
86
+ self.token_count += count
87
+
88
+ # --------------------------------------------------
89
+ # Operators
90
+ # --------------------------------------------------
91
+
92
+ def __add__(self, other: Self) -> Self:
93
+ """
94
+ Add metrics
95
+ """
96
+ result = self.model_copy()
97
+
98
+ result.token_count += other.token_count
99
+ result.file_size += other.file_size
100
+ result.text_file_count += other.text_file_count
101
+ result.image_file_count += other.image_file_count
102
+ result.audio_duration += other.audio_duration
103
+ result.audio_file_count += other.audio_file_count
104
+ result.video_duration += other.video_duration
105
+ result.video_file_count += other.video_file_count
106
+ result.pdf_page_count += other.pdf_page_count
107
+ result.pdf_file_count += other.pdf_file_count
108
+
109
+ result.text_token_count += other.text_token_count
110
+ result.image_token_count += other.image_token_count
111
+ result.audio_token_count += other.audio_token_count
112
+ result.video_token_count += other.video_token_count
113
+ result.pdf_token_count += other.pdf_token_count
114
+
115
+ return result
@@ -0,0 +1,113 @@
1
+ from typing import Self
2
+
3
+ from pydantic import BaseModel, computed_field
4
+
5
+ from .._operations.calculate_overflow import calculate_overflow
6
+ from .content_limits import ContentLimits
7
+ from .content_metrics import ContentMetrics
8
+
9
+
10
+ class ContentScale(BaseModel):
11
+ """
12
+ Content Scale
13
+ """
14
+
15
+ limits: ContentLimits
16
+ """Limits"""
17
+
18
+ metrics: ContentMetrics
19
+ """Metrics (usage)"""
20
+
21
+ overflow: ContentMetrics
22
+ """Overflow amount (in metrics format)"""
23
+
24
+ # --------------------------------------------------
25
+ # Properties
26
+ # --------------------------------------------------
27
+
28
+ @computed_field # type: ignore
29
+ @property
30
+ def token_ratio(self) -> float:
31
+ """
32
+ Token usage ratio
33
+ """
34
+ if self.limits.token_input_limit == 0:
35
+ return 0.0
36
+
37
+ return self.metrics.token_count / self.limits.token_input_limit
38
+
39
+ @computed_field # type: ignore
40
+ @property
41
+ def has_overflow(self) -> bool:
42
+ """
43
+ Whether overflow has occurred
44
+ """
45
+ return self.is_overflow()
46
+
47
+ @computed_field # type: ignore
48
+ @property
49
+ def error_message(self) -> str:
50
+ """
51
+ Error message
52
+ """
53
+ return self.get_error_message()
54
+
55
+ # --------------------------------------------------
56
+ # Methods
57
+ # --------------------------------------------------
58
+
59
+ def is_overflow(self) -> bool:
60
+ """
61
+ Whether the message has overflowed
62
+ """
63
+ return (
64
+ self.overflow.token_count > 0
65
+ or self.overflow.file_size > 0
66
+ or self.overflow.text_file_count > 0
67
+ or self.overflow.image_file_count > 0
68
+ or self.overflow.audio_duration > 0
69
+ or self.overflow.audio_file_count > 0
70
+ or self.overflow.video_duration > 0
71
+ or self.overflow.video_file_count > 0
72
+ or self.overflow.pdf_page_count > 0
73
+ or self.overflow.pdf_file_count > 0
74
+ )
75
+
76
+ def get_error_message(self) -> str:
77
+ """
78
+ Get error message
79
+ """
80
+ if self.overflow.file_size > 0:
81
+ return f"Total file size exceeds limit: {self.metrics.file_size} / {self.limits.file_size_limit} bytes"
82
+ elif self.overflow.pdf_file_count > 0:
83
+ return f"Number of PDF files exceeds limit: {self.metrics.pdf_file_count} / {self.limits.pdf_file_limit}"
84
+ elif self.overflow.pdf_page_count > 0:
85
+ return f"PDF page count exceeds limit: {self.metrics.pdf_page_count} / {self.limits.pdf_page_limit}"
86
+ elif self.overflow.video_file_count > 0:
87
+ return f"Number of video files exceeds limit: {self.metrics.video_file_count} / {self.limits.video_file_limit}"
88
+ elif self.overflow.video_duration > 0:
89
+ return f"Video duration exceeds limit: {self.metrics.video_duration}s / {self.limits.video_duration_limit}s"
90
+ elif self.overflow.audio_file_count > 0:
91
+ return f"Number of audio files exceeds limit: {self.metrics.audio_file_count} / {self.limits.audio_file_limit}"
92
+ elif self.overflow.audio_duration > 0:
93
+ return f"Audio duration exceeds limit: {self.metrics.audio_duration}s / {self.limits.audio_duration_limit}s"
94
+ elif self.overflow.image_file_count > 0:
95
+ return f"Number of image files exceeds limit: {self.metrics.image_file_count} / {self.limits.image_file_limit}"
96
+ elif self.overflow.text_file_count > 0:
97
+ return f"Number of text files exceeds limit: {self.metrics.text_file_count} / {self.limits.text_file_limit}"
98
+ elif self.overflow.token_count > 0:
99
+ return f"Token count exceeds limit: {self.metrics.token_count} / {self.limits.token_input_limit}"
100
+ else:
101
+ return "Message is valid"
102
+
103
+ # --------------------------------------------------
104
+ # Class Methods
105
+ # --------------------------------------------------
106
+
107
+ @classmethod
108
+ def create(cls, *, limits: ContentLimits, metrics: ContentMetrics) -> Self:
109
+ """
110
+ Create scale information from metrics and limits
111
+ """
112
+ overflow = calculate_overflow(limits=limits, metrics=metrics)
113
+ return cls(limits=limits, metrics=metrics, overflow=overflow)
File without changes
@@ -0,0 +1,62 @@
1
+ from .._models.content_limits import ContentLimits
2
+ from .._models.content_metrics import ContentMetrics
3
+
4
+
5
+ def calculate_overflow(
6
+ *, limits: ContentLimits, metrics: ContentMetrics
7
+ ) -> ContentMetrics:
8
+ """
9
+ Calculate overflow amount from metrics and limits
10
+ """
11
+ return ContentMetrics(
12
+ token_count=(
13
+ max(0, metrics.token_count - limits.token_input_limit)
14
+ if limits.token_input_limit != -1
15
+ else 0
16
+ ),
17
+ file_size=(
18
+ max(0, metrics.file_size - limits.file_size_limit)
19
+ if limits.file_size_limit != -1
20
+ else 0
21
+ ),
22
+ text_file_count=(
23
+ max(0, metrics.text_file_count - limits.text_file_limit)
24
+ if limits.text_file_limit != -1
25
+ else 0
26
+ ),
27
+ image_file_count=(
28
+ max(0, metrics.image_file_count - limits.image_file_limit)
29
+ if (limits.image_input_enabled and limits.image_file_limit != -1)
30
+ else 0
31
+ ),
32
+ audio_duration=(
33
+ max(0.0, metrics.audio_duration - limits.audio_duration_limit)
34
+ if limits.audio_input_enabled
35
+ else 0.0
36
+ ),
37
+ audio_file_count=(
38
+ max(0, metrics.audio_file_count - limits.audio_file_limit)
39
+ if (limits.audio_input_enabled and limits.audio_file_limit != -1)
40
+ else 0
41
+ ),
42
+ video_duration=(
43
+ max(0.0, metrics.video_duration - limits.video_duration_limit)
44
+ if limits.video_input_enabled
45
+ else 0.0
46
+ ),
47
+ video_file_count=(
48
+ max(0, metrics.video_file_count - limits.video_file_limit)
49
+ if (limits.video_input_enabled and limits.video_file_limit != -1)
50
+ else 0
51
+ ),
52
+ pdf_page_count=(
53
+ max(0, metrics.pdf_page_count - limits.pdf_page_limit)
54
+ if limits.pdf_input_enabled
55
+ else 0
56
+ ),
57
+ pdf_file_count=(
58
+ max(0, metrics.pdf_file_count - limits.pdf_file_limit)
59
+ if (limits.pdf_input_enabled and limits.pdf_file_limit != -1)
60
+ else 0
61
+ ),
62
+ )
@@ -1,9 +1,30 @@
1
- from ._model import RunContext
2
- from ._registry import create_run_context
3
- from .settings import settings_manager
1
+ import logging
2
+ from importlib import import_module
3
+ from typing import TYPE_CHECKING
4
+
5
+ if TYPE_CHECKING:
6
+ from ._model import RunContext
7
+ from ._registry import create_run_context
8
+ from .settings import settings_manager
4
9
 
5
10
  __all__ = [
6
11
  "create_run_context",
7
12
  "RunContext",
8
13
  "settings_manager",
9
14
  ]
15
+
16
+ logging.getLogger(__name__).addHandler(logging.NullHandler())
17
+
18
+
19
+ def __getattr__(name: str) -> object:
20
+ if name not in __all__:
21
+ raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
22
+
23
+ module_map = {
24
+ "create_run_context": "._registry",
25
+ "RunContext": "._model",
26
+ "settings_manager": ".settings",
27
+ }
28
+
29
+ globals()[name] = getattr(import_module(module_map[name], __name__), name)
30
+ return globals()[name]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kiarina-llm
3
- Version: 1.1.1
3
+ Version: 1.3.0
4
4
  Summary: LLM utilities and context management for kiarina namespace
5
5
  Project-URL: Homepage, https://github.com/kiarina/kiarina-python
6
6
  Project-URL: Repository, https://github.com/kiarina/kiarina-python
@@ -9,11 +9,10 @@ Project-URL: Changelog, https://github.com/kiarina/kiarina-python/blob/main/pack
9
9
  Project-URL: Documentation, https://github.com/kiarina/kiarina-python/tree/main/packages/kiarina-llm#readme
10
10
  Author-email: kiarina <kiarinadawa@gmail.com>
11
11
  Maintainer-email: kiarina <kiarinadawa@gmail.com>
12
- License: MIT
12
+ License-Expression: MIT
13
13
  Keywords: agent,ai,context,llm,pydantic,runner,settings
14
14
  Classifier: Development Status :: 4 - Beta
15
15
  Classifier: Intended Audience :: Developers
16
- Classifier: License :: OSI Approved :: MIT License
17
16
  Classifier: Operating System :: OS Independent
18
17
  Classifier: Programming Language :: Python :: 3
19
18
  Classifier: Programming Language :: Python :: 3.12
@@ -0,0 +1,19 @@
1
+ kiarina/llm/__init__.py,sha256=facCPVBHBVFzUYQvR0TNIx53S0CVVSJWIMoY59aofD8,77
2
+ kiarina/llm/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ kiarina/llm/content/__init__.py,sha256=T5He0dd0sp7hJg0QvVBL5XfLYS8OvIBNZ9lgXOPbJV0,1072
4
+ kiarina/llm/content/_models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ kiarina/llm/content/_models/content_limits.py,sha256=_F7T4ptYDs7P8QPjIw9IC7ylRaDMo9erHEwexa82Gbc,2093
6
+ kiarina/llm/content/_models/content_metrics.py,sha256=DkxEExiievi0saqwQRkP8DDguTep12gjPYXp4TICgZM,3211
7
+ kiarina/llm/content/_models/content_scale.py,sha256=YLYLHLpBcGngSvuLshqDRhedRcWZ-DQnOb23xJnlfzQ,4224
8
+ kiarina/llm/content/_operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ kiarina/llm/content/_operations/calculate_overflow.py,sha256=2Ly2XQXLtxMKXgk40tcKKpGS6aGuqDU4EYSMMoRk7QA,2123
10
+ kiarina/llm/run_context/__init__.py,sha256=QD5_5Jdp2kWDjJeRvyTHgIz-pEU_UOiHqxcpPrSPOaE,771
11
+ kiarina/llm/run_context/_model.py,sha256=P3TjszwVhvlLphZ6Jl7B3YQXNnFCZT9JA9AMoFsuOBQ,1161
12
+ kiarina/llm/run_context/_registry.py,sha256=ycbzMerfrlT6oU5m8l4q6y08wr-sjDoxMUgzzdytL6U,1247
13
+ kiarina/llm/run_context/settings.py,sha256=TOmJ2XytweOqxXTx12GE3IWD-Pni8FAPNvHO4nMywoU,1894
14
+ kiarina/llm/run_context/_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
+ kiarina/llm/run_context/_types/fs_name.py,sha256=U1_DWN0Tf164gPIcxwiKLBe6dqbJ7vCm0aYbPdgRA_U,1161
16
+ kiarina/llm/run_context/_types/id_str.py,sha256=BlfQXxoUw0P9X3q4wpfPjrvyDoq3yvqhddaZSyVOOf0,308
17
+ kiarina_llm-1.3.0.dist-info/METADATA,sha256=VAHC0SE7TtP6c-5IDQAwkDawPf0E8_kCAaUMQ8LgwiM,8802
18
+ kiarina_llm-1.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
+ kiarina_llm-1.3.0.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- kiarina/llm/__init__.py,sha256=facCPVBHBVFzUYQvR0TNIx53S0CVVSJWIMoY59aofD8,77
2
- kiarina/llm/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
- kiarina/llm/run_context/__init__.py,sha256=LGqGLnBOJrHCls1DRrmwT1zDvJ36gmEpWEMY3qE0vCg,195
4
- kiarina/llm/run_context/_model.py,sha256=P3TjszwVhvlLphZ6Jl7B3YQXNnFCZT9JA9AMoFsuOBQ,1161
5
- kiarina/llm/run_context/_registry.py,sha256=ycbzMerfrlT6oU5m8l4q6y08wr-sjDoxMUgzzdytL6U,1247
6
- kiarina/llm/run_context/settings.py,sha256=TOmJ2XytweOqxXTx12GE3IWD-Pni8FAPNvHO4nMywoU,1894
7
- kiarina/llm/run_context/_types/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- kiarina/llm/run_context/_types/fs_name.py,sha256=U1_DWN0Tf164gPIcxwiKLBe6dqbJ7vCm0aYbPdgRA_U,1161
9
- kiarina/llm/run_context/_types/id_str.py,sha256=BlfQXxoUw0P9X3q4wpfPjrvyDoq3yvqhddaZSyVOOf0,308
10
- kiarina_llm-1.1.1.dist-info/METADATA,sha256=mS85nu8Hf9zbcA_xMAASsDIQxveUKfRIfN_UCbXA9M0,8842
11
- kiarina_llm-1.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
12
- kiarina_llm-1.1.1.dist-info/RECORD,,