reactor-runtime 2.0.1__tar.gz → 2.0.2__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.
Files changed (125) hide show
  1. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/PKG-INFO +1 -1
  2. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/pyproject.toml +1 -1
  3. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_cli/commands/run.py +2 -4
  4. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/runtime_api.py +8 -1
  5. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/runtimes/http/http_runtime.py +13 -1
  6. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/decoders/h264.py +1 -11
  7. reactor_runtime-2.0.2/src/reactor_runtime/utils/launch.py +103 -0
  8. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/utils/log.py +43 -7
  9. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime.egg-info/PKG-INFO +1 -1
  10. reactor_runtime-2.0.1/src/reactor_runtime/utils/launch.py +0 -64
  11. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/README.md +0 -0
  12. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/setup.cfg +0 -0
  13. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/api/__init__.py +0 -0
  14. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_cli/commands/__init__.py +0 -0
  15. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_cli/commands/capabilities.py +0 -0
  16. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_cli/commands/init.py +0 -0
  17. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_cli/main.py +0 -0
  18. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_cli/utils/__init__.py +0 -0
  19. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_cli/utils/config.py +0 -0
  20. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_cli/utils/runtime.py +0 -0
  21. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_cli/utils/version.py +0 -0
  22. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/__init__.py +0 -0
  23. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/capabilities.py +0 -0
  24. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/config.py +0 -0
  25. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/__init__.py +0 -0
  26. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/events/__init__.py +0 -0
  27. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/events/connected.py +0 -0
  28. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/events/event.py +0 -0
  29. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/events/messages.py +0 -0
  30. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/internal/__init__.py +0 -0
  31. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/internal/input_buffer.py +0 -0
  32. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/internal/output_buffer.py +0 -0
  33. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/internal/reactor_core.py +0 -0
  34. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/model/__init__.py +0 -0
  35. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/model/decorators.py +0 -0
  36. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/model/handlers.py +0 -0
  37. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/model/input_fields.py +0 -0
  38. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/model/reactor_model.py +0 -0
  39. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/pipeline/__init__.py +0 -0
  40. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/pipeline/idle.py +0 -0
  41. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/pipeline/input_state.py +0 -0
  42. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/pipeline/reactor_pipeline.py +0 -0
  43. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/tracks/__init__.py +0 -0
  44. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/tracks/descriptors.py +0 -0
  45. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/tracks/input.py +0 -0
  46. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/interface/tracks/output.py +0 -0
  47. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/model_state.py +0 -0
  48. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/profiling/__init__.py +0 -0
  49. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/profiling/backends/__init__.py +0 -0
  50. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/profiling/backends/base.py +0 -0
  51. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/profiling/backends/file.py +0 -0
  52. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/profiling/backends/otlp.py +0 -0
  53. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/profiling/helpers.py +0 -0
  54. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/profiling/plotting/__init__.py +0 -0
  55. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/profiling/plotting/plot_profiling.py +0 -0
  56. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/profiling/profiler.py +0 -0
  57. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/profiling/singleton.py +0 -0
  58. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/runtimes/headless/config.py +0 -0
  59. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/runtimes/headless/headless_runtime.py +0 -0
  60. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/runtimes/headless/input_feeder.py +0 -0
  61. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/runtimes/http/config.py +0 -0
  62. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/runtimes/http/types.py +0 -0
  63. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/__init__.py +0 -0
  64. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/aiortc/__init__.py +0 -0
  65. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/aiortc/audio_track.py +0 -0
  66. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/aiortc/client.py +0 -0
  67. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/aiortc/frame_conversion.py +0 -0
  68. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/aiortc/ice_connection.py +0 -0
  69. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/aiortc/video_track.py +0 -0
  70. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/config.py +0 -0
  71. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/events.py +0 -0
  72. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/__init__.py +0 -0
  73. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/client.py +0 -0
  74. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/decoders/__init__.py +0 -0
  75. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/decoders/av1.py +0 -0
  76. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/decoders/base.py +0 -0
  77. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/decoders/factory.py +0 -0
  78. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/decoders/h265.py +0 -0
  79. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/decoders/vp8.py +0 -0
  80. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/decoders/vp9.py +0 -0
  81. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/encoders/__init__.py +0 -0
  82. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/encoders/av1.py +0 -0
  83. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/encoders/base.py +0 -0
  84. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/encoders/factory.py +0 -0
  85. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/encoders/h264.py +0 -0
  86. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/encoders/h265.py +0 -0
  87. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/encoders/opus.py +0 -0
  88. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/encoders/vp8.py +0 -0
  89. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/encoders/vp9.py +0 -0
  90. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/gst.py +0 -0
  91. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/gst_helpers.py +0 -0
  92. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/receiver/__init__.py +0 -0
  93. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/receiver/audio.py +0 -0
  94. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/receiver/base.py +0 -0
  95. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/receiver/video.py +0 -0
  96. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/sdp/__init__.py +0 -0
  97. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/sdp/bundle.py +0 -0
  98. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/sdp/codec.py +0 -0
  99. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/sdp/extmap.py +0 -0
  100. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/sdp/ice.py +0 -0
  101. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/sender/__init__.py +0 -0
  102. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/sender/audio.py +0 -0
  103. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/sender/base.py +0 -0
  104. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/sender/video.py +0 -0
  105. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/settings.py +0 -0
  106. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/gstreamer/signals.py +0 -0
  107. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/ice_uris.py +0 -0
  108. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/interface.py +0 -0
  109. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/media.py +0 -0
  110. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/transports/types.py +0 -0
  111. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/utils/loader.py +0 -0
  112. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/utils/messages.py +0 -0
  113. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime/utils/schema.py +0 -0
  114. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime.egg-info/SOURCES.txt +0 -0
  115. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime.egg-info/dependency_links.txt +0 -0
  116. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime.egg-info/entry_points.txt +0 -0
  117. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime.egg-info/requires.txt +0 -0
  118. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/reactor_runtime.egg-info/top_level.txt +0 -0
  119. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/template/README.md +0 -0
  120. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/template/__init__.py +0 -0
  121. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/template/config.yml +0 -0
  122. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/template/model.py +0 -0
  123. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/template/pipeline.py +0 -0
  124. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/template/reactor.yaml +0 -0
  125. {reactor_runtime-2.0.1 → reactor_runtime-2.0.2}/src/template/requirements.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reactor_runtime
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: Reactor runtime with public model API
5
5
  Author-email: Reactor <team@reactor.inc>
