3tears-agent-tools 0.14.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.
Files changed (66) hide show
  1. 3tears_agent_tools-0.14.0.dist-info/METADATA +207 -0
  2. 3tears_agent_tools-0.14.0.dist-info/RECORD +66 -0
  3. 3tears_agent_tools-0.14.0.dist-info/WHEEL +4 -0
  4. 3tears_agent_tools-0.14.0.dist-info/licenses/LICENSE +21 -0
  5. threetears/agent/tools/__init__.py +225 -0
  6. threetears/agent/tools/_coercion.py +102 -0
  7. threetears/agent/tools/aliases.py +280 -0
  8. threetears/agent/tools/base_tool.py +223 -0
  9. threetears/agent/tools/bootstrap.py +292 -0
  10. threetears/agent/tools/bridge.py +123 -0
  11. threetears/agent/tools/builtin/__init__.py +160 -0
  12. threetears/agent/tools/builtin/analyze_media.py +748 -0
  13. threetears/agent/tools/builtin/calculator.py +206 -0
  14. threetears/agent/tools/builtin/context_recall.py +168 -0
  15. threetears/agent/tools/builtin/current_date.py +156 -0
  16. threetears/agent/tools/builtin/dictionary.py +183 -0
  17. threetears/agent/tools/builtin/image_generation.py +298 -0
  18. threetears/agent/tools/builtin/image_prep.py +229 -0
  19. threetears/agent/tools/builtin/timezone_converter.py +188 -0
  20. threetears/agent/tools/builtin/unit_converter.py +141 -0
  21. threetears/agent/tools/builtin/web_fetch.py +243 -0
  22. threetears/agent/tools/builtin/web_search.py +159 -0
  23. threetears/agent/tools/call_scope.py +202 -0
  24. threetears/agent/tools/chunker.py +235 -0
  25. threetears/agent/tools/collections.py +521 -0
  26. threetears/agent/tools/config.py +127 -0
  27. threetears/agent/tools/consume.py +217 -0
  28. threetears/agent/tools/context.py +851 -0
  29. threetears/agent/tools/context_envelope.py +190 -0
  30. threetears/agent/tools/deliver.py +216 -0
  31. threetears/agent/tools/document.py +1031 -0
  32. threetears/agent/tools/engagement.py +139 -0
  33. threetears/agent/tools/engagement_resolver.py +284 -0
  34. threetears/agent/tools/entities.py +55 -0
  35. threetears/agent/tools/events.py +75 -0
  36. threetears/agent/tools/executor.py +133 -0
  37. threetears/agent/tools/graph_nodes.py +236 -0
  38. threetears/agent/tools/langchain_adapter.py +135 -0
  39. threetears/agent/tools/mcp.py +145 -0
  40. threetears/agent/tools/migrations/__init__.py +94 -0
  41. threetears/agent/tools/migrations/v001_create_context_items_table.py +61 -0
  42. threetears/agent/tools/migrations/v002_datetime_to_datetimetz.py +153 -0
  43. threetears/agent/tools/migrations/v003_align_context_items_shape.py +166 -0
  44. threetears/agent/tools/migrations/v004_tool_result_dedup_index.py +67 -0
  45. threetears/agent/tools/object_resolver.py +256 -0
  46. threetears/agent/tools/platform_migrations/__init__.py +80 -0
  47. threetears/agent/tools/platform_migrations/v001_add_tool_eligibility_columns.py +65 -0
  48. threetears/agent/tools/produce.py +206 -0
  49. threetears/agent/tools/protocols.py +31 -0
  50. threetears/agent/tools/py.typed +0 -0
  51. threetears/agent/tools/registry.py +81 -0
  52. threetears/agent/tools/report.py +539 -0
  53. threetears/agent/tools/reports/__init__.py +22 -0
  54. threetears/agent/tools/reports/markdown_compiler.py +271 -0
  55. threetears/agent/tools/reports/mermaid_generator.py +190 -0
  56. threetears/agent/tools/reports/pdf_renderer.py +152 -0
  57. threetears/agent/tools/reports/severity.py +93 -0
  58. threetears/agent/tools/router.py +228 -0
  59. threetears/agent/tools/serve.py +248 -0
  60. threetears/agent/tools/server.py +2086 -0
  61. threetears/agent/tools/strategy.py +302 -0
  62. threetears/agent/tools/todo.py +303 -0
  63. threetears/agent/tools/tool_llm_dispatch.py +233 -0
  64. threetears/agent/tools/types.py +18 -0
  65. threetears/agent/tools/utils.py +12 -0
  66. threetears/agent/tools/workflow.py +130 -0
