agentcrew-ai 0.8.13__py3-none-any.whl → 0.9.0__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.
- AgentCrew/__init__.py +1 -1
- AgentCrew/app.py +34 -633
- AgentCrew/main_docker.py +1 -30
- AgentCrew/modules/agents/local_agent.py +2 -2
- AgentCrew/modules/chat/message/command_processor.py +33 -8
- AgentCrew/modules/chat/message/handler.py +5 -1
- AgentCrew/modules/console/completers.py +1 -1
- AgentCrew/modules/console/console_ui.py +6 -11
- AgentCrew/modules/console/conversation_browser/__init__.py +9 -0
- AgentCrew/modules/console/conversation_browser/browser.py +84 -0
- AgentCrew/modules/console/conversation_browser/browser_input_handler.py +279 -0
- AgentCrew/modules/console/{conversation_browser.py → conversation_browser/browser_ui.py} +249 -163
- AgentCrew/modules/console/conversation_handler.py +34 -1
- AgentCrew/modules/console/display_handlers.py +123 -7
- AgentCrew/modules/gui/components/command_handler.py +137 -29
- AgentCrew/modules/gui/themes/README.md +30 -14
- AgentCrew/modules/gui/themes/__init__.py +2 -1
- AgentCrew/modules/gui/themes/atom_light.yaml +1287 -0
- AgentCrew/modules/gui/themes/catppuccin.yaml +1276 -0
- AgentCrew/modules/gui/themes/dracula.yaml +1262 -0
- AgentCrew/modules/gui/themes/nord.yaml +1267 -0
- AgentCrew/modules/gui/themes/saigontech.yaml +1268 -0
- AgentCrew/modules/gui/themes/style_provider.py +76 -264
- AgentCrew/modules/gui/themes/theme_loader.py +379 -0
- AgentCrew/modules/gui/themes/unicorn.yaml +1276 -0
- AgentCrew/modules/gui/widgets/configs/global_settings.py +3 -4
- AgentCrew/modules/llm/constants.py +13 -4
- AgentCrew/setup.py +470 -0
- {agentcrew_ai-0.8.13.dist-info → agentcrew_ai-0.9.0.dist-info}/METADATA +1 -1
- {agentcrew_ai-0.8.13.dist-info → agentcrew_ai-0.9.0.dist-info}/RECORD +34 -29
- {agentcrew_ai-0.8.13.dist-info → agentcrew_ai-0.9.0.dist-info}/WHEEL +1 -1
- AgentCrew/modules/gui/themes/atom_light.py +0 -1365
- AgentCrew/modules/gui/themes/catppuccin.py +0 -1404
- AgentCrew/modules/gui/themes/dracula.py +0 -1372
- AgentCrew/modules/gui/themes/nord.py +0 -1365
- AgentCrew/modules/gui/themes/saigontech.py +0 -1359
- AgentCrew/modules/gui/themes/unicorn.py +0 -1372
- {agentcrew_ai-0.8.13.dist-info → agentcrew_ai-0.9.0.dist-info}/entry_points.txt +0 -0
- {agentcrew_ai-0.8.13.dist-info → agentcrew_ai-0.9.0.dist-info}/licenses/LICENSE +0 -0
- {agentcrew_ai-0.8.13.dist-info → agentcrew_ai-0.9.0.dist-info}/top_level.txt +0 -0
AgentCrew/app.py
CHANGED
|
@@ -1,51 +1,22 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import sys
|
|
3
3
|
import json
|
|
4
|
-
import time
|
|
5
4
|
import asyncio
|
|
6
|
-
import
|
|
5
|
+
import functools
|
|
7
6
|
import nest_asyncio
|
|
8
7
|
from typing import Optional, Dict, Any, List
|
|
9
8
|
|
|
10
9
|
import click
|
|
11
|
-
import requests
|
|
12
10
|
|
|
11
|
+
from AgentCrew.setup import ApplicationSetup, PROVIDER_LIST
|
|
13
12
|
from AgentCrew.modules.config import ConfigManagement
|
|
14
|
-
from AgentCrew.modules.llm.model_registry import ModelRegistry
|
|
15
13
|
from AgentCrew.modules.llm.service_manager import ServiceManager
|
|
16
|
-
from AgentCrew.modules.memory.chroma_service import ChromaMemoryService
|
|
17
|
-
from AgentCrew.modules.memory.context_persistent import ContextPersistenceService
|
|
18
|
-
from AgentCrew.modules.clipboard import ClipboardService
|
|
19
|
-
from AgentCrew.modules.web_search import TavilySearchService
|
|
20
|
-
from AgentCrew.modules.code_analysis import CodeAnalysisService
|
|
21
|
-
from AgentCrew.modules.image_generation import ImageGenerationService
|
|
22
|
-
from AgentCrew.modules.browser_automation import BrowserAutomationService
|
|
23
|
-
from AgentCrew.modules.agents.manager import AgentManager
|
|
24
14
|
from AgentCrew.modules.agents.local_agent import LocalAgent
|
|
25
|
-
from AgentCrew.modules.agents.remote_agent import RemoteAgent
|
|
26
|
-
from AgentCrew.modules.agents.example import (
|
|
27
|
-
DEFAULT_NAME,
|
|
28
|
-
DEFAULT_DESCRIPTION,
|
|
29
|
-
DEFAULT_PROMPT,
|
|
30
|
-
)
|
|
31
15
|
|
|
32
16
|
nest_asyncio.apply()
|
|
33
17
|
|
|
34
18
|
|
|
35
|
-
PROVIDER_LIST = [
|
|
36
|
-
"claude",
|
|
37
|
-
"groq",
|
|
38
|
-
"openai",
|
|
39
|
-
"google",
|
|
40
|
-
"deepinfra",
|
|
41
|
-
"github_copilot",
|
|
42
|
-
"copilot_response",
|
|
43
|
-
]
|
|
44
|
-
|
|
45
|
-
|
|
46
19
|
def common_options(func):
|
|
47
|
-
import functools
|
|
48
|
-
|
|
49
20
|
@click.option(
|
|
50
21
|
"--provider",
|
|
51
22
|
type=click.Choice(PROVIDER_LIST),
|
|
@@ -79,381 +50,18 @@ def common_options(func):
|
|
|
79
50
|
|
|
80
51
|
|
|
81
52
|
class AgentCrewApplication:
|
|
82
|
-
"""
|
|
83
|
-
Centralized application class for AgentCrew.
|
|
84
|
-
|
|
85
|
-
This class handles:
|
|
86
|
-
- API key loading from configuration
|
|
87
|
-
- Service initialization and management
|
|
88
|
-
- Agent setup and configuration
|
|
89
|
-
- GitHub Copilot authentication
|
|
90
|
-
- Running different modes (console, GUI, server, job)
|
|
91
|
-
"""
|
|
92
|
-
|
|
93
53
|
def __init__(self):
|
|
94
|
-
"""Initialize the AgentCrew application."""
|
|
95
|
-
self.services: Optional[Dict[str, Any]] = None
|
|
96
|
-
self.agent_manager: Optional[AgentManager] = None
|
|
97
54
|
self.config_manager = ConfigManagement()
|
|
55
|
+
self.setup = ApplicationSetup(self.config_manager)
|
|
56
|
+
self.setup.load_api_keys_from_config()
|
|
98
57
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
"""
|
|
103
|
-
Loads API keys from the global config file and sets them as environment variables,
|
|
104
|
-
prioritizing them over any existing environment variables.
|
|
105
|
-
"""
|
|
106
|
-
config_file_path = os.getenv("AGENTCREW_CONFIG_PATH")
|
|
107
|
-
if not config_file_path:
|
|
108
|
-
# Default for when AGENTCREW_CONFIG_PATH is not set (e.g. dev mode, not using cli_prod)
|
|
109
|
-
config_file_path = "./config.json"
|
|
110
|
-
config_file_path = os.path.expanduser(config_file_path)
|
|
111
|
-
|
|
112
|
-
api_keys_config = {}
|
|
113
|
-
if os.path.exists(config_file_path):
|
|
114
|
-
try:
|
|
115
|
-
with open(config_file_path, "r", encoding="utf-8") as f:
|
|
116
|
-
loaded_config = json.load(f)
|
|
117
|
-
if isinstance(loaded_config, dict) and isinstance(
|
|
118
|
-
loaded_config.get("api_keys"), dict
|
|
119
|
-
):
|
|
120
|
-
api_keys_config = loaded_config["api_keys"]
|
|
121
|
-
else:
|
|
122
|
-
click.echo(
|
|
123
|
-
f"⚠️ API keys in {config_file_path} are not in the expected format.",
|
|
124
|
-
err=True,
|
|
125
|
-
)
|
|
126
|
-
except json.JSONDecodeError:
|
|
127
|
-
click.echo(
|
|
128
|
-
f"⚠️ Error decoding API keys from {config_file_path}.", err=True
|
|
129
|
-
)
|
|
130
|
-
except Exception as e:
|
|
131
|
-
click.echo(
|
|
132
|
-
f"⚠️ Could not load API keys from {config_file_path}: {e}",
|
|
133
|
-
err=True,
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
keys_to_check = [
|
|
137
|
-
"ANTHROPIC_API_KEY",
|
|
138
|
-
"GEMINI_API_KEY",
|
|
139
|
-
"OPENAI_API_KEY",
|
|
140
|
-
"GROQ_API_KEY",
|
|
141
|
-
"DEEPINFRA_API_KEY",
|
|
142
|
-
"GITHUB_COPILOT_API_KEY",
|
|
143
|
-
"TAVILY_API_KEY",
|
|
144
|
-
"VOYAGE_API_KEY",
|
|
145
|
-
"ELEVENLABS_API_KEY",
|
|
146
|
-
]
|
|
147
|
-
|
|
148
|
-
for key_name in keys_to_check:
|
|
149
|
-
if key_name in api_keys_config and api_keys_config[key_name]:
|
|
150
|
-
# Prioritize config file over existing environment variables
|
|
151
|
-
os.environ[key_name] = str(api_keys_config[key_name]).strip()
|
|
152
|
-
|
|
153
|
-
def _detect_provider(self) -> Optional[str]:
|
|
154
|
-
"""
|
|
155
|
-
Detect available LLM provider from environment or last used provider.
|
|
156
|
-
|
|
157
|
-
Returns:
|
|
158
|
-
Provider name or None if no provider found
|
|
159
|
-
"""
|
|
160
|
-
# Try to restore last used provider
|
|
161
|
-
try:
|
|
162
|
-
last_provider = self.config_manager.get_last_used_provider()
|
|
163
|
-
if last_provider:
|
|
164
|
-
# Verify the provider is still available
|
|
165
|
-
if last_provider in PROVIDER_LIST:
|
|
166
|
-
# Check if API key is available for this provider
|
|
167
|
-
api_key_map = {
|
|
168
|
-
"claude": "ANTHROPIC_API_KEY",
|
|
169
|
-
"google": "GEMINI_API_KEY",
|
|
170
|
-
"openai": "OPENAI_API_KEY",
|
|
171
|
-
"groq": "GROQ_API_KEY",
|
|
172
|
-
"deepinfra": "DEEPINFRA_API_KEY",
|
|
173
|
-
"github_copilot": "GITHUB_COPILOT_API_KEY",
|
|
174
|
-
"copilot_response": "GITHUB_COPILOT_API_KEY",
|
|
175
|
-
}
|
|
176
|
-
if os.getenv(api_key_map.get(last_provider, "")):
|
|
177
|
-
return last_provider
|
|
178
|
-
else:
|
|
179
|
-
# Check if it's a custom provider
|
|
180
|
-
custom_providers = (
|
|
181
|
-
self.config_manager.read_custom_llm_providers_config()
|
|
182
|
-
)
|
|
183
|
-
if any(p["name"] == last_provider for p in custom_providers):
|
|
184
|
-
return last_provider
|
|
185
|
-
except Exception as e:
|
|
186
|
-
click.echo(f"⚠️ Could not restore last used provider: {e}")
|
|
187
|
-
|
|
188
|
-
# Fall back to environment variable detection
|
|
189
|
-
if os.getenv("GITHUB_COPILOT_API_KEY"):
|
|
190
|
-
return "github_copilot"
|
|
191
|
-
elif os.getenv("ANTHROPIC_API_KEY"):
|
|
192
|
-
return "claude"
|
|
193
|
-
elif os.getenv("GEMINI_API_KEY"):
|
|
194
|
-
return "google"
|
|
195
|
-
elif os.getenv("OPENAI_API_KEY"):
|
|
196
|
-
return "openai"
|
|
197
|
-
elif os.getenv("GROQ_API_KEY"):
|
|
198
|
-
return "groq"
|
|
199
|
-
elif os.getenv("DEEPINFRA_API_KEY"):
|
|
200
|
-
return "deepinfra"
|
|
201
|
-
else:
|
|
202
|
-
custom_providers = self.config_manager.read_custom_llm_providers_config()
|
|
203
|
-
if len(custom_providers) > 0:
|
|
204
|
-
return custom_providers[0]["name"]
|
|
205
|
-
|
|
206
|
-
return None
|
|
207
|
-
|
|
208
|
-
def setup_services(
|
|
209
|
-
self, provider: str, memory_llm: Optional[str] = None, need_memory: bool = True
|
|
210
|
-
) -> Dict[str, Any]:
|
|
211
|
-
"""
|
|
212
|
-
Initialize and configure all AgentCrew services.
|
|
213
|
-
|
|
214
|
-
Args:
|
|
215
|
-
provider: LLM provider name (e.g., 'claude', 'openai', 'google')
|
|
216
|
-
memory_llm: Optional LLM provider for memory service
|
|
217
|
-
|
|
218
|
-
Returns:
|
|
219
|
-
Dictionary of initialized services
|
|
220
|
-
"""
|
|
221
|
-
registry = ModelRegistry.get_instance()
|
|
222
|
-
llm_manager = ServiceManager.get_instance()
|
|
223
|
-
|
|
224
|
-
models = registry.get_models_by_provider(provider)
|
|
225
|
-
if models:
|
|
226
|
-
default_model = next((m for m in models if m.default), models[0])
|
|
227
|
-
registry.set_current_model(f"{default_model.provider}/{default_model.id}")
|
|
228
|
-
|
|
229
|
-
llm_service = llm_manager.get_service(provider)
|
|
230
|
-
|
|
231
|
-
try:
|
|
232
|
-
last_model = self.config_manager.get_last_used_model()
|
|
233
|
-
last_provider = self.config_manager.get_last_used_provider()
|
|
234
|
-
|
|
235
|
-
if last_model and last_provider:
|
|
236
|
-
should_restore = False
|
|
237
|
-
if provider == last_provider:
|
|
238
|
-
should_restore = True
|
|
239
|
-
|
|
240
|
-
last_model_class = registry.get_model(last_model)
|
|
241
|
-
if should_restore and last_model_class:
|
|
242
|
-
llm_service.model = last_model_class.id
|
|
243
|
-
except Exception as e:
|
|
244
|
-
click.echo(f"⚠️ Could not restore last used model: {e}")
|
|
245
|
-
|
|
246
|
-
memory_service = None
|
|
247
|
-
context_service = None
|
|
248
|
-
if need_memory:
|
|
249
|
-
if memory_llm:
|
|
250
|
-
memory_service = ChromaMemoryService(
|
|
251
|
-
llm_service=llm_manager.initialize_standalone_service(memory_llm)
|
|
252
|
-
)
|
|
253
|
-
else:
|
|
254
|
-
memory_service = ChromaMemoryService(
|
|
255
|
-
llm_service=llm_manager.initialize_standalone_service(provider)
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
context_service = ContextPersistenceService()
|
|
259
|
-
clipboard_service = ClipboardService()
|
|
260
|
-
|
|
261
|
-
try:
|
|
262
|
-
search_service = TavilySearchService()
|
|
263
|
-
except Exception as e:
|
|
264
|
-
click.echo(f"⚠️ Web search tools not available: {str(e)}")
|
|
265
|
-
search_service = None
|
|
266
|
-
|
|
267
|
-
try:
|
|
268
|
-
code_analysis_llm = llm_manager.initialize_standalone_service(provider)
|
|
269
|
-
code_analysis_service = CodeAnalysisService(llm_service=code_analysis_llm)
|
|
270
|
-
except Exception as e:
|
|
271
|
-
click.echo(f"⚠️ Code analysis tool not available: {str(e)}")
|
|
272
|
-
code_analysis_service = None
|
|
273
|
-
|
|
274
|
-
try:
|
|
275
|
-
if os.getenv("OPENAI_API_KEY"):
|
|
276
|
-
image_gen_service = ImageGenerationService()
|
|
277
|
-
else:
|
|
278
|
-
image_gen_service = None
|
|
279
|
-
click.echo(
|
|
280
|
-
"⚠️ Image generation service not available: No API keys found."
|
|
281
|
-
)
|
|
282
|
-
except Exception as e:
|
|
283
|
-
click.echo(f"⚠️ Image generation service not available: {str(e)}")
|
|
284
|
-
image_gen_service = None
|
|
285
|
-
|
|
286
|
-
try:
|
|
287
|
-
browser_automation_service = BrowserAutomationService()
|
|
288
|
-
except Exception as e:
|
|
289
|
-
click.echo(f"⚠️ Browser automation service not available: {str(e)}")
|
|
290
|
-
browser_automation_service = None
|
|
291
|
-
|
|
292
|
-
try:
|
|
293
|
-
from AgentCrew.modules.file_editing import FileEditingService
|
|
294
|
-
|
|
295
|
-
file_editing_service = FileEditingService()
|
|
296
|
-
except Exception as e:
|
|
297
|
-
click.echo(f"⚠️ File editing service not available: {str(e)}")
|
|
298
|
-
file_editing_service = None
|
|
299
|
-
|
|
300
|
-
try:
|
|
301
|
-
from AgentCrew.modules.command_execution import CommandExecutionService
|
|
302
|
-
|
|
303
|
-
command_execution_service = CommandExecutionService.get_instance()
|
|
304
|
-
except Exception as e:
|
|
305
|
-
click.echo(f"⚠️ Command execution service not available: {str(e)}")
|
|
306
|
-
command_execution_service = None
|
|
307
|
-
|
|
308
|
-
self.services = {
|
|
309
|
-
"llm": llm_service,
|
|
310
|
-
"memory": memory_service,
|
|
311
|
-
"clipboard": clipboard_service,
|
|
312
|
-
"code_analysis": code_analysis_service,
|
|
313
|
-
"web_search": search_service,
|
|
314
|
-
"context_persistent": context_service,
|
|
315
|
-
"image_generation": image_gen_service,
|
|
316
|
-
"browser": browser_automation_service,
|
|
317
|
-
"file_editing": file_editing_service,
|
|
318
|
-
"command_execution": command_execution_service,
|
|
319
|
-
}
|
|
320
|
-
return self.services
|
|
321
|
-
|
|
322
|
-
def setup_agents(
|
|
323
|
-
self,
|
|
324
|
-
services: Dict[str, Any],
|
|
325
|
-
config_uri: Optional[str] = None,
|
|
326
|
-
remoting_provider: Optional[str] = None,
|
|
327
|
-
model_id: Optional[str] = None,
|
|
328
|
-
) -> AgentManager:
|
|
329
|
-
"""
|
|
330
|
-
Set up the agent system with specialized agents.
|
|
331
|
-
|
|
332
|
-
Args:
|
|
333
|
-
services: Dictionary of services
|
|
334
|
-
config_uri: Path to agent configuration file or url
|
|
335
|
-
remoting_provider: Provider for remoting mode
|
|
336
|
-
model_id: Model ID for remoting mode
|
|
337
|
-
|
|
338
|
-
Returns:
|
|
339
|
-
Configured AgentManager instance
|
|
340
|
-
"""
|
|
341
|
-
|
|
342
|
-
self.agent_manager = AgentManager.get_instance()
|
|
343
|
-
llm_manager = ServiceManager.get_instance()
|
|
344
|
-
|
|
345
|
-
services["agent_manager"] = self.agent_manager
|
|
346
|
-
|
|
347
|
-
global_config = self.config_manager.read_global_config_data()
|
|
348
|
-
self.agent_manager.context_shrink_enabled = global_config.get(
|
|
349
|
-
"global_settings", {}
|
|
350
|
-
).get("auto_context_shrink", True)
|
|
351
|
-
self.agent_manager.shrink_excluded_list = global_config.get(
|
|
352
|
-
"global_settings", {}
|
|
353
|
-
).get("shrink_excluded", [])
|
|
354
|
-
|
|
355
|
-
llm_service = services["llm"]
|
|
356
|
-
|
|
357
|
-
if config_uri:
|
|
358
|
-
os.environ["SW_AGENTS_CONFIG"] = config_uri
|
|
359
|
-
else:
|
|
360
|
-
config_uri = os.getenv("SW_AGENTS_CONFIG")
|
|
361
|
-
if not config_uri:
|
|
362
|
-
config_uri = "./agents.toml"
|
|
363
|
-
if not os.path.exists(config_uri):
|
|
364
|
-
click.echo(
|
|
365
|
-
f"Agent configuration not found at {config_uri}. Creating default configuration."
|
|
366
|
-
)
|
|
367
|
-
os.makedirs(os.path.dirname(config_uri), exist_ok=True)
|
|
368
|
-
|
|
369
|
-
default_config = f"""
|
|
370
|
-
[[agents]]
|
|
371
|
-
name = "{DEFAULT_NAME}"
|
|
372
|
-
description = "{DEFAULT_DESCRIPTION}"
|
|
373
|
-
system_prompt = '''{DEFAULT_PROMPT}'''
|
|
374
|
-
tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
375
|
-
"""
|
|
376
|
-
|
|
377
|
-
with open(config_uri, "w+", encoding="utf-8") as f:
|
|
378
|
-
f.write(default_config)
|
|
379
|
-
|
|
380
|
-
click.echo(f"Created default agent configuration at {config_uri}")
|
|
381
|
-
|
|
382
|
-
agent_definitions = AgentManager.load_agents_from_config(config_uri)
|
|
383
|
-
|
|
384
|
-
for agent_def in agent_definitions:
|
|
385
|
-
if agent_def.get("base_url", ""):
|
|
386
|
-
try:
|
|
387
|
-
agent = RemoteAgent(
|
|
388
|
-
agent_def["name"],
|
|
389
|
-
agent_def.get("base_url"),
|
|
390
|
-
headers=agent_def.get("headers", {}),
|
|
391
|
-
)
|
|
392
|
-
except Exception:
|
|
393
|
-
print("Error: cannot connect to remote agent, skipping...")
|
|
394
|
-
continue
|
|
395
|
-
else:
|
|
396
|
-
if remoting_provider:
|
|
397
|
-
llm_service = llm_manager.initialize_standalone_service(
|
|
398
|
-
remoting_provider
|
|
399
|
-
)
|
|
400
|
-
if model_id:
|
|
401
|
-
llm_service.model = model_id
|
|
402
|
-
agent = LocalAgent(
|
|
403
|
-
name=agent_def["name"],
|
|
404
|
-
description=agent_def["description"],
|
|
405
|
-
llm_service=llm_service,
|
|
406
|
-
services=services,
|
|
407
|
-
tools=agent_def["tools"],
|
|
408
|
-
temperature=agent_def.get("temperature", None),
|
|
409
|
-
voice_enabled=agent_def.get("voice_enabled", "disabled"),
|
|
410
|
-
voice_id=agent_def.get("voice_id", None),
|
|
411
|
-
)
|
|
412
|
-
agent.set_system_prompt(agent_def["system_prompt"])
|
|
413
|
-
if remoting_provider:
|
|
414
|
-
agent.set_custom_system_prompt(
|
|
415
|
-
self.agent_manager.get_remote_system_prompt()
|
|
416
|
-
)
|
|
417
|
-
agent.is_remoting_mode = True
|
|
418
|
-
agent.activate()
|
|
419
|
-
self.agent_manager.register_agent(agent)
|
|
420
|
-
|
|
421
|
-
from AgentCrew.modules.mcpclient.tool import register as mcp_register
|
|
422
|
-
|
|
423
|
-
mcp_register()
|
|
424
|
-
|
|
425
|
-
if remoting_provider:
|
|
426
|
-
from AgentCrew.modules.mcpclient import MCPSessionManager
|
|
427
|
-
|
|
428
|
-
mcp_manager = MCPSessionManager.get_instance()
|
|
429
|
-
mcp_manager.initialize_for_agent()
|
|
430
|
-
return self.agent_manager
|
|
58
|
+
@property
|
|
59
|
+
def services(self) -> Optional[Dict[str, Any]]:
|
|
60
|
+
return self.setup.services
|
|
431
61
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
initial_agent_selected = False
|
|
436
|
-
try:
|
|
437
|
-
last_agent = self.config_manager.get_last_used_agent()
|
|
438
|
-
|
|
439
|
-
if (
|
|
440
|
-
last_agent
|
|
441
|
-
and self.agent_manager
|
|
442
|
-
and last_agent in self.agent_manager.agents
|
|
443
|
-
):
|
|
444
|
-
if self.agent_manager.select_agent(last_agent):
|
|
445
|
-
initial_agent_selected = True
|
|
446
|
-
except Exception as e:
|
|
447
|
-
# Don't fail startup if restoration fails
|
|
448
|
-
click.echo(f"⚠️ Could not restore last used agent: {e}")
|
|
449
|
-
|
|
450
|
-
if not initial_agent_selected and self.agent_manager:
|
|
451
|
-
first_agent_name = list(self.agent_manager.agents.keys())[0]
|
|
452
|
-
if not self.agent_manager.select_agent(first_agent_name):
|
|
453
|
-
available_agents = ", ".join(self.agent_manager.agents.keys())
|
|
454
|
-
click.echo(
|
|
455
|
-
f"⚠️ Unknown agent: {first_agent_name}. Using default agent. Available agents: {available_agents}"
|
|
456
|
-
)
|
|
62
|
+
@property
|
|
63
|
+
def agent_manager(self):
|
|
64
|
+
return self.setup.agent_manager
|
|
457
65
|
|
|
458
66
|
def run_console(
|
|
459
67
|
self,
|
|
@@ -463,37 +71,26 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
463
71
|
memory_llm: Optional[str] = None,
|
|
464
72
|
with_voice: bool = False,
|
|
465
73
|
) -> None:
|
|
466
|
-
"""
|
|
467
|
-
Run AgentCrew in console/terminal mode.
|
|
468
|
-
|
|
469
|
-
Args:
|
|
470
|
-
provider: LLM provider name
|
|
471
|
-
agent_config: Path to agent configuration file
|
|
472
|
-
mcp_config: Path to MCP servers configuration file
|
|
473
|
-
memory_llm: LLM provider for memory service
|
|
474
|
-
with_voice: Enable voice input/output
|
|
475
|
-
"""
|
|
476
74
|
from AgentCrew.modules.console import ConsoleUI
|
|
477
75
|
from AgentCrew.modules.chat import MessageHandler
|
|
478
76
|
from AgentCrew.modules.mcpclient import MCPSessionManager
|
|
479
77
|
|
|
480
78
|
try:
|
|
481
79
|
if provider is None:
|
|
482
|
-
provider = self.
|
|
80
|
+
provider = self.setup.detect_provider()
|
|
483
81
|
if provider is None:
|
|
484
82
|
raise ValueError(
|
|
485
83
|
"No LLM API key found. Please set either ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY, GROQ_API_KEY, or DEEPINFRA_API_KEY"
|
|
486
84
|
)
|
|
487
85
|
|
|
488
|
-
services = self.setup_services(provider, memory_llm)
|
|
86
|
+
services = self.setup.setup_services(provider, memory_llm)
|
|
489
87
|
|
|
490
88
|
if mcp_config:
|
|
491
89
|
os.environ["MCP_CONFIG_PATH"] = mcp_config
|
|
492
90
|
|
|
493
|
-
self.setup_agents(services, agent_config)
|
|
494
|
-
self.restore_last_agent()
|
|
91
|
+
self.setup.setup_agents(services, agent_config)
|
|
92
|
+
self.setup.restore_last_agent()
|
|
495
93
|
|
|
496
|
-
# Create the message handler
|
|
497
94
|
message_handler = MessageHandler(
|
|
498
95
|
services["memory"], services["context_persistent"], with_voice
|
|
499
96
|
)
|
|
@@ -520,16 +117,6 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
520
117
|
memory_llm: Optional[str] = None,
|
|
521
118
|
with_voice: bool = False,
|
|
522
119
|
) -> None:
|
|
523
|
-
"""
|
|
524
|
-
Run AgentCrew in GUI mode.
|
|
525
|
-
|
|
526
|
-
Args:
|
|
527
|
-
provider: LLM provider name
|
|
528
|
-
agent_config: Path to agent configuration file
|
|
529
|
-
mcp_config: Path to MCP servers configuration file
|
|
530
|
-
memory_llm: LLM provider for memory service
|
|
531
|
-
with_voice: Enable voice input/output
|
|
532
|
-
"""
|
|
533
120
|
from PySide6.QtCore import QCoreApplication
|
|
534
121
|
from PySide6.QtCore import Qt
|
|
535
122
|
from PySide6.QtWidgets import QApplication
|
|
@@ -538,29 +125,25 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
538
125
|
from AgentCrew.modules.mcpclient import MCPSessionManager
|
|
539
126
|
|
|
540
127
|
try:
|
|
541
|
-
# Detect provider if not specified
|
|
542
128
|
if provider is None:
|
|
543
|
-
provider = self.
|
|
129
|
+
provider = self.setup.detect_provider()
|
|
544
130
|
if provider is None:
|
|
545
|
-
# Show config window to setup API keys
|
|
546
131
|
from AgentCrew.modules.gui.widgets.config_window import ConfigWindow
|
|
547
132
|
|
|
548
133
|
app = QApplication(sys.argv)
|
|
549
134
|
config_window = ConfigWindow()
|
|
550
|
-
config_window.tab_widget.setCurrentIndex(3)
|
|
135
|
+
config_window.tab_widget.setCurrentIndex(3)
|
|
551
136
|
config_window.show()
|
|
552
137
|
sys.exit(app.exec())
|
|
553
138
|
|
|
554
|
-
services = self.setup_services(provider, memory_llm)
|
|
139
|
+
services = self.setup.setup_services(provider, memory_llm)
|
|
555
140
|
|
|
556
141
|
if mcp_config:
|
|
557
142
|
os.environ["MCP_CONFIG_PATH"] = mcp_config
|
|
558
143
|
|
|
559
|
-
|
|
560
|
-
self.
|
|
561
|
-
self.restore_last_agent()
|
|
144
|
+
self.setup.setup_agents(services, agent_config)
|
|
145
|
+
self.setup.restore_last_agent()
|
|
562
146
|
|
|
563
|
-
# Create the message handler
|
|
564
147
|
message_handler = MessageHandler(
|
|
565
148
|
services["memory"], services["context_persistent"], with_voice
|
|
566
149
|
)
|
|
@@ -590,53 +173,36 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
590
173
|
mcp_config: Optional[str] = None,
|
|
591
174
|
memory_llm: Optional[str] = None,
|
|
592
175
|
) -> None:
|
|
593
|
-
"""
|
|
594
|
-
Run AgentCrew as an A2A server.
|
|
595
|
-
|
|
596
|
-
Args:
|
|
597
|
-
host: Host to bind the server to
|
|
598
|
-
port: Port to bind the server to
|
|
599
|
-
base_url: Base URL for agent endpoints
|
|
600
|
-
provider: LLM provider name
|
|
601
|
-
model_id: Model ID from provider
|
|
602
|
-
agent_config: Path to agent configuration file
|
|
603
|
-
api_key: API key for authentication (optional)
|
|
604
|
-
mcp_config: Path to MCP servers configuration file
|
|
605
|
-
memory_llm: LLM provider for memory service
|
|
606
|
-
"""
|
|
607
176
|
from AgentCrew.modules.a2a.server import A2AServer
|
|
608
177
|
from AgentCrew.modules.mcpclient import MCPSessionManager
|
|
609
178
|
|
|
610
179
|
try:
|
|
611
|
-
# Set default base URL if not provided
|
|
612
180
|
if not base_url:
|
|
613
181
|
base_url = f"http://{host}:{port}"
|
|
614
182
|
|
|
615
|
-
# Detect provider if not specified
|
|
616
183
|
if provider is None:
|
|
617
|
-
provider = self.
|
|
184
|
+
provider = self.setup.detect_provider()
|
|
618
185
|
if provider is None:
|
|
619
186
|
raise ValueError(
|
|
620
187
|
"No LLM API key found. Please set either ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY, GROQ_API_KEY, or DEEPINFRA_API_KEY"
|
|
621
188
|
)
|
|
622
189
|
|
|
623
|
-
services = self.setup_services(
|
|
190
|
+
services = self.setup.setup_services(
|
|
191
|
+
provider, memory_llm, need_memory=False
|
|
192
|
+
)
|
|
624
193
|
|
|
625
194
|
if mcp_config:
|
|
626
195
|
os.environ["MCP_CONFIG_PATH"] = mcp_config
|
|
627
196
|
|
|
628
197
|
os.environ["AGENTCREW_DISABLE_GUI"] = "true"
|
|
629
198
|
|
|
630
|
-
|
|
631
|
-
self.setup_agents(services, agent_config, provider, model_id)
|
|
199
|
+
self.setup.setup_agents(services, agent_config, provider, model_id)
|
|
632
200
|
|
|
633
201
|
if self.agent_manager is None:
|
|
634
202
|
raise ValueError("Agent manager is not initialized")
|
|
635
203
|
|
|
636
|
-
# Get agent manager
|
|
637
204
|
self.agent_manager.enforce_transfer = False
|
|
638
205
|
|
|
639
|
-
# Create and start server
|
|
640
206
|
server = A2AServer(
|
|
641
207
|
agent_manager=self.agent_manager,
|
|
642
208
|
host=host,
|
|
@@ -659,33 +225,17 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
659
225
|
MCPSessionManager.get_instance().cleanup()
|
|
660
226
|
|
|
661
227
|
def _parse_output_schema(self, schema_input: str) -> tuple[str, dict]:
|
|
662
|
-
"""
|
|
663
|
-
Parse output schema from file or JSON string and create a custom system prompt.
|
|
664
|
-
|
|
665
|
-
Args:
|
|
666
|
-
schema_input: Either a file path to a JSON schema or a JSON schema string
|
|
667
|
-
|
|
668
|
-
Returns:
|
|
669
|
-
Custom system prompt instructing the agent to follow the schema
|
|
670
|
-
|
|
671
|
-
Raises:
|
|
672
|
-
ValueError: If schema is invalid JSON or file doesn't exist
|
|
673
|
-
"""
|
|
674
228
|
try:
|
|
675
229
|
from AgentCrew.modules.prompts.constants import SCHEMA_ENFORCEMENT_PROMPT
|
|
676
230
|
|
|
677
|
-
# Try to parse as file path first
|
|
678
231
|
if os.path.exists(schema_input):
|
|
679
232
|
with open(schema_input, "r", encoding="utf-8") as f:
|
|
680
233
|
schema_dict = json.load(f)
|
|
681
234
|
else:
|
|
682
|
-
# Try to parse as JSON string
|
|
683
235
|
schema_dict = json.loads(schema_input)
|
|
684
236
|
|
|
685
|
-
# Format the schema as a pretty-printed JSON string
|
|
686
237
|
schema_json = json.dumps(schema_dict, indent=2)
|
|
687
238
|
|
|
688
|
-
# Create enforcement prompt
|
|
689
239
|
enforcement_prompt = SCHEMA_ENFORCEMENT_PROMPT.replace(
|
|
690
240
|
"{schema_json}", schema_json
|
|
691
241
|
)
|
|
@@ -699,21 +249,8 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
699
249
|
def _validate_response_against_schema(
|
|
700
250
|
self, response: str, schema_dict: Dict[str, Any]
|
|
701
251
|
) -> tuple[bool, Optional[str]]:
|
|
702
|
-
"""
|
|
703
|
-
Validate agent response against JSON schema.
|
|
704
|
-
|
|
705
|
-
Args:
|
|
706
|
-
response: Agent's response string (expected to be valid JSON)
|
|
707
|
-
schema_dict: JSON schema dictionary
|
|
708
|
-
|
|
709
|
-
Returns:
|
|
710
|
-
Tuple of (is_valid, error_message)
|
|
711
|
-
- is_valid: True if response matches schema, False otherwise
|
|
712
|
-
- error_message: Detailed error message if validation fails, None if valid
|
|
713
|
-
"""
|
|
714
252
|
from jsonschema import validate, ValidationError
|
|
715
253
|
|
|
716
|
-
# Parse response as JSON
|
|
717
254
|
try:
|
|
718
255
|
response_json = json.loads(response)
|
|
719
256
|
except json.JSONDecodeError as e:
|
|
@@ -722,7 +259,6 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
722
259
|
f"Response is not valid JSON: {e}\n\nResponse received:\n{response[:500]}",
|
|
723
260
|
)
|
|
724
261
|
|
|
725
|
-
# Validate against schema
|
|
726
262
|
try:
|
|
727
263
|
validate(instance=response_json, schema=schema_dict)
|
|
728
264
|
return True, None
|
|
@@ -751,46 +287,29 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
751
287
|
memory_llm: Optional[str] = None,
|
|
752
288
|
output_schema: Optional[str] = None,
|
|
753
289
|
) -> str:
|
|
754
|
-
"""
|
|
755
|
-
Run a single job/task with an agent.
|
|
756
|
-
|
|
757
|
-
Args:
|
|
758
|
-
agent: Name of the agent to run
|
|
759
|
-
task: Task description
|
|
760
|
-
files: List of file paths to attach
|
|
761
|
-
provider: LLM provider name
|
|
762
|
-
model_id: Model ID from provider
|
|
763
|
-
agent_config: Path to agent configuration file
|
|
764
|
-
mcp_config: Path to MCP servers configuration file
|
|
765
|
-
memory_llm: LLM provider for memory service
|
|
766
|
-
output_schema: JSON schema (file path or JSON string) for enforcing structured output
|
|
767
|
-
|
|
768
|
-
Returns:
|
|
769
|
-
Agent response as string
|
|
770
|
-
"""
|
|
771
290
|
from AgentCrew.modules.chat import MessageHandler
|
|
772
291
|
from AgentCrew.modules.mcpclient import MCPSessionManager
|
|
292
|
+
from AgentCrew.modules.llm.model_registry import ModelRegistry
|
|
773
293
|
|
|
774
294
|
try:
|
|
775
|
-
# Detect provider if not specified
|
|
776
295
|
if provider is None:
|
|
777
|
-
provider = self.
|
|
296
|
+
provider = self.setup.detect_provider()
|
|
778
297
|
if provider is None:
|
|
779
298
|
raise ValueError(
|
|
780
299
|
"No LLM API key found. Please set either ANTHROPIC_API_KEY, GEMINI_API_KEY, OPENAI_API_KEY, GROQ_API_KEY, or DEEPINFRA_API_KEY"
|
|
781
300
|
)
|
|
782
301
|
|
|
783
|
-
services = self.setup_services(
|
|
302
|
+
services = self.setup.setup_services(
|
|
303
|
+
provider, memory_llm, need_memory=False
|
|
304
|
+
)
|
|
784
305
|
|
|
785
306
|
if mcp_config:
|
|
786
307
|
os.environ["MCP_CONFIG_PATH"] = mcp_config
|
|
787
308
|
|
|
788
309
|
os.environ["AGENTCREW_DISABLE_GUI"] = "true"
|
|
789
310
|
|
|
790
|
-
|
|
791
|
-
self.setup_agents(services, agent_config)
|
|
311
|
+
self.setup.setup_agents(services, agent_config)
|
|
792
312
|
|
|
793
|
-
# Get agent manager
|
|
794
313
|
llm_manager = ServiceManager.get_instance()
|
|
795
314
|
|
|
796
315
|
llm_service = llm_manager.get_service(provider)
|
|
@@ -812,11 +331,8 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
812
331
|
current_agent = self.agent_manager.get_local_agent(agent)
|
|
813
332
|
|
|
814
333
|
if current_agent:
|
|
815
|
-
# Parse schema if provided
|
|
816
334
|
schema_dict = None
|
|
817
335
|
if output_schema:
|
|
818
|
-
from AgentCrew.modules.llm.model_registry import ModelRegistry
|
|
819
|
-
|
|
820
336
|
schema_prompt, schema_dict = self._parse_output_schema(
|
|
821
337
|
output_schema
|
|
822
338
|
)
|
|
@@ -824,7 +340,6 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
824
340
|
f"{provider}/{model_id}"
|
|
825
341
|
):
|
|
826
342
|
current_agent.llm.structured_output = schema_dict
|
|
827
|
-
|
|
828
343
|
else:
|
|
829
344
|
current_agent.set_custom_system_prompt(schema_prompt)
|
|
830
345
|
|
|
@@ -836,14 +351,12 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
836
351
|
message_handler.is_non_interactive = True
|
|
837
352
|
message_handler.agent = current_agent
|
|
838
353
|
|
|
839
|
-
# Process files if provided
|
|
840
354
|
if files:
|
|
841
355
|
for file_path in files:
|
|
842
356
|
asyncio.run(
|
|
843
357
|
message_handler.process_user_input(f"/file {file_path}")
|
|
844
358
|
)
|
|
845
359
|
|
|
846
|
-
# Process task with retry logic for schema validation
|
|
847
360
|
max_attempts = 4
|
|
848
361
|
attempt = 0
|
|
849
362
|
response = None
|
|
@@ -856,7 +369,7 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
856
369
|
message_handler.get_assistant_response()
|
|
857
370
|
)
|
|
858
371
|
if not output_schema or not schema_dict:
|
|
859
|
-
break
|
|
372
|
+
break
|
|
860
373
|
|
|
861
374
|
if response is None:
|
|
862
375
|
asyncio.run(
|
|
@@ -864,13 +377,13 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
864
377
|
"No response was generated. Please try again."
|
|
865
378
|
)
|
|
866
379
|
)
|
|
867
|
-
continue
|
|
380
|
+
continue
|
|
868
381
|
|
|
869
382
|
success, retry_message = self._validate_response_against_schema(
|
|
870
383
|
response, schema_dict
|
|
871
384
|
)
|
|
872
385
|
if success:
|
|
873
|
-
break
|
|
386
|
+
break
|
|
874
387
|
else:
|
|
875
388
|
if retry_message:
|
|
876
389
|
asyncio.run(
|
|
@@ -889,116 +402,4 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
889
402
|
raise
|
|
890
403
|
|
|
891
404
|
def login(self) -> bool:
|
|
892
|
-
|
|
893
|
-
Authenticate with GitHub Copilot and save the API key to config.
|
|
894
|
-
|
|
895
|
-
Returns:
|
|
896
|
-
True if authentication succeeded, False otherwise
|
|
897
|
-
"""
|
|
898
|
-
try:
|
|
899
|
-
click.echo("🔐 Starting GitHub Copilot authentication...")
|
|
900
|
-
|
|
901
|
-
# Step 1: Request device code
|
|
902
|
-
resp = requests.post(
|
|
903
|
-
"https://github.com/login/device/code",
|
|
904
|
-
headers={
|
|
905
|
-
"accept": "application/json",
|
|
906
|
-
"editor-version": "vscode/1.100.3",
|
|
907
|
-
"editor-plugin-version": "GitHub.copilot/1.330.0",
|
|
908
|
-
"content-type": "application/json",
|
|
909
|
-
"user-agent": "GithubCopilot/1.330.0",
|
|
910
|
-
"accept-encoding": "gzip,deflate,br",
|
|
911
|
-
},
|
|
912
|
-
data='{"client_id":"Iv1.b507a08c87ecfe98","scope":"read:user"}',
|
|
913
|
-
)
|
|
914
|
-
|
|
915
|
-
if resp.status_code != 200:
|
|
916
|
-
click.echo(
|
|
917
|
-
f"❌ Failed to get device code: {resp.status_code}", err=True
|
|
918
|
-
)
|
|
919
|
-
return False
|
|
920
|
-
|
|
921
|
-
# Parse the response json, isolating the device_code, user_code, and verification_uri
|
|
922
|
-
resp_json = resp.json()
|
|
923
|
-
device_code = resp_json.get("device_code")
|
|
924
|
-
user_code = resp_json.get("user_code")
|
|
925
|
-
verification_uri = resp_json.get("verification_uri")
|
|
926
|
-
|
|
927
|
-
if not all([device_code, user_code, verification_uri]):
|
|
928
|
-
click.echo("❌ Invalid response from GitHub", err=True)
|
|
929
|
-
return False
|
|
930
|
-
|
|
931
|
-
# Print the user code and verification uri
|
|
932
|
-
click.echo(
|
|
933
|
-
f"📋 Please visit {verification_uri} and enter code: {user_code}"
|
|
934
|
-
)
|
|
935
|
-
click.echo("⏳ Waiting for authentication...")
|
|
936
|
-
|
|
937
|
-
webbrowser.open(verification_uri)
|
|
938
|
-
|
|
939
|
-
# Step 2: Poll for access token
|
|
940
|
-
while True:
|
|
941
|
-
time.sleep(5)
|
|
942
|
-
|
|
943
|
-
resp = requests.post(
|
|
944
|
-
"https://github.com/login/oauth/access_token",
|
|
945
|
-
headers={
|
|
946
|
-
"accept": "application/json",
|
|
947
|
-
"editor-version": "vscode/1.100.3",
|
|
948
|
-
"editor-plugin-version": "GitHub.copilot/1.330.0",
|
|
949
|
-
"content-type": "application/json",
|
|
950
|
-
"user-agent": "GithubCopilot/1.330.0",
|
|
951
|
-
"accept-encoding": "gzip,deflate,br",
|
|
952
|
-
},
|
|
953
|
-
data=f'{{"client_id":"Iv1.b507a08c87ecfe98","device_code":"{device_code}","grant_type":"urn:ietf:params:oauth:grant-type:device_code"}}',
|
|
954
|
-
)
|
|
955
|
-
|
|
956
|
-
# Parse the response json
|
|
957
|
-
resp_json = resp.json()
|
|
958
|
-
access_token = resp_json.get("access_token")
|
|
959
|
-
error = resp_json.get("error")
|
|
960
|
-
|
|
961
|
-
if access_token:
|
|
962
|
-
click.echo("✅ Authentication successful!")
|
|
963
|
-
break
|
|
964
|
-
elif error == "authorization_pending":
|
|
965
|
-
continue # Keep polling
|
|
966
|
-
elif error == "slow_down":
|
|
967
|
-
time.sleep(5) # Additional delay
|
|
968
|
-
continue
|
|
969
|
-
elif error == "expired_token":
|
|
970
|
-
click.echo("❌ Authentication expired. Please try again.", err=True)
|
|
971
|
-
return False
|
|
972
|
-
elif error == "access_denied":
|
|
973
|
-
click.echo("❌ Authentication denied by user.", err=True)
|
|
974
|
-
return False
|
|
975
|
-
else:
|
|
976
|
-
click.echo(f"❌ Authentication error: {error}", err=True)
|
|
977
|
-
return False
|
|
978
|
-
|
|
979
|
-
# Step 3: Save the token to config
|
|
980
|
-
global_config = self.config_manager.read_global_config_data()
|
|
981
|
-
|
|
982
|
-
# Ensure api_keys section exists
|
|
983
|
-
if "api_keys" not in global_config:
|
|
984
|
-
global_config["api_keys"] = {}
|
|
985
|
-
|
|
986
|
-
# Save the token
|
|
987
|
-
global_config["api_keys"]["GITHUB_COPILOT_API_KEY"] = access_token
|
|
988
|
-
self.config_manager.write_global_config_data(global_config)
|
|
989
|
-
|
|
990
|
-
click.echo("💾 GitHub Copilot API key saved to config file!")
|
|
991
|
-
click.echo(
|
|
992
|
-
"🚀 You can now use GitHub Copilot with --provider github_copilot"
|
|
993
|
-
)
|
|
994
|
-
return True
|
|
995
|
-
|
|
996
|
-
except ImportError:
|
|
997
|
-
click.echo(
|
|
998
|
-
"❌ Error: 'requests' package is required for authentication", err=True
|
|
999
|
-
)
|
|
1000
|
-
click.echo("Install it with: pip install requests")
|
|
1001
|
-
return False
|
|
1002
|
-
except Exception as e:
|
|
1003
|
-
click.echo(f"❌ Authentication failed: {str(e)}", err=True)
|
|
1004
|
-
return False
|
|
405
|
+
return self.setup.login()
|