opsiom-cli 1.1.2__tar.gz → 1.1.4__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: opsiom-cli
3
- Version: 1.1.2
3
+ Version: 1.1.4
4
4
  Summary: CLI de chat pour Opsiom, l'assistant IA francophone.
5
5
  Author-email: Jules Ecorcheville <jules.ecorcheville@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "opsiom-cli"
7
- version = "1.1.2"
7
+ version = "1.1.4"
8
8
  description = "CLI de chat pour Opsiom, l'assistant IA francophone."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -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.2"
42
+ VERSION = "1.1.4"
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,36 +88,36 @@ def ok_text(text):
88
88
  # ----------------------------------------------------------------------------
89
89
 
90
90
  OCTOPUS_MARK = [
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",
91
+ " . +**=::::::::=**+. ",
92
+ " *#....::::::::::::::.:#* ",
93
+ " .=*:..::.::::::::::::::::::::*=. ",
94
+ " .=*:......::::::::::::::::::::::-*=. ",
95
+ " .+-.:...:::::::::::::::::::::::::::=*. ",
96
+ " -*.::::::::::::::::::::::::::::::::::-*=. ",
97
+ " #= =*.:...::::::::::::::::::::::::::::::::-*+ ",
98
+ " .. -= .*.:::::::::::::::::::::::::::::::::::::--*. ",
99
+ " +: - *.:::::::::::::::::::::::::::::::::::::::--* ",
100
+ " :==-. :=.:::::::::::::::::::::::::::::::::::::::--+: ",
101
+ " .-:: +:::::::::::::::::::::::::::::::::::::::::---* ",
102
+ " * -- +:::::::::::::::::::::::::::::::::::::::::---+ ",
103
+ " *::::::::::*## :+::::::::::::+: ##*::::::----* ",
104
+ " :::. :+:::::::::**###*-::::::::::-*###**::::::---+: ",
105
+ " ==...:++. *.::::::::===++=::::::::::::=++===:::::----* ",
106
+ " +::++::::=..*::::::-----:::::-*+::+*-:::::-----::----* ",
107
+ " .: :*=::+: :+::::::::::::::::::--:::::::::::::::---* ",
108
+ " .#+::+: .*:::::::::::::::::::::::::::::::::---*. ",
109
+ " -#**: *:+::+- .+::::::::::::::::::::::::::::::----+. ",
110
+ " -*-::.*-*=*:::=*::::=+-:::::::::::::::::::::::::::--+=:::-*+---*=*-*:::-*- ",
111
+ " :+=::::+#---::::-----+=::::::::::::::::::::::::::::++------------#+::::=+: ",
112
+ " -+=-::..=**=--+*--*+-::::::::::::::::::::::::::::::-+*--*+--=**=..::--+- ",
113
+ " ==*-::::..:-*#:::-+:::::-*::::::::::::::::::*-:::::+::::#*-:..::::-*== ",
114
+ " +-:+-:::::*-.=::-+:::--#:.:::::::-=:::::::::#--:::+:::=.-*:::::-+:-+ ",
115
+ " +-:*---:+-.-::-*+==**:.:::::::-##-:::::::.:**==+*:::-.-+:---*:-+ ",
116
+ " =*=:--+=:#::::::::.::::::::-===+-::::::::.::.:::::#:=+-=:-*= ",
117
+ " .--=*-..+::::::::::::::--+- -+--::::::::::::::+..-*=--. ",
118
+ " +:::+-:::::::::---=*- -#=---:::::::::-*:::+ ",
119
+ " .#-::-+-------+**- -+*+-------+-::-#: ",
120
+ " **-::--::-*+ =*=::--::-** ",
121
121
  ]
122
122
 
123
123
 
@@ -220,14 +220,14 @@ def save_transcript(transcript, filename=None):
220
220
  if not filename.endswith(".md"):
221
221
  filename += ".md"
222
222
  path = TRANSCRIPTS_DIR / filename
223
- lines = ["# Conversation Opsiom\n", ""]
223
+ lines = ["# Conversation Opsiom ", ""]
224
224
  for entry in transcript:
225
225
  if entry["role"] == "user":
226
226
  lines.append(f"**Vous :** {entry['text']}")
227
227
  else:
228
228
  lines.append(f"**Opsiom ({entry['model']}) :** {entry['text']} _({entry['elapsed_ms']} ms)_")
229
229
  lines.append("")
230
- path.write_text("\n".join(lines), encoding="utf-8")
230
+ path.write_text(" ".join(lines), encoding="utf-8")
231
231
  return path
232
232
 
233
233
 
@@ -251,14 +251,14 @@ def save_config(config):
251
251
 
