sagent 0.1.0__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.
- sagent/__init__.py +187 -0
- sagent/agent/__init__.py +37 -0
- sagent/agent/agent.py +1729 -0
- sagent/agent/compaction.py +170 -0
- sagent/agent/cost_tracker.py +55 -0
- sagent/agent/dispatch.py +314 -0
- sagent/agent/retry.py +352 -0
- sagent/agent/session_io.py +657 -0
- sagent/agents_md.py +454 -0
- sagent/assets/codex.yaml +39 -0
- sagent/assets/custom/compactor.md +107 -0
- sagent/assets/custom/compactor_continuation.md +3 -0
- sagent/assets/custom/compactor_no_tools.md +6 -0
- sagent/assets/custom/compactor_no_tools_reminder.md +1 -0
- sagent/assets/custom/compactor_partial.md +72 -0
- sagent/assets/custom/compactor_system.md +1 -0
- sagent/assets/custom/prompt.md +65 -0
- sagent/assets/custom/prompt_ant_code_style.md +7 -0
- sagent/assets/custom/prompt_ant_false_claims.md +1 -0
- sagent/assets/custom/prompt_ant_interruptions.md +1 -0
- sagent/assets/custom/prompt_ant_issue_share.md +1 -0
- sagent/assets/custom/prompt_ant_misconception.md +1 -0
- sagent/assets/custom/prompt_env.md +9 -0
- sagent/assets/custom/tools_agentself.md +31 -0
- sagent/assets/custom/tools_agentsend.md +20 -0
- sagent/assets/custom/tools_agentspawn.md +53 -0
- sagent/assets/custom/tools_backgroundtask.md +20 -0
- sagent/assets/custom/tools_bash.md +43 -0
- sagent/assets/custom/tools_edit.md +9 -0
- sagent/assets/custom/tools_glob.md +5 -0
- sagent/assets/custom/tools_grep.md +13 -0
- sagent/assets/custom/tools_list.md +7 -0
- sagent/assets/custom/tools_paperauthor.md +37 -0
- sagent/assets/custom/tools_paperdetails.md +48 -0
- sagent/assets/custom/tools_paperfetch.md +31 -0
- sagent/assets/custom/tools_papersearch.md +48 -0
- sagent/assets/custom/tools_read.md +17 -0
- sagent/assets/custom/tools_webfetch.md +14 -0
- sagent/assets/custom/tools_websearch.md +25 -0
- sagent/assets/custom/tools_write.md +7 -0
- sagent/assets/persona/default.md +18 -0
- sagent/assets/sagent.yaml +39 -0
- sagent/bin/__init__.py +1 -0
- sagent/bin/check_wheel.py +66 -0
- sagent/bin/cli.py +494 -0
- sagent/bin/slack.py +1041 -0
- sagent/bin/verify_models.py +299 -0
- sagent/compactor.py +662 -0
- sagent/custom_exceptions.py +91 -0
- sagent/custom_types.py +686 -0
- sagent/lib/__init__.py +7 -0
- sagent/lib/apikey.py +9 -0
- sagent/lib/asyncio_collections.py +95 -0
- sagent/lib/atomic_file.py +123 -0
- sagent/lib/compaction.py +141 -0
- sagent/lib/debug_log.py +149 -0
- sagent/lib/descriptors.py +336 -0
- sagent/lib/dotsagent.py +59 -0
- sagent/lib/env.py +21 -0
- sagent/lib/image.py +370 -0
- sagent/lib/json.py +146 -0
- sagent/lib/lazy_import.py +49 -0
- sagent/lib/message.py +175 -0
- sagent/lib/testing.py +60 -0
- sagent/lib/web/__init__.py +16 -0
- sagent/lib/web/fetch.py +515 -0
- sagent/lib/web/search.py +165 -0
- sagent/memory.py +203 -0
- sagent/prompt.py +240 -0
- sagent/providers/__init__.py +54 -0
- sagent/providers/anthropic.py +1081 -0
- sagent/providers/dashscope.py +152 -0
- sagent/providers/google.py +734 -0
- sagent/providers/lib/__init__.py +24 -0
- sagent/providers/lib/cost.py +54 -0
- sagent/providers/lib/id_remap.py +47 -0
- sagent/providers/lib/stop_reason.py +117 -0
- sagent/providers/minimax.py +82 -0
- sagent/providers/moonshot.py +101 -0
- sagent/providers/openai.py +238 -0
- sagent/providers/openai_compat.py +857 -0
- sagent/providers/providers.py +106 -0
- sagent/repl/__init__.py +76 -0
- sagent/repl/custom_types.py +24 -0
- sagent/repl/handlers.py +305 -0
- sagent/repl/input_pane.py +53 -0
- sagent/repl/keybindings.py +135 -0
- sagent/repl/render.py +145 -0
- sagent/repl/render_diff.py +395 -0
- sagent/repl/repl.py +241 -0
- sagent/repl/slash_commands.py +168 -0
- sagent/repl/tight_markdown.py +137 -0
- sagent/sessions.py +355 -0
- sagent/testing.py +41 -0
- sagent/tools/__init__.py +94 -0
- sagent/tools/advisor.py +178 -0
- sagent/tools/agent_self.py +415 -0
- sagent/tools/agent_send.py +144 -0
- sagent/tools/agent_spawn.py +804 -0
- sagent/tools/background_task.py +223 -0
- sagent/tools/bash.py +353 -0
- sagent/tools/core.py +1081 -0
- sagent/tools/edit.py +305 -0
- sagent/tools/glob_tool.py +193 -0
- sagent/tools/grep.py +865 -0
- sagent/tools/lib/__init__.py +45 -0
- sagent/tools/lib/bash.py +840 -0
- sagent/tools/lib/pdf.py +227 -0
- sagent/tools/linear.py +460 -0
- sagent/tools/list.py +197 -0
- sagent/tools/paper_author.py +417 -0
- sagent/tools/paper_common.py +567 -0
- sagent/tools/paper_details.py +367 -0
- sagent/tools/paper_fetch.py +228 -0
- sagent/tools/paper_search.py +612 -0
- sagent/tools/play_audio.py +179 -0
- sagent/tools/read.py +539 -0
- sagent/tools/result_storage.py +301 -0
- sagent/tools/skill.py +318 -0
- sagent/tools/slack.py +383 -0
- sagent/tools/web_fetch.py +254 -0
- sagent/tools/web_search.py +147 -0
- sagent/tools/wiki.py +318 -0
- sagent/tools/write.py +110 -0
- sagent-0.1.0.dist-info/METADATA +407 -0
- sagent-0.1.0.dist-info/RECORD +129 -0
- sagent-0.1.0.dist-info/WHEEL +4 -0
- sagent-0.1.0.dist-info/entry_points.txt +3 -0
- sagent-0.1.0.dist-info/licenses/LICENSE +201 -0
sagent/__init__.py
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
"""sagent -- a Python agent library.
|
|
2
|
+
|
|
3
|
+
::
|
|
4
|
+
|
|
5
|
+
from sagent import tools
|
|
6
|
+
from sagent.agent import Agent
|
|
7
|
+
from sagent.providers import Google
|
|
8
|
+
from sagent.lib.json import json_freeze
|
|
9
|
+
|
|
10
|
+
agent = Agent(
|
|
11
|
+
model=Google.from_env().model("gemini-2.5-flash"),
|
|
12
|
+
system="You are a scientist.",
|
|
13
|
+
tools=[tools.Bash(), tools.Read(), tools.Grep()],
|
|
14
|
+
)
|
|
15
|
+
result = await agent.run(json_freeze({"prompt": "analyze ./data/"}))
|
|
16
|
+
|
|
17
|
+
This module docstring is the authoritative reference for sagent's
|
|
18
|
+
cross-cutting design contracts. Submodule docstrings cover
|
|
19
|
+
implementation details within their scope.
|
|
20
|
+
|
|
21
|
+
Agent
|
|
22
|
+
-----
|
|
23
|
+
An Agent combines four parts:
|
|
24
|
+
|
|
25
|
+
- **Model** -- the LLM endpoint, from a **Provider**.
|
|
26
|
+
- **Tools** -- the agent's capabilities (``run(msg) -> Message``).
|
|
27
|
+
- **System prompt** -- static string or dict of named sections.
|
|
28
|
+
- **Compactor** -- summarizes conversation when the context window
|
|
29
|
+
fills. Optional; without one the agent runs until it hits the
|
|
30
|
+
window limit.
|
|
31
|
+
|
|
32
|
+
Inbox zero
|
|
33
|
+
----------
|
|
34
|
+
The agent loop pursues inbox zero: process everything, then go idle.
|
|
35
|
+
|
|
36
|
+
::
|
|
37
|
+
|
|
38
|
+
while True:
|
|
39
|
+
drain inbox -> inject as user messages
|
|
40
|
+
call LLM
|
|
41
|
+
if tool calls: dispatch, loop back to drain
|
|
42
|
+
if inbox empty and LLM done: go idle
|
|
43
|
+
|
|
44
|
+
The inbox is the ONLY entry point for user-message content. User
|
|
45
|
+
prompts, background results, agent-to-agent messages -- all go
|
|
46
|
+
through ``inbox.put()`` / ``inbox.put_left()``. There is ONE
|
|
47
|
+
drain point (``_drain_inbox``) at the top of each loop iteration.
|
|
48
|
+
No other code reads from the inbox or appends user messages to the
|
|
49
|
+
message history.
|
|
50
|
+
|
|
51
|
+
Context-affecting slash commands are inbox messages too. For example,
|
|
52
|
+
``/clear`` must be enqueued with ``put_left`` and interpreted by
|
|
53
|
+
``_drain_inbox``; REPL/keybinding code must not clear messages or
|
|
54
|
+
mutate context directly. REPL-local commands that do not touch model
|
|
55
|
+
context, such as ``/model`` display/swap and ``/login``, may be handled
|
|
56
|
+
by the surface before they enter the inbox.
|
|
57
|
+
|
|
58
|
+
Priority is insertion order: user messages go to the front
|
|
59
|
+
(``put_left``), everything else appends at the back.
|
|
60
|
+
|
|
61
|
+
Two queues connect the Agent to its surface (REPL, Slackbot,
|
|
62
|
+
parent agent):
|
|
63
|
+
|
|
64
|
+
- ``inbox`` (Deque[str]) -- inbound.
|
|
65
|
+
- ``events`` (Queue[Message | None]) -- outbound. Every observable
|
|
66
|
+
side effect flows here as a typed Message. ``None`` = request
|
|
67
|
+
boundary.
|
|
68
|
+
|
|
69
|
+
Message
|
|
70
|
+
-------
|
|
71
|
+
The universal unit of exchange. Every input, output, tool call,
|
|
72
|
+
tool result, event, and error is a ``Message``. There is no
|
|
73
|
+
``ToolResult`` or ``UserTurn`` class -- just ``Message`` with a
|
|
74
|
+
``descriptor`` that says what it carries.
|
|
75
|
+
|
|
76
|
+
::
|
|
77
|
+
|
|
78
|
+
type MessageContent = str | bytes | JSON | tuple[Message, ...]
|
|
79
|
+
|
|
80
|
+
@dataclass(frozen=True)
|
|
81
|
+
class Message:
|
|
82
|
+
content: MessageContent
|
|
83
|
+
descriptor: str # MIME-style type tag
|
|
84
|
+
id: int # process-wide auto-increment
|
|
85
|
+
parent_id: int # originating Message.id; -1 = unset
|
|
86
|
+
timestamp: int # nanosecond epoch
|
|
87
|
+
|
|
88
|
+
``content`` is recursive: a compound message (user message, tool
|
|
89
|
+
result) holds ``tuple[Message, ...]`` of atomic parts. Branch on
|
|
90
|
+
``descriptor`` to determine what a Message is, never on
|
|
91
|
+
``isinstance(content)``.
|
|
92
|
+
|
|
93
|
+
Descriptors
|
|
94
|
+
-----------
|
|
95
|
+
The single source of truth for all known descriptors and their
|
|
96
|
+
content-type groups is ``lib/descriptors.py``. See the constants
|
|
97
|
+
``TEXT_DESCRIPTORS``, ``IMAGE_DESCRIPTORS``, ``BINARY_DESCRIPTORS``,
|
|
98
|
+
``JSON_DESCRIPTORS``, ``MULTIPART_DESCRIPTORS``, and
|
|
99
|
+
``ALL_DESCRIPTORS`` defined there.
|
|
100
|
+
|
|
101
|
+
Tool IDs (``application/x-tool-<name>``) are dynamic -- one per tool
|
|
102
|
+
class -- and validated at registration time, not in the registry.
|
|
103
|
+
|
|
104
|
+
Tool contract
|
|
105
|
+
-------------
|
|
106
|
+
Message in, Message out. A tool receives a ``Message`` containing
|
|
107
|
+
the LLM's directive and returns a ``Message`` containing the
|
|
108
|
+
result. That is the entire execution interface.
|
|
109
|
+
|
|
110
|
+
- ``run(msg) -> Message`` -- execute the directive. Returns a
|
|
111
|
+
``Message`` with ``text/plain`` on success or ``text/x-error``
|
|
112
|
+
on failure. Never raises -- see error policy below.
|
|
113
|
+
- ``directive_schema`` -- JSON Schema describing valid directives.
|
|
114
|
+
- ``prompt() -> str | None`` -- per-request system prompt section.
|
|
115
|
+
``None`` = no change (avoids cache invalidation).
|
|
116
|
+
- ``help(msg) -> str`` -- human-readable invocation summary.
|
|
117
|
+
|
|
118
|
+
An Agent is itself a Tool (``run`` takes a prompt, returns the
|
|
119
|
+
final response), so agents compose recursively.
|
|
120
|
+
|
|
121
|
+
Model contract
|
|
122
|
+
--------------
|
|
123
|
+
``buffer()`` and ``stream()`` send a ``ModelRequest`` and return a
|
|
124
|
+
``ModelResponse``. They raise on failure (``PromptTooLongError``,
|
|
125
|
+
``RateLimitError``, ``StreamInterruptedError``). The Agent's retry
|
|
126
|
+
and compaction machinery handles recovery.
|
|
127
|
+
|
|
128
|
+
``is_context_overflow(error)`` returns ``True`` if the error is a
|
|
129
|
+
context-window overflow.
|
|
130
|
+
|
|
131
|
+
Compactor contract
|
|
132
|
+
------------------
|
|
133
|
+
- ``should_compact()`` -- whether to compact this request.
|
|
134
|
+
- ``compact()`` -- summarize messages into a compact list. Returns
|
|
135
|
+
a failure Message if all retry attempts are exhausted (not raise --
|
|
136
|
+
see error policy below).
|
|
137
|
+
- ``maintain()`` -- between-request context maintenance (e.g. clear
|
|
138
|
+
stale tool results).
|
|
139
|
+
|
|
140
|
+
CompactRestorable
|
|
141
|
+
-----------------
|
|
142
|
+
Optional protocol for tools that need to restore state after
|
|
143
|
+
compaction (e.g. re-inject invoked skill bodies). The Agent calls
|
|
144
|
+
``post_compact_restore(messages, tool_state, budget_chars=N)`` on
|
|
145
|
+
tools that implement it.
|
|
146
|
+
|
|
147
|
+
Error policy
|
|
148
|
+
------------
|
|
149
|
+
**If your output enters the conversation, return a Message.
|
|
150
|
+
If your output is infrastructure plumbing, raise.**
|
|
151
|
+
|
|
152
|
+
This yields three patterns:
|
|
153
|
+
|
|
154
|
+
1. **Tools return error Messages.** A tool's ``run()`` returns
|
|
155
|
+
``Message`` by contract, so errors are ``text/x-error`` parts.
|
|
156
|
+
The LLM sees the error as a tool result and can self-correct.
|
|
157
|
+
``_invoke_tool_safe`` is the safety net for unexpected
|
|
158
|
+
exceptions -- it wraps them in ``<tool_use_error>`` so the
|
|
159
|
+
LLM still sees something actionable.
|
|
160
|
+
|
|
161
|
+
2. **Infrastructure raises.** Model and Provider methods don't
|
|
162
|
+
return Messages -- they return ``ModelResponse`` or build
|
|
163
|
+
objects. Errors are exceptions. The Agent catches and handles:
|
|
164
|
+
retry, compact, disable, or surface to the user. The LLM
|
|
165
|
+
never sees these exceptions directly.
|
|
166
|
+
|
|
167
|
+
3. **Optional enrichment is individually isolated.** Post-compact
|
|
168
|
+
steps (file re-attachment, tool state restoration, background
|
|
169
|
+
status) are best-effort. Each is wrapped in its own try/except
|
|
170
|
+
that logs and continues. One failure does not cascade -- it
|
|
171
|
+
must not disable compaction or crash the conversation.
|
|
172
|
+
|
|
173
|
+
Compactor is illustrative: ``compact()`` returns ``list[Message]``
|
|
174
|
+
(a Message-returning interface), so on exhausted retries it
|
|
175
|
+
returns a failure Message rather than raising. The LLM needs to
|
|
176
|
+
know context was lost so it can recover.
|
|
177
|
+
"""
|
|
178
|
+
|
|
179
|
+
from sagent import providers, tools
|
|
180
|
+
from sagent.agent import Agent
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
__all__ = [
|
|
184
|
+
"Agent",
|
|
185
|
+
"providers",
|
|
186
|
+
"tools",
|
|
187
|
+
]
|
sagent/agent/__init__.py
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Agent: Model + Tools + Prompt + Compactor behind two queues.
|
|
2
|
+
|
|
3
|
+
See ``sagent/__init__.py`` for the Agent architecture, inbox zero
|
|
4
|
+
loop, error policy, and contract definitions.
|
|
5
|
+
|
|
6
|
+
Session persistence
|
|
7
|
+
-------------------
|
|
8
|
+
``_save_session`` serializes ``_messages`` and metadata to
|
|
9
|
+
``session.jsonl``. The request loop wraps in ``try/finally`` so
|
|
10
|
+
every exit path persists. Internal mutations (status change,
|
|
11
|
+
compaction, clear) save immediately.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from sagent.agent.agent import (
|
|
15
|
+
ERROR_MAX_TOOL_CALL_ROUNDS,
|
|
16
|
+
ERROR_NO_PROMPT,
|
|
17
|
+
QUIT_SENTINEL,
|
|
18
|
+
Agent,
|
|
19
|
+
RunHandle,
|
|
20
|
+
SystemPrompt,
|
|
21
|
+
)
|
|
22
|
+
from sagent.compactor import MICROCOMPACT_KEEP_RECENT
|
|
23
|
+
from sagent.custom_types import ContextBudget
|
|
24
|
+
from sagent.tools.background_task import BackgroundTaskEntry
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"ERROR_MAX_TOOL_CALL_ROUNDS",
|
|
29
|
+
"ERROR_NO_PROMPT",
|
|
30
|
+
"MICROCOMPACT_KEEP_RECENT",
|
|
31
|
+
"QUIT_SENTINEL",
|
|
32
|
+
"Agent",
|
|
33
|
+
"BackgroundTaskEntry",
|
|
34
|
+
"ContextBudget",
|
|
35
|
+
"RunHandle",
|
|
36
|
+
"SystemPrompt",
|
|
37
|
+
]
|