keystone-agent-tools 0.1.5__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.
Files changed (35) hide show
  1. keystone_agent_tools-0.1.5/PKG-INFO +15 -0
  2. keystone_agent_tools-0.1.5/__init__.py +18 -0
  3. keystone_agent_tools-0.1.5/attachments/__init__.py +61 -0
  4. keystone_agent_tools-0.1.5/attachments/audio.py +104 -0
  5. keystone_agent_tools-0.1.5/attachments/blocks.py +491 -0
  6. keystone_agent_tools-0.1.5/attachments/budget.py +61 -0
  7. keystone_agent_tools-0.1.5/attachments/images.py +387 -0
  8. keystone_agent_tools-0.1.5/files/__init__.py +105 -0
  9. keystone_agent_tools-0.1.5/files/client.py +650 -0
  10. keystone_agent_tools-0.1.5/files/content.py +147 -0
  11. keystone_agent_tools-0.1.5/files/descriptions.py +223 -0
  12. keystone_agent_tools-0.1.5/files/models.py +296 -0
  13. keystone_agent_tools-0.1.5/files/tools.py +979 -0
  14. keystone_agent_tools-0.1.5/files/wire.py +168 -0
  15. keystone_agent_tools-0.1.5/kb/__init__.py +36 -0
  16. keystone_agent_tools-0.1.5/kb/descriptions.py +147 -0
  17. keystone_agent_tools-0.1.5/kb/results.py +196 -0
  18. keystone_agent_tools-0.1.5/kb/tools.py +302 -0
  19. keystone_agent_tools-0.1.5/keystone_agent_tools.egg-info/PKG-INFO +15 -0
  20. keystone_agent_tools-0.1.5/keystone_agent_tools.egg-info/SOURCES.txt +36 -0
  21. keystone_agent_tools-0.1.5/keystone_agent_tools.egg-info/dependency_links.txt +1 -0
  22. keystone_agent_tools-0.1.5/keystone_agent_tools.egg-info/requires.txt +11 -0
  23. keystone_agent_tools-0.1.5/keystone_agent_tools.egg-info/top_level.txt +1 -0
  24. keystone_agent_tools-0.1.5/parity.py +120 -0
  25. keystone_agent_tools-0.1.5/pyproject.toml +53 -0
  26. keystone_agent_tools-0.1.5/setup.cfg +4 -0
  27. keystone_agent_tools-0.1.5/tests/test_attachments.py +504 -0
  28. keystone_agent_tools-0.1.5/tests/test_client.py +447 -0
  29. keystone_agent_tools-0.1.5/tests/test_content_wire.py +74 -0
  30. keystone_agent_tools-0.1.5/tests/test_kb_repeats.py +150 -0
  31. keystone_agent_tools-0.1.5/tests/test_kb_tools.py +123 -0
  32. keystone_agent_tools-0.1.5/tests/test_own_conversation.py +231 -0
  33. keystone_agent_tools-0.1.5/tests/test_tools.py +378 -0
  34. keystone_agent_tools-0.1.5/tests/test_transcription.py +233 -0
  35. keystone_agent_tools-0.1.5/transcription.py +347 -0
