loopview 0.1.0__tar.gz → 0.1.1__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.
- {loopview-0.1.0 → loopview-0.1.1}/PKG-INFO +1 -1
- {loopview-0.1.0 → loopview-0.1.1}/pyproject.toml +1 -1
- {loopview-0.1.0 → loopview-0.1.1}/pyproject.toml.orig +1 -1
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/__init__.py +1 -1
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/cli.py +25 -5
- loopview-0.1.1/src/loopview/static/assets/index-CkzKKDRu.js +21 -0
- loopview-0.1.1/src/loopview/static/assets/index-CnQIIz-O.css +1 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/index.html +2 -2
- loopview-0.1.0/src/loopview/static/assets/index-CEGppBgk.css +0 -1
- loopview-0.1.0/src/loopview/static/assets/index-DzC96LiQ.js +0 -21
- {loopview-0.1.0 → loopview-0.1.1}/README.md +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/app.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/cost/__init__.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/cost/pricing.json +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/cost/pricing.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/cost/split.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/demo_data/flagship.otlp.jsonl +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/devtools/__init__.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/devtools/dump_normalized.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/ingest/__init__.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/ingest/otlp.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/ingest/raw.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/live.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/normalize/__init__.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/normalize/adapters/__init__.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/normalize/adapters/base.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/normalize/adapters/gen_ai.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/normalize/adapters/generic.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/normalize/adapters/openinference.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/normalize/derived_tools.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/normalize/loop_nodes.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/normalize/normalizer.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/normalize/schema.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/normalize/transitions.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/py.typed +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/elk-worker.min-DfmSo98M.js +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/static/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/store/__init__.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/store/capture.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/store/memory.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/tools/__init__.py +0 -0
- {loopview-0.1.0 → loopview-0.1.1}/src/loopview/tools/report.py +0 -0
|
@@ -6,6 +6,7 @@ so an extra dependency is not worth it.
|
|
|
6
6
|
|
|
7
7
|
import argparse
|
|
8
8
|
import logging
|
|
9
|
+
import socket
|
|
9
10
|
import threading
|
|
10
11
|
import webbrowser
|
|
11
12
|
from pathlib import Path
|
|
@@ -82,16 +83,27 @@ def main(argv: list[str] | None = None) -> None:
|
|
|
82
83
|
# Flush now: when stdout is a pipe, Python buffers it and the banner would
|
|
83
84
|
# only appear when the server stops.
|
|
84
85
|
print(flush=True)
|
|
86
|
+
open_url = None
|
|
85
87
|
if args.command == "demo" and not args.no_browser:
|
|
86
|
-
|
|
87
|
-
url = f"http://{args.host}:{args.port}"
|
|
88
|
-
threading.Timer(1.0, webbrowser.open, args=[url]).start()
|
|
88
|
+
open_url = f"http://{args.host}:{args.port}"
|
|
89
89
|
app = create_app(store=store, capture=capture, pricing=merged_pricing(args.prices))
|
|
90
90
|
server = _Server(
|
|
91
|
-
uvicorn.Config(app, host=args.host, port=args.port, log_level="warning"),
|
|
91
|
+
uvicorn.Config(app, host=args.host, port=args.port, log_level="warning"),
|
|
92
|
+
app.state.hub,
|
|
93
|
+
open_url,
|
|
92
94
|
)
|
|
93
95
|
try:
|
|
94
96
|
server.run()
|
|
97
|
+
except SystemExit:
|
|
98
|
+
# uvicorn has logged the reason (usually the port is taken) and exits.
|
|
99
|
+
if not server.started:
|
|
100
|
+
print(
|
|
101
|
+
f"\nloopview could not listen on {args.host}:{args.port}. If another loopview "
|
|
102
|
+
"(or another OpenTelemetry collector) is using that port, stop it or choose "
|
|
103
|
+
"another one with --port.",
|
|
104
|
+
flush=True,
|
|
105
|
+
)
|
|
106
|
+
raise
|
|
95
107
|
except KeyboardInterrupt:
|
|
96
108
|
# uvicorn re-raises Ctrl+C once it has shut down cleanly; that's the
|
|
97
109
|
# normal way out, not an error.
|
|
@@ -106,9 +118,17 @@ class _Server(uvicorn.Server):
|
|
|
106
118
|
pressed. Otherwise it would wait for the browser's open event stream, which
|
|
107
119
|
never closes on its own, and then cancel it with a traceback."""
|
|
108
120
|
|
|
109
|
-
def __init__(self, config: uvicorn.Config, hub: LiveHub) -> None:
|
|
121
|
+
def __init__(self, config: uvicorn.Config, hub: LiveHub, open_url: str | None = None) -> None:
|
|
110
122
|
super().__init__(config)
|
|
111
123
|
self.hub = hub
|
|
124
|
+
self.open_url = open_url
|
|
125
|
+
|
|
126
|
+
async def startup(self, sockets: list[socket.socket] | None = None) -> None:
|
|
127
|
+
await super().startup(sockets)
|
|
128
|
+
# Open the browser only once the port is ours: if it was taken, the page
|
|
129
|
+
# would show whatever else is listening there.
|
|
130
|
+
if self.started and self.open_url:
|
|
131
|
+
threading.Thread(target=webbrowser.open, args=[self.open_url], daemon=True).start()
|
|
112
132
|
|
|
113
133
|
def handle_exit(self, sig: int, frame: FrameType | None) -> None:
|
|
114
134
|
self.hub.close()
|