npcsh 1.1.21__py3-none-any.whl → 1.1.22__py3-none-any.whl

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.
Files changed (136) hide show
  1. npcsh/_state.py +10 -5
  2. npcsh/benchmark/npcsh_agent.py +22 -14
  3. npcsh/benchmark/templates/install-npcsh.sh.j2 +2 -2
  4. npcsh/mcp_server.py +9 -1
  5. npcsh/npc_team/alicanto.npc +12 -6
  6. npcsh/npc_team/corca.npc +0 -1
  7. npcsh/npc_team/frederic.npc +2 -3
  8. npcsh/npc_team/jinxs/lib/core/edit_file.jinx +83 -61
  9. npcsh/npc_team/jinxs/modes/alicanto.jinx +102 -41
  10. npcsh/npc_team/jinxs/modes/build.jinx +378 -0
  11. npcsh/npc_team/jinxs/modes/convene.jinx +597 -0
  12. npcsh/npc_team/jinxs/modes/corca.jinx +777 -387
  13. npcsh/npc_team/jinxs/modes/kg.jinx +69 -2
  14. npcsh/npc_team/jinxs/modes/plonk.jinx +16 -7
  15. npcsh/npc_team/jinxs/modes/yap.jinx +628 -187
  16. npcsh/npc_team/kadiefa.npc +2 -1
  17. npcsh/npc_team/sibiji.npc +3 -3
  18. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.jinx +102 -41
  19. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.npc +12 -6
  20. npcsh-1.1.22.data/data/npcsh/npc_team/build.jinx +378 -0
  21. npcsh-1.1.22.data/data/npcsh/npc_team/corca.jinx +820 -0
  22. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca.npc +0 -1
  23. npcsh-1.1.22.data/data/npcsh/npc_team/edit_file.jinx +119 -0
  24. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/frederic.npc +2 -3
  25. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kadiefa.npc +2 -1
  26. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kg.jinx +69 -2
  27. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.jinx +16 -7
  28. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sibiji.npc +3 -3
  29. npcsh-1.1.22.data/data/npcsh/npc_team/yap.jinx +716 -0
  30. {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/METADATA +246 -281
  31. {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/RECORD +127 -130
  32. npcsh/npc_team/jinxs/lib/core/search/kg_search.jinx +0 -429
  33. npcsh/npc_team/jinxs/lib/core/search.jinx +0 -54
  34. npcsh/npc_team/jinxs/lib/utils/build.jinx +0 -65
  35. npcsh-1.1.21.data/data/npcsh/npc_team/build.jinx +0 -65
  36. npcsh-1.1.21.data/data/npcsh/npc_team/corca.jinx +0 -430
  37. npcsh-1.1.21.data/data/npcsh/npc_team/edit_file.jinx +0 -97
  38. npcsh-1.1.21.data/data/npcsh/npc_team/kg_search.jinx +0 -429
  39. npcsh-1.1.21.data/data/npcsh/npc_team/search.jinx +0 -54
  40. npcsh-1.1.21.data/data/npcsh/npc_team/yap.jinx +0 -275
  41. /npcsh/npc_team/jinxs/lib/{core → utils}/chat.jinx +0 -0
  42. /npcsh/npc_team/jinxs/lib/{core → utils}/cmd.jinx +0 -0
  43. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/add_tab.jinx +0 -0
  44. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/alicanto.png +0 -0
  45. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/arxiv.jinx +0 -0
  46. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/benchmark.jinx +0 -0
  47. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/browser_action.jinx +0 -0
  48. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/browser_screenshot.jinx +0 -0
  49. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/chat.jinx +0 -0
  50. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/click.jinx +0 -0
  51. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_browser.jinx +0 -0
  52. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_pane.jinx +0 -0
  53. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/close_tab.jinx +0 -0
  54. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/cmd.jinx +0 -0
  55. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/compile.jinx +0 -0
  56. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/compress.jinx +0 -0
  57. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/config_tui.jinx +0 -0
  58. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/confirm.jinx +0 -0
  59. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/convene.jinx +0 -0
  60. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca.png +0 -0
  61. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/corca_example.png +0 -0
  62. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/db_search.jinx +0 -0
  63. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/delegate.jinx +0 -0
  64. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/file_search.jinx +0 -0
  65. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/focus_pane.jinx +0 -0
  66. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/frederic4.png +0 -0
  67. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/git.jinx +0 -0
  68. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.jinx +0 -0
  69. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.npc +0 -0
  70. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/guac.png +0 -0
  71. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/help.jinx +0 -0
  72. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/incognide.jinx +0 -0
  73. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/init.jinx +0 -0
  74. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/jinxs.jinx +0 -0
  75. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/kadiefa.png +0 -0
  76. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/key_press.jinx +0 -0
  77. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/launch_app.jinx +0 -0
  78. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/list_panes.jinx +0 -0
  79. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/load_file.jinx +0 -0
  80. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/memories.jinx +0 -0
  81. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/models.jinx +0 -0
  82. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/navigate.jinx +0 -0
  83. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/notify.jinx +0 -0
  84. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/npcsh.ctx +0 -0
  85. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/npcsh_sibiji.png +0 -0
  86. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/nql.jinx +0 -0
  87. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/open_browser.jinx +0 -0
  88. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/open_pane.jinx +0 -0
  89. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/ots.jinx +0 -0
  90. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/papers.jinx +0 -0
  91. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/paste.jinx +0 -0
  92. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.npc +0 -0
  93. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonk.png +0 -0
  94. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/plonkjr.png +0 -0
  95. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/pti.jinx +0 -0
  96. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/python.jinx +0 -0
  97. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/read_pane.jinx +0 -0
  98. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/reattach.jinx +0 -0
  99. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/roll.jinx +0 -0
  100. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/run_terminal.jinx +0 -0
  101. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sample.jinx +0 -0
  102. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/screenshot.jinx +0 -0
  103. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/send_message.jinx +0 -0
  104. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/serve.jinx +0 -0
  105. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/set.jinx +0 -0
  106. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/setup.jinx +0 -0
  107. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sh.jinx +0 -0
  108. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/shh.jinx +0 -0
  109. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sibiji.png +0 -0
  110. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sleep.jinx +0 -0
  111. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/split_pane.jinx +0 -0
  112. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/spool.jinx +0 -0
  113. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/spool.png +0 -0
  114. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sql.jinx +0 -0
  115. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch.jinx +0 -0
  116. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch_npc.jinx +0 -0
  117. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switch_tab.jinx +0 -0
  118. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/switches.jinx +0 -0
  119. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/sync.jinx +0 -0
  120. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/team.jinx +0 -0
  121. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/teamviz.jinx +0 -0
  122. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/trigger.jinx +0 -0
  123. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/type_text.jinx +0 -0
  124. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/usage.jinx +0 -0
  125. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/verbose.jinx +0 -0
  126. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/vixynt.jinx +0 -0
  127. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/wait.jinx +0 -0
  128. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/wander.jinx +0 -0
  129. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/web_search.jinx +0 -0
  130. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/write_file.jinx +0 -0
  131. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/yap.png +0 -0
  132. {npcsh-1.1.21.data → npcsh-1.1.22.data}/data/npcsh/npc_team/zen_mode.jinx +0 -0
  133. {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/WHEEL +0 -0
  134. {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/entry_points.txt +0 -0
  135. {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/licenses/LICENSE +0 -0
  136. {npcsh-1.1.21.dist-info → npcsh-1.1.22.dist-info}/top_level.txt +0 -0
@@ -1,275 +0,0 @@
1
- jinx_name: yap
2
- description: Voice chat mode - speech-to-text input, text-to-speech output
3
- inputs:
4
- - model: null
5
- - provider: null
6
- - tts_model: kokoro
7
- - voice: af_heart
8
- - files: null
9
-
10
- steps:
11
- - name: yap_repl
12
- engine: python
13
- code: |
14
- import os
15
- import sys
16
- import time
17
- import tempfile
18
- import threading
19
- import queue
20
- from termcolor import colored
21
-
22
- # Audio imports with graceful fallback
23
- try:
24
- import torch
25
- import pyaudio
26
- import wave
27
- import numpy as np
28
- from faster_whisper import WhisperModel
29
- from gtts import gTTS
30
- from npcpy.data.audio import (
31
- FORMAT, CHANNELS, RATE, CHUNK,
32
- transcribe_recording, convert_mp3_to_wav
33
- )
34
- AUDIO_AVAILABLE = True
35
- except ImportError as e:
36
- AUDIO_AVAILABLE = False
37
- print(colored(f"Audio dependencies not available: {e}", "yellow"))
38
- print("Install with: pip install npcsh[audio]")
39
-
40
- from npcpy.llm_funcs import get_llm_response
41
- from npcpy.npc_sysenv import get_system_message, render_markdown
42
- from npcpy.data.load import load_file_contents
43
- from npcpy.data.text import rag_search
44
-
45
- npc = context.get('npc')
46
- team = context.get('team')
47
- messages = context.get('messages', [])
48
- files = context.get('files')
49
- tts_model = context.get('tts_model', 'kokoro')
50
- voice = context.get('voice', 'af_heart')
51
-
52
- # Resolve npc if it's a string (npc name) rather than NPC object
53
- if isinstance(npc, str) and team:
54
- npc = team.get(npc) if hasattr(team, 'get') else None
55
- elif isinstance(npc, str):
56
- npc = None
57
-
58
- model = context.get('model') or (npc.model if npc and hasattr(npc, 'model') else None)
59
- provider = context.get('provider') or (npc.provider if npc and hasattr(npc, 'provider') else None)
60
-
61
- print("""
62
- ██╗ ██╗ █████╗ ██████╗
63
- ╚██╗ ██╔╝██╔══██╗██╔══██╗
64
- ╚████╔╝ ███████║██████╔╝
65
- ╚██╔╝ ██╔══██║██╔═══╝
66
- ██║ ██║ ██║██║
67
- ╚═╝ ╚═╝ ╚═╝╚═╝
68
-
69
- Voice Chat Mode
70
- """)
71
-
72
- npc_name = npc.name if npc else "yap"
73
- print(f"Entering yap mode (NPC: {npc_name}). Type '/yq' to exit.")
74
-
75
- if not AUDIO_AVAILABLE:
76
- print(colored("Audio not available. Falling back to text mode.", "yellow"))
77
-
78
- # Load files for RAG context
79
- loaded_chunks = {}
80
- if files:
81
- if isinstance(files, str):
82
- files = [f.strip() for f in files.split(',')]
83
- for file_path in files:
84
- file_path = os.path.expanduser(file_path)
85
- if os.path.exists(file_path):
86
- try:
87
- chunks = load_file_contents(file_path)
88
- loaded_chunks[file_path] = chunks
89
- print(colored(f"Loaded: {file_path}", "green"))
90
- except Exception as e:
91
- print(colored(f"Error loading {file_path}: {e}", "red"))
92
-
93
- # System message for concise voice responses
94
- sys_msg = get_system_message(npc) if npc else "You are a helpful assistant."
95
- sys_msg += "\n\nProvide brief responses of 1-2 sentences unless asked for more detail. Keep responses clear and conversational for voice."
96
-
97
- if not messages or messages[0].get("role") != "system":
98
- messages.insert(0, {"role": "system", "content": sys_msg})
99
-
100
- # Audio state
101
- vad_model = None
102
- whisper_model = None
103
-
104
- if AUDIO_AVAILABLE:
105
- try:
106
- # Load VAD model for voice activity detection
107
- vad_model, _ = torch.hub.load(
108
- repo_or_dir="snakers4/silero-vad",
109
- model="silero_vad",
110
- force_reload=False,
111
- onnx=False,
112
- verbose=False
113
- )
114
- vad_model.to('cpu')
115
- print(colored("VAD model loaded.", "green"))
116
-
117
- # Load Whisper for STT
118
- whisper_model = WhisperModel("base", device="cpu", compute_type="int8")
119
- print(colored("Whisper model loaded.", "green"))
120
- except Exception as e:
121
- print(colored(f"Error loading audio models: {e}", "red"))
122
- AUDIO_AVAILABLE = False
123
-
124
- def speak_text(text, tts_model='kokoro', voice='af_heart'):
125
- """Convert text to speech and play it"""
126
- if not AUDIO_AVAILABLE:
127
- return
128
-
129
- try:
130
- # Use gTTS as fallback
131
- tts = gTTS(text=text, lang='en')
132
- with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as f:
133
- tts.save(f.name)
134
- wav_path = convert_mp3_to_wav(f.name)
135
-
136
- # Play audio
137
- import subprocess
138
- if sys.platform == 'darwin':
139
- subprocess.run(['afplay', wav_path], check=True)
140
- elif sys.platform == 'linux':
141
- subprocess.run(['aplay', wav_path], check=True)
142
- else:
143
- # Windows
144
- import winsound
145
- winsound.PlaySound(wav_path, winsound.SND_FILENAME)
146
-
147
- for _p in [f.name, wav_path]:
148
- try:
149
- os.remove(_p)
150
- except:
151
- pass
152
- except Exception as e:
153
- print(colored(f"TTS error: {e}", "red"))
154
-
155
- def record_audio(duration=5):
156
- """Record audio from microphone"""
157
- if not AUDIO_AVAILABLE:
158
- return None
159
-
160
- try:
161
- p = pyaudio.PyAudio()
162
- stream = p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK)
163
-
164
- print(colored("Recording...", "cyan"), end='', flush=True)
165
- frames = []
166
- for _ in range(0, int(RATE / CHUNK * duration)):
167
- data = stream.read(CHUNK)
168
- frames.append(data)
169
- print(colored(" Done.", "cyan"))
170
-
171
- stream.stop_stream()
172
- stream.close()
173
- p.terminate()
174
-
175
- # Save to temp file
176
- with tempfile.NamedTemporaryFile(suffix='.wav', delete=False) as f:
177
- wf = wave.open(f.name, 'wb')
178
- wf.setnchannels(CHANNELS)
179
- wf.setsampwidth(p.get_sample_size(FORMAT))
180
- wf.setframerate(RATE)
181
- wf.writeframes(b''.join(frames))
182
- wf.close()
183
- return f.name
184
- except Exception as e:
185
- print(colored(f"Recording error: {e}", "red"))
186
- return None
187
-
188
- def transcribe_audio(audio_path):
189
- """Transcribe audio to text using Whisper"""
190
- if not whisper_model or not audio_path:
191
- return ""
192
-
193
- try:
194
- segments, _ = whisper_model.transcribe(audio_path, beam_size=5)
195
- text = " ".join([seg.text for seg in segments])
196
- try:
197
- os.remove(audio_path)
198
- except:
199
- pass
200
- return text.strip()
201
- except Exception as e:
202
- print(colored(f"Transcription error: {e}", "red"))
203
- return ""
204
-
205
- # REPL loop
206
- while True:
207
- try:
208
- # Voice input or text input
209
- if AUDIO_AVAILABLE:
210
- prompt_str = f"{npc_name}:yap> [Press Enter to speak, or type] "
211
- else:
212
- prompt_str = f"{npc_name}:yap> "
213
-
214
- user_input = input(prompt_str).strip()
215
-
216
- if user_input.lower() == "/yq":
217
- print("Exiting yap mode.")
218
- break
219
-
220
- # Empty input = record audio
221
- if not user_input and AUDIO_AVAILABLE:
222
- audio_path = record_audio(5)
223
- if audio_path:
224
- user_input = transcribe_audio(audio_path)
225
- if user_input:
226
- print(colored(f"You said: {user_input}", "cyan"))
227
- else:
228
- print(colored("Could not transcribe audio.", "yellow"))
229
- continue
230
- else:
231
- continue
232
-
233
- if not user_input:
234
- continue
235
-
236
- # Add RAG context if files loaded
237
- current_prompt = user_input
238
- if loaded_chunks:
239
- context_content = ""
240
- for filename, chunks in loaded_chunks.items():
241
- full_text = "\n".join(chunks)
242
- retrieved = rag_search(user_input, full_text, similarity_threshold=0.3)
243
- if retrieved:
244
- context_content += f"\n{retrieved}\n"
245
- if context_content:
246
- current_prompt += f"\n\nContext:{context_content}"
247
-
248
- # Get response
249
- resp = get_llm_response(
250
- current_prompt,
251
- model=model,
252
- provider=provider,
253
- messages=messages,
254
- stream=False, # Don't stream for voice
255
- npc=npc
256
- )
257
-
258
- messages = resp.get('messages', messages)
259
- response_text = str(resp.get('response', ''))
260
-
261
- # Display and speak response
262
- print(colored(f"{npc_name}: ", "green") + response_text)
263
-
264
- if AUDIO_AVAILABLE:
265
- speak_text(response_text, tts_model, voice)
266
-
267
- except KeyboardInterrupt:
268
- print("\nUse '/yq' to exit or continue.")
269
- continue
270
- except EOFError:
271
- print("\nExiting yap mode.")
272
- break
273
-
274
- context['output'] = "Exited yap mode."
275
- context['messages'] = messages
File without changes
File without changes
File without changes