fmtr.tools 1.1.1__py3-none-any.whl → 1.3.81__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.
Files changed (67) hide show
  1. fmtr/tools/__init__.py +68 -52
  2. fmtr/tools/ai_tools/__init__.py +2 -2
  3. fmtr/tools/ai_tools/agentic_tools.py +151 -32
  4. fmtr/tools/ai_tools/inference_tools.py +2 -1
  5. fmtr/tools/api_tools.py +8 -5
  6. fmtr/tools/caching_tools.py +101 -3
  7. fmtr/tools/constants.py +33 -0
  8. fmtr/tools/context_tools.py +23 -0
  9. fmtr/tools/data_modelling_tools.py +227 -14
  10. fmtr/tools/database_tools/__init__.py +6 -0
  11. fmtr/tools/database_tools/document.py +51 -0
  12. fmtr/tools/datatype_tools.py +21 -1
  13. fmtr/tools/datetime_tools.py +12 -0
  14. fmtr/tools/debugging_tools.py +60 -0
  15. fmtr/tools/dns_tools/__init__.py +7 -0
  16. fmtr/tools/dns_tools/client.py +97 -0
  17. fmtr/tools/dns_tools/dm.py +257 -0
  18. fmtr/tools/dns_tools/proxy.py +66 -0
  19. fmtr/tools/dns_tools/server.py +138 -0
  20. fmtr/tools/docker_tools/__init__.py +6 -0
  21. fmtr/tools/entrypoints/__init__.py +0 -0
  22. fmtr/tools/entrypoints/cache_hfh.py +3 -0
  23. fmtr/tools/entrypoints/ep_test.py +2 -0
  24. fmtr/tools/entrypoints/install_yamlscript.py +8 -0
  25. fmtr/tools/{console_script_tools.py → entrypoints/remote_debug_test.py} +1 -6
  26. fmtr/tools/entrypoints/shell_debug.py +8 -0
  27. fmtr/tools/environment_tools.py +2 -2
  28. fmtr/tools/function_tools.py +77 -1
  29. fmtr/tools/google_api_tools.py +15 -4
  30. fmtr/tools/http_tools.py +26 -0
  31. fmtr/tools/inherit_tools.py +27 -0
  32. fmtr/tools/interface_tools/__init__.py +8 -0
  33. fmtr/tools/interface_tools/context.py +13 -0
  34. fmtr/tools/interface_tools/controls.py +354 -0
  35. fmtr/tools/interface_tools/interface_tools.py +189 -0
  36. fmtr/tools/iterator_tools.py +29 -0
  37. fmtr/tools/logging_tools.py +43 -16
  38. fmtr/tools/packaging_tools.py +14 -0
  39. fmtr/tools/path_tools/__init__.py +12 -0
  40. fmtr/tools/path_tools/app_path_tools.py +40 -0
  41. fmtr/tools/{path_tools.py → path_tools/path_tools.py} +156 -12
  42. fmtr/tools/path_tools/type_path_tools.py +3 -0
  43. fmtr/tools/pattern_tools.py +260 -0
  44. fmtr/tools/pdf_tools.py +39 -1
  45. fmtr/tools/settings_tools.py +23 -4
  46. fmtr/tools/setup_tools/__init__.py +8 -0
  47. fmtr/tools/setup_tools/setup_tools.py +447 -0
  48. fmtr/tools/string_tools.py +92 -13
  49. fmtr/tools/tabular_tools.py +61 -0
  50. fmtr/tools/tools.py +27 -2
  51. fmtr/tools/version +1 -1
  52. fmtr/tools/version_tools/__init__.py +12 -0
  53. fmtr/tools/version_tools/version_tools.py +51 -0
  54. fmtr/tools/webhook_tools.py +17 -0
  55. fmtr/tools/yaml_tools.py +66 -5
  56. {fmtr_tools-1.1.1.dist-info → fmtr_tools-1.3.81.dist-info}/METADATA +136 -54
  57. fmtr_tools-1.3.81.dist-info/RECORD +93 -0
  58. {fmtr_tools-1.1.1.dist-info → fmtr_tools-1.3.81.dist-info}/WHEEL +1 -1
  59. fmtr_tools-1.3.81.dist-info/entry_points.txt +6 -0
  60. fmtr_tools-1.3.81.dist-info/top_level.txt +1 -0
  61. fmtr/tools/docker_tools.py +0 -30
  62. fmtr/tools/interface_tools.py +0 -64
  63. fmtr/tools/version_tools.py +0 -62
  64. fmtr_tools-1.1.1.dist-info/RECORD +0 -65
  65. fmtr_tools-1.1.1.dist-info/entry_points.txt +0 -3
  66. fmtr_tools-1.1.1.dist-info/top_level.txt +0 -2
  67. {fmtr_tools-1.1.1.dist-info → fmtr_tools-1.3.81.dist-info}/licenses/LICENSE +0 -0
