kash-shell 0.3.11__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/render_as_html.py +2 -2
- kash/actions/core/show_webpage.py +2 -2
- kash/actions/core/strip_html.py +2 -2
- kash/commands/base/basic_file_commands.py +21 -3
- kash/commands/base/files_command.py +5 -4
- kash/commands/extras/parse_uv_lock.py +12 -3
- kash/commands/workspace/selection_commands.py +1 -1
- kash/commands/workspace/workspace_commands.py +1 -1
- 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 +4 -19
- kash/exec/action_exec.py +43 -23
- kash/exec/llm_transforms.py +2 -2
- kash/exec/preconditions.py +4 -12
- kash/exec/runtime_settings.py +134 -0
- kash/exec/shell_callable_action.py +5 -3
- kash/file_storage/file_store.py +18 -21
- kash/file_storage/item_file_format.py +6 -3
- kash/file_storage/store_filenames.py +6 -3
- 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/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 +71 -50
- 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/text_handling/doc_normalization.py +16 -8
- kash/text_handling/markdown_utils.py +83 -2
- kash/utils/common/format_utils.py +2 -8
- 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_formats_model.py +15 -0
- kash/utils/file_utils/file_sort_filter.py +10 -3
- kash/web_gen/templates/base_styles.css.jinja +8 -3
- 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.11.dist-info → kash_shell-0.3.12.dist-info}/METADATA +4 -4
- {kash_shell-0.3.11.dist-info → kash_shell-0.3.12.dist-info}/RECORD +60 -57
- kash/shell/utils/argparse_utils.py +0 -20
- kash/utils/lang_utils/inflection.py +0 -18
- {kash_shell-0.3.11.dist-info → kash_shell-0.3.12.dist-info}/WHEEL +0 -0
- {kash_shell-0.3.11.dist-info → kash_shell-0.3.12.dist-info}/entry_points.txt +0 -0
- {kash_shell-0.3.11.dist-info → kash_shell-0.3.12.dist-info}/licenses/LICENSE +0 -0
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
|
|
|
@@ -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)
|
kash/exec/action_exec.py
CHANGED
|
@@ -4,7 +4,12 @@ from dataclasses import replace
|
|
|
4
4
|
from prettyfmt import fmt_lines
|
|
5
5
|
|
|
6
6
|
from kash.config.logger import get_logger
|
|
7
|
-
from kash.config.text_styles import
|
|
7
|
+
from kash.config.text_styles import (
|
|
8
|
+
EMOJI_SKIP,
|
|
9
|
+
EMOJI_START,
|
|
10
|
+
EMOJI_SUCCESS,
|
|
11
|
+
EMOJI_TIMING,
|
|
12
|
+
)
|
|
8
13
|
from kash.exec.preconditions import is_url_item
|
|
9
14
|
from kash.exec.resolve_args import assemble_action_args
|
|
10
15
|
from kash.exec_model.args_model import CommandArg
|
|
@@ -17,15 +22,16 @@ from kash.model.actions_model import (
|
|
|
17
22
|
ExecContext,
|
|
18
23
|
PathOpType,
|
|
19
24
|
)
|
|
25
|
+
from kash.model.exec_model import RuntimeSettings
|
|
20
26
|
from kash.model.items_model import Item, State
|
|
21
27
|
from kash.model.operations_model import Input, Operation, Source
|
|
22
28
|
from kash.model.params_model import ALL_COMMON_PARAMS, GLOBAL_PARAMS, RawParamValues
|
|
23
29
|
from kash.model.paths_model import StorePath
|
|
24
|
-
from kash.shell.output.shell_output import PrintHooks
|
|
30
|
+
from kash.shell.output.shell_output import PrintHooks
|
|
31
|
+
from kash.utils.common.inflection import plural
|
|
25
32
|
from kash.utils.common.task_stack import task_stack
|
|
26
33
|
from kash.utils.common.type_utils import not_none
|
|
27
|
-
from kash.utils.errors import
|
|
28
|
-
from kash.utils.lang_utils.inflection import plural
|
|
34
|
+
from kash.utils.errors import ContentError, InvalidOutput, get_nonfatal_exceptions
|
|
29
35
|
from kash.workspaces import Selection, current_ws
|
|
30
36
|
from kash.workspaces.workspace_importing import import_and_load
|
|
31
37
|
|
|
@@ -63,6 +69,7 @@ def validate_action_input(
|
|
|
63
69
|
"""
|
|
64
70
|
Validate an action input, ensuring the right number of args, all explicit params are filled,
|
|
65
71
|
and the precondition holds and return an `Operation` that describes what will happen.
|
|
72
|
+
For flexibility, we don't require the items to be saved (have a store path).
|
|
66
73
|
"""
|
|
67
74
|
input_items = action_input.items
|
|
68
75
|
# Validations:
|
|
@@ -75,10 +82,16 @@ def validate_action_input(
|
|
|
75
82
|
|
|
76
83
|
# Now make a note of the the operation we will perform.
|
|
77
84
|
# If the inputs are paths, record the input paths, including hashes.
|
|
78
|
-
|
|
79
|
-
|
|
85
|
+
def input_for(item: Item) -> Input:
|
|
86
|
+
if item.store_path:
|
|
87
|
+
return Input(StorePath(item.store_path), ws.hash(StorePath(item.store_path)))
|
|
88
|
+
else:
|
|
89
|
+
return Input(path=None, source_info="unsaved")
|
|
90
|
+
|
|
91
|
+
inputs = [input_for(item) for item in input_items]
|
|
92
|
+
|
|
80
93
|
# Add any non-default runtime options into the options summary.
|
|
81
|
-
options = {**action.param_value_summary(), **context.
|
|
94
|
+
options = {**action.param_value_summary(), **context.settings.non_default_options}
|
|
82
95
|
operation = Operation(action.name, inputs, options)
|
|
83
96
|
|
|
84
97
|
return operation
|
|
@@ -89,7 +102,7 @@ def log_action(action: Action, action_input: ActionInput, operation: Operation):
|
|
|
89
102
|
Log the action and the operation we are about to run.
|
|
90
103
|
"""
|
|
91
104
|
PrintHooks.before_log_action_run()
|
|
92
|
-
|
|
105
|
+
log.message("%s Action: `%s`", EMOJI_START, action.name)
|
|
93
106
|
log.message("Running: `%s`", operation.command_line(with_options=True))
|
|
94
107
|
if len(action.param_value_summary()) > 0:
|
|
95
108
|
log.message("Parameters:\n%s", action.param_value_summary_str())
|
|
@@ -106,8 +119,9 @@ def check_for_existing_result(
|
|
|
106
119
|
already exist.
|
|
107
120
|
"""
|
|
108
121
|
action = context.action
|
|
109
|
-
|
|
110
|
-
|
|
122
|
+
settings = context.settings
|
|
123
|
+
ws = settings.workspace
|
|
124
|
+
rerun = settings.rerun
|
|
111
125
|
|
|
112
126
|
existing_result = None
|
|
113
127
|
|
|
@@ -154,6 +168,7 @@ def run_action_operation(
|
|
|
154
168
|
|
|
155
169
|
# Run the action.
|
|
156
170
|
action = context.action
|
|
171
|
+
settings = context.settings
|
|
157
172
|
if action.run_per_item:
|
|
158
173
|
result = _run_for_each_item(context, action_input)
|
|
159
174
|
else:
|
|
@@ -172,9 +187,9 @@ def run_action_operation(
|
|
|
172
187
|
item.update_history(Source(operation=this_op, output_num=i, cacheable=action.cacheable))
|
|
173
188
|
|
|
174
189
|
# Override the state if appropriate (this handles marking items as transient).
|
|
175
|
-
if
|
|
190
|
+
if settings.override_state:
|
|
176
191
|
for item in result.items:
|
|
177
|
-
item.state =
|
|
192
|
+
item.state = settings.override_state
|
|
178
193
|
|
|
179
194
|
log.info("Action `%s` result: %s", action.name, result)
|
|
180
195
|
|
|
@@ -233,7 +248,7 @@ def _run_for_each_item(context: ExecContext, input: ActionInput) -> ActionResult
|
|
|
233
248
|
log.info("Caught SkipItem exception, skipping run on this item")
|
|
234
249
|
result_items.append(item)
|
|
235
250
|
continue
|
|
236
|
-
except
|
|
251
|
+
except get_nonfatal_exceptions() as e:
|
|
237
252
|
errors.append(e)
|
|
238
253
|
had_error = True
|
|
239
254
|
|
|
@@ -293,14 +308,18 @@ def save_action_result(
|
|
|
293
308
|
fmt_lines(skipped_paths),
|
|
294
309
|
)
|
|
295
310
|
|
|
296
|
-
|
|
311
|
+
unsaved_items = [item for item in input_items if not item.store_path]
|
|
312
|
+
input_store_paths = [StorePath(item.store_path) for item in input_items if item.store_path]
|
|
297
313
|
result_store_paths = [StorePath(item.store_path) for item in result.items if item.store_path]
|
|
298
314
|
old_inputs = sorted(set(input_store_paths) - set(result_store_paths))
|
|
315
|
+
if unsaved_items:
|
|
316
|
+
log.info("unsaved_items:\n%s", fmt_lines(unsaved_items))
|
|
299
317
|
log.info("result_store_paths:\n%s", fmt_lines(result_store_paths))
|
|
300
|
-
|
|
318
|
+
if old_inputs:
|
|
319
|
+
log.info("old_inputs:\n%s", fmt_lines(old_inputs))
|
|
301
320
|
|
|
302
321
|
# If there is a hint that the action replaces the input, archive any inputs that are not in the result.
|
|
303
|
-
archived_store_paths = []
|
|
322
|
+
archived_store_paths: list[StorePath] = []
|
|
304
323
|
if result.replaces_input and input_items:
|
|
305
324
|
for input_store_path in old_inputs:
|
|
306
325
|
# Note some outputs may be missing if replace_input was used.
|
|
@@ -325,7 +344,8 @@ def run_action_with_caching(
|
|
|
325
344
|
Note: Mutates the input but only to add `context` to each item.
|
|
326
345
|
"""
|
|
327
346
|
action = context.action
|
|
328
|
-
|
|
347
|
+
settings = context.settings
|
|
348
|
+
ws = settings.workspace
|
|
329
349
|
|
|
330
350
|
# For convenience, we include the context to each item too (this helps so per-item
|
|
331
351
|
# functions don't have to take context args everywhere).
|
|
@@ -341,7 +361,7 @@ def run_action_with_caching(
|
|
|
341
361
|
# Check if a previous run already produced the result.
|
|
342
362
|
existing_result = check_for_existing_result(context, action_input, operation)
|
|
343
363
|
|
|
344
|
-
if existing_result and not
|
|
364
|
+
if existing_result and not settings.rerun:
|
|
345
365
|
# Use the cached result.
|
|
346
366
|
result = existing_result
|
|
347
367
|
result_store_paths = [StorePath(not_none(item.store_path)) for item in result.items]
|
|
@@ -349,7 +369,7 @@ def run_action_with_caching(
|
|
|
349
369
|
|
|
350
370
|
PrintHooks.before_done_message()
|
|
351
371
|
log.message(
|
|
352
|
-
"%s
|
|
372
|
+
"%s Skipped: `%s` completed with %s %s",
|
|
353
373
|
EMOJI_SKIP,
|
|
354
374
|
action.name,
|
|
355
375
|
len(result.items),
|
|
@@ -359,12 +379,12 @@ def run_action_with_caching(
|
|
|
359
379
|
# Run it!
|
|
360
380
|
result = run_action_operation(context, action_input, operation)
|
|
361
381
|
result_store_paths, archived_store_paths = save_action_result(
|
|
362
|
-
ws, result, action_input, as_tmp=
|
|
382
|
+
ws, result, action_input, as_tmp=settings.tmp_output, no_format=settings.no_format
|
|
363
383
|
)
|
|
364
384
|
|
|
365
385
|
PrintHooks.before_done_message()
|
|
366
386
|
log.message(
|
|
367
|
-
"%s
|
|
387
|
+
"%s Done: `%s` completed with %s %s",
|
|
368
388
|
EMOJI_SUCCESS,
|
|
369
389
|
action.name,
|
|
370
390
|
len(result.items),
|
|
@@ -415,8 +435,7 @@ def run_action_with_shell_context(
|
|
|
415
435
|
action_name = action.name
|
|
416
436
|
|
|
417
437
|
# Execution context. This is fixed for the duration of the action.
|
|
418
|
-
|
|
419
|
-
action=action,
|
|
438
|
+
settings = RuntimeSettings(
|
|
420
439
|
workspace_dir=ws.base_dir,
|
|
421
440
|
rerun=rerun,
|
|
422
441
|
refetch=refetch,
|
|
@@ -424,6 +443,7 @@ def run_action_with_shell_context(
|
|
|
424
443
|
tmp_output=tmp_output,
|
|
425
444
|
no_format=no_format,
|
|
426
445
|
)
|
|
446
|
+
context = ExecContext(action, settings)
|
|
427
447
|
|
|
428
448
|
# Collect args from the provided args or otherwise the current selection.
|
|
429
449
|
args, from_selection = assemble_action_args(*provided_args, use_selection=action.uses_selection)
|
kash/exec/llm_transforms.py
CHANGED
|
@@ -13,7 +13,7 @@ from kash.llm_utils.llm_completion import llm_template_completion
|
|
|
13
13
|
from kash.llm_utils.llm_messages import Message, MessageTemplate
|
|
14
14
|
from kash.model.actions_model import LLMOptions
|
|
15
15
|
from kash.model.items_model import Item
|
|
16
|
-
from kash.text_handling.doc_normalization import
|
|
16
|
+
from kash.text_handling.doc_normalization import normalize_formatting
|
|
17
17
|
from kash.utils.errors import InvalidInput
|
|
18
18
|
from kash.utils.file_utils.file_formats_model import Format
|
|
19
19
|
|
|
@@ -118,7 +118,7 @@ def llm_transform_item(
|
|
|
118
118
|
if strip_fence:
|
|
119
119
|
result_str = strip_markdown_fence(result_str)
|
|
120
120
|
if normalize:
|
|
121
|
-
result_str =
|
|
121
|
+
result_str = normalize_formatting(result_str, format=format)
|
|
122
122
|
|
|
123
123
|
result_item.body = result_str
|
|
124
124
|
return result_item
|
kash/exec/preconditions.py
CHANGED
|
@@ -85,13 +85,13 @@ def contains_curly_vars(item: Item) -> bool:
|
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
@kash_precondition
|
|
88
|
-
def
|
|
89
|
-
return has_body(item) and item.format
|
|
88
|
+
def has_simple_text_body(item: Item) -> bool:
|
|
89
|
+
return bool(has_body(item) and item.format and item.format.is_simple_text)
|
|
90
90
|
|
|
91
91
|
|
|
92
92
|
@kash_precondition
|
|
93
93
|
def has_html_body(item: Item) -> bool:
|
|
94
|
-
return has_body(item) and item.format
|
|
94
|
+
return bool(has_body(item) and item.format and item.format.is_html)
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
@kash_precondition
|
|
@@ -106,7 +106,7 @@ def is_plaintext(item: Item) -> bool:
|
|
|
106
106
|
|
|
107
107
|
@kash_precondition
|
|
108
108
|
def is_markdown(item: Item) -> bool:
|
|
109
|
-
return has_body(item) and item.format
|
|
109
|
+
return bool(has_body(item) and item.format and item.format.is_markdown)
|
|
110
110
|
|
|
111
111
|
|
|
112
112
|
@kash_precondition
|
|
@@ -119,14 +119,6 @@ def is_html(item: Item) -> bool:
|
|
|
119
119
|
return has_body(item) and item.format == Format.html
|
|
120
120
|
|
|
121
121
|
|
|
122
|
-
@kash_precondition
|
|
123
|
-
def is_text_doc(item: Item) -> bool:
|
|
124
|
-
"""
|
|
125
|
-
A document that can be processed by LLMs and other plaintext tools.
|
|
126
|
-
"""
|
|
127
|
-
return (is_plaintext(item) or is_markdown(item)) and has_body(item)
|
|
128
|
-
|
|
129
|
-
|
|
130
122
|
@kash_precondition
|
|
131
123
|
def is_markdown_list(item: Item) -> bool:
|
|
132
124
|
try:
|