commandchat 0.0.13__tar.gz → 0.0.14__tar.gz
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.
- {commandchat-0.0.13 → commandchat-0.0.14}/PKG-INFO +1 -1
- {commandchat-0.0.13 → commandchat-0.0.14}/commandchat.egg-info/PKG-INFO +1 -1
- {commandchat-0.0.13 → commandchat-0.0.14}/commandchat.egg-info/SOURCES.txt +9 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/occ/CommandChat.py +12 -1
- commandchat-0.0.14/occ/command/__main__.py +43 -0
- commandchat-0.0.14/occ/command/commands/__init__.py +2 -0
- commandchat-0.0.13/occ/command/__main__.py → commandchat-0.0.14/occ/command/commands/chat.py +36 -68
- commandchat-0.0.14/occ/command/commands/image.py +30 -0
- commandchat-0.0.14/occ/command/commands/profile.py +112 -0
- commandchat-0.0.14/occ/command/commands/prompt.py +226 -0
- commandchat-0.0.14/occ/command/interactive/__init__.py +2 -0
- commandchat-0.0.14/occ/command/interactive/profile_menu.py +91 -0
- commandchat-0.0.14/occ/command/interactive/prompt_menu.py +176 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/occ/commons/config.py +13 -0
- commandchat-0.0.14/occ/commons/prompts.py +146 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/occ/configuration/profile_config.py +34 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/pyproject.toml +1 -1
- {commandchat-0.0.13 → commandchat-0.0.14}/LICENSE +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/README.md +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/commandchat.egg-info/dependency_links.txt +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/commandchat.egg-info/entry_points.txt +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/commandchat.egg-info/requires.txt +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/commandchat.egg-info/top_level.txt +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/occ/ConvertLogToMarkDown.py +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/occ/__init__.py +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/occ/command/__init__.py +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/occ/commons/__init__.py +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/occ/configuration/__init__.py +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/occ/utils/CommonUtil.py +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/occ/utils/__init__.py +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/occ/utils/logger.py +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/setup.cfg +0 -0
- {commandchat-0.0.13 → commandchat-0.0.14}/setup.py +0 -0
|
@@ -13,8 +13,17 @@ occ/ConvertLogToMarkDown.py
|
|
|
13
13
|
occ/__init__.py
|
|
14
14
|
occ/command/__init__.py
|
|
15
15
|
occ/command/__main__.py
|
|
16
|
+
occ/command/commands/__init__.py
|
|
17
|
+
occ/command/commands/chat.py
|
|
18
|
+
occ/command/commands/image.py
|
|
19
|
+
occ/command/commands/profile.py
|
|
20
|
+
occ/command/commands/prompt.py
|
|
21
|
+
occ/command/interactive/__init__.py
|
|
22
|
+
occ/command/interactive/profile_menu.py
|
|
23
|
+
occ/command/interactive/prompt_menu.py
|
|
16
24
|
occ/commons/__init__.py
|
|
17
25
|
occ/commons/config.py
|
|
26
|
+
occ/commons/prompts.py
|
|
18
27
|
occ/configuration/__init__.py
|
|
19
28
|
occ/configuration/profile_config.py
|
|
20
29
|
occ/utils/CommonUtil.py
|
|
@@ -58,7 +58,7 @@ class CommandChat:
|
|
|
58
58
|
partial_text = []
|
|
59
59
|
role = None
|
|
60
60
|
|
|
61
|
-
def __init__(self, profile=None, chat_log_id=None, model=None):
|
|
61
|
+
def __init__(self, profile=None, chat_log_id=None, model=None, system_message=None):
|
|
62
62
|
now = time.strftime("%Y%m%d", time.localtime())
|
|
63
63
|
self.profile = profile or DEFAULT_PROFILE
|
|
64
64
|
self.api_server_type = get_env(self.profile, "api_server_type")
|
|
@@ -92,6 +92,17 @@ class CommandChat:
|
|
|
92
92
|
except json.JSONDecodeError:
|
|
93
93
|
continue
|
|
94
94
|
|
|
95
|
+
# Add system message if provided
|
|
96
|
+
if system_message:
|
|
97
|
+
# Check if there's already a system message at the beginning
|
|
98
|
+
has_system = len(self.messages) > 0 and self.messages[0].get('role') == 'system'
|
|
99
|
+
if has_system:
|
|
100
|
+
# Replace existing system message
|
|
101
|
+
self.messages[0] = {"role": "system", "content": system_message}
|
|
102
|
+
else:
|
|
103
|
+
# Insert system message at the beginning
|
|
104
|
+
self.messages.insert(0, {"role": "system", "content": system_message})
|
|
105
|
+
|
|
95
106
|
# Initialize client based on API server type
|
|
96
107
|
if self.api_server_type == "azure-openai":
|
|
97
108
|
# For Azure OpenAI, we'll initialize client per model in chat method
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""
|
|
2
|
+
OpenAI CommandChat - Command Line Interface
|
|
3
|
+
|
|
4
|
+
Main entry point for the occ CLI tool.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import absolute_import
|
|
8
|
+
|
|
9
|
+
import importlib.metadata
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
# Import command modules
|
|
13
|
+
from occ.command.commands.prompt import prompt
|
|
14
|
+
from occ.command.commands.profile import configure
|
|
15
|
+
from occ.command.commands.chat import chat
|
|
16
|
+
from occ.command.commands.image import image
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
VERSION = importlib.metadata.version("commandchat")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@click.group()
|
|
23
|
+
@click.version_option(version=VERSION, prog_name='openai-commandchat')
|
|
24
|
+
def cli():
|
|
25
|
+
"""OpenAI CommandChat - AI-powered command-line chat tool"""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# Register commands
|
|
30
|
+
cli.add_command(configure)
|
|
31
|
+
cli.add_command(chat)
|
|
32
|
+
cli.add_command(prompt)
|
|
33
|
+
cli.add_command(image)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def main():
|
|
37
|
+
"""Main entry point"""
|
|
38
|
+
cli()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
if __name__ == '__main__':
|
|
42
|
+
main()
|
|
43
|
+
|
commandchat-0.0.13/occ/command/__main__.py → commandchat-0.0.14/occ/command/commands/chat.py
RENAMED
|
@@ -1,50 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
"""Chat command"""
|
|
2
2
|
|
|
3
|
-
import importlib.metadata
|
|
4
3
|
import sys
|
|
5
|
-
|
|
6
4
|
import click
|
|
7
5
|
from prompt_toolkit import PromptSession, print_formatted_text, HTML
|
|
8
6
|
from prompt_toolkit.cursor_shapes import ModalCursorShapeConfig
|
|
9
7
|
from prompt_toolkit.styles import style_from_pygments_cls
|
|
10
|
-
from pygments.lexers.markup import MarkdownLexer
|
|
11
8
|
from pygments.styles.tango import TangoStyle
|
|
12
9
|
|
|
13
10
|
import occ.utils.logger as logger
|
|
14
11
|
from occ.CommandChat import CommandChat
|
|
15
12
|
|
|
16
13
|
|
|
17
|
-
VERSION = importlib.metadata.version("commandchat")
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@click.group()
|
|
21
|
-
@click.version_option(version=VERSION, prog_name='openai-commandchat')
|
|
22
|
-
def commandchat_operator():
|
|
23
|
-
pass
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@click.command()
|
|
27
|
-
@click.option('--profile', '-p', help='Specify profile name to configure')
|
|
28
|
-
def configure(profile):
|
|
29
|
-
"""Configure OpenAI or Azure OpenAI profiles"""
|
|
30
|
-
if profile is not None:
|
|
31
|
-
# If profile is specified, configure it directly
|
|
32
|
-
from occ.configuration.profile_config import configure_profile
|
|
33
|
-
configure_profile(profile)
|
|
34
|
-
else:
|
|
35
|
-
# No profile specified, show interactive selection
|
|
36
|
-
from occ.configuration.profile_config import select_profile_interactively
|
|
37
|
-
select_profile_interactively()
|
|
38
|
-
|
|
39
|
-
|
|
40
14
|
@click.command()
|
|
41
15
|
@click.argument('message', required=False)
|
|
42
|
-
@click.option('-id', help='
|
|
16
|
+
@click.option('-id', help='Enter chat id, something like context')
|
|
43
17
|
@click.option('--profile', '-p', envvar="OCC_PROFILE", help='Enable profile name')
|
|
44
18
|
@click.option("--model", "-m", envvar="OCC_MODEL", default="o1-mini",
|
|
45
19
|
help="Specify the model to use for this chat session")
|
|
46
|
-
@click.option('--file', '-f', type=click.Path(exists=True), help='
|
|
47
|
-
|
|
20
|
+
@click.option('--file', '-f', type=click.Path(exists=True), help='The prompt or message is from a file')
|
|
21
|
+
@click.option('--prompt', '-pt', help='Use a predefined prompt template (e.g., translate, improve). Use "occ prompt list" to see available prompts.')
|
|
22
|
+
def chat(message, id, profile, model, file, prompt):
|
|
23
|
+
"""Start a chat session with the AI"""
|
|
48
24
|
try:
|
|
49
25
|
import questionary
|
|
50
26
|
from occ.commons import config as cfg
|
|
@@ -104,6 +80,31 @@ def chat(message, id, profile, model, file):
|
|
|
104
80
|
logger.log_r(f"Model '{model}' not found in profile '{active_profile}'. Available models: {', '.join(available_models)}")
|
|
105
81
|
return
|
|
106
82
|
|
|
83
|
+
# Handle prompt template - command line -pt has highest priority
|
|
84
|
+
system_message = None
|
|
85
|
+
prompt_source = None # Track where the prompt came from
|
|
86
|
+
|
|
87
|
+
if prompt:
|
|
88
|
+
# Command line -pt parameter has highest priority
|
|
89
|
+
from occ.commons.prompts import get_prompt_system_message, list_prompts
|
|
90
|
+
system_message = get_prompt_system_message(prompt)
|
|
91
|
+
if not system_message:
|
|
92
|
+
logger.log_r(f"Prompt '{prompt}' not found. Available prompts:")
|
|
93
|
+
for key, value in list_prompts().items():
|
|
94
|
+
logger.log_g(f" - {key}: {value['description']}")
|
|
95
|
+
return
|
|
96
|
+
prompt_source = "command line"
|
|
97
|
+
logger.log_g(f"Using prompt template: {prompt} (from {prompt_source})")
|
|
98
|
+
else:
|
|
99
|
+
# Check for profile default prompt
|
|
100
|
+
profile_default_prompt = cfg.get_profile_default_prompt(active_profile)
|
|
101
|
+
if profile_default_prompt:
|
|
102
|
+
from occ.commons.prompts import get_prompt_system_message
|
|
103
|
+
system_message = get_prompt_system_message(profile_default_prompt)
|
|
104
|
+
if system_message:
|
|
105
|
+
prompt_source = f"profile '{active_profile}'"
|
|
106
|
+
logger.log_g(f"Using default prompt: {profile_default_prompt} (from {prompt_source})")
|
|
107
|
+
|
|
107
108
|
if file:
|
|
108
109
|
with open(file, 'r') as f:
|
|
109
110
|
message = f.read()
|
|
@@ -124,52 +125,19 @@ def chat(message, id, profile, model, file):
|
|
|
124
125
|
print_formatted_text(HTML(
|
|
125
126
|
"<b>Help Info: \n</b><ansigreen>Type your message and press ESC+Enter or OPT+Enter to send.\n"
|
|
126
127
|
"Use /exit or /quit or /q to leave the chat.\n"
|
|
127
|
-
"Use /help to show this message again
|
|
128
|
+
"Use /help to show this message again.\n"
|
|
129
|
+
"Use -pt <prompt_key> when starting chat to use a prompt template.\n"
|
|
130
|
+
"Run 'occ prompt list' to see available prompt templates.</ansigreen>\n"))
|
|
128
131
|
continue
|
|
129
132
|
if message.lower() in {"/exit", "/quit", "/q"}:
|
|
130
133
|
print_formatted_text(HTML("<ansired>Bye 👋</ansired>"))
|
|
131
134
|
exit(0)
|
|
132
|
-
CommandChat(profile=active_profile, chat_log_id=id).chat(message, model)
|
|
135
|
+
CommandChat(profile=active_profile, chat_log_id=id, system_message=system_message).chat(message, model)
|
|
133
136
|
print()
|
|
134
137
|
except KeyboardInterrupt:
|
|
135
138
|
print_formatted_text(HTML("<ansired>\n(Interrupted)</ansired>"))
|
|
136
139
|
exit(0)
|
|
137
|
-
CommandChat(profile=active_profile, chat_log_id=id).chat(message, model)
|
|
140
|
+
CommandChat(profile=active_profile, chat_log_id=id, system_message=system_message).chat(message, model)
|
|
138
141
|
except Exception as e:
|
|
139
142
|
logger.log_g(str(e))
|
|
140
143
|
|
|
141
|
-
|
|
142
|
-
size_map = {
|
|
143
|
-
"s": "256x256",
|
|
144
|
-
"S": "256x256",
|
|
145
|
-
"m": "512x512",
|
|
146
|
-
"M": "512x512",
|
|
147
|
-
"l": "1024x1024",
|
|
148
|
-
"L": "1024x1024"
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
@click.command()
|
|
153
|
-
@click.option('-desc', help=' Enter the description of the images you want')
|
|
154
|
-
@click.option('-size',
|
|
155
|
-
help=' Enter the size(S/s,M/m,L/l): \n small - 256x256 \n middle - 512x512 \n large - 1024x1024')
|
|
156
|
-
@click.option('-num', count=True, help=' Enter the number to generate the specified number of images')
|
|
157
|
-
@click.option('-profile', help='Enable profile name')
|
|
158
|
-
def image(desc, size, num, profile):
|
|
159
|
-
number = num if num > 0 else 1
|
|
160
|
-
size = size_map.get(size)
|
|
161
|
-
size_value = size if size is not None else "512x512"
|
|
162
|
-
CommandChat(profile=profile).image_create(desc, size_value, number if number < 5 else 4)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
commandchat_operator.add_command(configure)
|
|
166
|
-
commandchat_operator.add_command(chat)
|
|
167
|
-
commandchat_operator.add_command(image)
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
def main():
|
|
171
|
-
commandchat_operator()
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if __name__ == '__main__':
|
|
175
|
-
main()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""Image generation command"""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
from occ.CommandChat import CommandChat
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# Size mapping for convenience
|
|
8
|
+
SIZE_MAP = {
|
|
9
|
+
"s": "256x256",
|
|
10
|
+
"S": "256x256",
|
|
11
|
+
"m": "512x512",
|
|
12
|
+
"M": "512x512",
|
|
13
|
+
"l": "1024x1024",
|
|
14
|
+
"L": "1024x1024"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@click.command()
|
|
19
|
+
@click.option('-desc', help='Enter the description of the images you want')
|
|
20
|
+
@click.option('-size',
|
|
21
|
+
help='Enter the size(S/s,M/m,L/l): \n small - 256x256 \n middle - 512x512 \n large - 1024x1024')
|
|
22
|
+
@click.option('-num', count=True, help='Enter the number to generate the specified number of images')
|
|
23
|
+
@click.option('-profile', help='Enable profile name')
|
|
24
|
+
def image(desc, size, num, profile):
|
|
25
|
+
"""Generate images using AI"""
|
|
26
|
+
number = num if num > 0 else 1
|
|
27
|
+
size = SIZE_MAP.get(size)
|
|
28
|
+
size_value = size if size is not None else "512x512"
|
|
29
|
+
CommandChat(profile=profile).image_create(desc, size_value, number if number < 5 else 4)
|
|
30
|
+
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""Profile configuration commands"""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import occ.utils.logger as logger
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.group(invoke_without_command=True)
|
|
8
|
+
@click.pass_context
|
|
9
|
+
def configure(ctx):
|
|
10
|
+
"""Configure OpenAI or Azure OpenAI profiles"""
|
|
11
|
+
if ctx.invoked_subcommand is None:
|
|
12
|
+
# No subcommand provided, show interactive menu
|
|
13
|
+
from occ.command.interactive.profile_menu import interactive_profile_menu
|
|
14
|
+
interactive_profile_menu()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@click.command(name='profile')
|
|
18
|
+
@click.option('--profile', '-p', help='Specify profile name to configure')
|
|
19
|
+
def profile_cmd(profile):
|
|
20
|
+
"""Configure a profile"""
|
|
21
|
+
if profile is not None:
|
|
22
|
+
# If profile is specified, configure it directly
|
|
23
|
+
from occ.configuration.profile_config import configure_profile
|
|
24
|
+
configure_profile(profile)
|
|
25
|
+
else:
|
|
26
|
+
# No profile specified, show interactive selection
|
|
27
|
+
from occ.configuration.profile_config import select_profile_interactively
|
|
28
|
+
select_profile_interactively()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@click.command(name='delete')
|
|
32
|
+
@click.argument('profile', required=False)
|
|
33
|
+
@click.option('--yes', '-y', is_flag=True, help='Skip confirmation')
|
|
34
|
+
def delete_cmd(profile, yes):
|
|
35
|
+
"""Delete a profile"""
|
|
36
|
+
import questionary
|
|
37
|
+
from occ.commons import config as cfg
|
|
38
|
+
|
|
39
|
+
# If profile not provided, show interactive selection
|
|
40
|
+
if not profile:
|
|
41
|
+
profiles = [p for p in cfg.get_profiles() if p != 'default']
|
|
42
|
+
|
|
43
|
+
if not profiles:
|
|
44
|
+
logger.log_r("No profiles to delete (cannot delete 'default' profile)")
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
profile = questionary.select(
|
|
48
|
+
"Select a profile to delete:",
|
|
49
|
+
choices=profiles
|
|
50
|
+
).ask()
|
|
51
|
+
|
|
52
|
+
if not profile:
|
|
53
|
+
logger.log_g("Cancelled")
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
if not cfg.profile_exists(profile):
|
|
57
|
+
logger.log_r(f"Profile '{profile}' does not exist")
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
if profile == 'default':
|
|
61
|
+
logger.log_r("Cannot delete the 'default' profile")
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
# Confirm deletion
|
|
65
|
+
if not yes:
|
|
66
|
+
confirm = questionary.confirm(
|
|
67
|
+
f"Are you sure you want to delete profile '{profile}'? This will also remove all associated models.",
|
|
68
|
+
default=False
|
|
69
|
+
).ask()
|
|
70
|
+
|
|
71
|
+
if not confirm:
|
|
72
|
+
logger.log_g("Deletion cancelled")
|
|
73
|
+
return
|
|
74
|
+
|
|
75
|
+
cfg.remove_profile(profile)
|
|
76
|
+
logger.log_g(f"Profile '{profile}' deleted successfully")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@click.command(name='list')
|
|
80
|
+
def list_cmd():
|
|
81
|
+
"""List all configured profiles"""
|
|
82
|
+
from occ.commons import config as cfg
|
|
83
|
+
from rich.table import Table
|
|
84
|
+
from rich.console import Console
|
|
85
|
+
|
|
86
|
+
profiles = cfg.get_profiles()
|
|
87
|
+
if not profiles:
|
|
88
|
+
logger.log_r("No profiles found. Run 'occ configure profile' to create one.")
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
console = Console()
|
|
92
|
+
table = Table(title="Configured Profiles", show_header=True, header_style="bold magenta")
|
|
93
|
+
table.add_column("Profile", style="cyan", width=20)
|
|
94
|
+
table.add_column("API Type", style="green", width=15)
|
|
95
|
+
table.add_column("Default Prompt", style="yellow", width=20)
|
|
96
|
+
table.add_column("Models", style="blue")
|
|
97
|
+
|
|
98
|
+
for profile in profiles:
|
|
99
|
+
api_type = cfg.get_env(profile, 'api_server_type') or 'N/A'
|
|
100
|
+
default_prompt = cfg.get_profile_default_prompt(profile) or 'None'
|
|
101
|
+
models = cfg.get_profile_models(profile)
|
|
102
|
+
models_str = ', '.join(models) if models else 'N/A'
|
|
103
|
+
table.add_row(profile, api_type, default_prompt, models_str)
|
|
104
|
+
|
|
105
|
+
console.print(table)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
# Add subcommands to configure group
|
|
109
|
+
configure.add_command(profile_cmd)
|
|
110
|
+
configure.add_command(delete_cmd)
|
|
111
|
+
configure.add_command(list_cmd)
|
|
112
|
+
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
"""Prompt management commands"""
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import occ.utils.logger as logger
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@click.group(invoke_without_command=True)
|
|
8
|
+
@click.pass_context
|
|
9
|
+
def prompt(ctx):
|
|
10
|
+
"""Manage prompt templates"""
|
|
11
|
+
if ctx.invoked_subcommand is None:
|
|
12
|
+
# No subcommand provided, show interactive menu
|
|
13
|
+
from occ.command.interactive.prompt_menu import interactive_prompt_menu
|
|
14
|
+
interactive_prompt_menu()
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@click.command(name='list')
|
|
18
|
+
def list_cmd():
|
|
19
|
+
"""List all available prompt templates"""
|
|
20
|
+
from occ.commons.prompts import list_prompts
|
|
21
|
+
from rich.table import Table
|
|
22
|
+
from rich.console import Console
|
|
23
|
+
|
|
24
|
+
console = Console()
|
|
25
|
+
table = Table(title="Available Prompt Templates", show_header=True, header_style="bold magenta")
|
|
26
|
+
table.add_column("Key", style="cyan", width=15)
|
|
27
|
+
table.add_column("Name", style="green", width=25)
|
|
28
|
+
table.add_column("Description", style="yellow", width=35)
|
|
29
|
+
table.add_column("Type", style="blue", width=10)
|
|
30
|
+
|
|
31
|
+
for key, value in list_prompts().items():
|
|
32
|
+
prompt_type = "Built-in" if value.get("builtin", False) else "Custom"
|
|
33
|
+
table.add_row(key, value["name"], value["description"], prompt_type)
|
|
34
|
+
|
|
35
|
+
console.print(table)
|
|
36
|
+
console.print("\n[bold green]Usage:[/bold green] occ chat -pt <key> \"your message\"")
|
|
37
|
+
console.print("[bold green]Example:[/bold green] occ chat -pt translate \"你好世界\"")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@click.command(name='add')
|
|
41
|
+
@click.argument('key')
|
|
42
|
+
@click.option('--name', '-n', required=True, help='Display name for the prompt')
|
|
43
|
+
@click.option('--description', '-d', required=True, help='Brief description of the prompt')
|
|
44
|
+
@click.option('--system-prompt', '-s', required=True, help='The system prompt content')
|
|
45
|
+
def add_cmd(key, name, description, system_prompt):
|
|
46
|
+
"""Add a new custom prompt template"""
|
|
47
|
+
from occ.commons.prompts import add_prompt
|
|
48
|
+
|
|
49
|
+
success, message = add_prompt(key, name, description, system_prompt)
|
|
50
|
+
if success:
|
|
51
|
+
logger.log_g(message)
|
|
52
|
+
else:
|
|
53
|
+
logger.log_r(message)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@click.command(name='modify')
|
|
57
|
+
@click.argument('key', required=False)
|
|
58
|
+
@click.option('--name', '-n', help='New display name')
|
|
59
|
+
@click.option('--description', '-d', help='New description')
|
|
60
|
+
@click.option('--system-prompt', '-s', help='New system prompt content')
|
|
61
|
+
def modify_cmd(key, name, description, system_prompt):
|
|
62
|
+
"""Modify an existing custom prompt template"""
|
|
63
|
+
from occ.commons.prompts import modify_prompt, list_prompts, get_prompt
|
|
64
|
+
import questionary
|
|
65
|
+
|
|
66
|
+
# If key not provided, show interactive selection
|
|
67
|
+
if not key:
|
|
68
|
+
user_prompts = {k: v for k, v in list_prompts().items() if not v.get('builtin', False)}
|
|
69
|
+
|
|
70
|
+
if not user_prompts:
|
|
71
|
+
logger.log_r("No custom prompts to modify. Built-in prompts cannot be modified.")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
key = questionary.select(
|
|
75
|
+
"Select a prompt to modify:",
|
|
76
|
+
choices=list(user_prompts.keys())
|
|
77
|
+
).ask()
|
|
78
|
+
|
|
79
|
+
if not key:
|
|
80
|
+
logger.log_g("Cancelled")
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
# If no options provided, ask interactively
|
|
84
|
+
if not any([name, description, system_prompt]):
|
|
85
|
+
current_prompt = get_prompt(key)
|
|
86
|
+
|
|
87
|
+
fields = questionary.checkbox(
|
|
88
|
+
"What would you like to modify?",
|
|
89
|
+
choices=['Name', 'Description', 'System Prompt']
|
|
90
|
+
).ask()
|
|
91
|
+
|
|
92
|
+
if not fields:
|
|
93
|
+
logger.log_g("Cancelled")
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
if 'Name' in fields:
|
|
97
|
+
name = questionary.text(
|
|
98
|
+
"Enter new name:",
|
|
99
|
+
default=current_prompt['name']
|
|
100
|
+
).ask()
|
|
101
|
+
|
|
102
|
+
if 'Description' in fields:
|
|
103
|
+
description = questionary.text(
|
|
104
|
+
"Enter new description:",
|
|
105
|
+
default=current_prompt['description']
|
|
106
|
+
).ask()
|
|
107
|
+
|
|
108
|
+
if 'System Prompt' in fields:
|
|
109
|
+
system_prompt = questionary.text(
|
|
110
|
+
"Enter new system prompt:",
|
|
111
|
+
default=current_prompt['system_prompt'],
|
|
112
|
+
multiline=True
|
|
113
|
+
).ask()
|
|
114
|
+
|
|
115
|
+
if not any([name, description, system_prompt]):
|
|
116
|
+
logger.log_r("At least one of --name, --description, or --system-prompt must be provided")
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
success, message = modify_prompt(key, name, description, system_prompt)
|
|
120
|
+
if success:
|
|
121
|
+
logger.log_g(message)
|
|
122
|
+
else:
|
|
123
|
+
logger.log_r(message)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@click.command(name='remove')
|
|
127
|
+
@click.argument('key', required=False)
|
|
128
|
+
@click.option('--yes', '-y', is_flag=True, help='Skip confirmation')
|
|
129
|
+
def remove_cmd(key, yes):
|
|
130
|
+
"""Remove a custom prompt template"""
|
|
131
|
+
from occ.commons.prompts import remove_prompt, get_prompt, list_prompts
|
|
132
|
+
import questionary
|
|
133
|
+
|
|
134
|
+
# If key not provided, show interactive selection
|
|
135
|
+
if not key:
|
|
136
|
+
user_prompts = {k: v for k, v in list_prompts().items() if not v.get('builtin', False)}
|
|
137
|
+
|
|
138
|
+
if not user_prompts:
|
|
139
|
+
logger.log_r("No custom prompts to remove. Built-in prompts cannot be removed.")
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
key = questionary.select(
|
|
143
|
+
"Select a prompt to remove:",
|
|
144
|
+
choices=list(user_prompts.keys())
|
|
145
|
+
).ask()
|
|
146
|
+
|
|
147
|
+
if not key:
|
|
148
|
+
logger.log_g("Cancelled")
|
|
149
|
+
return
|
|
150
|
+
|
|
151
|
+
# Check if prompt exists
|
|
152
|
+
prompt = get_prompt(key)
|
|
153
|
+
if not prompt:
|
|
154
|
+
logger.log_r(f"Prompt '{key}' not found")
|
|
155
|
+
return
|
|
156
|
+
|
|
157
|
+
if prompt.get("builtin", False):
|
|
158
|
+
logger.log_r(f"Cannot remove built-in prompt '{key}'")
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
# Confirm removal
|
|
162
|
+
if not yes:
|
|
163
|
+
confirm = questionary.confirm(
|
|
164
|
+
f"Are you sure you want to remove prompt '{key}'?",
|
|
165
|
+
default=False
|
|
166
|
+
).ask()
|
|
167
|
+
|
|
168
|
+
if not confirm:
|
|
169
|
+
logger.log_g("Removal cancelled")
|
|
170
|
+
return
|
|
171
|
+
|
|
172
|
+
success, message = remove_prompt(key)
|
|
173
|
+
if success:
|
|
174
|
+
logger.log_g(message)
|
|
175
|
+
else:
|
|
176
|
+
logger.log_r(message)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@click.command(name='show')
|
|
180
|
+
@click.argument('key', required=False)
|
|
181
|
+
def show_cmd(key):
|
|
182
|
+
"""Show detailed information about a prompt template"""
|
|
183
|
+
from occ.commons.prompts import get_prompt, list_prompts
|
|
184
|
+
from rich.console import Console
|
|
185
|
+
from rich.panel import Panel
|
|
186
|
+
import questionary
|
|
187
|
+
|
|
188
|
+
# If key not provided, show interactive selection
|
|
189
|
+
if not key:
|
|
190
|
+
prompts_dict = list_prompts()
|
|
191
|
+
if not prompts_dict:
|
|
192
|
+
logger.log_r("No prompts available")
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
key = questionary.select(
|
|
196
|
+
"Select a prompt to view:",
|
|
197
|
+
choices=list(prompts_dict.keys())
|
|
198
|
+
).ask()
|
|
199
|
+
|
|
200
|
+
if not key:
|
|
201
|
+
logger.log_g("Cancelled")
|
|
202
|
+
return
|
|
203
|
+
|
|
204
|
+
prompt = get_prompt(key)
|
|
205
|
+
if not prompt:
|
|
206
|
+
logger.log_r(f"Prompt '{key}' not found")
|
|
207
|
+
return
|
|
208
|
+
|
|
209
|
+
console = Console()
|
|
210
|
+
prompt_type = "Built-in" if prompt.get("builtin", False) else "Custom"
|
|
211
|
+
|
|
212
|
+
console.print(f"\n[bold cyan]Key:[/bold cyan] {key}")
|
|
213
|
+
console.print(f"[bold cyan]Name:[/bold cyan] {prompt['name']}")
|
|
214
|
+
console.print(f"[bold cyan]Type:[/bold cyan] {prompt_type}")
|
|
215
|
+
console.print(f"[bold cyan]Description:[/bold cyan] {prompt['description']}")
|
|
216
|
+
console.print(f"\n[bold cyan]System Prompt:[/bold cyan]")
|
|
217
|
+
console.print(Panel(prompt['system_prompt'], border_style="green"))
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
# Add subcommands to prompt group
|
|
221
|
+
prompt.add_command(list_cmd)
|
|
222
|
+
prompt.add_command(add_cmd)
|
|
223
|
+
prompt.add_command(modify_cmd)
|
|
224
|
+
prompt.add_command(remove_cmd)
|
|
225
|
+
prompt.add_command(show_cmd)
|
|
226
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""Interactive menu for profile configuration"""
|
|
2
|
+
|
|
3
|
+
import questionary
|
|
4
|
+
from questionary import Style
|
|
5
|
+
import occ.utils.logger as logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Custom style for questionary
|
|
9
|
+
CUSTOM_STYLE = Style([
|
|
10
|
+
('qmark', 'fg:#673ab7 bold'),
|
|
11
|
+
('question', 'bold'),
|
|
12
|
+
('answer', 'fg:#f44336 bold'),
|
|
13
|
+
('pointer', 'fg:#673ab7 bold'),
|
|
14
|
+
('highlighted', 'fg:#673ab7 bold'),
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def interactive_profile_menu():
|
|
19
|
+
"""Interactive menu for managing profiles"""
|
|
20
|
+
while True:
|
|
21
|
+
action = questionary.select(
|
|
22
|
+
"Profile Configuration - What would you like to do?",
|
|
23
|
+
choices=[
|
|
24
|
+
'List all profiles',
|
|
25
|
+
'Configure profile',
|
|
26
|
+
'Delete profile',
|
|
27
|
+
'Exit'
|
|
28
|
+
],
|
|
29
|
+
style=CUSTOM_STYLE
|
|
30
|
+
).ask()
|
|
31
|
+
|
|
32
|
+
if action is None or action == 'Exit':
|
|
33
|
+
break
|
|
34
|
+
elif action == 'List all profiles':
|
|
35
|
+
from occ.command.commands.profile import list_cmd
|
|
36
|
+
list_cmd.callback()
|
|
37
|
+
elif action == 'Configure profile':
|
|
38
|
+
interactive_configure_profile()
|
|
39
|
+
elif action == 'Delete profile':
|
|
40
|
+
interactive_delete_profile()
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def interactive_configure_profile():
|
|
44
|
+
"""Interactive profile configuration"""
|
|
45
|
+
from occ.commons import config as cfg
|
|
46
|
+
from occ.configuration.profile_config import configure_profile
|
|
47
|
+
|
|
48
|
+
profiles = cfg.get_profiles()
|
|
49
|
+
|
|
50
|
+
if profiles:
|
|
51
|
+
choices = profiles + ['[Create New Profile]']
|
|
52
|
+
selection = questionary.select(
|
|
53
|
+
"Select a profile to configure:",
|
|
54
|
+
choices=choices,
|
|
55
|
+
style=CUSTOM_STYLE
|
|
56
|
+
).ask()
|
|
57
|
+
|
|
58
|
+
if not selection:
|
|
59
|
+
return
|
|
60
|
+
|
|
61
|
+
if selection == '[Create New Profile]':
|
|
62
|
+
profile_name = questionary.text("Enter new profile name:", style=CUSTOM_STYLE).ask()
|
|
63
|
+
if profile_name:
|
|
64
|
+
configure_profile(profile_name)
|
|
65
|
+
else:
|
|
66
|
+
configure_profile(selection)
|
|
67
|
+
else:
|
|
68
|
+
logger.log_g("No profiles found. Creating default profile...")
|
|
69
|
+
configure_profile('default')
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def interactive_delete_profile():
|
|
73
|
+
"""Interactive profile deletion"""
|
|
74
|
+
from occ.commons import config as cfg
|
|
75
|
+
from occ.command.commands.profile import delete_cmd
|
|
76
|
+
|
|
77
|
+
profiles = [p for p in cfg.get_profiles() if p != 'default']
|
|
78
|
+
|
|
79
|
+
if not profiles:
|
|
80
|
+
logger.log_r("No profiles to delete (cannot delete 'default' profile)")
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
profile = questionary.select(
|
|
84
|
+
"Select a profile to delete:",
|
|
85
|
+
choices=profiles,
|
|
86
|
+
style=CUSTOM_STYLE
|
|
87
|
+
).ask()
|
|
88
|
+
|
|
89
|
+
if profile:
|
|
90
|
+
delete_cmd.callback(profile, False)
|
|
91
|
+
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Interactive menu for prompt management"""
|
|
2
|
+
|
|
3
|
+
import questionary
|
|
4
|
+
from questionary import Style
|
|
5
|
+
import occ.utils.logger as logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Custom style for questionary
|
|
9
|
+
CUSTOM_STYLE = Style([
|
|
10
|
+
('qmark', 'fg:#673ab7 bold'),
|
|
11
|
+
('question', 'bold'),
|
|
12
|
+
('answer', 'fg:#f44336 bold'),
|
|
13
|
+
('pointer', 'fg:#673ab7 bold'),
|
|
14
|
+
('highlighted', 'fg:#673ab7 bold'),
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def interactive_prompt_menu():
|
|
19
|
+
"""Interactive menu for managing prompts"""
|
|
20
|
+
while True:
|
|
21
|
+
action = questionary.select(
|
|
22
|
+
"Prompt Management - What would you like to do?",
|
|
23
|
+
choices=[
|
|
24
|
+
'List all prompts',
|
|
25
|
+
'Show prompt details',
|
|
26
|
+
'Add new prompt',
|
|
27
|
+
'Modify prompt',
|
|
28
|
+
'Remove prompt',
|
|
29
|
+
'Exit'
|
|
30
|
+
],
|
|
31
|
+
style=CUSTOM_STYLE
|
|
32
|
+
).ask()
|
|
33
|
+
|
|
34
|
+
if action is None or action == 'Exit':
|
|
35
|
+
break
|
|
36
|
+
elif action == 'List all prompts':
|
|
37
|
+
from occ.command.commands.prompt import list_cmd
|
|
38
|
+
list_cmd.callback()
|
|
39
|
+
elif action == 'Show prompt details':
|
|
40
|
+
interactive_show_prompt()
|
|
41
|
+
elif action == 'Add new prompt':
|
|
42
|
+
interactive_add_prompt()
|
|
43
|
+
elif action == 'Modify prompt':
|
|
44
|
+
interactive_modify_prompt()
|
|
45
|
+
elif action == 'Remove prompt':
|
|
46
|
+
interactive_remove_prompt()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def interactive_show_prompt():
|
|
50
|
+
"""Interactive prompt selection for showing details"""
|
|
51
|
+
from occ.commons.prompts import list_prompts
|
|
52
|
+
from occ.command.commands.prompt import show_cmd
|
|
53
|
+
|
|
54
|
+
prompts_dict = list_prompts()
|
|
55
|
+
if not prompts_dict:
|
|
56
|
+
logger.log_r("No prompts available")
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
prompt_key = questionary.select(
|
|
60
|
+
"Select a prompt to view:",
|
|
61
|
+
choices=list(prompts_dict.keys()),
|
|
62
|
+
style=CUSTOM_STYLE
|
|
63
|
+
).ask()
|
|
64
|
+
|
|
65
|
+
if prompt_key:
|
|
66
|
+
show_cmd.callback(prompt_key)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def interactive_add_prompt():
|
|
70
|
+
"""Interactive prompt addition"""
|
|
71
|
+
from occ.command.commands.prompt import add_cmd
|
|
72
|
+
|
|
73
|
+
key = questionary.text("Enter prompt key (e.g., my-prompt):", style=CUSTOM_STYLE).ask()
|
|
74
|
+
if not key:
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
name = questionary.text("Enter prompt name:", style=CUSTOM_STYLE).ask()
|
|
78
|
+
if not name:
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
description = questionary.text("Enter prompt description:", style=CUSTOM_STYLE).ask()
|
|
82
|
+
if not description:
|
|
83
|
+
return
|
|
84
|
+
|
|
85
|
+
system_prompt = questionary.text(
|
|
86
|
+
"Enter system prompt content:",
|
|
87
|
+
multiline=True,
|
|
88
|
+
style=CUSTOM_STYLE
|
|
89
|
+
).ask()
|
|
90
|
+
if not system_prompt:
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
add_cmd.callback(key, name, description, system_prompt)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def interactive_modify_prompt():
|
|
97
|
+
"""Interactive prompt modification"""
|
|
98
|
+
from occ.commons.prompts import list_prompts, get_prompt
|
|
99
|
+
from occ.command.commands.prompt import modify_cmd
|
|
100
|
+
|
|
101
|
+
user_prompts = {k: v for k, v in list_prompts().items() if not v.get('builtin', False)}
|
|
102
|
+
|
|
103
|
+
if not user_prompts:
|
|
104
|
+
logger.log_r("No custom prompts to modify. Built-in prompts cannot be modified.")
|
|
105
|
+
return
|
|
106
|
+
|
|
107
|
+
prompt_key = questionary.select(
|
|
108
|
+
"Select a prompt to modify:",
|
|
109
|
+
choices=list(user_prompts.keys()),
|
|
110
|
+
style=CUSTOM_STYLE
|
|
111
|
+
).ask()
|
|
112
|
+
|
|
113
|
+
if not prompt_key:
|
|
114
|
+
return
|
|
115
|
+
|
|
116
|
+
current_prompt = get_prompt(prompt_key)
|
|
117
|
+
|
|
118
|
+
# Ask what to modify
|
|
119
|
+
fields = questionary.checkbox(
|
|
120
|
+
"What would you like to modify?",
|
|
121
|
+
choices=['Name', 'Description', 'System Prompt'],
|
|
122
|
+
style=CUSTOM_STYLE
|
|
123
|
+
).ask()
|
|
124
|
+
|
|
125
|
+
if not fields:
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
name = None
|
|
129
|
+
description = None
|
|
130
|
+
system_prompt = None
|
|
131
|
+
|
|
132
|
+
if 'Name' in fields:
|
|
133
|
+
name = questionary.text(
|
|
134
|
+
"Enter new name:",
|
|
135
|
+
default=current_prompt['name'],
|
|
136
|
+
style=CUSTOM_STYLE
|
|
137
|
+
).ask()
|
|
138
|
+
|
|
139
|
+
if 'Description' in fields:
|
|
140
|
+
description = questionary.text(
|
|
141
|
+
"Enter new description:",
|
|
142
|
+
default=current_prompt['description'],
|
|
143
|
+
style=CUSTOM_STYLE
|
|
144
|
+
).ask()
|
|
145
|
+
|
|
146
|
+
if 'System Prompt' in fields:
|
|
147
|
+
system_prompt = questionary.text(
|
|
148
|
+
"Enter new system prompt:",
|
|
149
|
+
default=current_prompt['system_prompt'],
|
|
150
|
+
multiline=True,
|
|
151
|
+
style=CUSTOM_STYLE
|
|
152
|
+
).ask()
|
|
153
|
+
|
|
154
|
+
modify_cmd.callback(prompt_key, name, description, system_prompt)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def interactive_remove_prompt():
|
|
158
|
+
"""Interactive prompt removal"""
|
|
159
|
+
from occ.commons.prompts import list_prompts
|
|
160
|
+
from occ.command.commands.prompt import remove_cmd
|
|
161
|
+
|
|
162
|
+
user_prompts = {k: v for k, v in list_prompts().items() if not v.get('builtin', False)}
|
|
163
|
+
|
|
164
|
+
if not user_prompts:
|
|
165
|
+
logger.log_r("No custom prompts to remove. Built-in prompts cannot be removed.")
|
|
166
|
+
return
|
|
167
|
+
|
|
168
|
+
prompt_key = questionary.select(
|
|
169
|
+
"Select a prompt to remove:",
|
|
170
|
+
choices=list(user_prompts.keys()),
|
|
171
|
+
style=CUSTOM_STYLE
|
|
172
|
+
).ask()
|
|
173
|
+
|
|
174
|
+
if prompt_key:
|
|
175
|
+
remove_cmd.callback(prompt_key, False)
|
|
176
|
+
|
|
@@ -142,6 +142,19 @@ def profile_exists(profile):
|
|
|
142
142
|
return config.has_section(profile)
|
|
143
143
|
|
|
144
144
|
|
|
145
|
+
def get_profile_default_prompt(profile):
|
|
146
|
+
"""Get the default prompt for a profile"""
|
|
147
|
+
return get_env(profile, 'default_prompt')
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def set_profile_default_prompt(profile, prompt_key):
|
|
151
|
+
"""Set the default prompt for a profile"""
|
|
152
|
+
if not profile_exists(profile):
|
|
153
|
+
return False, f"Profile '{profile}' does not exist"
|
|
154
|
+
set_env(profile, 'default_prompt', prompt_key)
|
|
155
|
+
return True, f"Default prompt for profile '{profile}' set to '{prompt_key}'"
|
|
156
|
+
|
|
157
|
+
|
|
145
158
|
def log_config():
|
|
146
159
|
level_input = 'DEBUG'
|
|
147
160
|
if level_input == 'DEBUG':
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Built-in and custom prompt template management
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
|
|
9
|
+
# Built-in default prompts (read-only)
|
|
10
|
+
DEFAULT_PROMPTS = {
|
|
11
|
+
"translate": {
|
|
12
|
+
"name": "Translation Assistant",
|
|
13
|
+
"description": "Translate Chinese to English",
|
|
14
|
+
"system_prompt": "You are a professional translation assistant. Please translate the Chinese text provided by the user into idiomatic English. Only output the translation result without additional explanations.",
|
|
15
|
+
"builtin": True
|
|
16
|
+
},
|
|
17
|
+
"improve": {
|
|
18
|
+
"name": "English Writing Assistant",
|
|
19
|
+
"description": "Improve English sentences and identify issues",
|
|
20
|
+
"system_prompt": "You are a professional English writing assistant. Please help users improve their English sentences to make them more natural for native speakers. Format your output as follows:\n\n**Improved Sentence:**\n[The improved English sentence]\n\n**Issues in Original:**\n[List grammar, word choice, and expression issues]",
|
|
21
|
+
"builtin": True
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def get_home_path():
|
|
27
|
+
homedir = os.environ.get('HOME', None)
|
|
28
|
+
if os.name == 'nt':
|
|
29
|
+
homedir = os.path.expanduser('~')
|
|
30
|
+
return homedir
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_prompts_file():
|
|
34
|
+
"""Get the path to user prompts configuration file"""
|
|
35
|
+
homedir = get_home_path()
|
|
36
|
+
prompts_dir = os.path.join(homedir, ".occ")
|
|
37
|
+
os.makedirs(prompts_dir, exist_ok=True)
|
|
38
|
+
return os.path.join(prompts_dir, "prompts.json")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_user_prompts():
|
|
42
|
+
"""Load user-defined prompts from file"""
|
|
43
|
+
prompts_file = get_prompts_file()
|
|
44
|
+
if not os.path.exists(prompts_file):
|
|
45
|
+
return {}
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
with open(prompts_file, 'r', encoding='utf-8') as f:
|
|
49
|
+
return json.load(f)
|
|
50
|
+
except Exception as e:
|
|
51
|
+
logging.error(f"Error loading user prompts: {e}")
|
|
52
|
+
return {}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def save_user_prompts(prompts):
|
|
56
|
+
"""Save user-defined prompts to file"""
|
|
57
|
+
prompts_file = get_prompts_file()
|
|
58
|
+
try:
|
|
59
|
+
with open(prompts_file, 'w', encoding='utf-8') as f:
|
|
60
|
+
json.dump(prompts, f, indent=2, ensure_ascii=False)
|
|
61
|
+
return True
|
|
62
|
+
except Exception as e:
|
|
63
|
+
logging.error(f"Error saving user prompts: {e}")
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def get_prompt(prompt_key):
|
|
68
|
+
"""Get a specific prompt template (checks user prompts first, then built-in)"""
|
|
69
|
+
user_prompts = load_user_prompts()
|
|
70
|
+
if prompt_key in user_prompts:
|
|
71
|
+
return user_prompts[prompt_key]
|
|
72
|
+
return DEFAULT_PROMPTS.get(prompt_key)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def list_prompts():
|
|
76
|
+
"""List all available prompts (built-in + user-defined)"""
|
|
77
|
+
all_prompts = DEFAULT_PROMPTS.copy()
|
|
78
|
+
user_prompts = load_user_prompts()
|
|
79
|
+
all_prompts.update(user_prompts)
|
|
80
|
+
return all_prompts
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_prompt_system_message(prompt_key):
|
|
84
|
+
"""Get the system message for a prompt"""
|
|
85
|
+
prompt = get_prompt(prompt_key)
|
|
86
|
+
if prompt:
|
|
87
|
+
return prompt["system_prompt"]
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def add_prompt(key, name, description, system_prompt):
|
|
92
|
+
"""Add a new user-defined prompt"""
|
|
93
|
+
if key in DEFAULT_PROMPTS:
|
|
94
|
+
return False, f"Cannot override built-in prompt '{key}'"
|
|
95
|
+
|
|
96
|
+
user_prompts = load_user_prompts()
|
|
97
|
+
user_prompts[key] = {
|
|
98
|
+
"name": name,
|
|
99
|
+
"description": description,
|
|
100
|
+
"system_prompt": system_prompt,
|
|
101
|
+
"builtin": False
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if save_user_prompts(user_prompts):
|
|
105
|
+
return True, f"Prompt '{key}' added successfully"
|
|
106
|
+
return False, "Failed to save prompt"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def modify_prompt(key, name=None, description=None, system_prompt=None):
|
|
110
|
+
"""Modify an existing user-defined prompt"""
|
|
111
|
+
if key in DEFAULT_PROMPTS:
|
|
112
|
+
return False, f"Cannot modify built-in prompt '{key}'"
|
|
113
|
+
|
|
114
|
+
user_prompts = load_user_prompts()
|
|
115
|
+
if key not in user_prompts:
|
|
116
|
+
return False, f"Prompt '{key}' not found"
|
|
117
|
+
|
|
118
|
+
# Update only provided fields
|
|
119
|
+
if name is not None:
|
|
120
|
+
user_prompts[key]["name"] = name
|
|
121
|
+
if description is not None:
|
|
122
|
+
user_prompts[key]["description"] = description
|
|
123
|
+
if system_prompt is not None:
|
|
124
|
+
user_prompts[key]["system_prompt"] = system_prompt
|
|
125
|
+
|
|
126
|
+
if save_user_prompts(user_prompts):
|
|
127
|
+
return True, f"Prompt '{key}' modified successfully"
|
|
128
|
+
return False, "Failed to save prompt"
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def remove_prompt(key):
|
|
132
|
+
"""Remove a user-defined prompt"""
|
|
133
|
+
if key in DEFAULT_PROMPTS:
|
|
134
|
+
return False, f"Cannot remove built-in prompt '{key}'"
|
|
135
|
+
|
|
136
|
+
user_prompts = load_user_prompts()
|
|
137
|
+
if key not in user_prompts:
|
|
138
|
+
return False, f"Prompt '{key}' not found"
|
|
139
|
+
|
|
140
|
+
del user_prompts[key]
|
|
141
|
+
|
|
142
|
+
if save_user_prompts(user_prompts):
|
|
143
|
+
return True, f"Prompt '{key}' removed successfully"
|
|
144
|
+
return False, "Failed to save prompts"
|
|
145
|
+
|
|
146
|
+
|
|
@@ -102,6 +102,40 @@ def configure_common_settings(profile_name, is_existing):
|
|
|
102
102
|
|
|
103
103
|
if limit_history:
|
|
104
104
|
config.set_env(profile_name, 'limit_history', limit_history)
|
|
105
|
+
|
|
106
|
+
# Configure default prompt
|
|
107
|
+
existing_prompt = config.get_profile_default_prompt(profile_name) if is_existing else None
|
|
108
|
+
|
|
109
|
+
set_prompt = questionary.confirm(
|
|
110
|
+
"Set a default prompt template for this profile?",
|
|
111
|
+
default=False,
|
|
112
|
+
style=custom_style
|
|
113
|
+
).ask()
|
|
114
|
+
|
|
115
|
+
if set_prompt:
|
|
116
|
+
from occ.commons.prompts import list_prompts
|
|
117
|
+
available_prompts = list(list_prompts().keys())
|
|
118
|
+
if available_prompts:
|
|
119
|
+
default_prompt = questionary.select(
|
|
120
|
+
"Select default prompt:",
|
|
121
|
+
choices=available_prompts + ['[None]'],
|
|
122
|
+
default=existing_prompt if existing_prompt in available_prompts else '[None]',
|
|
123
|
+
style=custom_style
|
|
124
|
+
).ask()
|
|
125
|
+
|
|
126
|
+
if default_prompt and default_prompt != '[None]':
|
|
127
|
+
config.set_env(profile_name, 'default_prompt', default_prompt)
|
|
128
|
+
logger.log_g(f"✓ Default prompt set to '{default_prompt}'")
|
|
129
|
+
elif default_prompt == '[None]' and existing_prompt:
|
|
130
|
+
# Remove default prompt
|
|
131
|
+
config.config.remove_option(profile_name, 'default_prompt')
|
|
132
|
+
config.write_config()
|
|
133
|
+
logger.log_g("✓ Default prompt removed")
|
|
134
|
+
else:
|
|
135
|
+
logger.log_r("No prompts available. Use 'occ prompts add' to create custom prompts.")
|
|
136
|
+
elif existing_prompt:
|
|
137
|
+
# Show existing default prompt
|
|
138
|
+
logger.log_g(f"Current default prompt: {existing_prompt}")
|
|
105
139
|
|
|
106
140
|
|
|
107
141
|
def configure_openai(profile_name, is_existing):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|