fmtr/tools/__init__.py CHANGED
@@ -1,8 +1,5 @@
1
- from fmtr.tools import version_tools as version
2
-
3
- __version__ = version.read()
4
-
5
-
1
+ import fmtr.tools.async_tools as asyncio
2
+ import fmtr.tools.database_tools as db
6
3
  import fmtr.tools.dataclass_tools as dataclass
7
4
  import fmtr.tools.datatype_tools as datatype
8
5
  import fmtr.tools.environment_tools as env
@@ -10,156 +7,175 @@ import fmtr.tools.environment_tools as environment
10
7
  import fmtr.tools.function_tools as function
11
8
  import fmtr.tools.hash_tools as hash
12
9
  import fmtr.tools.import_tools as import_
10
+ import fmtr.tools.inherit_tools as inherit
13
11
  import fmtr.tools.iterator_tools as iterator
14
12
  import fmtr.tools.json_tools as json
13
+ import fmtr.tools.logging_tools as logging
14
+ import fmtr.tools.name_tools as name
15
+ import fmtr.tools.packaging_tools as packaging
15
16
  import fmtr.tools.path_tools as path
16
17
  import fmtr.tools.platform_tools as platform
17
18
  import fmtr.tools.random_tools as random
19
+ import fmtr.tools.setup_tools as setup
18
20
  import fmtr.tools.string_tools as string
19
- import fmtr.tools.name_tools as name
20
- import fmtr.tools.logging_tools as logging
21
- import fmtr.tools.async_tools as asyncio
22
- from fmtr.tools.logging_tools import logger
23
-
24
- from fmtr.tools.import_tools import MissingExtraMockModule
25
- from fmtr.tools.path_tools import Path, PackagePaths
21
+ from fmtr.tools import ai_tools as ai
22
+ from fmtr.tools import datetime_tools as dt
23
+ from fmtr.tools import dns_tools as dns
24
+ from fmtr.tools import docker_tools as docker
25
+ from fmtr.tools import interface_tools as interface
26
+ from fmtr.tools import version_tools as version
26
27
  from fmtr.tools.constants import Constants
28
+ from fmtr.tools.import_tools import MissingExtraMockModule
29
+ from fmtr.tools.logging_tools import logger
30
+ # Submodules
31
+ from fmtr.tools.path_tools import Path, PackagePaths, AppPaths
32
+ from fmtr.tools.setup_tools import Setup, SetupPaths, Dependencies, Tools
27
33
 
28
34
  try:
29
35
  from fmtr.tools import augmentation_tools as augmentation
30
- except ImportError as exception:
36
+ except ModuleNotFoundError as exception:
31
37
  augmentation = MissingExtraMockModule('augmentation', exception)
32
38
 
33
39
  try:
34
40
  from fmtr.tools import yaml_tools as yaml
35
- except ImportError as exception:
41
+ except ModuleNotFoundError as exception:
36
42
  yaml = MissingExtraMockModule('yaml', exception)
37
43
 
38
- try:
39
- from fmtr.tools import docker_tools as docker
40
- from fmtr.tools.docker_tools import Container
41
- except ImportError as exception:
42
- docker = Container = MissingExtraMockModule('docker.api', exception)
43
44
 
44
45
  try:
