tau-coding-agent 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.
Files changed (283) hide show
  1. tau/__init__.py +0 -0
  2. tau/agent/__init__.py +11 -0
  3. tau/agent/prompt/__init__.py +10 -0
  4. tau/agent/prompt/builder.py +302 -0
  5. tau/agent/prompt/types.py +33 -0
  6. tau/agent/service.py +369 -0
  7. tau/agent/types.py +61 -0
  8. tau/auth/manager.py +247 -0
  9. tau/auth/storage.py +82 -0
  10. tau/auth/types.py +41 -0
  11. tau/builtins/__init__.py +4 -0
  12. tau/builtins/__pycache__/__init__.cpython-313.pyc +0 -0
  13. tau/builtins/__pycache__/__init__.cpython-314.pyc +0 -0
  14. tau/builtins/commands/__init__.py +41 -0
  15. tau/builtins/commands/__pycache__/__init__.cpython-313.pyc +0 -0
  16. tau/builtins/commands/__pycache__/__init__.cpython-314.pyc +0 -0
  17. tau/builtins/commands/__pycache__/clear.cpython-313.pyc +0 -0
  18. tau/builtins/commands/__pycache__/clear.cpython-314.pyc +0 -0
  19. tau/builtins/commands/__pycache__/compact.cpython-313.pyc +0 -0
  20. tau/builtins/commands/__pycache__/compact.cpython-314.pyc +0 -0
  21. tau/builtins/commands/__pycache__/reload.cpython-313.pyc +0 -0
  22. tau/builtins/commands/__pycache__/reload.cpython-314.pyc +0 -0
  23. tau/builtins/commands/__pycache__/session.cpython-313.pyc +0 -0
  24. tau/builtins/commands/__pycache__/session.cpython-314.pyc +0 -0
  25. tau/builtins/commands/clear.py +16 -0
  26. tau/builtins/commands/compact.py +28 -0
  27. tau/builtins/commands/reload.py +27 -0
  28. tau/builtins/commands/session.py +19 -0
  29. tau/builtins/extensions/footer/__init__.py +76 -0
  30. tau/builtins/extensions/footer/__pycache__/__init__.cpython-313.pyc +0 -0
  31. tau/builtins/extensions/footer/__pycache__/git.cpython-313.pyc +0 -0
  32. tau/builtins/extensions/footer/__pycache__/model.cpython-313.pyc +0 -0
  33. tau/builtins/extensions/footer/__pycache__/utils.cpython-313.pyc +0 -0
  34. tau/builtins/extensions/footer/git.py +26 -0
  35. tau/builtins/extensions/footer/model.py +69 -0
  36. tau/builtins/extensions/footer/utils.py +44 -0
  37. tau/builtins/extensions/header/__init__.py +18 -0
  38. tau/builtins/extensions/header/__pycache__/__init__.cpython-313.pyc +0 -0
  39. tau/builtins/models/__init__.py +0 -0
  40. tau/builtins/models/__pycache__/__init__.cpython-313.pyc +0 -0
  41. tau/builtins/models/__pycache__/text.cpython-313.pyc +0 -0
  42. tau/builtins/models/audio.py +43 -0
  43. tau/builtins/models/image.py +43 -0
  44. tau/builtins/models/text.py +482 -0
  45. tau/builtins/models/video.py +40 -0
  46. tau/builtins/prompts/commit.md +7 -0
  47. tau/builtins/prompts/docs.md +7 -0
  48. tau/builtins/prompts/explain.md +7 -0
  49. tau/builtins/prompts/fix.md +7 -0
  50. tau/builtins/prompts/refactor.md +7 -0
  51. tau/builtins/prompts/review.md +7 -0
  52. tau/builtins/prompts/test.md +7 -0
  53. tau/builtins/providers/__init__.py +0 -0
  54. tau/builtins/providers/__pycache__/__init__.cpython-313.pyc +0 -0
  55. tau/builtins/providers/__pycache__/text.cpython-313.pyc +0 -0
  56. tau/builtins/providers/audio.py +10 -0
  57. tau/builtins/providers/image.py +9 -0
  58. tau/builtins/providers/text.py +33 -0
  59. tau/builtins/providers/video.py +6 -0
  60. tau/builtins/skills/code-review/SKILL.md +4 -0
  61. tau/builtins/skills/debug/SKILL.md +4 -0
  62. tau/builtins/skills/git-commit/SKILL.md +4 -0
  63. tau/builtins/themes/dark.yaml +1 -0
  64. tau/builtins/themes/light.yaml +46 -0
  65. tau/builtins/tools/__init__.py +73 -0
  66. tau/builtins/tools/__pycache__/__init__.cpython-313.pyc +0 -0
  67. tau/builtins/tools/__pycache__/__init__.cpython-314.pyc +0 -0
  68. tau/builtins/tools/__pycache__/bash.cpython-313.pyc +0 -0
  69. tau/builtins/tools/__pycache__/bash.cpython-314.pyc +0 -0
  70. tau/builtins/tools/__pycache__/edit.cpython-313.pyc +0 -0
  71. tau/builtins/tools/__pycache__/edit.cpython-314.pyc +0 -0
  72. tau/builtins/tools/__pycache__/glob.cpython-313.pyc +0 -0
  73. tau/builtins/tools/__pycache__/glob.cpython-314.pyc +0 -0
  74. tau/builtins/tools/__pycache__/grep.cpython-313.pyc +0 -0
  75. tau/builtins/tools/__pycache__/grep.cpython-314.pyc +0 -0
  76. tau/builtins/tools/__pycache__/ls.cpython-313.pyc +0 -0
  77. tau/builtins/tools/__pycache__/ls.cpython-314.pyc +0 -0
  78. tau/builtins/tools/__pycache__/read.cpython-313.pyc +0 -0
  79. tau/builtins/tools/__pycache__/read.cpython-314.pyc +0 -0
  80. tau/builtins/tools/__pycache__/terminal.cpython-313.pyc +0 -0
  81. tau/builtins/tools/__pycache__/terminal.cpython-314.pyc +0 -0
  82. tau/builtins/tools/__pycache__/write.cpython-313.pyc +0 -0
  83. tau/builtins/tools/__pycache__/write.cpython-314.pyc +0 -0
  84. tau/builtins/tools/edit.py +215 -0
  85. tau/builtins/tools/glob.py +112 -0
  86. tau/builtins/tools/grep.py +146 -0
  87. tau/builtins/tools/ls.py +135 -0
  88. tau/builtins/tools/read.py +122 -0
  89. tau/builtins/tools/terminal.py +150 -0
  90. tau/builtins/tools/write.py +105 -0
  91. tau/commands/__init__.py +10 -0
  92. tau/commands/registry.py +71 -0
  93. tau/commands/types.py +33 -0
  94. tau/console/__init__.py +0 -0
  95. tau/console/cli.py +266 -0
  96. tau/console/commands/__init__.py +0 -0
  97. tau/console/commands/auth.py +193 -0
  98. tau/console/commands/packages.py +104 -0
  99. tau/console/commands/update.py +76 -0
  100. tau/core/__init__.py +0 -0
  101. tau/core/registry.py +102 -0
  102. tau/engine/__init__.py +47 -0
  103. tau/engine/service.py +768 -0
  104. tau/engine/types.py +163 -0
  105. tau/extensions/__init__.py +28 -0
  106. tau/extensions/api.py +928 -0
  107. tau/extensions/context.py +462 -0
  108. tau/extensions/events.py +70 -0
  109. tau/extensions/loader.py +386 -0
  110. tau/extensions/runtime.py +184 -0
  111. tau/extensions/settings.py +137 -0
  112. tau/hooks/__init__.py +112 -0
  113. tau/hooks/engine.py +237 -0
  114. tau/hooks/inference.py +21 -0
  115. tau/hooks/runtime.py +126 -0
  116. tau/hooks/service.py +121 -0
  117. tau/hooks/session.py +117 -0
  118. tau/hooks/tui.py +61 -0
  119. tau/hooks/types.py +72 -0
  120. tau/inference/__init__.py +180 -0
  121. tau/inference/api/__init__.py +0 -0
  122. tau/inference/api/audio/__init__.py +0 -0
  123. tau/inference/api/audio/base.py +29 -0
  124. tau/inference/api/audio/builtins.py +15 -0
  125. tau/inference/api/audio/elevenlabs_audio.py +183 -0
  126. tau/inference/api/audio/gemini_audio.py +95 -0
  127. tau/inference/api/audio/openai_audio.py +159 -0
  128. tau/inference/api/audio/registry.py +15 -0
  129. tau/inference/api/audio/sarvam_audio.py +163 -0
  130. tau/inference/api/audio/service.py +103 -0
  131. tau/inference/api/audio/utils.py +47 -0
  132. tau/inference/api/image/__init__.py +0 -0
  133. tau/inference/api/image/base.py +17 -0
  134. tau/inference/api/image/builtins.py +8 -0
  135. tau/inference/api/image/gemini_image.py +77 -0
  136. tau/inference/api/image/openai_image.py +103 -0
  137. tau/inference/api/image/openrouter.py +144 -0
  138. tau/inference/api/image/registry.py +15 -0
  139. tau/inference/api/image/service.py +71 -0
  140. tau/inference/api/registry.py +82 -0
  141. tau/inference/api/text/__init__.py +0 -0
  142. tau/inference/api/text/anthropic_claude_code.py +222 -0
  143. tau/inference/api/text/anthropic_messages.py +196 -0
  144. tau/inference/api/text/base.py +40 -0
  145. tau/inference/api/text/builtins.py +19 -0
  146. tau/inference/api/text/gemini_generate.py +234 -0
  147. tau/inference/api/text/github_copilot_chat.py +172 -0
  148. tau/inference/api/text/google_antigravity.py +522 -0
  149. tau/inference/api/text/mistral_chat.py +284 -0
  150. tau/inference/api/text/ollama_chat.py +200 -0
  151. tau/inference/api/text/openai_codex_responses.py +497 -0
  152. tau/inference/api/text/openai_completions.py +227 -0
  153. tau/inference/api/text/openai_responses.py +235 -0
  154. tau/inference/api/text/registry.py +50 -0
  155. tau/inference/api/text/service.py +297 -0
  156. tau/inference/api/text/types.py +7 -0
  157. tau/inference/api/text/utils.py +228 -0
  158. tau/inference/api/video/__init__.py +0 -0
  159. tau/inference/api/video/base.py +26 -0
  160. tau/inference/api/video/builtins.py +7 -0
  161. tau/inference/api/video/fal_video.py +119 -0
  162. tau/inference/api/video/openrouter_video.py +142 -0
  163. tau/inference/api/video/registry.py +15 -0
  164. tau/inference/api/video/service.py +72 -0
  165. tau/inference/model/__init__.py +0 -0
  166. tau/inference/model/registry.py +102 -0
  167. tau/inference/model/types.py +65 -0
  168. tau/inference/provider/__init__.py +0 -0
  169. tau/inference/provider/oauth/__init__.py +35 -0
  170. tau/inference/provider/oauth/anthropic_claude_code.py +286 -0
  171. tau/inference/provider/oauth/github_copilot.py +333 -0
  172. tau/inference/provider/oauth/google_antigravity.py +258 -0
  173. tau/inference/provider/oauth/openai_codex.py +309 -0
  174. tau/inference/provider/oauth/pkce.py +14 -0
  175. tau/inference/provider/oauth/types.py +46 -0
  176. tau/inference/provider/oauth/utils.py +154 -0
  177. tau/inference/provider/registry.py +141 -0
  178. tau/inference/provider/types.py +114 -0
  179. tau/inference/types.py +549 -0
  180. tau/inference/utils.py +219 -0
  181. tau/message/__init__.py +0 -0
  182. tau/message/types.py +482 -0
  183. tau/message/utils.py +178 -0
  184. tau/packages/__init__.py +11 -0
  185. tau/packages/manager.py +190 -0
  186. tau/packages/types.py +20 -0
  187. tau/packages/utils.py +67 -0
  188. tau/prompts/expand.py +58 -0
  189. tau/prompts/loader.py +69 -0
  190. tau/prompts/registry.py +45 -0
  191. tau/prompts/types.py +24 -0
  192. tau/rpc/__init__.py +8 -0
  193. tau/rpc/mode.py +783 -0
  194. tau/rpc/types.py +252 -0
  195. tau/runtime/service.py +759 -0
  196. tau/runtime/types.py +303 -0
  197. tau/session/branch_summarization.py +312 -0
  198. tau/session/compaction.py +646 -0
  199. tau/session/manager.py +652 -0
  200. tau/session/types.py +188 -0
  201. tau/session/utils.py +233 -0
  202. tau/settings/manager.py +1077 -0
  203. tau/settings/paths.py +150 -0
  204. tau/settings/storage.py +63 -0
  205. tau/settings/types.py +173 -0
  206. tau/settings/utils.py +25 -0
  207. tau/skills/loader.py +91 -0
  208. tau/skills/registry.py +70 -0
  209. tau/skills/types.py +25 -0
  210. tau/themes/loader.py +238 -0
  211. tau/themes/registry.py +108 -0
  212. tau/themes/types.py +19 -0
  213. tau/tool/__init__.py +3 -0
  214. tau/tool/registry.py +117 -0
  215. tau/tool/render.py +21 -0
  216. tau/tool/types.py +244 -0
  217. tau/trust/__init__.py +13 -0
  218. tau/trust/manager.py +80 -0
  219. tau/trust/types.py +14 -0
  220. tau/trust/utils.py +72 -0
  221. tau/tui/__init__.py +54 -0
  222. tau/tui/agent_hooks.py +346 -0
  223. tau/tui/ansi.py +330 -0
  224. tau/tui/app.py +540 -0
  225. tau/tui/autocomplete.py +33 -0
  226. tau/tui/capabilities.py +119 -0
  227. tau/tui/commands/__init__.py +3 -0
  228. tau/tui/commands/appearance.py +498 -0
  229. tau/tui/commands/auth.py +232 -0
  230. tau/tui/commands/context.py +38 -0
  231. tau/tui/commands/misc.py +82 -0
  232. tau/tui/commands/model.py +118 -0
  233. tau/tui/commands/session.py +464 -0
  234. tau/tui/component.py +268 -0
  235. tau/tui/components/__init__.py +0 -0
  236. tau/tui/components/autocomplete_manager.py +267 -0
  237. tau/tui/components/autocomplete_picker.py +143 -0
  238. tau/tui/components/box.py +90 -0
  239. tau/tui/components/command_palette.py +144 -0
  240. tau/tui/components/dynamic_border.py +19 -0
  241. tau/tui/components/file_picker.py +233 -0
  242. tau/tui/components/image.py +181 -0
  243. tau/tui/components/inline_selector.py +71 -0
  244. tau/tui/components/layout.py +1194 -0
  245. tau/tui/components/message_list.py +692 -0
  246. tau/tui/components/modal.py +97 -0
  247. tau/tui/components/model_palette.py +204 -0
  248. tau/tui/components/picker_overlay.py +174 -0
  249. tau/tui/components/prompt_overlay.py +236 -0
  250. tau/tui/components/resume_modal.py +372 -0
  251. tau/tui/components/select_list.py +222 -0
  252. tau/tui/components/settings_modal.py +274 -0
  253. tau/tui/components/settings_schema.py +203 -0
  254. tau/tui/components/spinner.py +119 -0
  255. tau/tui/components/text_input.py +396 -0
  256. tau/tui/components/text_prompt.py +82 -0
  257. tau/tui/components/tree_select_list.py +580 -0
  258. tau/tui/components/trust_screen.py +97 -0
  259. tau/tui/diff.py +114 -0
  260. tau/tui/fuzzy.py +99 -0
  261. tau/tui/input.py +496 -0
  262. tau/tui/input_handler.py +716 -0
  263. tau/tui/keybindings.py +87 -0
  264. tau/tui/markdown.py +286 -0
  265. tau/tui/message_renderers.py +31 -0
  266. tau/tui/overlay.py +326 -0
  267. tau/tui/renderer.py +378 -0
  268. tau/tui/terminal.py +499 -0
  269. tau/tui/theme.py +148 -0
  270. tau/tui/tui.py +544 -0
  271. tau/tui/ui_context.py +768 -0
  272. tau/tui/utils.py +20 -0
  273. tau/utils/__init__.py +0 -0
  274. tau/utils/http_proxy.py +221 -0
  275. tau/utils/image_processing.py +172 -0
  276. tau/utils/secrets.py +59 -0
  277. tau/utils/version_check.py +60 -0
  278. tau_coding_agent-0.1.0.dist-info/METADATA +177 -0
  279. tau_coding_agent-0.1.0.dist-info/RECORD +283 -0
  280. tau_coding_agent-0.1.0.dist-info/WHEEL +5 -0
  281. tau_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
  282. tau_coding_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
  283. tau_coding_agent-0.1.0.dist-info/top_level.txt +1 -0
