mini-code-cli 0.0.5__tar.gz → 0.0.7__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mini-code-cli
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: Minimal Coding CLI
5
5
  Author-email: Nikolai Rozanov <nikolai.rozanov@gmail.com>
6
6
  License: The MIT License (MIT)
@@ -0,0 +1,158 @@
1
+ import os
2
+ import json
3
+
4
+ from . import prompts
5
+ from . import tools
6
+ from . import utils
7
+
8
+ import argparse
9
+ import sys
10
+
11
+ # ANSI escape codes for colors and bold
12
+ BOLD = '\033[1m'
13
+ RED = '\033[91m'
14
+ GREEN = '\033[92m'
15
+ YELLOW = '\033[93m'
16
+ BLUE = '\033[94m'
17
+ MAGENTA = '\033[95m'
18
+ CYAN = '\033[96m'
19
+ RESET = '\033[0m'
20
+
21
+ def save_history(messages):
22
+ # Save messages to history.json
23
+ with open("history.json", "w") as f:
24
+ json.dump(messages, f, indent=2)
25
+
26
+
27
+ def parse_args():
28
+ parser = argparse.ArgumentParser(description="Agent loop for interacting with a language model")
29
+ parser.add_argument("--url", type=str, default="http://0.0.0.0:30000", help="API host")
30
+ parser.add_argument("--system_prompt", type=str, help="API host")
31
+ parser.add_argument("--api-key", type=str, default=None, help="API key for authentication")
32
+ parser.add_argument("--allowed-dir", type=str, default=".", help="Allowed directory for file operations")
33
+ parser.add_argument("--max-tokens", type=int, help="Maximum tokens for LLM response")
34
+ parser.add_argument("--temperature", type=float, default=0.0, help="Temperature for LLM")
35
+ parser.add_argument("--model", type=str, default="default", help="Model name")
36
+ args = parser.parse_args()
37
+ return args
38
+
39
+ def print_welcome(allowed_dir, server_url):
40
+ # Calculate the length of the longest line inside the box
41
+ lines = [
42
+ " Coding Agent Loop Started",
43
+ " Type 'quit' or 'exit' to exit the loop.",
44
+ f" Allowed dir: {allowed_dir}",
45
+ f" Server URL: {server_url}"
46
+ ]
47
+ max_len = max(len(line) for line in lines)
48
+ margin = 2 # Space on each side inside the box
49
+ inner_width = max_len + 2 * margin
50
+ # Build the box
51
+ top_bottom = "╔" + "═" * inner_width + "╗"
52
+ print(top_bottom)
53
+ for line in lines:
54
+ # Pad the line to inner_width characters
55
+ padded = line.ljust(inner_width)
56
+ print(f"║{padded}║")
57
+ bottom = "╚" + "═" * inner_width + "╝"
58
+ print(bottom)
59
+
60
+ def main():
61
+ args = parse_args()
62
+
63
+ allowed_dir = os.path.abspath(args.allowed_dir)
64
+ server_url = f"{args.url}"
65
+
66
+ tools_dict = tools.util_get_tools_dict()
67
+ llm_tools_dict = tools.util_get_tools()
68
+
69
+ if args.system_prompt:
70
+ system_prompt = args.system_prompt
71
+ else:
72
+ system_prompt = prompts.system_prompt(tools_dict=tools_dict, allowed_dir=allowed_dir)
73
+
74
+ messages = [{"role": "system", "content": system_prompt}]
75
+
76
+ print_welcome(allowed_dir, server_url)
77
+
78
+ while True:
79
+ llm_response_flag = False
80
+ llm_tool_response_flag = False
81
+ try:
82
+ print("-"*30)
83
+ user_input = input(f"{BOLD}{CYAN}You:{RESET} ")
84
+ if user_input.lower() in ["quit", "exit"]:
85
+ print("\nThank you. Goodbye! Saving history...")
86
+ save_history(messages)
87
+ break
88
+ except KeyboardInterrupt:
89
+ print("\nThank you. Goodbye! Saving history...")
90
+ save_history(messages)
91
+ break
92
+
93
+ messages.append({"role": "user", "content": user_input})
94
+
95
+ response_text, tool_calls = utils.call_openai_server(
96
+ messages,
97
+ max_tokens=args.max_tokens,
98
+ temperature=args.temperature,
99
+ model=args.model,
100
+ server_url=server_url,
101
+ tools=llm_tools_dict
102
+ )
103
+
104
+ if response_text:
105
+ print(f"{BOLD}{GREEN}Assistant:{RESET} {response_text}")
106
+ messages.append({"role": "assistant", "content": response_text})
107
+ else:
108
+ llm_response_flag = True
109
+
110
+ if tool_calls:
111
+ # Tool call parsing and execution
112
+ print(f"{BOLD}{YELLOW}Tool Call:{RESET} LLM is trying to call {len(tool_calls)} tools.")
113
+
114
+ for call in tool_calls:
115
+ call_id = call.get("id", "")
116
+ name = call.get("function", {}).get("name", "")
117
+ params_str = call.get("function", {}).get("arguments", "{}")
118
+ try:
119
+ tool_params = json.loads(params_str)
120
+ except json.JSONDecodeError:
121
+ print(f"{BOLD}{RED}Warning:{RESET} Error parsing arguments for tool {name}")
122
+ continue
123
+
124
+ if name in tools_dict:
125
+ # Check allowed directory for file operations
126
+ if name in ["read_file", "write_file", "grep", "glob"]:
127
+ filepath = tool_params.get("filepath", "")
128
+ if name == "write_file":
129
+ filepath = tool_params.get("filepath", "")
130
+ if filepath and not os.path.abspath(filepath).startswith(allowed_dir):
131
+ print(f"{BOLD}{RED}Warning:{RESET} File operation not allowed outside allowed directory: {allowed_dir}")
132
+ continue
133
+
134
+ func = tools_dict[name]["function"]
135
+ result = func(**tool_params)
136
+ # Limiting output to 300 characters for readability
137
+ result_str = str(result)
138
+ if len(result_str) > 300:
139
+ result_str = result_str[:300] + "... (truncated to 300 chars)"
140
+ print(f"{BOLD}{MAGENTA}Tool '{name}' returned:{RESET} {result_str}")
141
+
142
+ messages.append({
143
+ "role": "tool",
144
+ "tool_call_id": call_id,
145
+ "content": str(result)
146
+ })
147
+ else:
148
+ print(f"{BOLD}{RED}Warning:{RESET} Unknown tool: {name}")
149
+ else:
150
+ llm_tool_response_flag = True
151
+
152
+ if llm_response_flag and llm_tool_response_flag:
153
+ print(f"{BOLD}{RED}Warning:{RESET} No response was given by the LLM.")
154
+
155
+ save_history(messages)
156
+
157
+ if __name__ == "__main__":
158
+ main()
@@ -3,22 +3,30 @@ def system_prompt(tools_dict=None, allowed_dir=None):
3
3
  """
4
4
  Takes a list of tool names and returns a system prompt string.
5
5
  """