45
46
  from fmtr.tools import parallel_tools as parallel
46
- except ImportError as exception:
47
+ except ModuleNotFoundError as exception:
47
48
  parallel = MissingExtraMockModule('parallel', exception)
48
49
 
49
50
  try:
50
51
  from fmtr.tools import profiling_tools as profiling
51
52
  from fmtr.tools.profiling_tools import Timer
52
- except ImportError as exception:
53
+ except ModuleNotFoundError as exception:
53
54
  profiling = Timer = MissingExtraMockModule('profiling', exception)
54
55
 
55
56
  try:
56
57
  import fmtr.tools.process_tools as process
57
58
  from fmtr.tools.process_tools import ContextProcess
58
- except ImportError as exception:
59
+ except ModuleNotFoundError as exception:
59
60
  process = ContextProcess = MissingExtraMockModule('process', exception)
60
61
 
61
62
  try:
62
63
  from fmtr.tools import tokenization_tools as tokenization
63
- except ImportError as exception:
64
+ except ModuleNotFoundError as exception:
64
65
  tokenization = MissingExtraMockModule('tokenization', exception)
65
66
 
66
67
  try:
67
68
  from fmtr.tools import unicode_tools as unicode
68
- except ImportError as exception:
69
+ except ModuleNotFoundError as exception:
69
70
  unicode = MissingExtraMockModule('unicode', exception)
70
71
 
71
72
  try:
72
73
  from fmtr.tools import netrc_tools as netrc
73
- except ImportError as exception:
74
+ except ModuleNotFoundError as exception:
74
75
  netrc = MissingExtraMockModule('netrc', exception)
75
76
 
76
77
  try:
77
78
  from fmtr.tools import spaces_tools as spaces
78
- except ImportError as exception:
79
+ except ModuleNotFoundError as exception:
79
80
  spaces = MissingExtraMockModule('spaces', exception)
80
81
 
81
82
  try:
82
83
  from fmtr.tools import hfh_tools as hfh
83
- except ImportError as exception:
84
+ except ModuleNotFoundError as exception:
84
85
  hfh = MissingExtraMockModule('hfh', exception)
85
86
 
86
87
  try:
87
88
  from fmtr.tools import merging_tools as merging
88
89
  from fmtr.tools.merging_tools import merge
89
- except ImportError as exception:
90
+ except ModuleNotFoundError as exception:
90
91
  merging = merge = MissingExtraMockModule('merging', exception)
91
92
 
92
93
  try:
93
94
  from fmtr.tools import api_tools as api
94
- except ImportError as exception:
95
+ except ModuleNotFoundError as exception:
95
96
  api = MissingExtraMockModule('api', exception)
96
97
 
97
- try:
98
- from fmtr.tools import ai_tools as ai
99
- except ImportError as exception:
100
- ai = MissingExtraMockModule('ai', exception)
101
-
102
98
  try:
103
99
  from fmtr.tools import data_modelling_tools as dm
104
- except ImportError as exception:
100
+ except ModuleNotFoundError as exception:
105
101
  dm = MissingExtraMockModule('dm', exception)
106
102
 
107
103
  try:
108
104
  from fmtr.tools import json_fix_tools as json_fix
109
- except ImportError as exception:
105
+ except ModuleNotFoundError as exception:
110
106
  json_fix = MissingExtraMockModule('json_fix', exception)
111
107
 
112
108
  try:
113
109
  from fmtr.tools import semantic_tools as semantic
114
- except ImportError as exception:
110
+ except ModuleNotFoundError as exception:
115
111
  semantic = MissingExtraMockModule('semantic', exception)
116
112
 
117
113
  try:
118
114
  from fmtr.tools import metric_tools as metric
119
- except ImportError as exception:
115
+ except ModuleNotFoundError as exception:
120
116
  metric = MissingExtraMockModule('metric', exception)
121
117
 
122
118
  try:
123
119
  from fmtr.tools import html_tools as html
124
- except ImportError as exception:
120
+ except ModuleNotFoundError as exception:
125
121
  html = MissingExtraMockModule('html', exception)
126
122
 
