tzamuncode 0.1.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.
- tzamuncode/__init__.py +10 -0
- tzamuncode/agents/__init__.py +1 -0
- tzamuncode/agents/coder.py +144 -0
- tzamuncode/agents/tools.py +159 -0
- tzamuncode/auth/__init__.py +1 -0
- tzamuncode/auth/auth_manager.py +159 -0
- tzamuncode/cli/__init__.py +1 -0
- tzamuncode/cli/agentic_commands.py +131 -0
- tzamuncode/cli/auth_commands.py +125 -0
- tzamuncode/cli/commands.py +203 -0
- tzamuncode/cli/enhanced_chat.py +312 -0
- tzamuncode/cli/interactive_chat.py +323 -0
- tzamuncode/cli/main.py +444 -0
- tzamuncode/cli/realtime_chat.py +965 -0
- tzamuncode/cli/realtime_chat_methods.py +200 -0
- tzamuncode/cli/tui_chat.py +323 -0
- tzamuncode/config/__init__.py +1 -0
- tzamuncode/models/__init__.py +1 -0
- tzamuncode/models/ollama.py +124 -0
- tzamuncode/models/vllm_client.py +121 -0
- tzamuncode/utils/__init__.py +1 -0
- tzamuncode/utils/file_ops.py +59 -0
- tzamuncode/utils/project_scanner.py +193 -0
- tzamuncode-0.1.0.dist-info/METADATA +200 -0
- tzamuncode-0.1.0.dist-info/RECORD +29 -0
- tzamuncode-0.1.0.dist-info/WHEEL +5 -0
- tzamuncode-0.1.0.dist-info/entry_points.txt +3 -0
- tzamuncode-0.1.0.dist-info/licenses/LICENSE +21 -0
- tzamuncode-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authentication commands for TzamunCode CLI
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.prompt import Prompt, Confirm
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
import getpass
|
|
10
|
+
|
|
11
|
+
from ..auth.auth_manager import AuthManager
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def login_command():
|
|
17
|
+
"""Interactive login command"""
|
|
18
|
+
auth = AuthManager()
|
|
19
|
+
|
|
20
|
+
# Check if already logged in
|
|
21
|
+
if auth.is_authenticated():
|
|
22
|
+
user_info = auth.get_user_info()
|
|
23
|
+
console.print(f"\n[yellow]Already logged in as:[/yellow] [cyan]{user_info['username']}[/cyan]")
|
|
24
|
+
|
|
25
|
+
if not Confirm.ask("\n[bold]Logout and login again?[/bold]"):
|
|
26
|
+
return
|
|
27
|
+
|
|
28
|
+
auth.logout()
|
|
29
|
+
console.print("[dim]Logged out[/dim]\n")
|
|
30
|
+
|
|
31
|
+
# Show login options
|
|
32
|
+
console.print(Panel.fit(
|
|
33
|
+
"""[bold cyan]TzamunCode Authentication[/bold cyan]
|
|
34
|
+
|
|
35
|
+
Choose login method:
|
|
36
|
+
[bold]1.[/bold] Username & Password (TzamunAI account)
|
|
37
|
+
[bold]2.[/bold] API Key (from TzamunAI TCHub)
|
|
38
|
+
""",
|
|
39
|
+
border_style="blue",
|
|
40
|
+
title="[bold blue]Login[/bold blue]"
|
|
41
|
+
))
|
|
42
|
+
|
|
43
|
+
choice = Prompt.ask("\n[bold]Select option[/bold]", choices=["1", "2"], default="2")
|
|
44
|
+
|
|
45
|
+
if choice == "1":
|
|
46
|
+
# Username/Password login
|
|
47
|
+
console.print("\n[bold blue]Login with TzamunAI Account[/bold blue]\n")
|
|
48
|
+
|
|
49
|
+
username = Prompt.ask("[bold]Username[/bold]")
|
|
50
|
+
password = getpass.getpass("Password: ")
|
|
51
|
+
|
|
52
|
+
console.print("\n[dim]Authenticating...[/dim]")
|
|
53
|
+
|
|
54
|
+
if auth.login_with_credentials(username, password):
|
|
55
|
+
console.print(f"\n[bold green]✓ Successfully logged in as {username}![/bold green]\n")
|
|
56
|
+
else:
|
|
57
|
+
console.print("\n[bold red]✗ Login failed. Please check your credentials.[/bold red]\n")
|
|
58
|
+
|
|
59
|
+
else:
|
|
60
|
+
# API Key login
|
|
61
|
+
console.print("\n[bold blue]Login with API Key[/bold blue]\n")
|
|
62
|
+
console.print("[dim]Get your API key from: https://app.tzamun.ai/admin (API Config tab)[/dim]\n")
|
|
63
|
+
|
|
64
|
+
api_key = Prompt.ask("[bold]API Key[/bold]", password=True)
|
|
65
|
+
|
|
66
|
+
console.print("\n[dim]Validating API key...[/dim]")
|
|
67
|
+
|
|
68
|
+
if auth.login_with_api_key(api_key):
|
|
69
|
+
console.print("\n[bold green]✓ Successfully authenticated with API key![/bold green]\n")
|
|
70
|
+
else:
|
|
71
|
+
console.print("\n[bold red]✗ Invalid API key. Please check and try again.[/bold red]\n")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def logout_command():
|
|
75
|
+
"""Logout command"""
|
|
76
|
+
auth = AuthManager()
|
|
77
|
+
|
|
78
|
+
if not auth.is_authenticated():
|
|
79
|
+
console.print("\n[yellow]Not logged in[/yellow]\n")
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
user_info = auth.get_user_info()
|
|
83
|
+
|
|
84
|
+
if Confirm.ask(f"\n[bold]Logout from {user_info['username']}?[/bold]"):
|
|
85
|
+
auth.logout()
|
|
86
|
+
console.print("\n[bold green]✓ Logged out successfully[/bold green]\n")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def whoami_command():
|
|
90
|
+
"""Show current user info"""
|
|
91
|
+
auth = AuthManager()
|
|
92
|
+
|
|
93
|
+
if not auth.is_authenticated():
|
|
94
|
+
console.print("\n[yellow]Not logged in[/yellow]")
|
|
95
|
+
console.print("[dim]Run 'tzamuncode login' to authenticate[/dim]\n")
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
user_info = auth.get_user_info()
|
|
99
|
+
|
|
100
|
+
table = Table(title="Current User", border_style="blue")
|
|
101
|
+
table.add_column("Property", style="cyan")
|
|
102
|
+
table.add_column("Value", style="yellow")
|
|
103
|
+
|
|
104
|
+
table.add_row("Status", "✓ Authenticated")
|
|
105
|
+
table.add_row("Type", user_info['type'].replace('_', ' ').title())
|
|
106
|
+
table.add_row("User", user_info['username'])
|
|
107
|
+
|
|
108
|
+
console.print()
|
|
109
|
+
console.print(table)
|
|
110
|
+
console.print()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def require_auth(func):
|
|
114
|
+
"""Decorator to require authentication before running a command"""
|
|
115
|
+
def wrapper(*args, **kwargs):
|
|
116
|
+
auth = AuthManager()
|
|
117
|
+
|
|
118
|
+
if not auth.is_authenticated():
|
|
119
|
+
console.print("\n[bold red]✗ Authentication required[/bold red]")
|
|
120
|
+
console.print("[dim]Run 'tzamuncode login' to authenticate[/dim]\n")
|
|
121
|
+
return
|
|
122
|
+
|
|
123
|
+
return func(*args, **kwargs)
|
|
124
|
+
|
|
125
|
+
return wrapper
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Command implementations for TzamunCode CLI
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.markdown import Markdown
|
|
8
|
+
from rich.prompt import Prompt, Confirm
|
|
9
|
+
from rich.syntax import Syntax
|
|
10
|
+
from rich.live import Live
|
|
11
|
+
from rich.spinner import Spinner
|
|
12
|
+
from typing import Optional
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
from ..models.ollama import OllamaClient
|
|
16
|
+
from ..utils.file_ops import FileManager
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
def chat_command(model: str, system: Optional[str] = None):
|
|
21
|
+
"""Interactive chat with AI"""
|
|
22
|
+
client = OllamaClient(model=model)
|
|
23
|
+
|
|
24
|
+
console.print(Panel.fit(
|
|
25
|
+
f"[bold blue]TzamunCode Chat[/bold blue]\n\n"
|
|
26
|
+
f"Model: [cyan]{model}[/cyan]\n"
|
|
27
|
+
f"Type [bold]'exit'[/bold] or [bold]'quit'[/bold] to end the session",
|
|
28
|
+
border_style="blue"
|
|
29
|
+
))
|
|
30
|
+
|
|
31
|
+
conversation_history = []
|
|
32
|
+
|
|
33
|
+
if system:
|
|
34
|
+
conversation_history.append({"role": "system", "content": system})
|
|
35
|
+
|
|
36
|
+
while True:
|
|
37
|
+
try:
|
|
38
|
+
user_input = Prompt.ask("\n[bold green]You[/bold green]")
|
|
39
|
+
|
|
40
|
+
if user_input.lower() in ['exit', 'quit', 'q']:
|
|
41
|
+
console.print("[dim]Goodbye! 👋[/dim]")
|
|
42
|
+
break
|
|
43
|
+
|
|
44
|
+
conversation_history.append({"role": "user", "content": user_input})
|
|
45
|
+
|
|
46
|
+
console.print("\n[bold blue]TzamunCode[/bold blue]:", end=" ")
|
|
47
|
+
|
|
48
|
+
# Stream response
|
|
49
|
+
response = ""
|
|
50
|
+
for chunk in client.chat_stream(conversation_history):
|
|
51
|
+
response += chunk
|
|
52
|
+
console.print(chunk, end="")
|
|
53
|
+
|
|
54
|
+
console.print() # New line after response
|
|
55
|
+
|
|
56
|
+
conversation_history.append({"role": "assistant", "content": response})
|
|
57
|
+
|
|
58
|
+
except KeyboardInterrupt:
|
|
59
|
+
console.print("\n[dim]Interrupted. Goodbye! 👋[/dim]")
|
|
60
|
+
break
|
|
61
|
+
except Exception as e:
|
|
62
|
+
console.print(f"\n[bold red]Error:[/bold red] {e}")
|
|
63
|
+
|
|
64
|
+
def generate_command(prompt: str, model: str, output: Optional[str] = None):
|
|
65
|
+
"""Generate code based on prompt"""
|
|
66
|
+
client = OllamaClient(model=model)
|
|
67
|
+
file_manager = FileManager()
|
|
68
|
+
|
|
69
|
+
console.print(f"\n[bold blue]Generating:[/bold blue] {prompt}")
|
|
70
|
+
console.print(f"[dim]Model: {model}[/dim]\n")
|
|
71
|
+
|
|
72
|
+
# Create generation prompt
|
|
73
|
+
full_prompt = f"""Generate code for: {prompt}
|
|
74
|
+
|
|
75
|
+
Requirements:
|
|
76
|
+
- Write clean, well-documented code
|
|
77
|
+
- Include error handling
|
|
78
|
+
- Follow best practices
|
|
79
|
+
- Add comments explaining complex logic
|
|
80
|
+
|
|
81
|
+
Generate the complete, working code:"""
|
|
82
|
+
|
|
83
|
+
with console.status("[bold blue]Generating code...", spinner="dots"):
|
|
84
|
+
response = client.generate(full_prompt)
|
|
85
|
+
|
|
86
|
+
# Extract code from response
|
|
87
|
+
code = response.strip()
|
|
88
|
+
|
|
89
|
+
# Display generated code
|
|
90
|
+
console.print("\n[bold green]Generated Code:[/bold green]\n")
|
|
91
|
+
|
|
92
|
+
# Try to detect language for syntax highlighting
|
|
93
|
+
language = "python" # Default
|
|
94
|
+
if output:
|
|
95
|
+
if output.endswith('.js'):
|
|
96
|
+
language = "javascript"
|
|
97
|
+
elif output.endswith('.go'):
|
|
98
|
+
language = "go"
|
|
99
|
+
elif output.endswith('.rs'):
|
|
100
|
+
language = "rust"
|
|
101
|
+
|
|
102
|
+
syntax = Syntax(code, language, theme="monokai", line_numbers=True)
|
|
103
|
+
console.print(syntax)
|
|
104
|
+
|
|
105
|
+
# Save to file if output specified
|
|
106
|
+
if output:
|
|
107
|
+
if Confirm.ask(f"\n[bold]Save to {output}?[/bold]"):
|
|
108
|
+
file_manager.write_file(output, code)
|
|
109
|
+
console.print(f"[bold green]✓[/bold green] Saved to {output}")
|
|
110
|
+
else:
|
|
111
|
+
if Confirm.ask("\n[bold]Save to file?[/bold]"):
|
|
112
|
+
filename = Prompt.ask("Filename")
|
|
113
|
+
file_manager.write_file(filename, code)
|
|
114
|
+
console.print(f"[bold green]✓[/bold green] Saved to {filename}")
|
|
115
|
+
|
|
116
|
+
def edit_command(file_path: str, instruction: str, model: str, apply: bool = False):
|
|
117
|
+
"""Edit a file using AI"""
|
|
118
|
+
client = OllamaClient(model=model)
|
|
119
|
+
file_manager = FileManager()
|
|
120
|
+
|
|
121
|
+
# Read current file
|
|
122
|
+
try:
|
|
123
|
+
current_content = file_manager.read_file(file_path)
|
|
124
|
+
except FileNotFoundError:
|
|
125
|
+
console.print(f"[bold red]Error:[/bold red] File not found: {file_path}")
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
console.print(f"\n[bold blue]Editing:[/bold blue] {file_path}")
|
|
129
|
+
console.print(f"[bold blue]Instruction:[/bold blue] {instruction}")
|
|
130
|
+
console.print(f"[dim]Model: {model}[/dim]\n")
|
|
131
|
+
|
|
132
|
+
# Create edit prompt
|
|
133
|
+
edit_prompt = f"""You are editing the file: {file_path}
|
|
134
|
+
|
|
135
|
+
Current content:
|
|
136
|
+
```
|
|
137
|
+
{current_content}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Instruction: {instruction}
|
|
141
|
+
|
|
142
|
+
Provide the complete modified file content. Only output the code, no explanations:"""
|
|
143
|
+
|
|
144
|
+
with console.status("[bold blue]Generating changes...", spinner="dots"):
|
|
145
|
+
new_content = client.generate(edit_prompt)
|
|
146
|
+
|
|
147
|
+
# Show diff
|
|
148
|
+
diff = file_manager.show_diff(file_path, new_content)
|
|
149
|
+
|
|
150
|
+
if diff:
|
|
151
|
+
console.print("\n[bold yellow]Changes:[/bold yellow]\n")
|
|
152
|
+
console.print(diff)
|
|
153
|
+
|
|
154
|
+
# Apply changes
|
|
155
|
+
if apply or Confirm.ask("\n[bold]Apply these changes?[/bold]"):
|
|
156
|
+
file_manager.write_file(file_path, new_content)
|
|
157
|
+
console.print(f"[bold green]✓[/bold green] Changes applied to {file_path}")
|
|
158
|
+
else:
|
|
159
|
+
console.print("[dim]Changes not applied[/dim]")
|
|
160
|
+
else:
|
|
161
|
+
console.print("[dim]No changes needed[/dim]")
|
|
162
|
+
|
|
163
|
+
def explain_command(file_path: str, model: str, detailed: bool = False):
|
|
164
|
+
"""Explain code in a file"""
|
|
165
|
+
client = OllamaClient(model=model)
|
|
166
|
+
file_manager = FileManager()
|
|
167
|
+
|
|
168
|
+
# Read file
|
|
169
|
+
try:
|
|
170
|
+
content = file_manager.read_file(file_path)
|
|
171
|
+
except FileNotFoundError:
|
|
172
|
+
console.print(f"[bold red]Error:[/bold red] File not found: {file_path}")
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
console.print(f"\n[bold blue]Explaining:[/bold blue] {file_path}")
|
|
176
|
+
console.print(f"[dim]Model: {model}[/dim]\n")
|
|
177
|
+
|
|
178
|
+
# Create explanation prompt
|
|
179
|
+
detail_level = "detailed" if detailed else "concise"
|
|
180
|
+
explain_prompt = f"""Explain this code in a {detail_level} way:
|
|
181
|
+
|
|
182
|
+
File: {file_path}
|
|
183
|
+
```
|
|
184
|
+
{content}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
Provide a clear explanation covering:
|
|
188
|
+
- What the code does
|
|
189
|
+
- Key functions/classes
|
|
190
|
+
- Important logic
|
|
191
|
+
- Potential improvements (if detailed)
|
|
192
|
+
|
|
193
|
+
Explanation:"""
|
|
194
|
+
|
|
195
|
+
with console.status("[bold blue]Analyzing code...", spinner="dots"):
|
|
196
|
+
explanation = client.generate(explain_prompt)
|
|
197
|
+
|
|
198
|
+
# Display explanation
|
|
199
|
+
console.print(Panel(
|
|
200
|
+
Markdown(explanation),
|
|
201
|
+
title=f"[bold blue]Explanation: {file_path}[/bold blue]",
|
|
202
|
+
border_style="blue"
|
|
203
|
+
))
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Enhanced interactive chat with beautiful UI and slash commands
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
from rich.table import Table
|
|
8
|
+
from rich.prompt import Prompt
|
|
9
|
+
from rich.markdown import Markdown
|
|
10
|
+
from rich.layout import Layout
|
|
11
|
+
from rich.live import Live
|
|
12
|
+
from rich.text import Text
|
|
13
|
+
from typing import Optional, List, Dict
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
from ..models.ollama import OllamaClient
|
|
17
|
+
|
|
18
|
+
console = Console()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class EnhancedChat:
|
|
22
|
+
"""Enhanced chat interface with Claude Code-like UI"""
|
|
23
|
+
|
|
24
|
+
ASCII_LOGO = """
|
|
25
|
+
╔╦╗╔═╗╔═╗╔╦╗╦ ╦╔╗╔╔═╗╔═╗╔╦╗╔═╗
|
|
26
|
+
║ ╔═╝╠═╣║║║║ ║║║║║ ║ ║ ║║╣
|
|
27
|
+
╩ ╚═╝╩ ╩╩ ╩╚═╝╝╚╝╚═╝╚═╝═╩╝╚═╝
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
SLASH_COMMANDS = {
|
|
31
|
+
'/help': 'Show available commands',
|
|
32
|
+
'/models': 'List and switch models',
|
|
33
|
+
'/settings': 'Show settings',
|
|
34
|
+
'/clear': 'Clear conversation history',
|
|
35
|
+
'/save': 'Save conversation',
|
|
36
|
+
'/load': 'Load conversation',
|
|
37
|
+
'/exit': 'Exit chat',
|
|
38
|
+
'/quit': 'Exit chat',
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
def __init__(self, model: str = "qwen2.5:32b", system: Optional[str] = None):
|
|
42
|
+
self.model = model
|
|
43
|
+
self.system = system
|
|
44
|
+
self.client = OllamaClient(model=model)
|
|
45
|
+
self.conversation_history = []
|
|
46
|
+
self.available_models = []
|
|
47
|
+
self.settings = {
|
|
48
|
+
'temperature': 0.7,
|
|
49
|
+
'streaming': True,
|
|
50
|
+
'show_thinking': False,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
# Load available models
|
|
54
|
+
try:
|
|
55
|
+
self.available_models = self.client.list_models()
|
|
56
|
+
except:
|
|
57
|
+
self.available_models = [model]
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def show_command_menu(self):
|
|
61
|
+
"""Show command menu when user types '/'"""
|
|
62
|
+
console.print()
|
|
63
|
+
|
|
64
|
+
# Professional command menu
|
|
65
|
+
table = Table(
|
|
66
|
+
title="[bold cyan]Available Commands[/bold cyan]",
|
|
67
|
+
border_style="blue",
|
|
68
|
+
show_header=True,
|
|
69
|
+
header_style="bold white on blue",
|
|
70
|
+
title_style="bold cyan"
|
|
71
|
+
)
|
|
72
|
+
table.add_column("Command", style="bold cyan", no_wrap=True, width=20)
|
|
73
|
+
table.add_column("Description", style="white")
|
|
74
|
+
|
|
75
|
+
for cmd, desc in self.SLASH_COMMANDS.items():
|
|
76
|
+
table.add_row(cmd, desc)
|
|
77
|
+
|
|
78
|
+
console.print(table)
|
|
79
|
+
console.print()
|
|
80
|
+
|
|
81
|
+
def show_help(self):
|
|
82
|
+
"""Show help with all commands"""
|
|
83
|
+
self.show_command_menu()
|
|
84
|
+
|
|
85
|
+
def show_models(self) -> Optional[str]:
|
|
86
|
+
"""Show model selector and return selected model"""
|
|
87
|
+
console.print()
|
|
88
|
+
|
|
89
|
+
# Professional model selection table
|
|
90
|
+
table = Table(
|
|
91
|
+
title="[bold cyan]Available Models[/bold cyan]",
|
|
92
|
+
border_style="blue",
|
|
93
|
+
show_header=True,
|
|
94
|
+
header_style="bold white on blue"
|
|
95
|
+
)
|
|
96
|
+
table.add_column("#", style="dim", width=4)
|
|
97
|
+
table.add_column("Model Name", style="cyan")
|
|
98
|
+
table.add_column("Status", style="green", width=12)
|
|
99
|
+
|
|
100
|
+
for idx, model_name in enumerate(self.available_models, 1):
|
|
101
|
+
status = "✓ Active" if model_name == self.model else ""
|
|
102
|
+
table.add_row(str(idx), model_name, status)
|
|
103
|
+
|
|
104
|
+
console.print(table)
|
|
105
|
+
console.print()
|
|
106
|
+
|
|
107
|
+
choice = Prompt.ask(
|
|
108
|
+
"[bold blue]Select model number[/bold blue] [dim](or Enter to cancel)[/dim]",
|
|
109
|
+
default=""
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
if choice and choice.isdigit():
|
|
113
|
+
idx = int(choice) - 1
|
|
114
|
+
if 0 <= idx < len(self.available_models):
|
|
115
|
+
new_model = self.available_models[idx]
|
|
116
|
+
self.model = new_model
|
|
117
|
+
self.client = OllamaClient(model=new_model)
|
|
118
|
+
console.print(f"\n[bold green]✓[/bold green] Switched to [cyan]{new_model}[/cyan]\n")
|
|
119
|
+
return new_model
|
|
120
|
+
|
|
121
|
+
console.print("[dim]Cancelled[/dim]\n")
|
|
122
|
+
return None
|
|
123
|
+
|
|
124
|
+
def show_settings(self):
|
|
125
|
+
"""Show current settings"""
|
|
126
|
+
from .. import __version__
|
|
127
|
+
|
|
128
|
+
console.print()
|
|
129
|
+
|
|
130
|
+
# Professional settings panel
|
|
131
|
+
table = Table(
|
|
132
|
+
title="[bold cyan]Current Settings[/bold cyan]",
|
|
133
|
+
border_style="blue",
|
|
134
|
+
show_header=True,
|
|
135
|
+
header_style="bold white on blue"
|
|
136
|
+
)
|
|
137
|
+
table.add_column("Setting", style="bold cyan", width=20)
|
|
138
|
+
table.add_column("Value", style="yellow")
|
|
139
|
+
|
|
140
|
+
table.add_row("Version", __version__)
|
|
141
|
+
table.add_row("Company", "Tzamun Arabia IT Co. 🇸🇦")
|
|
142
|
+
table.add_row("Model", self.model)
|
|
143
|
+
table.add_row("Available Models", str(len(self.available_models)))
|
|
144
|
+
table.add_row("Temperature", str(self.settings['temperature']))
|
|
145
|
+
table.add_row("Streaming", "✓ Enabled" if self.settings['streaming'] else "✗ Disabled")
|
|
146
|
+
|
|
147
|
+
console.print(table)
|
|
148
|
+
console.print()
|
|
149
|
+
|
|
150
|
+
def handle_slash_command(self, command: str) -> bool:
|
|
151
|
+
"""
|
|
152
|
+
Handle slash commands
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
True if should continue chat, False if should exit
|
|
156
|
+
"""
|
|
157
|
+
command = command.lower().strip()
|
|
158
|
+
|
|
159
|
+
if command in ['/exit', '/quit']:
|
|
160
|
+
return False
|
|
161
|
+
|
|
162
|
+
elif command == '/help':
|
|
163
|
+
self.show_help()
|
|
164
|
+
|
|
165
|
+
elif command == '/models':
|
|
166
|
+
self.show_models()
|
|
167
|
+
|
|
168
|
+
elif command == '/settings':
|
|
169
|
+
self.show_settings()
|
|
170
|
+
|
|
171
|
+
elif command == '/clear':
|
|
172
|
+
self.conversation_history = []
|
|
173
|
+
self.show_header()
|
|
174
|
+
console.print("[bold green]✓[/bold green] Conversation cleared\n")
|
|
175
|
+
|
|
176
|
+
elif command == '/save':
|
|
177
|
+
console.print("[dim]Save feature coming soon...[/dim]\n")
|
|
178
|
+
|
|
179
|
+
elif command == '/load':
|
|
180
|
+
console.print("[dim]Load feature coming soon...[/dim]\n")
|
|
181
|
+
|
|
182
|
+
else:
|
|
183
|
+
console.print(f"[bold red]Unknown command:[/bold red] {command}")
|
|
184
|
+
console.print("[dim]Type '/help' to see available commands[/dim]\n")
|
|
185
|
+
|
|
186
|
+
return True
|
|
187
|
+
|
|
188
|
+
def format_user_message(self, message: str):
|
|
189
|
+
"""Format user message"""
|
|
190
|
+
console.print(f"\n[bold green]You[/bold green] [dim]›[/dim] {message}")
|
|
191
|
+
|
|
192
|
+
def format_ai_response(self, response: str):
|
|
193
|
+
"""Format AI response"""
|
|
194
|
+
console.print(f"\n[bold blue]TzamunCode[/bold blue] [dim]›[/dim]")
|
|
195
|
+
|
|
196
|
+
# Try to render as markdown if it looks like code
|
|
197
|
+
if '```' in response or response.strip().startswith('#'):
|
|
198
|
+
console.print(Markdown(response))
|
|
199
|
+
else:
|
|
200
|
+
console.print(response)
|
|
201
|
+
|
|
202
|
+
console.print()
|
|
203
|
+
|
|
204
|
+
def run(self):
|
|
205
|
+
"""Run the enhanced chat interface"""
|
|
206
|
+
# Professional header with complete information
|
|
207
|
+
from .. import __version__
|
|
208
|
+
|
|
209
|
+
console.print()
|
|
210
|
+
console.print("─" * console.width, style="dim")
|
|
211
|
+
console.print()
|
|
212
|
+
|
|
213
|
+
# ASCII Logo
|
|
214
|
+
console.print("[bold cyan]╔╦╗╔═╗╔═╗╔╦╗╦ ╦╔╗╔╔═╗╔═╗╔╦╗╔═╗[/bold cyan]")
|
|
215
|
+
console.print("[bold cyan] ║ ╔═╝╠═╣║║║║ ║║║║║ ║ ║ ║║╣[/bold cyan]")
|
|
216
|
+
console.print("[bold cyan] ╩ ╚═╝╩ ╩╩ ╩╚═╝╝╚╝╚═╝╚═╝═╩╝╚═╝[/bold cyan]")
|
|
217
|
+
|
|
218
|
+
console.print()
|
|
219
|
+
|
|
220
|
+
# Professional info panel
|
|
221
|
+
info_panel = Panel.fit(
|
|
222
|
+
f"""[bold]AI Coding Assistant[/bold]
|
|
223
|
+
|
|
224
|
+
[cyan]Version:[/cyan] {__version__}
|
|
225
|
+
[cyan]Company:[/cyan] Tzamun Arabia IT Co. 🇸🇦
|
|
226
|
+
[cyan]Model:[/cyan] {self.model}
|
|
227
|
+
[cyan]Models Available:[/cyan] {len(self.available_models)}
|
|
228
|
+
|
|
229
|
+
[dim]Type '/help' for commands • Type '?' for shortcuts[/dim]""",
|
|
230
|
+
border_style="blue",
|
|
231
|
+
padding=(0, 2)
|
|
232
|
+
)
|
|
233
|
+
console.print(info_panel)
|
|
234
|
+
console.print()
|
|
235
|
+
console.print("─" * console.width, style="dim")
|
|
236
|
+
console.print()
|
|
237
|
+
|
|
238
|
+
# Add system message if provided
|
|
239
|
+
if self.system:
|
|
240
|
+
self.conversation_history.append({"role": "system", "content": self.system})
|
|
241
|
+
|
|
242
|
+
while True:
|
|
243
|
+
try:
|
|
244
|
+
# Simple prompt
|
|
245
|
+
console.print()
|
|
246
|
+
user_input = Prompt.ask("[dim]›[/dim]", default="")
|
|
247
|
+
|
|
248
|
+
if not user_input or not user_input.strip():
|
|
249
|
+
continue
|
|
250
|
+
|
|
251
|
+
# Check for exit commands
|
|
252
|
+
if user_input.lower() in ['exit', 'quit', 'q']:
|
|
253
|
+
console.print("\n[dim]Goodbye! 👋[/dim]\n")
|
|
254
|
+
break
|
|
255
|
+
|
|
256
|
+
# Check if user just typed '/' - show command menu
|
|
257
|
+
if user_input.strip() == '/':
|
|
258
|
+
self.show_command_menu()
|
|
259
|
+
continue
|
|
260
|
+
|
|
261
|
+
# Check for slash commands
|
|
262
|
+
if user_input.startswith('/'):
|
|
263
|
+
should_continue = self.handle_slash_command(user_input)
|
|
264
|
+
if not should_continue:
|
|
265
|
+
console.print("\n[dim]Goodbye! 👋[/dim]\n")
|
|
266
|
+
break
|
|
267
|
+
continue
|
|
268
|
+
|
|
269
|
+
# Format and show user message
|
|
270
|
+
self.format_user_message(user_input)
|
|
271
|
+
|
|
272
|
+
# Add to conversation history
|
|
273
|
+
self.conversation_history.append({"role": "user", "content": user_input})
|
|
274
|
+
|
|
275
|
+
# Get AI response
|
|
276
|
+
if self.settings['streaming']:
|
|
277
|
+
# Stream response
|
|
278
|
+
console.print(f"\n[bold blue]TzamunCode[/bold blue] [dim]›[/dim] ", end="")
|
|
279
|
+
|
|
280
|
+
response = ""
|
|
281
|
+
for chunk in self.client.chat_stream(self.conversation_history):
|
|
282
|
+
response += chunk
|
|
283
|
+
console.print(chunk, end="")
|
|
284
|
+
|
|
285
|
+
console.print("\n")
|
|
286
|
+
else:
|
|
287
|
+
# Non-streaming response
|
|
288
|
+
with console.status("[bold blue]Thinking...", spinner="dots"):
|
|
289
|
+
response = self.client.chat(self.conversation_history)
|
|
290
|
+
|
|
291
|
+
self.format_ai_response(response)
|
|
292
|
+
|
|
293
|
+
# Add to conversation history
|
|
294
|
+
self.conversation_history.append({"role": "assistant", "content": response})
|
|
295
|
+
|
|
296
|
+
except KeyboardInterrupt:
|
|
297
|
+
console.print("\n\n[dim]Interrupted. Type 'exit' to quit or continue chatting.[/dim]\n")
|
|
298
|
+
continue
|
|
299
|
+
|
|
300
|
+
except EOFError:
|
|
301
|
+
console.print("\n[dim]Goodbye! 👋[/dim]\n")
|
|
302
|
+
break
|
|
303
|
+
|
|
304
|
+
except Exception as e:
|
|
305
|
+
console.print(f"\n[bold red]Error:[/bold red] {e}\n")
|
|
306
|
+
continue
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def enhanced_chat_command(model: str, system: Optional[str] = None):
|
|
310
|
+
"""Run enhanced chat interface"""
|
|
311
|
+
chat = EnhancedChat(model=model, system=system)
|
|
312
|
+
chat.run()
|