alita-sdk 0.3.457b0__py3-none-any.whl → 0.3.459__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.
- alita_sdk/cli/__init__.py +10 -0
- alita_sdk/cli/__main__.py +17 -0
- alita_sdk/cli/agents.py +1055 -0
- alita_sdk/cli/cli.py +156 -0
- alita_sdk/cli/config.py +134 -0
- alita_sdk/cli/formatting.py +182 -0
- alita_sdk/cli/toolkit.py +330 -0
- alita_sdk/runtime/toolkits/artifact.py +5 -6
- alita_sdk/runtime/tools/function.py +3 -4
- alita_sdk/tools/qtest/api_wrapper.py +790 -14
- alita_sdk/tools/sharepoint/api_wrapper.py +22 -2
- alita_sdk/tools/sharepoint/authorization_helper.py +17 -1
- {alita_sdk-0.3.457b0.dist-info → alita_sdk-0.3.459.dist-info}/METADATA +6 -1
- {alita_sdk-0.3.457b0.dist-info → alita_sdk-0.3.459.dist-info}/RECORD +18 -10
- alita_sdk-0.3.459.dist-info/entry_points.txt +2 -0
- {alita_sdk-0.3.457b0.dist-info → alita_sdk-0.3.459.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.457b0.dist-info → alita_sdk-0.3.459.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.457b0.dist-info → alita_sdk-0.3.459.dist-info}/top_level.txt +0 -0
alita_sdk/cli/agents.py
ADDED
|
@@ -0,0 +1,1055 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent commands for Alita CLI.
|
|
3
|
+
|
|
4
|
+
Provides commands to work with agents interactively or in handoff mode,
|
|
5
|
+
supporting both platform agents and local agent definition files.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
import json
|
|
10
|
+
import logging
|
|
11
|
+
import sqlite3
|
|
12
|
+
from typing import Optional, Dict, Any, List
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import yaml
|
|
15
|
+
|
|
16
|
+
from rich.console import Console
|
|
17
|
+
from rich.panel import Panel
|
|
18
|
+
from rich.table import Table
|
|
19
|
+
from rich.markdown import Markdown
|
|
20
|
+
from rich import box
|
|
21
|
+
from rich.text import Text
|
|
22
|
+
from rich.status import Status
|
|
23
|
+
from rich.live import Live
|
|
24
|
+
|
|
25
|
+
from .cli import get_client
|
|
26
|
+
from .config import substitute_env_vars
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
# Create a rich console for beautiful output
|
|
31
|
+
console = Console()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _print_banner(agent_name: str, agent_type: str = "local"):
|
|
35
|
+
"""Print a nice banner for the chat session using rich."""
|
|
36
|
+
content = Text()
|
|
37
|
+
content.append("🤖 ALITA AGENT CHAT\n\n", style="bold cyan")
|
|
38
|
+
content.append(f"Agent: ", style="bold")
|
|
39
|
+
content.append(f"{agent_name}\n", style="cyan")
|
|
40
|
+
content.append(f"Type: ", style="bold")
|
|
41
|
+
content.append(f"{agent_type}", style="cyan")
|
|
42
|
+
|
|
43
|
+
panel = Panel(
|
|
44
|
+
content,
|
|
45
|
+
box=box.DOUBLE,
|
|
46
|
+
border_style="cyan",
|
|
47
|
+
padding=(1, 2)
|
|
48
|
+
)
|
|
49
|
+
console.print(panel)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _print_help():
|
|
53
|
+
"""Print help message with commands using rich table."""
|
|
54
|
+
table = Table(
|
|
55
|
+
show_header=True,
|
|
56
|
+
header_style="bold yellow",
|
|
57
|
+
border_style="yellow",
|
|
58
|
+
box=box.ROUNDED,
|
|
59
|
+
title="Commands",
|
|
60
|
+
title_style="bold yellow"
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
table.add_column("Command", style="cyan", no_wrap=True)
|
|
64
|
+
table.add_column("Description", style="white")
|
|
65
|
+
|
|
66
|
+
table.add_row("/clear", "Clear conversation history")
|
|
67
|
+
table.add_row("/history", "Show conversation history")
|
|
68
|
+
table.add_row("/save", "Save conversation to file")
|
|
69
|
+
table.add_row("/help", "Show this help")
|
|
70
|
+
table.add_row("exit/quit", "End conversation")
|
|
71
|
+
table.add_row("", "")
|
|
72
|
+
table.add_row("@", "Mention files")
|
|
73
|
+
table.add_row("/", "Run commands")
|
|
74
|
+
|
|
75
|
+
console.print(table)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def load_agent_definition(file_path: str) -> Dict[str, Any]:
|
|
79
|
+
"""
|
|
80
|
+
Load agent definition from file.
|
|
81
|
+
|
|
82
|
+
Supports:
|
|
83
|
+
- YAML files (.yaml, .yml)
|
|
84
|
+
- JSON files (.json)
|
|
85
|
+
- Markdown files with YAML frontmatter (.md)
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
file_path: Path to agent definition file
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Dictionary with agent configuration
|
|
92
|
+
"""
|
|
93
|
+
path = Path(file_path)
|
|
94
|
+
|
|
95
|
+
if not path.exists():
|
|
96
|
+
raise FileNotFoundError(f"Agent definition not found: {file_path}")
|
|
97
|
+
|
|
98
|
+
content = path.read_text()
|
|
99
|
+
|
|
100
|
+
# Handle markdown with YAML frontmatter
|
|
101
|
+
if path.suffix == '.md':
|
|
102
|
+
if content.startswith('---'):
|
|
103
|
+
parts = content.split('---', 2)
|
|
104
|
+
if len(parts) >= 3:
|
|
105
|
+
frontmatter = yaml.safe_load(parts[1])
|
|
106
|
+
system_prompt = parts[2].strip()
|
|
107
|
+
|
|
108
|
+
# Apply environment variable substitution
|
|
109
|
+
system_prompt = substitute_env_vars(system_prompt)
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
'name': frontmatter.get('name', path.stem),
|
|
113
|
+
'description': frontmatter.get('description', ''),
|
|
114
|
+
'system_prompt': system_prompt,
|
|
115
|
+
'model': frontmatter.get('model'),
|
|
116
|
+
'tools': frontmatter.get('tools', []),
|
|
117
|
+
'temperature': frontmatter.get('temperature'),
|
|
118
|
+
'max_tokens': frontmatter.get('max_tokens'),
|
|
119
|
+
'toolkit_configs': frontmatter.get('toolkit_configs', []),
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# Plain markdown - use content as system prompt
|
|
123
|
+
return {
|
|
124
|
+
'name': path.stem,
|
|
125
|
+
'system_prompt': substitute_env_vars(content),
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
# Handle YAML
|
|
129
|
+
if path.suffix in ['.yaml', '.yml']:
|
|
130
|
+
content = substitute_env_vars(content)
|
|
131
|
+
config = yaml.safe_load(content)
|
|
132
|
+
if 'system_prompt' in config:
|
|
133
|
+
config['system_prompt'] = substitute_env_vars(config['system_prompt'])
|
|
134
|
+
return config
|
|
135
|
+
|
|
136
|
+
# Handle JSON
|
|
137
|
+
if path.suffix == '.json':
|
|
138
|
+
content = substitute_env_vars(content)
|
|
139
|
+
config = json.loads(content)
|
|
140
|
+
if 'system_prompt' in config:
|
|
141
|
+
config['system_prompt'] = substitute_env_vars(config['system_prompt'])
|
|
142
|
+
return config
|
|
143
|
+
|
|
144
|
+
raise ValueError(f"Unsupported file format: {path.suffix}")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def load_toolkit_config(file_path: str) -> Dict[str, Any]:
|
|
148
|
+
"""Load toolkit configuration from JSON file with env var substitution."""
|
|
149
|
+
path = Path(file_path)
|
|
150
|
+
|
|
151
|
+
if not path.exists():
|
|
152
|
+
raise FileNotFoundError(f"Toolkit configuration not found: {file_path}")
|
|
153
|
+
|
|
154
|
+
with open(path) as f:
|
|
155
|
+
content = f.read()
|
|
156
|
+
|
|
157
|
+
# Apply environment variable substitution
|
|
158
|
+
content = substitute_env_vars(content)
|
|
159
|
+
return json.loads(content)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def _select_agent_interactive(client, config) -> Optional[str]:
|
|
163
|
+
"""
|
|
164
|
+
Show interactive menu to select an agent from platform and local agents.
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
Agent source (name/id for platform, file path for local) or None if cancelled
|
|
168
|
+
"""
|
|
169
|
+
from .config import CLIConfig
|
|
170
|
+
|
|
171
|
+
console.print("\n🤖 [bold cyan]Select an agent to chat with:[/bold cyan]\n")
|
|
172
|
+
|
|
173
|
+
agents_list = []
|
|
174
|
+
|
|
175
|
+
# Load platform agents
|
|
176
|
+
try:
|
|
177
|
+
platform_agents = client.get_list_of_apps()
|
|
178
|
+
for agent in platform_agents:
|
|
179
|
+
agents_list.append({
|
|
180
|
+
'type': 'platform',
|
|
181
|
+
'name': agent['name'],
|
|
182
|
+
'source': agent['name'],
|
|
183
|
+
'description': agent.get('description', '')[:60]
|
|
184
|
+
})
|
|
185
|
+
except Exception as e:
|
|
186
|
+
logger.debug(f"Failed to load platform agents: {e}")
|
|
187
|
+
|
|
188
|
+
# Load local agents
|
|
189
|
+
agents_dir = config.agents_dir
|
|
190
|
+
search_dir = Path(agents_dir)
|
|
191
|
+
|
|
192
|
+
if search_dir.exists():
|
|
193
|
+
for pattern in ['*.agent.md', '*.agent.yaml', '*.agent.yml', '*.agent.json']:
|
|
194
|
+
for file_path in search_dir.rglob(pattern):
|
|
195
|
+
try:
|
|
196
|
+
agent_def = load_agent_definition(str(file_path))
|
|
197
|
+
agents_list.append({
|
|
198
|
+
'type': 'local',
|
|
199
|
+
'name': agent_def.get('name', file_path.stem),
|
|
200
|
+
'source': str(file_path),
|
|
201
|
+
'description': agent_def.get('description', '')[:60]
|
|
202
|
+
})
|
|
203
|
+
except Exception as e:
|
|
204
|
+
logger.debug(f"Failed to load {file_path}: {e}")
|
|
205
|
+
|
|
206
|
+
if not agents_list:
|
|
207
|
+
console.print("[yellow]No agents found. Create an agent first or check your configuration.[/yellow]")
|
|
208
|
+
return None
|
|
209
|
+
|
|
210
|
+
# Display agents with numbers using rich
|
|
211
|
+
for i, agent in enumerate(agents_list, 1):
|
|
212
|
+
agent_type = "📦 Platform" if agent['type'] == 'platform' else "📁 Local"
|
|
213
|
+
console.print(f"{i}. [[bold]{agent_type}[/bold]] [cyan]{agent['name']}[/cyan]")
|
|
214
|
+
if agent['description']:
|
|
215
|
+
console.print(f" [dim]{agent['description']}[/dim]")
|
|
216
|
+
|
|
217
|
+
console.print(f"\n[dim]0. Cancel[/dim]")
|
|
218
|
+
|
|
219
|
+
# Get user selection
|
|
220
|
+
while True:
|
|
221
|
+
try:
|
|
222
|
+
choice = input("\nSelect agent number: ").strip()
|
|
223
|
+
|
|
224
|
+
if choice == '0':
|
|
225
|
+
return None
|
|
226
|
+
|
|
227
|
+
idx = int(choice) - 1
|
|
228
|
+
if 0 <= idx < len(agents_list):
|
|
229
|
+
selected = agents_list[idx]
|
|
230
|
+
console.print(f"\n✓ [green]Selected:[/green] [bold]{selected['name']}[/bold]")
|
|
231
|
+
return selected['source']
|
|
232
|
+
else:
|
|
233
|
+
console.print(f"[yellow]Invalid selection. Please enter a number between 0 and {len(agents_list)}[/yellow]")
|
|
234
|
+
except ValueError:
|
|
235
|
+
console.print("[yellow]Please enter a valid number[/yellow]")
|
|
236
|
+
except (KeyboardInterrupt, EOFError):
|
|
237
|
+
console.print("\n\n[dim]Cancelled.[/dim]")
|
|
238
|
+
return None
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
@click.group()
|
|
242
|
+
def agent():
|
|
243
|
+
"""Agent testing and interaction commands."""
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@agent.command('list')
|
|
248
|
+
@click.option('--local', is_flag=True, help='List local agent definition files')
|
|
249
|
+
@click.option('--directory', default=None, help='Directory to search for local agents (defaults to AGENTS_DIR from .env)')
|
|
250
|
+
@click.pass_context
|
|
251
|
+
def agent_list(ctx, local: bool, directory: Optional[str]):
|
|
252
|
+
"""
|
|
253
|
+
List available agents.
|
|
254
|
+
|
|
255
|
+
By default, lists agents from the platform.
|
|
256
|
+
Use --local to list agent definition files in the local directory.
|
|
257
|
+
"""
|
|
258
|
+
formatter = ctx.obj['formatter']
|
|
259
|
+
config = ctx.obj['config']
|
|
260
|
+
|
|
261
|
+
try:
|
|
262
|
+
if local:
|
|
263
|
+
# List local agent definition files
|
|
264
|
+
if directory is None:
|
|
265
|
+
directory = config.agents_dir
|
|
266
|
+
search_dir = Path(directory)
|
|
267
|
+
|
|
268
|
+
if not search_dir.exists():
|
|
269
|
+
console.print(f"[red]Directory not found: {directory}[/red]")
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
agents = []
|
|
273
|
+
|
|
274
|
+
# Find agent definition files
|
|
275
|
+
for pattern in ['*.agent.md', '*.agent.yaml', '*.agent.yml', '*.agent.json']:
|
|
276
|
+
for file_path in search_dir.rglob(pattern):
|
|
277
|
+
try:
|
|
278
|
+
agent_def = load_agent_definition(str(file_path))
|
|
279
|
+
# Use relative path if already relative, otherwise make it relative to cwd
|
|
280
|
+
try:
|
|
281
|
+
display_path = str(file_path.relative_to(Path.cwd()))
|
|
282
|
+
except ValueError:
|
|
283
|
+
display_path = str(file_path)
|
|
284
|
+
|
|
285
|
+
agents.append({
|
|
286
|
+
'name': agent_def.get('name', file_path.stem),
|
|
287
|
+
'file': display_path,
|
|
288
|
+
'description': agent_def.get('description', '')[:80]
|
|
289
|
+
})
|
|
290
|
+
except Exception as e:
|
|
291
|
+
logger.debug(f"Failed to load {file_path}: {e}")
|
|
292
|
+
|
|
293
|
+
if not agents:
|
|
294
|
+
console.print(f"\n[yellow]No agent definition files found in {directory}[/yellow]")
|
|
295
|
+
return
|
|
296
|
+
|
|
297
|
+
# Display local agents in a table
|
|
298
|
+
table = Table(
|
|
299
|
+
title=f"Local Agent Definitions in {directory}",
|
|
300
|
+
show_header=True,
|
|
301
|
+
header_style="bold cyan",
|
|
302
|
+
border_style="cyan",
|
|
303
|
+
box=box.ROUNDED
|
|
304
|
+
)
|
|
305
|
+
table.add_column("Name", style="bold cyan", no_wrap=True)
|
|
306
|
+
table.add_column("File", style="dim")
|
|
307
|
+
table.add_column("Description", style="white")
|
|
308
|
+
|
|
309
|
+
for agent_info in sorted(agents, key=lambda x: x['name']):
|
|
310
|
+
table.add_row(
|
|
311
|
+
agent_info['name'],
|
|
312
|
+
agent_info['file'],
|
|
313
|
+
agent_info['description'] or "-"
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
console.print("\n")
|
|
317
|
+
console.print(table)
|
|
318
|
+
console.print(f"\n[green]Total: {len(agents)} local agents[/green]")
|
|
319
|
+
|
|
320
|
+
else:
|
|
321
|
+
# List platform agents
|
|
322
|
+
client = get_client(ctx)
|
|
323
|
+
|
|
324
|
+
agents = client.get_list_of_apps()
|
|
325
|
+
|
|
326
|
+
if formatter.__class__.__name__ == 'JSONFormatter':
|
|
327
|
+
click.echo(formatter._dump({'agents': agents, 'total': len(agents)}))
|
|
328
|
+
else:
|
|
329
|
+
table = Table(
|
|
330
|
+
title="Available Platform Agents",
|
|
331
|
+
show_header=True,
|
|
332
|
+
header_style="bold cyan",
|
|
333
|
+
border_style="cyan",
|
|
334
|
+
box=box.ROUNDED
|
|
335
|
+
)
|
|
336
|
+
table.add_column("ID", style="yellow", no_wrap=True)
|
|
337
|
+
table.add_column("Name", style="bold cyan")
|
|
338
|
+
table.add_column("Description", style="white")
|
|
339
|
+
|
|
340
|
+
for agent_info in agents:
|
|
341
|
+
table.add_row(
|
|
342
|
+
str(agent_info['id']),
|
|
343
|
+
agent_info['name'],
|
|
344
|
+
agent_info.get('description', '')[:80] or "-"
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
console.print("\n")
|
|
348
|
+
console.print(table)
|
|
349
|
+
console.print(f"\n[green]Total: {len(agents)} agents[/green]")
|
|
350
|
+
|
|
351
|
+
except Exception as e:
|
|
352
|
+
logger.exception("Failed to list agents")
|
|
353
|
+
error_panel = Panel(
|
|
354
|
+
str(e),
|
|
355
|
+
title="Error",
|
|
356
|
+
border_style="red",
|
|
357
|
+
box=box.ROUNDED
|
|
358
|
+
)
|
|
359
|
+
console.print(error_panel, style="red")
|
|
360
|
+
raise click.Abort()
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
@agent.command('show')
|
|
364
|
+
@click.argument('agent_source')
|
|
365
|
+
@click.option('--version', help='Agent version (for platform agents)')
|
|
366
|
+
@click.pass_context
|
|
367
|
+
def agent_show(ctx, agent_source: str, version: Optional[str]):
|
|
368
|
+
"""
|
|
369
|
+
Show agent details.
|
|
370
|
+
|
|
371
|
+
AGENT_SOURCE can be:
|
|
372
|
+
- Platform agent ID or name (e.g., "123" or "my-agent")
|
|
373
|
+
- Path to local agent file (e.g., ".github/agents/sdk-dev.agent.md")
|
|
374
|
+
"""
|
|
375
|
+
formatter = ctx.obj['formatter']
|
|
376
|
+
|
|
377
|
+
try:
|
|
378
|
+
# Check if it's a file path
|
|
379
|
+
if Path(agent_source).exists():
|
|
380
|
+
# Local agent file
|
|
381
|
+
agent_def = load_agent_definition(agent_source)
|
|
382
|
+
|
|
383
|
+
if formatter.__class__.__name__ == 'JSONFormatter':
|
|
384
|
+
click.echo(formatter._dump(agent_def))
|
|
385
|
+
else:
|
|
386
|
+
# Create details panel
|
|
387
|
+
details = Text()
|
|
388
|
+
details.append("File: ", style="bold")
|
|
389
|
+
details.append(f"{agent_source}\n", style="cyan")
|
|
390
|
+
|
|
391
|
+
if agent_def.get('description'):
|
|
392
|
+
details.append("\nDescription: ", style="bold")
|
|
393
|
+
details.append(f"{agent_def['description']}\n", style="white")
|
|
394
|
+
|
|
395
|
+
if agent_def.get('model'):
|
|
396
|
+
details.append("Model: ", style="bold")
|
|
397
|
+
details.append(f"{agent_def['model']}\n", style="cyan")
|
|
398
|
+
|
|
399
|
+
if agent_def.get('tools'):
|
|
400
|
+
details.append("Tools: ", style="bold")
|
|
401
|
+
details.append(f"{', '.join(agent_def['tools'])}\n", style="cyan")
|
|
402
|
+
|
|
403
|
+
if agent_def.get('temperature') is not None:
|
|
404
|
+
details.append("Temperature: ", style="bold")
|
|
405
|
+
details.append(f"{agent_def['temperature']}\n", style="cyan")
|
|
406
|
+
|
|
407
|
+
panel = Panel(
|
|
408
|
+
details,
|
|
409
|
+
title=f"Local Agent: {agent_def.get('name', 'Unknown')}",
|
|
410
|
+
title_align="left",
|
|
411
|
+
border_style="cyan",
|
|
412
|
+
box=box.ROUNDED
|
|
413
|
+
)
|
|
414
|
+
console.print("\n")
|
|
415
|
+
console.print(panel)
|
|
416
|
+
|
|
417
|
+
if agent_def.get('system_prompt'):
|
|
418
|
+
console.print("\n[bold]System Prompt:[/bold]")
|
|
419
|
+
console.print(Panel(agent_def['system_prompt'][:500] + "...", border_style="dim", box=box.ROUNDED))
|
|
420
|
+
|
|
421
|
+
else:
|
|
422
|
+
# Platform agent
|
|
423
|
+
client = get_client(ctx)
|
|
424
|
+
|
|
425
|
+
# Try to find agent by ID or name
|
|
426
|
+
agents = client.get_list_of_apps()
|
|
427
|
+
|
|
428
|
+
agent = None
|
|
429
|
+
try:
|
|
430
|
+
agent_id = int(agent_source)
|
|
431
|
+
agent = next((a for a in agents if a['id'] == agent_id), None)
|
|
432
|
+
except ValueError:
|
|
433
|
+
agent = next((a for a in agents if a['name'] == agent_source), None)
|
|
434
|
+
|
|
435
|
+
if not agent:
|
|
436
|
+
raise click.ClickException(f"Agent '{agent_source}' not found")
|
|
437
|
+
|
|
438
|
+
# Get details
|
|
439
|
+
details = client.get_app_details(agent['id'])
|
|
440
|
+
|
|
441
|
+
if formatter.__class__.__name__ == 'JSONFormatter':
|
|
442
|
+
click.echo(formatter._dump(details))
|
|
443
|
+
else:
|
|
444
|
+
# Create platform agent details panel
|
|
445
|
+
content = Text()
|
|
446
|
+
content.append("ID: ", style="bold")
|
|
447
|
+
content.append(f"{details['id']}\n", style="yellow")
|
|
448
|
+
|
|
449
|
+
if details.get('description'):
|
|
450
|
+
content.append("\nDescription: ", style="bold")
|
|
451
|
+
content.append(f"{details['description']}\n", style="white")
|
|
452
|
+
|
|
453
|
+
panel = Panel(
|
|
454
|
+
content,
|
|
455
|
+
title=f"Agent: {details['name']}",
|
|
456
|
+
title_align="left",
|
|
457
|
+
border_style="cyan",
|
|
458
|
+
box=box.ROUNDED
|
|
459
|
+
)
|
|
460
|
+
console.print("\n")
|
|
461
|
+
console.print(panel)
|
|
462
|
+
|
|
463
|
+
# Display versions in a table
|
|
464
|
+
if details.get('versions'):
|
|
465
|
+
console.print("\n[bold]Versions:[/bold]")
|
|
466
|
+
versions_table = Table(box=box.ROUNDED, border_style="dim")
|
|
467
|
+
versions_table.add_column("Name", style="cyan")
|
|
468
|
+
versions_table.add_column("ID", style="yellow")
|
|
469
|
+
for ver in details.get('versions', []):
|
|
470
|
+
versions_table.add_row(ver['name'], str(ver['id']))
|
|
471
|
+
console.print(versions_table)
|
|
472
|
+
|
|
473
|
+
except click.ClickException:
|
|
474
|
+
raise
|
|
475
|
+
except Exception as e:
|
|
476
|
+
logger.exception("Failed to show agent details")
|
|
477
|
+
error_panel = Panel(
|
|
478
|
+
str(e),
|
|
479
|
+
title="Error",
|
|
480
|
+
border_style="red",
|
|
481
|
+
box=box.ROUNDED
|
|
482
|
+
)
|
|
483
|
+
console.print(error_panel, style="red")
|
|
484
|
+
raise click.Abort()
|
|
485
|
+
|
|
486
|
+
|
|
487
|
+
@agent.command('chat')
|
|
488
|
+
@click.argument('agent_source', required=False)
|
|
489
|
+
@click.option('--version', help='Agent version (for platform agents)')
|
|
490
|
+
@click.option('--toolkit-config', multiple=True, type=click.Path(exists=True),
|
|
491
|
+
help='Toolkit configuration files (can specify multiple)')
|
|
492
|
+
@click.option('--thread-id', help='Continue existing conversation thread')
|
|
493
|
+
@click.option('--model', help='Override LLM model')
|
|
494
|
+
@click.option('--temperature', type=float, help='Override temperature')
|
|
495
|
+
@click.option('--max-tokens', type=int, help='Override max tokens')
|
|
496
|
+
@click.pass_context
|
|
497
|
+
def agent_chat(ctx, agent_source: Optional[str], version: Optional[str],
|
|
498
|
+
toolkit_config: tuple, thread_id: Optional[str],
|
|
499
|
+
model: Optional[str], temperature: Optional[float],
|
|
500
|
+
max_tokens: Optional[int]):
|
|
501
|
+
"""
|
|
502
|
+
Start interactive chat with an agent.
|
|
503
|
+
|
|
504
|
+
If AGENT_SOURCE is not provided, shows an interactive menu to select from
|
|
505
|
+
available agents (both platform and local).
|
|
506
|
+
|
|
507
|
+
AGENT_SOURCE can be:
|
|
508
|
+
- Platform agent ID or name
|
|
509
|
+
- Path to local agent file
|
|
510
|
+
|
|
511
|
+
Examples:
|
|
512
|
+
|
|
513
|
+
# Interactive selection
|
|
514
|
+
alita-cli agent chat
|
|
515
|
+
|
|
516
|
+
# Chat with platform agent
|
|
517
|
+
alita-cli agent chat my-agent
|
|
518
|
+
|
|
519
|
+
# Chat with local agent
|
|
520
|
+
alita-cli agent chat .github/agents/sdk-dev.agent.md
|
|
521
|
+
|
|
522
|
+
# With toolkit configurations
|
|
523
|
+
alita-cli agent chat my-agent \\
|
|
524
|
+
--toolkit-config jira-config.json \\
|
|
525
|
+
--toolkit-config github-config.json
|
|
526
|
+
|
|
527
|
+
# Continue previous conversation
|
|
528
|
+
alita-cli agent chat my-agent --thread-id abc123
|
|
529
|
+
"""
|
|
530
|
+
formatter = ctx.obj['formatter']
|
|
531
|
+
config = ctx.obj['config']
|
|
532
|
+
client = get_client(ctx)
|
|
533
|
+
|
|
534
|
+
try:
|
|
535
|
+
# If no agent specified, show selection menu
|
|
536
|
+
if not agent_source:
|
|
537
|
+
agent_source = _select_agent_interactive(client, config)
|
|
538
|
+
if not agent_source:
|
|
539
|
+
console.print("[yellow]No agent selected. Exiting.[/yellow]")
|
|
540
|
+
return
|
|
541
|
+
|
|
542
|
+
# Load agent
|
|
543
|
+
is_local = Path(agent_source).exists()
|
|
544
|
+
|
|
545
|
+
if is_local:
|
|
546
|
+
agent_def = load_agent_definition(agent_source)
|
|
547
|
+
agent_name = agent_def.get('name', Path(agent_source).stem)
|
|
548
|
+
agent_type = "Local Agent"
|
|
549
|
+
else:
|
|
550
|
+
# Platform agent - find it
|
|
551
|
+
agents = client.get_list_of_apps()
|
|
552
|
+
agent = None
|
|
553
|
+
|
|
554
|
+
try:
|
|
555
|
+
agent_id = int(agent_source)
|
|
556
|
+
agent = next((a for a in agents if a['id'] == agent_id), None)
|
|
557
|
+
except ValueError:
|
|
558
|
+
agent = next((a for a in agents if a['name'] == agent_source), None)
|
|
559
|
+
|
|
560
|
+
if not agent:
|
|
561
|
+
raise click.ClickException(f"Agent '{agent_source}' not found")
|
|
562
|
+
|
|
563
|
+
agent_name = agent['name']
|
|
564
|
+
agent_type = "Platform Agent"
|
|
565
|
+
|
|
566
|
+
# Print nice banner
|
|
567
|
+
_print_banner(agent_name, agent_type)
|
|
568
|
+
_print_help()
|
|
569
|
+
|
|
570
|
+
# Initialize conversation
|
|
571
|
+
chat_history = []
|
|
572
|
+
|
|
573
|
+
# Create memory for agent
|
|
574
|
+
from langgraph.checkpoint.sqlite import SqliteSaver
|
|
575
|
+
memory = SqliteSaver(sqlite3.connect(":memory:", check_same_thread=False))
|
|
576
|
+
|
|
577
|
+
# Load toolkits if provided
|
|
578
|
+
toolkit_configs = []
|
|
579
|
+
|
|
580
|
+
# First load from agent definition if local
|
|
581
|
+
if is_local and 'toolkit_configs' in agent_def:
|
|
582
|
+
for tk_config in agent_def['toolkit_configs']:
|
|
583
|
+
if isinstance(tk_config, dict):
|
|
584
|
+
if 'file' in tk_config:
|
|
585
|
+
# Load from file
|
|
586
|
+
config = load_toolkit_config(tk_config['file'])
|
|
587
|
+
toolkit_configs.append(config)
|
|
588
|
+
console.print(f"[dim]Loaded toolkit config from agent definition: {tk_config['file']}[/dim]")
|
|
589
|
+
elif 'config' in tk_config:
|
|
590
|
+
# Inline config
|
|
591
|
+
toolkit_configs.append(tk_config['config'])
|
|
592
|
+
console.print(f"[dim]Loaded inline toolkit config: {tk_config['config'].get('toolkit_name', 'unknown')}[/dim]")
|
|
593
|
+
|
|
594
|
+
# Then load from --toolkit-config options
|
|
595
|
+
if toolkit_config:
|
|
596
|
+
for config_path in toolkit_config:
|
|
597
|
+
config = load_toolkit_config(config_path)
|
|
598
|
+
toolkit_configs.append(config)
|
|
599
|
+
console.print(f"[dim]Loaded toolkit config: {config_path}[/dim]")
|
|
600
|
+
|
|
601
|
+
# Auto-add toolkits to tools if not already present
|
|
602
|
+
if is_local and toolkit_configs:
|
|
603
|
+
tools = agent_def.get('tools', [])
|
|
604
|
+
for tk_config in toolkit_configs:
|
|
605
|
+
toolkit_name = tk_config.get('toolkit_name')
|
|
606
|
+
if toolkit_name and toolkit_name not in tools:
|
|
607
|
+
tools.append(toolkit_name)
|
|
608
|
+
console.print(f"[dim]Auto-added toolkit to tools: {toolkit_name}[/dim]")
|
|
609
|
+
agent_def['tools'] = tools
|
|
610
|
+
|
|
611
|
+
# Create agent executor
|
|
612
|
+
if is_local:
|
|
613
|
+
# For local agents, use direct LLM integration
|
|
614
|
+
llm_model = model or agent_def.get('model', 'gpt-4o')
|
|
615
|
+
llm_temperature = temperature if temperature is not None else agent_def.get('temperature', 0.7)
|
|
616
|
+
llm_max_tokens = max_tokens or agent_def.get('max_tokens', 2000)
|
|
617
|
+
|
|
618
|
+
system_prompt = agent_def.get('system_prompt', '')
|
|
619
|
+
|
|
620
|
+
# Display configuration
|
|
621
|
+
console.print()
|
|
622
|
+
console.print(f"✓ [green]Using model:[/green] [bold]{llm_model}[/bold]")
|
|
623
|
+
console.print(f"✓ [green]Temperature:[/green] [bold]{llm_temperature}[/bold]")
|
|
624
|
+
if agent_def.get('tools'):
|
|
625
|
+
console.print(f"✓ [green]Tools:[/green] [bold]{', '.join(agent_def['tools'])}[/bold]")
|
|
626
|
+
console.print()
|
|
627
|
+
|
|
628
|
+
# Create LLM instance using AlitaClient
|
|
629
|
+
try:
|
|
630
|
+
llm = client.get_llm(
|
|
631
|
+
model_name=llm_model,
|
|
632
|
+
model_config={
|
|
633
|
+
'temperature': llm_temperature,
|
|
634
|
+
'max_tokens': llm_max_tokens
|
|
635
|
+
}
|
|
636
|
+
)
|
|
637
|
+
except Exception as e:
|
|
638
|
+
console.print(f"\n✗ [red]Failed to create LLM instance:[/red] {e}")
|
|
639
|
+
console.print("[yellow]Hint: Make sure OPENAI_API_KEY or other LLM credentials are set[/yellow]")
|
|
640
|
+
return
|
|
641
|
+
|
|
642
|
+
agent_executor = None # Local agents use direct LLM calls
|
|
643
|
+
else:
|
|
644
|
+
# Platform agent
|
|
645
|
+
details = client.get_app_details(agent['id'])
|
|
646
|
+
|
|
647
|
+
if version:
|
|
648
|
+
version_obj = next((v for v in details['versions'] if v['name'] == version), None)
|
|
649
|
+
if not version_obj:
|
|
650
|
+
raise click.ClickException(f"Version '{version}' not found")
|
|
651
|
+
version_id = version_obj['id']
|
|
652
|
+
else:
|
|
653
|
+
# Use first version
|
|
654
|
+
version_id = details['versions'][0]['id']
|
|
655
|
+
|
|
656
|
+
# Display configuration
|
|
657
|
+
console.print()
|
|
658
|
+
console.print("✓ [green]Connected to platform agent[/green]")
|
|
659
|
+
console.print()
|
|
660
|
+
|
|
661
|
+
agent_executor = client.application(
|
|
662
|
+
application_id=agent['id'],
|
|
663
|
+
application_version_id=version_id,
|
|
664
|
+
memory=memory,
|
|
665
|
+
chat_history=chat_history
|
|
666
|
+
)
|
|
667
|
+
llm = None # Platform agents don't use direct LLM
|
|
668
|
+
|
|
669
|
+
# Interactive chat loop
|
|
670
|
+
while True:
|
|
671
|
+
try:
|
|
672
|
+
# Styled prompt
|
|
673
|
+
console.print("\n[bold bright_white]>[/bold bright_white] ", end="")
|
|
674
|
+
user_input = input().strip()
|
|
675
|
+
|
|
676
|
+
if not user_input:
|
|
677
|
+
continue
|
|
678
|
+
|
|
679
|
+
# Handle commands
|
|
680
|
+
if user_input.lower() in ['exit', 'quit']:
|
|
681
|
+
console.print("\n[bold cyan]👋 Goodbye![/bold cyan]\n")
|
|
682
|
+
break
|
|
683
|
+
|
|
684
|
+
if user_input == '/clear':
|
|
685
|
+
chat_history = []
|
|
686
|
+
console.print("[green]✓ Conversation history cleared.[/green]")
|
|
687
|
+
continue
|
|
688
|
+
|
|
689
|
+
if user_input == '/history':
|
|
690
|
+
if not chat_history:
|
|
691
|
+
console.print("[yellow]No conversation history yet.[/yellow]")
|
|
692
|
+
else:
|
|
693
|
+
console.print("\n[bold cyan]── Conversation History ──[/bold cyan]")
|
|
694
|
+
for i, msg in enumerate(chat_history, 1):
|
|
695
|
+
role = msg.get('role', 'unknown')
|
|
696
|
+
content = msg.get('content', '')
|
|
697
|
+
role_color = 'blue' if role == 'user' else 'green'
|
|
698
|
+
console.print(f"\n[bold {role_color}]{i}. {role.upper()}:[/bold {role_color}] {content[:100]}...")
|
|
699
|
+
continue
|
|
700
|
+
|
|
701
|
+
if user_input == '/save':
|
|
702
|
+
console.print("[yellow]Save to file (default: conversation.json):[/yellow] ", end="")
|
|
703
|
+
filename = input().strip()
|
|
704
|
+
filename = filename or "conversation.json"
|
|
705
|
+
with open(filename, 'w') as f:
|
|
706
|
+
json.dump({'history': chat_history}, f, indent=2)
|
|
707
|
+
console.print(f"[green]✓ Conversation saved to {filename}[/green]")
|
|
708
|
+
continue
|
|
709
|
+
|
|
710
|
+
if user_input == '/help':
|
|
711
|
+
_print_help()
|
|
712
|
+
continue
|
|
713
|
+
|
|
714
|
+
# Execute agent
|
|
715
|
+
if is_local:
|
|
716
|
+
# Local agent: use direct LLM call with streaming
|
|
717
|
+
messages = []
|
|
718
|
+
if system_prompt:
|
|
719
|
+
messages.append({"role": "system", "content": system_prompt})
|
|
720
|
+
|
|
721
|
+
# Add chat history
|
|
722
|
+
for msg in chat_history:
|
|
723
|
+
messages.append(msg)
|
|
724
|
+
|
|
725
|
+
# Add user message
|
|
726
|
+
messages.append({"role": "user", "content": user_input})
|
|
727
|
+
|
|
728
|
+
try:
|
|
729
|
+
# Try streaming if available
|
|
730
|
+
if hasattr(llm, 'stream'):
|
|
731
|
+
output_chunks = []
|
|
732
|
+
first_chunk = True
|
|
733
|
+
|
|
734
|
+
# Show spinner until first token arrives
|
|
735
|
+
status = console.status("[yellow]Thinking...[/yellow]", spinner="dots")
|
|
736
|
+
status.start()
|
|
737
|
+
|
|
738
|
+
# Stream the response token by token
|
|
739
|
+
for chunk in llm.stream(messages):
|
|
740
|
+
if hasattr(chunk, 'content'):
|
|
741
|
+
token = chunk.content
|
|
742
|
+
else:
|
|
743
|
+
token = str(chunk)
|
|
744
|
+
|
|
745
|
+
if token:
|
|
746
|
+
# Stop spinner and show agent name on first token
|
|
747
|
+
if first_chunk:
|
|
748
|
+
status.stop()
|
|
749
|
+
console.print(f"\n[bold bright_cyan]{agent_name}:[/bold bright_cyan]\n", end="")
|
|
750
|
+
first_chunk = False
|
|
751
|
+
|
|
752
|
+
console.print(token, end="", markup=False)
|
|
753
|
+
output_chunks.append(token)
|
|
754
|
+
|
|
755
|
+
# Stop status if still running (no tokens received)
|
|
756
|
+
if first_chunk:
|
|
757
|
+
status.stop()
|
|
758
|
+
console.print(f"\n[bold bright_cyan]{agent_name}:[/bold bright_cyan]\n", end="")
|
|
759
|
+
|
|
760
|
+
output = ''.join(output_chunks)
|
|
761
|
+
console.print() # New line after streaming
|
|
762
|
+
else:
|
|
763
|
+
# Fallback to non-streaming with spinner
|
|
764
|
+
with console.status("[yellow]Thinking...[/yellow]", spinner="dots"):
|
|
765
|
+
response = llm.invoke(messages)
|
|
766
|
+
if hasattr(response, 'content'):
|
|
767
|
+
output = response.content
|
|
768
|
+
else:
|
|
769
|
+
output = str(response)
|
|
770
|
+
|
|
771
|
+
# Display response after spinner stops
|
|
772
|
+
console.print(f"\n[bold bright_cyan]{agent_name}:[/bold bright_cyan]")
|
|
773
|
+
if any(marker in output for marker in ['```', '**', '##', '- ', '* ']):
|
|
774
|
+
console.print(Markdown(output))
|
|
775
|
+
else:
|
|
776
|
+
console.print(output)
|
|
777
|
+
except Exception as e:
|
|
778
|
+
console.print(f"\n[red]✗ Error: {e}[/red]\n")
|
|
779
|
+
continue
|
|
780
|
+
else:
|
|
781
|
+
# Platform agent: use agent executor
|
|
782
|
+
with console.status("[yellow]Thinking...[/yellow]", spinner="dots"):
|
|
783
|
+
result = agent_executor.invoke({
|
|
784
|
+
"input": [user_input],
|
|
785
|
+
"chat_history": chat_history
|
|
786
|
+
})
|
|
787
|
+
output = result.get('output', '')
|
|
788
|
+
|
|
789
|
+
# Display response
|
|
790
|
+
console.print(f"\n[bold bright_cyan]{agent_name}:[/bold bright_cyan]")
|
|
791
|
+
if any(marker in output for marker in ['```', '**', '##', '- ', '* ']):
|
|
792
|
+
console.print(Markdown(output))
|
|
793
|
+
else:
|
|
794
|
+
console.print(output)
|
|
795
|
+
|
|
796
|
+
# Update chat history
|
|
797
|
+
chat_history.append({"role": "user", "content": user_input})
|
|
798
|
+
chat_history.append({"role": "assistant", "content": output})
|
|
799
|
+
|
|
800
|
+
except KeyboardInterrupt:
|
|
801
|
+
console.print("\n\n[yellow]Interrupted. Type 'exit' to quit or continue chatting.[/yellow]")
|
|
802
|
+
continue
|
|
803
|
+
except EOFError:
|
|
804
|
+
console.print("\n\n[bold cyan]Goodbye! 👋[/bold cyan]")
|
|
805
|
+
break
|
|
806
|
+
|
|
807
|
+
except click.ClickException:
|
|
808
|
+
raise
|
|
809
|
+
except Exception as e:
|
|
810
|
+
logger.exception("Failed to start chat")
|
|
811
|
+
error_panel = Panel(
|
|
812
|
+
str(e),
|
|
813
|
+
title="Error",
|
|
814
|
+
border_style="red",
|
|
815
|
+
box=box.ROUNDED
|
|
816
|
+
)
|
|
817
|
+
console.print(error_panel, style="red")
|
|
818
|
+
raise click.Abort()
|
|
819
|
+
|
|
820
|
+
|
|
821
|
+
@agent.command('run')
|
|
822
|
+
@click.argument('agent_source')
|
|
823
|
+
@click.argument('message')
|
|
824
|
+
@click.option('--version', help='Agent version (for platform agents)')
|
|
825
|
+
@click.option('--toolkit-config', multiple=True, type=click.Path(exists=True),
|
|
826
|
+
help='Toolkit configuration files')
|
|
827
|
+
@click.option('--model', help='Override LLM model')
|
|
828
|
+
@click.option('--temperature', type=float, help='Override temperature')
|
|
829
|
+
@click.option('--max-tokens', type=int, help='Override max tokens')
|
|
830
|
+
@click.option('--save-thread', help='Save thread ID to file for continuation')
|
|
831
|
+
@click.pass_context
|
|
832
|
+
def agent_run(ctx, agent_source: str, message: str, version: Optional[str],
|
|
833
|
+
toolkit_config: tuple, model: Optional[str],
|
|
834
|
+
temperature: Optional[float], max_tokens: Optional[int],
|
|
835
|
+
save_thread: Optional[str]):
|
|
836
|
+
"""
|
|
837
|
+
Run agent with a single message (handoff mode).
|
|
838
|
+
|
|
839
|
+
AGENT_SOURCE can be:
|
|
840
|
+
- Platform agent ID or name
|
|
841
|
+
- Path to local agent file
|
|
842
|
+
|
|
843
|
+
MESSAGE is the input message to send to the agent.
|
|
844
|
+
|
|
845
|
+
Examples:
|
|
846
|
+
|
|
847
|
+
# Simple query
|
|
848
|
+
alita-cli agent run my-agent "What is the status of JIRA-123?"
|
|
849
|
+
|
|
850
|
+
# With local agent
|
|
851
|
+
alita-cli agent run .github/agents/sdk-dev.agent.md \\
|
|
852
|
+
"Create a new toolkit for Stripe API"
|
|
853
|
+
|
|
854
|
+
# With toolkit configs and JSON output
|
|
855
|
+
alita-cli --output json agent run my-agent "Search for bugs" \\
|
|
856
|
+
--toolkit-config jira-config.json
|
|
857
|
+
|
|
858
|
+
# Save thread for continuation
|
|
859
|
+
alita-cli agent run my-agent "Start task" \\
|
|
860
|
+
--save-thread thread.txt
|
|
861
|
+
"""
|
|
862
|
+
formatter = ctx.obj['formatter']
|
|
863
|
+
client = get_client(ctx)
|
|
864
|
+
|
|
865
|
+
try:
|
|
866
|
+
# Load agent
|
|
867
|
+
is_local = Path(agent_source).exists()
|
|
868
|
+
|
|
869
|
+
# Load toolkits
|
|
870
|
+
toolkit_configs = []
|
|
871
|
+
|
|
872
|
+
if is_local:
|
|
873
|
+
agent_def = load_agent_definition(agent_source)
|
|
874
|
+
agent_name = agent_def.get('name', Path(agent_source).stem)
|
|
875
|
+
|
|
876
|
+
# Load toolkit configs from agent definition
|
|
877
|
+
if 'toolkit_configs' in agent_def:
|
|
878
|
+
for tk_config in agent_def['toolkit_configs']:
|
|
879
|
+
if isinstance(tk_config, dict):
|
|
880
|
+
if 'file' in tk_config:
|
|
881
|
+
config = load_toolkit_config(tk_config['file'])
|
|
882
|
+
toolkit_configs.append(config)
|
|
883
|
+
elif 'config' in tk_config:
|
|
884
|
+
toolkit_configs.append(tk_config['config'])
|
|
885
|
+
|
|
886
|
+
# Load additional toolkit configs from --toolkit-config options
|
|
887
|
+
if toolkit_config:
|
|
888
|
+
for config_path in toolkit_config:
|
|
889
|
+
config = load_toolkit_config(config_path)
|
|
890
|
+
toolkit_configs.append(config)
|
|
891
|
+
|
|
892
|
+
# Get LLM configuration
|
|
893
|
+
llm_model = model or agent_def.get('model', 'gpt-4o')
|
|
894
|
+
llm_temperature = temperature if temperature is not None else agent_def.get('temperature', 0.7)
|
|
895
|
+
llm_max_tokens = max_tokens or agent_def.get('max_tokens', 2000)
|
|
896
|
+
system_prompt = agent_def.get('system_prompt', '')
|
|
897
|
+
|
|
898
|
+
# Create LLM instance
|
|
899
|
+
try:
|
|
900
|
+
llm = client.get_llm(
|
|
901
|
+
model_name=llm_model,
|
|
902
|
+
model_config={
|
|
903
|
+
'temperature': llm_temperature,
|
|
904
|
+
'max_tokens': llm_max_tokens
|
|
905
|
+
}
|
|
906
|
+
)
|
|
907
|
+
except Exception as e:
|
|
908
|
+
error_panel = Panel(
|
|
909
|
+
f"Failed to create LLM instance: {e}",
|
|
910
|
+
title="Error",
|
|
911
|
+
border_style="red",
|
|
912
|
+
box=box.ROUNDED
|
|
913
|
+
)
|
|
914
|
+
console.print(error_panel, style="red")
|
|
915
|
+
raise click.Abort()
|
|
916
|
+
|
|
917
|
+
# Prepare messages
|
|
918
|
+
messages = []
|
|
919
|
+
if system_prompt:
|
|
920
|
+
messages.append({"role": "system", "content": system_prompt})
|
|
921
|
+
messages.append({"role": "user", "content": message})
|
|
922
|
+
|
|
923
|
+
# Execute with spinner for non-JSON output
|
|
924
|
+
if formatter.__class__.__name__ == 'JSONFormatter':
|
|
925
|
+
response = llm.invoke(messages)
|
|
926
|
+
if hasattr(response, 'content'):
|
|
927
|
+
output = response.content
|
|
928
|
+
else:
|
|
929
|
+
output = str(response)
|
|
930
|
+
|
|
931
|
+
click.echo(formatter._dump({
|
|
932
|
+
'agent': agent_name,
|
|
933
|
+
'message': message,
|
|
934
|
+
'response': output
|
|
935
|
+
}))
|
|
936
|
+
else:
|
|
937
|
+
# Show spinner while executing
|
|
938
|
+
with console.status("[yellow]Processing...[/yellow]", spinner="dots"):
|
|
939
|
+
response = llm.invoke(messages)
|
|
940
|
+
if hasattr(response, 'content'):
|
|
941
|
+
output = response.content
|
|
942
|
+
else:
|
|
943
|
+
output = str(response)
|
|
944
|
+
|
|
945
|
+
# Format and display output
|
|
946
|
+
console.print(f"\n[bold cyan]🤖 Agent: {agent_name}[/bold cyan]\n")
|
|
947
|
+
console.print(f"[bold]Message:[/bold] {message}\n")
|
|
948
|
+
console.print("[bold]Response:[/bold]")
|
|
949
|
+
# Render markdown if the response looks like it contains markdown
|
|
950
|
+
if any(marker in output for marker in ['```', '**', '##', '- ', '* ']):
|
|
951
|
+
console.print(Markdown(output))
|
|
952
|
+
else:
|
|
953
|
+
console.print(output)
|
|
954
|
+
console.print()
|
|
955
|
+
|
|
956
|
+
else:
|
|
957
|
+
# Platform agent
|
|
958
|
+
agents = client.get_list_of_apps()
|
|
959
|
+
agent = None
|
|
960
|
+
|
|
961
|
+
try:
|
|
962
|
+
agent_id = int(agent_source)
|
|
963
|
+
agent = next((a for a in agents if a['id'] == agent_id), None)
|
|
964
|
+
except ValueError:
|
|
965
|
+
agent = next((a for a in agents if a['name'] == agent_source), None)
|
|
966
|
+
|
|
967
|
+
if not agent:
|
|
968
|
+
raise click.ClickException(f"Agent '{agent_source}' not found")
|
|
969
|
+
|
|
970
|
+
# Get version
|
|
971
|
+
details = client.get_app_details(agent['id'])
|
|
972
|
+
|
|
973
|
+
if version:
|
|
974
|
+
version_obj = next((v for v in details['versions'] if v['name'] == version), None)
|
|
975
|
+
if not version_obj:
|
|
976
|
+
raise click.ClickException(f"Version '{version}' not found")
|
|
977
|
+
version_id = version_obj['id']
|
|
978
|
+
else:
|
|
979
|
+
version_id = details['versions'][0]['id']
|
|
980
|
+
|
|
981
|
+
# Load additional toolkit configs from --toolkit-config options
|
|
982
|
+
if toolkit_config:
|
|
983
|
+
for config_path in toolkit_config:
|
|
984
|
+
config = load_toolkit_config(config_path)
|
|
985
|
+
toolkit_configs.append(config)
|
|
986
|
+
|
|
987
|
+
# Create memory
|
|
988
|
+
from langgraph.checkpoint.sqlite import SqliteSaver
|
|
989
|
+
memory = SqliteSaver(sqlite3.connect(":memory:", check_same_thread=False))
|
|
990
|
+
|
|
991
|
+
# Create agent executor
|
|
992
|
+
agent_executor = client.application(
|
|
993
|
+
application_id=agent['id'],
|
|
994
|
+
application_version_id=version_id,
|
|
995
|
+
memory=memory
|
|
996
|
+
)
|
|
997
|
+
|
|
998
|
+
# Execute with spinner for non-JSON output
|
|
999
|
+
if formatter.__class__.__name__ == 'JSONFormatter':
|
|
1000
|
+
result = agent_executor.invoke({
|
|
1001
|
+
"input": [message],
|
|
1002
|
+
"chat_history": []
|
|
1003
|
+
})
|
|
1004
|
+
|
|
1005
|
+
click.echo(formatter._dump({
|
|
1006
|
+
'agent': agent['name'],
|
|
1007
|
+
'message': message,
|
|
1008
|
+
'response': result.get('output', ''),
|
|
1009
|
+
'full_result': result
|
|
1010
|
+
}))
|
|
1011
|
+
else:
|
|
1012
|
+
# Show spinner while executing
|
|
1013
|
+
with console.status("[yellow]Processing...[/yellow]", spinner="dots"):
|
|
1014
|
+
result = agent_executor.invoke({
|
|
1015
|
+
"input": [message],
|
|
1016
|
+
"chat_history": []
|
|
1017
|
+
})
|
|
1018
|
+
|
|
1019
|
+
# Format and display output
|
|
1020
|
+
console.print(f"\n[bold cyan]🤖 Agent: {agent['name']}[/bold cyan]\n")
|
|
1021
|
+
console.print(f"[bold]Message:[/bold] {message}\n")
|
|
1022
|
+
console.print("[bold]Response:[/bold]")
|
|
1023
|
+
response = result.get('output', 'No response')
|
|
1024
|
+
# Render markdown if the response looks like it contains markdown
|
|
1025
|
+
if any(marker in response for marker in ['```', '**', '##', '- ', '* ']):
|
|
1026
|
+
console.print(Markdown(response))
|
|
1027
|
+
else:
|
|
1028
|
+
console.print(response)
|
|
1029
|
+
console.print()
|
|
1030
|
+
|
|
1031
|
+
# Save thread if requested
|
|
1032
|
+
if save_thread:
|
|
1033
|
+
thread_data = {
|
|
1034
|
+
'agent_id': agent['id'],
|
|
1035
|
+
'agent_name': agent['name'],
|
|
1036
|
+
'version_id': version_id,
|
|
1037
|
+
'thread_id': result.get('thread_id'),
|
|
1038
|
+
'last_message': message
|
|
1039
|
+
}
|
|
1040
|
+
with open(save_thread, 'w') as f:
|
|
1041
|
+
json.dump(thread_data, f, indent=2)
|
|
1042
|
+
logger.info(f"Thread saved to {save_thread}")
|
|
1043
|
+
|
|
1044
|
+
except click.ClickException:
|
|
1045
|
+
raise
|
|
1046
|
+
except Exception as e:
|
|
1047
|
+
logger.exception("Failed to run agent")
|
|
1048
|
+
error_panel = Panel(
|
|
1049
|
+
str(e),
|
|
1050
|
+
title="Error",
|
|
1051
|
+
border_style="red",
|
|
1052
|
+
box=box.ROUNDED
|
|
1053
|
+
)
|
|
1054
|
+
console.print(error_panel, style="red")
|
|
1055
|
+
raise click.Abort()
|