127
- try:
128
- from fmtr.tools import interface_tools as interface
129
- except ImportError as exception:
130
- interface = MissingExtraMockModule('interface', exception)
131
-
132
123
  try:
133
124
  from fmtr.tools import openai_tools as openai
134
- except ImportError as exception:
125
+ except ModuleNotFoundError as exception:
135
126
  openai = MissingExtraMockModule('openai', exception)
136
127
 
137
128
  try:
138
129
  from fmtr.tools import google_api_tools as google_api
139
- except ImportError as exception:
130
+ except ModuleNotFoundError as exception:
140
131
  google_api = MissingExtraMockModule('google.api', exception)
141
132
 
142
133
  try:
143
134
  from fmtr.tools import caching_tools as caching
144
- except ImportError as exception:
135
+ except ModuleNotFoundError as exception:
145
136
  caching = MissingExtraMockModule('caching', exception)
146
137
 
147
138
  try:
148
139
  from fmtr.tools import pdf_tools as pdf
149
- except ImportError as exception:
140
+ except ModuleNotFoundError as exception:
150
141
  pdf = MissingExtraMockModule('pdf', exception)
151
142
 
152
143
  try:
153
144
  from fmtr.tools import tabular_tools as tabular
154
- except ImportError as exception:
145
+ except ModuleNotFoundError as exception:
155
146
  tabular = MissingExtraMockModule('tabular', exception)
156
147
 
157
148
  try:
158
149
  from fmtr.tools import debugging_tools as debug
159
- except ImportError as exception:
150
+ except ModuleNotFoundError as exception:
160
151
  debug = MissingExtraMockModule('debug', exception)
161
152
 
162
153
  try:
163
154
  from fmtr.tools import settings_tools as sets
164
- except ImportError as exception:
165
- sets = MissingExtraMockModule('sets', exception)
155
+ except ModuleNotFoundError as exception:
156
+ sets = MissingExtraMockModule('sets', exception)
157
+
158
+ try:
159
+ from fmtr.tools import pattern_tools as patterns
160
+ except ModuleNotFoundError as exception:
161
+ patterns = MissingExtraMockModule('patterns', exception)
162
+
163
+ try:
164
+ from fmtr.tools import http_tools as http
165
+ from fmtr.tools.http_tools import Client
166
+ except ModuleNotFoundError as exception:
167
+ http = Client = MissingExtraMockModule('http', exception)
168
+
169
+ try:
170
+ from fmtr.tools import webhook_tools as webhook
171
+ except ModuleNotFoundError as exception:
172
+ webhook = MissingExtraMockModule('webhook', exception)
173
+
174
+
175
+ def get_version():
176
+ """
177
+
178
+ Defer reading version
179
+
180
+ """
181
+ return version.read()
@@ -2,10 +2,10 @@ from fmtr.tools.import_tools import MissingExtraMockModule
2
2
 
3
3
  try:
4
4
  from fmtr.tools.ai_tools import inference_tools as infer
5
- except ImportError as exception:
5
+ except ModuleNotFoundError as exception:
6
6
  infer = MissingExtraMockModule('ai', exception)
7
7
 
8
8
  try:
9
9
  from fmtr.tools.ai_tools import agentic_tools as agentic
10
- except ImportError as exception:
10
+ except ModuleNotFoundError as exception:
11
11
  agentic = MissingExtraMockModule('ai.client', exception)
@@ -1,33 +1,57 @@
1
+ from typing import List, Optional, Any, Annotated, Generic
2
+
1
3
  import pydantic_ai
2
- from pydantic_ai import RunContext
4
+ from pydantic import PlainValidator
5
+ from pydantic_ai import RunContext, ModelRetry
6
+ from pydantic_ai._output import OutputDataT
3
7
  from pydantic_ai.agent import AgentRunResult, Agent
4
- from pydantic_ai.messages import ModelRequest, RetryPromptPart
5
8
  from pydantic_ai.models.openai import OpenAIModel
9
+ from pydantic_ai.output import OutputSpec, NativeOutput, ToolOutput
6
10
  from pydantic_ai.providers.openai import OpenAIProvider
