kash-shell 0.3.11__py3-none-any.whl → 0.3.13__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/core/markdownify.py +5 -4
- kash/actions/core/readability.py +4 -4
- kash/actions/core/render_as_html.py +8 -6
- kash/actions/core/show_webpage.py +2 -2
- kash/actions/core/strip_html.py +2 -2
- kash/commands/base/basic_file_commands.py +24 -3
- kash/commands/base/diff_commands.py +38 -3
- kash/commands/base/files_command.py +5 -4
- kash/commands/base/reformat_command.py +1 -1
- kash/commands/base/show_command.py +1 -1
- kash/commands/extras/parse_uv_lock.py +12 -3
- kash/commands/workspace/selection_commands.py +1 -1
- kash/commands/workspace/workspace_commands.py +62 -16
- kash/config/env_settings.py +2 -42
- kash/config/logger.py +30 -25
- kash/config/logger_basic.py +6 -6
- kash/config/settings.py +23 -7
- kash/config/setup.py +33 -5
- kash/config/text_styles.py +25 -22
- kash/docs/load_source_code.py +1 -1
- kash/embeddings/cosine.py +12 -4
- kash/embeddings/embeddings.py +16 -6
- kash/embeddings/text_similarity.py +10 -4
- kash/exec/__init__.py +3 -0
- kash/exec/action_decorators.py +4 -19
- kash/exec/action_exec.py +46 -27
- kash/exec/fetch_url_metadata.py +8 -5
- kash/exec/importing.py +4 -4
- kash/exec/llm_transforms.py +2 -2
- kash/exec/preconditions.py +11 -19
- kash/exec/runtime_settings.py +134 -0
- kash/exec/shell_callable_action.py +5 -3
- kash/file_storage/file_store.py +91 -53
- kash/file_storage/item_file_format.py +6 -3
- kash/file_storage/store_filenames.py +7 -3
- kash/help/help_embeddings.py +2 -2
- kash/llm_utils/clean_headings.py +1 -1
- kash/{text_handling → llm_utils}/custom_sliding_transforms.py +0 -3
- kash/llm_utils/init_litellm.py +16 -0
- kash/llm_utils/llm_api_keys.py +6 -2
- kash/llm_utils/llm_completion.py +12 -5
- kash/local_server/__init__.py +1 -1
- kash/local_server/local_server_commands.py +2 -1
- kash/mcp/__init__.py +1 -1
- kash/mcp/mcp_cli.py +3 -2
- kash/mcp/mcp_server_commands.py +8 -2
- kash/mcp/mcp_server_routes.py +11 -12
- kash/media_base/media_cache.py +10 -3
- kash/media_base/transcription_deepgram.py +15 -2
- kash/model/__init__.py +1 -1
- kash/model/actions_model.py +9 -54
- kash/model/exec_model.py +79 -0
- kash/model/items_model.py +131 -81
- kash/model/operations_model.py +38 -15
- kash/model/paths_model.py +2 -0
- kash/shell/output/shell_output.py +10 -8
- kash/shell/shell_main.py +2 -2
- kash/shell/ui/shell_results.py +2 -1
- kash/shell/utils/exception_printing.py +2 -2
- kash/utils/common/format_utils.py +0 -14
- kash/utils/common/import_utils.py +46 -18
- kash/utils/common/task_stack.py +4 -15
- kash/utils/errors.py +14 -9
- kash/utils/file_utils/file_formats_model.py +61 -26
- kash/utils/file_utils/file_sort_filter.py +10 -3
- kash/utils/file_utils/filename_parsing.py +41 -16
- kash/{text_handling → utils/text_handling}/doc_normalization.py +23 -13
- kash/utils/text_handling/escape_html_tags.py +156 -0
- kash/{text_handling → utils/text_handling}/markdown_utils.py +82 -4
- kash/utils/text_handling/markdownify_utils.py +87 -0
- kash/{text_handling → utils/text_handling}/unified_diffs.py +1 -44
- kash/web_content/file_cache_utils.py +42 -34
- kash/web_content/local_file_cache.py +29 -12
- kash/web_content/web_extract.py +1 -1
- kash/web_content/web_extract_readabilipy.py +4 -2
- kash/web_content/web_fetch.py +42 -7
- kash/web_content/web_page_model.py +2 -1
- kash/web_gen/simple_webpage.py +1 -1
- kash/web_gen/templates/base_styles.css.jinja +139 -16
- kash/web_gen/templates/simple_webpage.html.jinja +1 -1
- kash/workspaces/__init__.py +12 -3
- kash/workspaces/selections.py +2 -2
- kash/workspaces/workspace_dirs.py +58 -0
- kash/workspaces/workspace_importing.py +2 -2
- kash/workspaces/workspace_output.py +2 -2
- kash/workspaces/workspaces.py +26 -90
- kash/xonsh_custom/load_into_xonsh.py +4 -2
- {kash_shell-0.3.11.dist-info → kash_shell-0.3.13.dist-info}/METADATA +4 -4
- {kash_shell-0.3.11.dist-info → kash_shell-0.3.13.dist-info}/RECORD +93 -89
- kash/shell/utils/argparse_utils.py +0 -20
- kash/utils/lang_utils/inflection.py +0 -18
- /kash/{text_handling → utils/text_handling}/markdown_render.py +0 -0
- {kash_shell-0.3.11.dist-info → kash_shell-0.3.13.dist-info}/WHEEL +0 -0
- {kash_shell-0.3.11.dist-info → kash_shell-0.3.13.dist-info}/entry_points.txt +0 -0
- {kash_shell-0.3.11.dist-info → kash_shell-0.3.13.dist-info}/licenses/LICENSE +0 -0
kash/config/logger.py
CHANGED
|
@@ -55,6 +55,10 @@ class LogSettings:
|
|
|
55
55
|
log_objects_dir: Path
|
|
56
56
|
log_file_path: Path
|
|
57
57
|
|
|
58
|
+
@property
|
|
59
|
+
def is_quiet(self) -> bool:
|
|
60
|
+
return self.log_console_level >= LogLevel.error
|
|
61
|
+
|
|
58
62
|
|
|
59
63
|
LOG_NAME_GLOBAL = "workspace"
|
|
60
64
|
|
|
@@ -83,6 +87,13 @@ def get_log_settings() -> LogSettings:
|
|
|
83
87
|
return _log_settings.copy()
|
|
84
88
|
|
|
85
89
|
|
|
90
|
+
def is_console_quiet() -> bool:
|
|
91
|
+
"""
|
|
92
|
+
Whether to suppress non-logging console output.
|
|
93
|
+
"""
|
|
94
|
+
return global_settings().console_quiet
|
|
95
|
+
|
|
96
|
+
|
|
86
97
|
def make_valid_log_name(name: str) -> str:
|
|
87
98
|
name = str(name).strip().rstrip("/").removesuffix(".log")
|
|
88
99
|
name = re.sub(r"[^\w-]", "_", name)
|
|
@@ -244,14 +255,6 @@ def _do_logging_setup(log_settings: LogSettings):
|
|
|
244
255
|
|
|
245
256
|
# Manually adjust logging for a few packages, removing previous verbose default handlers.
|
|
246
257
|
|
|
247
|
-
try:
|
|
248
|
-
import litellm
|
|
249
|
-
from litellm import _logging # noqa: F401
|
|
250
|
-
|
|
251
|
-
litellm.suppress_debug_info = True # Suppress overly prominent exception messages.
|
|
252
|
-
except ImportError:
|
|
253
|
-
pass
|
|
254
|
-
|
|
255
258
|
log_levels = {
|
|
256
259
|
None: INFO,
|
|
257
260
|
"LiteLLM": INFO,
|
|
@@ -276,10 +279,12 @@ def prefix(line: str, emoji: str = "", warn_emoji: str = "") -> str:
|
|
|
276
279
|
return " ".join(filter(None, [prefix, emojis, line]))
|
|
277
280
|
|
|
278
281
|
|
|
279
|
-
def prefix_args(
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
282
|
+
def prefix_args(
|
|
283
|
+
msg: object, *other_args: object, emoji: str = "", warn_emoji: str = ""
|
|
284
|
+
) -> tuple[str, *tuple[object, ...]]:
|
|
285
|
+
"""Prefixes the string representation of msg and returns it with other_args."""
|
|
286
|
+
prefixed_msg = prefix(str(msg), emoji, warn_emoji)
|
|
287
|
+
return (prefixed_msg,) + other_args
|
|
283
288
|
|
|
284
289
|
|
|
285
290
|
class CustomLogger(logging.Logger):
|
|
@@ -290,29 +295,29 @@ class CustomLogger(logging.Logger):
|
|
|
290
295
|
"""
|
|
291
296
|
|
|
292
297
|
@override
|
|
293
|
-
def debug(self, *args, **kwargs):
|
|
294
|
-
super().debug(*prefix_args(args), **kwargs)
|
|
298
|
+
def debug(self, msg: object, *args: object, **kwargs: Any) -> None:
|
|
299
|
+
super().debug(*prefix_args(msg, *args), **kwargs)
|
|
295
300
|
|
|
296
301
|
@override
|
|
297
|
-
def info(self, *args, **kwargs):
|
|
298
|
-
super().info(*prefix_args(args), **kwargs)
|
|
302
|
+
def info(self, msg: object, *args: object, **kwargs: Any) -> None:
|
|
303
|
+
super().info(*prefix_args(msg, *args), **kwargs)
|
|
299
304
|
|
|
300
305
|
@override
|
|
301
|
-
def warning(self, *args, **kwargs):
|
|
302
|
-
super().warning(*prefix_args(args, warn_emoji=EMOJI_WARN), **kwargs)
|
|
306
|
+
def warning(self, msg: object, *args: object, **kwargs: Any) -> None:
|
|
307
|
+
super().warning(*prefix_args(msg, *args, warn_emoji=EMOJI_WARN), **kwargs)
|
|
303
308
|
|
|
304
309
|
@override
|
|
305
|
-
def error(self, *args, **kwargs):
|
|
306
|
-
super().error(*prefix_args(args, warn_emoji=EMOJI_ERROR), **kwargs)
|
|
310
|
+
def error(self, msg: object, *args: object, **kwargs: Any) -> None:
|
|
311
|
+
super().error(*prefix_args(msg, *args, warn_emoji=EMOJI_ERROR), **kwargs)
|
|
307
312
|
|
|
308
|
-
def log_at(self, level: LogLevel, *args, **kwargs):
|
|
313
|
+
def log_at(self, level: LogLevel, *args: object, **kwargs: Any) -> None:
|
|
309
314
|
getattr(self, level.name)(*args, **kwargs)
|
|
310
315
|
|
|
311
|
-
def message(self, *args, **kwargs):
|
|
316
|
+
def message(self, msg: object, *args: object, **kwargs: Any) -> None:
|
|
312
317
|
"""
|
|
313
318
|
An informative message that should appear even if log level is set to warning.
|
|
314
319
|
"""
|
|
315
|
-
super().warning(*prefix_args(args), **kwargs)
|
|
320
|
+
super().warning(*prefix_args(msg, *args), **kwargs)
|
|
316
321
|
|
|
317
322
|
def save_object(
|
|
318
323
|
self,
|
|
@@ -321,7 +326,7 @@ class CustomLogger(logging.Logger):
|
|
|
321
326
|
obj: Any,
|
|
322
327
|
level: LogLevel = LogLevel.info,
|
|
323
328
|
file_ext: str = "txt",
|
|
324
|
-
):
|
|
329
|
+
) -> None:
|
|
325
330
|
"""
|
|
326
331
|
Save an object to a file in the log directory. Useful for details too large to
|
|
327
332
|
log normally but useful for debugging.
|
|
@@ -342,7 +347,7 @@ class CustomLogger(logging.Logger):
|
|
|
342
347
|
|
|
343
348
|
self.log_at(level, "%s %s saved: %s", EMOJI_SAVED, description, path)
|
|
344
349
|
|
|
345
|
-
def dump_stack(self, all_threads: bool = True, level: LogLevel = LogLevel.info):
|
|
350
|
+
def dump_stack(self, all_threads: bool = True, level: LogLevel = LogLevel.info) -> None:
|
|
346
351
|
self.log_at(level, "Stack trace dump:\n%s", current_stack_traces(all_threads))
|
|
347
352
|
|
|
348
353
|
def __repr__(self):
|
kash/config/logger_basic.py
CHANGED
|
@@ -3,7 +3,7 @@ import sys
|
|
|
3
3
|
from logging import FileHandler, Formatter, LogRecord
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
from kash.config.settings import LogLevel
|
|
6
|
+
from kash.config.settings import LogLevel, LogLevelStr
|
|
7
7
|
from kash.config.suppress_warnings import demote_warnings
|
|
8
8
|
|
|
9
9
|
# Basic logging setup for non-interactive logging, like on a server.
|
|
@@ -16,21 +16,21 @@ class SuppressedWarningsStreamHandler(logging.StreamHandler):
|
|
|
16
16
|
super().emit(record)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
def basic_file_handler(path: Path, level: LogLevel) -> logging.FileHandler:
|
|
19
|
+
def basic_file_handler(path: Path, level: LogLevel | LogLevelStr) -> logging.FileHandler:
|
|
20
20
|
handler = logging.FileHandler(path)
|
|
21
|
-
handler.setLevel(level.value)
|
|
21
|
+
handler.setLevel(LogLevel.parse(level).value)
|
|
22
22
|
handler.setFormatter(Formatter("%(asctime)s %(levelname).1s %(name)s - %(message)s"))
|
|
23
23
|
return handler
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
def basic_stderr_handler(level: LogLevel) -> logging.StreamHandler:
|
|
26
|
+
def basic_stderr_handler(level: LogLevel | LogLevelStr) -> logging.StreamHandler:
|
|
27
27
|
handler = SuppressedWarningsStreamHandler(stream=sys.stderr)
|
|
28
|
-
handler.setLevel(level.value)
|
|
28
|
+
handler.setLevel(LogLevel.parse(level).value)
|
|
29
29
|
handler.setFormatter(Formatter("%(asctime)s %(levelname).1s %(name)s - %(message)s"))
|
|
30
30
|
return handler
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
def basic_logging_setup(log_path: Path | None, level: LogLevel):
|
|
33
|
+
def basic_logging_setup(log_path: Path | None, level: LogLevel | LogLevelStr):
|
|
34
34
|
"""
|
|
35
35
|
Set up basic logging to a file and to stderr.
|
|
36
36
|
"""
|
kash/config/settings.py
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import os
|
|
2
|
-
from
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from enum import IntEnum
|
|
3
6
|
from functools import cache
|
|
4
7
|
from logging import DEBUG, ERROR, INFO, WARNING
|
|
5
8
|
from pathlib import Path
|
|
9
|
+
from typing import Literal
|
|
6
10
|
|
|
7
|
-
from pydantic.dataclasses import dataclass
|
|
8
11
|
from strif import AtomicVar
|
|
9
12
|
|
|
10
13
|
from kash.config.env_settings import KashEnv
|
|
@@ -64,7 +67,14 @@ LOCAL_SERVER_PORTS_MAX = 30
|
|
|
64
67
|
LOCAL_SERVER_LOG_NAME = "local_server"
|
|
65
68
|
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
LogLevelStr = Literal["debug", "info", "message", "warning", "error"]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class LogLevel(IntEnum):
|
|
74
|
+
"""
|
|
75
|
+
Convenience enum for log levels with parsing and ordering.
|
|
76
|
+
"""
|
|
77
|
+
|
|
68
78
|
debug = DEBUG
|
|
69
79
|
info = INFO
|
|
70
80
|
warning = WARNING
|
|
@@ -72,7 +82,9 @@ class LogLevel(Enum):
|
|
|
72
82
|
error = ERROR
|
|
73
83
|
|
|
74
84
|
@classmethod
|
|
75
|
-
def parse(cls, level_str: str):
|
|
85
|
+
def parse(cls, level_str: str | LogLevelStr | LogLevel) -> LogLevel:
|
|
86
|
+
if isinstance(level_str, LogLevel):
|
|
87
|
+
return level_str
|
|
76
88
|
canon_name = level_str.strip().lower()
|
|
77
89
|
if canon_name == "warn":
|
|
78
90
|
canon_name = "warning"
|
|
@@ -87,7 +99,7 @@ class LogLevel(Enum):
|
|
|
87
99
|
return self.name
|
|
88
100
|
|
|
89
101
|
|
|
90
|
-
DEFAULT_LOG_LEVEL = LogLevel.parse(KashEnv.KASH_LOG_LEVEL.read_str("warning"))
|
|
102
|
+
DEFAULT_LOG_LEVEL = LogLevel.parse(KashEnv.KASH_LOG_LEVEL.read_str(default="warning"))
|
|
91
103
|
|
|
92
104
|
|
|
93
105
|
def resolve_and_create_dirs(path: Path | str, is_dir: bool = False) -> Path:
|
|
@@ -174,6 +186,9 @@ class Settings:
|
|
|
174
186
|
console_log_level: LogLevel
|
|
175
187
|
"""The log level for console-based logging."""
|
|
176
188
|
|
|
189
|
+
console_quiet: bool
|
|
190
|
+
"""If true, suppress non-logging console output."""
|
|
191
|
+
|
|
177
192
|
file_log_level: LogLevel
|
|
178
193
|
"""The log level for file-based logging."""
|
|
179
194
|
|
|
@@ -205,7 +220,7 @@ def _get_ws_root_dir() -> Path:
|
|
|
205
220
|
|
|
206
221
|
|
|
207
222
|
def _get_global_ws_dir() -> Path:
|
|
208
|
-
kash_ws_dir = KashEnv.KASH_GLOBAL_WS.read_path()
|
|
223
|
+
kash_ws_dir = KashEnv.KASH_GLOBAL_WS.read_path(default=None)
|
|
209
224
|
if kash_ws_dir:
|
|
210
225
|
return kash_ws_dir
|
|
211
226
|
else:
|
|
@@ -225,7 +240,7 @@ def _get_system_cache_dir() -> Path:
|
|
|
225
240
|
|
|
226
241
|
|
|
227
242
|
def _get_mcp_ws_dir() -> Path | None:
|
|
228
|
-
mcp_dir = KashEnv.KASH_MCP_WS.read_str()
|
|
243
|
+
mcp_dir = KashEnv.KASH_MCP_WS.read_str(default=None)
|
|
229
244
|
if mcp_dir:
|
|
230
245
|
return Path(mcp_dir).expanduser().resolve()
|
|
231
246
|
else:
|
|
@@ -254,6 +269,7 @@ def _read_settings():
|
|
|
254
269
|
default_editor="nano",
|
|
255
270
|
file_log_level=LogLevel.info,
|
|
256
271
|
console_log_level=DEFAULT_LOG_LEVEL,
|
|
272
|
+
console_quiet=False,
|
|
257
273
|
local_server_ports_start=LOCAL_SERVER_PORT_START,
|
|
258
274
|
local_server_ports_max=LOCAL_SERVER_PORTS_MAX,
|
|
259
275
|
local_server_port=0,
|
kash/config/setup.py
CHANGED
|
@@ -7,7 +7,13 @@ from clideps.env_vars.dotenv_utils import load_dotenv_paths
|
|
|
7
7
|
|
|
8
8
|
from kash.config.logger import reset_rich_logging
|
|
9
9
|
from kash.config.logger_basic import basic_logging_setup
|
|
10
|
-
from kash.config.settings import
|
|
10
|
+
from kash.config.settings import (
|
|
11
|
+
LogLevel,
|
|
12
|
+
LogLevelStr,
|
|
13
|
+
atomic_global_settings,
|
|
14
|
+
configure_ws_and_settings,
|
|
15
|
+
global_settings,
|
|
16
|
+
)
|
|
11
17
|
|
|
12
18
|
|
|
13
19
|
@cache
|
|
@@ -16,7 +22,9 @@ def kash_setup(
|
|
|
16
22
|
rich_logging: bool,
|
|
17
23
|
kash_ws_root: Path | None = None,
|
|
18
24
|
log_path: Path | None = None,
|
|
19
|
-
|
|
25
|
+
log_level: LogLevel | LogLevelStr | None = None,
|
|
26
|
+
console_log_level: LogLevel | LogLevelStr | None = None,
|
|
27
|
+
console_quiet: bool | None = None,
|
|
20
28
|
):
|
|
21
29
|
"""
|
|
22
30
|
One-time top-level setup of essential logging, keys, directories, and configs.
|
|
@@ -25,8 +33,14 @@ def kash_setup(
|
|
|
25
33
|
Can call this if embedding kash in another app.
|
|
26
34
|
Can be used to set the global default workspace and logs directory
|
|
27
35
|
and/or the default log file.
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
|
|
37
|
+
Basic logging is to the specified log file.
|
|
38
|
+
If enabled, rich logging is to the console as well.
|
|
39
|
+
|
|
40
|
+
By default console is "warning" level but can be controlled with
|
|
41
|
+
the `console_log_level` parameter.
|
|
42
|
+
All console/shell output can be suppressed with `console_quiet`. By default
|
|
43
|
+
console is quiet if `console_log_level` is "error" or higher.
|
|
30
44
|
"""
|
|
31
45
|
from kash.utils.common.stack_traces import add_stacktrace_handler
|
|
32
46
|
|
|
@@ -40,10 +54,24 @@ def kash_setup(
|
|
|
40
54
|
configure_ws_and_settings(kash_ws_root)
|
|
41
55
|
|
|
42
56
|
# Now set up logging, as it might depend on workspace root.
|
|
57
|
+
log_level = LogLevel.parse(log_level) if log_level else LogLevel.info
|
|
58
|
+
|
|
43
59
|
if rich_logging:
|
|
60
|
+
# These settings are only used for rich logging.
|
|
61
|
+
console_log_level = (
|
|
62
|
+
LogLevel.parse(console_log_level) if console_log_level else LogLevel.warning
|
|
63
|
+
)
|
|
64
|
+
console_quiet = (
|
|
65
|
+
console_quiet if console_quiet is not None else console_log_level >= LogLevel.error
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
with atomic_global_settings().updates() as settings:
|
|
69
|
+
settings.console_log_level = console_log_level
|
|
70
|
+
settings.file_log_level = log_level
|
|
71
|
+
settings.console_quiet = console_quiet
|
|
44
72
|
reset_rich_logging(log_path=log_path)
|
|
45
73
|
else:
|
|
46
|
-
basic_logging_setup(log_path=log_path, level=
|
|
74
|
+
basic_logging_setup(log_path=log_path, level=log_level)
|
|
47
75
|
|
|
48
76
|
_lib_setup()
|
|
49
77
|
|
kash/config/text_styles.py
CHANGED
|
@@ -73,8 +73,8 @@ COLOR_VALUE = "cyan"
|
|
|
73
73
|
COLOR_PATH = "cyan"
|
|
74
74
|
COLOR_HINT = "bright_black"
|
|
75
75
|
COLOR_HINT_DIM = "dim bright_black"
|
|
76
|
-
COLOR_SKIP = "
|
|
77
|
-
|
|
76
|
+
COLOR_SKIP = "bright_blue"
|
|
77
|
+
COLOR_ACTION = "magenta"
|
|
78
78
|
COLOR_SAVED = "blue"
|
|
79
79
|
COLOR_TIMING = "bright_black"
|
|
80
80
|
COLOR_CALL = "bright_yellow"
|
|
@@ -133,7 +133,7 @@ RICH_STYLES = {
|
|
|
133
133
|
"hint": COLOR_HINT,
|
|
134
134
|
"hint_dim": COLOR_HINT_DIM,
|
|
135
135
|
"skip": COLOR_SKIP,
|
|
136
|
-
"
|
|
136
|
+
"action": COLOR_ACTION,
|
|
137
137
|
"saved": COLOR_SAVED,
|
|
138
138
|
"timing": COLOR_TIMING,
|
|
139
139
|
"call": COLOR_CALL,
|
|
@@ -218,12 +218,14 @@ RICH_STYLES = {
|
|
|
218
218
|
"kash.size_k": STYLE_SIZE2,
|
|
219
219
|
"kash.size_m": STYLE_SIZE3,
|
|
220
220
|
"kash.size_gtp": STYLE_SIZE4,
|
|
221
|
-
"kash.filename": Style(color=
|
|
222
|
-
"kash.
|
|
223
|
-
"kash.
|
|
221
|
+
"kash.filename": Style(color=COLOR_HINT),
|
|
222
|
+
"kash.start_action": Style(color=COLOR_ACTION, bold=True),
|
|
223
|
+
"kash.task_stack_header": Style(color=COLOR_HINT),
|
|
224
|
+
"kash.task_stack": Style(color=COLOR_HINT),
|
|
224
225
|
"kash.task_stack_prefix": Style(color=COLOR_HINT),
|
|
225
226
|
# Emoji colors:
|
|
226
|
-
"kash.
|
|
227
|
+
"kash.action": Style(color=COLOR_ACTION),
|
|
228
|
+
"kash.start": Style(color=COLOR_ACTION, bold=True),
|
|
227
229
|
"kash.success": Style(color=COLOR_SUCCESS, bold=True),
|
|
228
230
|
"kash.skip": Style(color=COLOR_SKIP, bold=True),
|
|
229
231
|
"kash.failure": Style(color=COLOR_ERROR, bold=True),
|
|
@@ -259,17 +261,24 @@ PROMPT_ASSIST = "(assistant) ❯"
|
|
|
259
261
|
|
|
260
262
|
EMOJI_HINT = "👉"
|
|
261
263
|
|
|
262
|
-
|
|
264
|
+
EMOJI_MSG_INDENT = "⋮"
|
|
263
265
|
|
|
266
|
+
EMOJI_START = "[➤]"
|
|
264
267
|
|
|
265
|
-
|
|
268
|
+
EMOJI_SUCCESS = "[✔︎]"
|
|
266
269
|
|
|
267
|
-
|
|
270
|
+
EMOJI_SKIP = "[-]"
|
|
271
|
+
|
|
272
|
+
EMOJI_FAILURE = "[✘]"
|
|
268
273
|
|
|
269
274
|
EMOJI_SNIPPET = "❯"
|
|
270
275
|
|
|
271
276
|
EMOJI_HELP = "?"
|
|
272
277
|
|
|
278
|
+
EMOJI_ACTION = "⛭"
|
|
279
|
+
|
|
280
|
+
EMOJI_COMMAND = "⧁" # More ideas: ⦿⧁⧀⦿⦾⟐⦊⟡
|
|
281
|
+
|
|
273
282
|
EMOJI_SHELL = "⦊"
|
|
274
283
|
|
|
275
284
|
EMOJI_RECOMMENDED = "•"
|
|
@@ -282,26 +291,18 @@ EMOJI_SAVED = "⩣"
|
|
|
282
291
|
|
|
283
292
|
EMOJI_TIMING = "⏱"
|
|
284
293
|
|
|
285
|
-
EMOJI_SUCCESS = "[✔︎]"
|
|
286
|
-
|
|
287
|
-
EMOJI_SKIP = "[∕]"
|
|
288
|
-
|
|
289
|
-
EMOJI_FAILURE = "[✘]"
|
|
290
|
-
|
|
291
294
|
EMOJI_CALL_BEGIN = "≫"
|
|
292
295
|
|
|
293
296
|
EMOJI_CALL_END = "≪"
|
|
294
297
|
|
|
295
298
|
EMOJI_ASSISTANT = "🤖"
|
|
296
299
|
|
|
297
|
-
EMOJI_MSG_INDENT = "⋮"
|
|
298
|
-
|
|
299
300
|
EMOJI_BREADCRUMB_SEP = "›"
|
|
300
301
|
|
|
301
302
|
|
|
302
303
|
## Special headings
|
|
303
304
|
|
|
304
|
-
TASK_STACK_HEADER = "Task stack
|
|
305
|
+
TASK_STACK_HEADER = "Task stack"
|
|
305
306
|
|
|
306
307
|
|
|
307
308
|
## Rich setup
|
|
@@ -320,15 +321,17 @@ class KashHighlighter(RegexHighlighter):
|
|
|
320
321
|
base_style = "kash."
|
|
321
322
|
highlights = [
|
|
322
323
|
_combine_regex(
|
|
324
|
+
# Important patterns that color the whole line:
|
|
325
|
+
f"(?P<start_action>{re.escape(EMOJI_START + ' Action')}.*)",
|
|
326
|
+
f"(?P<timing>{re.escape(EMOJI_TIMING)}.*)",
|
|
323
327
|
# Task stack in logs:
|
|
324
328
|
f"(?P<task_stack_header>{re.escape(TASK_STACK_HEADER)})",
|
|
325
329
|
f"(?P<task_stack>{re.escape(EMOJI_BREADCRUMB_SEP)}.*)",
|
|
326
330
|
f"(?P<task_stack_prefix>{re.escape(EMOJI_MSG_INDENT)})",
|
|
327
|
-
# Emojis that color the whole line:
|
|
328
|
-
f"(?P<timing>{re.escape(EMOJI_TIMING)}.*)",
|
|
329
331
|
# Color emojis by themselves:
|
|
330
332
|
f"(?P<saved>{re.escape(EMOJI_SAVED)})",
|
|
331
|
-
f"(?P<
|
|
333
|
+
f"(?P<action>{re.escape(EMOJI_ACTION)})",
|
|
334
|
+
f"(?P<start>{re.escape(EMOJI_START)})",
|
|
332
335
|
f"(?P<success>{re.escape(EMOJI_SUCCESS)})",
|
|
333
336
|
f"(?P<skip>{re.escape(EMOJI_SKIP)})",
|
|
334
337
|
f"(?P<failure>{re.escape(EMOJI_FAILURE)})",
|
kash/docs/load_source_code.py
CHANGED
|
@@ -104,7 +104,7 @@ def load_source_code() -> SourceCode:
|
|
|
104
104
|
kash_base_path / "model" / "assistant_response_model.py",
|
|
105
105
|
),
|
|
106
106
|
text_tool_src=read_source_code(
|
|
107
|
-
kash_base_path / "text_handling",
|
|
107
|
+
kash_base_path / "utils" / "text_handling",
|
|
108
108
|
kash_base_path / "utils" / "lang_utils",
|
|
109
109
|
# TODO: Include additional dep libs like chopdiff TextDoc too?
|
|
110
110
|
),
|
kash/embeddings/cosine.py
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
from collections.abc import Sequence
|
|
2
|
-
from typing import Any, TypeAlias
|
|
4
|
+
from typing import TYPE_CHECKING, Any, TypeAlias
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from numpy import ndarray
|
|
5
8
|
|
|
6
|
-
# Type aliases for clarity
|
|
7
|
-
ArrayLike: TypeAlias = Sequence[float] |
|
|
9
|
+
# Type aliases for clarity
|
|
10
|
+
ArrayLike: TypeAlias = Sequence[float] | ndarray[Any, Any]
|
|
11
|
+
else:
|
|
12
|
+
# Keep numpy import lazy.
|
|
13
|
+
ArrayLike = Any
|
|
8
14
|
|
|
9
15
|
|
|
10
16
|
def cosine(u: ArrayLike, v: ArrayLike) -> float:
|
|
@@ -12,6 +18,8 @@ def cosine(u: ArrayLike, v: ArrayLike) -> float:
|
|
|
12
18
|
Compute the cosine distance between two 1-D arrays.
|
|
13
19
|
Could use scipy.spatial.distance.cosine, but avoiding the dependency.
|
|
14
20
|
"""
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
15
23
|
# Convert to numpy arrays
|
|
16
24
|
u_array = np.asarray(u, dtype=np.float64)
|
|
17
25
|
v_array = np.asarray(v, dtype=np.float64)
|
kash/embeddings/embeddings.py
CHANGED
|
@@ -3,17 +3,18 @@ from __future__ import annotations
|
|
|
3
3
|
import ast
|
|
4
4
|
from collections.abc import Iterable
|
|
5
5
|
from pathlib import Path
|
|
6
|
-
from typing import TypeAlias, cast
|
|
6
|
+
from typing import TYPE_CHECKING, TypeAlias, cast
|
|
7
7
|
|
|
8
|
-
import pandas as pd
|
|
9
|
-
from litellm import embedding
|
|
10
|
-
from litellm.types.utils import EmbeddingResponse
|
|
11
8
|
from pydantic.dataclasses import dataclass
|
|
12
9
|
from strif import abbrev_list
|
|
13
10
|
|
|
14
11
|
from kash.config.logger import get_logger
|
|
12
|
+
from kash.llm_utils.init_litellm import init_litellm
|
|
15
13
|
from kash.llm_utils.llms import DEFAULT_EMBEDDING_MODEL
|
|
16
14
|
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from pandas import DataFrame
|
|
17
|
+
|
|
17
18
|
log = get_logger(__name__)
|
|
18
19
|
|
|
19
20
|
|
|
@@ -41,11 +42,13 @@ class Embeddings:
|
|
|
41
42
|
def as_iterable(self) -> Iterable[tuple[Key, str, list[float]]]:
|
|
42
43
|
return ((key, text, emb) for key, (text, emb) in self.data.items())
|
|
43
44
|
|
|
44
|
-
def as_df(self) ->
|
|
45
|
+
def as_df(self) -> DataFrame:
|
|
46
|
+
from pandas import DataFrame
|
|
47
|
+
|
|
45
48
|
keys, texts, embeddings = zip(
|
|
46
49
|
*[(key, text, emb) for key, (text, emb) in self.data.items()], strict=False
|
|
47
50
|
)
|
|
48
|
-
return
|
|
51
|
+
return DataFrame(
|
|
49
52
|
{
|
|
50
53
|
"key": keys,
|
|
51
54
|
"text": texts,
|
|
@@ -61,6 +64,11 @@ class Embeddings:
|
|
|
61
64
|
|
|
62
65
|
@classmethod
|
|
63
66
|
def embed(cls, keyvals: list[KeyVal], model=DEFAULT_EMBEDDING_MODEL) -> Embeddings:
|
|
67
|
+
from litellm import embedding
|
|
68
|
+
from litellm.types.utils import EmbeddingResponse
|
|
69
|
+
|
|
70
|
+
init_litellm()
|
|
71
|
+
|
|
64
72
|
data = {}
|
|
65
73
|
log.message(
|
|
66
74
|
"Embedding %d texts (model %s, batch size %s)…",
|
|
@@ -102,6 +110,8 @@ class Embeddings:
|
|
|
102
110
|
|
|
103
111
|
@classmethod
|
|
104
112
|
def read_from_csv(cls, path: Path) -> Embeddings:
|
|
113
|
+
import pandas as pd
|
|
114
|
+
|
|
105
115
|
df = pd.read_csv(path)
|
|
106
116
|
df["embedding"] = df["embedding"].apply(ast.literal_eval)
|
|
107
117
|
data = {row["key"]: (row["text"], row["embedding"]) for _, row in df.iterrows()}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, cast
|
|
2
4
|
|
|
3
|
-
import litellm
|
|
4
5
|
from funlog import log_calls
|
|
5
|
-
from litellm import embedding
|
|
6
|
-
from litellm.types.utils import EmbeddingResponse
|
|
7
6
|
|
|
8
7
|
from kash.config.logger import get_logger
|
|
9
8
|
from kash.embeddings.cosine import ArrayLike, cosine
|
|
@@ -11,6 +10,9 @@ from kash.embeddings.embeddings import Embeddings
|
|
|
11
10
|
from kash.llm_utils.llms import DEFAULT_EMBEDDING_MODEL, EmbeddingModel
|
|
12
11
|
from kash.utils.errors import ApiResultError
|
|
13
12
|
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from litellm.types.utils import EmbeddingResponse
|
|
15
|
+
|
|
14
16
|
log = get_logger(__name__)
|
|
15
17
|
|
|
16
18
|
|
|
@@ -20,6 +22,10 @@ def cosine_relatedness(x: ArrayLike, y: ArrayLike) -> float:
|
|
|
20
22
|
|
|
21
23
|
@log_calls(level="info", show_return_value=False)
|
|
22
24
|
def embed_query(model: EmbeddingModel, query: str) -> EmbeddingResponse:
|
|
25
|
+
import litellm
|
|
26
|
+
from litellm import embedding
|
|
27
|
+
from litellm.types.utils import EmbeddingResponse
|
|
28
|
+
|
|
23
29
|
try:
|
|
24
30
|
response: EmbeddingResponse = cast(
|
|
25
31
|
EmbeddingResponse, embedding(model=model.litellm_name, input=[query])
|
kash/exec/__init__.py
CHANGED
|
@@ -12,6 +12,7 @@ from kash.exec.resolve_args import (
|
|
|
12
12
|
resolve_locator_arg,
|
|
13
13
|
resolve_path_arg,
|
|
14
14
|
)
|
|
15
|
+
from kash.exec.runtime_settings import current_runtime_settings, kash_runtime
|
|
15
16
|
|
|
16
17
|
__all__ = [
|
|
17
18
|
"kash_action",
|
|
@@ -20,6 +21,8 @@ __all__ = [
|
|
|
20
21
|
"prepare_action_input",
|
|
21
22
|
"run_action_with_shell_context",
|
|
22
23
|
"kash_command",
|
|
24
|
+
"kash_runtime",
|
|
25
|
+
"current_runtime_settings",
|
|
23
26
|
"import_and_register",
|
|
24
27
|
"llm_transform_item",
|
|
25
28
|
"llm_transform_str",
|
kash/exec/action_decorators.py
CHANGED
|
@@ -21,22 +21,22 @@ from typing_extensions import override
|
|
|
21
21
|
from kash.config.logger import get_logger
|
|
22
22
|
from kash.exec.action_exec import run_action_with_caching
|
|
23
23
|
from kash.exec.action_registry import register_action_class
|
|
24
|
+
from kash.exec.runtime_settings import current_runtime_settings
|
|
24
25
|
from kash.exec_model.args_model import ONE_ARG, ArgCount, ArgType
|
|
25
26
|
from kash.model.actions_model import (
|
|
26
27
|
Action,
|
|
27
28
|
ActionInput,
|
|
28
29
|
ActionResult,
|
|
29
|
-
ExecContext,
|
|
30
30
|
LLMOptions,
|
|
31
31
|
ParamSource,
|
|
32
32
|
TitleTemplate,
|
|
33
33
|
)
|
|
34
|
-
from kash.model.
|
|
34
|
+
from kash.model.exec_model import ExecContext
|
|
35
|
+
from kash.model.items_model import Item, ItemType
|
|
35
36
|
from kash.model.params_model import Param, ParamDeclarations, TypedParamValues
|
|
36
37
|
from kash.model.preconditions_model import Precondition
|
|
37
38
|
from kash.utils.common.function_inspect import FuncParam, inspect_function_params
|
|
38
39
|
from kash.utils.errors import InvalidDefinition
|
|
39
|
-
from kash.workspaces.workspaces import current_ws
|
|
40
40
|
|
|
41
41
|
log = get_logger(__name__)
|
|
42
42
|
|
|
@@ -209,13 +209,6 @@ def kash_action(
|
|
|
209
209
|
mcp_tool: bool = False,
|
|
210
210
|
title_template: TitleTemplate = TitleTemplate("{title}"),
|
|
211
211
|
llm_options: LLMOptions = LLMOptions(),
|
|
212
|
-
override_state: State | None = None,
|
|
213
|
-
# Including these for completeness but usually don't want to set them globally
|
|
214
|
-
# in the decorator:
|
|
215
|
-
rerun: bool = False,
|
|
216
|
-
refetch: bool = False,
|
|
217
|
-
tmp_output: bool = False,
|
|
218
|
-
no_format: bool = False,
|
|
219
212
|
) -> Callable[[AF], AF]:
|
|
220
213
|
"""
|
|
221
214
|
A function decorator to create and register an action. The annotated function must
|
|
@@ -386,15 +379,7 @@ def kash_action(
|
|
|
386
379
|
if provided_context:
|
|
387
380
|
context = provided_context
|
|
388
381
|
else:
|
|
389
|
-
context = ExecContext(
|
|
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,
|
|
397
|
-
)
|
|
382
|
+
context = ExecContext(action, current_runtime_settings())
|
|
398
383
|
|
|
399
384
|
# Run the action.
|
|
400
385
|
result, _, _ = run_action_with_caching(context, action_input)
|