6
6
  Requires-Python: >=3.9
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "reactor_runtime"
7
- version = "2.0.1"
7
+ version = "2.0.2"
8
8
  description = "Reactor runtime with public model API"
9
9
  authors = [
10
10
  { name = "Reactor", email = "team@reactor.inc" }
@@ -12,7 +12,6 @@ from reactor_cli.utils.config import (
12
12
  _valid_port,
13
13
  )
14
14
  from reactor_cli.utils.runtime import load_runtime
15
- from reactor_runtime import __version__
16
15
  from reactor_runtime.config import ReactorConfig
17
16
  from reactor_runtime.transports.config import (
18
17
  GSTREAMER_INSTALL_URL,
@@ -243,9 +242,7 @@ class RunCommand:
243
242
  # Run
244
243
  # ---------------------------------------------------------
245
244
  log_level = "DEBUG" if self.args.verbose else "INFO"
246
-
247
- print(f"Starting reactor runtime v{__version__} ({runtime_name})")
248
- print(f"Model: {reactor_config.name} ({reactor_config.model})")
245
+ detailed_logs = runtime_kwargs.pop("detailed_logs", False)
249
246
 
250
247
  try:
251
248
  run_reactor_runtime(
@@ -253,6 +250,7 @@ class RunCommand:
253
250
  reactor_config=reactor_config,
254
251
  model_root=str(model_path),
255
252
  log_level=log_level,
253
+ show_timestamps=detailed_logs,
256
254
  **runtime_kwargs,
257
255
  )
258
256
  except KeyboardInterrupt:
@@ -124,6 +124,13 @@ class RuntimeConfig:
124
124
  argparse.ArgumentParser with runtime-specific arguments
125
125
  """
126
126
  parser = argparse.ArgumentParser(add_help=False)
127
+ parser.add_argument(
128
+ "--detailed-logs",
129
+ action=argparse.BooleanOptionalAction,
130
+ default=False,
131
+ dest="detailed_logs",
132
+ help="Show timestamps and full log detail. Default: off.",
133
+ )
127
134
  parser.add_argument(
128
135
  "--orphan-timeout",
129
136
  type=float,
@@ -236,7 +243,7 @@ class Runtime(ModelStateMachine, ABC):
236
243
  )
237
244
  self.model_thread.start()
238
245
 
239
- logger.info(
246
+ logger.debug(
240
247
  "Model loaded, emission started, run() thread spawned",
241
248
  model=self.config.reactor_config.model,
242
249
  )
@@ -22,6 +22,7 @@ Other:
22
22
  """
23
23
 
24
24
  import asyncio
25
+ import os
25
26
  from typing import Optional
26
27
  import numpy as np
27
28
  from fastapi import FastAPI, HTTPException
@@ -510,6 +511,16 @@ class HttpRuntime(Runtime):
510
511
  if self._server is not None:
511
512
  self._server.should_exit = True
512
513
 
514
+ # -----------------------------------------------------------------
515
+ # State hooks
516
+ # -----------------------------------------------------------------
517
+
518
+ def on_before_initialization_success(self, **kwargs) -> None:
519
+ aqua, reset = "\033[96m", "\033[0m"
520
+ url = f"http://{self.config.host}:{self.config.port}"
521
+ logger.info("Started runtime process", pid=os.getpid())
522
+ logger.info(f"Reactor runtime running on {aqua}{url}{reset}")
523
+
513
524
  # -----------------------------------------------------------------
514
525
  # run()
515
526
  # -----------------------------------------------------------------
@@ -522,7 +533,8 @@ class HttpRuntime(Runtime):
522
533
  app=self.app,
523
534
  host=self.config.host,
524
535
  port=self.config.port,
525
- log_level="info",
536
+ log_level="warning",
537
+ access_log=False,
526
538
  )
527
539
  self._server = uvicorn.Server(uvicorn_config)
528
540
 
@@ -92,7 +92,6 @@ class H264DecoderBin(BaseRTPDecoderBin):
92
92
 
93
93
  1) NVIDIA hardware decoders (GPU accelerated)
