ollama_chat 0.0.87 → 0.0.89

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.
@@ -34,7 +34,7 @@ class OllamaChat::Chat
34
34
  include Tins::GO
35
35
  include Term::ANSIColor
36
36
  include OllamaChat::HTTPHandling
37
- include OllamaChat::CommandConcern
37
+ include OllamaChat::Commands
38
38
  include OllamaChat::Logging
39
39
  include OllamaChat::DocumentCache
40
40
  include OllamaChat::Switches
@@ -122,6 +122,7 @@ class OllamaChat::Chat
122
122
  @tool_call_results = Hash.new { |h, name| h[name] = [] }
123
123
  setup_personae_directory
124
124
  @opts[?S] and init_server_socket
125
+ info_session
125
126
  rescue ComplexConfig::AttributeMissing, ComplexConfig::ConfigurationSyntaxError => e
126
127
  fix_config(e)
127
128
  end
@@ -281,827 +282,6 @@ class OllamaChat::Chat
281
282
  !!@parse_content
282
283
  end
283
284
 
284
- # Chat commands
285
-
286
- ## Clipboard
287
-
288
- command(
289
- name: :copy,
290
- regexp: %r(^/copy(?:\s+(edit))?$),
291
- complete: [ 'copy', %w[ edit ] ],
292
- optional: true,
293
- help: 'Copy the last response to the clipboard (optionally edit it first)'
294
- ) do |subcommand|
295
- copy_to_clipboard(edit: subcommand == 'edit')
296
- :next
297
- end
298
-
299
- command(
300
- name: :paste,
301
- regexp: %r(^/paste(?:\s+(edit))?$),
302
- complete: [ 'paste', %w[ edit ] ],
303
- optional: true,
304
- help: 'Paste content from the clipboard (optionally edit it afterwards)'
305
- ) do |subcommand|
306
- disable_content_parsing
307
- paste_from_clipboard(edit: subcommand == 'edit')
308
- end
309
-
310
- ## Settings
311
-
312
- command(
313
- name: :config,
314
- regexp: %r(^/config(?:\s+(edit|reload))?$),
315
- complete: [ 'config', %w[ edit reload ] ],
316
- optional: true,
317
- help: 'View, edit, or reload configuration'
318
- ) do |subcommand|
319
- case subcommand
320
- when 'edit'
321
- edit_config
322
- when 'reload'
323
- reload_config
324
- else
325
- display_config
326
- end
327
- :next
328
- end
329
-
330
- command(
331
- name: :document_policy,
332
- regexp: %r(^/document policy$),
333
- complete: %w[ document policy ],
334
- help: 'Select a scanning policy for documents'
335
- ) do
336
- document_policy.choose
337
- :next
338
- end
339
-
340
- command(
341
- name: :toggle,
342
- regexp: %r(^/toggle(?:\s+(markdown|stream|location|runtime_info|voice|think_loud|think_strip))?$),
343
- complete: [ 'toggle', %w[ markdown stream location runtime_info voice think_loud think_strip embedding ] ],
344
- help: 'Toggle feature switches (markdown, stream, location, runtime_info, voice, think_loud, think_strip, embedding)'
345
- ) do |toggle_name|
346
- if toggle_name
347
- send(toggle_name).toggle
348
- else
349
- STDOUT.puts "Available toggles: markdown|stream|location|runtime_info|voice|think_loud|embedding"
350
- end
351
- :next
352
- end
353
-
354
- command(
355
- name: :toggle_embedding,
356
- regexp: %r(^/toggle\s+embedding$),
357
- complete: [],
358
- help: nil
359
- ) do
360
- embedding_paused.toggle(show: false)
361
- embedding.show
362
- :next
363
- end
364
-
365
- command(
366
- name: :favourite,
367
- regexp: %r(^/favourite(?:\s+(add|delete))?(?:\s+(model|prompt|system_prompt|persona))$),
368
- complete: [ 'favourite', %w[ add delete ], %w[ model prompt system_prompt persona ] ],
369
- help: 'Manage favorites for models, prompts, and personas (add, delete)'
370
- ) do |subcommand, type|
371
- case subcommand
372
- when 'add'
373
- add_favourite(type)
374
- when 'delete'
375
- delete_favourite(type)
376
- end
377
- :next
378
- end
379
-
380
- command(
381
- name: :model,
382
- regexp: %r(^/model(?:\s+(change|options|options from session|options to session))$),
383
- complete: [ 'model', %w[ change options options\ from\ session options\ to\ session ] ],
384
- help: <<~EOT
385
- Change the model or manage model options (change, options, options from
386
- session, options to session)
387
- EOT
388
- ) do |subcommand|
389
- case subcommand
390
- when 'change'
391
- begin
392
- use_model
393
- rescue OllamaChat::UnknownModelError => e
394
- msg = "Caught #{e.class}: #{e}"
395
- log(:error, msg, warn: true)
396
- end
397
- when 'options'
398
- edit_model_options(@model)
399
- when 'options from session'
400
- copy_model_options_from_session
401
- when 'options to session'
402
- copy_model_options_to_session
403
- end
404
- :next
405
- end
406
-
407
- command(
408
- name: :system,
409
- regexp: %r(^/system(?:\s+(change|info|edit|add|delete|list|duplicate|export|import|reset))?(?:\s+(\S+))?$),
410
- complete: [ 'system', %w[ change info edit add delete list duplicate export import reset ] ],
411
- optional: true,
412
- help: <<~EOT
413
- Manage the system prompt (change, info, edit, add, delete, list, duplicate,
414
- export, import, reset)
415
- EOT
416
- ) do |subcommand, filename|
417
- case subcommand
418
- when 'add'
419
- add_new_system_prompt and @messages.show_system_prompt
420
- when 'delete'
421
- choose_and_delete_system_prompt
422
- when 'edit'
423
- choose_and_edit_system_prompt
424
- when 'list'
425
- list_system_prompts
426
- when 'change'
427
- change_system_prompt(@system)
428
- @messages.show_system_prompt
429
- when 'duplicate'
430
- duplicate_system_prompt
431
- when 'import'
432
- import_system_prompt(filename)
433
- when 'export'
434
- export_system_prompt
435
- when 'info'
436
- info_system_prompt
437
- when 'reset'
438
- if prompt = choose_system_prompt
439
- if reset_system_prompt_to_default(prompt.name)
440
- STDOUT.puts "Reset system prompt #{bold{prompt.name}} to default."
441
- else
442
- STDOUT.puts "No default value found for system prompt #{bold{prompt.name}}."
443
- end
444
- end
445
- when nil
446
- @messages.show_system_prompt
447
- end
448
- :next
449
- end
450
-
451
- command(
452
- name: :think,
453
- regexp: %r(^/think$),
454
- help: 'Configure the think mode for models'
455
- ) do
456
- think_mode.choose
457
- :next
458
- end
459
-
460
- command(
461
- name: :tools,
462
- regexp: %r(^/tools(?:\s+(on|off|enable|disable))?),
463
- complete: [ 'tools', %w[ on off enable disable ] ],
464
- optional: true,
465
- help: 'Manage tool support and enabled tools (on, off, enable, disable)'
466
- ) do |subcommand|
467
- case subcommand
468
- when nil
469
- list_tools
470
- when 'enable'
471
- enable_tool
472
- when 'disable'
473
- disable_tool
474
- when 'on'
475
- tools_support.set(true, show: true)
476
- when 'off'
477
- tools_support.set(false, show: true)
478
- end
479
- :next
480
- end
481
-
482
- command(
483
- name: :voice,
484
- regexp: %r(^/voice$),
485
- help: 'Change the voice output settings'
486
- ) do
487
- change_voice
488
- :next
489
- end
490
-
491
- ## Session
492
-
493
- command(
494
- name: :session,
495
- regexp: %r(^/session(?:\s+(change|previous|list|new|duplicate|rename|summarize|delete|model options))?((?:\s+-(?:[sf]))*)(?:\s+(.+))?$),
496
- complete: [ 'session', %w[ change previous list new duplicate rename summarize delete model\ options ] ],
497
- optional: true,
498
- options: '[-s|-f] [name]',
499
- help: <<~EOT
500
- Manage chat sessions (change, previous, list, new, duplicate, rename, summarize,
501
- delete, model options).
502
- For summarize: -s (single sentence), -f (output to markdown file)
503
- EOT
504
- ) do |subcommand, opts, name|
505
- case subcommand
506
- when nil
507
- show_session
508
- when 'list'
509
- list_sessions
510
- when 'new'
511
- set_new_session
512
- when 'duplicate'
513
- duplicate_session
514
- when 'delete'
515
- delete_session
516
- when 'rename'
517
- rename_session
518
- when 'summarize'
519
- opts = go_command('fs', opts)
520
- if opts[?f] and
521
- filename = ask?(prompt: "❓ Enter filename: ").full? { Pathname.new(_1) }
522
- then
523
- if filename.exist? && !confirm?(
524
- prompt: "🔔 File #{filename.to_s.inspect} already exists, overwrite? (y/n) ",
525
- yes: /\Ay/i
526
- )
527
- then
528
- STDERR.puts "File not written!"
529
- next :next
530
- end
531
- summary = summarize_session(pretty: true, sentence: opts[?s]) do |content|
532
- infobar.puts kramdown_ansi_parse(content)
533
- end
534
- if summary.full?
535
- filename.write(summary)
536
- STDOUT.puts "File successfully written."
537
- else
538
- STDERR.puts "Nothing to summarize!"
539
- next :next
540
- end
541
- end
542
- summary = summarize_session(pretty: true, sentence: opts[?s]) do |content|
543
- infobar.puts kramdown_ansi_parse(content) << ?\n
544
- end
545
- if summary.full?
546
- use_pager do |output|
547
- output.puts kramdown_ansi_parse(summary)
548
- end
549
- else
550
- STDERR.puts "Nothing to summarize!"
551
- next :next
552
- end
553
- when 'change'
554
- change_session(name)
555
- when 'model options'
556
- edit_session_model_options
557
- when 'previous'
558
- if prev = previous_session
559
- change_session(prev.id)
560
- else
561
- STDOUT.puts "No previous session defined."
562
- end
563
- end
564
- :next
565
- end
566
-
567
- ## Conversation
568
-
569
- command(
570
- name: :list,
571
- regexp: %r(^/list(?:\s+(\d*))?$),
572
- options: '[n=1]',
573
- help: 'List the last n or all conversation exchanges'
574
- ) do
575
- n = 2 * _1.to_i if _1
576
- messages.list_conversation(n)
577
- :next
578
- end
579
-
580
- command(
581
- name: :last,
582
- regexp: %r(^/last(?:\s+(-p))?(?:\s+(\d*))?$),
583
- options: '[-p|n=1]',
584
- help: <<~EOT
585
- Show the last n or the most recent system/assistant message (-p for plain
586
- output = no pager)
587
- EOT
588
- ) do |opts,number|
589
- opts = go_command('p', opts.to_s)
590
- n = number.to_i.clamp(1..)
591
- messages.show_last(n, pager: !opts[?p])
592
- :next
593
- end
594
-
595
- command(
596
- name: :drop,
597
- regexp: %r(^/drop(?:\s+(\d*))?$),
598
- options: '[n=1]',
599
- help: 'Remove the last n conversation exchanges'
600
- ) do
601
- messages.drop(_1)
602
- messages.show_last
603
- :next
604
- end
605
-
606
- command(
607
- name: :clear,
608
- regexp: %r(^/clear(?:\s+(messages|images|links|history|tags|all))?$),
609
- complete: [ 'clear', %w[ messages images links history tags all ] ],
610
- optional: true,
611
- help: 'Clear messages, images, links, history, tags or all'
612
- ) do |subcommand|
613
- if result = clean(subcommand)
614
- disable_content_parsing
615
- result
616
- else
617
- :next
618
- end
619
- end
620
-
621
- command(
622
- name: :links,
623
- regexp: %r(^/links(?:\s+(clear))?$),
624
- complete: [ 'links', %w[ clear ] ],
625
- optional: true,
626
- help: 'Clear links used in the chat',
627
- ) do |subcommand|
628
- manage_links(subcommand)
629
- :next
630
- end
631
-
632
- command(
633
- name: :regenerate,
634
- regexp: %r(^/regenerate(?:\s+(edit))?$),
635
- complete: [ 'regenerate', %w[ edit ] ],
636
- optional: true,
637
- help: 'Regenerate the last response (optionally edit the user message)'
638
- ) do |subcommand|
639
- if message = messages.find_last { !_1.tool? && _1.role == 'user' }
640
- content = message.content.to_s
641
- messages.drop(1)
642
- if subcommand == 'edit'
643
- content = edit_text(content)
644
- end
645
- else
646
- STDOUT.puts "Not enough messages in this conversation."
647
- next :redo
648
- end
649
- disable_content_parsing
650
- content
651
- end
652
-
653
- command(
654
- name: :prompt,
655
- regexp: %r(^/prompt(?:\s+(edit|info|add|delete|list|duplicate|import|export|reset))?(?:\s+(\S+))?$),
656
- complete: [ 'prompt', %w[ edit info add delete list duplicate import export reset ] ],
657
- optional: true,
658
- help: <<~EOT,
659
- Manage preset prompt templates or prefill the prompt (edit, info, add,
660
- delete, list, duplicate, import, export, reset)
661
- EOT
662
- ) do |subcommand, filename|
663
- case subcommand
664
- when 'add'
665
- add_new_prompt
666
- when 'delete'
667
- choose_and_delete_prompt
668
- when 'edit'
669
- choose_and_edit_prompt
670
- when 'list'
671
- list_prompts
672
- when 'duplicate'
673
- duplicate_prompt
674
- when 'import'
675
- import_prompt(filename)
676
- when 'export'
677
- export_prompt
678
- when 'info'
679
- info_prompt
680
- when 'reset'
681
- if prompt = choose_prompt(default: true)
682
- if reset_prompt_to_default(prompt.name)
683
- STDOUT.puts "Reset prompt #{bold{prompt.name}} to default."
684
- else
685
- STDOUT.puts "No default value found for prompt #{bold{prompt.name}}."
686
- end
687
- end
688
- when nil
689
- @prefill_prompt = choose_prompt&.to_s
690
- end
691
- :next
692
- end
693
-
694
- command(
695
- name: :change_response,
696
- regexp: %r(^/change response$),
697
- complete: %w[ change response ],
698
- help: 'Edit the last assistant response in the editor',
699
- ) do
700
- change_response
701
- :next
702
- end
703
-
704
- command(
705
- name: :conversation,
706
- regexp: %r(^/conversation\s+(save|load)((?:\s+-(?:[c]))*)\s+(.+)$),
707
- complete: [ 'conversation', %w[ save load ] ],
708
- options: '[-c]',
709
- help: 'Load conversations or save conversations (-c to clean first)'
710
- ) do |subcommand,opts,path|
711
- opts = go_command('c', opts.to_s)
712
- case subcommand
713
- when 'save'
714
- save_conversation(path, clean: opts[?c])
715
- when 'load'
716
- load_conversation(path)
717
- end
718
- :next
719
- end
720
-
721
- ## Collection
722
-
723
- command(
724
- name: :collection,
725
- regexp: %r(^/collection(?:\s+(change|clear|list|rename|update))?$),
726
- complete: [ 'collection', %w[ change clear list rename update ] ],
727
- optional: true,
728
- help: <<~EOT
729
- Manage the current RAG document collection: change, clear, list,
730
- rename, update and show
731
- EOT
732
- ) do |subcommand|
733
- case subcommand
734
- when 'clear'
735
- clear_collection
736
- when 'change'
737
- choose_collection(collection)
738
- when 'list'
739
- list_collections
740
- when 'rename'
741
- rename_collection(collection)
742
- when 'update'
743
- update_collection
744
- when nil
745
- collection_stats
746
- end
747
- :next
748
- end
749
-
750
- ## Persona
751
-
752
- command(
753
- name: :persona,
754
- regexp: %r(^/persona(?:\s+(play|load|edit|info|list|add|delete|backup|import|export|duplicate))?$),
755
- complete: [ 'persona', %w[ play load edit info list add delete backup import export duplicate ] ],
756
- optional: true,
757
- help: <<~EOT,
758
- Manage and activate personas for roleplay (play, load, edit, info, list,
759
- add, delete, backup, import, export, duplicate)
760
- EOT
761
- ) do |subcommand|
762
- disable_content_parsing
763
- case subcommand
764
- when 'add'
765
- add_persona
766
- :next
767
- when 'delete'
768
- delete_persona
769
- :next
770
- when 'edit'
771
- edit_persona
772
- :next
773
- when 'backup'
774
- backup_persona
775
- :next
776
- when 'duplicate'
777
- duplicate_persona
778
- :next
779
- when 'import'
780
- filename = choose_filename('**/*.md')
781
- if filename and name = import_persona(filename)
782
- STDOUT.puts "Imported persona as #{name.inspect}."
783
- end
784
- :next
785
- when 'export'
786
- export_persona
787
- :next
788
- when 'info'
789
- info_persona
790
- :next
791
- when 'list'
792
- list_personae
793
- :next
794
- when 'load'
795
- if result = load_personae
796
- result
797
- else
798
- :next
799
- end
800
- when 'play'
801
- set_default_persona
802
- :next
803
- else
804
- select_persona_path
805
- :next
806
- end
807
- end
808
-
809
- command(
810
- name: :character,
811
- regexp: %r(^/character(?:\s+(info|load|import))(?:\s+(\S+))?$),
812
- complete: [ 'character', %w[ info load import ] ],
813
- help: 'Display character info, load or import a character from JSON/PNG as persona'
814
- ) do |subcommand, path|
815
- path = if path
816
- Pathname.new(path)
817
- else
818
- choose_filename('**/*.{png,json}')
819
- end
820
- case
821
- when path.nil?
822
- STDOUT.puts 'Cancelled.'
823
- next :next
824
- when !path.exist?
825
- STDERR.puts "Path #{path.to_s.inspect} does not exist!"
826
- next :next
827
- end
828
- data = case path.extname
829
- when '.json'
830
- path.read
831
- when '.png'
832
- path.open do |io|
833
- OllamaChat::Utils::PNGCharacterExtractor.extract_character_json(io)
834
- end
835
- else
836
- STDERR.puts "Only json and png characters are supported!"
837
- next :next
838
- end
839
- json_to_yaml = -> d {
840
- yaml = YAML.dump(JSON(d)).sub(%r{\A---\n}, '')
841
- Kramdown::ANSI::Width.wrap(
842
- yaml,
843
- length: Tins::Terminal.columns * 0.9
844
- )
845
- }
846
- case subcommand
847
- when 'info'
848
- puts json_to_yaml.(data)
849
- :next
850
- when 'load'
851
- disable_content_parsing
852
- data
853
- when 'import'
854
- markdown = convert_json_character_to_markdown(data)
855
- Tempfile.create('character.md') do |tmp|
856
- tmp.puts markdown
857
- tmp.flush
858
- import_persona(Pathname.new(tmp.path))
859
- end
860
- :next
861
- end
862
- end
863
-
864
- ## Input
865
-
866
- command(
867
- name: :compose,
868
- regexp: %r(^/compose$),
869
- help: 'Compose a message using the text editor'
870
- ) do
871
- edit_text.full? or :next
872
- end
873
-
874
- command(
875
- name: :web,
876
- regexp: %r(^/web\s+(?:(\d+)\s+)?(.+)),
877
- options: '[number=1] query',
878
- help: 'Query the web for a specified number of results'
879
- ) do |count, query|
880
- disable_content_parsing
881
- web(count, query)
882
- end
883
-
884
- command(
885
- name: :input,
886
- regexp: %r(^/input(?:\s+(path|context|embedding|summary)(?:\s*(?=\z))?)?((?:\s+-(?:[apr]|c\s*\w+|w\s*\d+|t\s*[-\w\.]+(?:,[-\w\.]+)*))*)(?:\s+(.+))?$),
887
- optional: true,
888
- complete: [ 'input', %w[ path context embedding summary ] ],
889
- options: '[-w|-a|-p|-c <collection>|-t <tags>] [arg…]',
890
- help: <<~EOT
891
- Import content from files, URLs, or globs into the context
892
- Use subcommands: path, context, embedding, summary,
893
- import (the default).
894
- Options:
895
- -p (enable pattern mode to allow using globs/wildcards)
896
- -w <words> (summary subcommand only, default 100)
897
- -a (pattern mode only, include all files for patterns)
898
- -c <collection> use this collection (embedding subcommand only)
899
- -t <tag1,tag2,…> the custom tags to appy (embedding subcommand only)
900
- EOT
901
- ) do |input_mode,opts,arg|
902
- disable_content_parsing
903
- case input_mode
904
- when 'summary'
905
- opts = go_command('paw:', opts)
906
- if opts[?p]
907
- words = opts.fetch(?w, 100)
908
- all = opts.fetch(?a, false)
909
- arg and patterns = arg.scan(/(\S+)/).flatten
910
- next provide_file_set_content(patterns, all:) { summarize(_1, words:) } || :next
911
- elsif arg
912
- words = opts.fetch(?w, 100)
913
- source = arg
914
- next summarize(source, words:) || :next
915
- else
916
- STDERR.puts "Need a source to summarize for input!"
917
- next :next
918
- end
919
- when 'context'
920
- opts = go_command('pa', opts)
921
- if opts[?p]
922
- all = opts.fetch(?a, false)
923
- patterns = arg&.scan(/(\S+)/)&.flatten.full? || [ '**/*' ]
924
- next context_spook(patterns, all:) || :next
925
- elsif arg
926
- next context_spook(Array(arg.to_s), all: true) || :next
927
- else
928
- next context_spook(nil) || :next
929
- end
930
- when 'embedding'
931
- opts = go_command('pac:t:', opts)
932
- switch_collection(opts[?c]) do |other_collection|
933
- if collection == other_collection and !confirm?(
934
- prompt: "🔔 Are you sure to embed into current collection #{other_collection.to_s.inspect}? (y/n) ",
935
- yes: /\Ay/i
936
- )
937
- then
938
- STDOUT.puts 'Cancelled.'
939
- next :next
940
- end
941
- tags = opts[?t].full?(:split, ?,)
942
- if opts[?p]
943
- all = opts.fetch(?a, false)
944
- arg and patterns = arg.scan(/(\S+)/).flatten
945
- next provide_file_set_content(patterns, all:) { embed(_1, tags:) } || :next
946
- elsif arg
947
- next embed(arg, tags:) || :next
948
- else
949
- STDERR.puts "Need a source to embed for input!"
950
- next :next
951
- end
952
- end
953
- when 'path'
954
- opts = go_command('pa', opts)
955
- if opts[?p]
956
- all = opts.fetch(?a, false)
957
- arg and patterns = arg.scan(/(\S+)/).flatten
958
- read = -> pathname {
959
- STDOUT.puts "Reading #{pathname.to_s.inspect}."
960
- pathname.read
961
- }
962
- next provide_file_set_content(patterns, all:, &read) || :next
963
- elsif arg
964
- filename = Pathname.new(arg).expand_path
965
- next filename.file? && filename.read || :next
966
- else
967
- STDERR.puts "Need a filename to read for input!"
968
- next :next
969
- end
970
- else
971
- opts = go_command('pa', opts)
972
- if opts[?p]
973
- all = opts.fetch(?a, false)
974
- arg and patterns = arg.scan(/(\S+)/).flatten
975
- next provide_file_set_content(patterns, all:) { import(_1) } || :next
976
- elsif arg
977
- source = arg
978
- next import(source) || :next
979
- else
980
- STDERR.puts "Need a source to import for input!"
981
- next :next
982
- end
983
- end
984
- end
985
-
986
- ## Output
987
-
988
- command(
989
- name: :pipe,
990
- regexp: %r(^/pipe\s+(.+)$),
991
- options: 'path',
992
- help: 'Pipe the last response into another command\'s stdin',
993
- ) do |command|
994
- pipe(command)
995
- :next
996
- end
997
-
998
- command(
999
- name: :vim,
1000
- regexp: %r(^/vim(?:\s+(.+))?$),
1001
- help: 'Insert the last message into a Vim server buffer'
1002
- ) do |servername|
1003
- if message = messages.last
1004
- vim(servername).insert message.content
1005
- else
1006
- STDERR.puts "Warning: No message found to insert into Vim"
1007
- end
1008
- :next
1009
- end
1010
-
1011
- command(
1012
- name: :output,
1013
- regexp: %r(^/output\s+(.+)$),
1014
- options: 'path',
1015
- help: 'Save the last response to a file',
1016
- ) do |path|
1017
- output(path)
1018
- :next
1019
- end
1020
-
1021
- ## Actions
1022
-
1023
- command(
1024
- name: :reconnect,
1025
- regexp: %r(^/reconnect$),
1026
- help: 'Reconnect to the Ollama server'
1027
- ) do
1028
- STDERR.print green { "Reconnecting to ollama #{base_url.to_s.inspect}…" }
1029
- connect_ollama
1030
- STDERR.puts green { " Done." }
1031
- :next
1032
- end
1033
-
1034
- command(
1035
- name: :quit,
1036
- regexp: %r(^/(?:quit|exit)$),
1037
- complete: [ %w[ quit exit ] ],
1038
- help: 'Quit the application',
1039
- ) do
1040
- STDOUT.puts "Goodbye."
1041
- :return
1042
- end
1043
-
1044
- ## Information
1045
-
1046
- command(
1047
- name: :info,
1048
- regexp: %r(^/info(?:\s+(session|model|runtime|rag))?$),
1049
- complete: [ 'info', %w[ session model runtime rag ] ],
1050
- optional: true,
1051
- help: 'Show info about the session, model, runtime, or RAG',
1052
- ) do |subcommand|
1053
- use_pager do |output|
1054
- case subcommand
1055
- when 'session'
1056
- info_session(output:)
1057
- when 'model'
1058
- info_model(output:)
1059
- when 'runtime'
1060
- info_runtime(output:)
1061
- when 'rag'
1062
- info_rag(output:)
1063
- else
1064
- info(output:)
1065
- end
1066
- end
1067
- :next
1068
- end
1069
-
1070
- command(
1071
- name: :help,
1072
- regexp: %r(^/help(?:\s+(\S+))?$),
1073
- optional: true,
1074
- complete: [ 'help', %w[ me ] ],
1075
- help: 'View the help menu (use \'me\' for AI help or a pattern to filter)'
1076
- ) do |subcommand|
1077
- case subcommand
1078
- when 'me'
1079
- disable_content_parsing
1080
- prompt(:help).to_s % { commands: help_message }
1081
- when /\S+/
1082
- display_chat_help(Regexp.new(Regexp.quote($&)))
1083
- :next
1084
- end
1085
- end
1086
-
1087
- command(
1088
- name: :help_fallback,
1089
- regexp: %r(^/),
1090
- complete: []
1091
- ) do
1092
- display_chat_help
1093
- :next
1094
- end
1095
-
1096
- command(
1097
- name: :type_quit,
1098
- regexp: nil,
1099
- complete: [],
1100
- ) do
1101
- STDOUT.puts "Type /quit to quit."
1102
- :next
1103
- end
1104
-
1105
285
  # Handles user input commands and processes chat interactions.
1106
286
  #
1107
287
  # @param content [String] The input content to process