autoglm-gui 0.4.3__py3-none-any.whl → 0.4.7__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.
- AutoGLM_GUI/__init__.py +9 -0
- AutoGLM_GUI/__main__.py +6 -5
- AutoGLM_GUI/api/agents.py +8 -9
- AutoGLM_GUI/api/media.py +109 -7
- AutoGLM_GUI/config.py +23 -0
- AutoGLM_GUI/scrcpy_stream.py +42 -21
- AutoGLM_GUI/state.py +0 -6
- AutoGLM_GUI/static/assets/{about-B8Uhchgz.js → about-DIdU3ZqP.js} +1 -1
- AutoGLM_GUI/static/assets/chat-_-u1G4Ee.js +25 -0
- AutoGLM_GUI/static/assets/{index-CVkp0My_.js → index--ElIPD22.js} +1 -1
- AutoGLM_GUI/static/assets/{index-BifYNxwo.js → index-BuFMN8G5.js} +1 -1
- AutoGLM_GUI/static/assets/index-DCrxTz-A.css +1 -0
- AutoGLM_GUI/static/index.html +2 -2
- {autoglm_gui-0.4.3.dist-info → autoglm_gui-0.4.7.dist-info}/METADATA +1 -1
- {autoglm_gui-0.4.3.dist-info → autoglm_gui-0.4.7.dist-info}/RECORD +18 -17
- AutoGLM_GUI/static/assets/chat-CCzEYvEh.js +0 -25
- AutoGLM_GUI/static/assets/index-C5w8tQiR.css +0 -1
- {autoglm_gui-0.4.3.dist-info → autoglm_gui-0.4.7.dist-info}/WHEEL +0 -0
- {autoglm_gui-0.4.3.dist-info → autoglm_gui-0.4.7.dist-info}/entry_points.txt +0 -0
- {autoglm_gui-0.4.3.dist-info → autoglm_gui-0.4.7.dist-info}/licenses/LICENSE +0 -0
AutoGLM_GUI/__init__.py
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""AutoGLM-GUI package metadata."""
|
|
2
|
+
|
|
3
|
+
from importlib import metadata
|
|
4
|
+
|
|
5
|
+
# Expose package version at runtime; fall back to "unknown" during editable/dev runs
|
|
6
|
+
try:
|
|
7
|
+
__version__ = metadata.version("autoglm-gui")
|
|
8
|
+
except metadata.PackageNotFoundError:
|
|
9
|
+
__version__ = "unknown"
|
AutoGLM_GUI/__main__.py
CHANGED
|
@@ -8,6 +8,8 @@ import threading
|
|
|
8
8
|
import time
|
|
9
9
|
import webbrowser
|
|
10
10
|
|
|
11
|
+
from AutoGLM_GUI import __version__
|
|
12
|
+
|
|
11
13
|
# Default configuration
|
|
12
14
|
DEFAULT_MODEL_NAME = "autoglm-phone-9b"
|
|
13
15
|
|
|
@@ -128,6 +130,7 @@ def main() -> None:
|
|
|
128
130
|
import uvicorn
|
|
129
131
|
|
|
130
132
|
from AutoGLM_GUI import server
|
|
133
|
+
from AutoGLM_GUI.config import config
|
|
131
134
|
|
|
132
135
|
# Set model configuration via environment variables (survives reload)
|
|
133
136
|
os.environ["AUTOGLM_BASE_URL"] = args.base_url
|
|
@@ -135,17 +138,15 @@ def main() -> None:
|
|
|
135
138
|
if args.apikey is not None:
|
|
136
139
|
os.environ["AUTOGLM_API_KEY"] = args.apikey
|
|
137
140
|
|
|
138
|
-
#
|
|
139
|
-
|
|
140
|
-
server.DEFAULT_MODEL_NAME = args.model
|
|
141
|
-
if args.apikey is not None:
|
|
142
|
-
server.DEFAULT_API_KEY = args.apikey
|
|
141
|
+
# Refresh config from environment variables
|
|
142
|
+
config.refresh_from_env()
|
|
143
143
|
|
|
144
144
|
# Display startup banner
|
|
145
145
|
print()
|
|
146
146
|
print("=" * 50)
|
|
147
147
|
print(" AutoGLM-GUI - Phone Agent Web Interface")
|
|
148
148
|
print("=" * 50)
|
|
149
|
+
print(f" Version: {__version__}")
|
|
149
150
|
print()
|
|
150
151
|
print(f" Server: http://{args.host}:{args.port}")
|
|
151
152
|
print()
|
AutoGLM_GUI/api/agents.py
CHANGED
|
@@ -4,10 +4,8 @@ import json
|
|
|
4
4
|
|
|
5
5
|
from fastapi import APIRouter, HTTPException
|
|
6
6
|
from fastapi.responses import StreamingResponse
|
|
7
|
-
from phone_agent import PhoneAgent
|
|
8
|
-
from phone_agent.agent import AgentConfig
|
|
9
|
-
from phone_agent.model import ModelConfig
|
|
10
7
|
|
|
8
|
+
from AutoGLM_GUI.config import config
|
|
11
9
|
from AutoGLM_GUI.schemas import (
|
|
12
10
|
APIAgentConfig,
|
|
13
11
|
APIModelConfig,
|
|
@@ -18,14 +16,14 @@ from AutoGLM_GUI.schemas import (
|
|
|
18
16
|
StatusResponse,
|
|
19
17
|
)
|
|
20
18
|
from AutoGLM_GUI.state import (
|
|
21
|
-
DEFAULT_API_KEY,
|
|
22
|
-
DEFAULT_BASE_URL,
|
|
23
|
-
DEFAULT_MODEL_NAME,
|
|
24
19
|
agent_configs,
|
|
25
20
|
agents,
|
|
26
21
|
non_blocking_takeover,
|
|
27
22
|
)
|
|
28
23
|
from AutoGLM_GUI.version import APP_VERSION
|
|
24
|
+
from phone_agent import PhoneAgent
|
|
25
|
+
from phone_agent.agent import AgentConfig
|
|
26
|
+
from phone_agent.model import ModelConfig
|
|
29
27
|
|
|
30
28
|
router = APIRouter()
|
|
31
29
|
|
|
@@ -41,10 +39,11 @@ def init_agent(request: InitRequest) -> dict:
|
|
|
41
39
|
raise HTTPException(
|
|
42
40
|
status_code=400, detail="device_id is required in agent_config"
|
|
43
41
|
)
|
|
42
|
+
config.refresh_from_env()
|
|
44
43
|
|
|
45
|
-
base_url = req_model_config.base_url or
|
|
46
|
-
api_key = req_model_config.api_key or
|
|
47
|
-
model_name = req_model_config.model_name or
|
|
44
|
+
base_url = req_model_config.base_url or config.base_url
|
|
45
|
+
api_key = req_model_config.api_key or config.api_key
|
|
46
|
+
model_name = req_model_config.model_name or config.model_name
|
|
48
47
|
|
|
49
48
|
if not base_url:
|
|
50
49
|
raise HTTPException(
|
AutoGLM_GUI/api/media.py
CHANGED
|
@@ -68,7 +68,10 @@ def take_screenshot(request: ScreenshotRequest) -> ScreenshotResponse:
|
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
@router.websocket("/api/video/stream")
|
|
71
|
-
async def video_stream_ws(
|
|
71
|
+
async def video_stream_ws(
|
|
72
|
+
websocket: WebSocket,
|
|
73
|
+
device_id: str | None = None,
|
|
74
|
+
):
|
|
72
75
|
"""Stream real-time H.264 video from scrcpy server via WebSocket(多设备支持)."""
|
|
73
76
|
await websocket.accept()
|
|
74
77
|
|
|
@@ -78,6 +81,20 @@ async def video_stream_ws(websocket: WebSocket, device_id: str | None = None):
|
|
|
78
81
|
|
|
79
82
|
print(f"[video/stream] WebSocket connection for device {device_id}")
|
|
80
83
|
|
|
84
|
+
# Debug: Save stream to file for analysis
|
|
85
|
+
# Set to True for debugging (default: False)
|
|
86
|
+
debug_save = False
|
|
87
|
+
debug_file = None
|
|
88
|
+
if debug_save:
|
|
89
|
+
import os
|
|
90
|
+
from pathlib import Path
|
|
91
|
+
|
|
92
|
+
debug_dir = Path("debug_streams")
|
|
93
|
+
debug_dir.mkdir(exist_ok=True)
|
|
94
|
+
debug_file_path = debug_dir / f"{device_id}_{int(__import__('time').time())}.h264"
|
|
95
|
+
debug_file = open(debug_file_path, "wb")
|
|
96
|
+
print(f"[video/stream] DEBUG: Saving stream to {debug_file_path}")
|
|
97
|
+
|
|
81
98
|
if device_id not in scrcpy_locks:
|
|
82
99
|
scrcpy_locks[device_id] = asyncio.Lock()
|
|
83
100
|
|
|
@@ -92,6 +109,54 @@ async def video_stream_ws(websocket: WebSocket, device_id: str | None = None):
|
|
|
92
109
|
print(f"[video/stream] Starting scrcpy server for device {device_id}")
|
|
93
110
|
await scrcpy_streamers[device_id].start()
|
|
94
111
|
print(f"[video/stream] Scrcpy server started for device {device_id}")
|
|
112
|
+
|
|
113
|
+
# Read initial chunks and accumulate into a single buffer
|
|
114
|
+
# Then parse the entire buffer to find complete NAL units
|
|
115
|
+
streamer = scrcpy_streamers[device_id]
|
|
116
|
+
accumulated_buffer = bytearray()
|
|
117
|
+
target_size = 50 * 1024 # Accumulate at least 50KB
|
|
118
|
+
|
|
119
|
+
print(f"[video/stream] Accumulating initial data (target: {target_size} bytes)...")
|
|
120
|
+
for attempt in range(10):
|
|
121
|
+
try:
|
|
122
|
+
# Disable auto-caching - we'll parse the entire buffer at once
|
|
123
|
+
chunk = await streamer.read_h264_chunk(auto_cache=False)
|
|
124
|
+
accumulated_buffer.extend(chunk)
|
|
125
|
+
print(
|
|
126
|
+
f"[video/stream] Read chunk ({len(chunk)} bytes, total: {len(accumulated_buffer)} bytes)"
|
|
127
|
+
)
|
|
128
|
+
except Exception as e:
|
|
129
|
+
print(f"[video/stream] Failed to read chunk: {e}")
|
|
130
|
+
await asyncio.sleep(0.5)
|
|
131
|
+
continue
|
|
132
|
+
|
|
133
|
+
# Check if we have enough data
|
|
134
|
+
if len(accumulated_buffer) >= target_size:
|
|
135
|
+
break
|
|
136
|
+
|
|
137
|
+
# Now parse the entire accumulated buffer at once
|
|
138
|
+
# This ensures NAL units spanning multiple chunks are detected as complete
|
|
139
|
+
print(f"[video/stream] Parsing accumulated buffer ({len(accumulated_buffer)} bytes)...")
|
|
140
|
+
streamer._cache_nal_units(bytes(accumulated_buffer))
|
|
141
|
+
|
|
142
|
+
# Get initialization data
|
|
143
|
+
init_data = streamer.get_initialization_data()
|
|
144
|
+
if not init_data:
|
|
145
|
+
raise RuntimeError(
|
|
146
|
+
f"Failed to find complete SPS/PPS/IDR in {len(accumulated_buffer)} bytes"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# Send initialization data to first client
|
|
150
|
+
await websocket.send_bytes(init_data)
|
|
151
|
+
print(
|
|
152
|
+
f"[video/stream] Sent initial data ({len(init_data)} bytes) to first client"
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
# Debug: Save to file
|
|
156
|
+
if debug_file:
|
|
157
|
+
debug_file.write(init_data)
|
|
158
|
+
debug_file.flush()
|
|
159
|
+
|
|
95
160
|
except Exception as e:
|
|
96
161
|
import traceback
|
|
97
162
|
|
|
@@ -108,15 +173,39 @@ async def video_stream_ws(websocket: WebSocket, device_id: str | None = None):
|
|
|
108
173
|
print(f"[video/stream] Reusing streamer for device {device_id}")
|
|
109
174
|
|
|
110
175
|
streamer = scrcpy_streamers[device_id]
|
|
111
|
-
|
|
112
|
-
|
|
176
|
+
# CRITICAL: Send complete initialization data (SPS+PPS+IDR)
|
|
177
|
+
# Without IDR frame, decoder cannot start and will show black screen
|
|
178
|
+
|
|
179
|
+
# Wait for initialization data to be ready (max 5 seconds)
|
|
180
|
+
init_data = None
|
|
181
|
+
for attempt in range(10):
|
|
182
|
+
init_data = streamer.get_initialization_data()
|
|
183
|
+
if init_data:
|
|
184
|
+
break
|
|
185
|
+
print(
|
|
186
|
+
f"[video/stream] Waiting for initialization data (attempt {attempt + 1}/10)..."
|
|
187
|
+
)
|
|
188
|
+
await asyncio.sleep(0.5)
|
|
189
|
+
|
|
190
|
+
if init_data:
|
|
113
191
|
await websocket.send_bytes(init_data)
|
|
114
|
-
print(f"[video/stream] Sent SPS/PPS for device {device_id}")
|
|
115
|
-
else:
|
|
116
192
|
print(
|
|
117
|
-
f"[video/stream]
|
|
193
|
+
f"[video/stream] Sent initialization data (SPS+PPS+IDR, {len(init_data)} bytes) for device {device_id}"
|
|
118
194
|
)
|
|
119
195
|
|
|
196
|
+
# Debug: Save to file
|
|
197
|
+
if debug_file:
|
|
198
|
+
debug_file.write(init_data)
|
|
199
|
+
debug_file.flush()
|
|
200
|
+
else:
|
|
201
|
+
error_msg = f"Initialization data not ready for device {device_id} after 5 seconds"
|
|
202
|
+
print(f"[video/stream] ERROR: {error_msg}")
|
|
203
|
+
try:
|
|
204
|
+
await websocket.send_json({"error": error_msg})
|
|
205
|
+
except Exception:
|
|
206
|
+
pass
|
|
207
|
+
return
|
|
208
|
+
|
|
120
209
|
streamer = scrcpy_streamers[device_id]
|
|
121
210
|
|
|
122
211
|
stream_failed = False
|
|
@@ -124,8 +213,16 @@ async def video_stream_ws(websocket: WebSocket, device_id: str | None = None):
|
|
|
124
213
|
chunk_count = 0
|
|
125
214
|
while True:
|
|
126
215
|
try:
|
|
127
|
-
|
|
216
|
+
# Disable auto_cache - we only cache once during initialization
|
|
217
|
+
# Later chunks may have incomplete NAL units that would corrupt the cache
|
|
218
|
+
h264_chunk = await streamer.read_h264_chunk(auto_cache=False)
|
|
128
219
|
await websocket.send_bytes(h264_chunk)
|
|
220
|
+
|
|
221
|
+
# Debug: Save to file
|
|
222
|
+
if debug_file:
|
|
223
|
+
debug_file.write(h264_chunk)
|
|
224
|
+
debug_file.flush()
|
|
225
|
+
|
|
129
226
|
chunk_count += 1
|
|
130
227
|
if chunk_count % 100 == 0:
|
|
131
228
|
print(
|
|
@@ -160,4 +257,9 @@ async def video_stream_ws(websocket: WebSocket, device_id: str | None = None):
|
|
|
160
257
|
scrcpy_streamers[device_id].stop()
|
|
161
258
|
del scrcpy_streamers[device_id]
|
|
162
259
|
|
|
260
|
+
# Debug: Close file
|
|
261
|
+
if debug_file:
|
|
262
|
+
debug_file.close()
|
|
263
|
+
print(f"[video/stream] DEBUG: Closed debug file")
|
|
264
|
+
|
|
163
265
|
print(f"[video/stream] Device {device_id}: Stream ended")
|
AutoGLM_GUI/config.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Application configuration singleton."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class AppConfig:
|
|
9
|
+
"""Global application configuration."""
|
|
10
|
+
|
|
11
|
+
base_url: str = ""
|
|
12
|
+
model_name: str = "autoglm-phone-9b"
|
|
13
|
+
api_key: str = "EMPTY"
|
|
14
|
+
|
|
15
|
+
def refresh_from_env(self):
|
|
16
|
+
"""从环境变量刷新配置(适用于 reload 模式)"""
|
|
17
|
+
self.base_url = os.getenv("AUTOGLM_BASE_URL", self.base_url)
|
|
18
|
+
self.model_name = os.getenv("AUTOGLM_MODEL_NAME", self.model_name)
|
|
19
|
+
self.api_key = os.getenv("AUTOGLM_API_KEY", self.api_key)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Global singleton instance
|
|
23
|
+
config = AppConfig()
|
AutoGLM_GUI/scrcpy_stream.py
CHANGED
|
@@ -307,11 +307,14 @@ class ScrcpyStreamer:
|
|
|
307
307
|
|
|
308
308
|
raise ConnectionError("Failed to connect to scrcpy server")
|
|
309
309
|
|
|
310
|
-
def _find_nal_units(
|
|
310
|
+
def _find_nal_units(
|
|
311
|
+
self, data: bytes
|
|
312
|
+
) -> list[tuple[int, int, int, bool]]:
|
|
311
313
|
"""Find NAL units in H.264 data.
|
|
312
314
|
|
|
313
315
|
Returns:
|
|
314
|
-
List of (start_pos, nal_type, nal_size) tuples
|
|
316
|
+
List of (start_pos, nal_type, nal_size, is_complete) tuples
|
|
317
|
+
is_complete=False if NAL unit extends to chunk boundary (may be truncated)
|
|
315
318
|
"""
|
|
316
319
|
nal_units = []
|
|
317
320
|
i = 0
|
|
@@ -336,18 +339,22 @@ class ScrcpyStreamer:
|
|
|
336
339
|
|
|
337
340
|
# Find next start code to determine NAL unit size
|
|
338
341
|
next_start = nal_start + 1
|
|
342
|
+
found_next = False
|
|
339
343
|
while next_start < data_len - 3:
|
|
340
344
|
if (
|
|
341
345
|
data[next_start : next_start + 4] == b"\x00\x00\x00\x01"
|
|
342
346
|
or data[next_start : next_start + 3] == b"\x00\x00\x01"
|
|
343
347
|
):
|
|
348
|
+
found_next = True
|
|
344
349
|
break
|
|
345
350
|
next_start += 1
|
|
346
351
|
else:
|
|
347
352
|
next_start = data_len
|
|
348
353
|
|
|
349
354
|
nal_size = next_start - i
|
|
350
|
-
|
|
355
|
+
# NAL unit is complete only if we found the next start code
|
|
356
|
+
is_complete = found_next
|
|
357
|
+
nal_units.append((i, nal_type, nal_size, is_complete))
|
|
351
358
|
|
|
352
359
|
i = next_start
|
|
353
360
|
|
|
@@ -356,13 +363,13 @@ class ScrcpyStreamer:
|
|
|
356
363
|
def _cache_nal_units(self, data: bytes) -> None:
|
|
357
364
|
"""Parse and cache INITIAL complete NAL units (SPS, PPS, IDR).
|
|
358
365
|
|
|
359
|
-
IMPORTANT:
|
|
360
|
-
NAL units
|
|
361
|
-
|
|
366
|
+
IMPORTANT: Caches NAL units with size validation.
|
|
367
|
+
For small NAL units (SPS/PPS), we cache even if at chunk boundary.
|
|
368
|
+
For large NAL units (IDR), we require minimum size to ensure completeness.
|
|
362
369
|
"""
|
|
363
370
|
nal_units = self._find_nal_units(data)
|
|
364
371
|
|
|
365
|
-
for start, nal_type, size in nal_units:
|
|
372
|
+
for start, nal_type, size, is_complete in nal_units:
|
|
366
373
|
nal_data = data[start : start + size]
|
|
367
374
|
|
|
368
375
|
if nal_type == 7: # SPS
|
|
@@ -375,11 +382,11 @@ class ScrcpyStreamer:
|
|
|
375
382
|
f"{b:02x}" for b in nal_data[: min(12, len(nal_data))]
|
|
376
383
|
)
|
|
377
384
|
print(
|
|
378
|
-
f"[ScrcpyStreamer] ✓ Cached
|
|
385
|
+
f"[ScrcpyStreamer] ✓ Cached SPS ({size} bytes, complete={is_complete}): {hex_preview}..."
|
|
379
386
|
)
|
|
380
387
|
elif size < 10:
|
|
381
388
|
print(
|
|
382
|
-
f"[ScrcpyStreamer] ✗ Skipped
|
|
389
|
+
f"[ScrcpyStreamer] ✗ Skipped short SPS ({size} bytes)"
|
|
383
390
|
)
|
|
384
391
|
|
|
385
392
|
elif nal_type == 8: # PPS
|
|
@@ -392,24 +399,33 @@ class ScrcpyStreamer:
|
|
|
392
399
|
f"{b:02x}" for b in nal_data[: min(12, len(nal_data))]
|
|
393
400
|
)
|
|
394
401
|
print(
|
|
395
|
-
f"[ScrcpyStreamer] ✓ Cached
|
|
402
|
+
f"[ScrcpyStreamer] ✓ Cached PPS ({size} bytes, complete={is_complete}): {hex_preview}..."
|
|
396
403
|
)
|
|
397
404
|
elif size < 6:
|
|
398
405
|
print(
|
|
399
|
-
f"[ScrcpyStreamer] ✗ Skipped
|
|
406
|
+
f"[ScrcpyStreamer] ✗ Skipped short PPS ({size} bytes)"
|
|
400
407
|
)
|
|
401
408
|
|
|
402
409
|
elif nal_type == 5: # IDR frame
|
|
403
|
-
#
|
|
404
|
-
#
|
|
405
|
-
if self.cached_sps and self.cached_pps:
|
|
410
|
+
# CRITICAL: Only cache COMPLETE IDR frames
|
|
411
|
+
# Incomplete IDR frames cause "error while decoding MB" errors
|
|
412
|
+
if self.cached_sps and self.cached_pps and is_complete and size >= 1024:
|
|
406
413
|
is_first = self.cached_idr is None
|
|
407
414
|
self.cached_idr = nal_data
|
|
408
415
|
if is_first:
|
|
409
416
|
print(
|
|
410
|
-
f"[ScrcpyStreamer] ✓ Cached
|
|
417
|
+
f"[ScrcpyStreamer] ✓ Cached COMPLETE IDR frame ({size} bytes)"
|
|
411
418
|
)
|
|
412
419
|
# Don't log every IDR update (too verbose)
|
|
420
|
+
elif not is_complete:
|
|
421
|
+
if size > 1024: # Only log if it's a large incomplete IDR
|
|
422
|
+
print(
|
|
423
|
+
f"[ScrcpyStreamer] ⚠ Skipped INCOMPLETE IDR ({size} bytes, extends to chunk boundary)"
|
|
424
|
+
)
|
|
425
|
+
elif size < 1024:
|
|
426
|
+
print(
|
|
427
|
+
f"[ScrcpyStreamer] ✗ Skipped small IDR ({size} bytes)"
|
|
428
|
+
)
|
|
413
429
|
|
|
414
430
|
# Lock SPS/PPS once we have complete initial parameters
|
|
415
431
|
if self.cached_sps and self.cached_pps and not self.sps_pps_locked:
|
|
@@ -435,7 +451,9 @@ class ScrcpyStreamer:
|
|
|
435
451
|
|
|
436
452
|
# Find all IDR frames
|
|
437
453
|
idr_positions = [
|
|
438
|
-
(start, size)
|
|
454
|
+
(start, size)
|
|
455
|
+
for start, nal_type, size, _ in nal_units
|
|
456
|
+
if nal_type == 5
|
|
439
457
|
]
|
|
440
458
|
|
|
441
459
|
if not idr_positions:
|
|
@@ -497,11 +515,14 @@ class ScrcpyStreamer:
|
|
|
497
515
|
return init_data
|
|
498
516
|
return None
|
|
499
517
|
|
|
500
|
-
async def read_h264_chunk(self) -> bytes:
|
|
518
|
+
async def read_h264_chunk(self, auto_cache: bool = True) -> bytes:
|
|
501
519
|
"""Read H.264 data chunk from socket.
|
|
502
520
|
|
|
521
|
+
Args:
|
|
522
|
+
auto_cache: If True, automatically cache SPS/PPS/IDR from this chunk
|
|
523
|
+
|
|
503
524
|
Returns:
|
|
504
|
-
bytes: Raw H.264 data
|
|
525
|
+
bytes: Raw H.264 data
|
|
505
526
|
|
|
506
527
|
Raises:
|
|
507
528
|
ConnectionError: If socket is closed or error occurs
|
|
@@ -524,9 +545,9 @@ class ScrcpyStreamer:
|
|
|
524
545
|
f"[ScrcpyStreamer] Large chunk received: {len(data) / 1024:.1f} KB"
|
|
525
546
|
)
|
|
526
547
|
|
|
527
|
-
#
|
|
528
|
-
|
|
529
|
-
|
|
548
|
+
# Optionally cache SPS/PPS/IDR from this chunk
|
|
549
|
+
if auto_cache:
|
|
550
|
+
self._cache_nal_units(data)
|
|
530
551
|
|
|
531
552
|
# NOTE: We don't automatically prepend SPS/PPS here because:
|
|
532
553
|
# 1. NAL units may be truncated across chunks
|
AutoGLM_GUI/state.py
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import asyncio
|
|
6
|
-
import os
|
|
7
6
|
from typing import TYPE_CHECKING
|
|
8
7
|
|
|
9
8
|
from phone_agent.agent import AgentConfig
|
|
@@ -22,11 +21,6 @@ agent_configs: dict[str, tuple[ModelConfig, AgentConfig]] = {}
|
|
|
22
21
|
scrcpy_streamers: dict[str, "ScrcpyStreamer"] = {}
|
|
23
22
|
scrcpy_locks: dict[str, asyncio.Lock] = {}
|
|
24
23
|
|
|
25
|
-
# Defaults pulled from env (used when request omits config)
|
|
26
|
-
DEFAULT_BASE_URL: str = os.getenv("AUTOGLM_BASE_URL", "")
|
|
27
|
-
DEFAULT_MODEL_NAME: str = os.getenv("AUTOGLM_MODEL_NAME", "autoglm-phone-9b")
|
|
28
|
-
DEFAULT_API_KEY: str = os.getenv("AUTOGLM_API_KEY", "EMPTY")
|
|
29
|
-
|
|
30
24
|
|
|
31
25
|
def non_blocking_takeover(message: str) -> None:
|
|
32
26
|
"""Log takeover requests without blocking for console input."""
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{j as o}from"./index
|
|
1
|
+
import{j as o}from"./index--ElIPD22.js";function t(){return o.jsx("div",{className:"p-2",children:o.jsx("h3",{children:"About"})})}export{t as component};
|