fred-oss 0.12.0__tar.gz → 0.14.0__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.
- {fred_oss-0.12.0/src/main/fred_oss.egg-info → fred_oss-0.14.0}/PKG-INFO +3 -1
- {fred_oss-0.12.0 → fred_oss-0.14.0}/requirements.txt +3 -1
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/cli/main.py +5 -0
- fred_oss-0.14.0/src/main/fred/version +1 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/info.py +54 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/plugins/__init__.py +0 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/plugins/_local.py +26 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/plugins/catalog.py +15 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/plugins/interface.py +152 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/rest/__init__.py +0 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/rest/cli_ext.py +32 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/rest/routers/__init__.py +0 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/rest/routers/_runner.py +40 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/rest/routers/catalog.py +18 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/rest/routers/interface.py +39 -0
- fred_oss-0.14.0/src/main/fred/worker/runner/rest/server.py +77 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0/src/main/fred_oss.egg-info}/PKG-INFO +3 -1
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred_oss.egg-info/SOURCES.txt +12 -0
- fred_oss-0.14.0/src/main/fred_oss.egg-info/requires.txt +5 -0
- fred_oss-0.12.0/src/main/fred/version +0 -1
- fred_oss-0.12.0/src/main/fred_oss.egg-info/requires.txt +0 -3
- {fred_oss-0.12.0 → fred_oss-0.14.0}/MANIFEST.in +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/NOTICE.txt +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/README.md +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/setup.cfg +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/setup.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/cli/__init__.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/cli/__main__.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/cli/interface.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/__init__.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/cli_ext.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/runtime.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/runtimes/__init__.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/runtimes/scanner.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/runtimes/sync.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/wrappers/__init__.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/wrappers/dbutils.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/runpod/__init__.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/runpod/cli_ext.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/runpod/helper.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/maturity.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/settings.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/utils/__init__.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/utils/dateops.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/utils/runtime.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/version.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/worker/__init__.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/worker/interface.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/worker/runner/__init__.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/worker/runner/client.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/worker/runner/handler.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/worker/runner/utils.py +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred_oss.egg-info/dependency_links.txt +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred_oss.egg-info/entry_points.txt +0 -0
- {fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred_oss.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fred-oss
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: FREDOSS
|
|
5
5
|
Home-page: https://fred.fahera.mx
|
|
6
6
|
Author: Fahera Research, Education, and Development
|
|
@@ -11,6 +11,8 @@ License-File: NOTICE.txt
|
|
|
11
11
|
Requires-Dist: fire==0.7.1
|
|
12
12
|
Requires-Dist: psutil==7.0.0
|
|
13
13
|
Requires-Dist: redis==6.4.0
|
|
14
|
+
Requires-Dist: fastapi==0.116.2
|
|
15
|
+
Requires-Dist: uvicorn[standard]==0.35.0
|
|
14
16
|
Dynamic: author
|
|
15
17
|
Dynamic: author-email
|
|
16
18
|
Dynamic: description
|
|
@@ -18,6 +18,11 @@ class CLIExtensionGroups:
|
|
|
18
18
|
def runpod(self):
|
|
19
19
|
from fred.integrations.runpod.cli_ext import RunPodExt
|
|
20
20
|
return RunPodExt()
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def runner_server(self):
|
|
24
|
+
from fred.worker.runner.rest.cli_ext import RunnerServerExt
|
|
25
|
+
return RunnerServerExt()
|
|
21
26
|
|
|
22
27
|
|
|
23
28
|
class CLI(AbstractCLI, CLIExtensionGroups):
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.14.0
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import uuid
|
|
2
|
+
from typing import Optional
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from fred.settings import logger_manager
|
|
6
|
+
|
|
7
|
+
logger = logger_manager.get_logger(name=__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True, slots=True)
|
|
11
|
+
class RunnerInfo:
|
|
12
|
+
runner_id: str
|
|
13
|
+
created_at: str
|
|
14
|
+
runner_inner_handler_classname: str
|
|
15
|
+
runner_inner_handler_classpath: str
|
|
16
|
+
lifetime: int
|
|
17
|
+
timeout: int
|
|
18
|
+
|
|
19
|
+
@classmethod
|
|
20
|
+
def create(
|
|
21
|
+
cls,
|
|
22
|
+
runner_inner_handler_classname: str,
|
|
23
|
+
runner_inner_handler_classpath: str,
|
|
24
|
+
runner_id: Optional[str] = None,
|
|
25
|
+
created_at: Optional[str] = None,
|
|
26
|
+
lifetime: int = 3600, # Default to 1 hour
|
|
27
|
+
timeout: int = 10, # Default to 10 seconds
|
|
28
|
+
) -> "RunnerInfo":
|
|
29
|
+
from fred.utils.dateops import datetime_utcnow
|
|
30
|
+
return cls(
|
|
31
|
+
runner_id=runner_id or str(uuid.uuid4()),
|
|
32
|
+
runner_inner_handler_classname=runner_inner_handler_classname,
|
|
33
|
+
runner_inner_handler_classpath=runner_inner_handler_classpath,
|
|
34
|
+
created_at=created_at or datetime_utcnow().isoformat(),
|
|
35
|
+
lifetime=lifetime,
|
|
36
|
+
timeout=timeout,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
def get_start_event(self, **kwargs) -> dict:
|
|
40
|
+
runner_id = kwargs.pop("runner_id", self.runner_id)
|
|
41
|
+
payload = {
|
|
42
|
+
"lifetime": self.lifetime,
|
|
43
|
+
"timeout": self.timeout,
|
|
44
|
+
"runner_configs": {
|
|
45
|
+
"id": runner_id,
|
|
46
|
+
"import_pattern": self.runner_inner_handler_classpath,
|
|
47
|
+
"handler_classname": self.runner_inner_handler_classname,
|
|
48
|
+
},
|
|
49
|
+
**kwargs,
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
"input": payload,
|
|
53
|
+
"id": runner_id,
|
|
54
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from fred.settings import logger_manager
|
|
4
|
+
from fred.worker.runner.info import RunnerInfo
|
|
5
|
+
from fred.worker.runner.handler import RunnerHandler
|
|
6
|
+
from fred.worker.runner.plugins.interface import PluginInterface
|
|
7
|
+
|
|
8
|
+
logger = logger_manager.get_logger(name=__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, slots=True)
|
|
12
|
+
class LocalPlugin(PluginInterface):
|
|
13
|
+
|
|
14
|
+
def _execute(
|
|
15
|
+
self,
|
|
16
|
+
runner_info: RunnerInfo,
|
|
17
|
+
outer_handler: RunnerHandler,
|
|
18
|
+
**kwargs
|
|
19
|
+
):
|
|
20
|
+
"""Execute the runner locally using the provided outer handler.
|
|
21
|
+
Args:
|
|
22
|
+
runner_info (RunnerInfo): Information about the runner to execute.
|
|
23
|
+
outer_handler (RunnerHandler): The outer handler to use for execution.
|
|
24
|
+
**kwargs: Additional keyword arguments to pass to the execution method implemented by the subclass.
|
|
25
|
+
"""
|
|
26
|
+
outer_handler.run(event=runner_info.get_start_event(**kwargs))
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
|
|
4
|
+
from fred.settings import logger_manager
|
|
5
|
+
from fred.worker.runner.plugins._local import LocalPlugin
|
|
6
|
+
|
|
7
|
+
logger = logger_manager.get_logger(name=__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PluginCatalog(enum.Enum):
|
|
11
|
+
"""Enum for the different plugins available in FRED."""
|
|
12
|
+
|
|
13
|
+
LOCAL = LocalPlugin.auto()
|
|
14
|
+
RUNPOD = None # Placeholder for future RunPod plugin
|
|
15
|
+
LAMBDA = None # Placeholder for future AWS Lambda plugin
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from threading import Thread
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
from redis import Redis
|
|
6
|
+
|
|
7
|
+
from fred.settings import logger_manager
|
|
8
|
+
from fred.worker.runner.info import RunnerInfo
|
|
9
|
+
from fred.worker.runner.handler import RunnerHandler
|
|
10
|
+
from fred.worker.runner.utils import get_redis_configs_from_payload
|
|
11
|
+
from fred.utils.dateops import datetime_utcnow
|
|
12
|
+
|
|
13
|
+
logger = logger_manager.get_logger(name=__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True, slots=True)
|
|
17
|
+
class PluginInterface:
|
|
18
|
+
"""Interface for the different plugins available for FRED Runners."""
|
|
19
|
+
redis: Redis
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def auto(cls, **kwargs) -> "PluginInterface":
|
|
23
|
+
"""Auto-instantiate the plugin interface with a Redis instance.
|
|
24
|
+
If a Redis instance is not provided, it will create one using configurations
|
|
25
|
+
extracted from the provided keyword arguments.
|
|
26
|
+
Args:
|
|
27
|
+
**kwargs: Keyword arguments that may contain Redis configurations or an existing Redis instance.
|
|
28
|
+
Returns:
|
|
29
|
+
PluginInterface: An instance of the PluginInterface with a Redis connection.
|
|
30
|
+
"""
|
|
31
|
+
redis = kwargs.pop("instance", None) or Redis(**get_redis_configs_from_payload(payload=kwargs, keep=False))
|
|
32
|
+
return cls(redis=redis)
|
|
33
|
+
|
|
34
|
+
def _execute(
|
|
35
|
+
self,
|
|
36
|
+
runner_info: RunnerInfo,
|
|
37
|
+
outer_handler: RunnerHandler,
|
|
38
|
+
**kwargs
|
|
39
|
+
):
|
|
40
|
+
raise NotImplementedError("This method should be overridden by subclasses.")
|
|
41
|
+
|
|
42
|
+
def _monitor(self, runner_info: RunnerInfo, **kwargs):
|
|
43
|
+
raise NotImplementedError("This method should be overridden by subclasses.")
|
|
44
|
+
|
|
45
|
+
def monitor(
|
|
46
|
+
self,
|
|
47
|
+
runner_info: RunnerInfo,
|
|
48
|
+
blocking: bool = False,
|
|
49
|
+
timeout: Optional[int] = None,
|
|
50
|
+
**kwargs
|
|
51
|
+
) -> Optional[Thread]:
|
|
52
|
+
"""Start monitoring the runner in a separate thread.
|
|
53
|
+
Args:
|
|
54
|
+
runner_info (RunnerInfo): Information about the runner to monitor.
|
|
55
|
+
blocking (bool, optional): Whether to block the main thread until monitoring is complete. Defaults to False.
|
|
56
|
+
timeout (Optional[int], optional): Timeout in seconds for the monitoring thread. Defaults to None.
|
|
57
|
+
**kwargs: Additional keyword arguments to pass to the monitoring method.
|
|
58
|
+
Returns:
|
|
59
|
+
Optional[Thread]: The thread object for the monitoring thread if not blocking, else None."""
|
|
60
|
+
# Start monitoring in a separate thread
|
|
61
|
+
thread = Thread(
|
|
62
|
+
target=self._monitor,
|
|
63
|
+
kwargs={
|
|
64
|
+
"runner_info": runner_info,
|
|
65
|
+
"blocking": blocking,
|
|
66
|
+
**kwargs
|
|
67
|
+
},
|
|
68
|
+
daemon=True,
|
|
69
|
+
)
|
|
70
|
+
thread.start()
|
|
71
|
+
# If blocking is True, wait for the thread to finish or timeout
|
|
72
|
+
return thread.join(timeout=timeout) if blocking else thread
|
|
73
|
+
|
|
74
|
+
def _execute_wrapper(
|
|
75
|
+
self,
|
|
76
|
+
runner_info: RunnerInfo,
|
|
77
|
+
outer_handler: RunnerHandler,
|
|
78
|
+
**kwargs
|
|
79
|
+
) -> str:
|
|
80
|
+
"""Wrapper method to handle execution and include error logging.
|
|
81
|
+
|
|
82
|
+
Since we don't control the implementation of the _execute method in subclasses, we
|
|
83
|
+
need to wrap it to add error handling and logging.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
runner_info (RunnerInfo): Information about the runner to execute.
|
|
87
|
+
outer_handler (RunnerHandler): The outer handler to use for execution.
|
|
88
|
+
**kwargs: Additional keyword arguments to pass to the execution method implemented by the subclass.
|
|
89
|
+
"""
|
|
90
|
+
runner_id = runner_info.runner_id
|
|
91
|
+
self.redis.set(f"runner:{runner_id}:status", f"STARTING:{datetime_utcnow().isoformat()}")
|
|
92
|
+
try:
|
|
93
|
+
self._execute(
|
|
94
|
+
runner_info=runner_info,
|
|
95
|
+
outer_handler=outer_handler,
|
|
96
|
+
**kwargs
|
|
97
|
+
)
|
|
98
|
+
except Exception as e:
|
|
99
|
+
self.redis.set(f"runner:{runner_id}:status", f"ERROR:{datetime_utcnow().isoformat()}")
|
|
100
|
+
logger.error(f"Error executing runner '{runner_id}': {e}")
|
|
101
|
+
raise
|
|
102
|
+
return runner_id
|
|
103
|
+
|
|
104
|
+
def execute(
|
|
105
|
+
self,
|
|
106
|
+
runner_info: RunnerInfo,
|
|
107
|
+
outer_handler: RunnerHandler,
|
|
108
|
+
wait_for_exec: bool = False,
|
|
109
|
+
timeout: Optional[int] = None,
|
|
110
|
+
enable_monitor: bool = False,
|
|
111
|
+
wait_for_monitor: bool = False,
|
|
112
|
+
**kwargs
|
|
113
|
+
) -> Thread:
|
|
114
|
+
"""Execute the runner using the specified plugin.
|
|
115
|
+
This method starts the execution of the runner in a separate thread. It can also
|
|
116
|
+
optionally start a monitoring thread to monitor the runner's status.
|
|
117
|
+
Args:
|
|
118
|
+
runner_info (RunnerInfo): Information about the runner to execute.
|
|
119
|
+
outer_handler (RunnerHandler): The outer handler to use for execution.
|
|
120
|
+
wait_for_exec (bool, optional): Whether to wait for the execution thread to complete. Defaults to False.
|
|
121
|
+
timeout (Optional[int], optional): Timeout in seconds for the execution thread. Defaults to None.
|
|
122
|
+
enable_monitor (bool, optional): Whether to enable monitoring of the runner. Defaults to False.
|
|
123
|
+
wait_for_monitor (bool, optional): Whether to wait for the monitoring thread to complete. Defaults to False.
|
|
124
|
+
**kwargs: Additional keyword arguments to pass to the execution and monitoring methods.
|
|
125
|
+
Returns:
|
|
126
|
+
Thread: The thread object for the execution.
|
|
127
|
+
"""
|
|
128
|
+
runner_id = runner_info.runner_id
|
|
129
|
+
logger.info(f"Starting thread runner '{runner_id}' using plugin '{self.__class__.__name__}'.")
|
|
130
|
+
thread = Thread(
|
|
131
|
+
target=self._execute_wrapper,
|
|
132
|
+
kwargs={
|
|
133
|
+
"runner_info": runner_info,
|
|
134
|
+
"outer_handler": outer_handler,
|
|
135
|
+
**kwargs
|
|
136
|
+
},
|
|
137
|
+
daemon=True,
|
|
138
|
+
)
|
|
139
|
+
thread.start()
|
|
140
|
+
if enable_monitor:
|
|
141
|
+
self.monitor(
|
|
142
|
+
runner_info=runner_info,
|
|
143
|
+
blocking=wait_for_monitor,
|
|
144
|
+
timeout=timeout,
|
|
145
|
+
**kwargs
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
logger.info(f"Monitoring disabled for runner '{runner_id}'.")
|
|
149
|
+
if wait_for_exec:
|
|
150
|
+
logger.info(f"Waiting for execution of runner '{runner_id}' to complete.")
|
|
151
|
+
thread.join()
|
|
152
|
+
return thread
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from fred.cli.interface import IntegrationExtCLI
|
|
4
|
+
from fred.settings import logger_manager
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
logger = logger_manager.get_logger(name=__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class RunnerServerExt(IntegrationExtCLI):
|
|
11
|
+
|
|
12
|
+
def start(
|
|
13
|
+
self,
|
|
14
|
+
include_routers: Optional[list[str]] = None,
|
|
15
|
+
exclude_routers: Optional[list[str]] = None,
|
|
16
|
+
fast_api_configs: Optional[dict] = None,
|
|
17
|
+
server_configs: Optional[dict] = None,
|
|
18
|
+
):
|
|
19
|
+
from fred.worker.runner.rest.server import RunnerServer
|
|
20
|
+
|
|
21
|
+
include_routers = include_routers or []
|
|
22
|
+
exclude_routers = exclude_routers or []
|
|
23
|
+
fast_api_configs = fast_api_configs or {}
|
|
24
|
+
server_configs = server_configs or {}
|
|
25
|
+
|
|
26
|
+
logger.info("Starting REST server...")
|
|
27
|
+
server = RunnerServer.auto(
|
|
28
|
+
include_routers=include_routers,
|
|
29
|
+
exclude_routers=exclude_routers,
|
|
30
|
+
**fast_api_configs,
|
|
31
|
+
)
|
|
32
|
+
server.start(**server_configs)
|
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from fred.worker.runner.rest.routers.interface import RouterInterface
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@dataclass(frozen=True, slots=False)
|
|
6
|
+
class RunnerRouter(RouterInterface):
|
|
7
|
+
|
|
8
|
+
def __post_init__(self):
|
|
9
|
+
self.router.add_api_route(
|
|
10
|
+
"/handler_exists",
|
|
11
|
+
self.handler_exists,
|
|
12
|
+
methods=["GET"],
|
|
13
|
+
tags=["Runner"],
|
|
14
|
+
summary="Check if a handler class exists and is a RunnerHandler.",
|
|
15
|
+
response_description="Details about the handler class.",
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
def handler_exists(self, classname: str, classpath: str) -> dict:
|
|
19
|
+
from fred.worker.runner.handler import RunnerHandler
|
|
20
|
+
from fred.worker.interface import HandlerInterface
|
|
21
|
+
|
|
22
|
+
result_payload = {
|
|
23
|
+
"handler_classname": classname,
|
|
24
|
+
"handler_classpath": classpath,
|
|
25
|
+
"exists": False,
|
|
26
|
+
"is_runner_handler": False,
|
|
27
|
+
"metadata": {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
try:
|
|
31
|
+
handler = HandlerInterface.find_handler(
|
|
32
|
+
import_pattern=classpath,
|
|
33
|
+
handler_classname=classname,
|
|
34
|
+
)
|
|
35
|
+
result_payload["is_runner_handler"] = isinstance(handler, RunnerHandler)
|
|
36
|
+
result_payload["exists"] = True
|
|
37
|
+
return result_payload
|
|
38
|
+
except Exception as e:
|
|
39
|
+
result_payload["metadata"]["error"] = str(e)
|
|
40
|
+
return result_payload
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import enum
|
|
2
|
+
|
|
3
|
+
from fred.worker.runner.rest.routers.interface import RouterInterface
|
|
4
|
+
from fred.worker.runner.rest.routers._runner import RunnerRouter
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class RouterCatalog(enum.Enum):
|
|
8
|
+
EXAMPLE = RouterInterface.auto() # Default example router directly from the base interface
|
|
9
|
+
RUNNER = RunnerRouter.auto(
|
|
10
|
+
prefix="/runner",
|
|
11
|
+
tags=["Runner"],
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
def get_router_instance(self) -> RouterInterface:
|
|
15
|
+
return self.value.router
|
|
16
|
+
|
|
17
|
+
def get_router_configs(self) -> dict:
|
|
18
|
+
return self.value.router_configs
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from fastapi import APIRouter
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass(frozen=True, slots=False)
|
|
8
|
+
class RouterInterface:
|
|
9
|
+
router: APIRouter
|
|
10
|
+
router_configs: dict
|
|
11
|
+
|
|
12
|
+
@classmethod
|
|
13
|
+
def auto(
|
|
14
|
+
cls,
|
|
15
|
+
router: Optional[APIRouter] = None,
|
|
16
|
+
**kwargs,
|
|
17
|
+
) -> "RouterInterface":
|
|
18
|
+
return cls(
|
|
19
|
+
router=router or APIRouter(),
|
|
20
|
+
router_configs=kwargs,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def __post_init__(self):
|
|
24
|
+
self.router.add_api_route(
|
|
25
|
+
"/ping",
|
|
26
|
+
self.ping,
|
|
27
|
+
methods=["GET"],
|
|
28
|
+
tags=["Health"],
|
|
29
|
+
summary="Ping the server to check if it's alive.",
|
|
30
|
+
response_description="A simple pong response.",
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def ping(self, pong: Optional[str] = None) -> dict:
|
|
34
|
+
from fred.utils.dateops import datetime_utcnow
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
"ping_time": datetime_utcnow().isoformat(),
|
|
38
|
+
"ping_response": pong or "pong",
|
|
39
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from fastapi import FastAPI
|
|
4
|
+
|
|
5
|
+
from fred.settings import logger_manager, get_environ_variable
|
|
6
|
+
from fred.worker.runner.rest.routers.catalog import RouterCatalog
|
|
7
|
+
|
|
8
|
+
logger = logger_manager.get_logger(name=__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True, slots=True)
|
|
12
|
+
class RunnerServer:
|
|
13
|
+
app: FastAPI
|
|
14
|
+
include_routers: list[str]
|
|
15
|
+
exclude_routers: list[str]
|
|
16
|
+
|
|
17
|
+
@classmethod
|
|
18
|
+
def auto(cls, **kwargs) -> "RunnerServer":
|
|
19
|
+
# Include routers by checking on keyword argument or environment variable
|
|
20
|
+
include_routers = kwargs.pop("include_routers", None) or get_environ_variable(
|
|
21
|
+
"FRD_RUNNER_API_INCLUDE_ROUTERS",
|
|
22
|
+
default=""
|
|
23
|
+
)
|
|
24
|
+
if isinstance(include_routers, str):
|
|
25
|
+
include_routers = [
|
|
26
|
+
name.upper()
|
|
27
|
+
for router in include_routers.split(";")
|
|
28
|
+
if (name := router.strip())
|
|
29
|
+
]
|
|
30
|
+
# Exclude routers by checking on keyword argument or environment variable
|
|
31
|
+
exclude_routers = kwargs.pop("exclude_routers", None) or get_environ_variable(
|
|
32
|
+
"FRD_RUNNER_API_EXCLUDE_ROUTERS",
|
|
33
|
+
default=""
|
|
34
|
+
)
|
|
35
|
+
if isinstance(exclude_routers, str):
|
|
36
|
+
exclude_routers = [
|
|
37
|
+
name.upper()
|
|
38
|
+
for router in exclude_routers.split(";")
|
|
39
|
+
if (name := router.strip())
|
|
40
|
+
]
|
|
41
|
+
# Create FastAPI instance
|
|
42
|
+
app_instance = FastAPI(**kwargs)
|
|
43
|
+
return cls(
|
|
44
|
+
app=app_instance,
|
|
45
|
+
include_routers=include_routers,
|
|
46
|
+
exclude_routers=exclude_routers,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
def __post_init__(self):
|
|
50
|
+
logger.info("Attempting to register routers...")
|
|
51
|
+
logger.info("Included routers candidates: %s", self.include_routers or "ALL")
|
|
52
|
+
logger.info("Excluded routers candidates: %s", self.exclude_routers or "NONE")
|
|
53
|
+
for router in RouterCatalog:
|
|
54
|
+
name = router.name.upper()
|
|
55
|
+
if self.include_routers and name not in self.include_routers:
|
|
56
|
+
logger.info(f"Skipping router '{name}' as it's not in the include list.")
|
|
57
|
+
continue
|
|
58
|
+
if self.exclude_routers and name in self.exclude_routers:
|
|
59
|
+
logger.info(f"Skipping router '{name}' as it's in the exclude list.")
|
|
60
|
+
continue
|
|
61
|
+
logger.info(f"Registering router '{name}'.")
|
|
62
|
+
self.app.include_router(
|
|
63
|
+
router.get_router_instance(),
|
|
64
|
+
**router.get_router_configs(),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
def start(self, **kwargs):
|
|
68
|
+
import uvicorn
|
|
69
|
+
|
|
70
|
+
server_kwargs = {
|
|
71
|
+
"host": "localhost",
|
|
72
|
+
"port": 8000,
|
|
73
|
+
"log_level": "info",
|
|
74
|
+
**kwargs,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
uvicorn.run(self.app, **server_kwargs)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: fred-oss
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.14.0
|
|
4
4
|
Summary: FREDOSS
|
|
5
5
|
Home-page: https://fred.fahera.mx
|
|
6
6
|
Author: Fahera Research, Education, and Development
|
|
@@ -11,6 +11,8 @@ License-File: NOTICE.txt
|
|
|
11
11
|
Requires-Dist: fire==0.7.1
|
|
12
12
|
Requires-Dist: psutil==7.0.0
|
|
13
13
|
Requires-Dist: redis==6.4.0
|
|
14
|
+
Requires-Dist: fastapi==0.116.2
|
|
15
|
+
Requires-Dist: uvicorn[standard]==0.35.0
|
|
14
16
|
Dynamic: author
|
|
15
17
|
Dynamic: author-email
|
|
16
18
|
Dynamic: description
|
|
@@ -30,7 +30,19 @@ src/main/fred/worker/interface.py
|
|
|
30
30
|
src/main/fred/worker/runner/__init__.py
|
|
31
31
|
src/main/fred/worker/runner/client.py
|
|
32
32
|
src/main/fred/worker/runner/handler.py
|
|
33
|
+
src/main/fred/worker/runner/info.py
|
|
33
34
|
src/main/fred/worker/runner/utils.py
|
|
35
|
+
src/main/fred/worker/runner/plugins/__init__.py
|
|
36
|
+
src/main/fred/worker/runner/plugins/_local.py
|
|
37
|
+
src/main/fred/worker/runner/plugins/catalog.py
|
|
38
|
+
src/main/fred/worker/runner/plugins/interface.py
|
|
39
|
+
src/main/fred/worker/runner/rest/__init__.py
|
|
40
|
+
src/main/fred/worker/runner/rest/cli_ext.py
|
|
41
|
+
src/main/fred/worker/runner/rest/server.py
|
|
42
|
+
src/main/fred/worker/runner/rest/routers/__init__.py
|
|
43
|
+
src/main/fred/worker/runner/rest/routers/_runner.py
|
|
44
|
+
src/main/fred/worker/runner/rest/routers/catalog.py
|
|
45
|
+
src/main/fred/worker/runner/rest/routers/interface.py
|
|
34
46
|
src/main/fred_oss.egg-info/PKG-INFO
|
|
35
47
|
src/main/fred_oss.egg-info/SOURCES.txt
|
|
36
48
|
src/main/fred_oss.egg-info/dependency_links.txt
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
0.12.0
|
|
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
|
{fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/runtimes/__init__.py
RENAMED
|
File without changes
|
{fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/runtimes/scanner.py
RENAMED
|
File without changes
|
|
File without changes
|
{fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/wrappers/__init__.py
RENAMED
|
File without changes
|
{fred_oss-0.12.0 → fred_oss-0.14.0}/src/main/fred/integrations/databricks/wrappers/dbutils.py
RENAMED
|
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
|