autoglm-gui 0.4.3__tar.gz → 0.4.7__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 (48) hide show
  1. autoglm_gui-0.4.7/AutoGLM_GUI/__init__.py +9 -0
  2. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/__main__.py +6 -5
  3. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/api/agents.py +8 -9
  4. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/api/media.py +109 -7
  5. autoglm_gui-0.4.7/AutoGLM_GUI/config.py +23 -0
  6. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/scrcpy_stream.py +42 -21
  7. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/state.py +0 -6
  8. autoglm_gui-0.4.3/AutoGLM_GUI/static/assets/about-B8Uhchgz.js → autoglm_gui-0.4.7/AutoGLM_GUI/static/assets/about-DIdU3ZqP.js +1 -1
  9. autoglm_gui-0.4.7/AutoGLM_GUI/static/assets/chat-_-u1G4Ee.js +25 -0
  10. autoglm_gui-0.4.3/AutoGLM_GUI/static/assets/index-CVkp0My_.js → autoglm_gui-0.4.7/AutoGLM_GUI/static/assets/index--ElIPD22.js +1 -1
  11. autoglm_gui-0.4.3/AutoGLM_GUI/static/assets/index-BifYNxwo.js → autoglm_gui-0.4.7/AutoGLM_GUI/static/assets/index-BuFMN8G5.js +1 -1
  12. autoglm_gui-0.4.7/AutoGLM_GUI/static/assets/index-DCrxTz-A.css +1 -0
  13. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/static/index.html +2 -2
  14. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/PKG-INFO +1 -1
  15. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/pyproject.toml +1 -1
  16. autoglm_gui-0.4.3/AutoGLM_GUI/__init__.py +0 -0
  17. autoglm_gui-0.4.3/AutoGLM_GUI/static/assets/chat-CCzEYvEh.js +0 -25
  18. autoglm_gui-0.4.3/AutoGLM_GUI/static/assets/index-C5w8tQiR.css +0 -1
  19. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/.gitignore +0 -0
  20. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/adb_plus/__init__.py +0 -0
  21. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/adb_plus/screenshot.py +0 -0
  22. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/adb_plus/touch.py +0 -0
  23. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/api/__init__.py +0 -0
  24. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/api/control.py +0 -0
  25. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/api/devices.py +0 -0
  26. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/schemas.py +0 -0
  27. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/server.py +0 -0
  28. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/AutoGLM_GUI/version.py +0 -0
  29. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/LICENSE +0 -0
  30. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/README.md +0 -0
  31. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/__init__.py +0 -0
  32. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/actions/__init__.py +0 -0
  33. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/actions/handler.py +0 -0
  34. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/adb/__init__.py +0 -0
  35. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/adb/connection.py +0 -0
  36. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/adb/device.py +0 -0
  37. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/adb/input.py +0 -0
  38. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/adb/screenshot.py +0 -0
  39. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/agent.py +0 -0
  40. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/config/__init__.py +0 -0
  41. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/config/apps.py +0 -0
  42. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/config/i18n.py +0 -0
  43. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/config/prompts.py +0 -0
  44. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/config/prompts_en.py +0 -0
  45. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/config/prompts_zh.py +0 -0
  46. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/model/__init__.py +0 -0
  47. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/phone_agent/model/client.py +0 -0
  48. {autoglm_gui-0.4.3 → autoglm_gui-0.4.7}/scrcpy-server-v3.3.3 +0 -0
@@ -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"
@@ -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
- # Also set directly for non-reload mode
139
- server.DEFAULT_BASE_URL = args.base_url
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()
@@ -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 DEFAULT_BASE_URL
46
- api_key = req_model_config.api_key or DEFAULT_API_KEY
47
- model_name = req_model_config.model_name or DEFAULT_MODEL_NAME
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(
@@ -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(websocket: WebSocket, device_id: str | None = None):
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
- if streamer.cached_sps and streamer.cached_pps:
112
- init_data = streamer.cached_sps + streamer.cached_pps
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] Warning: No cached SPS/PPS for device {device_id}"
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
- h264_chunk = await streamer.read_h264_chunk()
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")
@@ -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()
@@ -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(self, data: bytes) -> list[tuple[int, int, int]]:
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
- nal_units.append((i, nal_type, nal_size))
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: Only caches complete SPS/PPS from stream start.
360
- NAL units may be truncated across chunks, so we validate minimum sizes
361
- and lock the cache after getting complete initial parameters.
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 complete SPS ({size} bytes): {hex_preview}..."
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 truncated SPS ({size} bytes, too short)"
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 complete PPS ({size} bytes): {hex_preview}..."
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 truncated PPS ({size} bytes, too short)"
406
+ f"[ScrcpyStreamer] ✗ Skipped short PPS ({size} bytes)"
400
407
  )
401
408
 
402
409
  elif nal_type == 5: # IDR frame
403
- # ALWAYS update IDR to keep the LATEST frame
404
- # This gives better UX on reconnect (recent content, not stale startup frame)
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 initial IDR frame ({size} bytes)"
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) for start, nal_type, size in nal_units if nal_type == 5
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 with SPS/PPS prepended to IDR frames
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
- # Cache INITIAL complete SPS/PPS/IDR for future use
528
- # (Later chunks may have truncated NAL units, so we only cache once)
529
- self._cache_nal_units(data)
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
@@ -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-CVkp0My_.js";function t(){return o.jsx("div",{className:"p-2",children:o.jsx("h3",{children:"About"})})}export{t as component};
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};