11
+ from pydantic_ai.tools import AgentDepsT
7
12
 
8
13
  from fmtr.tools import environment_tools as env
9
14
  from fmtr.tools.constants import Constants
15
+ from fmtr.tools.logging_tools import logger
16
+ from fmtr.tools.string_tools import truncate_mid
10
17
 
11
18
  pydantic_ai.Agent.instrument_all()
12
19
 
13
- class Task:
20
+
21
+ class Validator:
22
+ """
23
+
24
+ Subclassable validator
25
+
26
+ """
27
+
28
+ async def validate(self, ctx: RunContext[Any], output: Any) -> List[str]:
29
+ raise NotImplementedError()
30
+
31
+
32
+ api_key = env.get(Constants.FMTR_OPENAI_API_KEY_KEY, None)
33
+
34
+ class Task(Generic[AgentDepsT, OutputDataT]):
14
35
  """
15
36
 
16
37
  Linear task definition, as Agent configuration and typing, plus state history.
17
38
 
18
39
  """
19
40
 
20
- PROVIDER = OpenAIProvider(api_key=env.get(Constants.FMTR_OPENAI_API_KEY_KEY))
41
+ TypeDeps = str
42
+ TypeOutput = str
43
+
44
+ PROVIDER = OpenAIProvider(api_key=api_key) if api_key else None
21
45
 
22
46
  API_HOST_FMTR = env.get(Constants.FMTR_AI_HOST_KEY, Constants.FMTR_AI_HOST_DEFAULT)
23
47
  API_URL_FMTR = f'https://{API_HOST_FMTR}/v1'
24
48
  PROVIDER_FMTR = OpenAIProvider(base_url=API_URL_FMTR)
25
49
 
26
50
  MODEL_ID = 'gpt-4o'
27
- SYSTEM_PROMPT = None
28
- DEPS_TYPE = str
29
- RESULT_TYPE = str
51
+ MODEL_ID_FMTR = 'qwen2.5-coder:32b'
52
+ SYSTEM_PROMPT_STATIC = None
30
53
  RESULT_RETRIES = 5
54
+ VALIDATORS: List[Validator] = []
31
55
 
32
56
  def __init__(self, *args, **kwargs):
33
57
  """
@@ -37,64 +61,159 @@ class Task:
37
61
  """
38
62
 
39
63
  self.model = OpenAIModel(self.MODEL_ID, provider=self.PROVIDER)