252
252
  def prompt_for_config():
253
253
  print(f" {accent('Configuration initiale', bold=True)}")
254
- print(f" {muted('ces informations te sont transmises par la personne qui héberge Opsiom')}\n")
254
+ print(f" {muted('ces informations te sont transmises par la personne qui héberge Opsiom')} ")
255
255
  url = input(" URL du serveur : ").strip().rstrip("/")
256
256
  api_key = input(" Clé API (vide si aucune) : ").strip()
257
257
  save = input(muted(" Sauvegarder localement pour la prochaine fois ? [O/n] ")).strip().lower()
258
258
  config = {"url": url, "api_key": api_key}
259
- if save in ("\n", "o\n", "oui\n", "y\n", "yes"):
259
+ if save in (" ", "o ", "oui ", "y ", "yes"):
260
260
  save_config(config)
261
- print(muted(f" → enregistré dans {CONFIG_PATH}\n"))
261
+ print(muted(f" → enregistré dans {CONFIG_PATH} "))
262
262
  return config
263
263
 
264
264
 
@@ -288,12 +288,12 @@ class OpsiomClient:
288
288
  self.session.headers.update(headers)
289
289
 
290
290
  def health(self):
291
- r = self.session.get(f"{self.base_url}/api/health\n", timeout=10)
291
+ r = self.session.get(f"{self.base_url}/api/health ", timeout=10)
292
292
  r.raise_for_status()
293
293
  return r.json()
294
294
 
295
295
  def list_models(self):
296
- r = self.session.get(f"{self.base_url}/api/models\n", timeout=10)
296
+ r = self.session.get(f"{self.base_url}/api/models ", timeout=10)
297
297
  r.raise_for_status()
298
298
  return r.json()
299
299
 
@@ -302,7 +302,7 @@ class OpsiomClient:
302
302
  if model:
303
303
  payload["model"] = model
304
304
  payload.update(params)
305
- r = self.session.post(f"{self.base_url}/api/chat\n", json=payload, timeout=self.timeout)
305
+ r = self.session.post(f"{self.base_url}/api/chat ", json=payload, timeout=self.timeout)
306
306
  r.raise_for_status()
307
307
  return r.json()
308
308
 
@@ -339,12 +339,12 @@ def print_error(text, detail=None):
339
339
 
340
340
  def choose_default_model(models_payload):
341
341
  default = models_payload.get("default")
342
- models = models_payload.get("models\n", [])
342
+ models = models_payload.get("models ", [])
343
343
  return default or (models[0]["id"] if models else None)
344
344
 
345
345
 
346
346
  def print_models(models_payload, current_model):
347
- for i, m in enumerate(models_payload.get("models\n", []), start=1):
347
+ for i, m in enumerate(models_payload.get("models ", []), start=1):
348
348
  is_current = m["id"] == current_model
349
349
  marker = accent("›") if is_current else " "
350
350
  label = accent(m["label"], bold=True) if is_current else m["label"]
@@ -366,12 +366,12 @@ def resolve_model_choice(choice, models):
366
366
 
367
367
 
368
368
  def select_model_interactively(models_payload, current_model):
369
- models = models_payload.get("models\n", [])
369
+ models = models_payload.get("models ", [])
370
370
  if not models:
371
371
  print_error("aucun modèle disponible côté serveur.")
372
372
  return current_model
373
373
  print_models(models_payload, current_model)
374
- choice = input(f"\n {muted('Numéro (Entrée pour annuler) :')} ").strip()
374
+ choice = input(f" {muted('Numéro (Entrée pour annuler) :')} ").strip()
375
375
  if not choice:
376
376
  return current_model
377
377
  resolved = resolve_model_choice(choice, models)
@@ -386,7 +386,7 @@ def print_assistant_message(text, elapsed_ms):
386
386
  width = max(term_width() - 4, 30)
387
387
  wrapped = textwrap.fill(text, width=width)
388
388
  print()
389
- for line in wrapped.split("\n"):
389
+ for line in wrapped.split(" "):
390
390
  print(f" {line}")
391
391
  print(f" {muted(format_elapsed(elapsed_ms))}")
392
392
 
@@ -402,7 +402,7 @@ def run_chat(client: OpsiomClient, url, api_key):
402
402
  try:
403
403
  health = client.health()
404
404
  connected = health.get("status") == "ok"
405
- models_loaded = health.get("models_loaded\n", [])
405
+ models_loaded = health.get("models_loaded ", [])
406
406
  except requests.RequestException:
407
407
  connected = False
408
408
 
@@ -413,7 +413,7 @@ def run_chat(client: OpsiomClient, url, api_key):
413
413
  pass
