feat(pipe): show Copilot CLI install progress during first run
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
title: GitHub Copilot Official SDK Pipe
|
title: GitHub Copilot Official SDK Pipe
|
||||||
author: Fu-Jie
|
author: Fu-Jie
|
||||||
author_url: https://github.com/Fu-Jie/openwebui-extensions
|
author_url: https://github.com/Fu-Jie/awesome-openwebui
|
||||||
funding_url: https://github.com/open-webui
|
funding_url: https://github.com/open-webui
|
||||||
openwebui_id: ce96f7b4-12fc-4ac3-9a01-875713e69359
|
openwebui_id: ce96f7b4-12fc-4ac3-9a01-875713e69359
|
||||||
description: Integrate GitHub Copilot SDK. Supports dynamic models, multi-turn conversation, streaming, multimodal input, infinite sessions, and frontend debug logging.
|
description: Integrate GitHub Copilot SDK. Supports dynamic models, multi-turn conversation, streaming, multimodal input, infinite sessions, and frontend debug logging.
|
||||||
@@ -2134,7 +2134,7 @@ class Pipe:
|
|||||||
|
|
||||||
# 1. Environment Setup (Only if needed or not done)
|
# 1. Environment Setup (Only if needed or not done)
|
||||||
if needs_setup:
|
if needs_setup:
|
||||||
self._setup_env(token=token)
|
self._setup_env(token=token, skip_cli_install=True)
|
||||||
self.__class__._last_update_check = now
|
self.__class__._last_update_check = now
|
||||||
else:
|
else:
|
||||||
# Still inject token for BYOK real-time updates
|
# Still inject token for BYOK real-time updates
|
||||||
@@ -2194,7 +2194,7 @@ class Pipe:
|
|||||||
logger.info("[Pipes] Refreshing model cache...")
|
logger.info("[Pipes] Refreshing model cache...")
|
||||||
try:
|
try:
|
||||||
# Use effective token for fetching
|
# Use effective token for fetching
|
||||||
self._setup_env(token=token)
|
self._setup_env(token=token, skip_cli_install=True)
|
||||||
|
|
||||||
# Fetch BYOK models if configured
|
# Fetch BYOK models if configured
|
||||||
byok = []
|
byok = []
|
||||||
@@ -2208,9 +2208,11 @@ class Pipe:
|
|||||||
byok = await self._fetch_byok_models(uv=uv)
|
byok = await self._fetch_byok_models(uv=uv)
|
||||||
|
|
||||||
standard = []
|
standard = []
|
||||||
if token:
|
cli_path = os.environ.get("COPILOT_CLI_PATH", "")
|
||||||
|
cli_ready = bool(cli_path and os.path.exists(cli_path))
|
||||||
|
if token and cli_ready:
|
||||||
client_config = {
|
client_config = {
|
||||||
"cli_path": os.environ.get("COPILOT_CLI_PATH"),
|
"cli_path": cli_path,
|
||||||
"cwd": self._get_workspace_dir(
|
"cwd": self._get_workspace_dir(
|
||||||
user_id=user_id, chat_id="listing"
|
user_id=user_id, chat_id="listing"
|
||||||
),
|
),
|
||||||
@@ -2266,11 +2268,18 @@ class Pipe:
|
|||||||
logger.error(f"[Pipes] Error listing models: {e}")
|
logger.error(f"[Pipes] Error listing models: {e}")
|
||||||
finally:
|
finally:
|
||||||
await c.stop()
|
await c.stop()
|
||||||
|
elif token and self.valves.DEBUG:
|
||||||
|
logger.info(
|
||||||
|
"[Pipes] Copilot CLI not ready during listing. Skip standard model probe to avoid blocking startup."
|
||||||
|
)
|
||||||
|
|
||||||
self._model_cache = standard + byok
|
self._model_cache = standard + byok
|
||||||
if not self._model_cache:
|
if not self._model_cache:
|
||||||
return [
|
return [
|
||||||
{"id": "error", "name": "No models found. Check Token/Network."}
|
{
|
||||||
|
"id": "warming_up",
|
||||||
|
"name": "Copilot CLI is preparing in background. Please retry in a moment.",
|
||||||
|
}
|
||||||
]
|
]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return [{"id": "error", "name": f"Error: {e}"}]
|
return [{"id": "error", "name": f"Error: {e}"}]
|
||||||
@@ -2318,8 +2327,26 @@ class Pipe:
|
|||||||
token: str = None,
|
token: str = None,
|
||||||
enable_mcp: bool = True,
|
enable_mcp: bool = True,
|
||||||
enable_cache: bool = True,
|
enable_cache: bool = True,
|
||||||
|
skip_cli_install: bool = False,
|
||||||
|
__event_emitter__=None,
|
||||||
):
|
):
|
||||||
"""Setup environment variables and verify Copilot CLI. Dynamic Token Injection."""
|
"""Setup environment variables and verify Copilot CLI. Dynamic Token Injection."""
|
||||||
|
def emit_status_sync(description: str, done: bool = False):
|
||||||
|
if not __event_emitter__:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
loop.create_task(
|
||||||
|
__event_emitter__(
|
||||||
|
{
|
||||||
|
"type": "status",
|
||||||
|
"data": {"description": description, "done": done},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
# 1. Real-time Token Injection (Always updates on each call)
|
# 1. Real-time Token Injection (Always updates on each call)
|
||||||
effective_token = token or self.valves.GH_TOKEN
|
effective_token = token or self.valves.GH_TOKEN
|
||||||
if effective_token:
|
if effective_token:
|
||||||
@@ -2418,41 +2445,106 @@ class Pipe:
|
|||||||
f"Version mismatch ({current_version} != {target_version})"
|
f"Version mismatch ({current_version} != {target_version})"
|
||||||
)
|
)
|
||||||
|
|
||||||
if should_install:
|
if should_install and not skip_cli_install:
|
||||||
self._emit_debug_log_sync(
|
self._emit_debug_log_sync(
|
||||||
f"Installing/Updating Copilot CLI: {install_reason}...",
|
f"Installing/Updating Copilot CLI: {install_reason}...",
|
||||||
__event_call__,
|
__event_call__,
|
||||||
debug_enabled=debug_enabled,
|
debug_enabled=debug_enabled,
|
||||||
)
|
)
|
||||||
|
emit_status_sync(
|
||||||
|
"🔧 正在安装/更新 Copilot CLI(首次可能需要 1-3 分钟)...",
|
||||||
|
done=False,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
env = os.environ.copy()
|
env = os.environ.copy()
|
||||||
if target_version:
|
if target_version:
|
||||||
env["VERSION"] = target_version
|
env["VERSION"] = target_version
|
||||||
subprocess.run(
|
proc = subprocess.Popen(
|
||||||
"curl -fsSL https://gh.io/copilot-install | bash",
|
"curl -fsSL https://gh.io/copilot-install | bash",
|
||||||
shell=True,
|
shell=True,
|
||||||
check=True,
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
text=True,
|
||||||
|
bufsize=1,
|
||||||
env=env,
|
env=env,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
progress_percent = -1
|
||||||
|
line_count = 0
|
||||||
|
while True:
|
||||||
|
raw_line = proc.stdout.readline() if proc.stdout else ""
|
||||||
|
if raw_line == "" and proc.poll() is not None:
|
||||||
|
break
|
||||||
|
|
||||||
|
line = (raw_line or "").strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
line_count += 1
|
||||||
|
percent_match = re.search(r"(\d{1,3})%", line)
|
||||||
|
if percent_match:
|
||||||
|
try:
|
||||||
|
pct = int(percent_match.group(1))
|
||||||
|
if pct >= progress_percent + 5:
|
||||||
|
progress_percent = pct
|
||||||
|
emit_status_sync(
|
||||||
|
f"📦 Copilot CLI 安装中:{pct}%", done=False
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
elif line_count % 20 == 0:
|
||||||
|
emit_status_sync(
|
||||||
|
f"📦 Copilot CLI 安装中:{line[:120]}", done=False
|
||||||
|
)
|
||||||
|
|
||||||
|
return_code = proc.wait()
|
||||||
|
if return_code != 0:
|
||||||
|
raise subprocess.CalledProcessError(
|
||||||
|
return_code,
|
||||||
|
"curl -fsSL https://gh.io/copilot-install | bash",
|
||||||
|
)
|
||||||
|
|
||||||
# Re-verify
|
# Re-verify
|
||||||
current_version = get_cli_version(cli_path)
|
current_version = get_cli_version(cli_path)
|
||||||
|
emit_status_sync(
|
||||||
|
f"✅ Copilot CLI 安装完成(v{current_version or target_version or 'latest'})",
|
||||||
|
done=False,
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self._emit_debug_log_sync(
|
self._emit_debug_log_sync(
|
||||||
f"CLI installation failed: {e}",
|
f"CLI installation failed: {e}",
|
||||||
__event_call__,
|
__event_call__,
|
||||||
debug_enabled=debug_enabled,
|
debug_enabled=debug_enabled,
|
||||||
)
|
)
|
||||||
|
emit_status_sync(
|
||||||
|
f"❌ Copilot CLI 安装失败:{str(e)[:120]}",
|
||||||
|
done=True,
|
||||||
|
)
|
||||||
|
elif should_install and skip_cli_install:
|
||||||
|
self._emit_debug_log_sync(
|
||||||
|
f"Skipping CLI install during model listing: {install_reason}",
|
||||||
|
__event_call__,
|
||||||
|
debug_enabled=debug_enabled,
|
||||||
|
)
|
||||||
|
|
||||||
# 4. Finalize
|
# 4. Finalize
|
||||||
os.environ["COPILOT_CLI_PATH"] = cli_path
|
cli_ready = bool(cli_path and os.path.exists(cli_path))
|
||||||
self.__class__._env_setup_done = True
|
if cli_ready:
|
||||||
|
os.environ["COPILOT_CLI_PATH"] = cli_path
|
||||||
|
|
||||||
|
self.__class__._env_setup_done = cli_ready
|
||||||
self.__class__._last_update_check = datetime.now().timestamp()
|
self.__class__._last_update_check = datetime.now().timestamp()
|
||||||
|
|
||||||
self._emit_debug_log_sync(
|
self._emit_debug_log_sync(
|
||||||
f"Environment setup complete. CLI: {cli_path} (v{current_version})",
|
f"Environment setup complete. CLI ready={cli_ready}. Path: {cli_path} (v{current_version})",
|
||||||
__event_call__,
|
__event_call__,
|
||||||
debug_enabled=debug_enabled,
|
debug_enabled=debug_enabled,
|
||||||
)
|
)
|
||||||
|
if not skip_cli_install:
|
||||||
|
if cli_ready:
|
||||||
|
emit_status_sync("✅ Copilot CLI 已就绪", done=True)
|
||||||
|
else:
|
||||||
|
emit_status_sync("⚠️ Copilot CLI 尚未就绪,请稍后重试。", done=True)
|
||||||
|
|
||||||
def _process_attachments(
|
def _process_attachments(
|
||||||
self,
|
self,
|
||||||
@@ -2737,6 +2829,7 @@ class Pipe:
|
|||||||
token=effective_token,
|
token=effective_token,
|
||||||
enable_mcp=effective_mcp,
|
enable_mcp=effective_mcp,
|
||||||
enable_cache=effective_cache,
|
enable_cache=effective_cache,
|
||||||
|
__event_emitter__=__event_emitter__,
|
||||||
)
|
)
|
||||||
|
|
||||||
cwd = self._get_workspace_dir(user_id=user_id, chat_id=chat_id)
|
cwd = self._get_workspace_dir(user_id=user_id, chat_id=chat_id)
|
||||||
|
|||||||
Reference in New Issue
Block a user