94
94
  2) avdec_h264 (libav software decoder)
95
- 3) openh264dec (generic fallback)
96
95
 
97
96
  Hardware decoder names vary depending on:
98
97
  - Distribution
@@ -121,15 +120,6 @@ class H264DecoderBin(BaseRTPDecoderBin):
121
120
  )
122
121
  return "avdec_h264"
123
122
 
124
- # ---------------------------------------------------------
125
- # Generic fallback (if present in build)
126
- # ---------------------------------------------------------
127
- if Gst.ElementFactory.find("openh264dec") is not None:
128
- logger.info(
129
- "H264DecoderBin: using software (SW) decoder: openh264dec",
130
- )
131
- return "openh264dec"
132
-
133
123
  raise RuntimeError(
134
- "No H264 decoder available (tried: nvh264dec, nvdec_h264, nvv4l2decoder, avdec_h264, openh264dec)"
124
+ "No H264 decoder available (tried: nvh264dec, nvdec_h264, nvv4l2decoder, avdec_h264)"
135
125
  )
@@ -0,0 +1,103 @@
1
+ # Copyright (c) 2026 Reactor Technologies, Inc. All rights reserved.
2
+ """Entry point for running the Reactor runtime."""
3
+
4
+ import asyncio
5
+ import logging
6
+ import os
7
+ import sys
8
+ from typing import Callable, List, Optional
9
+
10
+ from reactor_runtime import __version__
11
+ from reactor_runtime.config import ReactorConfig
12
+ from reactor_runtime.utils.log import (
13
+ LOG_DATEFMT,
14
+ LOG_FORMAT,
15
+ LOG_FORMAT_BRIEF,
16
+ ColorFormatter,
17
+ get_logger,
18
+ )
19
+
20
+ logger = get_logger(__name__)
21
+
22
+
23
+ def add_import_paths(paths: List[str]) -> None:
24
+ """Prepend provided directories to sys.path if they exist."""
25
+ for p in paths:
26
+ if not p:
27
+ continue
28
+ ap = os.path.abspath(p)
29
+ if os.path.isdir(ap) and ap not in sys.path:
30
+ sys.path.insert(0, ap)
31
+
32
+
33
+ def configure_logging(level: str, show_timestamps: bool = True) -> None:
34
+ fmt = LOG_FORMAT if show_timestamps else LOG_FORMAT_BRIEF
35
+ handler = logging.StreamHandler()
36
+ handler.setFormatter(ColorFormatter(fmt, datefmt=LOG_DATEFMT))
37
+ logging.root.addHandler(handler)
38
+ logging.root.setLevel(getattr(logging, level.upper(), logging.INFO))
39
+ logging.getLogger("aiortc").setLevel(logging.WARNING)
40
+ logging.getLogger("av").setLevel(logging.WARNING)
41
+ logging.getLogger("aioice").setLevel(logging.WARNING)
42
+ logging.getLogger("uvicorn").setLevel(logging.WARNING)
43
+ logging.getLogger("uvicorn.error").setLevel(logging.WARNING)
44
+ logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
45
+
46
+
47
+ def _infer_runtime_name(serve_fn: Callable) -> str:
48
+ """Derive a short runtime label from the serve function's module path.
49
+
50
+ e.g. ``reactor_runtime.runtimes.http.http_runtime`` -> ``http``
51
+ ``reactor_runtime.runtimes._redis._redis_runtime`` -> ``redis``
52
+ """
53
+ parts = getattr(serve_fn, "__module__", "").split(".")
54
+ if len(parts) >= 3:
55
+ return parts[2].lstrip("_")
56
+ return "unknown"
57
+
58
+
59
+ def run_reactor_runtime(
60
+ runtime_serve_fn: Callable,
61
+ reactor_config: ReactorConfig,
62
+ model_root: Optional[str] = None,
63
+ log_level: str = "INFO",
64
+ show_timestamps: bool = True,
65
+ **runtime_kwargs,
66
+ ) -> None:
67
+ """Run the Reactor runtime.
68
+
69
+ Args:
70
+ runtime_serve_fn: The ``serve()`` function from the runtime module.
71
+ reactor_config: Parsed ``reactor.yaml`` (with ``config`` resolved
72
+ to an absolute path and ``config_overrides`` populated).
73
+ model_root: Directory to add to sys.path for model imports.
74
+ log_level: Logging level.
75
+ show_timestamps: Include timestamps in log output (disabled for local
76
+ HTTP runtime where output is watched interactively).
77
+ **runtime_kwargs: Runtime-specific arguments passed to serve().
78
+ """
79
+ configure_logging(log_level, show_timestamps=show_timestamps)
80
+
81
+ runtime_name = _infer_runtime_name(runtime_serve_fn)
82
+ logger.info(
83
+ "Starting reactor runtime",
84
+ version=__version__,
85
+ runtime=runtime_name,
86
+ )
87
+ model_kw: dict = {
88
+ "name": reactor_config.name,
89
+ "entrypoint": reactor_config.model,
90
+ }
91
+ if reactor_config.config:
92
+ model_kw["config"] = reactor_config.config
93
+ logger.info("Model detected", **model_kw)
94
+
95
+ if model_root is not None:
96
+ add_import_paths([model_root])
97
+
98
+ asyncio.run(
99
+ runtime_serve_fn(
100
+ reactor_config=reactor_config,
101
+ **runtime_kwargs,
102
+ )
103
+ )
@@ -20,13 +20,46 @@ from typing import Any, Optional
20
20
  session_id_var: ContextVar[Optional[str]] = ContextVar("session_id", default=None)
