alita-sdk 0.3.459__py3-none-any.whl → 0.3.461__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/agent_executor.py +144 -0
- alita_sdk/cli/agent_loader.py +197 -0
- alita_sdk/cli/agent_ui.py +166 -0
- alita_sdk/cli/agents.py +278 -264
- alita_sdk/cli/callbacks.py +576 -0
- alita_sdk/cli/cli.py +3 -0
- alita_sdk/cli/config.py +23 -4
- alita_sdk/cli/mcp_loader.py +315 -0
- alita_sdk/cli/toolkit_loader.py +55 -0
- alita_sdk/cli/tools/__init__.py +9 -0
- alita_sdk/cli/tools/filesystem.py +905 -0
- alita_sdk/runtime/clients/client.py +1 -1
- alita_sdk/runtime/langchain/langraph_agent.py +1 -1
- alita_sdk/runtime/tools/function.py +15 -2
- alita_sdk/runtime/tools/llm.py +65 -7
- alita_sdk/tools/qtest/api_wrapper.py +84 -21
- {alita_sdk-0.3.459.dist-info → alita_sdk-0.3.461.dist-info}/METADATA +7 -7
- {alita_sdk-0.3.459.dist-info → alita_sdk-0.3.461.dist-info}/RECORD +22 -14
- {alita_sdk-0.3.459.dist-info → alita_sdk-0.3.461.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.459.dist-info → alita_sdk-0.3.461.dist-info}/entry_points.txt +0 -0
- {alita_sdk-0.3.459.dist-info → alita_sdk-0.3.461.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.459.dist-info → alita_sdk-0.3.461.dist-info}/top_level.txt +0 -0
alita_sdk/cli/agents.py
CHANGED
|
@@ -5,10 +5,12 @@ Provides commands to work with agents interactively or in handoff mode,
|
|
|
5
5
|
supporting both platform agents and local agent definition files.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
+
import asyncio
|
|
8
9
|
import click
|
|
9
10
|
import json
|
|
10
11
|
import logging
|
|
11
12
|
import sqlite3
|
|
13
|
+
import sys
|
|
12
14
|
from typing import Optional, Dict, Any, List
|
|
13
15
|
from pathlib import Path
|
|
14
16
|
import yaml
|
|
@@ -23,7 +25,12 @@ from rich.status import Status
|
|
|
23
25
|
from rich.live import Live
|
|
24
26
|
|
|
25
27
|
from .cli import get_client
|
|
26
|
-
from
|
|
28
|
+
# Import from refactored modules
|
|
29
|
+
from .agent_ui import print_welcome, print_help, display_output, extract_output_from_result
|
|
30
|
+
from .agent_loader import load_agent_definition
|
|
31
|
+
from .agent_executor import create_llm_instance, create_agent_executor, create_agent_executor_with_mcp
|
|
32
|
+
from .toolkit_loader import load_toolkit_config, load_toolkit_configs
|
|
33
|
+
from .callbacks import create_cli_callback, CLICallbackHandler
|
|
27
34
|
|
|
28
35
|
logger = logging.getLogger(__name__)
|
|
29
36
|
|
|
@@ -31,132 +38,95 @@ logger = logging.getLogger(__name__)
|
|
|
31
38
|
console = Console()
|
|
32
39
|
|
|
33
40
|
|
|
34
|
-
def
|
|
35
|
-
"""
|
|
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")
|
|
41
|
+
def _load_mcp_tools(agent_def: Dict[str, Any], mcp_config_path: str) -> List[Dict[str, Any]]:
|
|
42
|
+
"""Load MCP tools from agent definition with tool-level filtering.
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
Args:
|
|
45
|
+
agent_def: Agent definition dictionary containing mcps list
|
|
46
|
+
mcp_config_path: Path to mcp.json configuration file (workspace-level)
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
List of toolkit configurations for MCP servers
|
|
50
|
+
"""
|
|
51
|
+
from .mcp_loader import load_mcp_tools
|
|
52
|
+
return load_mcp_tools(agent_def, mcp_config_path)
|
|
50
53
|
|
|
51
54
|
|
|
52
|
-
def
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
header_style="bold yellow",
|
|
57
|
-
border_style="yellow",
|
|
58
|
-
box=box.ROUNDED,
|
|
59
|
-
title="Commands",
|
|
60
|
-
title_style="bold yellow"
|
|
61
|
-
)
|
|
55
|
+
def _setup_local_agent_executor(client, agent_def: Dict[str, Any], toolkit_config: tuple,
|
|
56
|
+
config, model: Optional[str], temperature: Optional[float],
|
|
57
|
+
max_tokens: Optional[int], memory, work_dir: Optional[str]):
|
|
58
|
+
"""Setup local agent executor with all configurations.
|
|
62
59
|
|
|
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
60
|
Returns:
|
|
91
|
-
|
|
61
|
+
Tuple of (agent_executor, mcp_session_manager, llm, llm_model, filesystem_tools)
|
|
92
62
|
"""
|
|
93
|
-
|
|
63
|
+
# Load toolkit configs
|
|
64
|
+
toolkit_configs = load_toolkit_configs(agent_def, toolkit_config)
|
|
94
65
|
|
|
95
|
-
|
|
96
|
-
|
|
66
|
+
# Load MCP tools
|
|
67
|
+
mcp_toolkit_configs = _load_mcp_tools(agent_def, config.mcp_config_path)
|
|
68
|
+
toolkit_configs.extend(mcp_toolkit_configs)
|
|
97
69
|
|
|
98
|
-
|
|
70
|
+
# Create LLM instance
|
|
71
|
+
llm, llm_model, llm_temperature, llm_max_tokens = create_llm_instance(
|
|
72
|
+
client, model, agent_def, temperature, max_tokens
|
|
73
|
+
)
|
|
99
74
|
|
|
100
|
-
#
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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
|
-
}
|
|
75
|
+
# Add filesystem tools if --dir is provided
|
|
76
|
+
filesystem_tools = None
|
|
77
|
+
if work_dir:
|
|
78
|
+
from .tools import get_filesystem_tools
|
|
79
|
+
preset = agent_def.get('filesystem_tools_preset')
|
|
80
|
+
include_tools = agent_def.get('filesystem_tools_include')
|
|
81
|
+
exclude_tools = agent_def.get('filesystem_tools_exclude')
|
|
82
|
+
filesystem_tools = get_filesystem_tools(work_dir, include_tools, exclude_tools, preset)
|
|
121
83
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
84
|
+
tool_count = len(filesystem_tools)
|
|
85
|
+
access_msg = f"✓ Granted filesystem access to: {work_dir} ({tool_count} tools)"
|
|
86
|
+
if preset:
|
|
87
|
+
access_msg += f" [preset: {preset}]"
|
|
88
|
+
if include_tools:
|
|
89
|
+
access_msg += f" [include: {', '.join(include_tools)}]"
|
|
90
|
+
if exclude_tools:
|
|
91
|
+
access_msg += f" [exclude: {', '.join(exclude_tools)}]"
|
|
92
|
+
console.print(f"[dim]{access_msg}[/dim]")
|
|
127
93
|
|
|
128
|
-
#
|
|
129
|
-
|
|
130
|
-
|
|
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
|
|
94
|
+
# Check if we have tools
|
|
95
|
+
has_tools = bool(agent_def.get('tools') or toolkit_configs or filesystem_tools)
|
|
96
|
+
has_mcp = any(tc.get('toolkit_type') == 'mcp' for tc in toolkit_configs)
|
|
135
97
|
|
|
136
|
-
|
|
137
|
-
|
|
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
|
|
98
|
+
if not has_tools:
|
|
99
|
+
return None, None, llm, llm_model, filesystem_tools
|
|
143
100
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
101
|
+
# Create agent executor with or without MCP
|
|
102
|
+
mcp_session_manager = None
|
|
103
|
+
if has_mcp:
|
|
104
|
+
# Create persistent event loop for MCP tools
|
|
105
|
+
from alita_sdk.runtime.tools.llm import LLMNode
|
|
106
|
+
if not hasattr(LLMNode, '_persistent_loop') or \
|
|
107
|
+
LLMNode._persistent_loop is None or \
|
|
108
|
+
LLMNode._persistent_loop.is_closed():
|
|
109
|
+
LLMNode._persistent_loop = asyncio.new_event_loop()
|
|
110
|
+
console.print("[dim]Created persistent event loop for MCP tools[/dim]")
|
|
111
|
+
|
|
112
|
+
# Load MCP tools using persistent loop
|
|
113
|
+
loop = LLMNode._persistent_loop
|
|
114
|
+
asyncio.set_event_loop(loop)
|
|
115
|
+
agent_executor, mcp_session_manager = loop.run_until_complete(
|
|
116
|
+
create_agent_executor_with_mcp(
|
|
117
|
+
client, agent_def, toolkit_configs,
|
|
118
|
+
llm, llm_model, llm_temperature, llm_max_tokens, memory,
|
|
119
|
+
filesystem_tools=filesystem_tools
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
else:
|
|
123
|
+
agent_executor = create_agent_executor(
|
|
124
|
+
client, agent_def, toolkit_configs,
|
|
125
|
+
llm, llm_model, llm_temperature, llm_max_tokens, memory,
|
|
126
|
+
filesystem_tools=filesystem_tools
|
|
127
|
+
)
|
|
156
128
|
|
|
157
|
-
|
|
158
|
-
content = substitute_env_vars(content)
|
|
159
|
-
return json.loads(content)
|
|
129
|
+
return agent_executor, mcp_session_manager, llm, llm_model, filesystem_tools
|
|
160
130
|
|
|
161
131
|
|
|
162
132
|
def _select_agent_interactive(client, config) -> Optional[str]:
|
|
@@ -493,11 +463,16 @@ def agent_show(ctx, agent_source: str, version: Optional[str]):
|
|
|
493
463
|
@click.option('--model', help='Override LLM model')
|
|
494
464
|
@click.option('--temperature', type=float, help='Override temperature')
|
|
495
465
|
@click.option('--max-tokens', type=int, help='Override max tokens')
|
|
466
|
+
@click.option('--dir', 'work_dir', type=click.Path(exists=True, file_okay=False, dir_okay=True),
|
|
467
|
+
help='Grant agent filesystem access to this directory')
|
|
468
|
+
@click.option('--verbose', '-v', type=click.Choice(['quiet', 'default', 'debug']), default='default',
|
|
469
|
+
help='Output verbosity level: quiet (final output only), default (tool calls + outputs), debug (all including LLM calls)')
|
|
496
470
|
@click.pass_context
|
|
497
471
|
def agent_chat(ctx, agent_source: Optional[str], version: Optional[str],
|
|
498
472
|
toolkit_config: tuple, thread_id: Optional[str],
|
|
499
473
|
model: Optional[str], temperature: Optional[float],
|
|
500
|
-
max_tokens: Optional[int]
|
|
474
|
+
max_tokens: Optional[int], work_dir: Optional[str],
|
|
475
|
+
verbose: str):
|
|
501
476
|
"""
|
|
502
477
|
Start interactive chat with an agent.
|
|
503
478
|
|
|
@@ -524,13 +499,26 @@ def agent_chat(ctx, agent_source: Optional[str], version: Optional[str],
|
|
|
524
499
|
--toolkit-config jira-config.json \\
|
|
525
500
|
--toolkit-config github-config.json
|
|
526
501
|
|
|
502
|
+
# With filesystem access
|
|
503
|
+
alita-cli agent chat my-agent --dir ./workspace
|
|
504
|
+
|
|
527
505
|
# Continue previous conversation
|
|
528
506
|
alita-cli agent chat my-agent --thread-id abc123
|
|
507
|
+
|
|
508
|
+
# Quiet mode (hide tool calls and thinking)
|
|
509
|
+
alita-cli agent chat my-agent --verbose quiet
|
|
510
|
+
|
|
511
|
+
# Debug mode (show all including LLM calls)
|
|
512
|
+
alita-cli agent chat my-agent --verbose debug
|
|
529
513
|
"""
|
|
530
514
|
formatter = ctx.obj['formatter']
|
|
531
515
|
config = ctx.obj['config']
|
|
532
516
|
client = get_client(ctx)
|
|
533
517
|
|
|
518
|
+
# Setup verbose level
|
|
519
|
+
show_verbose = verbose != 'quiet'
|
|
520
|
+
debug_mode = verbose == 'debug'
|
|
521
|
+
|
|
534
522
|
try:
|
|
535
523
|
# If no agent specified, show selection menu
|
|
536
524
|
if not agent_source:
|
|
@@ -563,9 +551,8 @@ def agent_chat(ctx, agent_source: Optional[str], version: Optional[str],
|
|
|
563
551
|
agent_name = agent['name']
|
|
564
552
|
agent_type = "Platform Agent"
|
|
565
553
|
|
|
566
|
-
# Print nice banner
|
|
567
|
-
|
|
568
|
-
_print_help()
|
|
554
|
+
# Print nice welcome banner
|
|
555
|
+
print_welcome(agent_name, agent_type)
|
|
569
556
|
|
|
570
557
|
# Initialize conversation
|
|
571
558
|
chat_history = []
|
|
@@ -574,72 +561,25 @@ def agent_chat(ctx, agent_source: Optional[str], version: Optional[str],
|
|
|
574
561
|
from langgraph.checkpoint.sqlite import SqliteSaver
|
|
575
562
|
memory = SqliteSaver(sqlite3.connect(":memory:", check_same_thread=False))
|
|
576
563
|
|
|
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
564
|
# Create agent executor
|
|
612
565
|
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
566
|
# Display configuration
|
|
567
|
+
llm_model_display = model or agent_def.get('model', 'gpt-4o')
|
|
568
|
+
llm_temperature_display = temperature if temperature is not None else agent_def.get('temperature', 0.7)
|
|
621
569
|
console.print()
|
|
622
|
-
console.print(f"✓ [green]Using model:[/green] [bold]{
|
|
623
|
-
console.print(f"✓ [green]Temperature:[/green] [bold]{
|
|
570
|
+
console.print(f"✓ [green]Using model:[/green] [bold]{llm_model_display}[/bold]")
|
|
571
|
+
console.print(f"✓ [green]Temperature:[/green] [bold]{llm_temperature_display}[/bold]")
|
|
624
572
|
if agent_def.get('tools'):
|
|
625
573
|
console.print(f"✓ [green]Tools:[/green] [bold]{', '.join(agent_def['tools'])}[/bold]")
|
|
626
574
|
console.print()
|
|
627
575
|
|
|
628
|
-
#
|
|
576
|
+
# Setup local agent executor (handles all config, tools, MCP, etc.)
|
|
629
577
|
try:
|
|
630
|
-
llm =
|
|
631
|
-
|
|
632
|
-
model_config={
|
|
633
|
-
'temperature': llm_temperature,
|
|
634
|
-
'max_tokens': llm_max_tokens
|
|
635
|
-
}
|
|
578
|
+
agent_executor, mcp_session_manager, llm, llm_model, filesystem_tools = _setup_local_agent_executor(
|
|
579
|
+
client, agent_def, toolkit_config, config, model, temperature, max_tokens, memory, work_dir
|
|
636
580
|
)
|
|
637
|
-
except Exception
|
|
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]")
|
|
581
|
+
except Exception:
|
|
640
582
|
return
|
|
641
|
-
|
|
642
|
-
agent_executor = None # Local agents use direct LLM calls
|
|
643
583
|
else:
|
|
644
584
|
# Platform agent
|
|
645
585
|
details = client.get_app_details(agent['id'])
|
|
@@ -708,12 +648,13 @@ def agent_chat(ctx, agent_source: Optional[str], version: Optional[str],
|
|
|
708
648
|
continue
|
|
709
649
|
|
|
710
650
|
if user_input == '/help':
|
|
711
|
-
|
|
651
|
+
print_help()
|
|
712
652
|
continue
|
|
713
653
|
|
|
714
654
|
# Execute agent
|
|
715
|
-
if is_local:
|
|
716
|
-
# Local agent: use direct LLM call with streaming
|
|
655
|
+
if is_local and agent_executor is None:
|
|
656
|
+
# Local agent without tools: use direct LLM call with streaming
|
|
657
|
+
system_prompt = agent_def.get('system_prompt', '')
|
|
717
658
|
messages = []
|
|
718
659
|
if system_prompt:
|
|
719
660
|
messages.append({"role": "system", "content": system_prompt})
|
|
@@ -778,13 +719,37 @@ def agent_chat(ctx, agent_source: Optional[str], version: Optional[str],
|
|
|
778
719
|
console.print(f"\n[red]✗ Error: {e}[/red]\n")
|
|
779
720
|
continue
|
|
780
721
|
else:
|
|
781
|
-
#
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
722
|
+
# Agent with tools or platform agent: use agent executor
|
|
723
|
+
# Setup callback for verbose output
|
|
724
|
+
from langchain_core.runnables import RunnableConfig
|
|
725
|
+
|
|
726
|
+
invoke_config = None
|
|
727
|
+
if show_verbose:
|
|
728
|
+
cli_callback = create_cli_callback(verbose=True, debug=debug_mode)
|
|
729
|
+
invoke_config = RunnableConfig(callbacks=[cli_callback])
|
|
730
|
+
|
|
731
|
+
# Show status only when not verbose (verbose shows its own progress)
|
|
732
|
+
if not show_verbose:
|
|
733
|
+
with console.status("[yellow]Thinking...[/yellow]", spinner="dots"):
|
|
734
|
+
result = agent_executor.invoke(
|
|
735
|
+
{
|
|
736
|
+
"input": [user_input] if not is_local else user_input,
|
|
737
|
+
"chat_history": chat_history
|
|
738
|
+
},
|
|
739
|
+
config=invoke_config
|
|
740
|
+
)
|
|
741
|
+
else:
|
|
742
|
+
console.print() # Add spacing before tool calls
|
|
743
|
+
result = agent_executor.invoke(
|
|
744
|
+
{
|
|
745
|
+
"input": [user_input] if not is_local else user_input,
|
|
746
|
+
"chat_history": chat_history
|
|
747
|
+
},
|
|
748
|
+
config=invoke_config
|
|
749
|
+
)
|
|
750
|
+
|
|
751
|
+
# Extract output from result
|
|
752
|
+
output = extract_output_from_result(result)
|
|
788
753
|
|
|
789
754
|
# Display response
|
|
790
755
|
console.print(f"\n[bold bright_cyan]{agent_name}:[/bold bright_cyan]")
|
|
@@ -828,11 +793,16 @@ def agent_chat(ctx, agent_source: Optional[str], version: Optional[str],
|
|
|
828
793
|
@click.option('--temperature', type=float, help='Override temperature')
|
|
829
794
|
@click.option('--max-tokens', type=int, help='Override max tokens')
|
|
830
795
|
@click.option('--save-thread', help='Save thread ID to file for continuation')
|
|
796
|
+
@click.option('--dir', 'work_dir', type=click.Path(exists=True, file_okay=False, dir_okay=True),
|
|
797
|
+
help='Grant agent filesystem access to this directory')
|
|
798
|
+
@click.option('--verbose', '-v', type=click.Choice(['quiet', 'default', 'debug']), default='default',
|
|
799
|
+
help='Output verbosity level: quiet (final output only), default (tool calls + outputs), debug (all including LLM calls)')
|
|
831
800
|
@click.pass_context
|
|
832
801
|
def agent_run(ctx, agent_source: str, message: str, version: Optional[str],
|
|
833
802
|
toolkit_config: tuple, model: Optional[str],
|
|
834
803
|
temperature: Optional[float], max_tokens: Optional[int],
|
|
835
|
-
save_thread: Optional[str]
|
|
804
|
+
save_thread: Optional[str], work_dir: Optional[str],
|
|
805
|
+
verbose: str):
|
|
836
806
|
"""
|
|
837
807
|
Run agent with a single message (handoff mode).
|
|
838
808
|
|
|
@@ -855,58 +825,46 @@ def agent_run(ctx, agent_source: str, message: str, version: Optional[str],
|
|
|
855
825
|
alita-cli --output json agent run my-agent "Search for bugs" \\
|
|
856
826
|
--toolkit-config jira-config.json
|
|
857
827
|
|
|
828
|
+
# With filesystem access
|
|
829
|
+
alita-cli agent run my-agent "Analyze the code in src/" --dir ./myproject
|
|
830
|
+
|
|
858
831
|
# Save thread for continuation
|
|
859
832
|
alita-cli agent run my-agent "Start task" \\
|
|
860
833
|
--save-thread thread.txt
|
|
834
|
+
|
|
835
|
+
# Quiet mode (hide tool calls and thinking)
|
|
836
|
+
alita-cli agent run my-agent "Query" --verbose quiet
|
|
837
|
+
|
|
838
|
+
# Debug mode (show all including LLM calls)
|
|
839
|
+
alita-cli agent run my-agent "Query" --verbose debug
|
|
861
840
|
"""
|
|
862
841
|
formatter = ctx.obj['formatter']
|
|
863
842
|
client = get_client(ctx)
|
|
864
843
|
|
|
844
|
+
# Setup verbose level
|
|
845
|
+
show_verbose = verbose != 'quiet'
|
|
846
|
+
debug_mode = verbose == 'debug'
|
|
847
|
+
|
|
865
848
|
try:
|
|
866
849
|
# Load agent
|
|
867
850
|
is_local = Path(agent_source).exists()
|
|
868
851
|
|
|
869
|
-
# Load toolkits
|
|
870
|
-
toolkit_configs = []
|
|
871
|
-
|
|
872
852
|
if is_local:
|
|
873
853
|
agent_def = load_agent_definition(agent_source)
|
|
874
854
|
agent_name = agent_def.get('name', Path(agent_source).stem)
|
|
875
855
|
|
|
876
|
-
#
|
|
877
|
-
|
|
878
|
-
|
|
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', '')
|
|
856
|
+
# Create memory for agent
|
|
857
|
+
from langgraph.checkpoint.sqlite import SqliteSaver
|
|
858
|
+
memory = SqliteSaver(sqlite3.connect(":memory:", check_same_thread=False))
|
|
897
859
|
|
|
898
|
-
#
|
|
860
|
+
# Setup local agent executor (reuses same logic as agent_chat)
|
|
899
861
|
try:
|
|
900
|
-
llm =
|
|
901
|
-
|
|
902
|
-
model_config={
|
|
903
|
-
'temperature': llm_temperature,
|
|
904
|
-
'max_tokens': llm_max_tokens
|
|
905
|
-
}
|
|
862
|
+
agent_executor, mcp_session_manager, llm, llm_model, filesystem_tools = _setup_local_agent_executor(
|
|
863
|
+
client, agent_def, toolkit_config, ctx.obj['config'], model, temperature, max_tokens, memory, work_dir
|
|
906
864
|
)
|
|
907
865
|
except Exception as e:
|
|
908
866
|
error_panel = Panel(
|
|
909
|
-
f"Failed to
|
|
867
|
+
f"Failed to setup agent: {e}",
|
|
910
868
|
title="Error",
|
|
911
869
|
border_style="red",
|
|
912
870
|
box=box.ROUNDED
|
|
@@ -914,44 +872,87 @@ def agent_run(ctx, agent_source: str, message: str, version: Optional[str],
|
|
|
914
872
|
console.print(error_panel, style="red")
|
|
915
873
|
raise click.Abort()
|
|
916
874
|
|
|
917
|
-
#
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
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)
|
|
875
|
+
# Execute agent
|
|
876
|
+
if agent_executor:
|
|
877
|
+
# Setup callback for verbose output
|
|
878
|
+
from langchain_core.runnables import RunnableConfig
|
|
930
879
|
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
880
|
+
invoke_config = None
|
|
881
|
+
if show_verbose:
|
|
882
|
+
cli_callback = create_cli_callback(verbose=True, debug=debug_mode)
|
|
883
|
+
invoke_config = RunnableConfig(callbacks=[cli_callback])
|
|
884
|
+
|
|
885
|
+
# Execute with spinner for non-JSON output
|
|
886
|
+
if formatter.__class__.__name__ == 'JSONFormatter':
|
|
887
|
+
# JSON output: always quiet, no callbacks
|
|
888
|
+
with console.status("[yellow]Processing...[/yellow]", spinner="dots"):
|
|
889
|
+
result = agent_executor.invoke({
|
|
890
|
+
"input": message,
|
|
891
|
+
"chat_history": []
|
|
892
|
+
})
|
|
893
|
+
|
|
894
|
+
click.echo(formatter._dump({
|
|
895
|
+
'agent': agent_name,
|
|
896
|
+
'message': message,
|
|
897
|
+
'response': extract_output_from_result(result),
|
|
898
|
+
'full_result': result
|
|
899
|
+
}))
|
|
900
|
+
else:
|
|
901
|
+
# Show status only when not verbose (verbose shows its own progress)
|
|
902
|
+
if not show_verbose:
|
|
903
|
+
with console.status("[yellow]Processing...[/yellow]", spinner="dots"):
|
|
904
|
+
result = agent_executor.invoke(
|
|
905
|
+
{
|
|
906
|
+
"input": message,
|
|
907
|
+
"chat_history": []
|
|
908
|
+
},
|
|
909
|
+
config=invoke_config
|
|
910
|
+
)
|
|
911
|
+
else:
|
|
912
|
+
console.print() # Add spacing before tool calls
|
|
913
|
+
result = agent_executor.invoke(
|
|
914
|
+
{
|
|
915
|
+
"input": message,
|
|
916
|
+
"chat_history": []
|
|
917
|
+
},
|
|
918
|
+
config=invoke_config
|
|
919
|
+
)
|
|
920
|
+
|
|
921
|
+
# Extract and display output
|
|
922
|
+
output = extract_output_from_result(result)
|
|
923
|
+
display_output(agent_name, message, output)
|
|
936
924
|
else:
|
|
937
|
-
#
|
|
938
|
-
|
|
925
|
+
# Simple LLM mode without tools
|
|
926
|
+
system_prompt = agent_def.get('system_prompt', '')
|
|
927
|
+
messages = []
|
|
928
|
+
if system_prompt:
|
|
929
|
+
messages.append({"role": "system", "content": system_prompt})
|
|
930
|
+
messages.append({"role": "user", "content": message})
|
|
931
|
+
|
|
932
|
+
# Execute with spinner for non-JSON output
|
|
933
|
+
if formatter.__class__.__name__ == 'JSONFormatter':
|
|
939
934
|
response = llm.invoke(messages)
|
|
940
935
|
if hasattr(response, 'content'):
|
|
941
936
|
output = response.content
|
|
942
937
|
else:
|
|
943
938
|
output = str(response)
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
if any(marker in output for marker in ['```', '**', '##', '- ', '* ']):
|
|
951
|
-
console.print(Markdown(output))
|
|
939
|
+
|
|
940
|
+
click.echo(formatter._dump({
|
|
941
|
+
'agent': agent_name,
|
|
942
|
+
'message': message,
|
|
943
|
+
'response': output
|
|
944
|
+
}))
|
|
952
945
|
else:
|
|
953
|
-
|
|
954
|
-
|
|
946
|
+
# Show spinner while executing
|
|
947
|
+
with console.status("[yellow]Processing...[/yellow]", spinner="dots"):
|
|
948
|
+
response = llm.invoke(messages)
|
|
949
|
+
if hasattr(response, 'content'):
|
|
950
|
+
output = response.content
|
|
951
|
+
else:
|
|
952
|
+
output = str(response)
|
|
953
|
+
|
|
954
|
+
# Display output
|
|
955
|
+
display_output(agent_name, message, output)
|
|
955
956
|
|
|
956
957
|
else:
|
|
957
958
|
# Platform agent
|
|
@@ -978,11 +979,11 @@ def agent_run(ctx, agent_source: str, message: str, version: Optional[str],
|
|
|
978
979
|
else:
|
|
979
980
|
version_id = details['versions'][0]['id']
|
|
980
981
|
|
|
981
|
-
# Load
|
|
982
|
+
# Load toolkit configs from CLI options
|
|
983
|
+
toolkit_configs = []
|
|
982
984
|
if toolkit_config:
|
|
983
985
|
for config_path in toolkit_config:
|
|
984
|
-
|
|
985
|
-
toolkit_configs.append(config)
|
|
986
|
+
toolkit_configs.append(load_toolkit_config(config_path))
|
|
986
987
|
|
|
987
988
|
# Create memory
|
|
988
989
|
from langgraph.checkpoint.sqlite import SqliteSaver
|
|
@@ -995,6 +996,14 @@ def agent_run(ctx, agent_source: str, message: str, version: Optional[str],
|
|
|
995
996
|
memory=memory
|
|
996
997
|
)
|
|
997
998
|
|
|
999
|
+
# Setup callback for verbose output
|
|
1000
|
+
from langchain_core.runnables import RunnableConfig
|
|
1001
|
+
|
|
1002
|
+
invoke_config = None
|
|
1003
|
+
if show_verbose:
|
|
1004
|
+
cli_callback = create_cli_callback(verbose=True, debug=debug_mode)
|
|
1005
|
+
invoke_config = RunnableConfig(callbacks=[cli_callback])
|
|
1006
|
+
|
|
998
1007
|
# Execute with spinner for non-JSON output
|
|
999
1008
|
if formatter.__class__.__name__ == 'JSONFormatter':
|
|
1000
1009
|
result = agent_executor.invoke({
|
|
@@ -1009,24 +1018,29 @@ def agent_run(ctx, agent_source: str, message: str, version: Optional[str],
|
|
|
1009
1018
|
'full_result': result
|
|
1010
1019
|
}))
|
|
1011
1020
|
else:
|
|
1012
|
-
# Show
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1021
|
+
# Show status only when not verbose
|
|
1022
|
+
if not show_verbose:
|
|
1023
|
+
with console.status("[yellow]Processing...[/yellow]", spinner="dots"):
|
|
1024
|
+
result = agent_executor.invoke(
|
|
1025
|
+
{
|
|
1026
|
+
"input": [message],
|
|
1027
|
+
"chat_history": []
|
|
1028
|
+
},
|
|
1029
|
+
config=invoke_config
|
|
1030
|
+
)
|
|
1031
|
+
else:
|
|
1032
|
+
console.print() # Add spacing before tool calls
|
|
1033
|
+
result = agent_executor.invoke(
|
|
1034
|
+
{
|
|
1035
|
+
"input": [message],
|
|
1036
|
+
"chat_history": []
|
|
1037
|
+
},
|
|
1038
|
+
config=invoke_config
|
|
1039
|
+
)
|
|
1018
1040
|
|
|
1019
|
-
#
|
|
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]")
|
|
1041
|
+
# Display output
|
|
1023
1042
|
response = result.get('output', 'No response')
|
|
1024
|
-
|
|
1025
|
-
if any(marker in response for marker in ['```', '**', '##', '- ', '* ']):
|
|
1026
|
-
console.print(Markdown(response))
|
|
1027
|
-
else:
|
|
1028
|
-
console.print(response)
|
|
1029
|
-
console.print()
|
|
1043
|
+
display_output(agent['name'], message, response)
|
|
1030
1044
|
|
|
1031
1045
|
# Save thread if requested
|
|
1032
1046
|
if save_thread:
|