flameox 0.1.1__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 (83) hide show
  1. flameox/__init__.py +10 -0
  2. flameox/__main__.py +4 -0
  3. flameox/adapters/__init__.py +55 -0
  4. flameox/adapters/builtins.py +312 -0
  5. flameox/adapters/client_setup.py +485 -0
  6. flameox/adapters/compatibility.py +49 -0
  7. flameox/adapters/coverage.py +188 -0
  8. flameox/adapters/memray.py +231 -0
  9. flameox/adapters/observations.py +121 -0
  10. flameox/adapters/perfetto.py +573 -0
  11. flameox/adapters/pyperf.py +193 -0
  12. flameox/adapters/registry.py +225 -0
  13. flameox/adapters/setup_runtime.py +201 -0
  14. flameox/analysis/__init__.py +43 -0
  15. flameox/analysis/comparison.py +143 -0
  16. flameox/analysis/recipes.py +1438 -0
  17. flameox/application/__init__.py +201 -0
  18. flameox/application/analysis_provenance.py +115 -0
  19. flameox/application/analysis_records.py +299 -0
  20. flameox/application/analysis_rows.py +35 -0
  21. flameox/application/artifacts.py +141 -0
  22. flameox/application/async_work.py +30 -0
  23. flameox/application/capabilities.py +338 -0
  24. flameox/application/capture.py +1185 -0
  25. flameox/application/compaction.py +77 -0
  26. flameox/application/comparisons.py +746 -0
  27. flameox/application/drilldown.py +365 -0
  28. flameox/application/environment.py +42 -0
  29. flameox/application/evidence_lookup.py +110 -0
  30. flameox/application/evidence_query.py +159 -0
  31. flameox/application/evidence_rows.py +51 -0
  32. flameox/application/execution_policy.py +17 -0
  33. flameox/application/experiments.py +713 -0
  34. flameox/application/gc.py +425 -0
  35. flameox/application/imports.py +141 -0
  36. flameox/application/integrity.py +156 -0
  37. flameox/application/quarantine.py +317 -0
  38. flameox/application/records.py +353 -0
  39. flameox/application/recoverable_move.py +45 -0
  40. flameox/application/recovery.py +125 -0
  41. flameox/application/repair.py +140 -0
  42. flameox/application/run_rows.py +24 -0
  43. flameox/application/setup.py +556 -0
  44. flameox/application/source.py +222 -0
  45. flameox/application/status.py +132 -0
  46. flameox/application/viewers.py +157 -0
  47. flameox/application/workloads.py +368 -0
  48. flameox/catalog.py +398 -0
  49. flameox/cli.py +1767 -0
  50. flameox/collectors/__init__.py +1 -0
  51. flameox/collectors/torch_launcher.py +46 -0
  52. flameox/config.py +66 -0
  53. flameox/domain/__init__.py +98 -0
  54. flameox/domain/cursors.py +78 -0
  55. flameox/domain/errors.py +57 -0
  56. flameox/domain/identity.py +36 -0
  57. flameox/domain/models.py +576 -0
  58. flameox/evidence/__init__.py +11 -0
  59. flameox/evidence/publisher.py +279 -0
  60. flameox/evidence/schemas.py +386 -0
  61. flameox/evidence_scope.py +76 -0
  62. flameox/execution.py +350 -0
  63. flameox/mcp/__init__.py +3 -0
  64. flameox/mcp/server.py +1318 -0
  65. flameox/models.py +19 -0
  66. flameox/observability.py +107 -0
  67. flameox/py.typed +1 -0
  68. flameox/sdk.py +79 -0
  69. flameox/setup_ui.py +126 -0
  70. flameox/storage/__init__.py +27 -0
  71. flameox/storage/artifacts.py +204 -0
  72. flameox/storage/atomic.py +59 -0
  73. flameox/storage/corpus.py +141 -0
  74. flameox/storage/quotas.py +84 -0
  75. flameox/storage/records.py +135 -0
  76. flameox/storage/runs.py +92 -0
  77. flameox/storage/workspace.py +297 -0
  78. flameox/workers/__init__.py +1 -0
  79. flameox/workers/perfetto.py +191 -0
  80. flameox-0.1.1.dist-info/METADATA +420 -0
  81. flameox-0.1.1.dist-info/RECORD +83 -0
  82. flameox-0.1.1.dist-info/WHEEL +4 -0
  83. flameox-0.1.1.dist-info/entry_points.txt +2 -0