6
- allowed_dir_prompt = ""
7
- if allowed_dir:
8
- allowed_dir_prompt = f"\nThe path you are allowed to operate in is called: {allowed_dir}"
6
+
9
7
  if tools_dict:
10
8
  tool_descriptions = "\n".join([f"{name} : {data['description']}" for name, data in tools_dict.items()])
11
- base_prompt = "You are a helpful AI coding assistant with access to the following tools:\n"
12
- end_prompt = "\n\nWhen answering, first use available tools if needed, then provide a compact summary of the results."
9
+ base_prompt = "You are a helpful AI assistant with access to the following tools:\n"
10
+ end_prompt = (
11
+ "Choose the most appropriate tool for the task at hand. "
12
+ "If a tool fails or you hit a limit, analyze the error and try a different approach. "
13
+ "Be concise and efficient in your tool usage."
14
+ )
13
15
  else:
14
16
  tool_descriptions = ""
15
- base_prompt = "You are a helpful AI coding assistant without any tools."
17
+ base_prompt = "You are a helpful AI assistant without any tools."
16
18
  end_prompt = ""
19
+
20
+
21
+ allowed_dir_prompt = ""
22
+ if allowed_dir:
23
+ allowed_dir_prompt = f"\nThe path you are allowed to operate in is called: {allowed_dir}"
24
+
17
25
 
