oscura 0.5.1__py3-none-any.whl → 0.7.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (497) hide show
  1. oscura/__init__.py +169 -167
  2. oscura/analyzers/__init__.py +3 -0
  3. oscura/analyzers/classification.py +659 -0
  4. oscura/analyzers/digital/edges.py +325 -65
  5. oscura/analyzers/digital/quality.py +293 -166
  6. oscura/analyzers/digital/timing.py +260 -115
  7. oscura/analyzers/digital/timing_numba.py +334 -0
  8. oscura/analyzers/entropy.py +605 -0
  9. oscura/analyzers/eye/diagram.py +176 -109
  10. oscura/analyzers/eye/metrics.py +5 -5
  11. oscura/analyzers/jitter/__init__.py +6 -4
  12. oscura/analyzers/jitter/ber.py +52 -52
  13. oscura/analyzers/jitter/classification.py +156 -0
  14. oscura/analyzers/jitter/decomposition.py +163 -113
  15. oscura/analyzers/jitter/spectrum.py +80 -64
  16. oscura/analyzers/ml/__init__.py +39 -0
  17. oscura/analyzers/ml/features.py +600 -0
  18. oscura/analyzers/ml/signal_classifier.py +604 -0
  19. oscura/analyzers/packet/daq.py +246 -158
  20. oscura/analyzers/packet/parser.py +12 -1
  21. oscura/analyzers/packet/payload.py +50 -2110
  22. oscura/analyzers/packet/payload_analysis.py +361 -181
  23. oscura/analyzers/packet/payload_patterns.py +133 -70
  24. oscura/analyzers/packet/stream.py +84 -23
  25. oscura/analyzers/patterns/__init__.py +26 -5
  26. oscura/analyzers/patterns/anomaly_detection.py +908 -0
  27. oscura/analyzers/patterns/clustering.py +169 -108
  28. oscura/analyzers/patterns/clustering_optimized.py +227 -0
  29. oscura/analyzers/patterns/discovery.py +1 -1
  30. oscura/analyzers/patterns/matching.py +581 -197
  31. oscura/analyzers/patterns/pattern_mining.py +778 -0
  32. oscura/analyzers/patterns/periodic.py +121 -38
  33. oscura/analyzers/patterns/sequences.py +175 -78
  34. oscura/analyzers/power/conduction.py +1 -1
  35. oscura/analyzers/power/soa.py +6 -6
  36. oscura/analyzers/power/switching.py +250 -110
  37. oscura/analyzers/protocol/__init__.py +17 -1
  38. oscura/analyzers/protocols/base.py +6 -6
  39. oscura/analyzers/protocols/ble/__init__.py +38 -0
  40. oscura/analyzers/protocols/ble/analyzer.py +809 -0
  41. oscura/analyzers/protocols/ble/uuids.py +288 -0
  42. oscura/analyzers/protocols/can.py +257 -127
  43. oscura/analyzers/protocols/can_fd.py +107 -80
  44. oscura/analyzers/protocols/flexray.py +139 -80
  45. oscura/analyzers/protocols/hdlc.py +93 -58
  46. oscura/analyzers/protocols/i2c.py +247 -106
  47. oscura/analyzers/protocols/i2s.py +138 -86
  48. oscura/analyzers/protocols/industrial/__init__.py +40 -0
  49. oscura/analyzers/protocols/industrial/bacnet/__init__.py +33 -0
  50. oscura/analyzers/protocols/industrial/bacnet/analyzer.py +708 -0
  51. oscura/analyzers/protocols/industrial/bacnet/encoding.py +412 -0
  52. oscura/analyzers/protocols/industrial/bacnet/services.py +622 -0
  53. oscura/analyzers/protocols/industrial/ethercat/__init__.py +30 -0
  54. oscura/analyzers/protocols/industrial/ethercat/analyzer.py +474 -0
  55. oscura/analyzers/protocols/industrial/ethercat/mailbox.py +339 -0
  56. oscura/analyzers/protocols/industrial/ethercat/topology.py +166 -0
  57. oscura/analyzers/protocols/industrial/modbus/__init__.py +31 -0
  58. oscura/analyzers/protocols/industrial/modbus/analyzer.py +525 -0
  59. oscura/analyzers/protocols/industrial/modbus/crc.py +79 -0
  60. oscura/analyzers/protocols/industrial/modbus/functions.py +436 -0
  61. oscura/analyzers/protocols/industrial/opcua/__init__.py +21 -0
  62. oscura/analyzers/protocols/industrial/opcua/analyzer.py +552 -0
  63. oscura/analyzers/protocols/industrial/opcua/datatypes.py +446 -0
  64. oscura/analyzers/protocols/industrial/opcua/services.py +264 -0
  65. oscura/analyzers/protocols/industrial/profinet/__init__.py +23 -0
  66. oscura/analyzers/protocols/industrial/profinet/analyzer.py +441 -0
  67. oscura/analyzers/protocols/industrial/profinet/dcp.py +263 -0
  68. oscura/analyzers/protocols/industrial/profinet/ptcp.py +200 -0
  69. oscura/analyzers/protocols/jtag.py +180 -98
  70. oscura/analyzers/protocols/lin.py +219 -114
  71. oscura/analyzers/protocols/manchester.py +4 -4
  72. oscura/analyzers/protocols/onewire.py +253 -149
  73. oscura/analyzers/protocols/parallel_bus/__init__.py +20 -0
  74. oscura/analyzers/protocols/parallel_bus/centronics.py +92 -0
  75. oscura/analyzers/protocols/parallel_bus/gpib.py +137 -0
  76. oscura/analyzers/protocols/spi.py +192 -95
  77. oscura/analyzers/protocols/swd.py +321 -167
  78. oscura/analyzers/protocols/uart.py +267 -125
  79. oscura/analyzers/protocols/usb.py +235 -131
  80. oscura/analyzers/side_channel/power.py +17 -12
  81. oscura/analyzers/signal/__init__.py +15 -0
  82. oscura/analyzers/signal/timing_analysis.py +1086 -0
  83. oscura/analyzers/signal_integrity/__init__.py +4 -1
  84. oscura/analyzers/signal_integrity/sparams.py +2 -19
  85. oscura/analyzers/spectral/chunked.py +129 -60
  86. oscura/analyzers/spectral/chunked_fft.py +300 -94
  87. oscura/analyzers/spectral/chunked_wavelet.py +100 -80
  88. oscura/analyzers/statistical/checksum.py +376 -217
  89. oscura/analyzers/statistical/classification.py +229 -107
  90. oscura/analyzers/statistical/entropy.py +78 -53
  91. oscura/analyzers/statistics/correlation.py +407 -211
  92. oscura/analyzers/statistics/outliers.py +2 -2
  93. oscura/analyzers/statistics/streaming.py +30 -5
  94. oscura/analyzers/validation.py +216 -101
  95. oscura/analyzers/waveform/measurements.py +9 -0
  96. oscura/analyzers/waveform/measurements_with_uncertainty.py +31 -15
  97. oscura/analyzers/waveform/spectral.py +500 -228
  98. oscura/api/__init__.py +31 -5
  99. oscura/api/dsl/__init__.py +582 -0
  100. oscura/{dsl → api/dsl}/commands.py +43 -76
  101. oscura/{dsl → api/dsl}/interpreter.py +26 -51
  102. oscura/{dsl → api/dsl}/parser.py +107 -77
  103. oscura/{dsl → api/dsl}/repl.py +2 -2
  104. oscura/api/dsl.py +1 -1
  105. oscura/{integrations → api/integrations}/__init__.py +1 -1
  106. oscura/{integrations → api/integrations}/llm.py +201 -102
  107. oscura/api/operators.py +3 -3
  108. oscura/api/optimization.py +144 -30
  109. oscura/api/rest_server.py +921 -0
  110. oscura/api/server/__init__.py +17 -0
  111. oscura/api/server/dashboard.py +850 -0
  112. oscura/api/server/static/README.md +34 -0
  113. oscura/api/server/templates/base.html +181 -0
  114. oscura/api/server/templates/export.html +120 -0
  115. oscura/api/server/templates/home.html +284 -0
  116. oscura/api/server/templates/protocols.html +58 -0
  117. oscura/api/server/templates/reports.html +43 -0
  118. oscura/api/server/templates/session_detail.html +89 -0
  119. oscura/api/server/templates/sessions.html +83 -0
  120. oscura/api/server/templates/waveforms.html +73 -0
  121. oscura/automotive/__init__.py +8 -1
  122. oscura/automotive/can/__init__.py +10 -0
  123. oscura/automotive/can/checksum.py +3 -1
  124. oscura/automotive/can/dbc_generator.py +590 -0
  125. oscura/automotive/can/message_wrapper.py +121 -74
  126. oscura/automotive/can/patterns.py +98 -21
  127. oscura/automotive/can/session.py +292 -56
  128. oscura/automotive/can/state_machine.py +6 -3
  129. oscura/automotive/can/stimulus_response.py +97 -75
  130. oscura/automotive/dbc/__init__.py +10 -2
  131. oscura/automotive/dbc/generator.py +84 -56
  132. oscura/automotive/dbc/parser.py +6 -6
  133. oscura/automotive/dtc/data.json +17 -102
  134. oscura/automotive/dtc/database.py +2 -2
  135. oscura/automotive/flexray/__init__.py +31 -0
  136. oscura/automotive/flexray/analyzer.py +504 -0
  137. oscura/automotive/flexray/crc.py +185 -0
  138. oscura/automotive/flexray/fibex.py +449 -0
  139. oscura/automotive/j1939/__init__.py +45 -8
  140. oscura/automotive/j1939/analyzer.py +605 -0
  141. oscura/automotive/j1939/spns.py +326 -0
  142. oscura/automotive/j1939/transport.py +306 -0
  143. oscura/automotive/lin/__init__.py +47 -0
  144. oscura/automotive/lin/analyzer.py +612 -0
  145. oscura/automotive/loaders/blf.py +13 -2
  146. oscura/automotive/loaders/csv_can.py +143 -72
  147. oscura/automotive/loaders/dispatcher.py +50 -2
  148. oscura/automotive/loaders/mdf.py +86 -45
  149. oscura/automotive/loaders/pcap.py +111 -61
  150. oscura/automotive/uds/__init__.py +4 -0
  151. oscura/automotive/uds/analyzer.py +725 -0
  152. oscura/automotive/uds/decoder.py +140 -58
  153. oscura/automotive/uds/models.py +7 -1
  154. oscura/automotive/visualization.py +1 -1
  155. oscura/cli/analyze.py +348 -0
  156. oscura/cli/batch.py +142 -122
  157. oscura/cli/benchmark.py +275 -0
  158. oscura/cli/characterize.py +137 -82
  159. oscura/cli/compare.py +224 -131
  160. oscura/cli/completion.py +250 -0
  161. oscura/cli/config_cmd.py +361 -0
  162. oscura/cli/decode.py +164 -87
  163. oscura/cli/export.py +286 -0
  164. oscura/cli/main.py +115 -31
  165. oscura/{onboarding → cli/onboarding}/__init__.py +3 -3
  166. oscura/{onboarding → cli/onboarding}/help.py +80 -58
  167. oscura/{onboarding → cli/onboarding}/tutorials.py +97 -72
  168. oscura/{onboarding → cli/onboarding}/wizard.py +55 -36
  169. oscura/cli/progress.py +147 -0
  170. oscura/cli/shell.py +157 -135
  171. oscura/cli/validate_cmd.py +204 -0
  172. oscura/cli/visualize.py +158 -0
  173. oscura/convenience.py +125 -79
  174. oscura/core/__init__.py +4 -2
  175. oscura/core/backend_selector.py +3 -3
  176. oscura/core/cache.py +126 -15
  177. oscura/core/cancellation.py +1 -1
  178. oscura/{config → core/config}/__init__.py +20 -11
  179. oscura/{config → core/config}/defaults.py +1 -1
  180. oscura/{config → core/config}/loader.py +7 -5
  181. oscura/{config → core/config}/memory.py +5 -5
  182. oscura/{config → core/config}/migration.py +1 -1
  183. oscura/{config → core/config}/pipeline.py +99 -23
  184. oscura/{config → core/config}/preferences.py +1 -1
  185. oscura/{config → core/config}/protocol.py +3 -3
  186. oscura/{config → core/config}/schema.py +426 -272
  187. oscura/{config → core/config}/settings.py +1 -1
  188. oscura/{config → core/config}/thresholds.py +195 -153
  189. oscura/core/correlation.py +5 -6
  190. oscura/core/cross_domain.py +0 -2
  191. oscura/core/debug.py +9 -5
  192. oscura/{extensibility → core/extensibility}/docs.py +158 -70
  193. oscura/{extensibility → core/extensibility}/extensions.py +160 -76
  194. oscura/{extensibility → core/extensibility}/logging.py +1 -1
  195. oscura/{extensibility → core/extensibility}/measurements.py +1 -1
  196. oscura/{extensibility → core/extensibility}/plugins.py +1 -1
  197. oscura/{extensibility → core/extensibility}/templates.py +73 -3
  198. oscura/{extensibility → core/extensibility}/validation.py +1 -1
  199. oscura/core/gpu_backend.py +11 -7
  200. oscura/core/log_query.py +101 -11
  201. oscura/core/logging.py +126 -54
  202. oscura/core/logging_advanced.py +5 -5
  203. oscura/core/memory_limits.py +108 -70
  204. oscura/core/memory_monitor.py +2 -2
  205. oscura/core/memory_progress.py +7 -7
  206. oscura/core/memory_warnings.py +1 -1
  207. oscura/core/numba_backend.py +13 -13
  208. oscura/{plugins → core/plugins}/__init__.py +9 -9
  209. oscura/{plugins → core/plugins}/base.py +7 -7
  210. oscura/{plugins → core/plugins}/cli.py +3 -3
  211. oscura/{plugins → core/plugins}/discovery.py +186 -106
  212. oscura/{plugins → core/plugins}/lifecycle.py +1 -1
  213. oscura/{plugins → core/plugins}/manager.py +7 -7
  214. oscura/{plugins → core/plugins}/registry.py +3 -3
  215. oscura/{plugins → core/plugins}/versioning.py +1 -1
  216. oscura/core/progress.py +16 -1
  217. oscura/core/provenance.py +8 -2
  218. oscura/{schemas → core/schemas}/__init__.py +2 -2
  219. oscura/{schemas → core/schemas}/device_mapping.json +2 -8
  220. oscura/{schemas → core/schemas}/packet_format.json +4 -24
  221. oscura/{schemas → core/schemas}/protocol_definition.json +2 -12
  222. oscura/core/types.py +4 -0
  223. oscura/core/uncertainty.py +3 -3
  224. oscura/correlation/__init__.py +52 -0
  225. oscura/correlation/multi_protocol.py +811 -0
  226. oscura/discovery/auto_decoder.py +117 -35
  227. oscura/discovery/comparison.py +191 -86
  228. oscura/discovery/quality_validator.py +155 -68
  229. oscura/discovery/signal_detector.py +196 -79
  230. oscura/export/__init__.py +18 -8
  231. oscura/export/kaitai_struct.py +513 -0
  232. oscura/export/scapy_layer.py +801 -0
  233. oscura/export/wireshark/generator.py +1 -1
  234. oscura/export/wireshark/templates/dissector.lua.j2 +2 -2
  235. oscura/export/wireshark_dissector.py +746 -0
  236. oscura/guidance/wizard.py +207 -111
  237. oscura/hardware/__init__.py +19 -0
  238. oscura/{acquisition → hardware/acquisition}/__init__.py +4 -4
  239. oscura/{acquisition → hardware/acquisition}/file.py +2 -2
  240. oscura/{acquisition → hardware/acquisition}/hardware.py +7 -7
  241. oscura/{acquisition → hardware/acquisition}/saleae.py +15 -12
  242. oscura/{acquisition → hardware/acquisition}/socketcan.py +1 -1
  243. oscura/{acquisition → hardware/acquisition}/streaming.py +2 -2
  244. oscura/{acquisition → hardware/acquisition}/synthetic.py +3 -3
  245. oscura/{acquisition → hardware/acquisition}/visa.py +33 -11
  246. oscura/hardware/firmware/__init__.py +29 -0
  247. oscura/hardware/firmware/pattern_recognition.py +874 -0
  248. oscura/hardware/hal_detector.py +736 -0
  249. oscura/hardware/security/__init__.py +37 -0
  250. oscura/hardware/security/side_channel_detector.py +1126 -0
  251. oscura/inference/__init__.py +4 -0
  252. oscura/inference/active_learning/observation_table.py +4 -1
  253. oscura/inference/alignment.py +216 -123
  254. oscura/inference/bayesian.py +113 -33
  255. oscura/inference/crc_reverse.py +101 -55
  256. oscura/inference/logic.py +6 -2
  257. oscura/inference/message_format.py +342 -183
  258. oscura/inference/protocol.py +95 -44
  259. oscura/inference/protocol_dsl.py +180 -82
  260. oscura/inference/signal_intelligence.py +1439 -706
  261. oscura/inference/spectral.py +99 -57
  262. oscura/inference/state_machine.py +810 -158
  263. oscura/inference/stream.py +270 -110
  264. oscura/iot/__init__.py +34 -0
  265. oscura/iot/coap/__init__.py +32 -0
  266. oscura/iot/coap/analyzer.py +668 -0
  267. oscura/iot/coap/options.py +212 -0
  268. oscura/iot/lorawan/__init__.py +21 -0
  269. oscura/iot/lorawan/crypto.py +206 -0
  270. oscura/iot/lorawan/decoder.py +801 -0
  271. oscura/iot/lorawan/mac_commands.py +341 -0
  272. oscura/iot/mqtt/__init__.py +27 -0
  273. oscura/iot/mqtt/analyzer.py +999 -0
  274. oscura/iot/mqtt/properties.py +315 -0
  275. oscura/iot/zigbee/__init__.py +31 -0
  276. oscura/iot/zigbee/analyzer.py +615 -0
  277. oscura/iot/zigbee/security.py +153 -0
  278. oscura/iot/zigbee/zcl.py +349 -0
  279. oscura/jupyter/display.py +125 -45
  280. oscura/{exploratory → jupyter/exploratory}/__init__.py +8 -8
  281. oscura/{exploratory → jupyter/exploratory}/error_recovery.py +298 -141
  282. oscura/jupyter/exploratory/fuzzy.py +746 -0
  283. oscura/{exploratory → jupyter/exploratory}/fuzzy_advanced.py +258 -100
  284. oscura/{exploratory → jupyter/exploratory}/legacy.py +464 -242
  285. oscura/{exploratory → jupyter/exploratory}/parse.py +167 -145
  286. oscura/{exploratory → jupyter/exploratory}/recovery.py +119 -87
  287. oscura/jupyter/exploratory/sync.py +612 -0
  288. oscura/{exploratory → jupyter/exploratory}/unknown.py +299 -176
  289. oscura/jupyter/magic.py +4 -4
  290. oscura/{ui → jupyter/ui}/__init__.py +2 -2
  291. oscura/{ui → jupyter/ui}/formatters.py +3 -3
  292. oscura/{ui → jupyter/ui}/progressive_display.py +153 -82
  293. oscura/loaders/__init__.py +183 -67
  294. oscura/loaders/binary.py +88 -1
  295. oscura/loaders/chipwhisperer.py +153 -137
  296. oscura/loaders/configurable.py +208 -86
  297. oscura/loaders/csv_loader.py +458 -215
  298. oscura/loaders/hdf5_loader.py +278 -119
  299. oscura/loaders/lazy.py +87 -54
  300. oscura/loaders/mmap_loader.py +1 -1
  301. oscura/loaders/numpy_loader.py +253 -116
  302. oscura/loaders/pcap.py +226 -151
  303. oscura/loaders/rigol.py +110 -49
  304. oscura/loaders/sigrok.py +201 -78
  305. oscura/loaders/tdms.py +81 -58
  306. oscura/loaders/tektronix.py +291 -174
  307. oscura/loaders/touchstone.py +182 -87
  308. oscura/loaders/tss.py +456 -0
  309. oscura/loaders/vcd.py +215 -117
  310. oscura/loaders/wav.py +155 -68
  311. oscura/reporting/__init__.py +9 -0
  312. oscura/reporting/analyze.py +352 -146
  313. oscura/reporting/argument_preparer.py +69 -14
  314. oscura/reporting/auto_report.py +97 -61
  315. oscura/reporting/batch.py +131 -58
  316. oscura/reporting/chart_selection.py +57 -45
  317. oscura/reporting/comparison.py +63 -17
  318. oscura/reporting/content/executive.py +76 -24
  319. oscura/reporting/core_formats/multi_format.py +11 -8
  320. oscura/reporting/engine.py +312 -158
  321. oscura/reporting/enhanced_reports.py +949 -0
  322. oscura/reporting/export.py +86 -43
  323. oscura/reporting/formatting/numbers.py +69 -42
  324. oscura/reporting/html.py +139 -58
  325. oscura/reporting/index.py +137 -65
  326. oscura/reporting/output.py +158 -67
  327. oscura/reporting/pdf.py +67 -102
  328. oscura/reporting/plots.py +191 -112
  329. oscura/reporting/sections.py +88 -47
  330. oscura/reporting/standards.py +104 -61
  331. oscura/reporting/summary_generator.py +75 -55
  332. oscura/reporting/tables.py +138 -54
  333. oscura/reporting/templates/enhanced/protocol_re.html +525 -0
  334. oscura/sessions/__init__.py +14 -23
  335. oscura/sessions/base.py +3 -3
  336. oscura/sessions/blackbox.py +106 -10
  337. oscura/sessions/generic.py +2 -2
  338. oscura/sessions/legacy.py +783 -0
  339. oscura/side_channel/__init__.py +63 -0
  340. oscura/side_channel/dpa.py +1025 -0
  341. oscura/utils/__init__.py +15 -1
  342. oscura/utils/bitwise.py +118 -0
  343. oscura/{builders → utils/builders}/__init__.py +1 -1
  344. oscura/{comparison → utils/comparison}/__init__.py +6 -6
  345. oscura/{comparison → utils/comparison}/compare.py +202 -101
  346. oscura/{comparison → utils/comparison}/golden.py +83 -63
  347. oscura/{comparison → utils/comparison}/limits.py +313 -89
  348. oscura/{comparison → utils/comparison}/mask.py +151 -45
  349. oscura/{comparison → utils/comparison}/trace_diff.py +1 -1
  350. oscura/{comparison → utils/comparison}/visualization.py +147 -89
  351. oscura/{component → utils/component}/__init__.py +3 -3
  352. oscura/{component → utils/component}/impedance.py +122 -58
  353. oscura/{component → utils/component}/reactive.py +165 -168
  354. oscura/{component → utils/component}/transmission_line.py +3 -3
  355. oscura/{filtering → utils/filtering}/__init__.py +6 -6
  356. oscura/{filtering → utils/filtering}/base.py +1 -1
  357. oscura/{filtering → utils/filtering}/convenience.py +2 -2
  358. oscura/{filtering → utils/filtering}/design.py +169 -93
  359. oscura/{filtering → utils/filtering}/filters.py +2 -2
  360. oscura/{filtering → utils/filtering}/introspection.py +2 -2
  361. oscura/utils/geometry.py +31 -0
  362. oscura/utils/imports.py +184 -0
  363. oscura/utils/lazy.py +1 -1
  364. oscura/{math → utils/math}/__init__.py +2 -2
  365. oscura/{math → utils/math}/arithmetic.py +114 -48
  366. oscura/{math → utils/math}/interpolation.py +139 -106
  367. oscura/utils/memory.py +129 -66
  368. oscura/utils/memory_advanced.py +92 -9
  369. oscura/utils/memory_extensions.py +10 -8
  370. oscura/{optimization → utils/optimization}/__init__.py +1 -1
  371. oscura/{optimization → utils/optimization}/search.py +2 -2
  372. oscura/utils/performance/__init__.py +58 -0
  373. oscura/utils/performance/caching.py +889 -0
  374. oscura/utils/performance/lsh_clustering.py +333 -0
  375. oscura/utils/performance/memory_optimizer.py +699 -0
  376. oscura/utils/performance/optimizations.py +675 -0
  377. oscura/utils/performance/parallel.py +654 -0
  378. oscura/utils/performance/profiling.py +661 -0
  379. oscura/{pipeline → utils/pipeline}/base.py +1 -1
  380. oscura/{pipeline → utils/pipeline}/composition.py +1 -1
  381. oscura/{pipeline → utils/pipeline}/parallel.py +3 -2
  382. oscura/{pipeline → utils/pipeline}/pipeline.py +1 -1
  383. oscura/{pipeline → utils/pipeline}/reverse_engineering.py +412 -221
  384. oscura/{search → utils/search}/__init__.py +3 -3
  385. oscura/{search → utils/search}/anomaly.py +188 -58
  386. oscura/utils/search/context.py +294 -0
  387. oscura/{search → utils/search}/pattern.py +138 -10
  388. oscura/utils/serial.py +51 -0
  389. oscura/utils/storage/__init__.py +61 -0
  390. oscura/utils/storage/database.py +1166 -0
  391. oscura/{streaming → utils/streaming}/chunked.py +302 -143
  392. oscura/{streaming → utils/streaming}/progressive.py +1 -1
  393. oscura/{streaming → utils/streaming}/realtime.py +3 -2
  394. oscura/{triggering → utils/triggering}/__init__.py +6 -6
  395. oscura/{triggering → utils/triggering}/base.py +6 -6
  396. oscura/{triggering → utils/triggering}/edge.py +2 -2
  397. oscura/{triggering → utils/triggering}/pattern.py +2 -2
  398. oscura/{triggering → utils/triggering}/pulse.py +115 -74
  399. oscura/{triggering → utils/triggering}/window.py +2 -2
  400. oscura/utils/validation.py +32 -0
  401. oscura/validation/__init__.py +121 -0
  402. oscura/{compliance → validation/compliance}/__init__.py +5 -5
  403. oscura/{compliance → validation/compliance}/advanced.py +5 -5
  404. oscura/{compliance → validation/compliance}/masks.py +1 -1
  405. oscura/{compliance → validation/compliance}/reporting.py +127 -53
  406. oscura/{compliance → validation/compliance}/testing.py +114 -52
  407. oscura/validation/compliance_tests.py +915 -0
  408. oscura/validation/fuzzer.py +990 -0
  409. oscura/validation/grammar_tests.py +596 -0
  410. oscura/validation/grammar_validator.py +904 -0
  411. oscura/validation/hil_testing.py +977 -0
  412. oscura/{quality → validation/quality}/__init__.py +4 -4
  413. oscura/{quality → validation/quality}/ensemble.py +251 -171
  414. oscura/{quality → validation/quality}/explainer.py +3 -3
  415. oscura/{quality → validation/quality}/scoring.py +1 -1
  416. oscura/{quality → validation/quality}/warnings.py +4 -4
  417. oscura/validation/regression_suite.py +808 -0
  418. oscura/validation/replay.py +788 -0
  419. oscura/{testing → validation/testing}/__init__.py +2 -2
  420. oscura/{testing → validation/testing}/synthetic.py +5 -5
  421. oscura/visualization/__init__.py +9 -0
  422. oscura/visualization/accessibility.py +1 -1
  423. oscura/visualization/annotations.py +64 -67
  424. oscura/visualization/colors.py +7 -7
  425. oscura/visualization/digital.py +180 -81
  426. oscura/visualization/eye.py +236 -85
  427. oscura/visualization/interactive.py +320 -143
  428. oscura/visualization/jitter.py +587 -247
  429. oscura/visualization/layout.py +169 -134
  430. oscura/visualization/optimization.py +103 -52
  431. oscura/visualization/palettes.py +1 -1
  432. oscura/visualization/power.py +427 -211
  433. oscura/visualization/power_extended.py +626 -297
  434. oscura/visualization/presets.py +2 -0
  435. oscura/visualization/protocols.py +495 -181
  436. oscura/visualization/render.py +79 -63
  437. oscura/visualization/reverse_engineering.py +171 -124
  438. oscura/visualization/signal_integrity.py +460 -279
  439. oscura/visualization/specialized.py +190 -100
  440. oscura/visualization/spectral.py +670 -255
  441. oscura/visualization/thumbnails.py +166 -137
  442. oscura/visualization/waveform.py +150 -63
  443. oscura/workflows/__init__.py +3 -0
  444. oscura/{batch → workflows/batch}/__init__.py +5 -5
  445. oscura/{batch → workflows/batch}/advanced.py +150 -75
  446. oscura/workflows/batch/aggregate.py +531 -0
  447. oscura/workflows/batch/analyze.py +236 -0
  448. oscura/{batch → workflows/batch}/logging.py +2 -2
  449. oscura/{batch → workflows/batch}/metrics.py +1 -1
  450. oscura/workflows/complete_re.py +1144 -0
  451. oscura/workflows/compliance.py +44 -54
  452. oscura/workflows/digital.py +197 -51
  453. oscura/workflows/legacy/__init__.py +12 -0
  454. oscura/{workflow → workflows/legacy}/dag.py +4 -1
  455. oscura/workflows/multi_trace.py +9 -9
  456. oscura/workflows/power.py +42 -62
  457. oscura/workflows/protocol.py +82 -49
  458. oscura/workflows/reverse_engineering.py +351 -150
  459. oscura/workflows/signal_integrity.py +157 -82
  460. oscura-0.7.0.dist-info/METADATA +661 -0
  461. oscura-0.7.0.dist-info/RECORD +591 -0
  462. oscura/batch/aggregate.py +0 -300
  463. oscura/batch/analyze.py +0 -139
  464. oscura/dsl/__init__.py +0 -73
  465. oscura/exceptions.py +0 -59
  466. oscura/exploratory/fuzzy.py +0 -513
  467. oscura/exploratory/sync.py +0 -384
  468. oscura/exporters/__init__.py +0 -94
  469. oscura/exporters/csv.py +0 -303
  470. oscura/exporters/exporters.py +0 -44
  471. oscura/exporters/hdf5.py +0 -217
  472. oscura/exporters/html_export.py +0 -701
  473. oscura/exporters/json_export.py +0 -291
  474. oscura/exporters/markdown_export.py +0 -367
  475. oscura/exporters/matlab_export.py +0 -354
  476. oscura/exporters/npz_export.py +0 -219
  477. oscura/exporters/spice_export.py +0 -210
  478. oscura/search/context.py +0 -149
  479. oscura/session/__init__.py +0 -34
  480. oscura/session/annotations.py +0 -289
  481. oscura/session/history.py +0 -313
  482. oscura/session/session.py +0 -520
  483. oscura/workflow/__init__.py +0 -13
  484. oscura-0.5.1.dist-info/METADATA +0 -583
  485. oscura-0.5.1.dist-info/RECORD +0 -481
  486. /oscura/core/{config.py → config/legacy.py} +0 -0
  487. /oscura/{extensibility → core/extensibility}/__init__.py +0 -0
  488. /oscura/{extensibility → core/extensibility}/registry.py +0 -0
  489. /oscura/{plugins → core/plugins}/isolation.py +0 -0
  490. /oscura/{schemas → core/schemas}/bus_configuration.json +0 -0
  491. /oscura/{builders → utils/builders}/signal_builder.py +0 -0
  492. /oscura/{optimization → utils/optimization}/parallel.py +0 -0
  493. /oscura/{pipeline → utils/pipeline}/__init__.py +0 -0
  494. /oscura/{streaming → utils/streaming}/__init__.py +0 -0
  495. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/WHEEL +0 -0
  496. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/entry_points.txt +0 -0
  497. {oscura-0.5.1.dist-info → oscura-0.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -7,7 +7,7 @@ analysis and assistance.
7
7
  Examples:
8
8
  Basic usage with auto-selection:
9
9
 
10
- >>> from oscura.integrations import llm
10
+ >>> from oscura.api.integrations import llm
11
11
  >>> client = llm.get_client() # Auto-selects available provider
12
12
  >>> response = client.chat_completion("What is signal rise time?")
13
13
 
@@ -428,7 +428,7 @@ class LLMIntegration:
428
428
  try:
429
429
  provider_enum = LLMProvider(provider.lower())
430
430
  except ValueError:
431
- raise LLMError(f"Unknown provider: {provider}") # noqa: B904
431
+ raise LLMError(f"Unknown provider: {provider}")
432
432
 
433
433
  self.config = LLMConfig(
434
434
  provider=provider_enum,
@@ -482,11 +482,9 @@ class LLMIntegration:
482
482
  LLMError: If OpenAI package not available or configuration invalid
483
483
  """
484
484
  try:
485
- import openai # type: ignore[import-not-found]
485
+ import openai # type: ignore[import-not-found] # noqa: F401
486
486
  except ImportError:
487
- raise LLMError( # noqa: B904
488
- "OpenAI package not installed. Install with: pip install openai"
489
- )
487
+ raise LLMError("OpenAI package not installed. Install with: pip install openai")
490
488
 
491
489
  if not self.config.api_key:
492
490
  raise LLMError("OpenAI API key required")
@@ -506,11 +504,9 @@ class LLMIntegration:
506
504
  LLMError: If Anthropic package not available or configuration invalid
507
505
  """
508
506
  try:
509
- import anthropic # type: ignore[import-not-found]
507
+ import anthropic # type: ignore[import-not-found] # noqa: F401
510
508
  except ImportError:
511
- raise LLMError( # noqa: B904
512
- "Anthropic package not installed. Install with: pip install anthropic"
513
- )
509
+ raise LLMError("Anthropic package not installed. Install with: pip install anthropic")
514
510
 
515
511
  if not self.config.api_key:
516
512
  raise LLMError("Anthropic API key required")
@@ -620,7 +616,7 @@ class LLMIntegration:
620
616
 
621
617
  except Exception as e:
622
618
  self.trigger_hook(AnalysisHook.ON_ERROR, trace, question, e)
623
- raise LLMError(f"LLM analysis failed: {e}") # noqa: B904
619
+ raise LLMError(f"LLM analysis failed: {e}")
624
620
 
625
621
  def explain(self, measurement: Any) -> str:
626
622
  """Explain a measurement result.
@@ -663,13 +659,11 @@ class OpenAIClient:
663
659
 
664
660
  # Import and initialize OpenAI client
665
661
  try:
666
- import openai # type: ignore[ignore-without-code]
662
+ import openai
667
663
 
668
664
  self._openai = openai
669
665
  except ImportError:
670
- raise LLMError( # noqa: B904
671
- "OpenAI package not installed. Install with: pip install openai"
672
- )
666
+ raise LLMError("OpenAI package not installed. Install with: pip install openai")
673
667
 
674
668
  # Get API key from config or environment
675
669
  api_key = config.api_key or os.environ.get("OPENAI_API_KEY")
@@ -746,25 +740,25 @@ class OpenAIClient:
746
740
  wait_time = 2**attempt
747
741
  time.sleep(wait_time)
748
742
  continue
749
- raise LLMError(f"OpenAI rate limit exceeded: {e}") # noqa: B904
743
+ raise LLMError(f"OpenAI rate limit exceeded: {e}")
750
744
 
751
745
  except self._openai.APITimeoutError as e:
752
746
  last_exception = e
753
747
  if attempt < self.config.max_retries - 1:
754
748
  time.sleep(1)
755
749
  continue
756
- raise LLMError(f"OpenAI request timeout: {e}") # noqa: B904
750
+ raise LLMError(f"OpenAI request timeout: {e}")
757
751
 
758
752
  except self._openai.APIError as e:
759
753
  last_exception = e
760
754
  if attempt < self.config.max_retries - 1:
761
755
  time.sleep(1)
762
756
  continue
763
- raise LLMError(f"OpenAI API error: {e}") # noqa: B904
757
+ raise LLMError(f"OpenAI API error: {e}")
764
758
 
765
759
  except Exception as e:
766
760
  last_exception = e
767
- raise LLMError(f"OpenAI request failed: {e}") # noqa: B904
761
+ raise LLMError(f"OpenAI request failed: {e}")
768
762
 
769
763
  raise LLMError(
770
764
  f"OpenAI request failed after {self.config.max_retries} retries: {last_exception}"
@@ -967,6 +961,77 @@ class OpenAIClient:
967
961
  return response.answer
968
962
 
969
963
 
964
+ def _convert_anthropic_messages(
965
+ messages: list[dict[str, str]], system: str | None
966
+ ) -> tuple[list[dict[str, str]], str | None]:
967
+ """Convert messages to Anthropic format (separate system from user messages).
968
+
969
+ Args:
970
+ messages: Original messages with mixed roles.
971
+ system: Optional system prompt.
972
+
973
+ Returns:
974
+ Tuple of (user_messages, system_message).
975
+ """
976
+ user_messages = []
977
+ system_message = system
978
+
979
+ for msg in messages:
980
+ if msg["role"] == "system" and not system_message:
981
+ system_message = msg["content"]
982
+ elif msg["role"] in ["user", "assistant"]:
983
+ user_messages.append(msg)
984
+
985
+ return (user_messages, system_message)
986
+
987
+
988
+ def _extract_anthropic_answer(response: Any) -> str:
989
+ """Extract text answer from Anthropic response.
990
+
991
+ Args:
992
+ response: Anthropic API response.
993
+
994
+ Returns:
995
+ Concatenated text from all content blocks.
996
+ """
997
+ answer = ""
998
+ for block in response.content:
999
+ if hasattr(block, "text"):
1000
+ answer += block.text
1001
+ return answer
1002
+
1003
+
1004
+ def _build_anthropic_response(response: Any, answer: str, estimated_cost: float) -> LLMResponse:
1005
+ """Build LLMResponse from Anthropic API response.
1006
+
1007
+ Args:
1008
+ response: Anthropic API response.
1009
+ answer: Extracted answer text.
1010
+ estimated_cost: Estimated API cost.
1011
+
1012
+ Returns:
1013
+ LLMResponse with metadata.
1014
+ """
1015
+ return LLMResponse(
1016
+ answer=answer,
1017
+ confidence=None, # Anthropic doesn't provide confidence scores
1018
+ suggested_commands=[],
1019
+ metadata={
1020
+ "model": response.model,
1021
+ "usage": {
1022
+ "input_tokens": response.usage.input_tokens,
1023
+ "output_tokens": response.usage.output_tokens,
1024
+ },
1025
+ "stop_reason": response.stop_reason,
1026
+ },
1027
+ raw_response={
1028
+ "id": response.id,
1029
+ "type": response.type,
1030
+ },
1031
+ estimated_cost=estimated_cost,
1032
+ )
1033
+
1034
+
970
1035
  class AnthropicClient:
971
1036
  """Anthropic client implementation.
972
1037
 
@@ -991,13 +1056,11 @@ class AnthropicClient:
991
1056
 
992
1057
  # Import and initialize Anthropic client
993
1058
  try:
994
- import anthropic # type: ignore[ignore-without-code]
1059
+ import anthropic
995
1060
 
996
1061
  self._anthropic = anthropic
997
1062
  except ImportError:
998
- raise LLMError( # noqa: B904
999
- "Anthropic package not installed. Install with: pip install anthropic"
1000
- )
1063
+ raise LLMError("Anthropic package not installed. Install with: pip install anthropic")
1001
1064
 
1002
1065
  # Get API key from config or environment
1003
1066
  api_key = config.api_key or os.environ.get("ANTHROPIC_API_KEY")
@@ -1030,100 +1093,138 @@ class AnthropicClient:
1030
1093
  """
1031
1094
  self.rate_limiter.acquire()
1032
1095
 
1033
- # Convert messages format (filter out system messages for Anthropic)
1034
- user_messages = []
1035
- system_message = system
1036
- for msg in messages:
1037
- if msg["role"] == "system" and not system_message:
1038
- system_message = msg["content"]
1039
- elif msg["role"] in ["user", "assistant"]:
1040
- user_messages.append(msg)
1096
+ # Convert messages format for Anthropic
1097
+ user_messages, system_message = _convert_anthropic_messages(messages, system)
1041
1098
 
1099
+ # Retry loop with exponential backoff
1042
1100
  last_exception = None
1043
1101
  for attempt in range(self.config.max_retries):
1044
1102
  try:
1045
- # Build request parameters
1046
- request_params = {
1047
- "model": self.config.model,
1048
- "messages": user_messages,
1049
- "max_tokens": kwargs.get("max_tokens", 1024),
1050
- }
1051
- if system_message:
1052
- request_params["system"] = system_message
1103
+ # Build and send request
1104
+ response = self._send_anthropic_request(user_messages, system_message, kwargs)
1053
1105
 
1054
- # Add any additional kwargs
1055
- for key in ["temperature", "top_p", "top_k"]:
1056
- if key in kwargs:
1057
- request_params[key] = kwargs[key]
1106
+ # Extract answer from response
1107
+ answer = _extract_anthropic_answer(response)
1058
1108
 
1059
- response = self.client.messages.create(**request_params)
1060
-
1061
- # Extract response content
1062
- answer = ""
1063
- for block in response.content:
1064
- if hasattr(block, "text"):
1065
- answer += block.text
1066
-
1067
- # Track costs./API-020
1068
- input_tokens = response.usage.input_tokens
1069
- output_tokens = response.usage.output_tokens
1070
- estimated_cost = 0.0
1109
+ # Track token usage and costs
1110
+ estimated_cost = self._track_anthropic_costs(response)
1071
1111
 
1072
- if self.config.track_costs:
1073
- estimated_cost = _global_cost_tracker.record(
1074
- response.model, input_tokens, output_tokens
1075
- )
1076
-
1077
- return LLMResponse(
1078
- answer=answer,
1079
- confidence=None, # Anthropic doesn't provide confidence scores
1080
- suggested_commands=[],
1081
- metadata={
1082
- "model": response.model,
1083
- "usage": {
1084
- "input_tokens": input_tokens,
1085
- "output_tokens": output_tokens,
1086
- },
1087
- "stop_reason": response.stop_reason,
1088
- },
1089
- raw_response={
1090
- "id": response.id,
1091
- "type": response.type,
1092
- },
1093
- estimated_cost=estimated_cost,
1094
- )
1112
+ # Build and return LLM response
1113
+ return _build_anthropic_response(response, answer, estimated_cost)
1095
1114
 
1096
1115
  except self._anthropic.RateLimitError as e:
1097
1116
  last_exception = e
1098
- if attempt < self.config.max_retries - 1:
1099
- # Exponential backoff for rate limits
1100
- wait_time = 2**attempt
1101
- time.sleep(wait_time)
1102
- continue
1103
- raise LLMError(f"Anthropic rate limit exceeded: {e}") # noqa: B904
1117
+ if not self._handle_rate_limit_retry(attempt):
1118
+ raise LLMError(f"Anthropic rate limit exceeded: {e}")
1104
1119
 
1105
1120
  except self._anthropic.APITimeoutError as e:
1106
1121
  last_exception = e
1107
- if attempt < self.config.max_retries - 1:
1108
- time.sleep(1)
1109
- continue
1110
- raise LLMError(f"Anthropic request timeout: {e}") # noqa: B904
1122
+ if not self._handle_timeout_retry(attempt):
1123
+ raise LLMError(f"Anthropic request timeout: {e}")
1111
1124
 
1112
1125
  except self._anthropic.APIError as e:
1113
1126
  last_exception = e
1114
- if attempt < self.config.max_retries - 1:
1115
- time.sleep(1)
1116
- continue
1117
- raise LLMError(f"Anthropic API error: {e}") # noqa: B904
1127
+ if not self._handle_api_error_retry(attempt):
1128
+ raise LLMError(f"Anthropic API error: {e}")
1118
1129
 
1119
1130
  except Exception as e:
1120
1131
  last_exception = e
1121
- raise LLMError(f"Anthropic request failed: {e}") # noqa: B904
1132
+ raise LLMError(f"Anthropic request failed: {e}")
1122
1133
 
1123
1134
  raise LLMError(
1124
1135
  f"Anthropic request failed after {self.config.max_retries} retries: {last_exception}"
1125
1136
  )
1126
1137
 
1138
+ def _send_anthropic_request(
1139
+ self,
1140
+ user_messages: list[dict[str, str]],
1141
+ system_message: str | None,
1142
+ kwargs: dict[str, Any],
1143
+ ) -> Any:
1144
+ """Send request to Anthropic API.
1145
+
1146
+ Args:
1147
+ user_messages: Filtered user/assistant messages.
1148
+ system_message: System prompt.
1149
+ kwargs: Additional API parameters.
1150
+
1151
+ Returns:
1152
+ Anthropic API response.
1153
+ """
1154
+ request_params = {
1155
+ "model": self.config.model,
1156
+ "messages": user_messages,
1157
+ "max_tokens": kwargs.get("max_tokens", 1024),
1158
+ }
1159
+ if system_message:
1160
+ request_params["system"] = system_message
1161
+
1162
+ # Add optional parameters
1163
+ for key in ["temperature", "top_p", "top_k"]:
1164
+ if key in kwargs:
1165
+ request_params[key] = kwargs[key]
1166
+
1167
+ return self.client.messages.create(**request_params)
1168
+
1169
+ def _track_anthropic_costs(self, response: Any) -> float:
1170
+ """Track token usage and return estimated cost.
1171
+
1172
+ Args:
1173
+ response: Anthropic API response.
1174
+
1175
+ Returns:
1176
+ Estimated cost in USD.
1177
+ """
1178
+ input_tokens = response.usage.input_tokens
1179
+ output_tokens = response.usage.output_tokens
1180
+
1181
+ if self.config.track_costs:
1182
+ return _global_cost_tracker.record(response.model, input_tokens, output_tokens)
1183
+ return 0.0
1184
+
1185
+ def _handle_rate_limit_retry(self, attempt: int) -> bool:
1186
+ """Handle rate limit error with exponential backoff.
1187
+
1188
+ Args:
1189
+ attempt: Current attempt number.
1190
+
1191
+ Returns:
1192
+ True if should continue retry, False if should raise.
1193
+ """
1194
+ if attempt < self.config.max_retries - 1:
1195
+ wait_time = 2**attempt
1196
+ time.sleep(wait_time)
1197
+ return True
1198
+ return False
1199
+
1200
+ def _handle_timeout_retry(self, attempt: int) -> bool:
1201
+ """Handle timeout error with retry.
1202
+
1203
+ Args:
1204
+ attempt: Current attempt number.
1205
+
1206
+ Returns:
1207
+ True if should continue retry, False if should raise.
1208
+ """
1209
+ if attempt < self.config.max_retries - 1:
1210
+ time.sleep(1)
1211
+ return True
1212
+ return False
1213
+
1214
+ def _handle_api_error_retry(self, attempt: int) -> bool:
1215
+ """Handle API error with retry.
1216
+
1217
+ Args:
1218
+ attempt: Current attempt number.
1219
+
1220
+ Returns:
1221
+ True if should continue retry, False if should raise.
1222
+ """
1223
+ if attempt < self.config.max_retries - 1:
1224
+ time.sleep(1)
1225
+ return True
1226
+ return False
1227
+
1127
1228
  def analyze_trace(self, trace: Any, question: str) -> LLMResponse:
1128
1229
  """Analyze trace with question.
1129
1230
 
@@ -1384,9 +1485,7 @@ def get_provider(name: str, **config_kwargs: Any) -> LLMClient:
1384
1485
  try:
1385
1486
  provider_enum = LLMProvider(name.lower())
1386
1487
  except ValueError:
1387
- raise LLMError( # noqa: B904
1388
- f"Unknown provider: {name}. Available: {[p.value for p in LLMProvider]}"
1389
- )
1488
+ raise LLMError(f"Unknown provider: {name}. Available: {[p.value for p in LLMProvider]}")
1390
1489
 
1391
1490
  # Build config with sensible defaults
1392
1491
  config = LLMConfig(
@@ -1416,7 +1515,7 @@ def get_provider(name: str, **config_kwargs: Any) -> LLMClient:
1416
1515
  )
1417
1516
  except ImportError as e:
1418
1517
  # .: Graceful degradation when API unavailable
1419
- raise LLMError( # noqa: B904
1518
+ raise LLMError(
1420
1519
  f"Provider {name} unavailable: {e}. "
1421
1520
  "Install the required package or use 'local' provider."
1422
1521
  )
@@ -1668,7 +1767,7 @@ class FailoverLLMClient:
1668
1767
  def operation(client: LLMClient) -> str:
1669
1768
  if hasattr(client, "chat_completion"):
1670
1769
  messages = [{"role": "user", "content": prompt}]
1671
- response = client.chat_completion(messages, **kwargs) # type: ignore[ignore-without-code]
1770
+ response = client.chat_completion(messages, **kwargs)
1672
1771
  return response.answer # type: ignore[no-any-return]
1673
1772
  else:
1674
1773
  response = client.query(prompt, {})
@@ -1697,7 +1796,7 @@ class FailoverLLMClient:
1697
1796
  trace = DictTrace(trace_data)
1698
1797
 
1699
1798
  if hasattr(client, "analyze_trace"):
1700
- response = client.analyze_trace(trace, "Analyze this signal") # type: ignore[ignore-without-code]
1799
+ response = client.analyze_trace(trace, "Analyze this signal")
1701
1800
  else:
1702
1801
  response = client.analyze(trace, "Analyze this signal")
1703
1802
 
@@ -1729,7 +1828,7 @@ class FailoverLLMClient:
1729
1828
  trace = CharTrace(signal_characteristics)
1730
1829
 
1731
1830
  if hasattr(client, "suggest_measurements"):
1732
- response = client.suggest_measurements(trace) # type: ignore[ignore-without-code]
1831
+ response = client.suggest_measurements(trace)
1733
1832
  else:
1734
1833
  response = client.analyze(trace, "What measurements should I perform?")
1735
1834
 
@@ -1795,7 +1894,7 @@ def is_provider_available(provider: str) -> bool:
1795
1894
  if not os.environ.get("OPENAI_API_KEY"):
1796
1895
  return False
1797
1896
  try:
1798
- import openai # type: ignore[ignore-without-code]
1897
+ import openai # noqa: F401
1799
1898
 
1800
1899
  return True
1801
1900
  except ImportError:
@@ -1805,7 +1904,7 @@ def is_provider_available(provider: str) -> bool:
1805
1904
  if not os.environ.get("ANTHROPIC_API_KEY"):
1806
1905
  return False
1807
1906
  try:
1808
- import anthropic # type: ignore[ignore-without-code]
1907
+ import anthropic # noqa: F401
1809
1908
 
1810
1909
  return True
1811
1910
  except ImportError:
oscura/api/operators.py CHANGED
@@ -46,7 +46,7 @@ class TimeIndex:
46
46
  """
47
47
 
48
48
  # Time unit multipliers to seconds
49
- TIME_UNITS = { # noqa: RUF012
49
+ TIME_UNITS = {
50
50
  "s": 1.0,
51
51
  "ms": 1e-3,
52
52
  "us": 1e-6,
@@ -212,7 +212,7 @@ class UnitConverter:
212
212
  """
213
213
 
214
214
  # SI prefixes
215
- SI_PREFIXES = { # noqa: RUF012
215
+ SI_PREFIXES = {
216
216
  "P": 1e15, # peta
217
217
  "T": 1e12, # tera
218
218
  "G": 1e9, # giga
@@ -227,7 +227,7 @@ class UnitConverter:
227
227
  }
228
228
 
229
229
  # Base units
230
- BASE_UNITS = { # noqa: RUF012
230
+ BASE_UNITS = {
231
231
  "V": "voltage",
232
232
  "A": "current",
233
233
  "W": "power",