agentcrew-ai 0.8.13__py3-none-any.whl → 0.9.1__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 +46 -634
- AgentCrew/main_docker.py +1 -30
- AgentCrew/modules/a2a/common/client/card_resolver.py +27 -8
- AgentCrew/modules/a2a/server.py +5 -0
- AgentCrew/modules/a2a/task_manager.py +1 -0
- AgentCrew/modules/agents/local_agent.py +2 -2
- AgentCrew/modules/chat/message/command_processor.py +33 -8
- AgentCrew/modules/chat/message/conversation.py +18 -1
- AgentCrew/modules/chat/message/handler.py +5 -1
- AgentCrew/modules/code_analysis/service.py +50 -7
- AgentCrew/modules/code_analysis/tool.py +9 -8
- AgentCrew/modules/console/completers.py +5 -1
- AgentCrew/modules/console/console_ui.py +23 -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 +127 -7
- AgentCrew/modules/console/visual_mode/__init__.py +5 -0
- AgentCrew/modules/console/visual_mode/viewer.py +41 -0
- AgentCrew/modules/console/visual_mode/viewer_input_handler.py +315 -0
- AgentCrew/modules/console/visual_mode/viewer_ui.py +608 -0
- AgentCrew/modules/gui/components/command_handler.py +137 -29
- AgentCrew/modules/gui/components/menu_components.py +8 -7
- 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 +78 -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 +4 -4
- AgentCrew/modules/gui/widgets/history_sidebar.py +6 -1
- AgentCrew/modules/llm/constants.py +28 -9
- AgentCrew/modules/mcpclient/service.py +0 -1
- AgentCrew/modules/memory/base_service.py +13 -0
- AgentCrew/modules/memory/chroma_service.py +50 -0
- AgentCrew/setup.py +470 -0
- {agentcrew_ai-0.8.13.dist-info → agentcrew_ai-0.9.1.dist-info}/METADATA +1 -1
- {agentcrew_ai-0.8.13.dist-info → agentcrew_ai-0.9.1.dist-info}/RECORD +49 -40
- {agentcrew_ai-0.8.13.dist-info → agentcrew_ai-0.9.1.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.1.dist-info}/entry_points.txt +0 -0
- {agentcrew_ai-0.8.13.dist-info → agentcrew_ai-0.9.1.dist-info}/licenses/LICENSE +0 -0
- {agentcrew_ai-0.8.13.dist-info → agentcrew_ai-0.9.1.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()
|
|
58
|
+
@property
|
|
59
|
+
def services(self) -> Optional[Dict[str, Any]]:
|
|
60
|
+
return self.setup.services
|
|
424
61
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
mcp_manager = MCPSessionManager.get_instance()
|
|
429
|
-
mcp_manager.initialize_for_agent()
|
|
430
|
-
return self.agent_manager
|
|
431
|
-
|
|
432
|
-
return self.agent_manager
|
|
433
|
-
|
|
434
|
-
def restore_last_agent(self) -> None:
|
|
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
|
)
|
|
@@ -696,33 +246,30 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
696
246
|
except Exception as e:
|
|
697
247
|
raise ValueError(f"Failed to load output schema: {e}")
|
|
698
248
|
|
|
249
|
+
def _clean_json_response(self, response: str) -> str:
|
|
250
|
+
import re
|
|
251
|
+
|
|
252
|
+
cleaned = response.strip()
|
|
253
|
+
pattern = r"^```(?:json)?\s*\n?(.*?)\n?```$"
|
|
254
|
+
match = re.match(pattern, cleaned, re.DOTALL)
|
|
255
|
+
if match:
|
|
256
|
+
cleaned = match.group(1).strip()
|
|
257
|
+
return cleaned
|
|
258
|
+
|
|
699
259
|
def _validate_response_against_schema(
|
|
700
260
|
self, response: str, schema_dict: Dict[str, Any]
|
|
701
261
|
) -> 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
262
|
from jsonschema import validate, ValidationError
|
|
715
263
|
|
|
716
|
-
# Parse response as JSON
|
|
717
264
|
try:
|
|
718
|
-
|
|
265
|
+
cleaned_response = self._clean_json_response(response)
|
|
266
|
+
response_json = json.loads(cleaned_response)
|
|
719
267
|
except json.JSONDecodeError as e:
|
|
720
268
|
return (
|
|
721
269
|
False,
|
|
722
270
|
f"Response is not valid JSON: {e}\n\nResponse received:\n{response[:500]}",
|
|
723
271
|
)
|
|
724
272
|
|
|
725
|
-
# Validate against schema
|
|
726
273
|
try:
|
|
727
274
|
validate(instance=response_json, schema=schema_dict)
|
|
728
275
|
return True, None
|
|
@@ -751,46 +298,29 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
751
298
|
memory_llm: Optional[str] = None,
|
|
752
299
|
output_schema: Optional[str] = None,
|
|
753
300
|
) -> 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
301
|
from AgentCrew.modules.chat import MessageHandler
|
|
772
302
|
from AgentCrew.modules.mcpclient import MCPSessionManager
|
|
303
|
+
from AgentCrew.modules.llm.model_registry import ModelRegistry
|
|
773
304
|
|
|
774
305
|
try:
|
|
775
|
-
# Detect provider if not specified
|
|
776
306
|
if provider is None:
|
|
777
|
-
provider = self.
|
|
307
|
+
provider = self.setup.detect_provider()
|
|
778
308
|
if provider is None:
|
|
779
309
|
raise ValueError(
|
|
780
310
|
"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
311
|
)
|
|
782
312
|
|
|
783
|
-
services = self.setup_services(
|
|
313
|
+
services = self.setup.setup_services(
|
|
314
|
+
provider, memory_llm, need_memory=False
|
|
315
|
+
)
|
|
784
316
|
|
|
785
317
|
if mcp_config:
|
|
786
318
|
os.environ["MCP_CONFIG_PATH"] = mcp_config
|
|
787
319
|
|
|
788
320
|
os.environ["AGENTCREW_DISABLE_GUI"] = "true"
|
|
789
321
|
|
|
790
|
-
|
|
791
|
-
self.setup_agents(services, agent_config)
|
|
322
|
+
self.setup.setup_agents(services, agent_config)
|
|
792
323
|
|
|
793
|
-
# Get agent manager
|
|
794
324
|
llm_manager = ServiceManager.get_instance()
|
|
795
325
|
|
|
796
326
|
llm_service = llm_manager.get_service(provider)
|
|
@@ -812,11 +342,8 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
812
342
|
current_agent = self.agent_manager.get_local_agent(agent)
|
|
813
343
|
|
|
814
344
|
if current_agent:
|
|
815
|
-
# Parse schema if provided
|
|
816
345
|
schema_dict = None
|
|
817
346
|
if output_schema:
|
|
818
|
-
from AgentCrew.modules.llm.model_registry import ModelRegistry
|
|
819
|
-
|
|
820
347
|
schema_prompt, schema_dict = self._parse_output_schema(
|
|
821
348
|
output_schema
|
|
822
349
|
)
|
|
@@ -824,7 +351,6 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
824
351
|
f"{provider}/{model_id}"
|
|
825
352
|
):
|
|
826
353
|
current_agent.llm.structured_output = schema_dict
|
|
827
|
-
|
|
828
354
|
else:
|
|
829
355
|
current_agent.set_custom_system_prompt(schema_prompt)
|
|
830
356
|
|
|
@@ -836,14 +362,12 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
836
362
|
message_handler.is_non_interactive = True
|
|
837
363
|
message_handler.agent = current_agent
|
|
838
364
|
|
|
839
|
-
# Process files if provided
|
|
840
365
|
if files:
|
|
841
366
|
for file_path in files:
|
|
842
367
|
asyncio.run(
|
|
843
368
|
message_handler.process_user_input(f"/file {file_path}")
|
|
844
369
|
)
|
|
845
370
|
|
|
846
|
-
# Process task with retry logic for schema validation
|
|
847
371
|
max_attempts = 4
|
|
848
372
|
attempt = 0
|
|
849
373
|
response = None
|
|
@@ -856,7 +380,7 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
856
380
|
message_handler.get_assistant_response()
|
|
857
381
|
)
|
|
858
382
|
if not output_schema or not schema_dict:
|
|
859
|
-
break
|
|
383
|
+
break
|
|
860
384
|
|
|
861
385
|
if response is None:
|
|
862
386
|
asyncio.run(
|
|
@@ -864,13 +388,13 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
864
388
|
"No response was generated. Please try again."
|
|
865
389
|
)
|
|
866
390
|
)
|
|
867
|
-
continue
|
|
391
|
+
continue
|
|
868
392
|
|
|
869
393
|
success, retry_message = self._validate_response_against_schema(
|
|
870
394
|
response, schema_dict
|
|
871
395
|
)
|
|
872
396
|
if success:
|
|
873
|
-
break
|
|
397
|
+
break
|
|
874
398
|
else:
|
|
875
399
|
if retry_message:
|
|
876
400
|
asyncio.run(
|
|
@@ -889,116 +413,4 @@ tools = ["memory", "browser", "web_search", "code_analysis"]
|
|
|
889
413
|
raise
|
|
890
414
|
|
|
891
415
|
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
|
|
416
|
+
return self.setup.login()
|