@@ -0,0 +1,207 @@
1
+ Metadata-Version: 2.4
2
+ Name: 3tears-agent-tools
3
+ Version: 0.14.0
4
+ Summary: Tool framework for LLM agents -- routing, execution, and builtin tools
5
+ Project-URL: Repository, https://github.com/pacepace/3tears
6
+ Author: pace
7
+ License-Expression: MIT
8
+ License-File: LICENSE
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Framework :: AsyncIO
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
15
+ Classifier: Topic :: Software Development :: Libraries
16
+ Classifier: Typing :: Typed
17
+ Requires-Python: >=3.14
18
+ Requires-Dist: 3tears
19
+ Requires-Dist: 3tears-agent-audit
20
+ Requires-Dist: 3tears-agent-memory
21
+ Requires-Dist: 3tears-langgraph
22
+ Requires-Dist: 3tears-media-contracts
23
+ Requires-Dist: 3tears-nats
24
+ Requires-Dist: 3tears-observe
25
+ Requires-Dist: httpx
26
+ Requires-Dist: langchain-core
27
+ Provides-Extra: all
28
+ Requires-Dist: openpyxl; extra == 'all'
29
+ Requires-Dist: pdf2image; extra == 'all'
30
+ Requires-Dist: pillow; extra == 'all'
31
+ Requires-Dist: pint; extra == 'all'
32
+ Requires-Dist: pymupdf; extra == 'all'
33
+ Requires-Dist: pytesseract; extra == 'all'
34
+ Requires-Dist: python-docx; extra == 'all'
35
+ Requires-Dist: simpleeval; extra == 'all'
36
+ Requires-Dist: trafilatura; extra == 'all'
37
+ Provides-Extra: calculator
38
+ Requires-Dist: simpleeval; extra == 'calculator'
39
+ Provides-Extra: document
40
+ Requires-Dist: openpyxl; extra == 'document'
41
+ Requires-Dist: pymupdf; extra == 'document'
42
+ Requires-Dist: python-docx; extra == 'document'
43
+ Provides-Extra: docx
44
+ Requires-Dist: python-docx; extra == 'docx'
45
+ Provides-Extra: fetch
46
+ Requires-Dist: trafilatura; extra == 'fetch'
47
+ Provides-Extra: ocr
48
+ Requires-Dist: pdf2image; extra == 'ocr'
49
+ Requires-Dist: pytesseract; extra == 'ocr'
50
+ Provides-Extra: pdf
51
+ Requires-Dist: pymupdf; extra == 'pdf'
52
+ Provides-Extra: units
53
+ Requires-Dist: pint; extra == 'units'
54
+ Provides-Extra: vision
55
+ Requires-Dist: pillow; extra == 'vision'
56
+ Provides-Extra: xlsx
57
+ Requires-Dist: openpyxl; extra == 'xlsx'
58
+ Description-Content-Type: text/markdown
59
+
60
+ # 3tears Agent Tools
61
+
62
+ Tool framework for LLM agents. Provides tool routing, execution, context management, MCP integration, and a set of builtin tools.
63
+
64
+ Part of the [3tears](https://github.com/pacepace/3tears) framework.
65
+
66
+ ## ToolServer baseline audit
67
+
68
+ `ToolServer.handle_call` stamps every dispatch with a unified `AuditEvent` envelope (`event_type='tool.call'`) via `threetears.agent.audit.publish_audit`. The baseline emission fires in a `finally` block so success, failure (tool returned `success=False`), and error (tool raised) outcomes all produce a row. Identity axes carry from the active `ToolCallScope` (`actor_user_id`, `calling_agent_id`, `owner_agent_id`, `customer_id`, `correlation_id`); `resource_namespace_id` / `resource_namespace_type` stay `None` at the baseline layer since the tool resolves its target inside `execute`. Per-tool additive events (e.g. `workspace.fs_write`) still publish via `publish_audit` and ride alongside the baseline row under the same `correlation_id`. The `(correlation_id, event_type)` partial unique index on `platform_audit.audit_events` keeps them distinct. Emission is fire-and-forget: NATS publish failures log WARN and never taint the tool's response.
69
+
70
+ ## Tool-as-namespace emission
71
+
72
+ Tool namespace materialization is platform-owned. `ToolServer.publish_registration` writes the `RegistrationManifest` (carrying `pod_id` + `tools` + the `owner_agent_id` / `customer_id` envelope fields), and a platform-side namespace emitter subscribes to `{ns}.tools.register` and upserts one `platform.namespaces` row of type `tool` per tool. This is the sole writer in the platform.
73
+
74
+ Agent-spun ToolServers stamp `agent_id` + `customer_id` on the `RegistrationManifest` so the emitter lands rows with the right owner scope; platform-built-in pods (admin tool server, datasource tool pod) leave both `None` and the row lands with NULL owner columns (admitted under the widened `namespaces_row_scope_customer_ck` carve-out for `tool` type alongside `system` / `model`).
75
+
76
+ The canonical `name` shape is `tools.<sanitized-mcp>.<sanitized-version>` (per `build_namespace_name`); `metadata` carries the pre-sanitized natural-identity fields `mcp_name` / `mcp_version` / `pod_id` so downstream pattern matching (platform access materializer agent.yaml `access.tools` patterns + registry authorizer canonical-name lookup) does not need to reverse the sanitization rules. Deterministic `uuid5` derived from `(mcp_name, version, owner_agent_id_hex)` keeps concurrent emitters race-safe via `ON CONFLICT (id) DO UPDATE`.
77
+
78
+ The `ToolServer.register_tool` / `deregister_tool` helpers still emit through an injected `namespace_collection` for callers that wire one explicitly, but in production deployments the platform-side emitter is the source of truth and the in-process emission is redundant.
79
+
80
+ ## Installation
81
+
82
+ ```bash
83
+ pip install 3tears-agent-tools
84
+
85
+ # Optional extras for builtin tools
86
+ pip install "3tears-agent-tools[calculator]" # simpleeval
87
+ pip install "3tears-agent-tools[units]" # pint
88
+ pip install "3tears-agent-tools[fetch]" # trafilatura
89
+ pip install "3tears-agent-tools[document]" # PyMuPDF, python-docx, openpyxl
90
+ pip install "3tears-agent-tools[all]" # everything
91
+ ```
92
+
93
+ ## Components
94
+
95
+ ### ToolRouter
96
+
97
+ Routes user messages to the appropriate tool using a lightweight LLM call. Includes recall-intent detection to avoid re-invoking tools when users ask about previous results.
98
+
99
+ ```python
100
+ from threetears.agent.tools import ToolRouter, is_recall_intent
101
+
102
+ # Quick check -- no LLM call needed
103
+ if is_recall_intent("show me what the calculator said"):
104
+ # User wants to recall, not invoke
105
+
106
+ # Full routing with LLM
107
+ router = ToolRouter(chat_model)
108
+ decision = await router.route(user_message, tool_descriptions)
109
+ # decision.tool_name, decision.reasoning
110
+ ```
111
+
112
+ ### ToolExecutor
113
+
114
+ Invokes a tool-LLM: sends the user message to a secondary model configured for a specific task.
115
+
116
+ ```python
117
+ from threetears.agent.tools import ToolExecutor
118
+
119
+ executor = ToolExecutor()
120
+ result = await executor.invoke_with_tools(
121
+ chat_model=tool_model,
122
+ user_message="What is 42 * 17?",
123
+ tools=[calculator_tool],
124
+ tool_name="calculator",
125
+ )
126
+ # result.content, result.tool_calls
127
+ ```
128
+
129
+ ### ToolContextManager
130
+
131
+ Tracks tool invocations and results across a conversation for recall support.
132
+
133
+ ```python
134
+ from threetears.agent.tools import ToolContextManager
135
+
136
+ ctx = ToolContextManager()
137
+ await ctx.record_invocation("calculator", "42 * 17", "714")
138
+ await ctx.get_recall_context("calculator") # Returns formatted recall string
139
+ ```
140
+
141
+ ### McpClient
142
+
143
+ MCP (Model Context Protocol) integration for connecting to external tool servers.
144
+
145
+ ```python
146
+ from threetears.agent.tools import McpClient
147
+
148
+ async with McpClient(server_config) as client:
149
+ tools = await client.list_tools()
150
+ result = await client.invoke_tool("tool_name", {"param": "value"})
151
+ ```
152
+
153
+ ### Builtin Tools
154
+
155
+ Register all builtin tools at once:
156
+
157
+ ```python
158
+ from threetears.agent.tools import register_builtins, ToolRegistry
159
+
160
+ registry = ToolRegistry()
161
+ register_builtins(registry)
162
+ # Registers: calculator, unit_converter, dice_roller, date_time,
163
+ # random_number, web_fetch, text_transform, parse_document
164
+ ```
165
+
166
+ ### Todo Tools
167
+
168
+ Todo list management behind a storage protocol:
169
+
170
+ ```python
171
+ from threetears.agent.tools import TodoStorage, load_todo_tools_from_storage
172
+
173
+ class MyTodoStorage(TodoStorage):
174
+ async def add(self, conv_id, user_id, title, list_name, msg_id) -> dict: ...
175
+ async def list_all(self, conv_id) -> list[dict]: ...
176
+ # ... other methods
177
+
178
+ tools = load_todo_tools_from_storage(my_storage, snapshot_callback=on_snapshot)
179
+ ```
180
+
181
+ ### Protocols
182
+
183
+ For media-related capabilities, implement these protocols:
184
+
185
+ ```python
186
+ from threetears.agent.tools import (
187
+ ImageGenerationBackend,
188
+ MediaStorage,
189
+ VisionProvider,
190
+ TranscriptionProvider,
191
+ )
192
+ ```
193
+
194
+ ### Document Parsing
195
+
196
+ Parse PDF, DOCX, XLSX, and plain text with optional OCR:
197
+
198
+ ```python
199
+ from threetears.agent.tools import parse_document, OcrConfig
200
+
201
+ result = await parse_document(
202
+ file_bytes=data,
203
+ filename="report.pdf",
204
+ ocr_config=OcrConfig(enabled=True),
205
+ )
206
+ # result.sections -- list of DocumentSection with title, content, page numbers
207
+ ```
@@ -0,0 +1,66 @@
1
+ threetears/agent/tools/__init__.py,sha256=z9TTY8-lAB201cGvlRyp2zBAqdWxMJ-qItu2U_g44vQ,10373
2
+ threetears/agent/tools/_coercion.py,sha256=nXtjsmWP9vNJ4aI1GHwPe9kt__RfeFG5QkHgUIFECuE,3863
3
+ threetears/agent/tools/aliases.py,sha256=lCYhlZJUXRlqUHTWBprwI1QpJVxWcPkvJm29MN0RU7U,10622
4
+ threetears/agent/tools/base_tool.py,sha256=GgUQnTSTMMwcowWIjq46GlXWUwpHLNxeglBrBmd8HCs,8260
5
+ threetears/agent/tools/bootstrap.py,sha256=1LIV7PfrcUcESJWiCUYMjBPruR6u9cd2l4cYEpn3FM4,11166
6
+ threetears/agent/tools/bridge.py,sha256=el9iVwFymmCbY6N8aaupTMfSxRu5ZNWVKmFYbxfWbnc,4361
7
+ threetears/agent/tools/call_scope.py,sha256=VauOPb4bGC_rRtd48fsy4zYXjFJiATzZ4JmdZ4UnRdc,8517
8
+ threetears/agent/tools/chunker.py,sha256=0m8DHxAWxYEawEkhS5EDfOVKxGFyaqL60BMOg-OpVes,6793
9
+ threetears/agent/tools/collections.py,sha256=4qKaEtitF8nXxcp4RYkCrT93fkH03qIx-0BhdzTwXR8,20669
10
+ threetears/agent/tools/config.py,sha256=N2zQ1qDnDFtnRi59QJnK7neoKMXbbA9pbrh31Wv8oWU,3878
11
+ threetears/agent/tools/consume.py,sha256=EocFDA7mCJfgAbU6caiOc2TuwDz0cJ21CU5rsfXD1lM,10132
12
+ threetears/agent/tools/context.py,sha256=IV6NFfn4GI0nSZBQhaPrCyh-LzTSW8q2sSmmUoM_P40,34431
13
+ threetears/agent/tools/context_envelope.py,sha256=6ksSLp2WIibAFpzrBiJFfpxpR62OM28qYL4au0ITuhc,9228
14
+ threetears/agent/tools/deliver.py,sha256=13XBGLSyDKiaka1slXBLy7viJUWnMQw0CK0h1DBtd5M,8510
15
+ threetears/agent/tools/document.py,sha256=eo-4tka10bF14ofixS2T8ReICW4l8WxkD7kZxRkvBHE,32425
16
+ threetears/agent/tools/engagement.py,sha256=1Gvb3iMWVRqXrVArDvfNOO5bF1vE-XsUmZHAf4u59FI,6950
17
+ threetears/agent/tools/engagement_resolver.py,sha256=MefjDsmivT8msOXC6wAbOjO7iqCoEz-JT7n29H-2PME,12422
18
+ threetears/agent/tools/entities.py,sha256=zHVoyeXYLg_AP9hMN00JcPNbzHPdZMR-Y1uwPqKQJwQ,1788
19
+ threetears/agent/tools/events.py,sha256=hfhWW2G-V5MkCzPyyy4LV1nSPBb1gRMpxKBhQTaKQ0c,3070
20
+ threetears/agent/tools/executor.py,sha256=ZQyDlhA0TOt0xkv7ZI-f6VYV-_tB5uMhxJMeVhjzzpc,4405
21
+ threetears/agent/tools/graph_nodes.py,sha256=7zAFLYuR3M-oVLAMpU3Z9FcVOrlan02m8NdjWCBGUFY,8538
22
+ threetears/agent/tools/langchain_adapter.py,sha256=YRlPBex2HV1moYYAq8kDmMjLQkKhbuQp2fjl3v5bHoA,6228
23
+ threetears/agent/tools/mcp.py,sha256=bMoqh4gwasiWV07UWM4PlawpLA8ciAAAK2lkJOvt2ng,4507
24
+ threetears/agent/tools/object_resolver.py,sha256=vfIXsQCLHiC7l7edZ1o6a0jO91MOgqvonPw2LTLrRxk,10795
25
+ threetears/agent/tools/produce.py,sha256=pe3C9taoh-FHh55uWIXKIjHPeP17d7m3kRcrj11QafU,8435
26
+ threetears/agent/tools/protocols.py,sha256=HOWu74BuvRyWwHsYBYnSDy62SQXOpEhYgX8kYgJctL8,834
27
+ threetears/agent/tools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
+ threetears/agent/tools/registry.py,sha256=MuPn6UObIzqnaIzcwmnr3NB9RJuepfq4wzkyDmb3euU,2670
29
+ threetears/agent/tools/report.py,sha256=9GnrdizPw07WqxZcU_uW7i9BNstsb9IPv-VgZI_InXQ,21136
30
+ threetears/agent/tools/router.py,sha256=3rx_JU-WHZf8h0SHrA9TV2uZhZWfi_lnIKllM_JekSo,8054
31
+ threetears/agent/tools/serve.py,sha256=pSi5Nr7qChGaGwwdcF-YThPgCmTor3NtcQAAL_-eJsI,9054
32
+ threetears/agent/tools/server.py,sha256=HuQUbZ5xx5NB-dRIcSqqJNzPuy0KRkyv0IADNIqi9tU,96435
33
+ threetears/agent/tools/strategy.py,sha256=-jVguOQDBzHfrhc0EsPhBmnqoDxEZZl1VpkNZhV90sE,14719
34
+ threetears/agent/tools/todo.py,sha256=HiFuXw4niioYKq9Rtq1qJFest0miO6cTREIAElMuj84,10823
35
+ threetears/agent/tools/tool_llm_dispatch.py,sha256=ZvbAPHtcxMoHUaB7RXnRF_qC_NkhuuF5_ZPb0dlktcY,8965
36
+ threetears/agent/tools/types.py,sha256=wiaJpmY3fZQH7lkrAY8n8ncbr8cHQUKXr6nZDAqXT8Y,457
37
+ threetears/agent/tools/utils.py,sha256=Ata2Tli2vflXtfArLuG-DzBTNRuc2ZprBkoowksZBds,295
38
+ threetears/agent/tools/workflow.py,sha256=q7fVPxWlO0Istg6bjwuo2rIPq6-KSe9DND4GrVc-TSA,4699
39
+ threetears/agent/tools/builtin/__init__.py,sha256=8zeSdSbnlS_CnTiHCZepkzRGUzKGkl_9fsi8Yhz_ggM,6230
40
+ threetears/agent/tools/builtin/analyze_media.py,sha256=UXeUZp3PpxFvUVDR3_PWxEGWlrh_Q_-wdxxTfsaio4c,27556
41
+ threetears/agent/tools/builtin/calculator.py,sha256=UoUiYD90vt2lM3IsVpeo_4WQplGHzWtQZ0C5BFeqlno,6567
42
+ threetears/agent/tools/builtin/context_recall.py,sha256=sMMCzjVhW6LuSt8SzzyQ6pENaE--hIjchHUfZfDTN1k,6066
43
+ threetears/agent/tools/builtin/current_date.py,sha256=Hl2aW_p2vS6rnFreu_lhUeYxqWwzZH2Zcd_EhdcNFo0,5735
44
+ threetears/agent/tools/builtin/dictionary.py,sha256=P9KrTuhVUO6v3c3vWyou8CoJlCfwJRuzSQNIrMnZeGM,5628
45
+ threetears/agent/tools/builtin/image_generation.py,sha256=pyZjVKPz-SHFH7ZCHfINHjNTj1I7e_LcXPRiEJhibbc,11397
46
+ threetears/agent/tools/builtin/image_prep.py,sha256=qn3f-Ky7MNktVnXq_Lv3m9DcZLLQ18uDvjH8J6A4CIo,7013
47
+ threetears/agent/tools/builtin/timezone_converter.py,sha256=ylTUSs5PHbz0Veea_X6CnXI3t95PW5eYyjxOZ5llrwQ,6444
48
+ threetears/agent/tools/builtin/unit_converter.py,sha256=7aRGm0opsnFMHEE3OtZBWs18BOK8v7sECzDthBAraO0,4530
49
+ threetears/agent/tools/builtin/web_fetch.py,sha256=hH8UEHirtB1nCcYYGvUFSS3h7SIJyxjoBMynCbw7wyY,7796
50
+ threetears/agent/tools/builtin/web_search.py,sha256=BIN_6WWwOOMGSuwyU_wUXc6OMF-kA1qrZqn589ZacKg,4862
51
+ threetears/agent/tools/migrations/__init__.py,sha256=_Hpb6V6jABgT_Vh3edu0C45tUjafcxFbl1_5MXdsCfg,3482
52
+ threetears/agent/tools/migrations/v001_create_context_items_table.py,sha256=BeI3A-wWQblHBSl0s_ryPxgj6EYYb5TEWlEpeEZUFQk,1923
53
+ threetears/agent/tools/migrations/v002_datetime_to_datetimetz.py,sha256=K-kj6H61fbB0SeyKnfiNw7SzXaZ4gVlleIU0YGtFh5U,5184
54
+ threetears/agent/tools/migrations/v003_align_context_items_shape.py,sha256=IvC0zPQC-nZ8Rtp4OE6tvZk40rnuSh3m18anT-yXWGU,7188
55
+ threetears/agent/tools/migrations/v004_tool_result_dedup_index.py,sha256=AoJ7Scy8PvOeHNoITwByB6QApbUZ_uBGcm_l2HEjd1w,2534
56
+ threetears/agent/tools/platform_migrations/__init__.py,sha256=WsXVhpWPLEEyn1TICYhgNyMTm_lGMCN8BztLNWaapUM,2893
57
+ threetears/agent/tools/platform_migrations/v001_add_tool_eligibility_columns.py,sha256=cRPfxB1A5RGCJjg5S9SFnK0YtjkQmHOV2jUjC7PTAFs,2384
58
+ threetears/agent/tools/reports/__init__.py,sha256=myR_uloZkUSbVA-vJzJLrxc9wV9u1fgwPNZ0ZRZJq68,761
59
+ threetears/agent/tools/reports/markdown_compiler.py,sha256=9evSspQ4pxSCVRxTVV2iudzBy8pH1BtFFQaisoZe4MQ,9352
60
+ threetears/agent/tools/reports/mermaid_generator.py,sha256=rao_f423W90kcfQCs6nWmm1LQRwfKBYNxdtM4UyeS2Y,6803
61
+ threetears/agent/tools/reports/pdf_renderer.py,sha256=7CltWlimHyavDtxnjD-rFPZONk9GBS2wGIlMvpOsZRA,4655
62
+ threetears/agent/tools/reports/severity.py,sha256=cK5XRXTgo1M9M31Q7jqIOwJ0UJfz3dT-M3qv-VJkqCY,2614
63
+ 3tears_agent_tools-0.14.0.dist-info/METADATA,sha256=5mUBkjY1_h_coWqtl6s-en-j9m1CYN_OMNCpSrJ1IlU,8290
64
+ 3tears_agent_tools-0.14.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
65
+ 3tears_agent_tools-0.14.0.dist-info/licenses/LICENSE,sha256=7GWEoEOcFJenZLt4LDzqH2K7QLxo_2m8rzG7Vv8VGXo,1066
66
+ 3tears_agent_tools-0.14.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Mark Pace
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,225 @@
1
+ """3tears agent-tools: tool registry, context management, and built-in tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ # Version derived from pyproject.toml so the metadata is the single
6
+ # source of truth -- a future release that bumps pyproject without
7
+ # updating ``__init__.py`` can't drift the runtime ``__version__``.
8
+ # The except guard handles the rare case where the package isn't
9
+ # installed via importlib.metadata (e.g. running directly from a
10
+ # checked-out source tree without ``uv sync``); the fallback keeps
11
+ # imports working but reports ``unknown`` rather than crashing.
12
+ from importlib.metadata import PackageNotFoundError as _PackageNotFoundError
13
+ from importlib.metadata import version as _version
14
+ from typing import TYPE_CHECKING
15
+
16
+ try:
17
+ __version__ = _version("3tears-agent-tools")
18
+ except _PackageNotFoundError: # pragma: no cover - dev fallback
19
+ __version__ = "unknown"
20
+
21
+ # lazy public API (PEP 562). the package namespace no longer imports its
22
+ # implementation modules eagerly: importing this package (or any of its
23
+ # submodules) costs only this file, and each public attribute resolves
24
+ # its defining module on first access. the TYPE_CHECKING block carries
25
+ # the real imports so mypy and IDEs see the full statically-typed API;
26
+ # the _LAZY map is the runtime equivalent. the three-way agreement
27
+ # between __all__, _LAZY, and the TYPE_CHECKING block is pinned by the
28
+ # package's lazy-surface consistency test.
29
+ # decision record: docs/separate-concerns-decisions.md (hand-rolled
30
+ # PEP 562 over lazy_loader -- zero added runtime deps, no stub drift).
31
+ if TYPE_CHECKING:
32
+ from threetears.agent.tools.builtin import register_builtins
33
+ from threetears.agent.tools.builtin.analyze_media import AnalyzerConfig, create_analyze_media_tool
34
+ from threetears.agent.tools.builtin.image_generation import (
35
+ ImageGenerationContext,
36
+ ImageGenerationInput,
37
+ create_image_generation_tool,
38
+ )
39
+ from threetears.agent.tools.chunker import (
40
+ ChunkResult,
41
+ ChunkStrategy,
42
+ chunk_by_headers,
43
+ chunk_by_lines,
44
+ chunk_by_sections,
45
+ chunk_content,
46
+ register_chunk_strategy,
47
+ )
48
+ from threetears.agent.tools.collections import (
49
+ ContextItemCollection,
50
+ context_items_table,
51
+ migrate_context_items_schema,
52
+ )
53
+ from threetears.agent.tools.context import ToolContextManager
54
+ from threetears.agent.tools.document import (
55
+ DocumentResult,
56
+ DocumentSection,
57
+ OcrConfig,
58
+ ParseDocumentInput,
59
+ create_parse_document_tool,
60
+ detect_mime_from_filename,
61
+ parse_document,
62
+ )
63
+ from threetears.agent.tools.entities import ContextItemEntity
64
+ from threetears.agent.tools.events import TodosChangedEvent
65
+ from threetears.agent.tools.executor import ToolExecutionResult, ToolExecutor
66
+ from threetears.agent.tools.mcp import McpClient, McpTool, McpToolResult
67
+ from threetears.agent.tools.protocols import (
68
+ GeneratedImage,
69
+ ImageGenerationBackend,
70
+ MediaInfo,
71
+ MediaStorage,
72
+ TextProvider,
73
+ TranscriptionProvider,
74
+ VisionProvider,
75
+ )
76
+ from threetears.agent.tools.registry import ToolRegistry
77
+ from threetears.agent.tools.router import DEFAULT_ROUTING_PROMPT, ToolRouter, ToolRoutingDecision, is_recall_intent
78
+ from threetears.agent.tools.todo import TodoStorage, load_todo_tools as load_todo_tools_from_storage
79
+ from threetears.agent.tools.tool_llm_dispatch import (
80
+ InvokeToolLlmInput,
81
+ ToolLlmInvocation,
82
+ ToolLlmResolver,
83
+ load_tool_llm_dispatch,
84
+ )
85
+ from threetears.agent.tools.types import ChatModelFactory
86
+ from threetears.agent.tools.workflow import load_workflow_tools
87
+
88
+ # public attribute -> (defining module, attribute name in that module)
89
+ _LAZY: dict[str, tuple[str, str]] = {
90
+ "AnalyzerConfig": ("threetears.agent.tools.builtin.analyze_media", "AnalyzerConfig"),
91
+ "ChatModelFactory": ("threetears.agent.tools.types", "ChatModelFactory"),
92
+ "ChunkResult": ("threetears.agent.tools.chunker", "ChunkResult"),
93
+ "ChunkStrategy": ("threetears.agent.tools.chunker", "ChunkStrategy"),
94
+ "ContextItemCollection": ("threetears.agent.tools.collections", "ContextItemCollection"),
95
+ "ContextItemEntity": ("threetears.agent.tools.entities", "ContextItemEntity"),
96
+ "DEFAULT_ROUTING_PROMPT": ("threetears.agent.tools.router", "DEFAULT_ROUTING_PROMPT"),
97
+ "DocumentResult": ("threetears.agent.tools.document", "DocumentResult"),
98
+ "DocumentSection": ("threetears.agent.tools.document", "DocumentSection"),
99
+ "GeneratedImage": ("threetears.agent.tools.protocols", "GeneratedImage"),
100
+ "ImageGenerationBackend": ("threetears.agent.tools.protocols", "ImageGenerationBackend"),
101
+ "ImageGenerationContext": ("threetears.agent.tools.builtin.image_generation", "ImageGenerationContext"),
102
+ "ImageGenerationInput": ("threetears.agent.tools.builtin.image_generation", "ImageGenerationInput"),
103
+ "InvokeToolLlmInput": ("threetears.agent.tools.tool_llm_dispatch", "InvokeToolLlmInput"),
104
+ "McpClient": ("threetears.agent.tools.mcp", "McpClient"),
105
+ "McpTool": ("threetears.agent.tools.mcp", "McpTool"),
106
+ "McpToolResult": ("threetears.agent.tools.mcp", "McpToolResult"),
107
+ "MediaInfo": ("threetears.agent.tools.protocols", "MediaInfo"),
108
+ "MediaStorage": ("threetears.agent.tools.protocols", "MediaStorage"),
109
+ "OcrConfig": ("threetears.agent.tools.document", "OcrConfig"),
110
+ "ParseDocumentInput": ("threetears.agent.tools.document", "ParseDocumentInput"),
111
+ "TextProvider": ("threetears.agent.tools.protocols", "TextProvider"),
112
+ "TodoStorage": ("threetears.agent.tools.todo", "TodoStorage"),
113
+ "TodosChangedEvent": ("threetears.agent.tools.events", "TodosChangedEvent"),
114
+ "ToolContextManager": ("threetears.agent.tools.context", "ToolContextManager"),
115
+ "ToolExecutionResult": ("threetears.agent.tools.executor", "ToolExecutionResult"),
116
+ "ToolExecutor": ("threetears.agent.tools.executor", "ToolExecutor"),
117
+ "ToolLlmInvocation": ("threetears.agent.tools.tool_llm_dispatch", "ToolLlmInvocation"),
118
+ "ToolLlmResolver": ("threetears.agent.tools.tool_llm_dispatch", "ToolLlmResolver"),
119
+ "ToolRegistry": ("threetears.agent.tools.registry", "ToolRegistry"),
120
+ "ToolRouter": ("threetears.agent.tools.router", "ToolRouter"),
121
+ "ToolRoutingDecision": ("threetears.agent.tools.router", "ToolRoutingDecision"),
122
+ "TranscriptionProvider": ("threetears.agent.tools.protocols", "TranscriptionProvider"),
123
+ "VisionProvider": ("threetears.agent.tools.protocols", "VisionProvider"),
124
+ "chunk_by_headers": ("threetears.agent.tools.chunker", "chunk_by_headers"),
125
+ "chunk_by_lines": ("threetears.agent.tools.chunker", "chunk_by_lines"),
126
+ "chunk_by_sections": ("threetears.agent.tools.chunker", "chunk_by_sections"),
127
+ "chunk_content": ("threetears.agent.tools.chunker", "chunk_content"),
128
+ "context_items_table": ("threetears.agent.tools.collections", "context_items_table"),
129
+ "create_analyze_media_tool": ("threetears.agent.tools.builtin.analyze_media", "create_analyze_media_tool"),
130
+ "create_image_generation_tool": ("threetears.agent.tools.builtin.image_generation", "create_image_generation_tool"),
131
+ "create_parse_document_tool": ("threetears.agent.tools.document", "create_parse_document_tool"),
132
+ "detect_mime_from_filename": ("threetears.agent.tools.document", "detect_mime_from_filename"),
133
+ "is_recall_intent": ("threetears.agent.tools.router", "is_recall_intent"),
134
+ "load_todo_tools_from_storage": ("threetears.agent.tools.todo", "load_todo_tools"),
135
+ "load_tool_llm_dispatch": ("threetears.agent.tools.tool_llm_dispatch", "load_tool_llm_dispatch"),
136
+ "load_workflow_tools": ("threetears.agent.tools.workflow", "load_workflow_tools"),
137
+ "migrate_context_items_schema": ("threetears.agent.tools.collections", "migrate_context_items_schema"),
138
+ "parse_document": ("threetears.agent.tools.document", "parse_document"),
139
+ "register_builtins": ("threetears.agent.tools.builtin", "register_builtins"),
140
+ "register_chunk_strategy": ("threetears.agent.tools.chunker", "register_chunk_strategy"),
141
+ }
142
+
143
+ __all__ = [
144
+ "AnalyzerConfig",
145
+ "ChunkResult",
146
+ "ChunkStrategy",
147
+ "ChatModelFactory",
148
+ "ContextItemCollection",
149
+ "ContextItemEntity",
150
+ "context_items_table",
151
+ "migrate_context_items_schema",
152
+ "DEFAULT_ROUTING_PROMPT",
153
+ "DocumentResult",
154
+ "DocumentSection",
155
+ "GeneratedImage",
156
+ "ImageGenerationBackend",
157
+ "ImageGenerationContext",
158
+ "ImageGenerationInput",
159
+ "InvokeToolLlmInput",
160
+ "McpClient",
161
+ "McpTool",
162
+ "McpToolResult",
163
+ "MediaInfo",
164
+ "MediaStorage",
165
+ "OcrConfig",
166
+ "TextProvider",
167
+ "ParseDocumentInput",
168
+ "ToolContextManager",
169
+ "TodoStorage",
170
+ "TodosChangedEvent",
171
+ "ToolExecutionResult",
172
+ "ToolExecutor",
173
+ "ToolLlmInvocation",
174
+ "ToolLlmResolver",
175
+ "ToolRegistry",
176
+ "ToolRouter",
177
+ "ToolRoutingDecision",
178
+ "TranscriptionProvider",
179
+ "VisionProvider",
180
+ "chunk_by_headers",
181
+ "chunk_by_lines",
182
+ "chunk_by_sections",
183
+ "chunk_content",
184
+ "create_analyze_media_tool",
185
+ "create_image_generation_tool",
186
+ "create_parse_document_tool",
187
+ "detect_mime_from_filename",
188
+ "is_recall_intent",
189
+ "load_tool_llm_dispatch",
190
+ "load_todo_tools_from_storage",
191
+ "load_workflow_tools",
192
+ "parse_document",
193
+ "register_builtins",
194
+ "register_chunk_strategy",
195
+ ]
196
+
197
+
198
+ def __getattr__(name: str) -> object:
199
+ """resolve a public attribute from its defining module on first access.
200
+
201
+ :param name: attribute name being resolved
202
+ :ptype name: str
203
+ :return: the resolved attribute (also cached in module globals so
204
+ ``__getattr__`` fires at most once per name)
205
+ :rtype: object
206
+ :raises AttributeError: when ``name`` is not part of the public API
207
+ """
208
+ entry = _LAZY.get(name)
209
+ if entry is None:
210
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
211
+ from importlib import import_module
212
+
213
+ module_name, attr = entry
214
+ value: object = getattr(import_module(module_name), attr)
215
+ globals()[name] = value
216
+ return value
217
+
218
+
219
+ def __dir__() -> list[str]:
220
+ """include lazy attributes in ``dir()`` output.
221
+
222
+ :return: sorted union of materialized globals and lazy names
223
+ :rtype: list[str]
224
+ """
225
+ return sorted(set(globals()) | set(_LAZY))
@@ -0,0 +1,102 @@
1
+ """input-value coercion helpers for TearsTool subclasses.
2
+
3
+ LLMs routinely send loose values for object/array tool parameters:
4
+ empty strings where a dict is expected, JSON-encoded strings where
5
+ a dict/list is expected, arguments omitted entirely. this module
6
+ provides pure functions that rewrite those loose values toward the
7
+ declared JSON schema type before dispatch, so subclasses receive
8
+ the native Python container they expect.
9
+
10
+ the coercion engages only for fields whose declared type is
11
+ ``object`` or ``array``. every other field passes through
12
+ untouched. explicit ``None`` is preserved as a "not provided"
13
+ sentinel so required-field checks in subclasses still fire.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import json
19
+ from typing import Any
20
+
21
+ __all__ = [
22
+ "coerce_value",
23
+ "normalize_kwargs",
24
+ ]
25
+
26
+
27
+ def coerce_value(value: Any, declared_type: str | None) -> Any:
28
+ """coerce single MCP input value toward its declared JSON schema type.
29
+
30
+ handles two LLM mistakes: empty string supplied for complex type
31
+ (becomes empty container), and JSON-encoded string supplied where
32
+ native dict/list is expected (gets decoded). values already of
33
+ correct type, or values that cannot be safely coerced, pass
34
+ through unchanged.
35
+
36
+ :param value: raw value supplied by caller
37
+ :ptype value: Any
38
+ :param declared_type: JSON schema ``type`` for this parameter or None
39
+ :ptype declared_type: str | None
40
+ :return: value coerced to declared type when possible, else original
41
+ :rtype: Any
42
+ """
43
+ result = value
44
+ # only empty *strings* are treated as "give me the empty container" --
45
+ # literal 0, False, or empty tuple must pass through so type-strict
46
+ # subclasses still see the original value (and can reject it).
47
+ if declared_type == "object" and not isinstance(value, dict):
48
+ if value == "":
49
+ result = {}
50
+ elif isinstance(value, str):
51
+ try:
52
+ parsed = json.loads(value)
53
+ except TypeError, ValueError:
54
+ parsed = None
55
+ if isinstance(parsed, dict):
56
+ result = parsed
57
+ elif declared_type == "array" and not isinstance(value, list):
58
+ if value == "":
59
+ result = []
60
+ elif isinstance(value, str):
61
+ try:
62
+ parsed = json.loads(value)
63
+ except TypeError, ValueError:
64
+ parsed = None
65
+ if isinstance(parsed, list):
66
+ result = parsed
67
+ return result
68
+
69
+
70
+ def normalize_kwargs(
71
+ kwargs: dict[str, Any],
72
+ input_schema: dict[str, Any],
73
+ ) -> dict[str, Any]:
74
+ """coerce loose LLM-supplied kwargs to match declared schema types.
75
+
76
+ inspects ``input_schema['properties']`` and rewrites entries in
77
+ kwargs whose declared type is ``object`` or ``array`` when the
78
+ supplied value is a wrong-shape loose container (empty string
79
+ or JSON-encoded string). values matching their declared type
80
+ are passed through untouched. keys not present in the schema
81
+ are passed through untouched. explicit ``None`` is preserved
82
+ so per-action required-field checks still function.
83
+
84
+ :param kwargs: raw tool input parameters as supplied by caller
85
+ :ptype kwargs: dict[str, Any]
86
+ :param input_schema: tool's JSON schema for input parameters
87
+ :ptype input_schema: dict[str, Any]
88
+ :return: parameters with wrong-type values coerced where possible
89
+ :rtype: dict[str, Any]
90
+ """
91
+ properties = input_schema.get("properties", {})
92
+ if not properties:
93
+ return dict(kwargs)
94
+ normalized: dict[str, Any] = {}
95
+ for key, value in kwargs.items():
96
+ prop = properties.get(key)
97
+ if prop is None or value is None:
98
+ normalized[key] = value
99
+ continue
100
+ declared_type = prop.get("type")
101
+ normalized[key] = coerce_value(value, declared_type)
102
+ return normalized