18
26
  prompt = f"""
19
27
  {base_prompt}
20
28
  {tool_descriptions}{end_prompt}{allowed_dir_prompt}
21
- Keep responses concise and focused.
29
+ Important: Only follow the user instruction. Do not create additional .md files (unless asked to do so). Do not create unit tests (unless asked to do so), etc.
22
30
  """
23
31
  return prompt.strip()
24
32
 
@@ -42,7 +50,7 @@ Conversation:
42
50
  return prompt.strip()
43
51
 
44
52
  if __name__=="__main__":
45
- import .tools
53
+ from . import tools
46
54
  tools_dict = tools.util_get_tools_dict()
47
55
  print(system_prompt(tools_dict))
48
56
 
@@ -31,7 +31,7 @@ def grep(pattern, filepath):
31
31
  except Exception as e:
32
32
  return f"Error: {e}"
33
33
 
34
- def glob(pattern):
34
+ def glob(pattern="./"):
35
35
  """Return list of file paths matching the given glob pattern. Param: pattern"""
36
36
  try:
37
37
  return glob_module.glob(pattern)
@@ -19,9 +19,9 @@ def call_openai_server(messages, max_tokens=2048, temperature=0.0, top_p=0.95, m
19
19
  payload = {
20
20
  "model": model,
21
21
  "messages": messages,
22
- "max_tokens": max_tokens,
23
22
  "temperature": temperature,
24
- "top_p": top_p
23
+ # "max_tokens": max_tokens,
24
+ # "top_p": top_p
25
25
  }
26
26
 
27
27
  if tools:
@@ -72,7 +72,7 @@ def parse_args():
72
72
 
73
73
 
74
74
  if __name__ == '__main__':
75
- import .tools
75
+ from . import tools
76
76
  tools_dict = tools.util_get_tools()
77
77
 
78
78
  args = parse_args()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mini-code-cli
3
- Version: 0.0.5
3
+ Version: 0.0.7
4
4
  Summary: Minimal Coding CLI
5
5
  Author-email: Nikolai Rozanov <nikolai.rozanov@gmail.com>
6
6
  License: The MIT License (MIT)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mini-code-cli"
3
- version = "0.0.5"
3
+ version = "0.0.7"
4
4
  description = "Minimal Coding CLI"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,138 +0,0 @@
1
- import os
2
- import json
3
-
4
- import .prompts
5
- import .tools
6
- import .utils
7
-
8
- import argparse
9
- import sys
10
-
11
- # ANSI escape codes for colors and bold
12
- BOLD = '\033[1m'
13
- RED = '\033[91m'
14
- GREEN = '\033[92m'
15
- YELLOW = '\033[93m'
16
- BLUE = '\033[94m'
17
- MAGENTA = '\033[95m'
18
- CYAN = '\033[96m'
19
- RESET = '\033[0m'
20
-
21
- def save_history(messages):
22
- # Save messages to history.json
23
- with open("history.json", "w") as f:
24
- json.dump(messages, f, indent=2)
25
-
26
- def parse_args():
27
- parser = argparse.ArgumentParser(description="Agent loop for interacting with a language model")
28
- parser.add_argument("--url", type=str, default="http://0.0.0.0:30000", help="API host")
29
- parser.add_argument("--api-key", type=str, default=None, help="API key for authentication")
30
- parser.add_argument("--allowed-dir", type=str, default=".", help="Allowed directory for file operations")
31
- parser.add_argument("--max-tokens", type=int, default=2048, help="Maximum tokens for LLM response")
32
- parser.add_argument("--temperature", type=float, default=0.0, help="Temperature for LLM")
33
- parser.add_argument("--model", type=str, default="default", help="Model name")
34
- args = parser.parse_args()
35
- return args
36
-
37
- def main():
38
- args = parse_args()
39
-
40
- allowed_dir = os.path.abspath(args.allowed_dir)
41
- server_url = f"{args.url}"
42
-
43
- tools_dict = tools.util_get_tools_dict()
44
- llm_tools_dict = tools.util_get_tools()
45
-
46
- system_prompt = prompts.system_prompt(tools_dict=tools_dict, allowed_dir=allowed_dir)
47
-
48
- messages = [{"role": "system", "content": system_prompt}]
49
-
50
- print(f"""
51
- ╔═══════════════════════════════════════════════════════════════════╗
52
- ║ Coding Agent Loop Started\t\t\t\t\t║
53
- ║ Type 'quit' or 'exit' to exit the loop.\t\t\t\t║
54
- ║ Allowed dir: {allowed_dir}\t║
55
- ╚═══════════════════════════════════════════════════════════════════╝
56
- """)
57
-
58
- while True:
59
- llm_response_flag = False
60
- llm_tool_response_flag = False
61
- try:
62
- print("-"*30)
63
- user_input = input(f"{BOLD}{CYAN}You:{RESET} ")
64
- if user_input.lower() in ["quit", "exit"]:
65
- print("\nThank you. Goodbye! Saving history...")
66
- save_history(messages)
67
- break
68
- except KeyboardInterrupt:
69
- print("\nThank you. Goodbye! Saving history...")
70
- save_history(messages)
71
- break
72
-
73
- messages.append({"role": "user", "content": user_input})
74
-
75
- response_text, tool_calls = utils.call_openai_server(
76
- messages,
77
- max_tokens=args.max_tokens,
78
- temperature=args.temperature,
79
- model=args.model,
80
- server_url=server_url,
81
- tools=llm_tools_dict
82
- )
83
-
84
- if response_text:
85
- print(f"{BOLD}{GREEN}Assistant:{RESET} {response_text}")
86
- messages.append({"role": "assistant", "content": response_text})
87
- else:
88
- llm_response_flag = True
89
-
90
- if tool_calls:
91
- # Tool call parsing and execution
92
- print(f"{BOLD}{YELLOW}Tool Call:{RESET} LLM is trying to call {len(tool_calls)} tools.")
93
- if tool_calls:
94
- for call in tool_calls:
95
- call_id = call.get("id", "")
96
- name = call.get("function", {}).get("name", "")
97
- params_str = call.get("function", {}).get("arguments", "{}")
98
- try:
99
- tool_params = json.loads(params_str)
100
- except json.JSONDecodeError:
101
- print(f"{BOLD}{RED}Warning:{RESET} Error parsing arguments for tool {name}")
102
- continue
103
-
104
- if name in tools_dict:
105
- # Check allowed directory for file operations
106
- if name in ["read_file", "write_file", "grep", "glob"]:
107
- filepath = tool_params.get("filepath", "")
108
- if name == "write_file":
109
- filepath = tool_params.get("filepath", "")
110
- if filepath and not os.path.abspath(filepath).startswith(allowed_dir):
111
- print(f"{BOLD}{RED}Warning:{RESET} File operation not allowed outside allowed directory: {allowed_dir}")
112
- continue
113
-
114
- func = tools_dict[name]["function"]
115
- result = func(**tool_params)
116
- # Limiting output to 300 characters for readability
117
- result_str = str(result)
118
- if len(result_str) > 300:
119
- result_str = result_str[:300] + "... (truncated to 300 chars)"
120
- print(f"{BOLD}{MAGENTA}Tool '{name}' returned:{RESET} {result_str}")
121
-
122
- messages.append({
123
- "role": "tool",
124
- "tool_call_id": call_id,
125
- "content": str(result)
126
- })
127
- else:
128
- print(f"{BOLD}{RED}Warning:{RESET} Unknown tool: {name}")
129
- else:
130
- llm_tool_response_flag = True
131
-
132
- if llm_response_flag and llm_tool_response_flag:
133
- print(f"{BOLD}{RED}Warning:{RESET} No response was given by the LLM.")
134
-
135
- save_history(messages)
136
-
137
- if __name__ == "__main__":
138
- main()
File without changes
File without changes
File without changes