flameox/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ """flameox local runtime evidence system."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ __version__ = version("flameox")
7
+ except PackageNotFoundError:
8
+ __version__ = "0.0.0"
9
+
10
+ __all__ = ["__version__"]
flameox/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from flameox.cli import app
2
+
3
+ if __name__ == "__main__":
4
+ app()
@@ -0,0 +1,55 @@
1
+ from flameox.adapters.client_setup import (
2
+ ALL_SETUP_CLIENTS,
3
+ ClientConfigEdit,
4
+ ClientConfigRegistry,
5
+ ClientPlanAction,
6
+ Launcher,
7
+ SetupClient,
8
+ )
9
+ from flameox.adapters.coverage import CoverageExtractionResult, CoverageExtractor
10
+ from flameox.adapters.memray import MemrayExtractionResult, MemrayExtractor
11
+ from flameox.adapters.observations import (
12
+ ObservationExtractionResult,
13
+ ObservationExtractor,
14
+ )
15
+ from flameox.adapters.perfetto import (
16
+ PerfettoExtractionResult,
17
+ PerfettoExtractor,
18
+ TraceEvent,
19
+ TraceWindowResult,
20
+ )
21
+ from flameox.adapters.pyperf import PyPerfExtractionResult, PyPerfExtractor
22
+ from flameox.adapters.registry import (
23
+ AdapterApproval,
24
+ AdapterDescriptor,
25
+ AdapterDiscoveryResult,
26
+ AdapterRegistry,
27
+ )
28
+ from flameox.adapters.setup_runtime import ManagedRuntime, RuntimeInstallation
29
+
30
+ __all__ = [
31
+ "ALL_SETUP_CLIENTS",
32
+ "AdapterApproval",
33
+ "AdapterDescriptor",
34
+ "AdapterDiscoveryResult",
35
+ "AdapterRegistry",
36
+ "ClientConfigEdit",
37
+ "ClientConfigRegistry",
38
+ "ClientPlanAction",
39
+ "CoverageExtractionResult",
40
+ "CoverageExtractor",
41
+ "Launcher",
42
+ "ManagedRuntime",
43
+ "MemrayExtractionResult",
44
+ "MemrayExtractor",
45
+ "ObservationExtractionResult",
46
+ "ObservationExtractor",
47
+ "PerfettoExtractionResult",
48
+ "PerfettoExtractor",
49
+ "PyPerfExtractionResult",
50
+ "PyPerfExtractor",
51
+ "RuntimeInstallation",
52
+ "SetupClient",
53
+ "TraceEvent",
54
+ "TraceWindowResult",
55
+ ]
@@ -0,0 +1,312 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+ from typing import Literal
6
+
7
+ from flameox.domain import ArtifactKind, DomainError, ErrorCode
8
+
9
+ DependencyKind = Literal["internal", "executable", "package"]
10
+
11
+
12
+ @dataclass(frozen=True, slots=True)
13
+ class BuiltinAdapter:
14
+ name: str
15
+ dependency_kind: DependencyKind
16
+ dependency: str | None
17
+ supported_modes: tuple[str, ...]
18
+ supported_formats: tuple[str, ...]
19
+ features: tuple[str, ...] = ()
20
+ remediation: tuple[str, ...] = ()
21
+ version_args: tuple[str, ...] = ()
22
+ permissions: tuple[str, ...] = ()
23
+ restriction_path: Path | None = None
24
+ supported_platforms: tuple[str, ...] | None = None
25
+ output_filename: str | None = None
26
+ artifact_kinds: tuple[ArtifactKind, ...] = ()
27
+ expected_overhead: str | None = None
28
+ capture_limitations: tuple[str, ...] = ()
29
+
30
+
31
+ @dataclass(frozen=True, slots=True)
32
+ class CaptureInvocation:
33
+ argv: tuple[str, ...]
34
+ artifact_kinds: tuple[ArtifactKind, ...]
35
+ expected_overhead: str
36
+ limitations: tuple[str, ...]
37
+
38
+
39
+ BUILTIN_ADAPTERS = {
40
+ adapter.name: adapter
41
+ for adapter in (
42
+ BuiltinAdapter(
43
+ name="command",
44
+ dependency_kind="internal",
45
+ dependency=None,
46
+ supported_modes=("named_workload",),
47
+ supported_formats=("process-output",),
48
+ output_filename="capture.bin",
49
+ artifact_kinds=(ArtifactKind.PROCESS_OUTPUT,),
50
+ expected_overhead="No profiler overhead; process output only.",
51
+ capture_limitations=("No sampled stack or operator evidence is collected.",),
52
+ ),
53
+ BuiltinAdapter(
54
+ name="py-spy",
55
+ dependency_kind="executable",
56
+ dependency="py-spy",
57
+ supported_modes=("record", "attach", "chrome"),
58
+ supported_formats=("speedscope", "chrome-trace", "raw"),
59
+ features=("sampled_stacks", "python_symbols"),
60
+ remediation=("Install py-spy and grant ptrace access for attach mode.",),
61
+ version_args=("--version",),
62
+ permissions=("ptrace",),
63
+ restriction_path=Path("/proc/sys/kernel/yama/ptrace_scope"),
64
+ output_filename="profile.json",
65
+ artifact_kinds=(ArtifactKind.SAMPLE_PROFILE,),
66
+ expected_overhead=(
67
+ "Sampling overhead; exact rate and native-frame scope are collector defaults."
68
+ ),
69
+ capture_limitations=(
70
+ "GIL, native-frame, idle-thread, and subprocess modes are disabled.",
71
+ ),
72
+ ),
73
+ BuiltinAdapter(
74
+ name="perf",
75
+ dependency_kind="executable",
76
+ dependency="perf",
77
+ supported_modes=("record", "stat", "sched"),
78
+ supported_formats=("perf.data",),
79
+ features=("sampled_stacks", "native_symbols"),
80
+ remediation=("Install Linux perf matching the running kernel.",),
81
+ version_args=("--version",),
82
+ restriction_path=Path("/proc/sys/kernel/perf_event_paranoid"),
83
+ supported_platforms=("linux",),
84
+ output_filename="perf.data",
85
+ artifact_kinds=(ArtifactKind.SAMPLE_PROFILE,),
86
+ expected_overhead=(
87
+ "Kernel sampling overhead; symbol coverage depends on build identities."
88
+ ),
89
+ ),
90
+ BuiltinAdapter(
91
+ name="perfetto",
92
+ dependency_kind="executable",
93
+ dependency="trace_processor_shell",
94
+ supported_modes=("import", "query"),
95
+ supported_formats=("perfetto", "chrome-trace", "pprof", "perf.data"),
96
+ features=("trace_sql", "temporal_slices"),
97
+ remediation=("Install Perfetto Trace Processor or configure its local path.",),
98
+ version_args=("--version",),
99
+ ),
100
+ BuiltinAdapter(
101
+ name="pyperf",
102
+ dependency_kind="package",
103
+ dependency="pyperf",
104
+ supported_modes=("import", "benchmark"),
105
+ supported_formats=("pyperf-json",),
106
+ features=("worker_hierarchy", "warmups", "raw_samples"),
107
+ output_filename="benchmark.json",
108
+ artifact_kinds=(ArtifactKind.BENCHMARK_SAMPLES,),
109
+ expected_overhead=(
110
+ "Repeated calibrated process execution; three workers, three values, "
111
+ "and one warm-up per worker."
112
+ ),
113
+ capture_limitations=(
114
+ "Experiment-level treatment randomization is separate from "
115
+ "pyperf's worker hierarchy.",
116
+ ),
117
+ ),
118
+ BuiltinAdapter(
119
+ name="torch.profiler",
120
+ dependency_kind="package",
121
+ dependency="torch",
122
+ supported_modes=("trace_import", "launcher", "sdk"),
123
+ supported_formats=("chrome-trace",),
124
+ features=("operators", "input_shapes", "allocations", "stacks"),
125
+ output_filename="torch-trace.json",
126
+ artifact_kinds=(ArtifactKind.EXECUTION_TRACE,),
127
+ expected_overhead=(
128
+ "Operator tracing with shapes, memory, and Python stacks has substantial overhead."
129
+ ),
130
+ capture_limitations=(
131
+ "Whole-entrypoint mode cannot distinguish application-specific "
132
+ "warm-up and steady-state phases.",
133
+ "FLOP estimates and module hierarchy are not enabled.",
134
+ ),
135
+ ),
136
+ BuiltinAdapter(
137
+ name="memray",
138
+ dependency_kind="package",
139
+ dependency="memray",
140
+ supported_modes=("import", "run"),
141
+ supported_formats=("memray",),
142
+ features=("allocations", "retained_memory", "stacks"),
143
+ output_filename="memory.bin",
144
+ artifact_kinds=(ArtifactKind.MEMORY_PROFILE,),
145
+ expected_overhead=(
146
+ "Allocation tracing overhead; native traces are disabled by default."
147
+ ),
148
+ capture_limitations=(
149
+ "The capture records the main process unless follow-fork is "
150
+ "explicitly added in a future plan mode.",
151
+ ),
152
+ ),
153
+ BuiltinAdapter(
154
+ name="coverage",
155
+ dependency_kind="package",
156
+ dependency="coverage",
157
+ supported_modes=("import", "run"),
158
+ supported_formats=("coverage-data",),
159
+ features=("lines", "branches"),
160
+ output_filename=".coverage",
161
+ artifact_kinds=(ArtifactKind.EXECUTION_COVERAGE,),
162
+ expected_overhead="Tracing overhead; branch collection is enabled.",
163
+ capture_limitations=("Coverage records execution, not values or control-flow causes.",),
164
+ ),
165
+ )
166
+ }
167
+
168
+
169
+ def builtin_adapter(name: str) -> BuiltinAdapter | None:
170
+ return BUILTIN_ADAPTERS.get(name)
171
+
172
+
173
+ def build_capture_invocation(
174
+ adapter_name: str,
175
+ workload_argv: tuple[str, ...],
176
+ output_root: Path,
177
+ *,
178
+ executable: str | None,
179
+ ) -> CaptureInvocation:
180
+ adapter = BUILTIN_ADAPTERS.get(adapter_name)
181
+ if (
182
+ adapter is None
183
+ or not adapter.artifact_kinds
184
+ or adapter.output_filename is None
185
+ or adapter.expected_overhead is None
186
+ ):
187
+ raise DomainError(
188
+ ErrorCode.CAPABILITY_UNAVAILABLE,
189
+ f"Adapter {adapter_name!r} does not support capture planning.",
190
+ )
191
+ output = str(output_root / adapter.output_filename)
192
+ if adapter_name == "command":
193
+ argv = workload_argv
194
+ elif adapter_name == "py-spy":
195
+ argv = (
196
+ _require_executable(adapter_name, executable),
197
+ "record",
198
+ "--format",
199
+ "chrometrace",
200
+ "--output",
201
+ output,
202
+ "--",
203
+ *workload_argv,
204
+ )
205
+ elif adapter_name == "perf":
206
+ argv = (
207
+ _require_executable(adapter_name, executable),
208
+ "record",
209
+ "-o",
210
+ output,
211
+ "--",
212
+ *workload_argv,
213
+ )
214
+ elif adapter_name in {"coverage", "memray", "torch.profiler"}:
215
+ python, target = _python_target(workload_argv)
216
+ if adapter_name == "coverage":
217
+ argv = (
218
+ python,
219
+ "-m",
220
+ "coverage",
221
+ "run",
222
+ "--branch",
223
+ f"--data-file={output}",
224
+ *target,
225
+ )
226
+ elif adapter_name == "memray":
227
+ argv = (
228
+ python,
229
+ "-m",
230
+ "memray",
231
+ "run",
232
+ "--output",
233
+ output,
234
+ *target,
235
+ )
236
+ else:
237
+ if target[0] == "-m":
238
+ if len(target) < 2:
239
+ raise DomainError(
240
+ ErrorCode.INVALID_CAPTURE_PLAN,
241
+ "The Python module name is missing.",
242
+ )
243
+ launcher_target = ("--module", target[1], *target[2:])
244
+ else:
245
+ launcher_target = ("--script", target[0], *target[1:])
246
+ argv = (
247
+ python,
248
+ "-m",
249
+ "flameox.collectors.torch_launcher",
250
+ "--output",
251
+ output,
252
+ *launcher_target,
253
+ )
254
+ elif adapter_name == "pyperf":
255
+ python, _ = _python_target(workload_argv)
256
+ argv = (
257
+ python,
258
+ "-m",
259
+ "pyperf",
260
+ "command",
261
+ "--output",
262
+ output,
263
+ "--processes",
264
+ "3",
265
+ "--values",
266
+ "3",
267
+ "--warmups",
268
+ "1",
269
+ "--name",
270
+ "workload",
271
+ "--",
272
+ *workload_argv,
273
+ )
274
+ else:
275
+ raise DomainError(
276
+ ErrorCode.CAPABILITY_UNAVAILABLE,
277
+ f"Adapter {adapter_name!r} does not support capture planning.",
278
+ )
279
+ return CaptureInvocation(
280
+ argv=argv,
281
+ artifact_kinds=adapter.artifact_kinds,
282
+ expected_overhead=adapter.expected_overhead,
283
+ limitations=adapter.capture_limitations,
284
+ )
285
+
286
+
287
+ def _python_target(
288
+ workload_argv: tuple[str, ...],
289
+ ) -> tuple[str, tuple[str, ...]]:
290
+ executable = Path(workload_argv[0]).name
291
+ if not executable.startswith("python"):
292
+ raise DomainError(
293
+ ErrorCode.INVALID_CAPTURE_PLAN,
294
+ "This adapter can launch only a declared Python script or module.",
295
+ )
296
+ arguments = workload_argv[1:]
297
+ if not arguments or arguments[0] == "-c":
298
+ raise DomainError(
299
+ ErrorCode.INVALID_CAPTURE_PLAN,
300
+ "Inline Python commands cannot be wrapped by this adapter.",
301
+ remediation=("Declare a script or `python -m module` workload.",),
302
+ )
303
+ return workload_argv[0], arguments
304
+
305
+
306
+ def _require_executable(adapter: str, executable: str | None) -> str:
307
+ if executable is None:
308
+ raise DomainError(
309
+ ErrorCode.CAPABILITY_UNAVAILABLE,
310
+ f"Adapter {adapter!r} has no resolved executable.",
311
+ )
312
+ return executable