alita-sdk 0.3.379__py3-none-any.whl → 0.3.462__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.
Potentially problematic release.
This version of alita-sdk might be problematic. Click here for more details.
- alita_sdk/cli/__init__.py +10 -0
- alita_sdk/cli/__main__.py +17 -0
- 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 +1069 -0
- alita_sdk/cli/callbacks.py +576 -0
- alita_sdk/cli/cli.py +159 -0
- alita_sdk/cli/config.py +153 -0
- alita_sdk/cli/formatting.py +182 -0
- alita_sdk/cli/mcp_loader.py +315 -0
- alita_sdk/cli/toolkit.py +330 -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/configurations/bitbucket.py +95 -0
- alita_sdk/configurations/confluence.py +96 -1
- alita_sdk/configurations/gitlab.py +79 -0
- alita_sdk/configurations/jira.py +103 -0
- alita_sdk/configurations/testrail.py +88 -0
- alita_sdk/configurations/xray.py +93 -0
- alita_sdk/configurations/zephyr_enterprise.py +93 -0
- alita_sdk/configurations/zephyr_essential.py +75 -0
- alita_sdk/runtime/clients/client.py +47 -10
- alita_sdk/runtime/clients/mcp_discovery.py +342 -0
- alita_sdk/runtime/clients/mcp_manager.py +262 -0
- alita_sdk/runtime/clients/sandbox_client.py +8 -0
- alita_sdk/runtime/langchain/assistant.py +37 -16
- alita_sdk/runtime/langchain/constants.py +6 -1
- alita_sdk/runtime/langchain/document_loaders/AlitaDocxMammothLoader.py +315 -3
- alita_sdk/runtime/langchain/document_loaders/AlitaJSONLoader.py +4 -1
- alita_sdk/runtime/langchain/document_loaders/constants.py +28 -12
- alita_sdk/runtime/langchain/langraph_agent.py +146 -31
- alita_sdk/runtime/langchain/utils.py +39 -7
- alita_sdk/runtime/models/mcp_models.py +61 -0
- alita_sdk/runtime/toolkits/__init__.py +24 -0
- alita_sdk/runtime/toolkits/application.py +8 -1
- alita_sdk/runtime/toolkits/artifact.py +5 -6
- alita_sdk/runtime/toolkits/mcp.py +895 -0
- alita_sdk/runtime/toolkits/tools.py +137 -56
- alita_sdk/runtime/tools/__init__.py +7 -2
- alita_sdk/runtime/tools/application.py +7 -0
- alita_sdk/runtime/tools/function.py +29 -25
- alita_sdk/runtime/tools/graph.py +10 -4
- alita_sdk/runtime/tools/image_generation.py +104 -8
- alita_sdk/runtime/tools/llm.py +204 -114
- alita_sdk/runtime/tools/mcp_inspect_tool.py +284 -0
- alita_sdk/runtime/tools/mcp_remote_tool.py +166 -0
- alita_sdk/runtime/tools/mcp_server_tool.py +3 -1
- alita_sdk/runtime/tools/sandbox.py +57 -43
- alita_sdk/runtime/tools/vectorstore.py +2 -1
- alita_sdk/runtime/tools/vectorstore_base.py +19 -3
- alita_sdk/runtime/utils/mcp_oauth.py +164 -0
- alita_sdk/runtime/utils/mcp_sse_client.py +405 -0
- alita_sdk/runtime/utils/streamlit.py +34 -3
- alita_sdk/runtime/utils/toolkit_utils.py +14 -4
- alita_sdk/tools/__init__.py +46 -31
- alita_sdk/tools/ado/repos/__init__.py +1 -0
- alita_sdk/tools/ado/test_plan/__init__.py +1 -1
- alita_sdk/tools/ado/wiki/__init__.py +1 -5
- alita_sdk/tools/ado/work_item/__init__.py +1 -5
- alita_sdk/tools/ado/work_item/ado_wrapper.py +17 -8
- alita_sdk/tools/base_indexer_toolkit.py +105 -43
- alita_sdk/tools/bitbucket/__init__.py +1 -0
- alita_sdk/tools/chunkers/sematic/proposal_chunker.py +1 -1
- alita_sdk/tools/code/sonar/__init__.py +1 -1
- alita_sdk/tools/code_indexer_toolkit.py +13 -3
- alita_sdk/tools/confluence/__init__.py +2 -2
- alita_sdk/tools/confluence/api_wrapper.py +29 -7
- alita_sdk/tools/confluence/loader.py +10 -0
- alita_sdk/tools/github/__init__.py +2 -2
- alita_sdk/tools/gitlab/__init__.py +2 -1
- alita_sdk/tools/gitlab/api_wrapper.py +11 -7
- alita_sdk/tools/gitlab_org/__init__.py +1 -2
- alita_sdk/tools/google_places/__init__.py +2 -1
- alita_sdk/tools/jira/__init__.py +1 -0
- alita_sdk/tools/jira/api_wrapper.py +1 -1
- alita_sdk/tools/memory/__init__.py +1 -1
- alita_sdk/tools/openapi/__init__.py +10 -1
- alita_sdk/tools/pandas/__init__.py +1 -1
- alita_sdk/tools/postman/__init__.py +2 -1
- alita_sdk/tools/pptx/__init__.py +2 -2
- alita_sdk/tools/qtest/__init__.py +3 -3
- alita_sdk/tools/qtest/api_wrapper.py +1708 -76
- alita_sdk/tools/rally/__init__.py +1 -2
- alita_sdk/tools/report_portal/__init__.py +1 -0
- alita_sdk/tools/salesforce/__init__.py +1 -0
- alita_sdk/tools/servicenow/__init__.py +2 -3
- alita_sdk/tools/sharepoint/__init__.py +1 -0
- alita_sdk/tools/sharepoint/api_wrapper.py +125 -34
- alita_sdk/tools/sharepoint/authorization_helper.py +191 -1
- alita_sdk/tools/sharepoint/utils.py +8 -2
- alita_sdk/tools/slack/__init__.py +1 -0
- alita_sdk/tools/sql/__init__.py +2 -1
- alita_sdk/tools/testio/__init__.py +1 -0
- alita_sdk/tools/testrail/__init__.py +1 -3
- alita_sdk/tools/utils/content_parser.py +27 -16
- alita_sdk/tools/vector_adapters/VectorStoreAdapter.py +18 -5
- alita_sdk/tools/xray/__init__.py +2 -1
- alita_sdk/tools/zephyr/__init__.py +2 -1
- alita_sdk/tools/zephyr_enterprise/__init__.py +1 -0
- alita_sdk/tools/zephyr_essential/__init__.py +1 -0
- alita_sdk/tools/zephyr_scale/__init__.py +1 -0
- alita_sdk/tools/zephyr_squad/__init__.py +1 -0
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.462.dist-info}/METADATA +8 -2
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.462.dist-info}/RECORD +110 -86
- alita_sdk-0.3.462.dist-info/entry_points.txt +2 -0
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.462.dist-info}/WHEEL +0 -0
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.462.dist-info}/licenses/LICENSE +0 -0
- {alita_sdk-0.3.379.dist-info → alita_sdk-0.3.462.dist-info}/top_level.txt +0 -0
alita_sdk/cli/cli.py
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main CLI application for Alita SDK.
|
|
3
|
+
|
|
4
|
+
Provides command-line interface for testing agents and toolkits,
|
|
5
|
+
using the same .env authentication as SDK tests and Streamlit interface.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# Suppress warnings FIRST before any other imports
|
|
9
|
+
import warnings
|
|
10
|
+
warnings.filterwarnings('ignore', category=DeprecationWarning)
|
|
11
|
+
warnings.filterwarnings('ignore', category=UserWarning)
|
|
12
|
+
warnings.filterwarnings('ignore', message='Unverified HTTPS request')
|
|
13
|
+
|
|
14
|
+
import click
|
|
15
|
+
import logging
|
|
16
|
+
import sys
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
from .config import get_config
|
|
20
|
+
from .formatting import get_formatter
|
|
21
|
+
|
|
22
|
+
# Configure logging
|
|
23
|
+
logging.basicConfig(
|
|
24
|
+
level=logging.WARNING,
|
|
25
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
26
|
+
)
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@click.group()
|
|
31
|
+
@click.option('--env-file', default='.env', help='Path to .env file')
|
|
32
|
+
@click.option('--debug', is_flag=True, help='Enable debug logging')
|
|
33
|
+
@click.option('--output', type=click.Choice(['text', 'json']), default='text',
|
|
34
|
+
help='Output format')
|
|
35
|
+
@click.pass_context
|
|
36
|
+
def cli(ctx, env_file: str, debug: bool, output: str):
|
|
37
|
+
"""
|
|
38
|
+
Alita SDK CLI - Test agents and toolkits from the command line.
|
|
39
|
+
|
|
40
|
+
Credentials are loaded from .env file with variables:
|
|
41
|
+
- DEPLOYMENT_URL: Alita deployment URL
|
|
42
|
+
- PROJECT_ID: Project ID
|
|
43
|
+
- API_KEY: API authentication key
|
|
44
|
+
|
|
45
|
+
Example .env file:
|
|
46
|
+
|
|
47
|
+
DEPLOYMENT_URL=https://api.elitea.ai
|
|
48
|
+
PROJECT_ID=123
|
|
49
|
+
API_KEY=your_api_key_here
|
|
50
|
+
"""
|
|
51
|
+
ctx.ensure_object(dict)
|
|
52
|
+
|
|
53
|
+
# Enable debug logging if requested
|
|
54
|
+
if debug:
|
|
55
|
+
logging.getLogger('alita_sdk').setLevel(logging.DEBUG)
|
|
56
|
+
logger.setLevel(logging.DEBUG)
|
|
57
|
+
logger.debug("Debug logging enabled")
|
|
58
|
+
|
|
59
|
+
# Load configuration
|
|
60
|
+
config = get_config(env_file=env_file)
|
|
61
|
+
ctx.obj['config'] = config
|
|
62
|
+
ctx.obj['formatter'] = get_formatter(output)
|
|
63
|
+
ctx.obj['debug'] = debug
|
|
64
|
+
|
|
65
|
+
# Check if configuration is valid (but don't fail yet - some commands don't need it)
|
|
66
|
+
if not config.is_configured():
|
|
67
|
+
missing = config.get_missing_config()
|
|
68
|
+
ctx.obj['config_error'] = f"Missing required configuration: {', '.join(missing)}"
|
|
69
|
+
logger.debug(f"Configuration incomplete: {missing}")
|
|
70
|
+
else:
|
|
71
|
+
ctx.obj['config_error'] = None
|
|
72
|
+
logger.debug(f"Configuration loaded from {env_file}")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_client(ctx):
|
|
76
|
+
"""
|
|
77
|
+
Get configured AlitaClient from context.
|
|
78
|
+
|
|
79
|
+
Raises click.ClickException if configuration is invalid.
|
|
80
|
+
"""
|
|
81
|
+
if ctx.obj.get('config_error'):
|
|
82
|
+
raise click.ClickException(
|
|
83
|
+
f"{ctx.obj['config_error']}\n\n"
|
|
84
|
+
"Please ensure your .env file contains:\n"
|
|
85
|
+
" DEPLOYMENT_URL=https://api.elitea.ai\n"
|
|
86
|
+
" PROJECT_ID=123\n"
|
|
87
|
+
" API_KEY=your_api_key_here"
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
# Import here to avoid loading SDK if not needed
|
|
91
|
+
from alita_sdk.runtime.clients.client import AlitaClient
|
|
92
|
+
|
|
93
|
+
config = ctx.obj['config']
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
client = AlitaClient(
|
|
97
|
+
base_url=config.deployment_url,
|
|
98
|
+
project_id=config.project_id,
|
|
99
|
+
auth_token=config.api_key
|
|
100
|
+
)
|
|
101
|
+
logger.debug(f"AlitaClient initialized for project {config.project_id}")
|
|
102
|
+
return client
|
|
103
|
+
except Exception as e:
|
|
104
|
+
raise click.ClickException(f"Failed to initialize AlitaClient: {str(e)}")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@cli.command()
|
|
108
|
+
@click.pass_context
|
|
109
|
+
def config(ctx):
|
|
110
|
+
"""Show current configuration (credentials masked)."""
|
|
111
|
+
config_obj = ctx.obj['config']
|
|
112
|
+
formatter = ctx.obj['formatter']
|
|
113
|
+
|
|
114
|
+
if formatter.__class__.__name__ == 'JSONFormatter':
|
|
115
|
+
click.echo(formatter._dump(config_obj.to_dict()))
|
|
116
|
+
else:
|
|
117
|
+
click.echo("\nCurrent configuration:\n")
|
|
118
|
+
for key, value in config_obj.to_dict().items():
|
|
119
|
+
click.echo(f" {key}: {value}")
|
|
120
|
+
|
|
121
|
+
if not config_obj.is_configured():
|
|
122
|
+
missing = config_obj.get_missing_config()
|
|
123
|
+
click.echo(f"\n⚠ Missing: {', '.join(missing)}")
|
|
124
|
+
else:
|
|
125
|
+
click.echo("\n✓ Configuration is complete")
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# Import subcommands
|
|
129
|
+
from . import toolkit
|
|
130
|
+
from . import agents
|
|
131
|
+
|
|
132
|
+
# Register subcommands
|
|
133
|
+
cli.add_command(toolkit.toolkit)
|
|
134
|
+
cli.add_command(agents.agent)
|
|
135
|
+
|
|
136
|
+
# Add top-level 'chat' command as alias to 'agent chat'
|
|
137
|
+
cli.add_command(agents.agent_chat, name='chat')
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def main():
|
|
141
|
+
"""Entry point for CLI."""
|
|
142
|
+
# Suppress warnings at entry point
|
|
143
|
+
warnings.filterwarnings('ignore', category=DeprecationWarning)
|
|
144
|
+
warnings.filterwarnings('ignore', category=UserWarning)
|
|
145
|
+
warnings.filterwarnings('ignore', message='Unverified HTTPS request')
|
|
146
|
+
|
|
147
|
+
try:
|
|
148
|
+
cli()
|
|
149
|
+
except KeyboardInterrupt:
|
|
150
|
+
click.echo("\n\nInterrupted by user", err=True)
|
|
151
|
+
sys.exit(130)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logger.exception("Unexpected error")
|
|
154
|
+
click.echo(f"\nError: {str(e)}", err=True)
|
|
155
|
+
sys.exit(1)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
if __name__ == '__main__':
|
|
159
|
+
main()
|
alita_sdk/cli/config.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration management for Alita CLI.
|
|
3
|
+
|
|
4
|
+
Loads credentials and settings from .env files using the same pattern
|
|
5
|
+
as the SDK tests and Streamlit interface.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
from typing import Optional, Dict, Any
|
|
11
|
+
from dotenv import load_dotenv
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class CLIConfig:
|
|
18
|
+
"""Configuration manager for Alita CLI."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, env_file: Optional[str] = None):
|
|
21
|
+
"""
|
|
22
|
+
Initialize CLI configuration.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
env_file: Path to .env file. If None, uses default (.env in current directory)
|
|
26
|
+
"""
|
|
27
|
+
self.env_file = env_file or '.env'
|
|
28
|
+
self._load_env()
|
|
29
|
+
|
|
30
|
+
def _load_env(self):
|
|
31
|
+
"""Load environment variables from .env file."""
|
|
32
|
+
if os.path.exists(self.env_file):
|
|
33
|
+
load_dotenv(self.env_file)
|
|
34
|
+
logger.debug(f"Loaded environment from {self.env_file}")
|
|
35
|
+
else:
|
|
36
|
+
logger.debug(f"No .env file found at {self.env_file}, using system environment")
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def deployment_url(self) -> Optional[str]:
|
|
40
|
+
"""Get deployment URL from environment."""
|
|
41
|
+
return os.getenv('DEPLOYMENT_URL')
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def project_id(self) -> Optional[int]:
|
|
45
|
+
"""Get project ID from environment."""
|
|
46
|
+
try:
|
|
47
|
+
value = os.getenv('PROJECT_ID')
|
|
48
|
+
return int(value) if value else None
|
|
49
|
+
except (TypeError, ValueError):
|
|
50
|
+
return None
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def api_key(self) -> Optional[str]:
|
|
54
|
+
"""Get API key from environment."""
|
|
55
|
+
return os.getenv('API_KEY')
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def alita_dir(self) -> str:
|
|
59
|
+
"""Get Alita directory from environment (defaults to .alita)."""
|
|
60
|
+
return os.getenv('ALITA_DIR', '.alita')
|
|
61
|
+
|
|
62
|
+
@property
|
|
63
|
+
def agents_dir(self) -> str:
|
|
64
|
+
"""Get agents directory (derived from ALITA_DIR)."""
|
|
65
|
+
alita_agents = os.path.join(self.alita_dir, 'agents')
|
|
66
|
+
# Fallback to .github/agents if .alita/agents doesn't exist
|
|
67
|
+
if self.alita_dir == '.alita' and not os.path.exists(alita_agents):
|
|
68
|
+
if os.path.exists('.github/agents'):
|
|
69
|
+
return '.github/agents'
|
|
70
|
+
return alita_agents
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def tools_dir(self) -> str:
|
|
74
|
+
"""Get tools directory (derived from ALITA_DIR)."""
|
|
75
|
+
return os.path.join(self.alita_dir, 'tools')
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def mcp_config_path(self) -> str:
|
|
79
|
+
"""Get MCP configuration path (derived from ALITA_DIR)."""
|
|
80
|
+
alita_mcp = os.path.join(self.alita_dir, 'mcp.json')
|
|
81
|
+
# Fallback to mcp.json in current directory
|
|
82
|
+
if not os.path.exists(alita_mcp) and os.path.exists('mcp.json'):
|
|
83
|
+
return 'mcp.json'
|
|
84
|
+
return alita_mcp
|
|
85
|
+
|
|
86
|
+
def is_configured(self) -> bool:
|
|
87
|
+
"""Check if all required configuration is present."""
|
|
88
|
+
return all([
|
|
89
|
+
self.deployment_url,
|
|
90
|
+
self.project_id is not None,
|
|
91
|
+
self.api_key
|
|
92
|
+
])
|
|
93
|
+
|
|
94
|
+
def get_missing_config(self) -> list[str]:
|
|
95
|
+
"""Get list of missing configuration items."""
|
|
96
|
+
missing = []
|
|
97
|
+
if not self.deployment_url:
|
|
98
|
+
missing.append('DEPLOYMENT_URL')
|
|
99
|
+
if self.project_id is None:
|
|
100
|
+
missing.append('PROJECT_ID')
|
|
101
|
+
if not self.api_key:
|
|
102
|
+
missing.append('API_KEY')
|
|
103
|
+
return missing
|
|
104
|
+
|
|
105
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
106
|
+
"""Convert configuration to dictionary."""
|
|
107
|
+
return {
|
|
108
|
+
'deployment_url': self.deployment_url,
|
|
109
|
+
'project_id': self.project_id,
|
|
110
|
+
'api_key': '***' if self.api_key else None # Masked for security
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def get_config(env_file: Optional[str] = None) -> CLIConfig:
|
|
115
|
+
"""
|
|
116
|
+
Get CLI configuration instance.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
env_file: Optional path to .env file
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
CLIConfig instance
|
|
123
|
+
"""
|
|
124
|
+
return CLIConfig(env_file=env_file)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def substitute_env_vars(text: str) -> str:
|
|
128
|
+
"""
|
|
129
|
+
Substitute environment variables in text.
|
|
130
|
+
|
|
131
|
+
Supports both ${VAR} and $VAR syntax.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
text: Text containing environment variable references
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Text with environment variables substituted
|
|
138
|
+
"""
|
|
139
|
+
# Replace ${VAR} syntax
|
|
140
|
+
def replace_braced(match):
|
|
141
|
+
var_name = match.group(1)
|
|
142
|
+
return os.getenv(var_name, match.group(0))
|
|
143
|
+
|
|
144
|
+
text = re.sub(r'\$\{([^}]+)\}', replace_braced, text)
|
|
145
|
+
|
|
146
|
+
# Replace $VAR syntax (word boundaries)
|
|
147
|
+
def replace_simple(match):
|
|
148
|
+
var_name = match.group(1)
|
|
149
|
+
return os.getenv(var_name, match.group(0))
|
|
150
|
+
|
|
151
|
+
text = re.sub(r'\$([A-Za-z_][A-Za-z0-9_]*)', replace_simple, text)
|
|
152
|
+
|
|
153
|
+
return text
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Output formatting utilities for Alita CLI.
|
|
3
|
+
|
|
4
|
+
Provides text and JSON formatters for displaying toolkit test results,
|
|
5
|
+
agent responses, and other CLI outputs.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OutputFormatter:
|
|
14
|
+
"""Base class for output formatters."""
|
|
15
|
+
|
|
16
|
+
def format_toolkit_result(self, result: Dict[str, Any]) -> str:
|
|
17
|
+
"""Format toolkit test result."""
|
|
18
|
+
raise NotImplementedError
|
|
19
|
+
|
|
20
|
+
def format_error(self, error: str) -> str:
|
|
21
|
+
"""Format error message."""
|
|
22
|
+
raise NotImplementedError
|
|
23
|
+
|
|
24
|
+
def format_toolkit_list(self, toolkits: List[Dict[str, Any]]) -> str:
|
|
25
|
+
"""Format list of available toolkits."""
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class TextFormatter(OutputFormatter):
|
|
30
|
+
"""Human-readable text formatter."""
|
|
31
|
+
|
|
32
|
+
def format_toolkit_result(self, result: Dict[str, Any]) -> str:
|
|
33
|
+
"""Format toolkit test result as text."""
|
|
34
|
+
if not result.get('success', False):
|
|
35
|
+
return self.format_error(result.get('error', 'Unknown error'))
|
|
36
|
+
|
|
37
|
+
lines = [
|
|
38
|
+
"\n✓ Tool executed successfully\n",
|
|
39
|
+
f"Tool: {result.get('tool_name', 'unknown')}",
|
|
40
|
+
f"Toolkit: {result.get('toolkit_config', {}).get('type', 'unknown')}",
|
|
41
|
+
f"LLM Model: {result.get('llm_model', 'N/A')}",
|
|
42
|
+
f"Execution time: {result.get('execution_time_seconds', 0):.3f}s",
|
|
43
|
+
"",
|
|
44
|
+
"Result:",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
# Format result based on type
|
|
48
|
+
tool_result = result.get('result')
|
|
49
|
+
if isinstance(tool_result, str):
|
|
50
|
+
lines.append(f" {tool_result}")
|
|
51
|
+
elif isinstance(tool_result, dict):
|
|
52
|
+
for key, value in tool_result.items():
|
|
53
|
+
lines.append(f" {key}: {value}")
|
|
54
|
+
else:
|
|
55
|
+
lines.append(f" {str(tool_result)}")
|
|
56
|
+
|
|
57
|
+
# Add events if present
|
|
58
|
+
events = result.get('events_dispatched', [])
|
|
59
|
+
if events:
|
|
60
|
+
lines.extend([
|
|
61
|
+
"",
|
|
62
|
+
f"Events dispatched: {len(events)}"
|
|
63
|
+
])
|
|
64
|
+
for event in events[:5]: # Limit to first 5 events
|
|
65
|
+
event_data = event.get('data', {})
|
|
66
|
+
message = event_data.get('message', str(event_data))
|
|
67
|
+
lines.append(f" - {event.get('name', 'event')}: {message}")
|
|
68
|
+
|
|
69
|
+
if len(events) > 5:
|
|
70
|
+
lines.append(f" ... and {len(events) - 5} more events")
|
|
71
|
+
|
|
72
|
+
return "\n".join(lines)
|
|
73
|
+
|
|
74
|
+
def format_error(self, error: str) -> str:
|
|
75
|
+
"""Format error message as text."""
|
|
76
|
+
return f"\n✗ Error: {error}\n"
|
|
77
|
+
|
|
78
|
+
def format_toolkit_list(self, toolkits: List[Dict[str, Any]]) -> str:
|
|
79
|
+
"""Format list of available toolkits as text."""
|
|
80
|
+
lines = ["\nAvailable toolkits:\n"]
|
|
81
|
+
|
|
82
|
+
for toolkit in sorted(toolkits, key=lambda x: x.get('name', '')):
|
|
83
|
+
name = toolkit.get('name', 'unknown')
|
|
84
|
+
class_name = toolkit.get('class_name', '')
|
|
85
|
+
lines.append(f" - {name}" + (f" ({class_name})" if class_name else ""))
|
|
86
|
+
|
|
87
|
+
lines.append(f"\nTotal: {len(toolkits)} toolkits")
|
|
88
|
+
return "\n".join(lines)
|
|
89
|
+
|
|
90
|
+
def format_toolkit_schema(self, toolkit_name: str, schema: Dict[str, Any]) -> str:
|
|
91
|
+
"""Format toolkit schema as text."""
|
|
92
|
+
lines = [
|
|
93
|
+
f"\n{toolkit_name.title()} Toolkit Configuration Schema:\n",
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
properties = schema.get('properties', {})
|
|
97
|
+
required = schema.get('required', [])
|
|
98
|
+
|
|
99
|
+
for field_name, field_schema in properties.items():
|
|
100
|
+
field_type = field_schema.get('type', 'any')
|
|
101
|
+
description = field_schema.get('description', '')
|
|
102
|
+
is_required = field_name in required
|
|
103
|
+
default = field_schema.get('default')
|
|
104
|
+
|
|
105
|
+
req_text = "required" if is_required else "optional"
|
|
106
|
+
lines.append(f" - {field_name} ({req_text}): {description}")
|
|
107
|
+
lines.append(f" Type: {field_type}")
|
|
108
|
+
|
|
109
|
+
if default is not None:
|
|
110
|
+
lines.append(f" Default: {default}")
|
|
111
|
+
|
|
112
|
+
# Show enum values if present
|
|
113
|
+
if 'enum' in field_schema:
|
|
114
|
+
lines.append(f" Options: {', '.join(map(str, field_schema['enum']))}")
|
|
115
|
+
|
|
116
|
+
# Handle nested objects
|
|
117
|
+
if field_type == 'object' and 'properties' in field_schema:
|
|
118
|
+
lines.append(f" Fields:")
|
|
119
|
+
for nested_name, nested_schema in field_schema['properties'].items():
|
|
120
|
+
nested_desc = nested_schema.get('description', '')
|
|
121
|
+
lines.append(f" - {nested_name}: {nested_desc}")
|
|
122
|
+
|
|
123
|
+
lines.append("")
|
|
124
|
+
|
|
125
|
+
return "\n".join(lines)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class JSONFormatter(OutputFormatter):
|
|
129
|
+
"""JSON formatter for scripting and automation."""
|
|
130
|
+
|
|
131
|
+
def __init__(self, pretty: bool = True):
|
|
132
|
+
"""
|
|
133
|
+
Initialize JSON formatter.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
pretty: If True, format JSON with indentation
|
|
137
|
+
"""
|
|
138
|
+
self.pretty = pretty
|
|
139
|
+
|
|
140
|
+
def _dump(self, data: Any) -> str:
|
|
141
|
+
"""Dump data as JSON."""
|
|
142
|
+
if self.pretty:
|
|
143
|
+
return json.dumps(data, indent=2, default=str)
|
|
144
|
+
return json.dumps(data, default=str)
|
|
145
|
+
|
|
146
|
+
def format_toolkit_result(self, result: Dict[str, Any]) -> str:
|
|
147
|
+
"""Format toolkit test result as JSON."""
|
|
148
|
+
return self._dump(result)
|
|
149
|
+
|
|
150
|
+
def format_error(self, error: str) -> str:
|
|
151
|
+
"""Format error message as JSON."""
|
|
152
|
+
return self._dump({'success': False, 'error': error})
|
|
153
|
+
|
|
154
|
+
def format_toolkit_list(self, toolkits: List[Dict[str, Any]]) -> str:
|
|
155
|
+
"""Format list of available toolkits as JSON."""
|
|
156
|
+
return self._dump({
|
|
157
|
+
'toolkits': toolkits,
|
|
158
|
+
'total': len(toolkits)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
def format_toolkit_schema(self, toolkit_name: str, schema: Dict[str, Any]) -> str:
|
|
162
|
+
"""Format toolkit schema as JSON."""
|
|
163
|
+
return self._dump({
|
|
164
|
+
'toolkit': toolkit_name,
|
|
165
|
+
'schema': schema
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def get_formatter(output_format: str = 'text', pretty: bool = True) -> OutputFormatter:
|
|
170
|
+
"""
|
|
171
|
+
Get output formatter by name.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
output_format: Format type ('text' or 'json')
|
|
175
|
+
pretty: For JSON formatter, whether to pretty-print
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
OutputFormatter instance
|
|
179
|
+
"""
|
|
180
|
+
if output_format == 'json':
|
|
181
|
+
return JSONFormatter(pretty=pretty)
|
|
182
|
+
return TextFormatter()
|