kash-shell 0.3.10__py3-none-any.whl → 0.3.12__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/format_markdown_template.py +2 -5
- kash/actions/core/markdownify.py +2 -4
- kash/actions/core/readability.py +2 -4
- kash/actions/core/render_as_html.py +30 -11
- kash/actions/core/show_webpage.py +6 -11
- kash/actions/core/strip_html.py +4 -8
- kash/actions/core/{webpage_config.py → tabbed_webpage_config.py} +5 -3
- kash/actions/core/{webpage_generate.py → tabbed_webpage_generate.py} +5 -4
- kash/commands/base/basic_file_commands.py +21 -3
- kash/commands/base/files_command.py +29 -10
- kash/commands/extras/parse_uv_lock.py +12 -3
- kash/commands/workspace/selection_commands.py +1 -1
- kash/commands/workspace/workspace_commands.py +2 -3
- kash/config/colors.py +2 -2
- 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/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 +10 -25
- kash/exec/action_exec.py +43 -23
- kash/exec/llm_transforms.py +6 -3
- kash/exec/preconditions.py +10 -12
- kash/exec/resolve_args.py +4 -0
- kash/exec/runtime_settings.py +134 -0
- kash/exec/shell_callable_action.py +5 -3
- kash/file_storage/file_store.py +37 -38
- kash/file_storage/item_file_format.py +6 -3
- kash/file_storage/store_filenames.py +6 -3
- kash/help/function_param_info.py +1 -1
- kash/llm_utils/init_litellm.py +16 -0
- kash/llm_utils/llm_api_keys.py +6 -2
- kash/llm_utils/llm_completion.py +11 -4
- kash/local_server/local_server_routes.py +1 -7
- kash/mcp/mcp_cli.py +3 -2
- kash/mcp/mcp_server_routes.py +11 -12
- kash/media_base/transcription_deepgram.py +15 -2
- kash/model/__init__.py +1 -1
- kash/model/actions_model.py +6 -54
- kash/model/exec_model.py +79 -0
- kash/model/items_model.py +102 -35
- 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/utils/exception_printing.py +2 -2
- kash/shell/utils/shell_function_wrapper.py +15 -15
- kash/text_handling/doc_normalization.py +16 -8
- kash/text_handling/markdown_render.py +1 -0
- kash/text_handling/markdown_utils.py +105 -2
- kash/utils/common/format_utils.py +2 -8
- kash/utils/common/function_inspect.py +360 -110
- kash/utils/common/inflection.py +22 -0
- kash/utils/common/task_stack.py +4 -15
- kash/utils/errors.py +14 -9
- kash/utils/file_utils/file_ext.py +4 -0
- kash/utils/file_utils/file_formats_model.py +32 -1
- kash/utils/file_utils/file_sort_filter.py +10 -3
- kash/web_gen/__init__.py +0 -4
- kash/web_gen/simple_webpage.py +52 -0
- kash/web_gen/tabbed_webpage.py +23 -16
- kash/web_gen/template_render.py +37 -2
- kash/web_gen/templates/base_styles.css.jinja +84 -59
- kash/web_gen/templates/base_webpage.html.jinja +85 -67
- kash/web_gen/templates/item_view.html.jinja +47 -37
- kash/web_gen/templates/simple_webpage.html.jinja +24 -0
- kash/web_gen/templates/tabbed_webpage.html.jinja +42 -32
- kash/workspaces/__init__.py +12 -3
- kash/workspaces/workspace_dirs.py +58 -0
- kash/workspaces/workspace_importing.py +1 -1
- kash/workspaces/workspaces.py +26 -90
- {kash_shell-0.3.10.dist-info → kash_shell-0.3.12.dist-info}/METADATA +7 -7
- {kash_shell-0.3.10.dist-info → kash_shell-0.3.12.dist-info}/RECORD +81 -76
- kash/shell/utils/argparse_utils.py +0 -20
- kash/utils/lang_utils/inflection.py +0 -18
- {kash_shell-0.3.10.dist-info → kash_shell-0.3.12.dist-info}/WHEEL +0 -0
- {kash_shell-0.3.10.dist-info → kash_shell-0.3.12.dist-info}/entry_points.txt +0 -0
- {kash_shell-0.3.10.dist-info → kash_shell-0.3.12.dist-info}/licenses/LICENSE +0 -0
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/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
|
|
|
@@ -174,7 +174,7 @@ def _merge_param_declarations(
|
|
|
174
174
|
merged_params[fp.name] = Param(
|
|
175
175
|
name=fp.name,
|
|
176
176
|
description=None,
|
|
177
|
-
type=fp.
|
|
177
|
+
type=fp.effective_type or str,
|
|
178
178
|
default_value=fp.default if fp.has_default else None,
|
|
179
179
|
is_explicit=not fp.has_default,
|
|
180
180
|
)
|
|
@@ -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
|
|
@@ -248,13 +241,13 @@ def kash_action(
|
|
|
248
241
|
|
|
249
242
|
# Inspect and sanity check the formal params.
|
|
250
243
|
func_params = inspect_function_params(orig_func)
|
|
251
|
-
if len(func_params) == 0 or func_params[0].
|
|
244
|
+
if len(func_params) == 0 or func_params[0].effective_type not in (ActionInput, Item):
|
|
252
245
|
raise InvalidDefinition(
|
|
253
246
|
f"Decorator `@kash_action` requires exactly one positional parameter, "
|
|
254
247
|
f"`input` of type `ActionInput` or `Item` on function `{orig_func.__name__}` but "
|
|
255
248
|
f"got params: {func_params}"
|
|
256
249
|
)
|
|
257
|
-
if any(fp.
|
|
250
|
+
if any(fp.is_pure_positional for fp in func_params[1:]):
|
|
258
251
|
raise InvalidDefinition(
|
|
259
252
|
"Decorator `@kash_action` requires all parameters after the first positional "
|
|
260
253
|
f"parameter to be keyword parameters on function `{orig_func.__name__}` but "
|
|
@@ -265,7 +258,7 @@ def kash_action(
|
|
|
265
258
|
context_param = next((fp for fp in func_params if fp.name == "context"), None)
|
|
266
259
|
if context_param:
|
|
267
260
|
func_params.remove(context_param)
|
|
268
|
-
if context_param and context_param.
|
|
261
|
+
if context_param and context_param.is_pure_positional:
|
|
269
262
|
raise InvalidDefinition(
|
|
270
263
|
"Decorator `@kash_action` requires the `context` parameter to be a keyword "
|
|
271
264
|
"parameter, not positional, on function `{func.__name__}`"
|
|
@@ -273,7 +266,7 @@ def kash_action(
|
|
|
273
266
|
|
|
274
267
|
# If the original function is a simple action function (processes a single item),
|
|
275
268
|
# wrap it to convert to an ActionFunction.
|
|
276
|
-
is_simple_func = func_params[0].
|
|
269
|
+
is_simple_func = func_params[0].effective_type == Item
|
|
277
270
|
action_func: ActionFunction
|
|
278
271
|
if is_simple_func:
|
|
279
272
|
simple_func = cast(SimpleActionFunction, orig_func)
|
|
@@ -333,7 +326,7 @@ def kash_action(
|
|
|
333
326
|
if context_param:
|
|
334
327
|
kw_args["context"] = context
|
|
335
328
|
for fp in func_params[1:]:
|
|
336
|
-
if fp.
|
|
329
|
+
if fp.is_pure_positional:
|
|
337
330
|
pos_args.append(self.get_param(fp.name))
|
|
338
331
|
else:
|
|
339
332
|
kw_args[fp.name] = self.get_param(fp.name)
|
|
@@ -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)
|