21
21
 
22
22
 
23
- # Shared log format / date-format used across the runtime.
24
- LOG_FORMAT = "%(asctime)s %(levelname)-8s %(name)s: %(message)s"
23
+ LOG_FORMAT = "%(asctime)s %(levelname)s %(name)s: %(message)s"
24
+ LOG_FORMAT_BRIEF = "%(levelname)s %(message)s"
25
25
  LOG_DATEFMT = "%Y-%m-%dT%H:%M:%S%z"
26
26
 
27
- # Keep underscore-prefixed aliases so existing imports don't break.
28
- _LOG_FORMAT = LOG_FORMAT
29
- _LOG_DATEFMT = LOG_DATEFMT
27
+ _RESET = "\033[0m"
28
+
29
+ _LEVEL_COLORS: dict[int, str] = {
30
+ logging.DEBUG: "\033[37m", # gray
31
+ logging.INFO: "\033[32m", # green
32
+ logging.WARNING: "\033[33m", # yellow
33
+ logging.ERROR: "\033[31m", # red
34
+ logging.CRITICAL: "\033[1;31m", # bold red
35
+ }
36
+
37
+
38
+ _RUNTIME_PREFIXES = ("reactor_runtime.", "reactor_cli.")
39
+ _MODEL_COLOR = "\033[36m"
40
+
41
+
42
+ class ColorFormatter(logging.Formatter):
43
+ """Formatter that colorizes log levels and tags model logs."""
44
+
45
+ _COL = 12 # visual width: fits "CRITICAL MDL"
46
+
47
+ def format(self, record: logging.LogRecord) -> str:
48
+ orig = record.levelname
49
+ color = _LEVEL_COLORS.get(record.levelno, "")
50
+
51
+ if record.name.startswith(_RUNTIME_PREFIXES):
52
+ pad = self._COL - len(orig)
53
+ record.levelname = f"{color}{orig}{_RESET}{' ' * pad}"
54
+ else:
55
+ pad = self._COL - len(orig) - 4 # 4 = len(" MDL")
56
+ record.levelname = (
57
+ f"{color}{orig}{_RESET} {_MODEL_COLOR}MDL{_RESET}{' ' * max(pad, 0)}"
58
+ )
59
+
60
+ result = super().format(record)
61
+ record.levelname = orig
62
+ return result
30
63
 