414
414
 
415
415
  print_header(url, connected, api_key, models_loaded, current_model)
416
- print(f" {muted('Tapez votre message, ou /help pour la liste des commandes.')}\n")
416
+ print(f" {muted('Tapez votre message, ou /help pour la liste des commandes.')} ")
417
417
 
418
418
  while True:
419
419
  try:
@@ -430,7 +430,7 @@ def run_chat(client: OpsiomClient, url, api_key):
430
430
  arg = rest[0].strip() if rest else ""
431
431
  cmd = cmd.lower()
432
432
 
433
- if cmd in ("quit\n", "exit"):
433
+ if cmd in ("quit ", "exit"):
434
434
  break
435
435
  elif cmd == "help":
436
436
  print(muted(HELP_TEXT))
@@ -450,12 +450,12 @@ def run_chat(client: OpsiomClient, url, api_key):
450
450
  models_payload = client.list_models()
451
451
  print_models(models_payload, current_model)
452
452
  except requests.RequestException as e:
453
- print_error("impossible de récupérer les modèles\n", str(e))
453
+ print_error("impossible de récupérer les modèles ", str(e))
454
454
  elif cmd == "model":
455
455
  if not arg:
456
456
  current_model = select_model_interactively(models_payload, current_model)
457
457
  else:
458
- resolved = resolve_model_choice(arg, models_payload.get("models\n", [])) or arg
458
+ resolved = resolve_model_choice(arg, models_payload.get("models ", [])) or arg
459
459
  current_model = resolved
460
460
  print(f" {accent('→')} modèle actif : {accent(resolved, bold=True)}")
461
461
  else:
@@ -463,7 +463,7 @@ def run_chat(client: OpsiomClient, url, api_key):
463
463
  print()
464
464
  continue
465
465
 
466
- transcript.append({"role": "user\n", "text": user_input})
466
+ transcript.append({"role": "user ", "text": user_input})
467
467
 
468
468
  spinner = Spinner(label="Réflexion")
469
469
  spinner.start()
@@ -472,21 +472,21 @@ def run_chat(client: OpsiomClient, url, api_key):
472
472
  result = client.chat(user_input, model=current_model)
473
473
  except KeyboardInterrupt:
474
474
  spinner.stop()
475
- print(muted(" (requête annulée)\n"))
475
+ print(muted(" (requête annulée) "))
476
476
  continue
477
477
  except requests.RequestException as e:
478
478
  spinner.stop()
479
- print_error("la requête a échoué\n", str(e))
479
+ print_error("la requête a échoué ", str(e))
480
480
  print()
481
481
  continue
482
482
  spinner.stop()
483
483
  elapsed_ms = int((time.perf_counter() - start_time) * 1000)
484
484
 
485
- response_text = result.get("response\n", "(réponse vide)")
486
- used_model = result.get("model\n", current_model or "?")
485
+ response_text = result.get("response ", "(réponse vide)")
486
+ used_model = result.get("model ", current_model or "?")
487
487
  print_assistant_message(response_text, elapsed_ms)
488
488
  print()
489
- transcript.append({"role": "assistant\n", "text": response_text, "model": used_model, "elapsed_ms": elapsed_ms})
489
+ transcript.append({"role": "assistant ", "text": response_text, "model": used_model, "elapsed_ms": elapsed_ms})
490
490
 
491
491
  save_history()
492
492
 
@@ -496,12 +496,12 @@ def run_chat(client: OpsiomClient, url, api_key):
496
496
  # ----------------------------------------------------------------------------
497
497
 
498
498
  def main():
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}")
499
+ parser = argparse.ArgumentParser(prog="opsiom ", description="CLI de chat pour Opsiom")
500
+ parser.add_argument("--url ", default=None, help="URL du serveur Opsiom (sinon config/prompt)")
501
+ parser.add_argument("--api-key ", default=None, help="clé API si le serveur en exige une")
502
+ parser.add_argument("--configure ", action="store_true ", help="reconfigure l'URL/clé et les réenregistre")
503
+ parser.add_argument("--no-logo ", action="store_true ", help="masque le repère ASCII au démarrage")
504
+ parser.add_argument("--version ", action="version ", version=f"opsiom-cli {VERSION}")
505
505
  args = parser.parse_args()
506
506
 
507
507
  url, api_key = resolve_settings(args)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: opsiom-cli
3
- Version: 1.1.2
3
+ Version: 1.1.4
4
4
  Summary: CLI de chat pour Opsiom, l'assistant IA francophone.
5
5
  Author-email: Jules Ecorcheville <jules.ecorcheville@gmail.com>
6
6
  License-Expression: MIT
File without changes
File without changes