codegpt-ai 1.0.0 → 1.1.0

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.
package/mobile.py DELETED
@@ -1,422 +0,0 @@
1
- """CodeGPT Mobile — Flet app for Android + Desktop. Connects to Ollama."""
2
-
3
- import json
4
- import time
5
- import threading
6
- from pathlib import Path
7
-
8
- import flet as ft
9
- import requests
10
-
11
- # --- Config ---
12
-
13
- DEFAULT_SERVER = "http://192.168.1.237:5050" # CodeGPT backend
14
- DEFAULT_MODEL = "" # Empty = use server default
15
- CONFIG_DIR = Path.home() / ".codegpt"
16
- MOBILE_CONFIG = CONFIG_DIR / "mobile_config.json"
17
-
18
- SYSTEM_PROMPT = """You are an AI modeled after a highly technical, system-focused developer mindset.
19
- Be direct, concise, and dense with information. No fluff, no filler, no emojis.
20
- Give conclusions first, then minimal necessary explanation.
21
- Focus on: AI, coding, automation, cybersecurity, system design.
22
- Blunt but intelligent. Slightly dark tone is acceptable.
23
- Keep responses concise for mobile reading."""
24
-
25
- PERSONAS = {
26
- "Default": SYSTEM_PROMPT,
27
- "Hacker": "You are a cybersecurity expert. Technical jargon, CVEs, attack vectors. Defensive security only. Be concise for mobile.",
28
- "Teacher": "You are a patient programming teacher. Step by step, analogies, examples. Adapt to the student. Concise for mobile.",
29
- "Roast": "You are a brutally sarcastic code reviewer. Roast bad code then give the fix. Dark humor. Concise for mobile.",
30
- "Minimal": "Shortest possible answer. One line if possible. Code only, no commentary.",
31
- }
32
-
33
-
34
- def load_config():
35
- CONFIG_DIR.mkdir(parents=True, exist_ok=True)
36
- if MOBILE_CONFIG.exists():
37
- try:
38
- return json.loads(MOBILE_CONFIG.read_text())
39
- except Exception:
40
- pass
41
- return {"server": DEFAULT_SERVER, "model": DEFAULT_MODEL, "persona": "Default"}
42
-
43
-
44
- def save_config(config):
45
- CONFIG_DIR.mkdir(parents=True, exist_ok=True)
46
- MOBILE_CONFIG.write_text(json.dumps(config, indent=2))
47
-
48
-
49
- # --- Main App ---
50
-
51
- def main(page: ft.Page):
52
- # Theme
53
- page.title = "CodeGPT"
54
- page.theme_mode = ft.ThemeMode.DARK
55
- page.theme = ft.Theme(
56
- color_scheme_seed=ft.Colors.CYAN,
57
- font_family="Roboto",
58
- )
59
- page.padding = 0
60
-
61
- # State
62
- config = load_config()
63
- messages = []
64
- is_streaming = [False]
65
-
66
- # --- UI Components ---
67
-
68
- chat_list = ft.ListView(
69
- expand=True,
70
- spacing=8,
71
- padding=ft.padding.symmetric(horizontal=12, vertical=8),
72
- auto_scroll=True,
73
- )
74
-
75
- input_field = ft.TextField(
76
- hint_text="Message CodeGPT...",
77
- border_radius=24,
78
- filled=True,
79
- expand=True,
80
- on_submit=lambda e: send_message(e),
81
- text_size=14,
82
- content_padding=ft.padding.symmetric(horizontal=16, vertical=10),
83
- )
84
-
85
- status_text = ft.Text(
86
- f"{config['model']} | 0 msgs",
87
- size=11,
88
- color=ft.Colors.with_opacity(0.5, ft.Colors.WHITE),
89
- )
90
-
91
- def update_status():
92
- status_text.value = f"{config['model']} | {len(messages)} msgs | {config.get('persona', 'Default')}"
93
- try:
94
- page.update()
95
- except Exception:
96
- pass
97
-
98
- # --- Message Bubbles ---
99
-
100
- def add_user_bubble(text):
101
- chat_list.controls.append(
102
- ft.Container(
103
- content=ft.Column([
104
- ft.Text("You", size=11, color=ft.Colors.CYAN, weight=ft.FontWeight.BOLD),
105
- ft.Text(text, size=14, color=ft.Colors.WHITE, selectable=True),
106
- ], spacing=4),
107
- padding=ft.padding.all(12),
108
- border_radius=ft.border_radius.only(
109
- top_left=16, top_right=16, bottom_left=16, bottom_right=4,
110
- ),
111
- bgcolor=ft.Colors.with_opacity(0.15, ft.Colors.CYAN),
112
- margin=ft.margin.only(left=48, bottom=4),
113
- )
114
- )
115
-
116
- def add_ai_bubble(text, stats=""):
117
- bubble = ft.Container(
118
- content=ft.Column([
119
- ft.Text("AI", size=11, color=ft.Colors.GREEN, weight=ft.FontWeight.BOLD),
120
- ft.Markdown(
121
- text,
122
- selectable=True,
123
- extension_set=ft.MarkdownExtensionSet.GITHUB_WEB,
124
- code_theme=ft.MarkdownCodeTheme.MONOKAI,
125
- on_tap_link=lambda e: page.launch_url(e.data),
126
- ),
127
- ft.Text(stats, size=10, color=ft.Colors.with_opacity(0.4, ft.Colors.WHITE)) if stats else ft.Container(),
128
- ], spacing=4),
129
- padding=ft.padding.all(12),
130
- border_radius=ft.border_radius.only(
131
- top_left=16, top_right=16, bottom_left=4, bottom_right=16,
132
- ),
133
- bgcolor=ft.Colors.with_opacity(0.1, ft.Colors.GREEN),
134
- margin=ft.margin.only(right=48, bottom=4),
135
- key="ai_latest",
136
- )
137
- chat_list.controls.append(bubble)
138
-
139
- def add_thinking_bubble():
140
- chat_list.controls.append(
141
- ft.Container(
142
- content=ft.Row([
143
- ft.ProgressRing(width=16, height=16, stroke_width=2, color=ft.Colors.GREEN),
144
- ft.Text("Thinking...", size=13, color=ft.Colors.with_opacity(0.5, ft.Colors.WHITE), italic=True),
145
- ], spacing=8),
146
- padding=ft.padding.all(12),
147
- border_radius=16,
148
- bgcolor=ft.Colors.with_opacity(0.05, ft.Colors.GREEN),
149
- margin=ft.margin.only(right=48, bottom=4),
150
- key="thinking",
151
- )
152
- )
153
-
154
- def remove_thinking():
155
- chat_list.controls[:] = [c for c in chat_list.controls if getattr(c, 'key', None) != "thinking"]
156
-
157
- # --- Send Message ---
158
-
159
- def send_message(e):
160
- text = input_field.value.strip()
161
- if not text or is_streaming[0]:
162
- return
163
-
164
- input_field.value = ""
165
- add_user_bubble(text)
166
- add_thinking_bubble()
167
- page.update()
168
-
169
- messages.append({"role": "user", "content": text})
170
- is_streaming[0] = True
171
-
172
- def do_request():
173
- server = config.get("server", DEFAULT_SERVER)
174
- model = config.get("model", DEFAULT_MODEL)
175
- persona = config.get("persona", "Default")
176
- system = PERSONAS.get(persona, SYSTEM_PROMPT)
177
-
178
- ollama_messages = [{"role": "system", "content": system}]
179
- ollama_messages.extend(messages)
180
-
181
- try:
182
- response = requests.post(
183
- f"{server}/chat",
184
- json={
185
- "messages": messages,
186
- "model": model,
187
- "persona": persona,
188
- },
189
- stream=True,
190
- timeout=120,
191
- )
192
- response.raise_for_status()
193
-
194
- full = []
195
- stats = ""
196
- last_update = 0
197
-
198
- for line in response.iter_lines():
199
- if not line:
200
- continue
201
- try:
202
- chunk = json.loads(line)
203
- except json.JSONDecodeError:
204
- continue
205
-
206
- content = chunk.get("content", "")
207
- if content:
208
- full.append(content)
209
-
210
- now = time.time()
211
- if now - last_update >= 0.5:
212
- remove_thinking()
213
- chat_list.controls[:] = [
214
- c for c in chat_list.controls
215
- if getattr(c, 'key', None) != "ai_latest"
216
- ]
217
- add_ai_bubble("".join(full), "streaming...")
218
- try:
219
- page.update()
220
- except Exception:
221
- pass
222
- last_update = now
223
-
224
- if chunk.get("done"):
225
- stats = chunk.get("stats", "")
226
- if not stats:
227
- tokens = chunk.get("tokens", {})
228
- provider = chunk.get("provider", "")
229
- out_tok = tokens.get("output", 0)
230
- stats = f"{out_tok} tok | {provider}"
231
-
232
- final_text = "".join(full)
233
- messages.append({"role": "assistant", "content": final_text})
234
-
235
- # Final render
236
- remove_thinking()
237
- chat_list.controls[:] = [
238
- c for c in chat_list.controls
239
- if getattr(c, 'key', None) != "ai_latest"
240
- ]
241
- add_ai_bubble(final_text, stats)
242
-
243
- except requests.ConnectionError:
244
- remove_thinking()
245
- add_ai_bubble("Cannot connect to Ollama.\nCheck server IP in settings.", "error")
246
- if messages and messages[-1]["role"] == "user":
247
- messages.pop()
248
- except requests.Timeout:
249
- remove_thinking()
250
- add_ai_bubble("Request timed out.", "error")
251
- if messages and messages[-1]["role"] == "user":
252
- messages.pop()
253
- except Exception as ex:
254
- remove_thinking()
255
- add_ai_bubble(f"Error: {ex}", "error")
256
- if messages and messages[-1]["role"] == "user":
257
- messages.pop()
258
- finally:
259
- is_streaming[0] = False
260
- update_status()
261
- try:
262
- page.update()
263
- except Exception:
264
- pass
265
-
266
- threading.Thread(target=do_request, daemon=True).start()
267
-
268
- # --- Settings Dialog ---
269
-
270
- def open_settings(e):
271
- server_field = ft.TextField(
272
- value=config.get("server", DEFAULT_SERVER),
273
- label="Ollama Server URL",
274
- border_radius=12,
275
- text_size=14,
276
- )
277
- model_field = ft.TextField(
278
- value=config.get("model", DEFAULT_MODEL),
279
- label="Model",
280
- border_radius=12,
281
- text_size=14,
282
- )
283
- persona_dropdown = ft.Dropdown(
284
- value=config.get("persona", "Default"),
285
- label="Persona",
286
- options=[ft.dropdown.Option(p) for p in PERSONAS],
287
- border_radius=12,
288
- text_size=14,
289
- )
290
-
291
- def save_settings(e):
292
- config["server"] = server_field.value.strip().rstrip("/")
293
- config["model"] = model_field.value.strip()
294
- config["persona"] = persona_dropdown.value
295
- save_config(config)
296
- update_status()
297
- dlg.open = False
298
- page.update()
299
- page.open(ft.SnackBar(ft.Text("Settings saved"), duration=1500))
300
-
301
- def test_connection(e):
302
- server = server_field.value.strip().rstrip("/")
303
- try:
304
- resp = requests.get(f"{server}/health", timeout=5)
305
- data = resp.json()
306
- provider = data.get("provider", "?")
307
- model = data.get("model", "?")
308
- page.open(ft.SnackBar(
309
- ft.Text(f"Connected. Provider: {provider}, Model: {model}"),
310
- duration=3000,
311
- ))
312
- except Exception as ex:
313
- page.open(ft.SnackBar(ft.Text(f"Failed: {ex}"), duration=3000))
314
-
315
- dlg = ft.AlertDialog(
316
- title=ft.Text("Settings"),
317
- content=ft.Container(
318
- content=ft.Column([
319
- server_field,
320
- ft.ElevatedButton("Test Connection", on_click=test_connection, icon=ft.Icons.WIFI),
321
- model_field,
322
- persona_dropdown,
323
- ], spacing=16, tight=True),
324
- width=320,
325
- padding=ft.padding.only(top=8),
326
- ),
327
- actions=[
328
- ft.TextButton("Cancel", on_click=lambda e: setattr(dlg, 'open', False) or page.update()),
329
- ft.ElevatedButton("Save", on_click=save_settings),
330
- ],
331
- )
332
- page.open(dlg)
333
-
334
- # --- New Chat ---
335
-
336
- def new_chat(e):
337
- messages.clear()
338
- chat_list.controls.clear()
339
-
340
- # Welcome
341
- chat_list.controls.append(
342
- ft.Container(
343
- content=ft.Column([
344
- ft.Text("CodeGPT", size=28, weight=ft.FontWeight.BOLD, color=ft.Colors.CYAN,
345
- text_align=ft.TextAlign.CENTER),
346
- ft.Text("Local AI assistant on your phone.", size=14,
347
- color=ft.Colors.with_opacity(0.5, ft.Colors.WHITE),
348
- text_align=ft.TextAlign.CENTER),
349
- ft.Divider(height=20, color=ft.Colors.TRANSPARENT),
350
- *[
351
- ft.OutlinedButton(
352
- text=s,
353
- on_click=lambda e, txt=s: (setattr(input_field, 'value', txt), send_message(e)),
354
- style=ft.ButtonStyle(shape=ft.RoundedRectangleBorder(radius=12)),
355
- )
356
- for s in [
357
- "Explain TCP/IP",
358
- "Python CPU monitor script",
359
- "OWASP top 10",
360
- "Design a REST API",
361
- ]
362
- ],
363
- ], horizontal_alignment=ft.CrossAxisAlignment.CENTER, spacing=8),
364
- padding=ft.padding.all(32),
365
- alignment=ft.alignment.center,
366
- )
367
- )
368
- update_status()
369
- page.update()
370
-
371
- # --- App Bar ---
372
-
373
- page.appbar = ft.AppBar(
374
- leading=ft.Icon(ft.Icons.TERMINAL, color=ft.Colors.CYAN),
375
- title=ft.Text("CodeGPT", weight=ft.FontWeight.BOLD),
376
- center_title=False,
377
- bgcolor=ft.Colors.with_opacity(0.9, ft.Colors.BLACK),
378
- actions=[
379
- ft.IconButton(ft.Icons.ADD_COMMENT, on_click=new_chat, tooltip="New Chat"),
380
- ft.IconButton(ft.Icons.SETTINGS, on_click=open_settings, tooltip="Settings"),
381
- ],
382
- )
383
-
384
- # --- Bottom Input Bar ---
385
-
386
- send_btn = ft.IconButton(
387
- ft.Icons.SEND_ROUNDED,
388
- icon_color=ft.Colors.CYAN,
389
- on_click=send_message,
390
- tooltip="Send",
391
- )
392
-
393
- input_bar = ft.Container(
394
- content=ft.Row([input_field, send_btn], spacing=8),
395
- padding=ft.padding.symmetric(horizontal=12, vertical=8),
396
- bgcolor=ft.Colors.with_opacity(0.95, ft.Colors.BLACK),
397
- border=ft.border.only(top=ft.BorderSide(1, ft.Colors.with_opacity(0.1, ft.Colors.WHITE))),
398
- )
399
-
400
- # --- Status Bar ---
401
-
402
- status_bar = ft.Container(
403
- content=status_text,
404
- padding=ft.padding.symmetric(horizontal=16, vertical=4),
405
- bgcolor=ft.Colors.with_opacity(0.95, ft.Colors.BLACK),
406
- )
407
-
408
- # --- Layout ---
409
-
410
- page.add(
411
- ft.Column([
412
- ft.Container(content=chat_list, expand=True),
413
- status_bar,
414
- input_bar,
415
- ], expand=True, spacing=0),
416
- )
417
-
418
- # Show welcome
419
- new_chat(None)
420
-
421
-
422
- ft.app(target=main)
package/pyproject.toml DELETED
@@ -1,33 +0,0 @@
1
- [build-system]
2
- requires = ["setuptools>=68.0", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "codegpt"
7
- version = "1.0.0"
8
- description = "Local AI Assistant Hub — CLI, agents, training, 15+ tool integrations"
9
- readme = "CLAUDE.md"
10
- license = {text = "MIT"}
11
- requires-python = ">=3.10"
12
- authors = [{name = "ArukuX"}]
13
-
14
- dependencies = [
15
- "requests>=2.31.0",
16
- "rich>=13.0.0",
17
- "prompt-toolkit>=3.0.0",
18
- ]
19
-
20
- [project.optional-dependencies]
21
- full = [
22
- "textual>=0.40.0",
23
- "python-telegram-bot>=20.0",
24
- "flask>=3.0.0",
25
- "groq>=0.4.0",
26
- "SpeechRecognition>=3.10.0",
27
- ]
28
-
29
- [project.scripts]
30
- ai = "ai_cli.__main__:main"
31
-
32
- [tool.setuptools.packages.find]
33
- include = ["ai_cli*"]
package/requirements.txt DELETED
@@ -1,12 +0,0 @@
1
- requests>=2.31.0
2
- rich>=13.0.0
3
- prompt_toolkit>=3.0.0
4
- textual>=1.0.0
5
- python-telegram-bot>=21.0
6
- SpeechRecognition>=3.10.0
7
- # PyAudio - install separately for voice input:
8
- # pipwin install pyaudio OR
9
- # pip install pipwin && pipwin install pyaudio
10
- flask>=3.0.0
11
- groq>=0.4.0
12
- flet>=0.20.0
package/run.py DELETED
@@ -1,157 +0,0 @@
1
- """CodeGPT Launcher
2
-
3
- Usage:
4
- python run.py Start CLI chat (default)
5
- python run.py chat Start CLI chat
6
- python run.py tui Start TUI with sidebar
7
- python run.py bot Start Telegram bot
8
- python run.py server Start backend server
9
- python run.py mobile Start mobile app (desktop preview)
10
- python run.py apk Build Android APK
11
- """
12
-
13
- import os
14
- import sys
15
- import json
16
- import subprocess
17
- from pathlib import Path
18
-
19
- CONFIG_DIR = Path.home() / ".codegpt"
20
- CONFIG_FILE = CONFIG_DIR / "config.json"
21
-
22
-
23
- def load_config():
24
- CONFIG_DIR.mkdir(parents=True, exist_ok=True)
25
- if CONFIG_FILE.exists():
26
- try:
27
- return json.loads(CONFIG_FILE.read_text())
28
- except Exception:
29
- pass
30
- return {}
31
-
32
-
33
- def save_config(config):
34
- CONFIG_DIR.mkdir(parents=True, exist_ok=True)
35
- CONFIG_FILE.write_text(json.dumps(config, indent=2))
36
-
37
-
38
- def run_script(name):
39
- script = Path(__file__).parent / name
40
- subprocess.run([sys.executable, str(script)])
41
-
42
-
43
- def run_bot(token=None):
44
- config = load_config()
45
- bot_token = token or os.environ.get("CODEGPT_BOT_TOKEN") or config.get("bot_token")
46
-
47
- if not bot_token:
48
- print(" No token. Get one from @BotFather on Telegram.")
49
- try:
50
- bot_token = input(" Paste token > ").strip()
51
- except (KeyboardInterrupt, EOFError):
52
- return
53
- if not bot_token:
54
- return
55
- try:
56
- if input(" Save for later? (y/n) > ").strip().lower() == "y":
57
- config["bot_token"] = bot_token
58
- save_config(config)
59
- print(" Saved.\n")
60
- except (KeyboardInterrupt, EOFError):
61
- pass
62
-
63
- os.environ["CODEGPT_BOT_TOKEN"] = bot_token
64
- run_script("bot.py")
65
-
66
-
67
- def run_server():
68
- config = load_config()
69
- groq_key = os.environ.get("GROQ_API_KEY") or config.get("groq_api_key")
70
-
71
- if not groq_key:
72
- print(" No Groq API key found.")
73
- print(" Get a free one at: https://console.groq.com/keys\n")
74
- print(" Without it, the server will use local Ollama.\n")
75
- try:
76
- groq_key = input(" Paste Groq key (or Enter to skip) > ").strip()
77
- except (KeyboardInterrupt, EOFError):
78
- groq_key = ""
79
-
80
- if groq_key:
81
- try:
82
- if input(" Save for later? (y/n) > ").strip().lower() == "y":
83
- config["groq_api_key"] = groq_key
84
- save_config(config)
85
- print(" Saved.\n")
86
- except (KeyboardInterrupt, EOFError):
87
- pass
88
-
89
- if groq_key:
90
- os.environ["GROQ_API_KEY"] = groq_key
91
-
92
- run_script("server.py")
93
-
94
-
95
- def build_apk():
96
- project = Path(__file__).parent
97
- print(" Building APK with Flet...\n")
98
- result = subprocess.run(
99
- ["flet", "build", "apk", "--project", str(project), "--module-name", "mobile"],
100
- cwd=str(project),
101
- )
102
- if result.returncode == 0:
103
- # Copy APK to desktop
104
- apk_path = project / "build" / "apk" / "app-release.apk"
105
- desktop = Path.home() / "Desktop"
106
- if not desktop.exists():
107
- desktop = Path.home() / "OneDrive" / "Desktop"
108
- if apk_path.exists():
109
- import shutil
110
- dest = desktop / "CodeGPT.apk"
111
- shutil.copy2(str(apk_path), str(dest))
112
- print(f"\n APK copied to: {dest}")
113
- else:
114
- print(f"\n APK built. Check: {project / 'build'}")
115
- else:
116
- print("\n Build failed. Make sure Flet is installed: pip install flet")
117
-
118
-
119
- def main():
120
- args = sys.argv[1:]
121
-
122
- if not args or args[0].lower() in ("chat", "cli"):
123
- run_script("chat.py")
124
- elif args[0].lower() in ("tui", "ui", "app"):
125
- run_script("app.py")
126
- elif args[0].lower() in ("bot", "telegram"):
127
- token = None
128
- if "--token" in args:
129
- idx = args.index("--token")
130
- if idx + 1 < len(args):
131
- token = args[idx + 1]
132
- if "--save-token" in args:
133
- idx = args.index("--save-token")
134
- if idx + 1 < len(args):
135
- token = args[idx + 1]
136
- config = load_config()
137
- config["bot_token"] = token
138
- save_config(config)
139
- print(" Token saved.")
140
- run_bot(token)
141
- elif args[0].lower() in ("server", "backend", "api"):
142
- run_server()
143
- elif args[0].lower() in ("mobile", "phone"):
144
- run_script("mobile.py")
145
- elif args[0].lower() in ("web",):
146
- run_script("web.py")
147
- elif args[0].lower() in ("apk", "build"):
148
- build_apk()
149
- elif args[0].lower() in ("help", "--help", "-h"):
150
- print(__doc__)
151
- else:
152
- print(f" Unknown: {args[0]}")
153
- print(" Use: chat, tui, bot, server, mobile, or apk")
154
-
155
-
156
- if __name__ == "__main__":
157
- main()