40
- self.agent = Agent(
64
+ self.agent = Agent[AgentDepsT, OutputDataT](
41
65
  *args,
42
66
  model=self.model,
43
- system_prompt=self.SYSTEM_PROMPT or [],
44
- deps_type=self.DEPS_TYPE,
45
- result_type=self.RESULT_TYPE,
46
- result_retries=self.RESULT_RETRIES,
67
+ system_prompt=self.SYSTEM_PROMPT_STATIC or [],
68
+ deps_type=self.TypeDeps,
69
+ output_type=self.tool_output,
70
+ output_retries=self.RESULT_RETRIES,
47
71
  **kwargs
48
72
  )
49
73
 
50
74
  self.agent.output_validator(self.validate)
75
+ self.agent.system_prompt(self.add_system_prompt)
51
76
  self.history = []
52
77
 
53
- async def run(self, *args, **kwargs) -> AgentRunResult[RESULT_TYPE]:
78
+ @property
79
+ def tool_output(self) -> OutputSpec[OutputDataT]:
54
80
  """
55
81
 
56
- Run Agent storing history
82
+ Tool output specification (e.g. ToolOutput/NativeOutput etc.)
57
83
 
58
84
  """
85
+ return ToolOutput(self.TypeOutput)
59
86
 
60
- result = await self.agent.run(*args, message_history=self.history, **kwargs)
61
- self.history = result.all_messages()
62
- return result
63
87
 
64
- async def revert(self, msg, deps):
88
+ @property
89
+ def sync_runner(self):
65
90
  """
66
91
 
67
- Post-hoc, user-initiated tool retry.
92
+ Convenience/debug function to run without async.
68
93
 
69
94
  """
70
- msg_final = self.history.pop(-1)
71
- tool_return_part = msg_final.parts[0]
95
+ import asyncio
96
+ return asyncio.run
72
97
 
73
- retry_prompt = RetryPromptPart(content=msg, tool_name=tool_return_part.tool_name, tool_call_id=tool_return_part.tool_call_id, timestamp=tool_return_part.timestamp, part_kind='retry-prompt')
74
- retry_request = ModelRequest(parts=[retry_prompt], instructions=msg_final.instructions, kind='request')
98
+ async def run(self, *args, deps=None, **kwargs) -> AgentRunResult[OutputDataT]:
99
+ """
75
100
 
76
- self.history.append(retry_request)
101
+ Run Agent with deps-relative user prompt and while storing history
77
102
 
78
- result = await Task.run(self, deps=deps)
103
+ """
104
+ result = await self.agent.run(*args, user_prompt=self.get_prompt(deps), deps=deps, message_history=self.history, **kwargs)
105
+ self.history = result.all_messages()
79
106
  return result
80
107
 
81
- async def validate(self, ctx: RunContext[DEPS_TYPE], output: RESULT_TYPE) -> RESULT_TYPE:
108
+ async def validate(self, ctx: RunContext[AgentDepsT], output: OutputDataT) -> OutputDataT:
82
109
  """
83
110
 
84
- Dummy validator
111
+ Aggregate any validation failures and combine them into a single ModelRetry exception
85
112
 
86
113
  """
114
+ msgs = []
115
+ for validator in self.VALIDATORS:
116
+ msgs += validator.validate(ctx, output)
117
+
118
+ if msgs:
119
+ msg = '. '.join(msgs)
120
+ logger.warning(msg)
121
+ raise ModelRetry(msg)
122
+
87
123
  return output
88
124
 
125
+ def get_prompt(self, deps: Optional[AgentDepsT]) -> Optional[str]:
126
+ """
127
+
128
+ Dummy prompt generator
89
129
 
90
- class TaskTest(Task):
91
- PROVIDER = Task.PROVIDER_FMTR
92
- MODEL_ID = 'mixtral:8x7b'
130
+ """
131
+ return None
132
+
133
+ def add_system_prompt(self, ctx: RunContext[AgentDepsT]) -> str | List[str]:
134
+ """
135
+
136
+ Dummy system prompt append
137
+
138
+ """
139
+
140
+ return []
141
+
142
+ def reset(self):
143
+ """
144
+
145
+ Reset the task by deleting its history.
146
+
147
+ """
148
+ self.history = []
149
+
150
+ @property
151
+ def tool_schema(self):
152
+ """
153
+
154
+ Impossible to find otherwise.
155
+
156
+ """
157
+ return self.agent._output_toolset
158
+
159
+ def __repr__(self):
160
+ """
161
+
162
+ String representation of the object
163
+
164
+ """
165
+ return f'{self.__class__.__name__}({repr(truncate_mid(self.SYSTEM_PROMPT_STATIC, 100))})'
166
+
167
+
168
+ def default_prompt_none_specified(text):
169
+ """
170
+
171
+ If the prompt is falsey, explicitly state None Specified
172
+
173
+ """
174
+ if not (text or '').strip():
175
+ return Constants.PROMPT_NONE_SPECIFIED
176
+ return text
177
+
178
+
179
+ StringDefaultNoneSpecified = Annotated[Optional[str], PlainValidator(default_prompt_none_specified)]
93
180
 
94
181
 
95
182
  if __name__ == '__main__':
96
183
  import asyncio
184
+ from fmtr.tools import dm
185
+
186
+
187
+ class TestOutput(dm.Base):
188
+ text: str
189
+
190
+
191
+ class TestDeps(dm.Base):
192
+ lang: str
193
+ subject: str
194
+
195
+
196
+ class TaskTest(Task):
197
+ PROVIDER = Task.PROVIDER_FMTR
198
+ MODEL_ID = Task.MODEL_ID_FMTR
199
+ TypeOutput = TestOutput
200
+ SYSTEM_PROMPT_STATIC = 'Tell the user jokes.'
201
+
202
+ @property
203
+ def tool_output(self) -> OutputSpec[TestOutput]:
204
+ return NativeOutput(self.TypeOutput)
205
+
206
+ def add_system_prompt(self, ctx: RunContext[TestDeps]) -> str:
207
+ return f'The jokes must be in the {ctx.deps.lang} language.'
208
+
209
+ def get_prompt(self, deps: Optional[TestDeps]) -> str:
210
+ return f'Tell me one about {deps.subject}.'
97
211
 
98
212
  task = TaskTest()
99
- result = asyncio.run(task.run('Hello! What is your name?'))
100
- result
213
+ deps = TestDeps(lang='English', subject='eggs')
214
+ result1 = task.sync_runner(task.run(deps=deps))
215
+ result1
216
+
217
+ deps = TestDeps(lang='German', subject='sausages')
218
+ result2 = task.sync_runner(task.run(deps=deps))
219
+ result2
@@ -266,6 +266,7 @@ class BulkInferenceManager:
266
266
  yield ids_output
267
267
 
268
268
  except RuntimeError as exception:
269
+ # Instability causes CUBLAS_STATUS in str(exception) too
269
270
  if "CUDA out of memory" in str(exception):
270
271
  logger.warning(f"Ran out of memory. Reducing batch size: {repr(exception)}")
271
272
  batcher.reduce()
@@ -412,5 +413,5 @@ def tst_tool():
412
413
 
413
414
 
414
415
  if __name__ == '__main__':
415
- texts = tst_tool()
416
+ texts = tst()
416
417
  texts
fmtr/tools/api_tools.py CHANGED
@@ -3,7 +3,7 @@ from dataclasses import dataclass
3
3
  from fastapi import FastAPI, Request
4
4
  from typing import Callable, List, Optional, Union
5
5
 
6
- from fmtr.tools.environment_tools import IS_DEBUG
6
+ from fmtr.tools import environment_tools
7
7
  from fmtr.tools.iterator_tools import enlist
8
8
  from fmtr.tools.logging_tools import logger
9
9
 
@@ -24,7 +24,7 @@ class Endpoint:
24
24
  self.tags = enlist(self.tags)
25
25
 
26
26
 
27
- class ApiBase:
27
+ class Base:
28
28
  """
29
29
 
30
30
  Simple API base class, generalising endpoint-as-method config.
@@ -33,6 +33,8 @@ class ApiBase:
33
33
  TITLE = 'Base API'
34
34
  HOST = '0.0.0.0'
35
35
  PORT = 8080
36
+ SWAGGER_PARAMS = dict(tryItOutEnabled=True)
37
+ URL_DOCS = '/docs'
36
38
 
37
39
  def add_endpoint(self, endpoint: Endpoint):
38
40
  """
@@ -51,12 +53,13 @@ class ApiBase:
51
53
  )(endpoint.method)
52
54
 
53
55
  def __init__(self):
54
- self.app = FastAPI(title=self.TITLE)
56
+ self.app = FastAPI(title=self.TITLE, swagger_ui_parameters=self.SWAGGER_PARAMS, docs_url=self.URL_DOCS)
57
+ logger.instrument_fastapi(self.app)
55
58
 
56
59
  for endpoint in self.get_endpoints():
57
60
  self.add_endpoint(endpoint)
58
61
 
59
- if IS_DEBUG:
62
+ if environment_tools.IS_DEV:
60
63
  self.app.exception_handler(Exception)(self.handle_exception)
61
64
 
62
65
  def get_endpoints(self) -> List[Endpoint]:
@@ -94,4 +97,4 @@ class ApiBase:
94
97
 
95
98
 
96
99
  if __name__ == '__main__':
97
- ApiBase.launch()
100
+ Base.launch()
@@ -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 logger, Path
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. "{path=}"...')
38
+ logger.warning(f'Cache does not exist. Will be created. {str(path)=}...')
35
39
 
36
- logger.info(f'Initializing Disk Cache at path "{path=}"...')
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