31
64
 
32
65
  class StructuredLogger:
@@ -39,15 +72,18 @@ class StructuredLogger:
39
72
 
40
73
  # -- internal helpers --------------------------------------------------
41
74
 
75
+ _BLUE = "\033[38;5;25m"
76
+ _RESET = "\033[0m"
77
+
42
78
  @staticmethod
43
79
  def _fmt(msg: str, kw: dict[str, Any]) -> str:
44
- # Auto-inject session_id from ContextVar if not explicitly provided.
45
80
  sid = session_id_var.get()
46
81
  if sid and "session_id" not in kw:
47
82
  kw["session_id"] = sid
48
83
  if not kw:
49
84
  return msg
50
- pairs = " ".join(f"{k}={v}" for k, v in kw.items())
85
+ b, r = StructuredLogger._BLUE, StructuredLogger._RESET
86
+ pairs = " ".join(f"{b}{k}={r}{v}" for k, v in kw.items())
51
87
  return f"{msg} {pairs}"
52
88
 
53
89
  # -- public API --------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reactor_runtime
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: Reactor runtime with public model API
5
5
  Author-email: Reactor <team@reactor.inc>
6
6
  Requires-Python: >=3.9
@@ -1,64 +0,0 @@
1
- # Copyright (c) 2026 Reactor Technologies, Inc. All rights reserved.
2
- """Entry point for running the Reactor runtime."""
3
-
4
- import asyncio
5
- import logging
6
- import os
7
- import sys
8
- from typing import Callable, List, Optional
9
-
10
- from reactor_runtime.config import ReactorConfig
11
- from reactor_runtime.utils.log import _LOG_DATEFMT, _LOG_FORMAT, get_logger
12
-
13
- logger = get_logger(__name__)
14
-
15
-
16
- def add_import_paths(paths: List[str]) -> None:
17
- """Prepend provided directories to sys.path if they exist."""
18
- for p in paths:
19
- if not p:
20
- continue
21
- ap = os.path.abspath(p)
22
- if os.path.isdir(ap) and ap not in sys.path:
23
- sys.path.insert(0, ap)
24
-
25
-
26
- def configure_logging(level: str) -> None:
27
- logging.basicConfig(
28
- level=getattr(logging, level.upper(), logging.INFO),
29
- format=_LOG_FORMAT,
30
- datefmt=_LOG_DATEFMT,
31
- )
32
- logging.getLogger("aiortc").setLevel(logging.WARNING)
33
- logging.getLogger("av").setLevel(logging.WARNING)
34
- logging.getLogger("aioice").setLevel(logging.WARNING)
35
-
36
-
37
- def run_reactor_runtime(
38
- runtime_serve_fn: Callable,
39
- reactor_config: ReactorConfig,
40
- model_root: Optional[str] = None,
41
- log_level: str = "INFO",
42
- **runtime_kwargs,
43
- ) -> None:
44
- """Run the Reactor runtime.
45
-
46
- Args:
47
- runtime_serve_fn: The ``serve()`` function from the runtime module.
48
- reactor_config: Parsed ``reactor.yaml`` (with ``config`` resolved
49
- to an absolute path and ``config_overrides`` populated).
50
- model_root: Directory to add to sys.path for model imports.
51
- log_level: Logging level.
52
- **runtime_kwargs: Runtime-specific arguments passed to serve().
53
- """
54
- configure_logging(log_level)
55
-
56
- if model_root is not None:
57
- add_import_paths([model_root])
58
-
59
- asyncio.run(
60
- runtime_serve_fn(
61
- reactor_config=reactor_config,
62
- **runtime_kwargs,
63
- )
64
- )