fmtr.tools 1.3.7__tar.gz → 1.3.9__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 fmtr.tools might be problematic. Click here for more details.
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/PKG-INFO +44 -42
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/caching_tools.py +101 -3
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/constants.py +4 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/dns_tools/dm.py +41 -5
- fmtr_tools-1.3.9/fmtr/tools/dns_tools/proxy.py +57 -0
- fmtr_tools-1.3.9/fmtr/tools/dns_tools/server.py +94 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/logging_tools.py +1 -1
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/path_tools/path_tools.py +10 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/pattern_tools.py +5 -4
- fmtr_tools-1.3.9/fmtr/tools/version +1 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr.tools.egg-info/PKG-INFO +44 -42
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr.tools.egg-info/requires.txt +43 -41
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/setup.py +1 -1
- fmtr_tools-1.3.7/fmtr/tools/dns_tools/proxy.py +0 -67
- fmtr_tools-1.3.7/fmtr/tools/dns_tools/server.py +0 -39
- fmtr_tools-1.3.7/fmtr/tools/version +0 -1
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/LICENSE +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/README.md +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/__init__.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/ai_tools/__init__.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/ai_tools/agentic_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/ai_tools/inference_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/api_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/async_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/augmentation_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/data_modelling_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/dataclass_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/datatype_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/debugging_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/dns_tools/__init__.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/dns_tools/client.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/docker_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/entrypoints/__init__.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/entrypoints/cache_hfh.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/entrypoints/ep_test.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/entrypoints/install_yamlscript.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/entrypoints/remote_debug_test.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/entrypoints/shell_debug.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/environment_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/function_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/google_api_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/hash_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/hfh_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/html_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/http_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/import_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/inspection_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/interface_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/iterator_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/json_fix_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/json_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/merging_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/metric_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/name_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/netrc_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/openai_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/packaging_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/parallel_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/path_tools/__init__.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/path_tools/app_path_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/path_tools/type_path_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/pdf_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/platform_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/process_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/profiling_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/random_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/semantic_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/settings_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/setup_tools/__init__.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/setup_tools/setup_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/spaces_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/string_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/tabular_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/tests/__init__.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/tests/conftest.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/tests/helpers.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/tests/test_datatype.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/tests/test_environment.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/tests/test_json.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/tests/test_path.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/tests/test_yaml.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/tokenization_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/unicode_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/version_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr/tools/yaml_tools.py +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr.tools.egg-info/SOURCES.txt +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr.tools.egg-info/dependency_links.txt +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr.tools.egg-info/entry_points.txt +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/fmtr.tools.egg-info/top_level.txt +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/pyproject.toml +0 -0
- {fmtr_tools-1.3.7 → fmtr_tools-1.3.9}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmtr.tools
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.9
|
|
4
4
|
Summary: Collection of high-level tools to simplify everyday development tasks, with a focus on AI/ML
|
|
5
5
|
Home-page: https://github.com/fmtr/fmtr.tools
|
|
6
6
|
Author: Frontmatter
|
|
@@ -97,6 +97,7 @@ Requires-Dist: google-auth-httplib2; extra == "google-api"
|
|
|
97
97
|
Requires-Dist: google-api-python-client; extra == "google-api"
|
|
98
98
|
Provides-Extra: caching
|
|
99
99
|
Requires-Dist: diskcache; extra == "caching"
|
|
100
|
+
Requires-Dist: cachetools; extra == "caching"
|
|
100
101
|
Provides-Extra: pdf
|
|
101
102
|
Requires-Dist: pymupdf; extra == "pdf"
|
|
102
103
|
Requires-Dist: pydantic; extra == "pdf"
|
|
@@ -130,60 +131,61 @@ Requires-Dist: logfire[httpx]; extra == "http"
|
|
|
130
131
|
Provides-Extra: setup
|
|
131
132
|
Requires-Dist: setuptools; extra == "setup"
|
|
132
133
|
Provides-Extra: all
|
|
133
|
-
Requires-Dist: dnspython[doh]; extra == "all"
|
|
134
|
-
Requires-Dist: pymupdf4llm; extra == "all"
|
|
135
|
-
Requires-Dist: regex; extra == "all"
|
|
136
134
|
Requires-Dist: uvicorn[standard]; extra == "all"
|
|
137
|
-
Requires-Dist: tokenizers; extra == "all"
|
|
138
|
-
Requires-Dist: filetype; extra == "all"
|
|
139
|
-
Requires-Dist: flet[all]; extra == "all"
|
|
140
|
-
Requires-Dist: Unidecode; extra == "all"
|
|
141
|
-
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
142
|
-
Requires-Dist: logfire[httpx]; extra == "all"
|
|
143
|
-
Requires-Dist: bokeh; extra == "all"
|
|
144
|
-
Requires-Dist: fastapi; extra == "all"
|
|
145
|
-
Requires-Dist: pytest-cov; extra == "all"
|
|
146
135
|
Requires-Dist: json_repair; extra == "all"
|
|
147
|
-
Requires-Dist: semver; extra == "all"
|
|
148
|
-
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
149
|
-
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
150
|
-
Requires-Dist: html2text; extra == "all"
|
|
151
|
-
Requires-Dist: sre_yield; extra == "all"
|
|
152
|
-
Requires-Dist: pymupdf; extra == "all"
|
|
153
|
-
Requires-Dist: flet-video; extra == "all"
|
|
154
|
-
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
155
|
-
Requires-Dist: pydantic-settings; extra == "all"
|
|
156
|
-
Requires-Dist: ollama; extra == "all"
|
|
157
|
-
Requires-Dist: distributed; extra == "all"
|
|
158
136
|
Requires-Dist: httpx; extra == "all"
|
|
159
|
-
Requires-Dist:
|
|
137
|
+
Requires-Dist: openai; extra == "all"
|
|
160
138
|
Requires-Dist: openpyxl; extra == "all"
|
|
161
|
-
Requires-Dist:
|
|
139
|
+
Requires-Dist: flet[all]; extra == "all"
|
|
140
|
+
Requires-Dist: regex; extra == "all"
|
|
162
141
|
Requires-Dist: tabulate; extra == "all"
|
|
163
142
|
Requires-Dist: tinynetrc; extra == "all"
|
|
164
|
-
Requires-Dist:
|
|
165
|
-
Requires-Dist:
|
|
166
|
-
Requires-Dist: pyyaml; extra == "all"
|
|
167
|
-
Requires-Dist: diskcache; extra == "all"
|
|
168
|
-
Requires-Dist: faker; extra == "all"
|
|
169
|
-
Requires-Dist: docker; extra == "all"
|
|
143
|
+
Requires-Dist: filetype; extra == "all"
|
|
144
|
+
Requires-Dist: google-api-python-client; extra == "all"
|
|
170
145
|
Requires-Dist: yamlscript; extra == "all"
|
|
146
|
+
Requires-Dist: tokenizers; extra == "all"
|
|
147
|
+
Requires-Dist: deepmerge; extra == "all"
|
|
148
|
+
Requires-Dist: sre_yield; extra == "all"
|
|
149
|
+
Requires-Dist: diskcache; extra == "all"
|
|
150
|
+
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
151
|
+
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
152
|
+
Requires-Dist: Unidecode; extra == "all"
|
|
153
|
+
Requires-Dist: sentence_transformers; extra == "all"
|
|
154
|
+
Requires-Dist: logfire; extra == "all"
|
|
155
|
+
Requires-Dist: logfire[fastapi]; extra == "all"
|
|
171
156
|
Requires-Dist: pydantic; extra == "all"
|
|
172
|
-
Requires-Dist:
|
|
157
|
+
Requires-Dist: semver; extra == "all"
|
|
158
|
+
Requires-Dist: dnspython[doh]; extra == "all"
|
|
173
159
|
Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
|
|
174
|
-
Requires-Dist:
|
|
175
|
-
Requires-Dist:
|
|
176
|
-
Requires-Dist:
|
|
177
|
-
Requires-Dist:
|
|
160
|
+
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
161
|
+
Requires-Dist: pymupdf4llm; extra == "all"
|
|
162
|
+
Requires-Dist: html2text; extra == "all"
|
|
163
|
+
Requires-Dist: google-auth; extra == "all"
|
|
164
|
+
Requires-Dist: pydantic-settings; extra == "all"
|
|
178
165
|
Requires-Dist: huggingface_hub; extra == "all"
|
|
179
|
-
Requires-Dist:
|
|
166
|
+
Requires-Dist: pyyaml; extra == "all"
|
|
167
|
+
Requires-Dist: torchvision; extra == "all"
|
|
168
|
+
Requires-Dist: pymupdf; extra == "all"
|
|
169
|
+
Requires-Dist: fastapi; extra == "all"
|
|
170
|
+
Requires-Dist: logfire[httpx]; extra == "all"
|
|
171
|
+
Requires-Dist: contexttimer; extra == "all"
|
|
180
172
|
Requires-Dist: httpx_retries; extra == "all"
|
|
173
|
+
Requires-Dist: dask[bag]; extra == "all"
|
|
174
|
+
Requires-Dist: peft; extra == "all"
|
|
175
|
+
Requires-Dist: distributed; extra == "all"
|
|
176
|
+
Requires-Dist: docker; extra == "all"
|
|
177
|
+
Requires-Dist: faker; extra == "all"
|
|
178
|
+
Requires-Dist: cachetools; extra == "all"
|
|
179
|
+
Requires-Dist: torchaudio; extra == "all"
|
|
181
180
|
Requires-Dist: setuptools; extra == "all"
|
|
182
|
-
Requires-Dist:
|
|
183
|
-
Requires-Dist:
|
|
181
|
+
Requires-Dist: pytest-cov; extra == "all"
|
|
182
|
+
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
183
|
+
Requires-Dist: flet-video; extra == "all"
|
|
184
|
+
Requires-Dist: flet-webview; extra == "all"
|
|
185
|
+
Requires-Dist: appdirs; extra == "all"
|
|
184
186
|
Requires-Dist: pandas; extra == "all"
|
|
185
|
-
Requires-Dist:
|
|
186
|
-
Requires-Dist:
|
|
187
|
+
Requires-Dist: bokeh; extra == "all"
|
|
188
|
+
Requires-Dist: ollama; extra == "all"
|
|
187
189
|
Dynamic: author
|
|
188
190
|
Dynamic: author-email
|
|
189
191
|
Dynamic: description
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import cachetools
|
|
2
|
+
from datetime import timedelta, datetime
|
|
1
3
|
from diskcache import Cache
|
|
2
4
|
|
|
3
|
-
from fmtr.tools import
|
|
5
|
+
from fmtr.tools.constants import Constants
|
|
6
|
+
from fmtr.tools.logging_tools import logger
|
|
7
|
+
from fmtr.tools.path_tools.path_tools import Path
|
|
4
8
|
|
|
5
9
|
|
|
6
10
|
class Dump(dict):
|
|
@@ -31,9 +35,9 @@ class Disk(Cache):
|
|
|
31
35
|
if not path.parent.exists():
|
|
32
36
|
raise FileNotFoundError(f"Directory {path.parent=} does not exist")
|
|
33
37
|
if path and not path.exists():
|
|
34
|
-
logger.warning(f'Cache does not exist. Will be created.
|
|
38
|
+
logger.warning(f'Cache does not exist. Will be created. {str(path)=}...')
|
|
35
39
|
|
|
36
|
-
logger.info(f'Initializing Disk Cache
|
|
40
|
+
logger.info(f'Initializing Disk Cache {str(path)=}...')
|
|
37
41
|
|
|
38
42
|
super().__init__(directory=str(path / self.ROOT_KEY), **settings)
|
|
39
43
|
|
|
@@ -95,7 +99,101 @@ class Disk(Cache):
|
|
|
95
99
|
return self.dump()
|
|
96
100
|
|
|
97
101
|
|
|
102
|
+
class TLRU(cachetools.TLRUCache):
|
|
103
|
+
"""
|
|
104
|
+
|
|
105
|
+
Subclass to include logging and simplify global TTU
|
|
106
|
+
|
|
107
|
+
"""
|
|
108
|
+
MASK_MAPPING = '{key} ' + Constants.ARROW + ' {value}'
|
|
109
|
+
|
|
110
|
+
def __init__(self, maxsize=1_024, timer=datetime.now, getsizeof=None, ttu_static=None, desc=None):
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
Add overridable TTU method
|
|
114
|
+
|
|
115
|
+
"""
|
|
116
|
+
super().__init__(maxsize=maxsize, ttu=self.get_ttu, timer=timer, getsizeof=getsizeof)
|
|
117
|
+
self.ttu_static = ttu_static
|
|
118
|
+
self.desc = desc
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def cache_desc(self):
|
|
122
|
+
"""
|
|
123
|
+
|
|
124
|
+
Friendly description of cache
|
|
125
|
+
|
|
126
|
+
"""
|
|
127
|
+
desc = self.desc or self.__class__.__name__
|
|
128
|
+
return desc
|
|
129
|
+
|
|
130
|
+
def get_ttu(self, _key, value, now) -> float | timedelta:
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
Default implementation just adds on the static TTU
|
|
134
|
+
|
|
135
|
+
"""
|
|
136
|
+
return now + self.ttu_static
|
|
137
|
+
|
|
138
|
+
def expire(self, time=None):
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
Log expiry
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
items = super().expire(time)
|
|
145
|
+
if not items:
|
|
146
|
+
return items
|
|
147
|
+
|
|
148
|
+
with logger.span(f'{self.desc} cache expiry {len(items)=}...'):
|
|
149
|
+
for key, value in items:
|
|
150
|
+
logger.debug(self.MASK_MAPPING.format(key=key, value=value))
|
|
151
|
+
|
|
152
|
+
return items
|
|
153
|
+
|
|
154
|
+
def popitem(self):
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
Log eviction
|
|
158
|
+
|
|
159
|
+
"""
|
|
160
|
+
key, value = super().popitem()
|
|
161
|
+
logger.debug(f'{self.desc} cache eviction: {self.MASK_MAPPING.format(key=key, value=value)}')
|
|
162
|
+
return key, value
|
|
163
|
+
|
|
164
|
+
def dump(self):
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
Dump contents
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
data = Dump(self.items())
|
|
171
|
+
return data
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def data(self):
|
|
175
|
+
"""
|
|
176
|
+
|
|
177
|
+
Dump as property
|
|
178
|
+
|
|
179
|
+
"""
|
|
180
|
+
return self.dump()
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
|
|
98
184
|
if __name__ == '__main__':
|
|
185
|
+
sec10 = timedelta(seconds=10)
|
|
186
|
+
c = TLRU(ttu_static=sec10, maxsize=2, desc='Test Data')
|
|
187
|
+
c['test'] = 'val'
|
|
188
|
+
c['test2'] = 'val2'
|
|
189
|
+
c['test3'] = 'val3'
|
|
190
|
+
c
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
99
197
|
path_tmp_cache = Path.cwd().parent.parent / 'data' / 'cache'
|
|
100
198
|
tc = Disk(path_tmp_cache)
|
|
101
199
|
|
|
@@ -14,6 +14,9 @@ class Constants:
|
|
|
14
14
|
DATETIME_NOW_STR = DATETIME_NOW.strftime(DATETIME_FILENAME_FORMAT)
|
|
15
15
|
SERIALIZATION_INDENT = 4
|
|
16
16
|
|
|
17
|
+
ARROW = '→'
|
|
18
|
+
ARROW_SEP = f' {ARROW} '
|
|
19
|
+
|
|
17
20
|
FMTR_LOG_LEVEL_KEY = 'FMTR_LOG_LEVEL'
|
|
18
21
|
FMTR_OBS_API_KEY_KEY = 'FMTR_OBS_API_KEY'
|
|
19
22
|
FMTR_OBS_HOST = 'obs.sv.fmtr.dev'
|
|
@@ -31,6 +34,7 @@ class Constants:
|
|
|
31
34
|
FILENAME_CONFIG = 'settings.yaml'
|
|
32
35
|
DIR_NAME_REPO = 'repo'
|
|
33
36
|
DIR_NAME_DATA = 'data'
|
|
37
|
+
DIR_NAME_CACHE = 'cache'
|
|
34
38
|
DIR_NAME_ARTIFACT = 'artifact'
|
|
35
39
|
DIR_NAME_SOURCE = 'source'
|
|
36
40
|
FILENAME_VERSION = 'version'
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
from functools import cached_property
|
|
3
|
-
from typing import Self, Optional
|
|
4
|
-
|
|
5
1
|
import dns
|
|
6
2
|
import httpx
|
|
3
|
+
from dataclasses import dataclass
|
|
7
4
|
from dns.message import Message, QueryMessage
|
|
8
5
|
from dns.rrset import RRset
|
|
6
|
+
from functools import cached_property
|
|
7
|
+
from typing import Self, Optional
|
|
8
|
+
|
|
9
|
+
from fmtr.tools.string_tools import join
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
@dataclass
|
|
@@ -25,7 +26,6 @@ class BaseDNSData:
|
|
|
25
26
|
def from_message(cls, message: Message) -> Self:
|
|
26
27
|
return cls(message.to_wire())
|
|
27
28
|
|
|
28
|
-
|
|
29
29
|
@dataclass
|
|
30
30
|
class Response(BaseDNSData):
|
|
31
31
|
"""
|
|
@@ -39,15 +39,41 @@ class Response(BaseDNSData):
|
|
|
39
39
|
|
|
40
40
|
@classmethod
|
|
41
41
|
def from_http(cls, response: httpx.Response) -> Self:
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
Initialise from an HTTP response.
|
|
45
|
+
|
|
46
|
+
"""
|
|
42
47
|
self = cls(response.content, http=response)
|
|
43
48
|
return self
|
|
44
49
|
|
|
45
50
|
@property
|
|
46
51
|
def answer(self) -> Optional[RRset]:
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
Get the latest answer, if one exists.
|
|
55
|
+
|
|
56
|
+
"""
|
|
47
57
|
if not self.message.answer:
|
|
48
58
|
return None
|
|
49
59
|
return self.message.answer[-1]
|
|
50
60
|
|
|
61
|
+
def __str__(self):
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
Put answer and ID text in string representation.
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
answer = self.answer
|
|
68
|
+
|
|
69
|
+
if answer:
|
|
70
|
+
answer = join(answer.to_text().splitlines(), sep=', ')
|
|
71
|
+
|
|
72
|
+
string = join([answer, self.message.flags], sep=', ')
|
|
73
|
+
string = f'{self.__class__.__name__}({string})'
|
|
74
|
+
return string
|
|
75
|
+
|
|
76
|
+
|
|
51
77
|
|
|
52
78
|
@dataclass
|
|
53
79
|
class Request(BaseDNSData):
|
|
@@ -157,3 +183,13 @@ class Exchange:
|
|
|
157
183
|
question_last = self.question_last
|
|
158
184
|
query = dns.message.make_query(qname=question_last.name, rdclass=question_last.rdclass, rdtype=question_last.rdtype)
|
|
159
185
|
return query
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def key(self):
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
Hashable key for caching
|
|
192
|
+
|
|
193
|
+
"""
|
|
194
|
+
data = tuple(self.request.question.to_text().split())
|
|
195
|
+
return data
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from fmtr.tools.dns_tools import server, client
|
|
4
|
+
from fmtr.tools.dns_tools.dm import Exchange
|
|
5
|
+
from fmtr.tools.logging_tools import logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(kw_only=True, eq=False)
|
|
9
|
+
class Proxy(server.Plain):
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
Base for a DNS Proxy server (plain server) TODO: Allow subclassing of any server type.
|
|
13
|
+
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
client: client.HTTP
|
|
17
|
+
|
|
18
|
+
def process_question(self, exchange: Exchange):
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
Modify exchange based on initial question.
|
|
22
|
+
|
|
23
|
+
"""
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
def process_upstream(self, exchange: Exchange):
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
Modify exchange after upstream response.
|
|
30
|
+
|
|
31
|
+
"""
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
def resolve(self, exchange: Exchange) -> Exchange:
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
Resolve a request, processing each stage, initial question, upstream response etc.
|
|
38
|
+
Subclasses can override the relevant processing methods to implement custom behaviour.
|
|
39
|
+
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
with logger.span(f'Processing question...'):
|
|
43
|
+
self.process_question(exchange)
|
|
44
|
+
if exchange.response.is_complete:
|
|
45
|
+
return exchange
|
|
46
|
+
|
|
47
|
+
with logger.span(f'Making upstream request...'):
|
|
48
|
+
self.client.resolve(exchange)
|
|
49
|
+
if exchange.response.is_complete:
|
|
50
|
+
return exchange
|
|
51
|
+
|
|
52
|
+
with logger.span(f'Processing upstream response...'):
|
|
53
|
+
self.process_upstream(exchange)
|
|
54
|
+
if exchange.response.is_complete:
|
|
55
|
+
return exchange
|
|
56
|
+
|
|
57
|
+
return exchange
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import socket
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from datetime import timedelta
|
|
4
|
+
from functools import cached_property
|
|
5
|
+
|
|
6
|
+
from fmtr.tools import caching_tools as caching
|
|
7
|
+
from fmtr.tools.dns_tools.dm import Exchange
|
|
8
|
+
from fmtr.tools.logging_tools import logger
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(kw_only=True, eq=False)
|
|
12
|
+
class Plain:
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
Base for starting a plain DNS server
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
host: str
|
|
20
|
+
port: int
|
|
21
|
+
|
|
22
|
+
@cached_property
|
|
23
|
+
def sock(self):
|
|
24
|
+
return socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
25
|
+
|
|
26
|
+
@cached_property
|
|
27
|
+
def cache(self):
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
Overridable cache.
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
cache = caching.TLRU(maxsize=1_024, ttu_static=timedelta(hours=1), desc='DNS Request')
|
|
34
|
+
return cache
|
|
35
|
+
|
|
36
|
+
def start(self):
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
Listen and resolve via overridden resolve method.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
43
|
+
sock.bind((self.host, self.port))
|
|
44
|
+
logger.info(f'Listening on {self.host}:{self.port}')
|
|
45
|
+
while True:
|
|
46
|
+
data, (ip, port) = sock.recvfrom(512)
|
|
47
|
+
exchange = Exchange.from_wire(data, ip=ip, port=port)
|
|
48
|
+
self.handle(exchange)
|
|
49
|
+
sock.sendto(exchange.response.message.to_wire(), (ip, port))
|
|
50
|
+
|
|
51
|
+
def resolve(self, exchange: Exchange) -> Exchange:
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
Defined in subclasses
|
|
55
|
+
|
|
56
|
+
"""
|
|
57
|
+
raise NotImplemented
|
|
58
|
+
|
|
59
|
+
def check_cache(self, exchange: Exchange):
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
Check cache, patch in in new ID and mark complete
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
if exchange.key in self.cache:
|
|
66
|
+
logger.info(f'Request found in cache.')
|
|
67
|
+
exchange.response = self.cache[exchange.key]
|
|
68
|
+
exchange.response.message.id = exchange.request.message.id
|
|
69
|
+
exchange.response.is_complete = True
|
|
70
|
+
|
|
71
|
+
def handle(self, exchange: Exchange):
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
Check validity of request, presence in cache and resolve.
|
|
75
|
+
|
|
76
|
+
"""
|
|
77
|
+
request = exchange.request
|
|
78
|
+
|
|
79
|
+
if not request.is_valid:
|
|
80
|
+
raise ValueError(f'Only one question per request is supported. Got {len(request.question)} questions.')
|
|
81
|
+
|
|
82
|
+
with logger.span(f'Handling request {request.message.id=} {request.question=} {exchange.client=}...'):
|
|
83
|
+
|
|
84
|
+
with logger.span(f'Checking cache...'):
|
|
85
|
+
self.check_cache(exchange)
|
|
86
|
+
|
|
87
|
+
if not exchange.response.is_complete:
|
|
88
|
+
exchange = self.resolve(exchange)
|
|
89
|
+
exchange.response.is_complete = True
|
|
90
|
+
|
|
91
|
+
self.cache[exchange.key] = exchange.response
|
|
92
|
+
|
|
93
|
+
logger.info(f'Resolution complete {request.message.id=} {exchange.response.answer=}')
|
|
94
|
+
return exchange
|
|
@@ -52,7 +52,7 @@ def get_logger(name, version=None, host=Constants.FMTR_OBS_HOST, key=None, org=C
|
|
|
52
52
|
lev_name_otel = logfire._internal.constants.NUMBER_TO_LEVEL[lev_num_otel]
|
|
53
53
|
|
|
54
54
|
console_opts = logfire.ConsoleOptions(
|
|
55
|
-
colors='always'
|
|
55
|
+
colors='always',
|
|
56
56
|
min_log_level=lev_name_otel,
|
|
57
57
|
)
|
|
58
58
|
|
|
@@ -305,6 +305,16 @@ class PackagePaths(FromCallerMixin):
|
|
|
305
305
|
|
|
306
306
|
return self.dev / Constants.DIR_NAME_REPO / self.name_ns / self.dir_name_data
|
|
307
307
|
|
|
308
|
+
@property
|
|
309
|
+
def cache(self) -> Path:
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
Path of cache directory.
|
|
313
|
+
|
|
314
|
+
"""
|
|
315
|
+
|
|
316
|
+
return self.data / Constants.DIR_NAME_CACHE
|
|
317
|
+
|
|
308
318
|
@property
|
|
309
319
|
def artifact(self) -> Path:
|
|
310
320
|
"""
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import regex as re
|
|
1
2
|
from dataclasses import dataclass, asdict
|
|
2
3
|
from functools import cached_property
|
|
3
4
|
from typing import List, Any
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
from fmtr.tools import Constants
|
|
7
7
|
from fmtr.tools.logging_tools import logger
|
|
8
8
|
from fmtr.tools.string_tools import join
|
|
9
9
|
|
|
@@ -92,7 +92,8 @@ class Item:
|
|
|
92
92
|
source: Key
|
|
93
93
|
target: Key
|
|
94
94
|
|
|
95
|
-
|
|
95
|
+
|
|
96
|
+
@dataclass(kw_only=True)
|
|
96
97
|
class Transformer:
|
|
97
98
|
"""
|
|
98
99
|
|
|
@@ -210,7 +211,7 @@ class Transformer:
|
|
|
210
211
|
previous = key
|
|
211
212
|
|
|
212
213
|
def get_history_str():
|
|
213
|
-
return join(history, sep=
|
|
214
|
+
return join(history, sep=Constants.ARROW_SEP)
|
|
214
215
|
|
|
215
216
|
while True:
|
|
216
217
|
if previous in history:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
1.3.9
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fmtr.tools
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.9
|
|
4
4
|
Summary: Collection of high-level tools to simplify everyday development tasks, with a focus on AI/ML
|
|
5
5
|
Home-page: https://github.com/fmtr/fmtr.tools
|
|
6
6
|
Author: Frontmatter
|
|
@@ -97,6 +97,7 @@ Requires-Dist: google-auth-httplib2; extra == "google-api"
|
|
|
97
97
|
Requires-Dist: google-api-python-client; extra == "google-api"
|
|
98
98
|
Provides-Extra: caching
|
|
99
99
|
Requires-Dist: diskcache; extra == "caching"
|
|
100
|
+
Requires-Dist: cachetools; extra == "caching"
|
|
100
101
|
Provides-Extra: pdf
|
|
101
102
|
Requires-Dist: pymupdf; extra == "pdf"
|
|
102
103
|
Requires-Dist: pydantic; extra == "pdf"
|
|
@@ -130,60 +131,61 @@ Requires-Dist: logfire[httpx]; extra == "http"
|
|
|
130
131
|
Provides-Extra: setup
|
|
131
132
|
Requires-Dist: setuptools; extra == "setup"
|
|
132
133
|
Provides-Extra: all
|
|
133
|
-
Requires-Dist: dnspython[doh]; extra == "all"
|
|
134
|
-
Requires-Dist: pymupdf4llm; extra == "all"
|
|
135
|
-
Requires-Dist: regex; extra == "all"
|
|
136
134
|
Requires-Dist: uvicorn[standard]; extra == "all"
|
|
137
|
-
Requires-Dist: tokenizers; extra == "all"
|
|
138
|
-
Requires-Dist: filetype; extra == "all"
|
|
139
|
-
Requires-Dist: flet[all]; extra == "all"
|
|
140
|
-
Requires-Dist: Unidecode; extra == "all"
|
|
141
|
-
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
142
|
-
Requires-Dist: logfire[httpx]; extra == "all"
|
|
143
|
-
Requires-Dist: bokeh; extra == "all"
|
|
144
|
-
Requires-Dist: fastapi; extra == "all"
|
|
145
|
-
Requires-Dist: pytest-cov; extra == "all"
|
|
146
135
|
Requires-Dist: json_repair; extra == "all"
|
|
147
|
-
Requires-Dist: semver; extra == "all"
|
|
148
|
-
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
149
|
-
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
150
|
-
Requires-Dist: html2text; extra == "all"
|
|
151
|
-
Requires-Dist: sre_yield; extra == "all"
|
|
152
|
-
Requires-Dist: pymupdf; extra == "all"
|
|
153
|
-
Requires-Dist: flet-video; extra == "all"
|
|
154
|
-
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
155
|
-
Requires-Dist: pydantic-settings; extra == "all"
|
|
156
|
-
Requires-Dist: ollama; extra == "all"
|
|
157
|
-
Requires-Dist: distributed; extra == "all"
|
|
158
136
|
Requires-Dist: httpx; extra == "all"
|
|
159
|
-
Requires-Dist:
|
|
137
|
+
Requires-Dist: openai; extra == "all"
|
|
160
138
|
Requires-Dist: openpyxl; extra == "all"
|
|
161
|
-
Requires-Dist:
|
|
139
|
+
Requires-Dist: flet[all]; extra == "all"
|
|
140
|
+
Requires-Dist: regex; extra == "all"
|
|
162
141
|
Requires-Dist: tabulate; extra == "all"
|
|
163
142
|
Requires-Dist: tinynetrc; extra == "all"
|
|
164
|
-
Requires-Dist:
|
|
165
|
-
Requires-Dist:
|
|
166
|
-
Requires-Dist: pyyaml; extra == "all"
|
|
167
|
-
Requires-Dist: diskcache; extra == "all"
|
|
168
|
-
Requires-Dist: faker; extra == "all"
|
|
169
|
-
Requires-Dist: docker; extra == "all"
|
|
143
|
+
Requires-Dist: filetype; extra == "all"
|
|
144
|
+
Requires-Dist: google-api-python-client; extra == "all"
|
|
170
145
|
Requires-Dist: yamlscript; extra == "all"
|
|
146
|
+
Requires-Dist: tokenizers; extra == "all"
|
|
147
|
+
Requires-Dist: deepmerge; extra == "all"
|
|
148
|
+
Requires-Dist: sre_yield; extra == "all"
|
|
149
|
+
Requires-Dist: diskcache; extra == "all"
|
|
150
|
+
Requires-Dist: transformers[sentencepiece]; extra == "all"
|
|
151
|
+
Requires-Dist: google-auth-oauthlib; extra == "all"
|
|
152
|
+
Requires-Dist: Unidecode; extra == "all"
|
|
153
|
+
Requires-Dist: sentence_transformers; extra == "all"
|
|
154
|
+
Requires-Dist: logfire; extra == "all"
|
|
155
|
+
Requires-Dist: logfire[fastapi]; extra == "all"
|
|
171
156
|
Requires-Dist: pydantic; extra == "all"
|
|
172
|
-
Requires-Dist:
|
|
157
|
+
Requires-Dist: semver; extra == "all"
|
|
158
|
+
Requires-Dist: dnspython[doh]; extra == "all"
|
|
173
159
|
Requires-Dist: pydevd-pycharm~=251.25410.159; extra == "all"
|
|
174
|
-
Requires-Dist:
|
|
175
|
-
Requires-Dist:
|
|
176
|
-
Requires-Dist:
|
|
177
|
-
Requires-Dist:
|
|
160
|
+
Requires-Dist: google-auth-httplib2; extra == "all"
|
|
161
|
+
Requires-Dist: pymupdf4llm; extra == "all"
|
|
162
|
+
Requires-Dist: html2text; extra == "all"
|
|
163
|
+
Requires-Dist: google-auth; extra == "all"
|
|
164
|
+
Requires-Dist: pydantic-settings; extra == "all"
|
|
178
165
|
Requires-Dist: huggingface_hub; extra == "all"
|
|
179
|
-
Requires-Dist:
|
|
166
|
+
Requires-Dist: pyyaml; extra == "all"
|
|
167
|
+
Requires-Dist: torchvision; extra == "all"
|
|
168
|
+
Requires-Dist: pymupdf; extra == "all"
|
|
169
|
+
Requires-Dist: fastapi; extra == "all"
|
|
170
|
+
Requires-Dist: logfire[httpx]; extra == "all"
|
|
171
|
+
Requires-Dist: contexttimer; extra == "all"
|
|
180
172
|
Requires-Dist: httpx_retries; extra == "all"
|
|
173
|
+
Requires-Dist: dask[bag]; extra == "all"
|
|
174
|
+
Requires-Dist: peft; extra == "all"
|
|
175
|
+
Requires-Dist: distributed; extra == "all"
|
|
176
|
+
Requires-Dist: docker; extra == "all"
|
|
177
|
+
Requires-Dist: faker; extra == "all"
|
|
178
|
+
Requires-Dist: cachetools; extra == "all"
|
|
179
|
+
Requires-Dist: torchaudio; extra == "all"
|
|
181
180
|
Requires-Dist: setuptools; extra == "all"
|
|
182
|
-
Requires-Dist:
|
|
183
|
-
Requires-Dist:
|
|
181
|
+
Requires-Dist: pytest-cov; extra == "all"
|
|
182
|
+
Requires-Dist: pydantic-ai[logfire,openai]; extra == "all"
|
|
183
|
+
Requires-Dist: flet-video; extra == "all"
|
|
184
|
+
Requires-Dist: flet-webview; extra == "all"
|
|
185
|
+
Requires-Dist: appdirs; extra == "all"
|
|
184
186
|
Requires-Dist: pandas; extra == "all"
|
|
185
|
-
Requires-Dist:
|
|
186
|
-
Requires-Dist:
|
|
187
|
+
Requires-Dist: bokeh; extra == "all"
|
|
188
|
+
Requires-Dist: ollama; extra == "all"
|
|
187
189
|
Dynamic: author
|
|
188
190
|
Dynamic: author-email
|
|
189
191
|
Dynamic: description
|
|
@@ -15,60 +15,61 @@ pydantic-ai[logfire,openai]
|
|
|
15
15
|
ollama
|
|
16
16
|
|
|
17
17
|
[all]
|
|
18
|
-
dnspython[doh]
|
|
19
|
-
pymupdf4llm
|
|
20
|
-
regex
|
|
21
18
|
uvicorn[standard]
|
|
22
|
-
tokenizers
|
|
23
|
-
filetype
|
|
24
|
-
flet[all]
|
|
25
|
-
Unidecode
|
|
26
|
-
pydantic-ai[logfire,openai]
|
|
27
|
-
logfire[httpx]
|
|
28
|
-
bokeh
|
|
29
|
-
fastapi
|
|
30
|
-
pytest-cov
|
|
31
19
|
json_repair
|
|
32
|
-
semver
|
|
33
|
-
google-auth-oauthlib
|
|
34
|
-
google-auth-httplib2
|
|
35
|
-
html2text
|
|
36
|
-
sre_yield
|
|
37
|
-
pymupdf
|
|
38
|
-
flet-video
|
|
39
|
-
transformers[sentencepiece]
|
|
40
|
-
pydantic-settings
|
|
41
|
-
ollama
|
|
42
|
-
distributed
|
|
43
20
|
httpx
|
|
44
|
-
|
|
21
|
+
openai
|
|
45
22
|
openpyxl
|
|
46
|
-
|
|
23
|
+
flet[all]
|
|
24
|
+
regex
|
|
47
25
|
tabulate
|
|
48
26
|
tinynetrc
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
pyyaml
|
|
52
|
-
diskcache
|
|
53
|
-
faker
|
|
54
|
-
docker
|
|
27
|
+
filetype
|
|
28
|
+
google-api-python-client
|
|
55
29
|
yamlscript
|
|
30
|
+
tokenizers
|
|
31
|
+
deepmerge
|
|
32
|
+
sre_yield
|
|
33
|
+
diskcache
|
|
34
|
+
transformers[sentencepiece]
|
|
35
|
+
google-auth-oauthlib
|
|
36
|
+
Unidecode
|
|
37
|
+
sentence_transformers
|
|
38
|
+
logfire
|
|
39
|
+
logfire[fastapi]
|
|
56
40
|
pydantic
|
|
57
|
-
|
|
41
|
+
semver
|
|
42
|
+
dnspython[doh]
|
|
58
43
|
pydevd-pycharm~=251.25410.159
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
44
|
+
google-auth-httplib2
|
|
45
|
+
pymupdf4llm
|
|
46
|
+
html2text
|
|
47
|
+
google-auth
|
|
48
|
+
pydantic-settings
|
|
63
49
|
huggingface_hub
|
|
64
|
-
|
|
50
|
+
pyyaml
|
|
51
|
+
torchvision
|
|
52
|
+
pymupdf
|
|
53
|
+
fastapi
|
|
54
|
+
logfire[httpx]
|
|
55
|
+
contexttimer
|
|
65
56
|
httpx_retries
|
|
57
|
+
dask[bag]
|
|
58
|
+
peft
|
|
59
|
+
distributed
|
|
60
|
+
docker
|
|
61
|
+
faker
|
|
62
|
+
cachetools
|
|
63
|
+
torchaudio
|
|
66
64
|
setuptools
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
pytest-cov
|
|
66
|
+
pydantic-ai[logfire,openai]
|
|
67
|
+
flet-video
|
|
68
|
+
flet-webview
|
|
69
|
+
appdirs
|
|
69
70
|
pandas
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
bokeh
|
|
72
|
+
ollama
|
|
72
73
|
|
|
73
74
|
[api]
|
|
74
75
|
fastapi
|
|
@@ -84,6 +85,7 @@ sre_yield
|
|
|
84
85
|
|
|
85
86
|
[caching]
|
|
86
87
|
diskcache
|
|
88
|
+
cachetools
|
|
87
89
|
|
|
88
90
|
[debug]
|
|
89
91
|
pydevd-pycharm~=251.25410.159
|
|
@@ -29,7 +29,7 @@ DEPENDENCIES = {
|
|
|
29
29
|
'html': ['html2text'],
|
|
30
30
|
'interface': ['flet[all]', 'flet-video', 'flet-webview', 'dm'],
|
|
31
31
|
'google.api': ['google-auth', 'google-auth-oauthlib', 'google-auth-httplib2', 'google-api-python-client'],
|
|
32
|
-
'caching': ['diskcache'],
|
|
32
|
+
'caching': ['diskcache', 'cachetools'],
|
|
33
33
|
'pdf': ['pymupdf', 'dm', 'pymupdf4llm'],
|
|
34
34
|
'debug': ['pydevd-pycharm~=251.25410.159'],
|
|
35
35
|
'sets': ['pydantic-settings', 'dm', 'yaml'],
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
|
|
3
|
-
from fmtr.tools import logger
|
|
4
|
-
from fmtr.tools.dns_tools import server, client
|
|
5
|
-
from fmtr.tools.dns_tools.dm import Exchange
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@dataclass
|
|
9
|
-
class Proxy(server.Plain):
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
Base for a DNS Proxy server (plain server) TODO: Allow subclassing of any server type.
|
|
13
|
-
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
client: client.HTTP
|
|
17
|
-
|
|
18
|
-
def process_question(self, exchange: Exchange):
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
Modify exchange based on initial question.
|
|
22
|
-
|
|
23
|
-
"""
|
|
24
|
-
return
|
|
25
|
-
|
|
26
|
-
def process_upstream(self, exchange: Exchange):
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
Modify exchange after upstream response.
|
|
30
|
-
|
|
31
|
-
"""
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
def resolve(self, exchange: Exchange):
|
|
35
|
-
"""
|
|
36
|
-
|
|
37
|
-
Resolve a request, processing each stage, initial question, upstream response etc.
|
|
38
|
-
Subclasses can override the relevant processing methods to implement custom behaviour.
|
|
39
|
-
|
|
40
|
-
"""
|
|
41
|
-
|
|
42
|
-
request = exchange.request
|
|
43
|
-
|
|
44
|
-
with logger.span(f'Handling request {request.message.id=} {request.question=} {exchange.client=}...'):
|
|
45
|
-
|
|
46
|
-
if not request.is_valid:
|
|
47
|
-
raise ValueError(f'Only one question per request is supported. Got {len(request.question)} questions.')
|
|
48
|
-
|
|
49
|
-
with logger.span(f'Processing question...'):
|
|
50
|
-
self.process_question(exchange)
|
|
51
|
-
if exchange.response.is_complete:
|
|
52
|
-
return
|
|
53
|
-
|
|
54
|
-
with logger.span(f'Making upstream request...'):
|
|
55
|
-
self.client.resolve(exchange)
|
|
56
|
-
if exchange.response.is_complete:
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
with logger.span(f'Processing upstream response...'):
|
|
60
|
-
self.process_upstream(exchange)
|
|
61
|
-
if exchange.response.is_complete:
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
exchange.response.is_complete = True
|
|
65
|
-
|
|
66
|
-
logger.info(f'Resolution complete {request.message.id=} {exchange.response.answer=}')
|
|
67
|
-
return
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import socket
|
|
2
|
-
from dataclasses import dataclass
|
|
3
|
-
|
|
4
|
-
from fmtr.tools.dns_tools.dm import Exchange
|
|
5
|
-
from fmtr.tools.logging_tools import logger
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
@dataclass
|
|
9
|
-
class Plain:
|
|
10
|
-
"""
|
|
11
|
-
|
|
12
|
-
Base for starting a plain DNS server
|
|
13
|
-
|
|
14
|
-
"""
|
|
15
|
-
|
|
16
|
-
host: str
|
|
17
|
-
port: int
|
|
18
|
-
|
|
19
|
-
def __post_init__(self):
|
|
20
|
-
|
|
21
|
-
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
22
|
-
|
|
23
|
-
def resolve(self, exchange: Exchange):
|
|
24
|
-
raise NotImplemented
|
|
25
|
-
|
|
26
|
-
def start(self):
|
|
27
|
-
"""
|
|
28
|
-
|
|
29
|
-
Listen and resolve via overridden resolve method.
|
|
30
|
-
|
|
31
|
-
"""
|
|
32
|
-
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
33
|
-
sock.bind((self.host, self.port))
|
|
34
|
-
logger.info(f'Listening on {self.host}:{self.port}')
|
|
35
|
-
while True:
|
|
36
|
-
data, (ip, port) = sock.recvfrom(512)
|
|
37
|
-
exchange = Exchange.from_wire(data, ip=ip, port=port)
|
|
38
|
-
self.resolve(exchange)
|
|
39
|
-
sock.sendto(exchange.response.message.to_wire(), (ip, port))
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
1.3.7
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|