@@ -0,0 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: keystone-agent-tools
3
+ Version: 0.1.5
4
+ Summary: The file tools a platform agent gets (read / search / create / render / edit an artifact through file-artifact), the extended knowledge-base toolset (FR-180), and how a turn's attachments reach the model. One definition for visual-builder and master-agent, so the two cannot drift.
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: httpx<1,>=0.27
7
+ Requires-Dist: pydantic<3,>=2.7
8
+ Requires-Dist: langchain-core<2,>=1
9
+ Requires-Dist: langgraph<2,>=1
10
+ Requires-Dist: Pillow<14,>=13
11
+ Provides-Extra: dev
12
+ Requires-Dist: pytest<9,>=8; extra == "dev"
13
+ Requires-Dist: pytest-asyncio<1,>=0.24; extra == "dev"
14
+ Requires-Dist: pytest-cov<7,>=6; extra == "dev"
15
+ Requires-Dist: ruff==0.15.15; extra == "dev"
@@ -0,0 +1,18 @@
1
+ """The tools a platform agent uses on files, shared by visual-builder and master-agent (FDP-6967).
2
+
3
+ * :mod:`keystone.agent_tools.files` — the file-artifact client (create / render as background
4
+ jobs, FDP-7030) and the file tools the model calls (``read_artifact``, ``search_in_artifact``,
5
+ ``search_across_thread``, ``create_artifact``, ``render_artifact``, ``edit_artifact`` and the
6
+ ``list`` / ``get_metadata`` / ``publish`` tools they refer to). The names, arguments and the
7
+ words the model reads are written ONCE, here.
8
+ * :mod:`keystone.agent_tools.kb` — the extended knowledge-base toolset (FR-180, FDP-7133): the six
9
+ ``kb_*`` tools, their descriptions and the Knowledge Bases block of the system prompt, with rag's
10
+ agent guide (and default metadata filter) of each KB. visual-builder and pro-code use it as is.
11
+ * :mod:`keystone.agent_tools.attachments` — how the files attached to a turn become content
12
+ blocks for the model: one token budget per turn, pictures downscaled to the provider limit,
13
+ what could not be sent noted for the model.
14
+
15
+ What a service still owns: who the call acts as (its headers), which turn a file belongs to, how
16
+ a file card reaches its chat, and the shape of a tool result in its graph state. A service plugs
17
+ those in through :class:`keystone.agent_tools.files.FileToolHost`.
18
+ """
@@ -0,0 +1,61 @@
1
+ """How a turn's attachments reach the model (FDP-6967, item 2) — one set of rules for every agent.
2
+
3
+ from keystone.agent_tools.attachments import AttachedFile, TurnPictures, document_blocks, image_blocks
4
+
5
+ See :mod:`keystone.agent_tools.attachments.blocks` for the rules, :mod:`.budget` for the turn budget
6
+ and :mod:`.images` for fitting a picture to the provider's limit.
7
+ """
8
+
9
+ from keystone.agent_tools.attachments.audio import AUDIO_HEARD_NOTE, audio_blocks, transcript_blocks
10
+ from keystone.agent_tools.attachments.blocks import (
11
+ IMAGE_MIMES,
12
+ AttachedFile,
13
+ AttachmentLimits,
14
+ FileBlocks,
15
+ TurnPictures,
16
+ aggregate_refused_note,
17
+ attached_marker,
18
+ document_blocks,
19
+ fmt_size,
20
+ image_blocks,
21
+ is_document,
22
+ is_image,
23
+ named,
24
+ text_cap_chars,
25
+ )
26
+ from keystone.agent_tools.attachments.budget import (
27
+ MIN_FLOOR_TOKENS_PER_FILE,
28
+ STATIC_TURN_ATTACHMENT_BUDGET,
29
+ cap_bytes_for_share,
30
+ compute_share_per_file,
31
+ refuses_content,
32
+ )
33
+ from keystone.agent_tools.attachments.images import DecodeRefused, fit_image, fit_image_async
34
+
35
+ __all__ = [
36
+ "AUDIO_HEARD_NOTE",
37
+ "audio_blocks",
38
+ "transcript_blocks",
39
+ "IMAGE_MIMES",
40
+ "MIN_FLOOR_TOKENS_PER_FILE",
41
+ "STATIC_TURN_ATTACHMENT_BUDGET",
42
+ "AttachedFile",
43
+ "AttachmentLimits",
44
+ "DecodeRefused",
45
+ "FileBlocks",
46
+ "TurnPictures",
47
+ "aggregate_refused_note",
48
+ "attached_marker",
49
+ "cap_bytes_for_share",
50
+ "compute_share_per_file",
51
+ "document_blocks",
52
+ "fit_image",
53
+ "fit_image_async",
54
+ "fmt_size",
55
+ "image_blocks",
56
+ "is_document",
57
+ "is_image",
58
+ "named",
59
+ "refuses_content",
60
+ "text_cap_chars",
61
+ ]
@@ -0,0 +1,104 @@
1
+ """An audio clip attached to a turn, as blocks: its transcript, or why it could not be heard (FDP-6967).
2
+
3
+ The words are visual-builder's (``services/agent_version/attachments.py``), which master-agent had
4
+ copied; now one copy. :func:`audio_blocks` calls the shared speech-to-text client
5
+ (:mod:`keystone.agent_tools.transcription`) — a host passes its identity headers, the agent's STT
6
+ model, and how to reach the clip (bytes, a spool, or a presigned URL for a long one).
7
+ """
8
+
9
+ # Feature: FR-130 | Tracking: FDP-3032, FDP-6967
10
+
11
+ from __future__ import annotations
12
+
13
+ import logging
14
+ from typing import Any
15
+
16
+ from keystone.agent_tools.attachments.blocks import AttachedFile, AttachmentLimits, FileBlocks, text_cap_chars
17
+ from keystone.agent_tools.transcription import Transcript, TranscriptionFailed
18
+
19
+ __all__ = ["AUDIO_HEARD_NOTE", "audio_blocks", "transcript_blocks"]
20
+
21
+ _logger = logging.getLogger("keystone.agent_tools.attachments")
22
+
23
+ #: Once per turn that carries a heard clip: without it models answered "I can't listen to audio, but
24
+ #: I can read the transcript you attached" — the block read to them as a transcript the USER pasted.
25
+ AUDIO_HEARD_NOTE = (
26
+ "[system-note: the audio above is what the user said, heard through speech-to-text. "
27
+ "Answer from what was said; do not say you cannot listen to audio.]"
28
+ )
29
+
30
+
31
+ def transcript_blocks(name: str | None, result: Transcript | str, *, cap_chars: int) -> list[dict[str, Any]]:
32
+ """What the model reads for one clip: its transcript, or (``result`` a str) why it was not heard."""
33
+ label = name or "audio"
34
+ if isinstance(result, str):
35
+ return [
36
+ {
37
+ "type": "text",
38
+ "text": (
39
+ f"[system-note: the user attached the audio file {label}, but it could not be "
40
+ f"transcribed ({result}). Tell the user you could not hear it and ask them to "
41
+ "send it again or type the message.]"
42
+ ),
43
+ }
44
+ ]
45
+ text = result.text
46
+ if not text:
47
+ return [
48
+ {
49
+ "type": "text",
50
+ "text": f"[system-note: the audio file {label} was transcribed, but no speech was found in it.]",
51
+ }
52
+ ]
53
+ length = f", {result.duration_seconds:.0f} s" if result.duration_seconds else ""
54
+ note = ""
55
+ if cap_chars > 0 and len(text) > cap_chars:
56
+ text = text[:cap_chars]
57
+ note = " Only the start is shown: the conversation has no room for the rest."
58
+ # Worded as audio the agent HEARD, not a transcript the user supplied (see AUDIO_HEARD_NOTE).
59
+ return [
60
+ {
61
+ "type": "text",
62
+ "text": (
63
+ f"[The user sent an audio clip ({label}{length}). What was said, transcribed "
64
+ f"automatically (a word may be misheard):{note}]\n{text}"
65
+ ),
66
+ }
67
+ ]
68
+
69
+
70
+ async def audio_blocks(
71
+ transcriber: Any,
72
+ file: AttachedFile,
73
+ *,
74
+ model: str,
75
+ headers: dict[str, str],
76
+ share_tokens: int,
77
+ limits: AttachmentLimits = AttachmentLimits(), # noqa: B008 - frozen, shared default
78
+ content: bytes | None = None,
79
+ open_content: Any = None,
80
+ audio_url: Any = None,
81
+ ) -> FileBlocks:
82
+ """One clip through the shared STT client. ``status``: ``shown`` when heard (speech or not),
83
+ ``unreadable`` when it could not be transcribed — never raises."""
84
+ try:
85
+ heard = await transcriber.transcribe(
86
+ filename=file.name,
87
+ mime=file.base_mime,
88
+ model=model,
89
+ headers=headers,
90
+ size=file.size,
91
+ content=content,
92
+ open_content=open_content,
93
+ audio_url=audio_url,
94
+ )
95
+ except TranscriptionFailed as exc:
96
+ _logger.warning("audio attachment not transcribed artifact_id=%s error=%s", file.artifact_id, exc)
97
+ reason = (
98
+ "it is over the size limit"
99
+ if exc.code == "AUDIO_TOO_LARGE"
100
+ else "the speech-to-text service could not process it"
101
+ )
102
+ return FileBlocks(transcript_blocks(file.name, reason, cap_chars=0), "unreadable")
103
+ cap = text_cap_chars(share_tokens, limits)
104
+ return FileBlocks(transcript_blocks(file.name, heard, cap_chars=cap), "shown")