chatatp-cli 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chatatp_cli/__init__.py +0 -0
- chatatp_cli/api_client.py +183 -0
- chatatp_cli/config.py +68 -0
- chatatp_cli/main.py +830 -0
- chatatp_cli-1.0.0.dist-info/METADATA +14 -0
- chatatp_cli-1.0.0.dist-info/RECORD +9 -0
- chatatp_cli-1.0.0.dist-info/WHEEL +5 -0
- chatatp_cli-1.0.0.dist-info/entry_points.txt +2 -0
- chatatp_cli-1.0.0.dist-info/top_level.txt +1 -0
chatatp_cli/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
from typing import Dict, List, Optional, Any
|
|
3
|
+
import json
|
|
4
|
+
from config import Config
|
|
5
|
+
|
|
6
|
+
class ChatATPAPI:
|
|
7
|
+
def __init__(self, config: Config):
|
|
8
|
+
self.config = config
|
|
9
|
+
self.session = requests.Session()
|
|
10
|
+
self.session.headers.update(self.config.get_headers())
|
|
11
|
+
|
|
12
|
+
def _get(self, endpoint: str, params: Optional[Dict] = None) -> Dict:
|
|
13
|
+
"""Make GET request"""
|
|
14
|
+
url = f"{self.config.api_base_url}{endpoint}"
|
|
15
|
+
response = self.session.get(url, params=params)
|
|
16
|
+
response.raise_for_status()
|
|
17
|
+
return response.json()
|
|
18
|
+
|
|
19
|
+
def _post(self, endpoint: str, data: Optional[Dict] = None) -> Dict:
|
|
20
|
+
"""Make POST request"""
|
|
21
|
+
url = f"{self.config.api_base_url}{endpoint}"
|
|
22
|
+
response = self.session.post(url, json=data)
|
|
23
|
+
response.raise_for_status()
|
|
24
|
+
return response.json()
|
|
25
|
+
|
|
26
|
+
def _patch(self, endpoint: str, data: Optional[Dict] = None) -> Dict:
|
|
27
|
+
"""Make PATCH request"""
|
|
28
|
+
url = f"{self.config.api_base_url}{endpoint}"
|
|
29
|
+
response = self.session.patch(url, json=data)
|
|
30
|
+
response.raise_for_status()
|
|
31
|
+
return response.json()
|
|
32
|
+
|
|
33
|
+
def _delete(self, endpoint: str) -> Dict:
|
|
34
|
+
"""Make DELETE request"""
|
|
35
|
+
url = f"{self.config.api_base_url}{endpoint}"
|
|
36
|
+
response = self.session.delete(url)
|
|
37
|
+
response.raise_for_status()
|
|
38
|
+
return response.json() if response.content else {}
|
|
39
|
+
|
|
40
|
+
# Account endpoints
|
|
41
|
+
def get_account(self) -> Dict:
|
|
42
|
+
"""Get user account information"""
|
|
43
|
+
return self._get('/api/v1/account/')
|
|
44
|
+
|
|
45
|
+
# Models endpoints
|
|
46
|
+
def list_models(self) -> List[Dict]:
|
|
47
|
+
"""List available models"""
|
|
48
|
+
return self._get('/api/v1/list-models/')
|
|
49
|
+
|
|
50
|
+
# Collections/Toolkits endpoints
|
|
51
|
+
def list_collections(self) -> Dict:
|
|
52
|
+
"""List toolkits collections"""
|
|
53
|
+
return self._get('/api/v1/collections/toolkits/')
|
|
54
|
+
|
|
55
|
+
# MCP endpoints
|
|
56
|
+
def list_mcp_connections(self) -> Dict:
|
|
57
|
+
"""List MCP connections"""
|
|
58
|
+
return self._get('/api/v1/mcp/connections/')
|
|
59
|
+
|
|
60
|
+
def list_mcp_servers(self) -> Dict:
|
|
61
|
+
"""List MCP servers"""
|
|
62
|
+
return self._get('/api/v1/mcp/servers/')
|
|
63
|
+
|
|
64
|
+
# Chat endpoints
|
|
65
|
+
def list_chatrooms(self) -> Dict:
|
|
66
|
+
"""List chatrooms"""
|
|
67
|
+
return self._get('/api/v1/chatrooms/')
|
|
68
|
+
|
|
69
|
+
def get_chatroom(self, room_id: str) -> Dict:
|
|
70
|
+
"""Get chatroom details"""
|
|
71
|
+
return self._get(f'/api/v1/chats/{room_id}/')
|
|
72
|
+
|
|
73
|
+
def create_chatroom(self, message: str, model: str = None, toolkit_ids: List[str] = None,
|
|
74
|
+
mcp_server_connection_ids: List[str] = None) -> Dict:
|
|
75
|
+
"""Create new chatroom"""
|
|
76
|
+
data = {'message': message}
|
|
77
|
+
if model:
|
|
78
|
+
data['model'] = model
|
|
79
|
+
if toolkit_ids:
|
|
80
|
+
data['toolkit_ids'] = toolkit_ids
|
|
81
|
+
if mcp_server_connection_ids:
|
|
82
|
+
data['mcp_server_connection_ids'] = mcp_server_connection_ids
|
|
83
|
+
return self._post('/api/v1/chats/new/', data)
|
|
84
|
+
|
|
85
|
+
def send_chat_message_stream(self, room_id: str, message: str, model: str = None,
|
|
86
|
+
toolkit_ids: List[str] = None, mcp_server_connection_ids: List[str] = None,
|
|
87
|
+
attachments: List = None):
|
|
88
|
+
"""Send chat message and yield streaming response chunks"""
|
|
89
|
+
url = f"{self.config.api_base_url}/api/v1/chats/{room_id}/completions/stream/"
|
|
90
|
+
data = {
|
|
91
|
+
'message_type': 'chat_message',
|
|
92
|
+
'message': message,
|
|
93
|
+
'model': model or self.config.default_model,
|
|
94
|
+
'toolkit_ids': toolkit_ids or [],
|
|
95
|
+
'mcp_server_connection_ids': mcp_server_connection_ids or [],
|
|
96
|
+
'attachments': attachments or []
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
with self.session.post(url, json=data, stream=True) as response:
|
|
100
|
+
response.raise_for_status()
|
|
101
|
+
buffer = ""
|
|
102
|
+
|
|
103
|
+
for line in response.iter_lines():
|
|
104
|
+
if not line:
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
line = line.decode('utf-8')
|
|
108
|
+
|
|
109
|
+
# Handle SSE format: "data: {...}"
|
|
110
|
+
if line.startswith('data: '):
|
|
111
|
+
line = line[6:]
|
|
112
|
+
|
|
113
|
+
# Skip SSE comments or keep-alive
|
|
114
|
+
if line.startswith(':'):
|
|
115
|
+
continue
|
|
116
|
+
|
|
117
|
+
buffer += line
|
|
118
|
+
|
|
119
|
+
# Try to parse whatever is in the buffer
|
|
120
|
+
while buffer:
|
|
121
|
+
buffer = buffer.strip()
|
|
122
|
+
if not buffer:
|
|
123
|
+
break
|
|
124
|
+
try:
|
|
125
|
+
chunk, idx = json.JSONDecoder().raw_decode(buffer)
|
|
126
|
+
yield chunk
|
|
127
|
+
buffer = buffer[idx:].strip()
|
|
128
|
+
except json.JSONDecodeError:
|
|
129
|
+
# Incomplete JSON, wait for more data
|
|
130
|
+
break
|
|
131
|
+
|
|
132
|
+
# Media endpoints
|
|
133
|
+
def list_media(self, page: int = 1, page_size: int = 12, media_type: str = None, search: str = None) -> Dict:
|
|
134
|
+
"""List user media"""
|
|
135
|
+
params = {'page': page, 'page_size': page_size}
|
|
136
|
+
if media_type:
|
|
137
|
+
params['type'] = media_type
|
|
138
|
+
if search:
|
|
139
|
+
params['search'] = search
|
|
140
|
+
return self._get('/api/v1/media/', params)
|
|
141
|
+
|
|
142
|
+
# Integrations endpoints
|
|
143
|
+
def list_integrations(self) -> Dict:
|
|
144
|
+
"""List integrated accounts"""
|
|
145
|
+
return self._get('/api/v1/integrations/')
|
|
146
|
+
|
|
147
|
+
def list_custom_integrations(self) -> Dict:
|
|
148
|
+
"""List custom integrated accounts"""
|
|
149
|
+
return self._get('/api/v1/integrations/custom/')
|
|
150
|
+
|
|
151
|
+
def list_ai_configs(self) -> List[Dict]:
|
|
152
|
+
"""List AI provider configurations"""
|
|
153
|
+
return self._get('/api/v1/integrations/ai/providers/configs/')
|
|
154
|
+
|
|
155
|
+
def list_ai_providers(self) -> List[Dict]:
|
|
156
|
+
"""List AI providers"""
|
|
157
|
+
return self._get('/api/v1/integrations/ai/providers/')
|
|
158
|
+
|
|
159
|
+
def list_provider_models(self, provider_id: str) -> List[Dict]:
|
|
160
|
+
"""List models for a provider"""
|
|
161
|
+
return self._get('/api/v1/list-provider-models/', {'provider': provider_id})
|
|
162
|
+
|
|
163
|
+
def get_ai_settings(self) -> Dict:
|
|
164
|
+
"""Get AI settings"""
|
|
165
|
+
return self._get('/api/v1/integrations/ai/settings/')
|
|
166
|
+
|
|
167
|
+
# Store endpoints
|
|
168
|
+
def list_featured_toolkits(self) -> Dict:
|
|
169
|
+
"""List featured toolkits"""
|
|
170
|
+
return self._get('/api/v1/store/toolkits/featured/')
|
|
171
|
+
|
|
172
|
+
def list_popular_toolkits(self) -> Dict:
|
|
173
|
+
"""List popular toolkits"""
|
|
174
|
+
return self._get('/api/v1/store/toolkits/popular/')
|
|
175
|
+
|
|
176
|
+
def list_recommended_toolkits(self) -> Dict:
|
|
177
|
+
"""List recommended toolkits"""
|
|
178
|
+
return self._get('/api/v1/store/toolkits/for_you/')
|
|
179
|
+
|
|
180
|
+
# Pricing endpoints
|
|
181
|
+
def get_pricing(self) -> List[Dict]:
|
|
182
|
+
"""Get pricing information"""
|
|
183
|
+
return self._get('/api/v1/payments/pricing/')
|
chatatp_cli/config.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from typing import Optional
|
|
3
|
+
import yaml
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
class Config:
|
|
7
|
+
def __init__(self, config_file: str = None):
|
|
8
|
+
self.config_file = config_file or os.path.join(Path.home(), '.chatatp', 'config.yaml')
|
|
9
|
+
self._config = {}
|
|
10
|
+
self.load_config()
|
|
11
|
+
|
|
12
|
+
def load_config(self):
|
|
13
|
+
"""Load configuration from file"""
|
|
14
|
+
try:
|
|
15
|
+
if os.path.exists(self.config_file):
|
|
16
|
+
with open(self.config_file, 'r') as f:
|
|
17
|
+
self._config = yaml.safe_load(f) or {}
|
|
18
|
+
else:
|
|
19
|
+
self._config = {}
|
|
20
|
+
except Exception as e:
|
|
21
|
+
print(f"Warning: Could not load config file: {e}")
|
|
22
|
+
self._config = {}
|
|
23
|
+
|
|
24
|
+
def save_config(self):
|
|
25
|
+
"""Save configuration to file"""
|
|
26
|
+
try:
|
|
27
|
+
os.makedirs(os.path.dirname(self.config_file), exist_ok=True)
|
|
28
|
+
with open(self.config_file, 'w') as f:
|
|
29
|
+
yaml.dump(self._config, f, default_flow_style=False)
|
|
30
|
+
except Exception as e:
|
|
31
|
+
print(f"Error saving config: {e}")
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def api_base_url(self) -> str:
|
|
35
|
+
return self._config.get('api_base_url', 'https://api.chat-atp.com')
|
|
36
|
+
|
|
37
|
+
@api_base_url.setter
|
|
38
|
+
def api_base_url(self, value: str):
|
|
39
|
+
self._config['api_base_url'] = value
|
|
40
|
+
self.save_config()
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def api_token(self) -> Optional[str]:
|
|
44
|
+
return self._config.get('api_token')
|
|
45
|
+
|
|
46
|
+
@api_token.setter
|
|
47
|
+
def api_token(self, value: str):
|
|
48
|
+
self._config['api_token'] = value
|
|
49
|
+
self.save_config()
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def default_model(self) -> str:
|
|
53
|
+
return self._config.get('default_model', 'gpt-oss-120b')
|
|
54
|
+
|
|
55
|
+
@default_model.setter
|
|
56
|
+
def default_model(self, value: str):
|
|
57
|
+
self._config['default_model'] = value
|
|
58
|
+
self.save_config()
|
|
59
|
+
|
|
60
|
+
def get_headers(self) -> dict:
|
|
61
|
+
"""Get headers for API requests"""
|
|
62
|
+
headers = {
|
|
63
|
+
'Content-Type': 'application/json',
|
|
64
|
+
'Accept': 'application/json'
|
|
65
|
+
}
|
|
66
|
+
if self.api_token:
|
|
67
|
+
headers['Authorization'] = f'Token {self.api_token}'
|
|
68
|
+
return headers
|
chatatp_cli/main.py
ADDED
|
@@ -0,0 +1,830 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
ChatATP CLI - Terminal Interface for ChatATP API
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Optional, List
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
from rich.table import Table
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
from rich.live import Live
|
|
15
|
+
from rich.spinner import Spinner
|
|
16
|
+
from rich.markdown import Markdown
|
|
17
|
+
from .config import Config
|
|
18
|
+
from .api_client import ChatATPAPI
|
|
19
|
+
|
|
20
|
+
console = Console()
|
|
21
|
+
config_manager = Config()
|
|
22
|
+
api = ChatATPAPI(config_manager)
|
|
23
|
+
|
|
24
|
+
def format_json(data) -> str:
|
|
25
|
+
"""Format JSON data for display"""
|
|
26
|
+
return json.dumps(data, indent=2, ensure_ascii=False)
|
|
27
|
+
|
|
28
|
+
def check_auth():
|
|
29
|
+
"""Check if user is authenticated"""
|
|
30
|
+
if not config_manager.api_token:
|
|
31
|
+
console.print("[red]Error: No API token configured. Use 'chatatp config set-token <token>' to set it.[/red]")
|
|
32
|
+
sys.exit(1)
|
|
33
|
+
|
|
34
|
+
@click.group()
|
|
35
|
+
@click.version_option(version="1.0.0")
|
|
36
|
+
def cli():
|
|
37
|
+
"""ChatATP CLI - Terminal Interface for ChatATP API"""
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
# Configuration commands
|
|
41
|
+
@cli.group()
|
|
42
|
+
def config():
|
|
43
|
+
"""Configuration management"""
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
@config.command()
|
|
47
|
+
@click.argument('token')
|
|
48
|
+
def set_token(token):
|
|
49
|
+
"""Set API token"""
|
|
50
|
+
config_manager.api_token = token
|
|
51
|
+
console.print("[green]API token set successfully![/green]")
|
|
52
|
+
|
|
53
|
+
@config.command()
|
|
54
|
+
@click.argument('url')
|
|
55
|
+
def set_base_url(url):
|
|
56
|
+
"""Set API base URL"""
|
|
57
|
+
config_manager.api_base_url = url
|
|
58
|
+
console.print("[green]API base URL set successfully![/green]")
|
|
59
|
+
|
|
60
|
+
@config.command()
|
|
61
|
+
@click.argument('model')
|
|
62
|
+
def set_default_model(model):
|
|
63
|
+
"""Set default model"""
|
|
64
|
+
config_manager.default_model = model
|
|
65
|
+
console.print("[green]Default model set successfully![/green]")
|
|
66
|
+
|
|
67
|
+
@config.command()
|
|
68
|
+
def show():
|
|
69
|
+
"""Show current configuration"""
|
|
70
|
+
table = Table(title="Configuration")
|
|
71
|
+
table.add_column("Setting", style="cyan")
|
|
72
|
+
table.add_column("Value", style="magenta")
|
|
73
|
+
|
|
74
|
+
table.add_row("API Base URL", config_manager.api_base_url)
|
|
75
|
+
table.add_row("API Token", config_manager.api_token[:20] + "..." if config_manager.api_token else "Not set")
|
|
76
|
+
table.add_row("Default Model", config_manager.default_model)
|
|
77
|
+
|
|
78
|
+
console.print(table)
|
|
79
|
+
|
|
80
|
+
# Account commands
|
|
81
|
+
@cli.command()
|
|
82
|
+
def account():
|
|
83
|
+
"""Get account information"""
|
|
84
|
+
check_auth()
|
|
85
|
+
try:
|
|
86
|
+
with console.status("[bold green]Fetching account info..."):
|
|
87
|
+
data = api.get_account()
|
|
88
|
+
|
|
89
|
+
user = data['user_account']['user']
|
|
90
|
+
console.print(Panel.fit(
|
|
91
|
+
f"[bold blue]Name:[/bold blue] {user['first_name']} {user['last_name']}\n"
|
|
92
|
+
f"[bold blue]Username:[/bold blue] {user['username']}\n"
|
|
93
|
+
f"[bold blue]Email:[/bold blue] {user['email']}\n"
|
|
94
|
+
f"[bold blue]Bio:[/bold blue] {data['user_account']['bio']}\n"
|
|
95
|
+
f"[bold blue]Company:[/bold blue] {data['user_account']['company_name']}\n"
|
|
96
|
+
f"[bold blue]Credits:[/bold blue] {data['user_account']['credits']}",
|
|
97
|
+
title="Account Information"
|
|
98
|
+
))
|
|
99
|
+
except Exception as e:
|
|
100
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
101
|
+
|
|
102
|
+
# Models commands
|
|
103
|
+
@cli.command()
|
|
104
|
+
def models():
|
|
105
|
+
"""List available models"""
|
|
106
|
+
check_auth()
|
|
107
|
+
try:
|
|
108
|
+
with console.status("[bold green]Fetching models..."):
|
|
109
|
+
data = api.list_models()
|
|
110
|
+
|
|
111
|
+
table = Table(title="Available Models")
|
|
112
|
+
table.add_column("ID", style="cyan")
|
|
113
|
+
table.add_column("Name", style="magenta")
|
|
114
|
+
table.add_column("Description", style="white")
|
|
115
|
+
table.add_column("Default", style="green")
|
|
116
|
+
|
|
117
|
+
for model in data:
|
|
118
|
+
table.add_row(
|
|
119
|
+
model['id'],
|
|
120
|
+
model['name'],
|
|
121
|
+
model['description'],
|
|
122
|
+
"✓" if model.get('default', False) else ""
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
console.print(table)
|
|
126
|
+
except Exception as e:
|
|
127
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
128
|
+
|
|
129
|
+
# Toolkits commands
|
|
130
|
+
@cli.command()
|
|
131
|
+
def toolkits():
|
|
132
|
+
"""List user's toolkits"""
|
|
133
|
+
check_auth()
|
|
134
|
+
try:
|
|
135
|
+
with console.status("[bold green]Fetching toolkits..."):
|
|
136
|
+
data = api.list_collections()
|
|
137
|
+
|
|
138
|
+
table = Table(title="Your Toolkits")
|
|
139
|
+
table.add_column("Name", style="cyan")
|
|
140
|
+
table.add_column("Display Name", style="magenta")
|
|
141
|
+
table.add_column("Category", style="white")
|
|
142
|
+
table.add_column("Installs", style="green")
|
|
143
|
+
|
|
144
|
+
for toolkit in data['toolkits']:
|
|
145
|
+
table.add_row(
|
|
146
|
+
toolkit['tool_kit']['name'],
|
|
147
|
+
toolkit['tool_kit']['display_name'],
|
|
148
|
+
toolkit['tool_kit']['category'],
|
|
149
|
+
str(toolkit['tool_kit']['installs'])
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
console.print(table)
|
|
153
|
+
except Exception as e:
|
|
154
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
155
|
+
|
|
156
|
+
# Chat commands
|
|
157
|
+
@cli.group()
|
|
158
|
+
def chat():
|
|
159
|
+
"""Chat management"""
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
@chat.command()
|
|
163
|
+
def rooms():
|
|
164
|
+
"""List chatrooms"""
|
|
165
|
+
check_auth()
|
|
166
|
+
try:
|
|
167
|
+
with console.status("[bold green]Fetching chatrooms..."):
|
|
168
|
+
data = api.list_chatrooms()
|
|
169
|
+
|
|
170
|
+
table = Table(title="Chat Rooms")
|
|
171
|
+
table.add_column("Room ID", style="cyan")
|
|
172
|
+
table.add_column("Name", style="magenta")
|
|
173
|
+
table.add_column("Created", style="white")
|
|
174
|
+
|
|
175
|
+
for room in data['chatrooms']:
|
|
176
|
+
table.add_row(
|
|
177
|
+
room['room_id'],
|
|
178
|
+
room['group_name'],
|
|
179
|
+
room['created'][:19]
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
console.print(table)
|
|
183
|
+
except Exception as e:
|
|
184
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
185
|
+
|
|
186
|
+
@chat.command()
|
|
187
|
+
@click.argument('room_id')
|
|
188
|
+
def show(room_id):
|
|
189
|
+
"""Show chatroom details"""
|
|
190
|
+
check_auth()
|
|
191
|
+
try:
|
|
192
|
+
with console.status("[bold green]Fetching chat details..."):
|
|
193
|
+
data = api.get_chatroom(room_id)
|
|
194
|
+
|
|
195
|
+
room = data['chatroom']
|
|
196
|
+
console.print(Panel.fit(
|
|
197
|
+
f"[bold blue]Name:[/bold blue] {room['group_name']}\n"
|
|
198
|
+
f"[bold blue]Description:[/bold blue] {room['group_description']}\n"
|
|
199
|
+
f"[bold blue]Created:[/bold blue] {room['created'][:19]}\n"
|
|
200
|
+
f"[bold blue]Members:[/bold blue] {len(room['members'])}",
|
|
201
|
+
title=f"Chat Room: {room['room_id']}"
|
|
202
|
+
))
|
|
203
|
+
|
|
204
|
+
if data['chats']:
|
|
205
|
+
console.print("\n[bold]Recent Messages:[/bold]")
|
|
206
|
+
for chat in data['chats'][-5:]: # Show last 5 messages
|
|
207
|
+
sender = chat['sender_account']['user']['first_name']
|
|
208
|
+
time = chat['created'][:19]
|
|
209
|
+
message = chat['text'][:100] + "..." if len(chat['text']) > 100 else chat['text']
|
|
210
|
+
console.print(f"[cyan]{time}[/cyan] [magenta]{sender}:[/magenta] {message}")
|
|
211
|
+
except Exception as e:
|
|
212
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
213
|
+
|
|
214
|
+
def chat_loop(room_id, initial_message=None, model=None, toolkits=None, mcp_connections=None):
|
|
215
|
+
"""Interactive chat loop for back-and-forth conversation"""
|
|
216
|
+
console.print(f"\n[bold cyan]Entered chatroom: {room_id}[/bold cyan]")
|
|
217
|
+
console.print("[dim]Type your message or '/exit' to quit, '/help' for commands[/dim]\n")
|
|
218
|
+
|
|
219
|
+
# Send initial message if provided
|
|
220
|
+
if initial_message:
|
|
221
|
+
console.print(f"[bold green]You:[/bold green] {initial_message}")
|
|
222
|
+
send_single_message(room_id, initial_message, model, toolkits, mcp_connections)
|
|
223
|
+
|
|
224
|
+
while True:
|
|
225
|
+
try:
|
|
226
|
+
# Get user input
|
|
227
|
+
user_input = console.input("[bold green]You:[/bold green] ").strip()
|
|
228
|
+
|
|
229
|
+
if not user_input:
|
|
230
|
+
continue
|
|
231
|
+
|
|
232
|
+
# Handle commands
|
|
233
|
+
if user_input.lower() in ['/exit', '/quit', '/q']:
|
|
234
|
+
console.print("[yellow]Exiting chat...[/yellow]")
|
|
235
|
+
break
|
|
236
|
+
elif user_input.lower() in ['/help', '/h']:
|
|
237
|
+
console.print("\n[bold]Available commands:[/bold]")
|
|
238
|
+
console.print(" /exit, /quit, /q - Exit the chat")
|
|
239
|
+
console.print(" /help, /h - Show this help")
|
|
240
|
+
console.print(" /clear - Clear the screen")
|
|
241
|
+
console.print(" /history - Show chat history")
|
|
242
|
+
console.print()
|
|
243
|
+
continue
|
|
244
|
+
elif user_input.lower() == '/clear':
|
|
245
|
+
console.clear()
|
|
246
|
+
console.print(f"[bold cyan]Chatroom: {room_id}[/bold cyan]")
|
|
247
|
+
console.print("[dim]Type your message or '/exit' to quit[/dim]\n")
|
|
248
|
+
continue
|
|
249
|
+
elif user_input.lower() == '/history':
|
|
250
|
+
try:
|
|
251
|
+
with console.status("[bold cyan]Loading history...[/bold cyan]"):
|
|
252
|
+
data = api.get_chatroom(room_id)
|
|
253
|
+
if data.get('chats'):
|
|
254
|
+
console.print("\n[bold]Recent messages:[/bold]")
|
|
255
|
+
for chat in data['chats'][-10:]: # Last 10 messages
|
|
256
|
+
sender = chat['sender_account']['user']['first_name']
|
|
257
|
+
time = chat['created'][:19]
|
|
258
|
+
message = chat['text'][:100] + "..." if len(chat['text']) > 100 else chat['text']
|
|
259
|
+
if sender == "ChatATP":
|
|
260
|
+
console.print(f"[cyan]{time}[/cyan] [magenta]{sender}:[/magenta] {message}")
|
|
261
|
+
else:
|
|
262
|
+
console.print(f"[cyan]{time}[/cyan] [green]{sender}:[/green] {message}")
|
|
263
|
+
console.print()
|
|
264
|
+
else:
|
|
265
|
+
console.print("[yellow]No chat history found.[/yellow]\n")
|
|
266
|
+
except Exception as e:
|
|
267
|
+
console.print(f"[red]Error loading history: {e}[/red]\n")
|
|
268
|
+
continue
|
|
269
|
+
|
|
270
|
+
# Send the message
|
|
271
|
+
send_single_message(room_id, user_input, model, toolkits, mcp_connections)
|
|
272
|
+
|
|
273
|
+
except KeyboardInterrupt:
|
|
274
|
+
console.print("\n[yellow]Interrupted. Use '/exit' to quit properly.[/yellow]")
|
|
275
|
+
except EOFError:
|
|
276
|
+
console.print("\n[yellow]EOF received. Exiting...[/yellow]")
|
|
277
|
+
break
|
|
278
|
+
|
|
279
|
+
def send_single_message(room_id, message, model=None, toolkits=None, mcp_connections=None, debug=False):
|
|
280
|
+
"""Send a single message and handle the streaming response"""
|
|
281
|
+
try:
|
|
282
|
+
full_response = ""
|
|
283
|
+
in_think_block = False
|
|
284
|
+
chunk_count = 0
|
|
285
|
+
response_started = False
|
|
286
|
+
tool_lines = []
|
|
287
|
+
|
|
288
|
+
with console.status("[bold cyan]Sending...[/bold cyan]", spinner="dots") as status:
|
|
289
|
+
|
|
290
|
+
for chunk in api.send_chat_message_stream(
|
|
291
|
+
room_id=room_id,
|
|
292
|
+
message=message,
|
|
293
|
+
model=model,
|
|
294
|
+
toolkit_ids=list(toolkits) if toolkits else None,
|
|
295
|
+
mcp_server_connection_ids=list(mcp_connections) if mcp_connections else None
|
|
296
|
+
):
|
|
297
|
+
chunk_count += 1
|
|
298
|
+
|
|
299
|
+
if debug:
|
|
300
|
+
console.print(f"[dim]CHUNK #{chunk_count}: {chunk}[/dim]")
|
|
301
|
+
|
|
302
|
+
msg_type = chunk.get('message_type', '')
|
|
303
|
+
|
|
304
|
+
# ── tool_call ──────────────────────────────────────────────
|
|
305
|
+
if msg_type == 'tool_call':
|
|
306
|
+
tool = chunk.get('tool', {})
|
|
307
|
+
tool_name = tool.get('name') or chunk.get('toolkit_name') or 'tool'
|
|
308
|
+
toolkit = chunk.get('toolkit_name', '')
|
|
309
|
+
args = tool.get('arguments', {})
|
|
310
|
+
|
|
311
|
+
arg_hint = ''
|
|
312
|
+
if args:
|
|
313
|
+
first_val = next(iter(args.values()), None)
|
|
314
|
+
if first_val and isinstance(first_val, str) and len(first_val) < 60:
|
|
315
|
+
arg_hint = f' [dim]"{first_val}"[/dim]'
|
|
316
|
+
|
|
317
|
+
# Print a running line — no emoji, clean mono style
|
|
318
|
+
console.print(
|
|
319
|
+
f"\n [dim]┌[/dim] [bold white]{tool_name}[/bold white]"
|
|
320
|
+
f"[dim] · {toolkit}{arg_hint}[/dim]"
|
|
321
|
+
)
|
|
322
|
+
status.update(
|
|
323
|
+
f"[cyan]Running [bold]{tool_name}[/bold]...[/cyan]"
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
# ── tool_result ────────────────────────────────────────────
|
|
327
|
+
elif msg_type == 'tool_result':
|
|
328
|
+
executions = chunk.get('timing_metrics', {}).get('tool_executions', [])
|
|
329
|
+
if executions:
|
|
330
|
+
last = executions[-1]
|
|
331
|
+
tool_name = last.get('tool_name', 'tool')
|
|
332
|
+
duration = last.get('execution_duration', 0)
|
|
333
|
+
ok = last.get('status', 'success') == 'success'
|
|
334
|
+
mark = '[bold green]done[/bold green]' if ok else '[bold red]failed[/bold red]'
|
|
335
|
+
console.print(
|
|
336
|
+
f" [dim]└[/dim] {mark} [dim]{duration:.2f}s[/dim]"
|
|
337
|
+
)
|
|
338
|
+
else:
|
|
339
|
+
console.print(f" [dim]└[/dim] [bold green]done[/bold green]")
|
|
340
|
+
|
|
341
|
+
status.update("[cyan]Processing...[/cyan]")
|
|
342
|
+
|
|
343
|
+
# ── chat_message ───────────────────────────────────────────
|
|
344
|
+
elif msg_type == 'chat_message':
|
|
345
|
+
message_chunk = chunk.get('message', '')
|
|
346
|
+
|
|
347
|
+
if message_chunk:
|
|
348
|
+
if '<think>' in message_chunk:
|
|
349
|
+
in_think_block = True
|
|
350
|
+
|
|
351
|
+
if in_think_block:
|
|
352
|
+
if '</think>' in message_chunk:
|
|
353
|
+
in_think_block = False
|
|
354
|
+
status.update("[cyan]Processing...[/cyan]")
|
|
355
|
+
else:
|
|
356
|
+
status.update("[yellow]Thinking...[/yellow]")
|
|
357
|
+
continue
|
|
358
|
+
|
|
359
|
+
if not response_started:
|
|
360
|
+
response_started = True
|
|
361
|
+
status.stop()
|
|
362
|
+
console.print(
|
|
363
|
+
f"\n[bold white]ChatATP[/bold white] [dim]·[/dim]\n"
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
full_response += message_chunk
|
|
367
|
+
|
|
368
|
+
if chunk.get('is_typing') == False:
|
|
369
|
+
break
|
|
370
|
+
|
|
371
|
+
if response_started:
|
|
372
|
+
console.print(Markdown(full_response))
|
|
373
|
+
console.print("\n[dim]─────────────────────────────────[/dim]")
|
|
374
|
+
elif chunk_count == 0:
|
|
375
|
+
console.print("[red]No data received.[/red]")
|
|
376
|
+
else:
|
|
377
|
+
console.print(f"[yellow]No message content in {chunk_count} chunks.[/yellow]")
|
|
378
|
+
if not debug:
|
|
379
|
+
console.print("[dim]Run with --debug to inspect.[/dim]")
|
|
380
|
+
|
|
381
|
+
except Exception as e:
|
|
382
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
383
|
+
if debug:
|
|
384
|
+
import traceback
|
|
385
|
+
traceback.print_exc()
|
|
386
|
+
|
|
387
|
+
@chat.command()
|
|
388
|
+
@click.argument('message')
|
|
389
|
+
@click.option('--model', default=None, help='Model to use')
|
|
390
|
+
@click.option('--toolkits', multiple=True, help='Toolkit IDs to use')
|
|
391
|
+
@click.option('--mcp-connections', multiple=True, help='MCP connection IDs to use')
|
|
392
|
+
def new(message, model, toolkits, mcp_connections):
|
|
393
|
+
"""Create new chatroom and start interactive chat"""
|
|
394
|
+
check_auth()
|
|
395
|
+
try:
|
|
396
|
+
with console.status("[bold green]Creating chatroom..."):
|
|
397
|
+
data = api.create_chatroom(
|
|
398
|
+
message=message,
|
|
399
|
+
model=model,
|
|
400
|
+
toolkit_ids=list(toolkits) if toolkits else None,
|
|
401
|
+
mcp_server_connection_ids=list(mcp_connections) if mcp_connections else None
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
room_id = data['room_id']
|
|
405
|
+
console.print(f"[green]Chatroom created: {room_id}[/green]")
|
|
406
|
+
|
|
407
|
+
# Start the interactive chat loop
|
|
408
|
+
chat_loop(room_id, initial_message=message, model=model, toolkits=toolkits, mcp_connections=mcp_connections)
|
|
409
|
+
|
|
410
|
+
except Exception as e:
|
|
411
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
412
|
+
|
|
413
|
+
@chat.command()
|
|
414
|
+
@click.argument('room_id')
|
|
415
|
+
@click.option('--model', default=None, help='Model to use')
|
|
416
|
+
@click.option('--toolkits', multiple=True, help='Toolkit IDs to use')
|
|
417
|
+
@click.option('--mcp-connections', multiple=True, help='MCP connection IDs to use')
|
|
418
|
+
def converse(room_id, model, toolkits, mcp_connections):
|
|
419
|
+
"""Enter existing chatroom for interactive chat"""
|
|
420
|
+
check_auth()
|
|
421
|
+
try:
|
|
422
|
+
# Verify the room exists
|
|
423
|
+
with console.status("[bold cyan]Entering chatroom...[/bold cyan]"):
|
|
424
|
+
data = api.get_chatroom(room_id)
|
|
425
|
+
|
|
426
|
+
# Start the interactive chat loop
|
|
427
|
+
chat_loop(room_id, initial_message=None, model=model, toolkits=toolkits, mcp_connections=mcp_connections)
|
|
428
|
+
|
|
429
|
+
except Exception as e:
|
|
430
|
+
console.print(f"[red]Error entering chatroom: {e}[/red]")
|
|
431
|
+
|
|
432
|
+
@chat.command()
|
|
433
|
+
@click.argument('room_id')
|
|
434
|
+
@click.argument('message')
|
|
435
|
+
@click.option('--model', default=None, help='Model to use')
|
|
436
|
+
@click.option('--toolkits', multiple=True, help='Toolkit IDs to use')
|
|
437
|
+
@click.option('--mcp-connections', multiple=True, help='MCP connection IDs to use')
|
|
438
|
+
@click.option('--debug', is_flag=True, help='Show raw stream chunks')
|
|
439
|
+
def send(room_id, message, model, toolkits, mcp_connections, debug):
|
|
440
|
+
"""Send message to chatroom"""
|
|
441
|
+
check_auth()
|
|
442
|
+
try:
|
|
443
|
+
full_response = ""
|
|
444
|
+
in_think_block = False
|
|
445
|
+
chunk_count = 0
|
|
446
|
+
response_started = False
|
|
447
|
+
tool_lines = [] # track printed tool lines to update them
|
|
448
|
+
|
|
449
|
+
with console.status("[bold cyan]Sending...[/bold cyan]", spinner="dots") as status:
|
|
450
|
+
|
|
451
|
+
for chunk in api.send_chat_message_stream(
|
|
452
|
+
room_id=room_id,
|
|
453
|
+
message=message,
|
|
454
|
+
model=model,
|
|
455
|
+
toolkit_ids=list(toolkits) if toolkits else None,
|
|
456
|
+
mcp_server_connection_ids=list(mcp_connections) if mcp_connections else None
|
|
457
|
+
):
|
|
458
|
+
chunk_count += 1
|
|
459
|
+
|
|
460
|
+
if debug:
|
|
461
|
+
console.print(f"[dim]CHUNK #{chunk_count}: {chunk}[/dim]")
|
|
462
|
+
|
|
463
|
+
msg_type = chunk.get('message_type', '')
|
|
464
|
+
|
|
465
|
+
# ── tool_call ──────────────────────────────────────────────
|
|
466
|
+
if msg_type == 'tool_call':
|
|
467
|
+
tool = chunk.get('tool', {})
|
|
468
|
+
tool_name = tool.get('name') or chunk.get('toolkit_name') or 'tool'
|
|
469
|
+
toolkit = chunk.get('toolkit_name', '')
|
|
470
|
+
args = tool.get('arguments', {})
|
|
471
|
+
|
|
472
|
+
arg_hint = ''
|
|
473
|
+
if args:
|
|
474
|
+
first_val = next(iter(args.values()), None)
|
|
475
|
+
if first_val and isinstance(first_val, str) and len(first_val) < 60:
|
|
476
|
+
arg_hint = f' [dim]"{first_val}"[/dim]'
|
|
477
|
+
|
|
478
|
+
# Print a running line — no emoji, clean mono style
|
|
479
|
+
console.print(
|
|
480
|
+
f"\n [dim]┌[/dim] [bold white]{tool_name}[/bold white]"
|
|
481
|
+
f"[dim] · {toolkit}{arg_hint}[/dim]"
|
|
482
|
+
)
|
|
483
|
+
status.update(
|
|
484
|
+
f"[cyan]Running [bold]{tool_name}[/bold]...[/cyan]"
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
# ── tool_result ────────────────────────────────────────────
|
|
488
|
+
elif msg_type == 'tool_result':
|
|
489
|
+
executions = chunk.get('timing_metrics', {}).get('tool_executions', [])
|
|
490
|
+
if executions:
|
|
491
|
+
last = executions[-1]
|
|
492
|
+
tool_name = last.get('tool_name', 'tool')
|
|
493
|
+
duration = last.get('execution_duration', 0)
|
|
494
|
+
ok = last.get('status', 'success') == 'success'
|
|
495
|
+
mark = '[bold green]done[/bold green]' if ok else '[bold red]failed[/bold red]'
|
|
496
|
+
console.print(
|
|
497
|
+
f" [dim]└[/dim] {mark} [dim]{duration:.2f}s[/dim]"
|
|
498
|
+
)
|
|
499
|
+
else:
|
|
500
|
+
console.print(f" [dim]└[/dim] [bold green]done[/bold green]")
|
|
501
|
+
|
|
502
|
+
status.update("[cyan]Processing...[/cyan]")
|
|
503
|
+
|
|
504
|
+
# ── chat_message ───────────────────────────────────────────
|
|
505
|
+
elif msg_type == 'chat_message':
|
|
506
|
+
message_chunk = chunk.get('message', '')
|
|
507
|
+
|
|
508
|
+
if message_chunk:
|
|
509
|
+
if '<think>' in message_chunk:
|
|
510
|
+
in_think_block = True
|
|
511
|
+
|
|
512
|
+
if in_think_block:
|
|
513
|
+
if '</think>' in message_chunk:
|
|
514
|
+
in_think_block = False
|
|
515
|
+
status.update("[cyan]Processing...[/cyan]")
|
|
516
|
+
else:
|
|
517
|
+
status.update("[yellow]Thinking...[/yellow]")
|
|
518
|
+
continue
|
|
519
|
+
|
|
520
|
+
if not response_started:
|
|
521
|
+
response_started = True
|
|
522
|
+
status.stop()
|
|
523
|
+
console.print(
|
|
524
|
+
f"\n[bold white]ChatATP[/bold white] [dim]·[/dim]\n"
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
full_response += message_chunk
|
|
528
|
+
|
|
529
|
+
if chunk.get('is_typing') == False:
|
|
530
|
+
break
|
|
531
|
+
|
|
532
|
+
if response_started:
|
|
533
|
+
console.print(Markdown(full_response))
|
|
534
|
+
console.print("\n[dim]─────────────────────────────────[/dim]")
|
|
535
|
+
elif chunk_count == 0:
|
|
536
|
+
console.print("[red]No data received.[/red]")
|
|
537
|
+
else:
|
|
538
|
+
console.print(f"[yellow]No message content in {chunk_count} chunks.[/yellow]")
|
|
539
|
+
if not debug:
|
|
540
|
+
console.print("[dim]Run with --debug to inspect.[/dim]")
|
|
541
|
+
|
|
542
|
+
except Exception as e:
|
|
543
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
544
|
+
if debug:
|
|
545
|
+
import traceback
|
|
546
|
+
traceback.print_exc()
|
|
547
|
+
|
|
548
|
+
# Integrations commands
|
|
549
|
+
@cli.group()
|
|
550
|
+
def integrations():
|
|
551
|
+
"""Integration management"""
|
|
552
|
+
pass
|
|
553
|
+
|
|
554
|
+
@integrations.command()
|
|
555
|
+
def list():
|
|
556
|
+
"""List integrations"""
|
|
557
|
+
check_auth()
|
|
558
|
+
try:
|
|
559
|
+
with console.status("[bold green]Fetching integrations..."):
|
|
560
|
+
data = api.list_integrations()
|
|
561
|
+
|
|
562
|
+
table = Table(title="OAuth Integrations")
|
|
563
|
+
table.add_column("Platform", style="cyan")
|
|
564
|
+
table.add_column("Display Name", style="magenta")
|
|
565
|
+
table.add_column("Status", style="green")
|
|
566
|
+
|
|
567
|
+
for integration in data['integrations']:
|
|
568
|
+
platform = integration['platform']['display_name'] if integration.get('platform') else 'N/A'
|
|
569
|
+
table.add_row(
|
|
570
|
+
integration['platform']['name'] if integration.get('platform') else 'Unknown',
|
|
571
|
+
platform,
|
|
572
|
+
"Connected" if integration.get('access_token') else "Not Connected"
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
console.print(table)
|
|
576
|
+
except Exception as e:
|
|
577
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
578
|
+
|
|
579
|
+
@integrations.command()
|
|
580
|
+
def custom():
|
|
581
|
+
"""List custom integrations"""
|
|
582
|
+
check_auth()
|
|
583
|
+
try:
|
|
584
|
+
with console.status("[bold green]Fetching custom integrations..."):
|
|
585
|
+
data = api.list_custom_integrations()
|
|
586
|
+
|
|
587
|
+
table = Table(title="Custom Integrations")
|
|
588
|
+
table.add_column("Name", style="cyan")
|
|
589
|
+
table.add_column("Unique Name", style="magenta")
|
|
590
|
+
table.add_column("API Key", style="green")
|
|
591
|
+
|
|
592
|
+
for integration in data['integrations']:
|
|
593
|
+
table.add_row(
|
|
594
|
+
integration['name'],
|
|
595
|
+
integration['unique_name'],
|
|
596
|
+
"Set" if integration['api_key'] else "Not Set"
|
|
597
|
+
)
|
|
598
|
+
|
|
599
|
+
console.print(table)
|
|
600
|
+
except Exception as e:
|
|
601
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
602
|
+
|
|
603
|
+
# AI commands
|
|
604
|
+
@cli.group()
|
|
605
|
+
def ai():
|
|
606
|
+
"""AI management"""
|
|
607
|
+
pass
|
|
608
|
+
|
|
609
|
+
@ai.command()
|
|
610
|
+
def providers():
|
|
611
|
+
"""List AI providers"""
|
|
612
|
+
check_auth()
|
|
613
|
+
try:
|
|
614
|
+
with console.status("[bold green]Fetching providers..."):
|
|
615
|
+
data = api.list_ai_providers()
|
|
616
|
+
|
|
617
|
+
table = Table(title="AI Providers")
|
|
618
|
+
table.add_column("Name", style="cyan")
|
|
619
|
+
table.add_column("Unique Name", style="magenta")
|
|
620
|
+
table.add_column("Category", style="white")
|
|
621
|
+
table.add_column("Connected", style="green")
|
|
622
|
+
|
|
623
|
+
for provider in data:
|
|
624
|
+
table.add_row(
|
|
625
|
+
provider['name'],
|
|
626
|
+
provider['unique_name'],
|
|
627
|
+
provider['category'],
|
|
628
|
+
"✓" if provider.get('connected', False) else ""
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
console.print(table)
|
|
632
|
+
except Exception as e:
|
|
633
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
634
|
+
|
|
635
|
+
@ai.command()
|
|
636
|
+
def configs():
|
|
637
|
+
"""List AI configurations"""
|
|
638
|
+
check_auth()
|
|
639
|
+
try:
|
|
640
|
+
with console.status("[bold green]Fetching configs..."):
|
|
641
|
+
data = api.list_ai_configs()
|
|
642
|
+
|
|
643
|
+
table = Table(title="AI Configurations")
|
|
644
|
+
table.add_column("Provider", style="cyan")
|
|
645
|
+
table.add_column("Config ID", style="magenta")
|
|
646
|
+
table.add_column("Default", style="green")
|
|
647
|
+
table.add_column("API Key", style="white")
|
|
648
|
+
|
|
649
|
+
for config_item in data:
|
|
650
|
+
provider_name = config_item['provider']['name']
|
|
651
|
+
table.add_row(
|
|
652
|
+
provider_name,
|
|
653
|
+
config_item['config_id'],
|
|
654
|
+
"✓" if config_item.get('default', False) else "",
|
|
655
|
+
"Set" if config_item.get('api_key') else "Not Set"
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
console.print(table)
|
|
659
|
+
except Exception as e:
|
|
660
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
661
|
+
|
|
662
|
+
@ai.command()
|
|
663
|
+
@click.argument('provider_id')
|
|
664
|
+
def provider_models(provider_id):
|
|
665
|
+
"""List models for a provider"""
|
|
666
|
+
check_auth()
|
|
667
|
+
try:
|
|
668
|
+
with console.status("[bold green]Fetching provider models..."):
|
|
669
|
+
data = api.list_provider_models(provider_id)
|
|
670
|
+
|
|
671
|
+
table = Table(title=f"Models for Provider {provider_id}")
|
|
672
|
+
table.add_column("ID", style="cyan")
|
|
673
|
+
table.add_column("Name", style="magenta")
|
|
674
|
+
table.add_column("Description", style="white")
|
|
675
|
+
|
|
676
|
+
for model in data:
|
|
677
|
+
table.add_row(
|
|
678
|
+
model['id'],
|
|
679
|
+
model['name'],
|
|
680
|
+
model['description']
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
console.print(table)
|
|
684
|
+
except Exception as e:
|
|
685
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
686
|
+
|
|
687
|
+
@ai.command()
|
|
688
|
+
def settings():
|
|
689
|
+
"""Show AI settings"""
|
|
690
|
+
check_auth()
|
|
691
|
+
try:
|
|
692
|
+
with console.status("[bold green]Fetching settings..."):
|
|
693
|
+
data = api.get_ai_settings()
|
|
694
|
+
|
|
695
|
+
console.print(Panel.fit(
|
|
696
|
+
f"[bold blue]Default Chat Model:[/bold blue] {data['default_chat_completion_model_id']}\n"
|
|
697
|
+
f"[bold blue]Default Image Model:[/bold blue] {data['default_image_gen_model_id']}\n"
|
|
698
|
+
f"[bold blue]Default Speech Model:[/bold blue] {data['default_speech_gen_model_id']}\n"
|
|
699
|
+
f"[bold blue]Temperature:[/bold blue] {data['temperature']}\n"
|
|
700
|
+
f"[bold blue]Max Tokens:[/bold blue] {data['max_tokens']}\n"
|
|
701
|
+
f"[bold blue]System Instruction:[/bold blue] {data['system_instruction'][:100]}...",
|
|
702
|
+
title="AI Settings"
|
|
703
|
+
))
|
|
704
|
+
except Exception as e:
|
|
705
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
706
|
+
|
|
707
|
+
# Store commands
|
|
708
|
+
@cli.group()
|
|
709
|
+
def store():
|
|
710
|
+
"""Store management"""
|
|
711
|
+
pass
|
|
712
|
+
|
|
713
|
+
@store.command()
|
|
714
|
+
def featured():
|
|
715
|
+
"""List featured toolkits"""
|
|
716
|
+
check_auth()
|
|
717
|
+
try:
|
|
718
|
+
with console.status("[bold green]Fetching featured toolkits..."):
|
|
719
|
+
data = api.list_featured_toolkits()
|
|
720
|
+
|
|
721
|
+
table = Table(title="Featured Toolkits")
|
|
722
|
+
table.add_column("Name", style="cyan")
|
|
723
|
+
table.add_column("Display Name", style="magenta")
|
|
724
|
+
table.add_column("Category", style="white")
|
|
725
|
+
table.add_column("Installs", style="green")
|
|
726
|
+
|
|
727
|
+
for toolkit in data['featured']:
|
|
728
|
+
table.add_row(
|
|
729
|
+
toolkit['name'],
|
|
730
|
+
toolkit['display_name'],
|
|
731
|
+
toolkit['category'],
|
|
732
|
+
str(toolkit['installs'])
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
console.print(table)
|
|
736
|
+
except Exception as e:
|
|
737
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
738
|
+
|
|
739
|
+
@store.command()
|
|
740
|
+
def popular():
|
|
741
|
+
"""List popular toolkits"""
|
|
742
|
+
check_auth()
|
|
743
|
+
try:
|
|
744
|
+
with console.status("[bold green]Fetching popular toolkits..."):
|
|
745
|
+
data = api.list_popular_toolkits()
|
|
746
|
+
|
|
747
|
+
table = Table(title="Popular Toolkits")
|
|
748
|
+
table.add_column("Name", style="cyan")
|
|
749
|
+
table.add_column("Display Name", style="magenta")
|
|
750
|
+
table.add_column("Category", style="white")
|
|
751
|
+
table.add_column("Installs", style="green")
|
|
752
|
+
|
|
753
|
+
for toolkit in data['popular_toolkits']:
|
|
754
|
+
table.add_row(
|
|
755
|
+
toolkit['name'],
|
|
756
|
+
toolkit['display_name'],
|
|
757
|
+
toolkit['category'],
|
|
758
|
+
str(toolkit['installs'])
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
console.print(table)
|
|
762
|
+
except Exception as e:
|
|
763
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
764
|
+
|
|
765
|
+
# Media commands
|
|
766
|
+
@cli.command()
|
|
767
|
+
@click.option('--page', default=1, help='Page number')
|
|
768
|
+
@click.option('--page-size', default=12, help='Items per page')
|
|
769
|
+
@click.option('--type', 'media_type', help='Media type (image, video, audio, document)')
|
|
770
|
+
@click.option('--search', help='Search query')
|
|
771
|
+
def media(page, page_size, media_type, search):
|
|
772
|
+
"""List user media"""
|
|
773
|
+
check_auth()
|
|
774
|
+
try:
|
|
775
|
+
with console.status("[bold green]Fetching media..."):
|
|
776
|
+
data = api.list_media(page=page, page_size=page_size, media_type=media_type, search=search)
|
|
777
|
+
|
|
778
|
+
table = Table(title="Media Files")
|
|
779
|
+
table.add_column("Type", style="cyan")
|
|
780
|
+
table.add_column("File Name", style="magenta")
|
|
781
|
+
table.add_column("Created", style="white")
|
|
782
|
+
table.add_column("URL", style="green")
|
|
783
|
+
|
|
784
|
+
for item in data['results']:
|
|
785
|
+
media_type_display = "Document" if item['media_is_doc'] else "Image" if item['media_is_img'] else "Video" if item['media_is_vid'] else "Audio" if item['media_is_aud'] else "Other"
|
|
786
|
+
filename = item['extra_data'].get('original_name', 'Unknown')
|
|
787
|
+
table.add_row(
|
|
788
|
+
media_type_display,
|
|
789
|
+
filename,
|
|
790
|
+
item['created'][:19],
|
|
791
|
+
item['media_url'][:50] + "..."
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
console.print(table)
|
|
795
|
+
console.print(f"\nPage {data['next']} of {len(data['results'])} items")
|
|
796
|
+
except Exception as e:
|
|
797
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
798
|
+
|
|
799
|
+
# Pricing command
|
|
800
|
+
@cli.command()
|
|
801
|
+
def pricing():
|
|
802
|
+
"""Show pricing information"""
|
|
803
|
+
check_auth()
|
|
804
|
+
try:
|
|
805
|
+
with console.status("[bold green]Fetching pricing..."):
|
|
806
|
+
data = api.get_pricing()
|
|
807
|
+
|
|
808
|
+
for plan in data:
|
|
809
|
+
console.print(Panel.fit(
|
|
810
|
+
f"[bold blue]Name:[/bold blue] {plan['name']}\n"
|
|
811
|
+
f"[bold blue]Description:[/bold blue] {plan['description']}\n"
|
|
812
|
+
f"[bold blue]Features:[/bold blue]\n" +
|
|
813
|
+
"\n".join(f" • {feature}" for feature in plan['features']),
|
|
814
|
+
title=f"Plan: {plan['name']}"
|
|
815
|
+
))
|
|
816
|
+
|
|
817
|
+
if plan.get('prices'):
|
|
818
|
+
console.print("[bold]Pricing:[/bold]")
|
|
819
|
+
for price in plan['prices']:
|
|
820
|
+
interval = price['interval']
|
|
821
|
+
amount = price['amount']
|
|
822
|
+
currency = price['currency'].upper()
|
|
823
|
+
console.print(f" {interval.capitalize()}: ${amount} {currency}")
|
|
824
|
+
|
|
825
|
+
console.print() # Spacing between plans
|
|
826
|
+
except Exception as e:
|
|
827
|
+
console.print(f"[red]Error: {e}[/red]")
|
|
828
|
+
|
|
829
|
+
if __name__ == '__main__':
|
|
830
|
+
cli()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chatatp-cli
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: ChatATP CLI - Terminal Interface for ChatATP API
|
|
5
|
+
Author-email: ChatATP Team <support@chatatp.com>
|
|
6
|
+
Project-URL: Homepage, https://chat-atp.com
|
|
7
|
+
Project-URL: Documentation, https://docs.chat-atp.com
|
|
8
|
+
Project-URL: Repository, https://github.com/sam-14uel/ChatATP-Python-Cli
|
|
9
|
+
Project-URL: Issues, https://github.com/sam-14uel/ChatATP-Python-Cli/issues
|
|
10
|
+
Requires-Dist: requests
|
|
11
|
+
Requires-Dist: click
|
|
12
|
+
Requires-Dist: rich
|
|
13
|
+
Requires-Dist: pyyaml
|
|
14
|
+
Requires-Dist: python-dotenv
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
chatatp_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
chatatp_cli/api_client.py,sha256=VF6zeUWFmHKySvSTbOBwjJg4jsGQPdgH8H0K7IfY-us,6758
|
|
3
|
+
chatatp_cli/config.py,sha256=MKOBTJNTCAGBkiten_CwVIdnevggYqn4_5UfdAPUDpU,2129
|
|
4
|
+
chatatp_cli/main.py,sha256=pv9j8n40HbrA7rORVESxI4Dygft_RwL13imA-FkFcqM,32707
|
|
5
|
+
chatatp_cli-1.0.0.dist-info/METADATA,sha256=l-iFwsombzRka25jYDq0EEoQS2mXr7bBScogbOt83OU,539
|
|
6
|
+
chatatp_cli-1.0.0.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
7
|
+
chatatp_cli-1.0.0.dist-info/entry_points.txt,sha256=YdQ_m7TbOrgPga1fnM4LbMm_4R5HZOm81_y44bnGSZ0,49
|
|
8
|
+
chatatp_cli-1.0.0.dist-info/top_level.txt,sha256=zs71L0-w-kyZboyFlu5ah49qJgT-H-wB_T9Ae0kTWoQ,12
|
|
9
|
+
chatatp_cli-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
chatatp_cli
|