zettcode-one-system 0.5.1__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.
@@ -0,0 +1,12 @@
1
+ """One system message: every instruction the agent gives the model, joined into the one message before the provider.
2
+
3
+ A part of the program that lives in a distribution of its own, found through the ``zettcode.plugin`` entry point. It is
4
+ on unless ``[plugins] disable = ["one-system"]`` names it: what it changes is the shape of the request, not what the
5
+ model is told, so leaving it on is what an endpoint that accepts one system message expects.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from .plugin import OneSystem
11
+
12
+ __all__ = ["OneSystem"]
@@ -0,0 +1,81 @@
1
+ """The hook that turns a request's instructions into one system message, and stops there.
2
+
3
+ A request reaches the provider with several system messages: the agent's own prompt, the working directory, the
4
+ project's instructions, skills, MCP notices, and whatever guidance an extension added for this request. Every one of
5
+ them is an instruction, and every provider has its own way of taking them: the Anthropic-compatible one collects them
6
+ and joins them with a blank line, an OpenAI-compatible one sends a message per entry, and an endpoint that accepts a
7
+ single system message refuses the rest.
8
+
9
+ Joining them here makes the shape the same everywhere, and does it on the text the model was going to read anyway: the
10
+ parts are concatenated in the order they were already in, separated by a blank line, and the result sits where the first
11
+ one was. Nothing is reordered past a conversation message, nothing is dropped, and a request that already has one
12
+ system message is handed on untouched.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from collections.abc import AsyncIterator, Sequence
18
+ from dataclasses import replace
19
+
20
+ from zett_agent.agent import AgentRunContext
21
+ from zett_agent.extensions.base import AgentExtension, ModelRequestNext
22
+ from zett_agent.messages import AnyMessage, SystemMessage
23
+ from zett_agent.model import ModelEvent, ModelRequest
24
+
25
+ __all__ = ["OneSystemMessage", "merge_system"]
26
+
27
+
28
+ def merge_system(messages: Sequence[AnyMessage]) -> Sequence[AnyMessage]:
29
+ """Return ``messages`` with every system message joined into the first one's place.
30
+
31
+ A request with fewer than two system messages, or with nothing but blank text between them, is returned as it was —
32
+ the same object, so a caller can tell the two cases apart without comparing contents.
33
+
34
+ Args:
35
+ messages: The request's messages, in the order the provider would see them.
36
+
37
+ Returns:
38
+ The messages with one :class:`~zett_agent.messages.SystemMessage` where the first one was, or ``messages``
39
+ itself when there was nothing to join.
40
+ """
41
+ found = [(index, message) for index, message in enumerate(messages) if isinstance(message, SystemMessage)]
42
+ if len(found) < 2:
43
+ return messages
44
+ joined = "\n\n".join(message.content.strip() for _index, message in found if message.content.strip())
45
+ if not joined:
46
+ return messages
47
+ # The first message's own flags and attributes are kept: only its text changes. `dataclasses.replace` gives a
48
+ # new message, so the one the runtime holds for later requests is left as it was.
49
+ where, first_message = found[0]
50
+ merged = replace(first_message, content=joined)
51
+ removed = {index for index, _message in found}
52
+ kept = [message for index, message in enumerate(messages) if index not in removed]
53
+ # Every removed place is at or after the first, so the first's index still counts among the ones that remain.
54
+ kept.insert(where, merged)
55
+ return tuple(kept)
56
+
57
+
58
+ class OneSystemMessage(AgentExtension):
59
+ """Join a request's system messages into one, just before the provider is handed the request.
60
+
61
+ The priority is higher than any other extension's so that this runs innermost: a part that adds an instruction while
62
+ the request is being built — the harness's guidance, for one — has already been added by the time this sees the
63
+ request, and none is left behind to reach the provider on its own.
64
+ """
65
+
66
+ #: Innermost: the last extension to change the request, and the one closest to the provider. It has to clear the
67
+ #: two the framework itself runs late — the JIT harness's guidance at 900_000 and plan mode at 1_000_000 — because
68
+ #: both add a system message while the request is being built, and a tie is broken by registration order.
69
+ priority = 1_000_001
70
+
71
+ async def on_model_request(
72
+ self, context: AgentRunContext, request: ModelRequest, call_next: ModelRequestNext
73
+ ) -> AsyncIterator[ModelEvent]:
74
+ """Hand the provider one system message, or the very request it was given when there is nothing to join."""
75
+ merged = merge_system(request.messages)
76
+ if merged is request.messages:
77
+ async for event in call_next(request):
78
+ yield event
79
+ return
80
+ async for event in call_next(replace(request, messages=merged)):
81
+ yield event
@@ -0,0 +1,38 @@
1
+ """The plugin that hands the agent the one-system-message hook."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ from zettcode.engine.plugins.context import PluginContext
8
+ from zettcode.engine.plugins.plugin import Plugin
9
+
10
+ if TYPE_CHECKING: # pragma: no cover - annotations only
11
+ from zett_agent.extensions.base import AgentExtension
12
+
13
+ from zettcode.engine.plugins.state import AgentBuild
14
+
15
+
16
+ class OneSystem(Plugin[PluginContext]):
17
+ """Every instruction in one system message, for the endpoints that take one and for the ones that take many.
18
+
19
+ The merge runs once per request, at the point the provider would see the messages, so it covers the instructions a
20
+ part added for that request as well as the ones the agent carries everywhere. It changes nothing a model reads — the
21
+ same text, in the same order, separated by the blank line the Anthropic-compatible provider already used between
22
+ them — and it is off for `[plugins] disable = ["one-system"]` like any other part.
23
+ """
24
+
25
+ name = "one-system"
26
+ description = "All instructions the model is given in one system message"
27
+
28
+ def activate(self, ctx: PluginContext) -> None:
29
+ """Register the extension as a factory: it is built with the agent, not while the first frame is drawn."""
30
+
31
+ # Imported in the factory: the extension imports the framework's message and hook types, which a launch that
32
+ # never builds an agent does not need.
33
+ def build(_: AgentBuild) -> AgentExtension:
34
+ from .extension import OneSystemMessage
35
+
36
+ return OneSystemMessage()
37
+
38
+ ctx.agent.register_extension_factory("OneSystemMessage", build)
File without changes
@@ -0,0 +1,19 @@
1
+ Metadata-Version: 2.5
2
+ Name: zettcode-one-system
3
+ Version: 0.5.1
4
+ Summary: Every instruction ZettCode gives the model in one system message, as a part of the agent that lives apart from it
5
+ Project-URL: Repository, https://github.com/Chang-LeHung/zettcode
6
+ Author: Chang-LeHung
7
+ License-Expression: MIT
8
+ Keywords: agent,prompt,system-message,zettcode
9
+ Classifier: Development Status :: 5 - Production/Stable
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: Programming Language :: Python :: 3.13
14
+ Classifier: Programming Language :: Python :: 3.14
15
+ Classifier: Programming Language :: Python :: 3.15
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.10
18
+ Requires-Dist: zett-agent<0.3,>=0.2.10
19
+ Requires-Dist: zettcode-engine<0.6,>=0.5.1
@@ -0,0 +1,8 @@
1
+ zettcode/one_system/__init__.py,sha256=2OsABcOsB5tyjXWdYpESd8vkvHZiB3_eWDNQIObJktY,544
2
+ zettcode/one_system/extension.py,sha256=RbmJ0i4kNdAqIoSozIoVcK--wKQyYkp-Y2inWwB6_-I,4344
3
+ zettcode/one_system/plugin.py,sha256=7zDk5xNrK9phckqaejeVqZJ1y-SCljoUMxNhkCppoMM,1684
4
+ zettcode/one_system/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
+ zettcode_one_system-0.5.1.dist-info/METADATA,sha256=Zurg4BsYInnnPNvYtBx3I_vlPRgu1Fu3AqPvEP0l6jM,840
6
+ zettcode_one_system-0.5.1.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
7
+ zettcode_one_system-0.5.1.dist-info/entry_points.txt,sha256=N_kR-dJzRKscTVr6x1tqrUmxXy0CFV0OQtwnAJyvqJE,68
8
+ zettcode_one_system-0.5.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [zettcode.plugin]
2
+ one-system = zettcode.one_system.plugin:OneSystem