systemr-cli 1.1.3__tar.gz → 1.2.0__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.
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/PKG-INFO +1 -1
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/__init__.py +1 -1
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/commands/chat_commands.py +38 -30
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/pyproject.toml +1 -1
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/.gitignore +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/DESIGN.md +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/README.md +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/clawhub/systemr-trading/SKILL.md +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/clawhub/systemr-trading/scripts/mcp_stdio_proxy.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/__main__.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/auth.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/cli.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/client.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/commands/__init__.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/commands/auth_commands.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/commands/cron_commands.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/commands/doctor_command.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/commands/eval_commands.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/commands/journal_commands.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/commands/plan_commands.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/commands/risk_commands.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/commands/scan_commands.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/commands/size_commands.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/config.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/confirmation.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/credits.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/cron.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/display/__init__.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/display/chat_renderer.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/display/formatters.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/display/tables.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/display/theme.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/hooks.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/logging.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/model_failover.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/orchestrator.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/profile.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/store.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/streaming.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/tool_router.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/neo/types.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/__init__.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_auth.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_chat_helpers.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_chat_renderer.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_cli.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_config.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_confirmation.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_credits.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_cron.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_doctor.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_formatters.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_hooks.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_logging.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_model_failover.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_orchestrator.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_profile.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_store.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_streaming.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_tool_router.py +0 -0
- {systemr_cli-1.1.3 → systemr_cli-1.2.0}/tests/test_types.py +0 -0
|
@@ -17,9 +17,7 @@ import httpx
|
|
|
17
17
|
import structlog
|
|
18
18
|
from prompt_toolkit import PromptSession
|
|
19
19
|
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
|
20
|
-
from prompt_toolkit.formatted_text import HTML
|
|
21
20
|
from prompt_toolkit.history import FileHistory
|
|
22
|
-
from prompt_toolkit.styles import Style as PTStyle
|
|
23
21
|
|
|
24
22
|
from neo.auth import AuthManager
|
|
25
23
|
from neo.config import SYSTEMR_HOME, SESSIONS_DIR, ensure_systemr_home
|
|
@@ -223,16 +221,9 @@ def chat(ctx: click.Context, model_name: str | None, research: bool, resume: boo
|
|
|
223
221
|
history_file = SYSTEMR_HOME / "chat_history"
|
|
224
222
|
loop = asyncio.new_event_loop()
|
|
225
223
|
|
|
226
|
-
# Dark theme for prompt_toolkit — matches terminal background
|
|
227
|
-
pt_style = PTStyle.from_dict({
|
|
228
|
-
"bottom-toolbar": "bg:#111114 #52525B",
|
|
229
|
-
"prompt": "bold #F5F5F5",
|
|
230
|
-
})
|
|
231
|
-
|
|
232
224
|
prompt_session: PromptSession[str] = PromptSession(
|
|
233
225
|
history=FileHistory(str(history_file)),
|
|
234
226
|
auto_suggest=AutoSuggestFromHistory(),
|
|
235
|
-
style=pt_style,
|
|
236
227
|
)
|
|
237
228
|
|
|
238
229
|
# Broker state (will be dynamic when broker connection is implemented)
|
|
@@ -245,29 +236,18 @@ def chat(ctx: click.Context, model_name: str | None, research: bool, resume: boo
|
|
|
245
236
|
|
|
246
237
|
try:
|
|
247
238
|
while True:
|
|
248
|
-
# Status bar as prompt_toolkit bottom_toolbar (visible while typing)
|
|
249
|
-
def _toolbar():
|
|
250
|
-
if connected_broker:
|
|
251
|
-
broker_html = f'<style fg="#3ECF8E">●</style> {connected_broker}'
|
|
252
|
-
else:
|
|
253
|
-
broker_html = '<style fg="#EF4444">○</style> broker not connected'
|
|
254
|
-
return HTML(
|
|
255
|
-
f' <style fg="#3ECF8E">●</style> {display_model}'
|
|
256
|
-
f' <style fg="#3F3F46">│</style> {broker_html}'
|
|
257
|
-
f' <style fg="#3F3F46">│</style> /help commands'
|
|
258
|
-
)
|
|
259
|
-
|
|
260
239
|
print_separator()
|
|
261
240
|
try:
|
|
262
|
-
user_input = prompt_session.prompt(
|
|
263
|
-
" ❯ ",
|
|
264
|
-
bottom_toolbar=_toolbar,
|
|
265
|
-
).strip()
|
|
241
|
+
user_input = prompt_session.prompt(" ❯ ").strip()
|
|
266
242
|
except (EOFError, KeyboardInterrupt):
|
|
267
243
|
console.print()
|
|
268
244
|
console.print(f" [{DIM}]Session closed.[/]")
|
|
269
245
|
break
|
|
270
246
|
|
|
247
|
+
# Bottom separator + status bar
|
|
248
|
+
print_separator()
|
|
249
|
+
print_prompt_bar(model=display_model, broker=connected_broker)
|
|
250
|
+
|
|
271
251
|
if not user_input:
|
|
272
252
|
continue
|
|
273
253
|
|
|
@@ -429,22 +409,33 @@ async def _do_chat(
|
|
|
429
409
|
Returns:
|
|
430
410
|
The collected assistant response text.
|
|
431
411
|
"""
|
|
412
|
+
import time as _time
|
|
413
|
+
import sys
|
|
414
|
+
|
|
432
415
|
collected_text = ""
|
|
433
416
|
tool_count = 0
|
|
434
417
|
has_trade = False
|
|
418
|
+
streaming_started = False
|
|
419
|
+
start_time = _time.time()
|
|
435
420
|
|
|
436
421
|
try:
|
|
437
422
|
async for event in stream_chat(request, access_token):
|
|
438
423
|
if event.event == "thinking":
|
|
439
|
-
text = event.parsed.get("text", event.parsed.get("content", ""))
|
|
424
|
+
text = event.parsed.get("text", event.parsed.get("status", event.parsed.get("content", "")))
|
|
440
425
|
if text:
|
|
441
|
-
|
|
426
|
+
# Clear line and show thinking status
|
|
427
|
+
sys.stdout.write(f"\r \033[2m◐ {text[:70]}\033[0m\033[K")
|
|
428
|
+
sys.stdout.flush()
|
|
442
429
|
|
|
443
430
|
elif event.event == "action":
|
|
444
431
|
tool = event.parsed.get("action", event.parsed.get("tool", event.parsed.get("name", "")))
|
|
445
432
|
params = event.parsed.get("parameters", event.parsed.get("args", {}))
|
|
446
433
|
tool_count += 1
|
|
447
434
|
|
|
435
|
+
# Clear thinking line before showing tool
|
|
436
|
+
sys.stdout.write("\r\033[K")
|
|
437
|
+
sys.stdout.flush()
|
|
438
|
+
|
|
448
439
|
# Fire BEFORE_TOOL_CALL hook — can inspect/log tool calls
|
|
449
440
|
if tool:
|
|
450
441
|
fire_hook(HookEvent.BEFORE_TOOL_CALL, context={
|
|
@@ -515,12 +506,29 @@ async def _do_chat(
|
|
|
515
506
|
elif event.event == "text_delta":
|
|
516
507
|
text = event.parsed.get("text", event.parsed.get("delta", event.data))
|
|
517
508
|
if text and text != "null":
|
|
509
|
+
# Start green border on first chunk
|
|
510
|
+
if not streaming_started:
|
|
511
|
+
sys.stdout.write("\r\033[K") # Clear thinking line
|
|
512
|
+
sys.stdout.write(f"\n \033[38;2;62;207;142m┃\033[0m ")
|
|
513
|
+
streaming_started = True
|
|
514
|
+
# Stream text in real-time, handle newlines with border
|
|
515
|
+
for ch in text:
|
|
516
|
+
if ch == "\n":
|
|
517
|
+
sys.stdout.write(f"\n \033[38;2;62;207;142m┃\033[0m ")
|
|
518
|
+
else:
|
|
519
|
+
sys.stdout.write(ch)
|
|
520
|
+
sys.stdout.flush()
|
|
518
521
|
collected_text += text
|
|
519
522
|
|
|
520
523
|
elif event.event == "done":
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
+
# Close the streaming block
|
|
525
|
+
if streaming_started:
|
|
526
|
+
elapsed = _time.time() - start_time
|
|
527
|
+
sys.stdout.write(f"\n \033[38;2;62;207;142m┃\033[0m")
|
|
528
|
+
sys.stdout.write(f"\n\n \033[2m✦ {elapsed:.1f}s\033[0m")
|
|
529
|
+
sys.stdout.write("\n")
|
|
530
|
+
sys.stdout.flush()
|
|
531
|
+
elif not collected_text.strip():
|
|
524
532
|
full = event.parsed.get(
|
|
525
533
|
"response", event.parsed.get("content", ""),
|
|
526
534
|
)
|
|
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
|
|
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
|
|
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
|
|
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
|