kash-shell 0.3.8__py3-none-any.whl → 0.3.10__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.
- kash/actions/__init__.py +4 -4
- kash/actions/core/markdownify.py +5 -2
- kash/actions/core/readability.py +5 -2
- kash/actions/core/render_as_html.py +18 -0
- kash/actions/core/webpage_config.py +12 -4
- kash/commands/__init__.py +8 -20
- kash/commands/base/basic_file_commands.py +15 -0
- kash/commands/base/debug_commands.py +15 -2
- kash/commands/base/general_commands.py +27 -18
- kash/commands/base/logs_commands.py +1 -4
- kash/commands/base/model_commands.py +8 -8
- kash/commands/base/search_command.py +3 -2
- kash/commands/base/show_command.py +5 -3
- kash/commands/extras/parse_uv_lock.py +186 -0
- kash/commands/help/doc_commands.py +2 -31
- kash/commands/help/welcome.py +33 -0
- kash/commands/workspace/selection_commands.py +11 -6
- kash/commands/workspace/workspace_commands.py +19 -16
- kash/config/colors.py +2 -0
- kash/config/env_settings.py +72 -0
- kash/config/init.py +2 -2
- kash/config/logger.py +61 -59
- kash/config/logger_basic.py +12 -5
- kash/config/server_config.py +6 -6
- kash/config/settings.py +117 -67
- kash/config/setup.py +35 -9
- kash/config/suppress_warnings.py +30 -12
- kash/config/text_styles.py +3 -13
- kash/docs/load_api_docs.py +2 -1
- kash/docs/markdown/topics/a2_installation.md +7 -3
- kash/docs/markdown/topics/a3_getting_started.md +3 -2
- kash/docs/markdown/warning.md +3 -8
- kash/docs/markdown/welcome.md +4 -0
- kash/docs_base/load_recipe_snippets.py +1 -1
- kash/docs_base/recipes/{general_system_commands.ksh → general_system_commands.sh} +1 -1
- kash/{concepts → embeddings}/cosine.py +2 -1
- kash/embeddings/text_similarity.py +57 -0
- kash/exec/__init__.py +20 -3
- kash/exec/action_decorators.py +18 -4
- kash/exec/action_exec.py +41 -23
- kash/exec/action_registry.py +13 -48
- kash/exec/command_registry.py +2 -1
- kash/exec/fetch_url_metadata.py +4 -6
- kash/exec/importing.py +56 -0
- kash/exec/llm_transforms.py +6 -6
- kash/exec/precondition_registry.py +2 -1
- kash/exec/preconditions.py +16 -1
- kash/exec/shell_callable_action.py +33 -19
- kash/file_storage/file_store.py +23 -14
- kash/file_storage/item_file_format.py +13 -3
- kash/file_storage/metadata_dirs.py +11 -2
- kash/help/assistant.py +2 -2
- kash/help/assistant_instructions.py +2 -1
- kash/help/help_embeddings.py +2 -2
- kash/help/help_printing.py +14 -10
- kash/help/tldr_help.py +5 -3
- kash/llm_utils/clean_headings.py +1 -1
- kash/llm_utils/llm_api_keys.py +4 -4
- kash/llm_utils/llm_completion.py +2 -2
- kash/llm_utils/llm_features.py +68 -0
- kash/llm_utils/llm_messages.py +1 -2
- kash/llm_utils/llm_names.py +1 -1
- kash/llm_utils/llms.py +17 -12
- kash/local_server/__init__.py +5 -2
- kash/local_server/local_server.py +56 -46
- kash/local_server/local_server_commands.py +15 -15
- kash/local_server/local_server_routes.py +2 -2
- kash/local_server/local_url_formatters.py +1 -1
- kash/mcp/__init__.py +5 -2
- kash/mcp/mcp_cli.py +54 -17
- kash/mcp/mcp_server_commands.py +5 -6
- kash/mcp/mcp_server_routes.py +14 -11
- kash/mcp/mcp_server_sse.py +61 -34
- kash/mcp/mcp_server_stdio.py +0 -8
- kash/media_base/audio_processing.py +81 -7
- kash/media_base/media_cache.py +18 -18
- kash/media_base/media_services.py +1 -1
- kash/media_base/media_tools.py +6 -6
- kash/media_base/services/local_file_media.py +2 -2
- kash/media_base/{speech_transcription.py → transcription_deepgram.py} +25 -109
- kash/media_base/transcription_format.py +73 -0
- kash/media_base/transcription_whisper.py +38 -0
- kash/model/__init__.py +73 -5
- kash/model/actions_model.py +38 -4
- kash/model/concept_model.py +30 -0
- kash/model/items_model.py +56 -13
- kash/model/params_model.py +24 -0
- kash/shell/completions/completion_scoring.py +37 -5
- kash/shell/output/kerm_codes.py +1 -2
- kash/shell/output/shell_formatting.py +14 -4
- kash/shell/shell_main.py +2 -2
- kash/shell/utils/exception_printing.py +6 -0
- kash/shell/utils/native_utils.py +26 -20
- kash/text_handling/custom_sliding_transforms.py +12 -4
- kash/text_handling/doc_normalization.py +6 -2
- kash/text_handling/markdown_render.py +117 -0
- kash/text_handling/markdown_utils.py +204 -0
- kash/utils/common/import_utils.py +12 -3
- kash/utils/common/type_utils.py +0 -29
- kash/utils/common/url.py +80 -28
- kash/utils/errors.py +6 -0
- kash/utils/file_utils/{dir_size.py → dir_info.py} +25 -4
- kash/utils/file_utils/file_ext.py +2 -3
- kash/utils/file_utils/file_formats.py +28 -2
- kash/utils/file_utils/file_formats_model.py +50 -19
- kash/utils/file_utils/filename_parsing.py +10 -4
- kash/web_content/dir_store.py +1 -2
- kash/web_content/file_cache_utils.py +37 -10
- kash/web_content/file_processing.py +68 -0
- kash/web_content/local_file_cache.py +12 -9
- kash/web_content/web_extract.py +8 -3
- kash/web_content/web_fetch.py +12 -4
- kash/web_gen/tabbed_webpage.py +5 -2
- kash/web_gen/templates/base_styles.css.jinja +120 -14
- kash/web_gen/templates/base_webpage.html.jinja +60 -13
- kash/web_gen/templates/content_styles.css.jinja +4 -2
- kash/web_gen/templates/item_view.html.jinja +2 -2
- kash/web_gen/templates/tabbed_webpage.html.jinja +1 -2
- kash/workspaces/__init__.py +15 -2
- kash/workspaces/selections.py +18 -3
- kash/workspaces/source_items.py +4 -2
- kash/workspaces/workspace_output.py +11 -4
- kash/workspaces/workspaces.py +5 -11
- kash/xonsh_custom/command_nl_utils.py +40 -19
- kash/xonsh_custom/custom_shell.py +44 -12
- kash/xonsh_custom/customize_prompt.py +39 -21
- kash/xonsh_custom/load_into_xonsh.py +26 -27
- kash/xonsh_custom/shell_load_commands.py +2 -2
- kash/xonsh_custom/xonsh_completers.py +2 -249
- kash/xonsh_custom/xonsh_keybindings.py +282 -0
- kash/xonsh_custom/xonsh_modern_tools.py +3 -3
- kash/xontrib/kash_extension.py +5 -6
- {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/METADATA +26 -12
- {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/RECORD +140 -140
- {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/entry_points.txt +1 -1
- kash/concepts/concept_formats.py +0 -23
- kash/concepts/text_similarity.py +0 -112
- kash/shell/clideps/api_keys.py +0 -99
- kash/shell/clideps/dotenv_setup.py +0 -114
- kash/shell/clideps/dotenv_utils.py +0 -89
- kash/shell/clideps/pkg_deps.py +0 -232
- kash/shell/clideps/platforms.py +0 -11
- kash/shell/clideps/terminal_features.py +0 -56
- kash/shell/utils/osc_utils.py +0 -95
- kash/shell/utils/terminal_images.py +0 -133
- kash/text_handling/markdown_util.py +0 -167
- kash/utils/common/atomic_var.py +0 -158
- kash/utils/common/string_replace.py +0 -93
- kash/utils/common/string_template.py +0 -101
- /kash/docs_base/recipes/{python_dev_commands.ksh → python_dev_commands.sh} +0 -0
- /kash/docs_base/recipes/{tldr_standard_commands.ksh → tldr_standard_commands.sh} +0 -0
- /kash/{concepts → embeddings}/embeddings.py +0 -0
- {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/WHEEL +0 -0
- {kash_shell-0.3.8.dist-info → kash_shell-0.3.10.dist-info}/licenses/LICENSE +0 -0
kash/config/settings.py
CHANGED
|
@@ -3,17 +3,18 @@ from enum import Enum
|
|
|
3
3
|
from functools import cache
|
|
4
4
|
from logging import DEBUG, ERROR, INFO, WARNING
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import overload
|
|
7
6
|
|
|
8
7
|
from pydantic.dataclasses import dataclass
|
|
8
|
+
from strif import AtomicVar
|
|
9
9
|
|
|
10
|
-
from kash.
|
|
10
|
+
from kash.config.env_settings import KashEnv
|
|
11
11
|
|
|
12
12
|
APP_NAME = "kash"
|
|
13
13
|
|
|
14
14
|
DOT_DIR = ".kash"
|
|
15
15
|
|
|
16
|
-
GLOBAL_WS_NAME = "
|
|
16
|
+
GLOBAL_WS_NAME = "workspace"
|
|
17
|
+
"""Name of the global workspace."""
|
|
17
18
|
|
|
18
19
|
RECOMMENDED_API_KEYS = [
|
|
19
20
|
"OPENAI_API_KEY",
|
|
@@ -23,59 +24,28 @@ RECOMMENDED_API_KEYS = [
|
|
|
23
24
|
]
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
def path_from_env(env_name: str, default: None) -> None: ...
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@overload
|
|
31
|
-
def path_from_env(env_name: str, default: Path) -> Path: ...
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def path_from_env(env_name: str, default: Path | None) -> Path | None:
|
|
35
|
-
value = os.environ.get(env_name)
|
|
36
|
-
if value:
|
|
37
|
-
return Path(value).expanduser().resolve()
|
|
38
|
-
else:
|
|
39
|
-
return default.expanduser().resolve() if default else None
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def get_ws_root_dir() -> Path:
|
|
43
|
-
"""Default root directory for kash workspaces."""
|
|
44
|
-
return path_from_env("KASH_WS_ROOT", Path("~/Kash").expanduser().resolve())
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def get_global_ws_dir() -> Path:
|
|
48
|
-
"""Default global workspace directory."""
|
|
49
|
-
kash_ws_dir = path_from_env("KASH_GLOBAL_WS", None)
|
|
50
|
-
if kash_ws_dir:
|
|
51
|
-
return kash_ws_dir
|
|
52
|
-
else:
|
|
53
|
-
return get_ws_root_dir() / GLOBAL_WS_NAME
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def get_system_logs_dir() -> Path:
|
|
57
|
-
"""Default global and system logs directory (for server logs, etc)."""
|
|
58
|
-
return path_from_env("KASH_SYSTEM_LOGS_DIR", get_ws_root_dir() / "logs")
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
def get_system_cache_dir() -> Path:
|
|
62
|
-
"""Default global and system cache directory (for global media, content, etc)."""
|
|
63
|
-
return path_from_env("KASH_SYSTEM_CACHE_DIR", get_ws_root_dir() / "cache")
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
def get_mcp_ws_dir() -> Path | None:
|
|
27
|
+
def get_all_common_api_env_vars() -> list[str]:
|
|
67
28
|
"""
|
|
68
|
-
Get the
|
|
29
|
+
Get all the common environment variables that are recommended to be set.
|
|
69
30
|
"""
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
31
|
+
from clideps.env_vars.env_names import get_all_common_env_names
|
|
32
|
+
|
|
33
|
+
return list(set(get_all_common_env_names() + RECOMMENDED_API_KEYS))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
RECOMMENDED_PKGS = [
|
|
37
|
+
"less",
|
|
38
|
+
"eza",
|
|
39
|
+
"ripgrep",
|
|
40
|
+
"bat",
|
|
41
|
+
"zoxide",
|
|
42
|
+
"dust",
|
|
43
|
+
"duf",
|
|
44
|
+
"pygmentize",
|
|
45
|
+
"hexyl",
|
|
46
|
+
"ffmpeg",
|
|
47
|
+
"imagemagick",
|
|
48
|
+
]
|
|
79
49
|
|
|
80
50
|
|
|
81
51
|
MEDIA_CACHE_NAME = "media"
|
|
@@ -91,7 +61,7 @@ LOCAL_SERVER_PORT_START = 4470
|
|
|
91
61
|
LOCAL_SERVER_PORTS_MAX = 30
|
|
92
62
|
|
|
93
63
|
|
|
94
|
-
|
|
64
|
+
LOCAL_SERVER_LOG_NAME = "local_server"
|
|
95
65
|
|
|
96
66
|
|
|
97
67
|
class LogLevel(Enum):
|
|
@@ -117,7 +87,7 @@ class LogLevel(Enum):
|
|
|
117
87
|
return self.name
|
|
118
88
|
|
|
119
89
|
|
|
120
|
-
DEFAULT_LOG_LEVEL = LogLevel.parse(
|
|
90
|
+
DEFAULT_LOG_LEVEL = LogLevel.parse(KashEnv.KASH_LOG_LEVEL.read_str("warning"))
|
|
121
91
|
|
|
122
92
|
|
|
123
93
|
def resolve_and_create_dirs(path: Path | str, is_dir: bool = False) -> Path:
|
|
@@ -149,11 +119,15 @@ def find_in_cwd_or_parents(filename: Path | str) -> Path | None:
|
|
|
149
119
|
return None
|
|
150
120
|
|
|
151
121
|
|
|
122
|
+
def _get_rcfile_path() -> Path:
|
|
123
|
+
return _get_system_config_dir() / "kashrc"
|
|
124
|
+
|
|
125
|
+
|
|
152
126
|
def find_rcfiles() -> list[Path]:
|
|
153
127
|
"""
|
|
154
128
|
Find active rcfiles. Currently only supports one.
|
|
155
129
|
"""
|
|
156
|
-
rcfile_path =
|
|
130
|
+
rcfile_path = _get_rcfile_path()
|
|
157
131
|
if rcfile_path.exists():
|
|
158
132
|
return [rcfile_path]
|
|
159
133
|
else:
|
|
@@ -162,9 +136,30 @@ def find_rcfiles() -> list[Path]:
|
|
|
162
136
|
|
|
163
137
|
@dataclass
|
|
164
138
|
class Settings:
|
|
139
|
+
ws_root_dir: Path
|
|
140
|
+
"""A default root directory for kash workspaces (typically `~/Kash`)."""
|
|
141
|
+
|
|
142
|
+
global_ws_dir: Path
|
|
143
|
+
"""The directory for the default global workspace."""
|
|
144
|
+
|
|
145
|
+
system_config_dir: Path
|
|
146
|
+
"""The directory for system-wide configuration files."""
|
|
147
|
+
|
|
165
148
|
media_cache_dir: Path
|
|
166
149
|
"""The workspace media cache directory, for caching audio, video, and transcripts."""
|
|
167
150
|
|
|
151
|
+
system_logs_dir: Path
|
|
152
|
+
"""Default global and system logs directory (for server logs, etc)."""
|
|
153
|
+
|
|
154
|
+
system_cache_dir: Path
|
|
155
|
+
"""Default global and system cache directory (for global media, content, etc)."""
|
|
156
|
+
|
|
157
|
+
mcp_ws_dir: Path | None
|
|
158
|
+
"""The directory for the MCP workspace, if set."""
|
|
159
|
+
|
|
160
|
+
local_server_log_path: Path
|
|
161
|
+
"""The path to the local server log."""
|
|
162
|
+
|
|
168
163
|
content_cache_dir: Path
|
|
169
164
|
"""The workspace content cache directory, for caching web or local files."""
|
|
170
165
|
# TODO: Separate workspace cached content (e.g. thumbnails) vs global files
|
|
@@ -201,18 +196,60 @@ class Settings:
|
|
|
201
196
|
"""If true, use Nerd Icons in file listings. Requires a compatible font."""
|
|
202
197
|
|
|
203
198
|
|
|
204
|
-
|
|
205
|
-
def server_log_file_path(name: str, port: int | str) -> Path:
|
|
206
|
-
# Use a different log file for each port (server instance).
|
|
207
|
-
return resolve_and_create_dirs(SERVER_LOG_FILE.format(name=name, port=port))
|
|
199
|
+
ws_root_dir = Path("~/Kash").expanduser()
|
|
208
200
|
|
|
209
201
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
202
|
+
def _get_ws_root_dir() -> Path:
|
|
203
|
+
"""Default root directory for kash workspaces."""
|
|
204
|
+
return KashEnv.KASH_WS_ROOT.read_path(default=ws_root_dir)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _get_global_ws_dir() -> Path:
|
|
208
|
+
kash_ws_dir = KashEnv.KASH_GLOBAL_WS.read_path()
|
|
209
|
+
if kash_ws_dir:
|
|
210
|
+
return kash_ws_dir
|
|
211
|
+
else:
|
|
212
|
+
return _get_ws_root_dir() / GLOBAL_WS_NAME
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def get_system_logs_dir() -> Path:
|
|
216
|
+
return KashEnv.KASH_SYSTEM_LOGS_DIR.read_path(default=_get_ws_root_dir() / "logs")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def _get_system_config_dir() -> Path:
|
|
220
|
+
return Path("~/.config/kash").expanduser().resolve()
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _get_system_cache_dir() -> Path:
|
|
224
|
+
return KashEnv.KASH_SYSTEM_CACHE_DIR.read_path(default=_get_ws_root_dir() / "cache")
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _get_mcp_ws_dir() -> Path | None:
|
|
228
|
+
mcp_dir = KashEnv.KASH_MCP_WS.read_str()
|
|
229
|
+
if mcp_dir:
|
|
230
|
+
return Path(mcp_dir).expanduser().resolve()
|
|
231
|
+
else:
|
|
232
|
+
return None
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
@cache
|
|
236
|
+
def _get_local_server_log_path() -> Path:
|
|
237
|
+
return resolve_and_create_dirs(get_system_logs_dir() / f"{LOCAL_SERVER_LOG_NAME}.log")
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def _read_settings():
|
|
241
|
+
return Settings(
|
|
242
|
+
# Essential system settings for logs, workspaces, and configs:
|
|
243
|
+
ws_root_dir=_get_ws_root_dir(),
|
|
244
|
+
global_ws_dir=_get_global_ws_dir(),
|
|
245
|
+
system_config_dir=_get_system_config_dir(),
|
|
246
|
+
system_logs_dir=get_system_logs_dir(),
|
|
247
|
+
system_cache_dir=_get_system_cache_dir(),
|
|
248
|
+
mcp_ws_dir=_get_mcp_ws_dir(),
|
|
249
|
+
local_server_log_path=_get_local_server_log_path(),
|
|
213
250
|
# These default to the global but can be overridden by workspace settings.
|
|
214
|
-
media_cache_dir=
|
|
215
|
-
content_cache_dir=
|
|
251
|
+
media_cache_dir=_get_system_cache_dir() / MEDIA_CACHE_NAME,
|
|
252
|
+
content_cache_dir=_get_system_cache_dir() / CONTENT_CACHE_NAME,
|
|
216
253
|
debug_assistant=True,
|
|
217
254
|
default_editor="nano",
|
|
218
255
|
file_log_level=LogLevel.info,
|
|
@@ -224,7 +261,20 @@ _settings = AtomicVar(
|
|
|
224
261
|
use_kerm_codes=False,
|
|
225
262
|
use_nerd_icons=True,
|
|
226
263
|
)
|
|
227
|
-
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
# Initial default settings.
|
|
267
|
+
_settings = AtomicVar(_read_settings())
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def configure_ws_and_settings(root_dir: Path):
|
|
271
|
+
"""
|
|
272
|
+
Reset and reload all settings, deriving all paths from the new workspace
|
|
273
|
+
root. Good if embedding kash in another app.
|
|
274
|
+
"""
|
|
275
|
+
global ws_root_dir
|
|
276
|
+
ws_root_dir = root_dir
|
|
277
|
+
_settings.set(_read_settings())
|
|
228
278
|
|
|
229
279
|
|
|
230
280
|
def atomic_global_settings() -> AtomicVar[Settings]:
|
kash/config/setup.py
CHANGED
|
@@ -1,25 +1,51 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
2
|
from functools import cache
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
from typing import Any
|
|
4
5
|
|
|
6
|
+
from clideps.env_vars.dotenv_utils import load_dotenv_paths
|
|
7
|
+
|
|
8
|
+
from kash.config.logger import reset_rich_logging
|
|
9
|
+
from kash.config.logger_basic import basic_logging_setup
|
|
10
|
+
from kash.config.settings import LogLevel, configure_ws_and_settings, global_settings
|
|
11
|
+
|
|
5
12
|
|
|
6
13
|
@cache
|
|
7
|
-
def
|
|
14
|
+
def kash_setup(
|
|
15
|
+
*,
|
|
16
|
+
rich_logging: bool,
|
|
17
|
+
kash_ws_root: Path | None = None,
|
|
18
|
+
log_path: Path | None = None,
|
|
19
|
+
level: LogLevel = LogLevel.info,
|
|
20
|
+
):
|
|
8
21
|
"""
|
|
9
|
-
One-time setup of essential keys, directories, and configs.
|
|
22
|
+
One-time top-level setup of essential logging, keys, directories, and configs.
|
|
23
|
+
Idempotent.
|
|
24
|
+
|
|
25
|
+
Can call this if embedding kash in another app.
|
|
26
|
+
Can be used to set the global default workspace and logs directory
|
|
27
|
+
and/or the default log file.
|
|
28
|
+
If `rich_logging` is True, then rich logging with warnings only for console use.
|
|
29
|
+
If `rich_logging` is False, then use basic logging to a file and stderr.
|
|
10
30
|
"""
|
|
11
|
-
from kash.config.logger import reload_rich_logging_setup
|
|
12
|
-
from kash.shell.clideps.dotenv_utils import load_dotenv_paths
|
|
13
31
|
from kash.utils.common.stack_traces import add_stacktrace_handler
|
|
14
32
|
|
|
15
|
-
|
|
16
|
-
reload_rich_logging_setup()
|
|
33
|
+
add_stacktrace_handler()
|
|
17
34
|
|
|
18
|
-
|
|
35
|
+
# Settings may depend on environment variables, so load them first.
|
|
36
|
+
load_dotenv_paths(True, True, global_settings().system_config_dir)
|
|
19
37
|
|
|
20
|
-
|
|
38
|
+
# Then configure the workspace and settings before finalizing logging.
|
|
39
|
+
if kash_ws_root:
|
|
40
|
+
configure_ws_and_settings(kash_ws_root)
|
|
21
41
|
|
|
22
|
-
|
|
42
|
+
# Now set up logging, as it might depend on workspace root.
|
|
43
|
+
if rich_logging:
|
|
44
|
+
reset_rich_logging(log_path=log_path)
|
|
45
|
+
else:
|
|
46
|
+
basic_logging_setup(log_path=log_path, level=level)
|
|
47
|
+
|
|
48
|
+
_lib_setup()
|
|
23
49
|
|
|
24
50
|
|
|
25
51
|
def _lib_setup():
|
kash/config/suppress_warnings.py
CHANGED
|
@@ -1,27 +1,45 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import warnings
|
|
3
3
|
from logging import LogRecord
|
|
4
|
+
from typing import Any
|
|
5
|
+
from warnings import formatwarning
|
|
6
|
+
|
|
7
|
+
FILTER_PATTERNS = [
|
|
8
|
+
"deprecated",
|
|
9
|
+
"Deprecation",
|
|
10
|
+
"PydanticDeprecatedSince20",
|
|
11
|
+
"pydantic",
|
|
12
|
+
# "pydub",
|
|
13
|
+
]
|
|
14
|
+
"""Warning messages to always suppress in console output."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def should_suppress(message: Any):
|
|
18
|
+
return any(pattern in str(message) for pattern in FILTER_PATTERNS)
|
|
4
19
|
|
|
5
20
|
|
|
6
21
|
def filter_warnings():
|
|
22
|
+
for pattern in FILTER_PATTERNS:
|
|
23
|
+
warnings.filterwarnings("ignore", message=f".*{pattern}.*")
|
|
7
24
|
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
8
|
-
warnings.filterwarnings("ignore", message=".*deprecated.*")
|
|
9
|
-
warnings.filterwarnings("ignore", message=".*Deprecation.*")
|
|
10
|
-
warnings.filterwarnings("ignore", message=".*PydanticDeprecatedSince20.*")
|
|
11
|
-
warnings.filterwarnings("ignore", module="pydub")
|
|
12
|
-
warnings.filterwarnings("ignore", module="pydantic")
|
|
13
25
|
warnings.filterwarnings("ignore", category=RuntimeWarning, module="xonsh.tools")
|
|
14
26
|
|
|
27
|
+
log = logging.getLogger("warnings")
|
|
28
|
+
|
|
29
|
+
def custom_showwarning(message, category, filename, lineno, _file=None, line=None):
|
|
30
|
+
if not should_suppress(message) and not should_suppress(category):
|
|
31
|
+
log.warning(formatwarning(message, category, filename, lineno, line))
|
|
32
|
+
|
|
33
|
+
# Override system default, which writes to stderr.
|
|
34
|
+
warnings.showwarning = custom_showwarning
|
|
35
|
+
|
|
15
36
|
|
|
16
37
|
filter_warnings()
|
|
17
38
|
|
|
18
39
|
|
|
19
|
-
#
|
|
20
|
-
def demote_warnings(record: LogRecord):
|
|
40
|
+
# An even more brute force approach if the approach above doesn't work.
|
|
41
|
+
def demote_warnings(record: LogRecord, level: int = logging.INFO):
|
|
21
42
|
if record.levelno == logging.WARNING:
|
|
22
43
|
# Check for any warning patterns that we're filtering in filter_warnings
|
|
23
|
-
if
|
|
24
|
-
|
|
25
|
-
for pattern in ["deprecated", "Deprecation", "PydanticDeprecatedSince20"]
|
|
26
|
-
) or any(module in str(record.pathname) for module in ["pydub", "pydantic", "xonsh.tools"]):
|
|
27
|
-
record.levelno = logging.INFO
|
|
44
|
+
if should_suppress(record.msg) or should_suppress(record.module):
|
|
45
|
+
record.levelno = level
|
kash/config/text_styles.py
CHANGED
|
@@ -46,7 +46,9 @@ SPINNER = "dots12"
|
|
|
46
46
|
|
|
47
47
|
BAT_THEME = "Coldark-Dark"
|
|
48
48
|
|
|
49
|
-
BAT_STYLE = "header-filename,header-filesize,grid,changes"
|
|
49
|
+
BAT_STYLE = "header-filename,header-filesize,grid,numbers,changes"
|
|
50
|
+
|
|
51
|
+
BAT_STYLE_PLAIN = "plain"
|
|
50
52
|
|
|
51
53
|
|
|
52
54
|
## Colors
|
|
@@ -296,18 +298,6 @@ EMOJI_MSG_INDENT = "⋮"
|
|
|
296
298
|
|
|
297
299
|
EMOJI_BREADCRUMB_SEP = "›"
|
|
298
300
|
|
|
299
|
-
EMOJI_TRUE = "✔︎"
|
|
300
|
-
|
|
301
|
-
EMOJI_FALSE = "✘"
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
def success_emoji(value: bool, success_only: bool = False) -> str:
|
|
305
|
-
return EMOJI_TRUE if value else " " if success_only else EMOJI_FALSE
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
def format_success_emoji(value: bool, success_only: bool = False) -> Text:
|
|
309
|
-
return Text(success_emoji(value, success_only), style=COLOR_SUCCESS if value else COLOR_FAILURE)
|
|
310
|
-
|
|
311
301
|
|
|
312
302
|
## Special headings
|
|
313
303
|
|
kash/docs/load_api_docs.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
from strif import StringTemplate
|
|
2
|
+
|
|
1
3
|
from kash.config.logger import get_logger
|
|
2
4
|
from kash.docs.load_help_topics import load_help_src
|
|
3
5
|
from kash.docs.load_source_code import load_source_code
|
|
4
|
-
from kash.utils.common.string_template import StringTemplate
|
|
5
6
|
|
|
6
7
|
log = get_logger(__name__)
|
|
7
8
|
|
|
@@ -65,15 +65,19 @@ These are for `kash-media` but you can use a `kash-shell` for a more basic setup
|
|
|
65
65
|
For macOS, you can again use brew:
|
|
66
66
|
|
|
67
67
|
```shell
|
|
68
|
-
# Install pyenv, pipx, and other tools:
|
|
69
68
|
brew update
|
|
70
|
-
brew install ripgrep bat eza hexyl imagemagick
|
|
69
|
+
brew install libmagic ffmpeg ripgrep bat eza hexyl imagemagick zoxide
|
|
71
70
|
```
|
|
72
71
|
|
|
73
72
|
For Ubuntu:
|
|
74
73
|
|
|
75
74
|
```shell
|
|
76
|
-
apt
|
|
75
|
+
sudo apt-get update
|
|
76
|
+
sudo apt-get install -y libgl1 ffmpeg libmagic-dev
|
|
77
|
+
# For the additional command-line tools, pixi is better on Ubuntu:
|
|
78
|
+
curl -fsSL https://pixi.sh/install.sh | sh
|
|
79
|
+
. ~/.bashrc
|
|
80
|
+
pixi global install ripgrep bat eza hexyl imagemagick zoxide
|
|
77
81
|
```
|
|
78
82
|
|
|
79
83
|
For Windows or other platforms, see the uv instructions.
|
|
@@ -243,8 +243,9 @@ A few of the most important commands for managing files and work are these:
|
|
|
243
243
|
browser to view it.
|
|
244
244
|
|
|
245
245
|
- `workspace` shows or selects or creates a new workspace.
|
|
246
|
-
Initially you work in the
|
|
247
|
-
create a workspace, which is a directory to hold
|
|
246
|
+
Initially you work in the default global workspace (typically at `~/Kash/workspace`)
|
|
247
|
+
but for more real work you'll want to create a workspace, which is a directory to hold
|
|
248
|
+
the files you are working with.
|
|
248
249
|
|
|
249
250
|
- `select` shows or sets selections, which are the set of files the next command will
|
|
250
251
|
run on, within the current workspace.
|
kash/docs/markdown/warning.md
CHANGED
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
*
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
Discuss at github.com/jlevy/kash and contact info at: github.com/jlevy*
|
|
5
|
-
|
|
6
|
-
**Warning:** *This is a shell so actions and commands can be destructive or dangerous,
|
|
7
|
-
including deleting files.
|
|
8
|
-
Exercise caution and review commands carefully!*
|
|
1
|
+
**Important:** *Kash will not execute commands without confirmation.
|
|
2
|
+
But this is a shell so commands can be destructive or dangerous.
|
|
3
|
+
Review commands carefully!*
|
kash/docs/markdown/welcome.md
CHANGED
|
@@ -5,5 +5,9 @@ You may simply ask a question and the kash assistant will help you.
|
|
|
5
5
|
Press **space** (or type **?**), then write your question or request.
|
|
6
6
|
Use `logs` for detailed logs.
|
|
7
7
|
|
|
8
|
+
*I'd love to hear from you with issues, bugs, and ideas.
|
|
9
|
+
Discuss at github.com/jlevy/kash or contact me github.com/jlevy or x.com/ojoshe (DMs
|
|
10
|
+
open).*
|
|
11
|
+
|
|
8
12
|
Try: `What is kash?`, `How can I transcribe a YouTube video?`, `getting_started`, `faq`,
|
|
9
13
|
`self_check`, `self_configure`
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
from collections.abc import Sequence
|
|
2
|
+
from typing import Any, TypeAlias
|
|
2
3
|
|
|
3
4
|
import numpy as np
|
|
4
5
|
|
|
5
6
|
# Type aliases for clarity
|
|
6
|
-
ArrayLike = Sequence[float] | np.ndarray
|
|
7
|
+
ArrayLike: TypeAlias = Sequence[float] | np.ndarray[Any, Any]
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
def cosine(u: ArrayLike, v: ArrayLike) -> float:
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from typing import cast
|
|
2
|
+
|
|
3
|
+
import litellm
|
|
4
|
+
from funlog import log_calls
|
|
5
|
+
from litellm import embedding
|
|
6
|
+
from litellm.types.utils import EmbeddingResponse
|
|
7
|
+
|
|
8
|
+
from kash.config.logger import get_logger
|
|
9
|
+
from kash.embeddings.cosine import ArrayLike, cosine
|
|
10
|
+
from kash.embeddings.embeddings import Embeddings
|
|
11
|
+
from kash.llm_utils.llms import DEFAULT_EMBEDDING_MODEL, EmbeddingModel
|
|
12
|
+
from kash.utils.errors import ApiResultError
|
|
13
|
+
|
|
14
|
+
log = get_logger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def cosine_relatedness(x: ArrayLike, y: ArrayLike) -> float:
|
|
18
|
+
return 1 - cosine(x, y)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@log_calls(level="info", show_return_value=False)
|
|
22
|
+
def embed_query(model: EmbeddingModel, query: str) -> EmbeddingResponse:
|
|
23
|
+
try:
|
|
24
|
+
response: EmbeddingResponse = cast(
|
|
25
|
+
EmbeddingResponse, embedding(model=model.litellm_name, input=[query])
|
|
26
|
+
)
|
|
27
|
+
except litellm.exceptions.APIError as e:
|
|
28
|
+
log.info("API error embedding query: %s", e)
|
|
29
|
+
raise ApiResultError(str(e))
|
|
30
|
+
if not response.data:
|
|
31
|
+
log.info("API error embedding query, got: %s", response)
|
|
32
|
+
raise ApiResultError("No embedding response data")
|
|
33
|
+
return response
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@log_calls(level="info", show_return_value=False)
|
|
37
|
+
def rank_by_relatedness(
|
|
38
|
+
query: str,
|
|
39
|
+
embeddings: Embeddings,
|
|
40
|
+
relatedness_fn=cosine_relatedness,
|
|
41
|
+
model=DEFAULT_EMBEDDING_MODEL,
|
|
42
|
+
top_n: int = -1,
|
|
43
|
+
) -> list[tuple[str, str, float]]:
|
|
44
|
+
"""
|
|
45
|
+
Returns a list of strings and relatednesses, sorted from most related to least.
|
|
46
|
+
"""
|
|
47
|
+
response = embed_query(model, query)
|
|
48
|
+
|
|
49
|
+
query_embedding = response.data[0]["embedding"]
|
|
50
|
+
|
|
51
|
+
scored_strings = [
|
|
52
|
+
(key, text, relatedness_fn(query_embedding, emb))
|
|
53
|
+
for key, text, emb in embeddings.as_iterable()
|
|
54
|
+
]
|
|
55
|
+
scored_strings.sort(key=lambda x: x[2], reverse=True)
|
|
56
|
+
|
|
57
|
+
return scored_strings[:top_n]
|
kash/exec/__init__.py
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
# flake8: noqa: F401
|
|
2
|
-
|
|
3
1
|
from kash.exec.action_decorators import kash_action, kash_action_class
|
|
4
2
|
from kash.exec.action_exec import SkipItem, prepare_action_input, run_action_with_shell_context
|
|
5
|
-
from kash.exec.action_registry import import_action_subdirs
|
|
6
3
|
from kash.exec.command_registry import kash_command
|
|
4
|
+
from kash.exec.importing import import_and_register
|
|
7
5
|
from kash.exec.llm_transforms import llm_transform_item, llm_transform_str
|
|
8
6
|
from kash.exec.precondition_registry import kash_precondition
|
|
9
7
|
from kash.exec.resolve_args import (
|
|
@@ -14,3 +12,22 @@ from kash.exec.resolve_args import (
|
|
|
14
12
|
resolve_locator_arg,
|
|
15
13
|
resolve_path_arg,
|
|
16
14
|
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"kash_action",
|
|
18
|
+
"kash_action_class",
|
|
19
|
+
"SkipItem",
|
|
20
|
+
"prepare_action_input",
|
|
21
|
+
"run_action_with_shell_context",
|
|
22
|
+
"kash_command",
|
|
23
|
+
"import_and_register",
|
|
24
|
+
"llm_transform_item",
|
|
25
|
+
"llm_transform_str",
|
|
26
|
+
"kash_precondition",
|
|
27
|
+
"assemble_path_args",
|
|
28
|
+
"assemble_store_path_args",
|
|
29
|
+
"import_locator_args",
|
|
30
|
+
"resolvable_paths",
|
|
31
|
+
"resolve_locator_arg",
|
|
32
|
+
"resolve_path_arg",
|
|
33
|
+
]
|
kash/exec/action_decorators.py
CHANGED
|
@@ -210,7 +210,12 @@ def kash_action(
|
|
|
210
210
|
title_template: TitleTemplate = TitleTemplate("{title}"),
|
|
211
211
|
llm_options: LLMOptions = LLMOptions(),
|
|
212
212
|
override_state: State | None = None,
|
|
213
|
+
# Including these for completeness but usually don't want to set them globally
|
|
214
|
+
# in the decorator:
|
|
213
215
|
rerun: bool = False,
|
|
216
|
+
refetch: bool = False,
|
|
217
|
+
tmp_output: bool = False,
|
|
218
|
+
no_format: bool = False,
|
|
214
219
|
) -> Callable[[AF], AF]:
|
|
215
220
|
"""
|
|
216
221
|
A function decorator to create and register an action. The annotated function must
|
|
@@ -333,8 +338,11 @@ def kash_action(
|
|
|
333
338
|
else:
|
|
334
339
|
kw_args[fp.name] = self.get_param(fp.name)
|
|
335
340
|
|
|
336
|
-
|
|
337
|
-
|
|
341
|
+
if self.params:
|
|
342
|
+
log.info("Action function param declarations:\n%s", fmt_lines(self.params))
|
|
343
|
+
log.info("Action function param values:\n%s", self.param_value_summary_str())
|
|
344
|
+
else:
|
|
345
|
+
log.info("Action function has no declared params")
|
|
338
346
|
|
|
339
347
|
log.message(
|
|
340
348
|
"Action function call:\n%s",
|
|
@@ -379,11 +387,17 @@ def kash_action(
|
|
|
379
387
|
context = provided_context
|
|
380
388
|
else:
|
|
381
389
|
context = ExecContext(
|
|
382
|
-
action,
|
|
390
|
+
action,
|
|
391
|
+
current_ws().base_dir,
|
|
392
|
+
rerun=rerun,
|
|
393
|
+
refetch=refetch,
|
|
394
|
+
override_state=override_state,
|
|
395
|
+
tmp_output=tmp_output,
|
|
396
|
+
no_format=no_format,
|
|
383
397
|
)
|
|
384
398
|
|
|
385
399
|
# Run the action.
|
|
386
|
-
result, _, _ = run_action_with_caching(context, action_input
|
|
400
|
+
result, _, _ = run_action_with_caching(context, action_input)
|
|
387
401
|
|
|
388
402
|
return result
|
|
389
403
|
|