zettcode-engine 0.4.1__tar.gz → 0.4.2__tar.gz
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.
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/.gitignore +5 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/PKG-INFO +3 -2
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/pyproject.toml +3 -1
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/budget.py +1 -1
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/config.py +26 -12
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/agent/context.py +20 -37
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/agent/turns.py +61 -15
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/core.py +193 -18
- zettcode_engine-0.4.2/src/zettcode/engine/delivery.py +51 -0
- zettcode_engine-0.4.2/src/zettcode/engine/delivery_runtime.py +56 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/dialogue/side.py +1 -1
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/export/__init__.py +3 -3
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/extensions/capabilities.py +1 -1
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/extensions/compaction.py +4 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/extensions/files.py +4 -4
- zettcode_engine-0.4.2/src/zettcode/engine/inbox.py +131 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/permission/__init__.py +1 -1
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/permission/sandbox.py +1 -1
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/__init__.py +26 -1
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/commands.py +35 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/context.py +89 -11
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/extension.py +6 -1
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/groups.py +37 -0
- zettcode_engine-0.4.2/src/zettcode/engine/plugins/llm.py +56 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/loader.py +166 -57
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/plugin.py +46 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/services.py +32 -3
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/switches.py +7 -1
- zettcode_engine-0.4.2/src/zettcode/engine/plugins/triggers.py +273 -0
- zettcode_engine-0.4.2/src/zettcode/engine/service_contracts.py +101 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/storage/__init__.py +2 -1
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/storage/locks.py +7 -7
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/storage/store.py +15 -2
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/mention.py +9 -2
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/paths.py +4 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/plugin_tui.py +1 -1
- zettcode_engine-0.4.2/src/zettcode/relay.py +35 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/statusline_parts.py +1 -1
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/version.py +1 -1
- zettcode_engine-0.4.2/src/zettcode/wire.py +68 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/wording.py +1 -1
- zettcode_engine-0.4.1/src/zettcode/engine/extensions/harness.py +0 -208
- zettcode_engine-0.4.1/src/zettcode/engine/extensions/tool_output.py +0 -153
- zettcode_engine-0.4.1/src/zettcode/engine/extensions/truncation.py +0 -156
- zettcode_engine-0.4.1/src/zettcode/engine/tools/__init__.py +0 -7
- zettcode_engine-0.4.1/src/zettcode/engine/tools/boundary.py +0 -56
- zettcode_engine-0.4.1/src/zettcode/engine/tools/files.py +0 -262
- zettcode_engine-0.4.1/src/zettcode/engine/tools/images.py +0 -32
- zettcode_engine-0.4.1/src/zettcode/engine/tools/output.py +0 -81
- zettcode_engine-0.4.1/src/zettcode/engine/tools/shell.py +0 -181
- zettcode_engine-0.4.1/src/zettcode/engine/tools/workspace.py +0 -271
- zettcode_engine-0.4.1/src/zettcode/relay.py +0 -267
- zettcode_engine-0.4.1/src/zettcode/wire.py +0 -30
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/_compat.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/config_edit.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/__init__.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/agent/__init__.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/agent/base.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/agent/exports.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/agent/interaction.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/agent/sessions.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/agent/settings.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/dialogue/__init__.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/dialogue/ask.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/dialogue/title.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/export/trace/script.js +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/export/trace/style.css +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/export/trace/template.html +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/extensions/__init__.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/extensions/context.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/extensions/guidance.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/extensions/harness_switch.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/extensions/mcp_notice.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/extensions/relay.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/extensions/subagents.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/extensions/usage.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/harness_paths.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/permission/approval.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/permission/readonly.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/pipeline.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/graph.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/mixins.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/settings.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/plugins/state.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/py.typed +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/server_tools.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/storage/layout.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/storage/ledger.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/storage/metadata.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/engine/storage/records.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/error_text.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/filelock.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/logging_setup.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/modes.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/processes.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/prompt.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/providers.py +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/py.typed +0 -0
- {zettcode_engine-0.4.1 → zettcode_engine-0.4.2}/src/zettcode/registry.py +0 -0
|
@@ -35,3 +35,8 @@ docs/internal/palettes.html
|
|
|
35
35
|
|
|
36
36
|
# The release order is generated from the pyproject.toml files (`make order`); it is not kept in git.
|
|
37
37
|
packages/order.txt
|
|
38
|
+
|
|
39
|
+
# The desktop and phone apps build the gateway's page into their own folders, and electron-builder writes its
|
|
40
|
+
# installers under release/.
|
|
41
|
+
clients/desktop/renderer/
|
|
42
|
+
clients/desktop/release/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: zettcode-engine
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2
|
|
4
4
|
Summary: The agent core of ZettCode: the runtime over zett-agent, and the leaf types it is written on
|
|
5
5
|
Project-URL: Repository, https://github.com/Chang-LeHung/zettcode
|
|
6
6
|
Author: Chang-LeHung
|
|
@@ -18,6 +18,7 @@ Requires-Python: >=3.10
|
|
|
18
18
|
Requires-Dist: exceptiongroup<2,>=1.2; python_version < '3.11'
|
|
19
19
|
Requires-Dist: pydantic<3,>=2
|
|
20
20
|
Requires-Dist: tomli<3,>=2; python_version < '3.11'
|
|
21
|
-
Requires-Dist: zett-agent<0.3,>=0.2.
|
|
21
|
+
Requires-Dist: zett-agent<0.3,>=0.2.10
|
|
22
|
+
Requires-Dist: zettcode-tools
|
|
22
23
|
Provides-Extra: tiktoken
|
|
23
24
|
Requires-Dist: tiktoken<1,>=0.12; extra == 'tiktoken'
|
|
@@ -17,8 +17,10 @@ classifiers = [
|
|
|
17
17
|
"Typing :: Typed",
|
|
18
18
|
]
|
|
19
19
|
dependencies = [
|
|
20
|
+
# The coding tools the runtime hands the model, a leaf of its own beneath the engine.
|
|
21
|
+
"zettcode-tools",
|
|
20
22
|
# The runtime this is a core over. Imported directly: the agent, the message types, the approval mode.
|
|
21
|
-
"zett-agent>=0.2.
|
|
23
|
+
"zett-agent>=0.2.10,<0.3",
|
|
22
24
|
# The session store's records are pydantic models.
|
|
23
25
|
"pydantic>=2,<3",
|
|
24
26
|
# The stdlib gained both of these in 3.11; older runtimes take the backports.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""What a context budget is made of: the vocabulary the engine measures with and the views draw with.
|
|
2
2
|
|
|
3
|
-
A leaf with no imports, shared like :mod:`zettcode.
|
|
3
|
+
A leaf with no imports, shared like :mod:`zettcode.statusline_parts` and :mod:`zettcode.providers`, so the engine that counts
|
|
4
4
|
the request and the page that shows the count agree on the names without either importing the other.
|
|
5
5
|
"""
|
|
6
6
|
|
|
@@ -112,7 +112,7 @@ SCOPES = frozenset({"global", "workspace", "both"})
|
|
|
112
112
|
DEFAULT_SCOPE = "both"
|
|
113
113
|
|
|
114
114
|
#: Keys the ``[plugins]`` table may set.
|
|
115
|
-
PLUGIN_KEYS = frozenset({"enabled", "disable"})
|
|
115
|
+
PLUGIN_KEYS = frozenset({"enabled", "disable", "enable"})
|
|
116
116
|
|
|
117
117
|
#: Keys the ``[update]`` table may set.
|
|
118
118
|
UPDATE_KEYS = frozenset({"enabled"})
|
|
@@ -448,16 +448,18 @@ class ZettCodeConfig:
|
|
|
448
448
|
self_compaction: Whether a session lets the model request compaction unless it says otherwise;
|
|
449
449
|
the default a new session starts with; the terminal's ``/plugins`` switch writes it.
|
|
450
450
|
statusline_hidden: The names of the header and status-line parts the reader switched off in the
|
|
451
|
-
``[statusline]`` table (``ctx = false``); see :mod:`zettcode.statusline`. Every part not named
|
|
451
|
+
``[statusline]`` table (``ctx = false``); see :mod:`zettcode.presenters.statusline`. Every part not named
|
|
452
452
|
is shown.
|
|
453
453
|
plugins_enabled: Load third-party plugins published under the
|
|
454
|
-
``zettcode.
|
|
454
|
+
``zettcode.plugin`` entry-point group. The builtin rows always
|
|
455
455
|
load, so switching this off leaves the shell looking the same.
|
|
456
456
|
disabled_plugins: Entry-point names not to load when
|
|
457
457
|
:attr:`plugins_enabled` is true; a way to switch off one plugin
|
|
458
458
|
without uninstalling it. This is the configuration file's own
|
|
459
459
|
``[plugins] disable`` list; the names the older switch file adds are merged in by
|
|
460
460
|
:attr:`off_names`, so a switch can tell the two apart.
|
|
461
|
+
enabled_plugins: Entry-point names to load although the plugin itself says it is off by default
|
|
462
|
+
(``Plugin.default_enabled``); this is the configuration file's ``[plugins] enable`` list.
|
|
461
463
|
plugin_settings: The ``[plugins.<name>]`` tables, by plugin name, exactly as written. A plugin declares
|
|
462
464
|
what its own keys mean and what they default to (``ctx.settings``); the configuration only carries the
|
|
463
465
|
reader's values, so a key no plugin knows is harmless and a plugin that ships later needs no change
|
|
@@ -517,6 +519,8 @@ class ZettCodeConfig:
|
|
|
517
519
|
statusline_hidden: tuple[str, ...] = ()
|
|
518
520
|
plugins_enabled: bool = True
|
|
519
521
|
disabled_plugins: tuple[str, ...] = ()
|
|
522
|
+
#: Default-off plugins the reader asked to load; this is ``[plugins] enable``.
|
|
523
|
+
enabled_plugins: tuple[str, ...] = ()
|
|
520
524
|
#: The reader's own keys for a plugin, from its ``[plugins.<name>]`` table; the plugin supplies the defaults.
|
|
521
525
|
plugin_settings: Mapping[str, Mapping[str, object]] = field(default_factory=dict)
|
|
522
526
|
subagent_enabled: bool = True
|
|
@@ -917,17 +921,19 @@ def read_disabled_plugins(path: Path | None = None) -> tuple[str, ...]:
|
|
|
917
921
|
return tuple(name.strip() for name in names if isinstance(name, str) and name.strip())
|
|
918
922
|
|
|
919
923
|
|
|
920
|
-
def
|
|
921
|
-
"""Return the entry-point names
|
|
924
|
+
def _read_plugin_names(raw: object, source: Path, key: str) -> tuple[str, ...]:
|
|
925
|
+
"""Return the entry-point names a ``[plugins]`` list holds, validating the shape."""
|
|
922
926
|
if raw is None:
|
|
923
927
|
return ()
|
|
924
928
|
if not isinstance(raw, list) or any(not isinstance(entry, str) or not entry.strip() for entry in raw):
|
|
925
|
-
raise ValueError(f"Config key 'plugins.
|
|
929
|
+
raise ValueError(f"Config key 'plugins.{key}' in {source} must be an array of non-empty strings")
|
|
926
930
|
return tuple(entry.strip() for entry in raw)
|
|
927
931
|
|
|
928
932
|
|
|
929
|
-
def read_plugins_table(
|
|
930
|
-
|
|
933
|
+
def read_plugins_table(
|
|
934
|
+
raw: object, source: Path
|
|
935
|
+
) -> tuple[bool, tuple[str, ...], tuple[str, ...], dict[str, dict[str, object]]]:
|
|
936
|
+
"""Return ``[plugins]`` as ``(enabled, disable, enable, settings)``: the one reading of that table.
|
|
931
937
|
|
|
932
938
|
The configuration and ``zettcode plugin`` both need it, and the second must not build the whole settings object
|
|
933
939
|
(that insists on a model). They used to each write the rules down, and the copies drifted: the command refused a
|
|
@@ -938,15 +944,20 @@ def read_plugins_table(raw: object, source: Path) -> tuple[bool, tuple[str, ...]
|
|
|
938
944
|
source: The file, for messages.
|
|
939
945
|
|
|
940
946
|
Returns:
|
|
941
|
-
Whether installed plugins load at all, the entry-point names left out,
|
|
942
|
-
name.
|
|
947
|
+
Whether installed plugins load at all, the entry-point names left out, the default-off names asked for, and
|
|
948
|
+
each plugin's own settings table by name.
|
|
943
949
|
|
|
944
950
|
Raises:
|
|
945
951
|
ValueError: The table, a plugin's table, or a value in one is not what it must be.
|
|
946
952
|
"""
|
|
947
953
|
own, settings = _read_plugins(raw, source)
|
|
948
954
|
enabled = _typed(own.get("enabled", True), f"Config key 'plugins.enabled' in {source}", bool)
|
|
949
|
-
return
|
|
955
|
+
return (
|
|
956
|
+
enabled,
|
|
957
|
+
_read_plugin_names(own.get("disable"), source, "disable"),
|
|
958
|
+
_read_plugin_names(own.get("enable"), source, "enable"),
|
|
959
|
+
settings,
|
|
960
|
+
)
|
|
950
961
|
|
|
951
962
|
|
|
952
963
|
def _read_theme_name(raw: object, source: Path) -> str | None:
|
|
@@ -1016,7 +1027,9 @@ def load_config(
|
|
|
1016
1027
|
ask_user = _read_table(data.get("ask_user"), "ask_user", source, ASK_USER_KEYS)
|
|
1017
1028
|
skills = _read_table(data.get("skills"), "skills", source, SKILL_KEYS)
|
|
1018
1029
|
mcp = _read_table(data.get("mcp"), "mcp", source, MCP_KEYS)
|
|
1019
|
-
plugins_enabled, disabled_plugins, plugin_settings = read_plugins_table(
|
|
1030
|
+
plugins_enabled, disabled_plugins, enabled_plugins, plugin_settings = read_plugins_table(
|
|
1031
|
+
data.get("plugins"), source
|
|
1032
|
+
)
|
|
1020
1033
|
transcript = _read_table(data.get("transcript"), "transcript", source, TRANSCRIPT_KEYS)
|
|
1021
1034
|
update = _read_table(data.get("update"), "update", source, UPDATE_KEYS)
|
|
1022
1035
|
harness = _read_table(data.get("harness"), "harness", source, HARNESS_KEYS)
|
|
@@ -1072,6 +1085,7 @@ def load_config(
|
|
|
1072
1085
|
# The configuration file's own list. The legacy switch file's names are read where they are needed
|
|
1073
1086
|
# (`zettcode.engine.plugins.switches`), so a switch can tell which list it may write.
|
|
1074
1087
|
disabled_plugins=disabled_plugins,
|
|
1088
|
+
enabled_plugins=enabled_plugins,
|
|
1075
1089
|
plugin_settings=plugin_settings,
|
|
1076
1090
|
subagent_enabled=bool(_setting(harness, "subagent", bool, True)),
|
|
1077
1091
|
subagent_max_iterations=_typed(
|
|
@@ -14,7 +14,7 @@ from zett_agent.extensions.events import CompactionEvent
|
|
|
14
14
|
from zett_agent.messages import AnyMessage, SystemMessage
|
|
15
15
|
from zett_agent.model import ToolDefinition
|
|
16
16
|
|
|
17
|
-
from zettcode.engine.core import build_system_prompt
|
|
17
|
+
from zettcode.engine.core import ZettCodeEngine, build_system_prompt
|
|
18
18
|
from zettcode.engine.extensions.context import ContextReport
|
|
19
19
|
from zettcode.engine.extensions.usage import UsageSnapshot
|
|
20
20
|
from zettcode.engine.storage import Session
|
|
@@ -22,6 +22,23 @@ from zettcode.engine.storage import Session
|
|
|
22
22
|
from .base import AgentBase
|
|
23
23
|
|
|
24
24
|
|
|
25
|
+
async def report_of(runtime: ZettCodeEngine, session_id: str) -> ContextReport | None:
|
|
26
|
+
"""Return what the next request carries, or ``None`` before it was assembled.
|
|
27
|
+
|
|
28
|
+
The tokenizer is prepared first; the total is the provider's count of this request, clamped out when it reaches
|
|
29
|
+
the window, because a window-sized figure is about something else. A rebuilt request is not counted locally:
|
|
30
|
+
its list carries messages the live request no longer does, so the provider's count is the only honest total.
|
|
31
|
+
``window = 0`` means no model is configured; nothing is measured and no share is computed from it.
|
|
32
|
+
"""
|
|
33
|
+
tokenizer = runtime.tokenizer
|
|
34
|
+
await tokenizer.prepare()
|
|
35
|
+
model = runtime.active_model
|
|
36
|
+
window = 0 if model is None else model.context_window
|
|
37
|
+
counted = runtime.usage.snapshot(session_id).context_tokens
|
|
38
|
+
reported = counted if 0 < counted < window else None
|
|
39
|
+
return runtime.context.report(session_id, window=window, tokenizer=tokenizer, reported=reported)
|
|
40
|
+
|
|
41
|
+
|
|
25
42
|
class ContextMixin(AgentBase):
|
|
26
43
|
"""Report the request the next turn would send, and compact the conversation behind it."""
|
|
27
44
|
|
|
@@ -36,42 +53,8 @@ class ContextMixin(AgentBase):
|
|
|
36
53
|
return self.runtime.usage.snapshot(self.session_id)
|
|
37
54
|
|
|
38
55
|
async def context_report(self) -> ContextReport | None:
|
|
39
|
-
"""Return what the next request carries
|
|
40
|
-
|
|
41
|
-
The shares come from the tokenizer, which is prepared first: it prefers
|
|
42
|
-
tiktoken and falls back to a character estimate when that vocabulary
|
|
43
|
-
cannot be opened, which the report names so the numbers are not mistaken
|
|
44
|
-
for exact ones. The total comes from the provider when it counted this
|
|
45
|
-
very request — it counts what it bills — and from the tokenizer when the
|
|
46
|
-
request was primed from the store, whose stored figure belongs to a call
|
|
47
|
-
the dialogue may have outgrown.
|
|
48
|
-
"""
|
|
49
|
-
tokenizer = self.runtime.tokenizer
|
|
50
|
-
await tokenizer.prepare()
|
|
51
|
-
model = self.runtime.active_model
|
|
52
|
-
# No model configured means no window to measure against. Nothing is reported either way: a request is
|
|
53
|
-
# refused before one is assembled, so the report is None and no share is ever computed from this 0.
|
|
54
|
-
window = 0 if model is None else model.context_window
|
|
55
|
-
counted = self.runtime.usage.snapshot(self.session_id).context_tokens
|
|
56
|
-
# The provider's own count of this conversation is the total: what it
|
|
57
|
-
# billed for the request this process assembled, or — for a session
|
|
58
|
-
# rebuilt from the store, which has no call of its own yet — what it
|
|
59
|
-
# billed for the last call that session made. Locally counting a rebuilt
|
|
60
|
-
# request is not a substitute: on this machine's own session the rebuilt
|
|
61
|
-
# list measured 1.65 M tokens against a provider's 326 k, because a
|
|
62
|
-
# rebuilt list holds messages the live request no longer carries.
|
|
63
|
-
#
|
|
64
|
-
# A count that reaches the window is refused instead: a request cannot
|
|
65
|
-
# carry more than the window it is measured against, so a figure that does
|
|
66
|
-
# is about something else, and a share pinned at a hundred is worse than
|
|
67
|
-
# the measurement.
|
|
68
|
-
reported = counted if 0 < counted < window else None
|
|
69
|
-
return self.runtime.context.report(
|
|
70
|
-
self.session_id,
|
|
71
|
-
window=window,
|
|
72
|
-
tokenizer=tokenizer,
|
|
73
|
-
reported=reported,
|
|
74
|
-
)
|
|
56
|
+
"""Return what the next request carries, or ``None`` before it was assembled."""
|
|
57
|
+
return await report_of(self.runtime, self.session_id)
|
|
75
58
|
|
|
76
59
|
async def ensure_compactable(self) -> None:
|
|
77
60
|
"""Refuse a compaction that would do nothing, before a row is put up for it.
|
|
@@ -19,7 +19,6 @@ from zett_agent.resume import ResumePoint
|
|
|
19
19
|
|
|
20
20
|
from zettcode.engine.dialogue.side import ORIGIN, READER, SIDE, WATCHED, question, watched_by
|
|
21
21
|
from zettcode.prompt import MentionPart, PromptPart, carries_image, model_text_part, plain_text
|
|
22
|
-
from zettcode.relay import Relayed
|
|
23
22
|
|
|
24
23
|
from .base import AgentBase
|
|
25
24
|
|
|
@@ -57,9 +56,47 @@ class TurnsMixin(AgentBase):
|
|
|
57
56
|
side: bool = False,
|
|
58
57
|
origin: str = READER,
|
|
59
58
|
watched: bool | None = None,
|
|
60
|
-
|
|
59
|
+
incoming: UserMessage | None = None,
|
|
61
60
|
cache: CacheMode | str | None = None,
|
|
62
61
|
) -> AsyncIterator[AgentEvent]:
|
|
62
|
+
"""Run one turn, counting it as a running request for as long as its events are read.
|
|
63
|
+
|
|
64
|
+
The count is what tells an external event whether it can steer this request or has to wait for the next turn
|
|
65
|
+
(see :mod:`zettcode.engine.delivery`). A side question is not a request of the conversation: nothing steers
|
|
66
|
+
into it, and it does not keep a follow-up waiting.
|
|
67
|
+
"""
|
|
68
|
+
if side:
|
|
69
|
+
async with aclosing(
|
|
70
|
+
self._stream(
|
|
71
|
+
parts, hint=hint, side=True, origin=origin, watched=watched, incoming=incoming, cache=cache
|
|
72
|
+
)
|
|
73
|
+
) as events:
|
|
74
|
+
async for event in events:
|
|
75
|
+
yield event
|
|
76
|
+
return
|
|
77
|
+
self.runtime.run_started()
|
|
78
|
+
try:
|
|
79
|
+
async with aclosing(
|
|
80
|
+
self._stream(
|
|
81
|
+
parts, hint=hint, side=False, origin=origin, watched=watched, incoming=incoming, cache=cache
|
|
82
|
+
)
|
|
83
|
+
) as events:
|
|
84
|
+
async for event in events:
|
|
85
|
+
yield event
|
|
86
|
+
finally:
|
|
87
|
+
self.runtime.run_finished()
|
|
88
|
+
|
|
89
|
+
async def _stream(
|
|
90
|
+
self,
|
|
91
|
+
parts: Sequence[PromptPart],
|
|
92
|
+
*,
|
|
93
|
+
hint: str | None = None,
|
|
94
|
+
side: bool = False,
|
|
95
|
+
origin: str = READER,
|
|
96
|
+
watched: bool | None = None,
|
|
97
|
+
incoming: UserMessage | None = None,
|
|
98
|
+
cache: CacheMode | str | None = None,
|
|
99
|
+
) -> AsyncGenerator[AgentEvent, None]:
|
|
63
100
|
"""Run one turn with the selected session, model, and reasoning effort.
|
|
64
101
|
|
|
65
102
|
Args:
|
|
@@ -73,8 +110,8 @@ class TurnsMixin(AgentBase):
|
|
|
73
110
|
was typed and a restored session shows it.
|
|
74
111
|
side: Ask without letting the exchange join the conversation; see
|
|
75
112
|
:mod:`zettcode.engine.dialogue.side`.
|
|
76
|
-
|
|
77
|
-
|
|
113
|
+
incoming: The turn is a message another session sent, already framed by the part that owns the
|
|
114
|
+
conversation with it. It is what the model reads, and ``parts`` and ``hint`` are not used.
|
|
78
115
|
cache: How this request may use the provider's prompt cache: ``"read_write"`` (the default) extends
|
|
79
116
|
it, ``"read"`` reuses a warm prefix without marking this request's new content for caching, and
|
|
80
117
|
``"off"`` sends no cache directive at all. A side question starts at ``"read"`` unless told
|
|
@@ -103,8 +140,8 @@ class TurnsMixin(AgentBase):
|
|
|
103
140
|
"AsyncGenerator[AgentEvent]",
|
|
104
141
|
client.stream(
|
|
105
142
|
self.request(parts, hint=hint, side=side, origin=origin, watched=watched)
|
|
106
|
-
if
|
|
107
|
-
else
|
|
143
|
+
if incoming is None
|
|
144
|
+
else incoming,
|
|
108
145
|
config=AgentRunConfig(
|
|
109
146
|
session_id=session_id,
|
|
110
147
|
cache_mode=cache_mode_of(cache, side=side),
|
|
@@ -297,12 +334,21 @@ class TurnsMixin(AgentBase):
|
|
|
297
334
|
if not message.text.strip() and not message.parts:
|
|
298
335
|
return False
|
|
299
336
|
payload = {"message": message}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
337
|
+
return steer_in_flight(self.runtime, self.session_id, payload)
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
def steer_in_flight(runtime: object, session_id: str, payload: dict[str, object]) -> bool:
|
|
341
|
+
"""Emit the steering event for one message, and say whether the running request took it.
|
|
342
|
+
|
|
343
|
+
The engine's own delivery (:class:`zettcode.engine.delivery.Delivery`) and the agent's ``steer`` both come through
|
|
344
|
+
here, so the one rule for what a request accepts has one home.
|
|
345
|
+
"""
|
|
346
|
+
try:
|
|
347
|
+
agent = runtime.started.agent # type: ignore[attr-defined]
|
|
348
|
+
except RuntimeError:
|
|
349
|
+
return False
|
|
350
|
+
accepted = agent.emit_external_event(
|
|
351
|
+
ExternalEvent(name=STEERING_MESSAGE_EVENT_NAME, payload=payload),
|
|
352
|
+
config=AgentRunConfig(session_id=session_id),
|
|
353
|
+
)
|
|
354
|
+
return bool(accepted)
|