opsiom-cli 1.1.0__tar.gz → 1.1.2__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.
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/PKG-INFO +2 -2
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/pyproject.toml +2 -2
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/src/opsiom_cli/cli.py +59 -35
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/src/opsiom_cli.egg-info/PKG-INFO +2 -2
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/README.md +0 -0
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/setup.cfg +0 -0
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/src/opsiom_cli/__init__.py +0 -0
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/src/opsiom_cli.egg-info/SOURCES.txt +0 -0
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/src/opsiom_cli.egg-info/dependency_links.txt +0 -0
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/src/opsiom_cli.egg-info/entry_points.txt +0 -0
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/src/opsiom_cli.egg-info/requires.txt +0 -0
- {opsiom_cli-1.1.0 → opsiom_cli-1.1.2}/src/opsiom_cli.egg-info/top_level.txt +0 -0
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opsiom-cli
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: CLI de chat pour Opsiom, l'assistant IA francophone.
|
|
5
|
-
Author-email:
|
|
5
|
+
Author-email: Jules Ecorcheville <jules.ecorcheville@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/ton-compte/opsiom-cli
|
|
8
8
|
Keywords: opsiom,cli,chatbot,ia
|
|
@@ -4,12 +4,12 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "opsiom-cli"
|
|
7
|
-
version = "1.1.
|
|
7
|
+
version = "1.1.2"
|
|
8
8
|
description = "CLI de chat pour Opsiom, l'assistant IA francophone."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.9"
|
|
11
11
|
license = "MIT"
|
|
12
|
-
authors = [{ name = "
|
|
12
|
+
authors = [{ name = "Jules Ecorcheville", email = "jules.ecorcheville@gmail.com" }]
|
|
13
13
|
dependencies = ["requests>=2.31"]
|
|
14
14
|
keywords = ["opsiom", "cli", "chatbot", "ia"]
|
|
15
15
|
|
|
@@ -39,7 +39,7 @@ try:
|
|
|
39
39
|
except ImportError:
|
|
40
40
|
readline = None # absent par défaut sur certains Windows (pyreadline3 comble le manque)
|
|
41
41
|
|
|
42
|
-
VERSION = "1.1.
|
|
42
|
+
VERSION = "1.1.2"
|
|
43
43
|
CONFIG_PATH = Path.home() / ".config" / "opsiom" / "config.json"
|
|
44
44
|
HISTORY_PATH = Path.home() / ".local" / "share" / "opsiom" / "history"
|
|
45
45
|
TRANSCRIPTS_DIR = Path.home() / "opsiom-conversations"
|
|
@@ -88,14 +88,44 @@ def ok_text(text):
|
|
|
88
88
|
# ----------------------------------------------------------------------------
|
|
89
89
|
|
|
90
90
|
OCTOPUS_MARK = [
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
"
|
|
91
|
+
" . +**=::::::::=**+. \n",
|
|
92
|
+
" *#....::::::::::::::.:#* \n",
|
|
93
|
+
" .=*:..::.::::::::::::::::::::*=. \n",
|
|
94
|
+
" .=*:......::::::::::::::::::::::-*=. \n",
|
|
95
|
+
" .+-.:...:::::::::::::::::::::::::::=*. \n",
|
|
96
|
+
" -*.::::::::::::::::::::::::::::::::::-*=. \n",
|
|
97
|
+
" #= =*.:...::::::::::::::::::::::::::::::::-*+ \n",
|
|
98
|
+
" .. -= .*.:::::::::::::::::::::::::::::::::::::--*. \n",
|
|
99
|
+
" +: - *.:::::::::::::::::::::::::::::::::::::::--* \n",
|
|
100
|
+
" :==-. :=.:::::::::::::::::::::::::::::::::::::::--+: \n",
|
|
101
|
+
" .-:: +:::::::::::::::::::::::::::::::::::::::::---* \n",
|
|
102
|
+
" * -- +:::::::::::::::::::::::::::::::::::::::::---+ \n",
|
|
103
|
+
" *::::::::::*## :+::::::::::::+: ##*::::::----* \n",
|
|
104
|
+
" :::. :+:::::::::**###*-::::::::::-*###**::::::---+: \n",
|
|
105
|
+
" ==...:++. *.::::::::===++=::::::::::::=++===:::::----* \n",
|
|
106
|
+
" +::++::::=..*::::::-----:::::-*+::+*-:::::-----::----* \n",
|
|
107
|
+
" .: :*=::+: :+::::::::::::::::::--:::::::::::::::---* \n",
|
|
108
|
+
" .#+::+: .*:::::::::::::::::::::::::::::::::---*. \n",
|
|
109
|
+
" -#**: *:+::+- .+::::::::::::::::::::::::::::::----+. \n",
|
|
110
|
+
" -*-::.*-*=*:::=*::::=+-:::::::::::::::::::::::::::--+=:::-*+---*=*-*:::-*- \n",
|
|
111
|
+
" :+=::::+#---::::-----+=::::::::::::::::::::::::::::++------------#+::::=+: \n",
|
|
112
|
+
" -+=-::..=**=--+*--*+-::::::::::::::::::::::::::::::-+*--*+--=**=..::--+- \n",
|
|
113
|
+
" ==*-::::..:-*#:::-+:::::-*::::::::::::::::::*-:::::+::::#*-:..::::-*== \n",
|
|
114
|
+
" +-:+-:::::*-.=::-+:::--#:.:::::::-=:::::::::#--:::+:::=.-*:::::-+:-+ \n",
|
|
115
|
+
" +-:*---:+-.-::-*+==**:.:::::::-##-:::::::.:**==+*:::-.-+:---*:-+ \n",
|
|
116
|
+
" =*=:--+=:#::::::::.::::::::-===+-::::::::.::.:::::#:=+-=:-*= \n",
|
|
117
|
+
" .--=*-..+::::::::::::::--+- -+--::::::::::::::+..-*=--. \n",
|
|
118
|
+
" +:::+-:::::::::---=*- -#=---:::::::::-*:::+ \n",
|
|
119
|
+
" .#-::-+-------+**- -+*+-------+-::-#: \n",
|
|
120
|
+
" **-::--::-*+ =*=::--::-** \n",
|
|
96
121
|
]
|
|
97
122
|
|
|
98
123
|
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
|
|
99
129
|
def print_header(url, connected, api_key, models_loaded, current_model, show_mark=True):
|
|
100
130
|
print()
|
|
101
131
|
if show_mark:
|
|
@@ -190,7 +220,7 @@ def save_transcript(transcript, filename=None):
|
|
|
190
220
|
if not filename.endswith(".md"):
|
|
191
221
|
filename += ".md"
|
|
192
222
|
path = TRANSCRIPTS_DIR / filename
|
|
193
|
-
lines = ["# Conversation Opsiom", ""]
|
|
223
|
+
lines = ["# Conversation Opsiom\n", ""]
|
|
194
224
|
for entry in transcript:
|
|
195
225
|
if entry["role"] == "user":
|
|
196
226
|
lines.append(f"**Vous :** {entry['text']}")
|
|
@@ -226,7 +256,7 @@ def prompt_for_config():
|
|
|
226
256
|
api_key = input(" Clé API (vide si aucune) : ").strip()
|
|
227
257
|
save = input(muted(" Sauvegarder localement pour la prochaine fois ? [O/n] ")).strip().lower()
|
|
228
258
|
config = {"url": url, "api_key": api_key}
|
|
229
|
-
if save in ("", "o", "oui", "y", "yes"):
|
|
259
|
+
if save in ("\n", "o\n", "oui\n", "y\n", "yes"):
|
|
230
260
|
save_config(config)
|
|
231
261
|
print(muted(f" → enregistré dans {CONFIG_PATH}\n"))
|
|
232
262
|
return config
|
|
@@ -258,12 +288,12 @@ class OpsiomClient:
|
|
|
258
288
|
self.session.headers.update(headers)
|
|
259
289
|
|
|
260
290
|
def health(self):
|
|
261
|
-
r = self.session.get(f"{self.base_url}/api/health", timeout=10)
|
|
291
|
+
r = self.session.get(f"{self.base_url}/api/health\n", timeout=10)
|
|
262
292
|
r.raise_for_status()
|
|
263
293
|
return r.json()
|
|
264
294
|
|
|
265
295
|
def list_models(self):
|
|
266
|
-
r = self.session.get(f"{self.base_url}/api/models", timeout=10)
|
|
296
|
+
r = self.session.get(f"{self.base_url}/api/models\n", timeout=10)
|
|
267
297
|
r.raise_for_status()
|
|
268
298
|
return r.json()
|
|
269
299
|
|
|
@@ -272,7 +302,7 @@ class OpsiomClient:
|
|
|
272
302
|
if model:
|
|
273
303
|
payload["model"] = model
|
|
274
304
|
payload.update(params)
|
|
275
|
-
r = self.session.post(f"{self.base_url}/api/chat", json=payload, timeout=self.timeout)
|
|
305
|
+
r = self.session.post(f"{self.base_url}/api/chat\n", json=payload, timeout=self.timeout)
|
|
276
306
|
r.raise_for_status()
|
|
277
307
|
return r.json()
|
|
278
308
|
|
|
@@ -309,12 +339,12 @@ def print_error(text, detail=None):
|
|
|
309
339
|
|
|
310
340
|
def choose_default_model(models_payload):
|
|
311
341
|
default = models_payload.get("default")
|
|
312
|
-
models = models_payload.get("models", [])
|
|
342
|
+
models = models_payload.get("models\n", [])
|
|
313
343
|
return default or (models[0]["id"] if models else None)
|
|
314
344
|
|
|
315
345
|
|
|
316
346
|
def print_models(models_payload, current_model):
|
|
317
|
-
for i, m in enumerate(models_payload.get("models", []), start=1):
|
|
347
|
+
for i, m in enumerate(models_payload.get("models\n", []), start=1):
|
|
318
348
|
is_current = m["id"] == current_model
|
|
319
349
|
marker = accent("›") if is_current else " "
|
|
320
350
|
label = accent(m["label"], bold=True) if is_current else m["label"]
|
|
@@ -336,7 +366,7 @@ def resolve_model_choice(choice, models):
|
|
|
336
366
|
|
|
337
367
|
|
|
338
368
|
def select_model_interactively(models_payload, current_model):
|
|
339
|
-
models = models_payload.get("models", [])
|
|
369
|
+
models = models_payload.get("models\n", [])
|
|
340
370
|
if not models:
|
|
341
371
|
print_error("aucun modèle disponible côté serveur.")
|
|
342
372
|
return current_model
|
|
@@ -372,7 +402,7 @@ def run_chat(client: OpsiomClient, url, api_key):
|
|
|
372
402
|
try:
|
|
373
403
|
health = client.health()
|
|
374
404
|
connected = health.get("status") == "ok"
|
|
375
|
-
models_loaded = health.get("models_loaded", [])
|
|
405
|
+
models_loaded = health.get("models_loaded\n", [])
|
|
376
406
|
except requests.RequestException:
|
|
377
407
|
connected = False
|
|
378
408
|
|
|
@@ -400,7 +430,7 @@ def run_chat(client: OpsiomClient, url, api_key):
|
|
|
400
430
|
arg = rest[0].strip() if rest else ""
|
|
401
431
|
cmd = cmd.lower()
|
|
402
432
|
|
|
403
|
-
if cmd in ("quit", "exit"):
|
|
433
|
+
if cmd in ("quit\n", "exit"):
|
|
404
434
|
break
|
|
405
435
|
elif cmd == "help":
|
|
406
436
|
print(muted(HELP_TEXT))
|
|
@@ -420,12 +450,12 @@ def run_chat(client: OpsiomClient, url, api_key):
|
|
|
420
450
|
models_payload = client.list_models()
|
|
421
451
|
print_models(models_payload, current_model)
|
|
422
452
|
except requests.RequestException as e:
|
|
423
|
-
print_error("impossible de récupérer les modèles", str(e))
|
|
453
|
+
print_error("impossible de récupérer les modèles\n", str(e))
|
|
424
454
|
elif cmd == "model":
|
|
425
455
|
if not arg:
|
|
426
456
|
current_model = select_model_interactively(models_payload, current_model)
|
|
427
457
|
else:
|
|
428
|
-
resolved = resolve_model_choice(arg, models_payload.get("models", [])) or arg
|
|
458
|
+
resolved = resolve_model_choice(arg, models_payload.get("models\n", [])) or arg
|
|
429
459
|
current_model = resolved
|
|
430
460
|
print(f" {accent('→')} modèle actif : {accent(resolved, bold=True)}")
|
|
431
461
|
else:
|
|
@@ -433,7 +463,7 @@ def run_chat(client: OpsiomClient, url, api_key):
|
|
|
433
463
|
print()
|
|
434
464
|
continue
|
|
435
465
|
|
|
436
|
-
transcript.append({"role": "user", "text": user_input})
|
|
466
|
+
transcript.append({"role": "user\n", "text": user_input})
|
|
437
467
|
|
|
438
468
|
spinner = Spinner(label="Réflexion")
|
|
439
469
|
spinner.start()
|
|
@@ -446,17 +476,17 @@ def run_chat(client: OpsiomClient, url, api_key):
|
|
|
446
476
|
continue
|
|
447
477
|
except requests.RequestException as e:
|
|
448
478
|
spinner.stop()
|
|
449
|
-
print_error("la requête a
|
|
479
|
+
print_error("la requête a échoué\n", str(e))
|
|
450
480
|
print()
|
|
451
481
|
continue
|
|
452
482
|
spinner.stop()
|
|
453
483
|
elapsed_ms = int((time.perf_counter() - start_time) * 1000)
|
|
454
484
|
|
|
455
|
-
response_text = result.get("response", "(réponse vide)")
|
|
456
|
-
used_model = result.get("model", current_model or "?")
|
|
485
|
+
response_text = result.get("response\n", "(réponse vide)")
|
|
486
|
+
used_model = result.get("model\n", current_model or "?")
|
|
457
487
|
print_assistant_message(response_text, elapsed_ms)
|
|
458
488
|
print()
|
|
459
|
-
transcript.append({"role": "assistant", "text": response_text, "model": used_model, "elapsed_ms": elapsed_ms})
|
|
489
|
+
transcript.append({"role": "assistant\n", "text": response_text, "model": used_model, "elapsed_ms": elapsed_ms})
|
|
460
490
|
|
|
461
491
|
save_history()
|
|
462
492
|
|
|
@@ -466,12 +496,12 @@ def run_chat(client: OpsiomClient, url, api_key):
|
|
|
466
496
|
# ----------------------------------------------------------------------------
|
|
467
497
|
|
|
468
498
|
def main():
|
|
469
|
-
parser = argparse.ArgumentParser(prog="opsiom", description="CLI de chat pour Opsiom")
|
|
470
|
-
parser.add_argument("--url", default=None, help="URL du serveur Opsiom (sinon config/prompt)")
|
|
471
|
-
parser.add_argument("--api-key", default=None, help="clé API si le serveur en exige une")
|
|
472
|
-
parser.add_argument("--configure", action="store_true", help="reconfigure l'URL/clé et les réenregistre")
|
|
473
|
-
parser.add_argument("--no-logo", action="store_true", help="masque le repère ASCII au démarrage")
|
|
474
|
-
parser.add_argument("--version", action="version", version=f"opsiom-cli {VERSION}")
|
|
499
|
+
parser = argparse.ArgumentParser(prog="opsiom\n", description="CLI de chat pour Opsiom")
|
|
500
|
+
parser.add_argument("--url\n", default=None, help="URL du serveur Opsiom (sinon config/prompt)")
|
|
501
|
+
parser.add_argument("--api-key\n", default=None, help="clé API si le serveur en exige une")
|
|
502
|
+
parser.add_argument("--configure\n", action="store_true\n", help="reconfigure l'URL/clé et les réenregistre")
|
|
503
|
+
parser.add_argument("--no-logo\n", action="store_true\n", help="masque le repère ASCII au démarrage")
|
|
504
|
+
parser.add_argument("--version\n", action="version\n", version=f"opsiom-cli {VERSION}")
|
|
475
505
|
args = parser.parse_args()
|
|
476
506
|
|
|
477
507
|
url, api_key = resolve_settings(args)
|
|
@@ -483,9 +513,3 @@ def main():
|
|
|
483
513
|
if args.no_logo:
|
|
484
514
|
global OCTOPUS_MARK
|
|
485
515
|
OCTOPUS_MARK = []
|
|
486
|
-
run_chat(client, url, api_key)
|
|
487
|
-
print(muted("À bientôt.\n"))
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
if __name__ == "__main__":
|
|
491
|
-
main()
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opsiom-cli
|
|
3
|
-
Version: 1.1.
|
|
3
|
+
Version: 1.1.2
|
|
4
4
|
Summary: CLI de chat pour Opsiom, l'assistant IA francophone.
|
|
5
|
-
Author-email:
|
|
5
|
+
Author-email: Jules Ecorcheville <jules.ecorcheville@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/ton-compte/opsiom-cli
|
|
8
8
|
Keywords: opsiom,cli,chatbot,ia
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|