tau/inference/types.py ADDED
@@ -0,0 +1,549 @@
1
+ from __future__ import annotations
2
+ from dataclasses import dataclass, field
3
+ from datetime import timedelta
4
+ from enum import Enum
5
+ from typing import Any, Callable, Optional, TYPE_CHECKING
6
+ import asyncio
7
+ import time
8
+ from copy import deepcopy
9
+
10
+ from pydantic import BaseModel
11
+
12
+ if TYPE_CHECKING:
13
+ from tau.message.types import LLMMessage, TextContent, ThinkingContent, ToolCallContent, ImageContent
14
+ from tau.tool.types import Tool
15
+
16
+
17
+ # ── Shared enums ──────────────────────────────────────────────────────────────
18
+
19
+ class Transport(str, Enum):
20
+ """Wire transport protocol used to reach the provider endpoint."""
21
+
22
+ Auto = "auto"
23
+ HTTP = "http"
24
+ WEBSOCKET = "websocket"
25
+ SSE = "sse"
26
+
27
+
28
+ class AuthType(str, Enum):
29
+ """Authentication mechanism used by a provider."""
30
+
31
+ ApiKey = "api_key"
32
+ OAuth = "oauth"
33
+
34
+
35
+ class StopReason(str, Enum):
36
+ """Normalised reason a model generation stopped."""
37
+
38
+ Stop = "stop"
39
+ Length = "length"
40
+ ToolCalls = "tool_calls"
41
+ ContentFilter = "content_filter"
42
+ Abort = "abort"
43
+ Error = "error"
44
+
45
+
46
+ class ThinkingLevel(str, Enum):
47
+ """Ordered thinking/reasoning intensity levels mapped to provider budgets."""
48
+
49
+ Off = "off"
50
+ Minimal = "minimal"
51
+ Low = "low"
52
+ Medium = "medium"
53
+ High = "high"
54
+ XHigh = "xhigh"
55
+ Max = "max"
56
+
57
+
58
+ @dataclass
59
+ class ThinkingBudgets:
60
+ """Token budgets for providers that map ThinkingLevel to budget_tokens."""
61
+ minimal: Optional[int] = 1024
62
+ low: Optional[int] = 2048
63
+ medium: Optional[int] = 4096
64
+ high: Optional[int] = 8192
65
+ xhigh: Optional[int] = 16384
66
+ max: Optional[int] = 32768
67
+
68
+ def get(self, level: ThinkingLevel) -> int:
69
+ """Return the budget_tokens value for the given ThinkingLevel, falling back to built-in defaults."""
70
+ _defaults = {
71
+ "minimal": 1024, "low": 2048, "medium": 4096,
72
+ "high": 8192, "xhigh": 16384, "max": 32768,
73
+ }
74
+ value = getattr(self, level.value, None)
75
+ return value if value is not None else _defaults[level.value]
76
+
77
+
78
+ # ── LLM types ─────────────────────────────────────────────────────────────────
79
+
80
+ class LLMEventType(str, Enum):
81
+ """Discriminant tag carried by every LLMEvent dataclass."""
82
+
83
+ Start = "start"
84
+ Retry = "retry"
85
+ Error = "error"
86
+ End = "end"
87
+ TextStart = "text_start"
88
+ TextDelta = "text_delta"
89
+ TextEnd = "text_end"
90
+ ThinkingStart = "thinking_start"
91
+ ThinkingDelta = "thinking_delta"
92
+ ThinkingEnd = "thinking_end"
93
+ ToolCallStart = "tool_call_start"
94
+ ToolCallDelta = "tool_call_delta"
95
+ ToolCallEnd = "tool_call_end"
96
+
97
+
98
+ AbortSignal = asyncio.Event
99
+ PayloadCallback = Callable[[dict[str, Any]], Optional[dict[str, Any]]]
100
+ ResponseCallback = Callable[[Any], None]
101
+
102
+
103
+ @dataclass
104
+ class LLMOptions:
105
+ """Runtime configuration passed to every BaseLLMAPI constructor and stream() call."""
106
+
107
+ api_key: Optional[str] = None
108
+ base_url: Optional[str] = None
109
+ headers: Optional[dict[str, str]] = None
110
+ max_retries: int = 3
111
+ retry_base_delay_ms: int = 1000
112
+ timeout: timedelta = field(default_factory=lambda: timedelta(seconds=60))
113
+ temperature: float = 1.0
114
+ max_tokens: Optional[int] = None
115
+ transport: Transport = Transport.HTTP
116
+ thinking_level: Optional[ThinkingLevel] = None
117
+ thinking_budgets: Optional[ThinkingBudgets] = None
118
+ signal: Optional[AbortSignal] = None
119
+ extra_params: Optional[dict[str, Any]] = None
120
+ on_payload: Optional[PayloadCallback] = None
121
+ on_response: Optional[ResponseCallback] = None
122
+
123
+
124
+ @dataclass
125
+ class StructuredResponseFormat:
126
+ """Normalised structured output spec (JSON schema + name + strict flag)."""
127
+
128
+ schema: dict[str, Any]
129
+ name: str = "response"
130
+ strict: bool = True
131
+
132
+
133
+ StructuredResponseInput = StructuredResponseFormat | type[Any] | dict[str, Any]
134
+
135
+
136
+ def normalize_structured_response_format(response_format: StructuredResponseInput | None) -> StructuredResponseFormat | None:
137
+ """Coerce any supported response_format shape into a StructuredResponseFormat, or None."""
138
+ if response_format is None:
139
+ return None
140
+
141
+ if isinstance(response_format, StructuredResponseFormat):
142
+ return response_format
143
+
144
+ if isinstance(response_format, type) and issubclass(response_format, BaseModel):
145
+ return StructuredResponseFormat(
146
+ name=response_format.__name__,
147
+ schema=response_format.model_json_schema(),
148
+ )
149
+
150
+ if isinstance(response_format, dict):
151
+ schema = deepcopy(response_format)
152
+ if isinstance(schema.get("format"), dict):
153
+ schema = deepcopy(schema["format"])
154
+ if isinstance(schema.get("json_schema"), dict):
155
+ schema = deepcopy(schema["json_schema"])
156
+ name = str(schema.get("name") or schema.get("title") or "response")
157
+ strict = bool(schema.pop("strict", True))
158
+ if "schema" in schema and isinstance(schema["schema"], dict):
159
+ name = str(schema.pop("name", name))
160
+ schema = deepcopy(schema["schema"])
161
+ return StructuredResponseFormat(name=name, schema=schema, strict=strict)
162
+
163
+ raise TypeError("response_format must be a Pydantic model class, JSON schema dict, or StructuredResponseFormat")
164
+
165
+
166
+ @dataclass
167
+ class LLMContext:
168
+ """All inputs required to execute one LLM turn: messages, tools, and optional overrides."""
169
+
170
+ messages: list["LLMMessage"]
171
+ tools: list["Tool"] = field(default_factory=list)
172
+ system_prompt: Optional[str] = None
173
+ response_format: Optional[StructuredResponseInput] = None
174
+
175
+
176
+ def _default_text_event_data():
177
+ """Return an empty TextContent; used as a field default_factory to avoid mutable defaults."""
178
+ from tau.message.types import TextContent
179
+ return TextContent(content="")
180
+
181
+
182
+ @dataclass
183
+ class TextEventData:
184
+ """Mixin carrying a TextContent payload for text-phase events."""
185
+
186
+ text: "TextContent" = field(default_factory=_default_text_event_data)
187
+
188
+
189
+ @dataclass
190
+ class ThinkingEventData:
191
+ """Mixin carrying an optional ThinkingContent payload for thinking-phase events."""
192
+
193
+ thinking: Optional["ThinkingContent"] = None
194
+
195
+
196
+ @dataclass
197
+ class ToolCallEventData:
198
+ """Mixin carrying an optional ToolCallContent payload for tool-call-phase events."""
199
+
200
+ tool_call: Optional["ToolCallContent"] = None
201
+
202
+
203
+ @dataclass
204
+ class StartEvent:
205
+ """Emitted once at the very beginning of a stream before any content."""
206
+
207
+ type: LLMEventType = field(default=LLMEventType.Start, init=False)
208
+
209
+
210
+ @dataclass
211
+ class RetryEvent:
212
+ """Emitted before each retry attempt after a transient pre-stream failure."""
213
+
214
+ type: LLMEventType = field(default=LLMEventType.Retry, init=False)
215
+ attempt: int = 0
216
+ max_retries: int = 0
217
+ error: str = ""
218
+
219
+
220
+ @dataclass
221
+ class ErrorEvent:
222
+ """Emitted when the stream terminates due to an error or cancellation."""
223
+
224
+ type: LLMEventType = field(default=LLMEventType.Error, init=False)
225
+ reason: StopReason = StopReason.Stop
226
+ error: str = ""
227
+
228
+
229
+ @dataclass
230
+ class EndEvent:
231
+ """Emitted once at the end of a stream carrying token usage and the stop reason."""
232
+
233
+ type: LLMEventType = field(default=LLMEventType.End, init=False)
234
+ reason: StopReason = StopReason.Stop
235
+ input_tokens: int = 0
236
+ output_tokens: int = 0
237
+ cache_read_tokens: int = 0
238
+ cache_write_tokens: int = 0
239
+
240
+
241
+ @dataclass
242
+ class TextStartEvent:
243
+ """Signals the opening of a new text content block."""
244
+
245
+ type: LLMEventType = field(default=LLMEventType.TextStart, init=False)
246
+ text: "TextContent"
247
+
248
+
249
+ @dataclass
250
+ class TextDeltaEvent:
251
+ """Carries an incremental text chunk within the active text block."""
252
+
253
+ type: LLMEventType = field(default=LLMEventType.TextDelta, init=False)
254
+ text: "TextContent"
255
+
256
+
257
+ @dataclass
258
+ class TextEndEvent:
259
+ """Signals the close of a text block, carrying the fully accumulated text."""
260
+
261
+ type: LLMEventType = field(default=LLMEventType.TextEnd, init=False)
262
+ text: "TextContent"
263
+
264
+
265
+ @dataclass
266
+ class ThinkingStartEvent:
267
+ """Signals the opening of a thinking/reasoning content block."""
268
+
269
+ type: LLMEventType = field(default=LLMEventType.ThinkingStart, init=False)
270
+ thinking: Optional["ThinkingContent"] = None
271
+
272
+
273
+ @dataclass
274
+ class ThinkingDeltaEvent:
275
+ """Carries an incremental chunk of thinking/reasoning text."""
276
+
277
+ type: LLMEventType = field(default=LLMEventType.ThinkingDelta, init=False)
278
+ thinking: "ThinkingContent"
279
+
280
+
281
+ @dataclass
282
+ class ThinkingEndEvent:
283
+ """Signals the close of a thinking block, carrying the fully accumulated text."""
284
+
285
+ type: LLMEventType = field(default=LLMEventType.ThinkingEnd, init=False)
286
+ thinking: "ThinkingContent"
287
+
288
+
289
+ @dataclass
290
+ class ToolCallStartEvent:
291
+ """Signals that the model has started emitting a tool call (id and name known)."""
292
+
293
+ type: LLMEventType = field(default=LLMEventType.ToolCallStart, init=False)
294
+ tool_call: "ToolCallContent"
295
+
296
+
297
+ @dataclass
298
+ class ToolCallDeltaEvent:
299
+ """Carries a partial JSON arguments chunk for an in-progress tool call."""
300
+
301
+ type: LLMEventType = field(default=LLMEventType.ToolCallDelta, init=False)
302
+ tool_call: "ToolCallContent"
303
+
304
+
305
+ @dataclass
306
+ class ToolCallEndEvent:
307
+ """Signals the completion of a tool call with the final parsed arguments."""
308
+
309
+ type: LLMEventType = field(default=LLMEventType.ToolCallEnd, init=False)
310
+ tool_call: "ToolCallContent"
311
+
312
+
313
+ LLMEvent = (
314
+ StartEvent
315
+ | RetryEvent
316
+ | ErrorEvent
317
+ | EndEvent
318
+ | TextStartEvent
319
+ | TextDeltaEvent
320
+ | TextEndEvent
321
+ | ThinkingStartEvent
322
+ | ThinkingDeltaEvent
323
+ | ThinkingEndEvent
324
+ | ToolCallStartEvent
325
+ | ToolCallDeltaEvent
326
+ | ToolCallEndEvent
327
+ )
328
+
329
+
330
+ # ── Image types ───────────────────────────────────────────────────────────────
331
+
332
+ class ImageStopReason(str, Enum):
333
+ """Normalised reason an image generation run stopped."""
334
+
335
+ Stop = "stop"
336
+ Error = "error"
337
+ Abort = "abort"
338
+
339
+
340
+ @dataclass
341
+ class ImageOptions:
342
+ """Runtime configuration for image generation API calls."""
343
+
344
+ api_key: Optional[str] = None
345
+ base_url: Optional[str] = None
346
+ headers: Optional[dict[str, str]] = None
347
+ timeout: timedelta = field(default_factory=lambda: timedelta(seconds=120))
348
+ max_retries: int = 3
349
+ on_payload: Optional[PayloadCallback] = None
350
+ on_response: Optional[ResponseCallback] = None
351
+
352
+
353
+ @dataclass
354
+ class ImageContext:
355
+ """Inputs for a single image generation request."""
356
+
357
+ contents: list["TextContent | ImageContent"]
358
+ size: Optional[str] = None
359
+ quality: Optional[str] = None
360
+ n: int = 1
361
+
362
+
363
+ @dataclass
364
+ class GeneratedImage:
365
+ """Result of a completed image generation call."""
366
+
367
+ model_id: str
368
+ provider: str
369
+ output: list["TextContent | ImageContent"]
370
+ stop_reason: ImageStopReason
371
+ usage: Any = field(default_factory=lambda: __import__("tau.message.types", fromlist=["Usage"]).Usage())
372
+ error: str = ""
373
+ timestamp: float = field(default_factory=time.time)
374
+
375
+
376
+ # ── Video types ───────────────────────────────────────────────────────────────
377
+
378
+ class VideoFormat(str, Enum):
379
+ """Container format for generated video output."""
380
+
381
+ MP4 = "mp4"
382
+ WEBM = "webm"
383
+ MOV = "mov"
384
+ GIF = "gif"
385
+
386
+
387
+ class VideoStopReason(str, Enum):
388
+ """Normalised reason a video generation job stopped."""
389
+
390
+ Stop = "stop"
391
+ Error = "error"
392
+ Abort = "abort"
393
+ Timeout = "timeout"
394
+
395
+
396
+ @dataclass
397
+ class VideoOptions:
398
+ """Runtime configuration for video generation API calls."""
399
+
400
+ api_key: Optional[str] = None
401
+ base_url: Optional[str] = None
402
+ headers: Optional[dict[str, str]] = None
403
+ timeout: timedelta = field(default_factory=lambda: timedelta(seconds=600))
404
+ poll_interval: float = 3.0
405
+ max_retries: int = 3
406
+ on_payload: Optional[PayloadCallback] = None
407
+ on_response: Optional[ResponseCallback] = None
408
+
409
+
410
+ @dataclass
411
+ class VideoContext:
412
+ """Inputs for a single video generation request."""
413
+
414
+ prompt: str
415
+ image: Optional[bytes] = None
416
+ duration: Optional[float] = None
417
+ aspect_ratio: Optional[str] = None
418
+ resolution: Optional[str] = None
419
+
420
+
421
+ @dataclass
422
+ class GeneratedVideo:
423
+ """Result of a completed video generation job."""
424
+
425
+ model_id: str
426
+ provider: str
427
+ url: Optional[str] = None
428
+ video: Optional[bytes] = None
429
+ format: VideoFormat = VideoFormat.MP4
430
+ duration: Optional[float] = None
431
+ stop_reason: VideoStopReason = VideoStopReason.Stop
432
+ usage: Any = None
433
+ error: str = ""
434
+ timestamp: float = field(default_factory=time.time)
435
+
436
+
437
+ # ── Audio types ───────────────────────────────────────────────────────────────
438
+
439
+ class AudioFormat(str, Enum):
440
+ """Audio codec/container format for TTS output or STT input."""
441
+
442
+ MP3 = "mp3"
443
+ WAV = "wav"
444
+ OPUS = "opus"
445
+ AAC = "aac"
446
+ FLAC = "flac"
447
+ PCM = "pcm"
448
+
449
+
450
+ class AudioStopReason(str, Enum):
451
+ """Normalised reason an audio synthesis or transcription call stopped."""
452
+
453
+ Stop = "stop"
454
+ Error = "error"
455
+ Abort = "abort"
456
+
457
+
458
+ class TimestampGranularity(str, Enum):
459
+ """Level of timestamp detail requested in a transcription response."""
460
+
461
+ Word = "word"
462
+ Segment = "segment"
463
+
464
+
465
+ @dataclass
466
+ class AudioOptions:
467
+ """Runtime configuration for TTS and STT API calls."""
468
+
469
+ api_key: Optional[str] = None
470
+ base_url: Optional[str] = None
471
+ headers: Optional[dict[str, str]] = None
472
+ timeout: timedelta = field(default_factory=lambda: timedelta(seconds=120))
473
+ max_retries: int = 3
474
+ on_payload: Optional[PayloadCallback] = None
475
+ on_response: Optional[ResponseCallback] = None
476
+
477
+
478
+ @dataclass
479
+ class TTSContext:
480
+ """Inputs for a single text-to-speech synthesis request."""
481
+
482
+ input: str
483
+ voice: str
484
+ speed: float = 1.0
485
+ response_format: AudioFormat = AudioFormat.MP3
486
+ language: Optional[str] = None
487
+ instructions: Optional[str] = None
488
+
489
+
490
+ @dataclass
491
+ class WordTimestamp:
492
+ """Timing information for a single word in a transcription."""
493
+
494
+ word: str
495
+ start: float
496
+ end: float
497
+
498
+
499
+ @dataclass
500
+ class SegmentTimestamp:
501
+ """Timing information for a sentence-level segment in a transcription."""
502
+
503
+ id: int
504
+ text: str
505
+ start: float
506
+ end: float
507
+
508
+
509
+ @dataclass
510
+ class STTContext:
511
+ """Inputs for a single speech-to-text transcription request."""
512
+
513
+ audio: bytes
514
+ format: AudioFormat = AudioFormat.MP3
515
+ language: Optional[str] = None
516
+ temperature: float = 0.0
517
+ timestamp_granularities: list[TimestampGranularity] = field(default_factory=list)
518
+ prompt: Optional[str] = None
519
+
520
+
521
+ @dataclass
522
+ class SynthesizedAudio:
523
+ """Result of a completed TTS synthesis call."""
524
+
525
+ model_id: str
526
+ provider: str
527
+ audio: bytes
528
+ format: AudioFormat
529
+ stop_reason: AudioStopReason
530
+ usage: Any = None
531
+ error: str = ""
532
+ timestamp: float = field(default_factory=time.time)
533
+
534
+
535
+ @dataclass
536
+ class TranscribedAudio:
537
+ """Result of a completed STT transcription call."""
538
+
539
+ model_id: str
540
+ provider: str
541
+ text: str
542
+ language: Optional[str] = None
543
+ duration: Optional[float] = None
544
+ words: list[WordTimestamp] = field(default_factory=list)
545
+ segments: list[SegmentTimestamp] = field(default_factory=list)
546
+ stop_reason: AudioStopReason = AudioStopReason.Stop
547
+ usage: Any = None
548
+ error: str = ""
549
+ timestamp: float = field(default_factory=time.time)