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.
- tau/__init__.py +0 -0
- tau/agent/__init__.py +11 -0
- tau/agent/prompt/__init__.py +10 -0
- tau/agent/prompt/builder.py +302 -0
- tau/agent/prompt/types.py +33 -0
- tau/agent/service.py +369 -0
- tau/agent/types.py +61 -0
- tau/auth/manager.py +247 -0
- tau/auth/storage.py +82 -0
- tau/auth/types.py +41 -0
- tau/builtins/__init__.py +4 -0
- tau/builtins/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/__pycache__/__init__.cpython-314.pyc +0 -0
- tau/builtins/commands/__init__.py +41 -0
- tau/builtins/commands/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/commands/__pycache__/__init__.cpython-314.pyc +0 -0
- tau/builtins/commands/__pycache__/clear.cpython-313.pyc +0 -0
- tau/builtins/commands/__pycache__/clear.cpython-314.pyc +0 -0
- tau/builtins/commands/__pycache__/compact.cpython-313.pyc +0 -0
- tau/builtins/commands/__pycache__/compact.cpython-314.pyc +0 -0
- tau/builtins/commands/__pycache__/reload.cpython-313.pyc +0 -0
- tau/builtins/commands/__pycache__/reload.cpython-314.pyc +0 -0
- tau/builtins/commands/__pycache__/session.cpython-313.pyc +0 -0
- tau/builtins/commands/__pycache__/session.cpython-314.pyc +0 -0
- tau/builtins/commands/clear.py +16 -0
- tau/builtins/commands/compact.py +28 -0
- tau/builtins/commands/reload.py +27 -0
- tau/builtins/commands/session.py +19 -0
- tau/builtins/extensions/footer/__init__.py +76 -0
- tau/builtins/extensions/footer/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/extensions/footer/__pycache__/git.cpython-313.pyc +0 -0
- tau/builtins/extensions/footer/__pycache__/model.cpython-313.pyc +0 -0
- tau/builtins/extensions/footer/__pycache__/utils.cpython-313.pyc +0 -0
- tau/builtins/extensions/footer/git.py +26 -0
- tau/builtins/extensions/footer/model.py +69 -0
- tau/builtins/extensions/footer/utils.py +44 -0
- tau/builtins/extensions/header/__init__.py +18 -0
- tau/builtins/extensions/header/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/models/__init__.py +0 -0
- tau/builtins/models/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/models/__pycache__/text.cpython-313.pyc +0 -0
- tau/builtins/models/audio.py +43 -0
- tau/builtins/models/image.py +43 -0
- tau/builtins/models/text.py +482 -0
- tau/builtins/models/video.py +40 -0
- tau/builtins/prompts/commit.md +7 -0
- tau/builtins/prompts/docs.md +7 -0
- tau/builtins/prompts/explain.md +7 -0
- tau/builtins/prompts/fix.md +7 -0
- tau/builtins/prompts/refactor.md +7 -0
- tau/builtins/prompts/review.md +7 -0
- tau/builtins/prompts/test.md +7 -0
- tau/builtins/providers/__init__.py +0 -0
- tau/builtins/providers/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/providers/__pycache__/text.cpython-313.pyc +0 -0
- tau/builtins/providers/audio.py +10 -0
- tau/builtins/providers/image.py +9 -0
- tau/builtins/providers/text.py +33 -0
- tau/builtins/providers/video.py +6 -0
- tau/builtins/skills/code-review/SKILL.md +4 -0
- tau/builtins/skills/debug/SKILL.md +4 -0
- tau/builtins/skills/git-commit/SKILL.md +4 -0
- tau/builtins/themes/dark.yaml +1 -0
- tau/builtins/themes/light.yaml +46 -0
- tau/builtins/tools/__init__.py +73 -0
- tau/builtins/tools/__pycache__/__init__.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/__init__.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/bash.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/bash.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/edit.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/edit.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/glob.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/glob.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/grep.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/grep.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/ls.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/ls.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/read.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/read.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/terminal.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/terminal.cpython-314.pyc +0 -0
- tau/builtins/tools/__pycache__/write.cpython-313.pyc +0 -0
- tau/builtins/tools/__pycache__/write.cpython-314.pyc +0 -0
- tau/builtins/tools/edit.py +215 -0
- tau/builtins/tools/glob.py +112 -0
- tau/builtins/tools/grep.py +146 -0
- tau/builtins/tools/ls.py +135 -0
- tau/builtins/tools/read.py +122 -0
- tau/builtins/tools/terminal.py +150 -0
- tau/builtins/tools/write.py +105 -0
- tau/commands/__init__.py +10 -0
- tau/commands/registry.py +71 -0
- tau/commands/types.py +33 -0
- tau/console/__init__.py +0 -0
- tau/console/cli.py +266 -0
- tau/console/commands/__init__.py +0 -0
- tau/console/commands/auth.py +193 -0
- tau/console/commands/packages.py +104 -0
- tau/console/commands/update.py +76 -0
- tau/core/__init__.py +0 -0
- tau/core/registry.py +102 -0
- tau/engine/__init__.py +47 -0
- tau/engine/service.py +768 -0
- tau/engine/types.py +163 -0
- tau/extensions/__init__.py +28 -0
- tau/extensions/api.py +928 -0
- tau/extensions/context.py +462 -0
- tau/extensions/events.py +70 -0
- tau/extensions/loader.py +386 -0
- tau/extensions/runtime.py +184 -0
- tau/extensions/settings.py +137 -0
- tau/hooks/__init__.py +112 -0
- tau/hooks/engine.py +237 -0
- tau/hooks/inference.py +21 -0
- tau/hooks/runtime.py +126 -0
- tau/hooks/service.py +121 -0
- tau/hooks/session.py +117 -0
- tau/hooks/tui.py +61 -0
- tau/hooks/types.py +72 -0
- tau/inference/__init__.py +180 -0
- tau/inference/api/__init__.py +0 -0
- tau/inference/api/audio/__init__.py +0 -0
- tau/inference/api/audio/base.py +29 -0
- tau/inference/api/audio/builtins.py +15 -0
- tau/inference/api/audio/elevenlabs_audio.py +183 -0
- tau/inference/api/audio/gemini_audio.py +95 -0
- tau/inference/api/audio/openai_audio.py +159 -0
- tau/inference/api/audio/registry.py +15 -0
- tau/inference/api/audio/sarvam_audio.py +163 -0
- tau/inference/api/audio/service.py +103 -0
- tau/inference/api/audio/utils.py +47 -0
- tau/inference/api/image/__init__.py +0 -0
- tau/inference/api/image/base.py +17 -0
- tau/inference/api/image/builtins.py +8 -0
- tau/inference/api/image/gemini_image.py +77 -0
- tau/inference/api/image/openai_image.py +103 -0
- tau/inference/api/image/openrouter.py +144 -0
- tau/inference/api/image/registry.py +15 -0
- tau/inference/api/image/service.py +71 -0
- tau/inference/api/registry.py +82 -0
- tau/inference/api/text/__init__.py +0 -0
- tau/inference/api/text/anthropic_claude_code.py +222 -0
- tau/inference/api/text/anthropic_messages.py +196 -0
- tau/inference/api/text/base.py +40 -0
- tau/inference/api/text/builtins.py +19 -0
- tau/inference/api/text/gemini_generate.py +234 -0
- tau/inference/api/text/github_copilot_chat.py +172 -0
- tau/inference/api/text/google_antigravity.py +522 -0
- tau/inference/api/text/mistral_chat.py +284 -0
- tau/inference/api/text/ollama_chat.py +200 -0
- tau/inference/api/text/openai_codex_responses.py +497 -0
- tau/inference/api/text/openai_completions.py +227 -0
- tau/inference/api/text/openai_responses.py +235 -0
- tau/inference/api/text/registry.py +50 -0
- tau/inference/api/text/service.py +297 -0
- tau/inference/api/text/types.py +7 -0
- tau/inference/api/text/utils.py +228 -0
- tau/inference/api/video/__init__.py +0 -0
- tau/inference/api/video/base.py +26 -0
- tau/inference/api/video/builtins.py +7 -0
- tau/inference/api/video/fal_video.py +119 -0
- tau/inference/api/video/openrouter_video.py +142 -0
- tau/inference/api/video/registry.py +15 -0
- tau/inference/api/video/service.py +72 -0
- tau/inference/model/__init__.py +0 -0
- tau/inference/model/registry.py +102 -0
- tau/inference/model/types.py +65 -0
- tau/inference/provider/__init__.py +0 -0
- tau/inference/provider/oauth/__init__.py +35 -0
- tau/inference/provider/oauth/anthropic_claude_code.py +286 -0
- tau/inference/provider/oauth/github_copilot.py +333 -0
- tau/inference/provider/oauth/google_antigravity.py +258 -0
- tau/inference/provider/oauth/openai_codex.py +309 -0
- tau/inference/provider/oauth/pkce.py +14 -0
- tau/inference/provider/oauth/types.py +46 -0
- tau/inference/provider/oauth/utils.py +154 -0
- tau/inference/provider/registry.py +141 -0
- tau/inference/provider/types.py +114 -0
- tau/inference/types.py +549 -0
- tau/inference/utils.py +219 -0
- tau/message/__init__.py +0 -0
- tau/message/types.py +482 -0
- tau/message/utils.py +178 -0
- tau/packages/__init__.py +11 -0
- tau/packages/manager.py +190 -0
- tau/packages/types.py +20 -0
- tau/packages/utils.py +67 -0
- tau/prompts/expand.py +58 -0
- tau/prompts/loader.py +69 -0
- tau/prompts/registry.py +45 -0
- tau/prompts/types.py +24 -0
- tau/rpc/__init__.py +8 -0
- tau/rpc/mode.py +783 -0
- tau/rpc/types.py +252 -0
- tau/runtime/service.py +759 -0
- tau/runtime/types.py +303 -0
- tau/session/branch_summarization.py +312 -0
- tau/session/compaction.py +646 -0
- tau/session/manager.py +652 -0
- tau/session/types.py +188 -0
- tau/session/utils.py +233 -0
- tau/settings/manager.py +1077 -0
- tau/settings/paths.py +150 -0
- tau/settings/storage.py +63 -0
- tau/settings/types.py +173 -0
- tau/settings/utils.py +25 -0
- tau/skills/loader.py +91 -0
- tau/skills/registry.py +70 -0
- tau/skills/types.py +25 -0
- tau/themes/loader.py +238 -0
- tau/themes/registry.py +108 -0
- tau/themes/types.py +19 -0
- tau/tool/__init__.py +3 -0
- tau/tool/registry.py +117 -0
- tau/tool/render.py +21 -0
- tau/tool/types.py +244 -0
- tau/trust/__init__.py +13 -0
- tau/trust/manager.py +80 -0
- tau/trust/types.py +14 -0
- tau/trust/utils.py +72 -0
- tau/tui/__init__.py +54 -0
- tau/tui/agent_hooks.py +346 -0
- tau/tui/ansi.py +330 -0
- tau/tui/app.py +540 -0
- tau/tui/autocomplete.py +33 -0
- tau/tui/capabilities.py +119 -0
- tau/tui/commands/__init__.py +3 -0
- tau/tui/commands/appearance.py +498 -0
- tau/tui/commands/auth.py +232 -0
- tau/tui/commands/context.py +38 -0
- tau/tui/commands/misc.py +82 -0
- tau/tui/commands/model.py +118 -0
- tau/tui/commands/session.py +464 -0
- tau/tui/component.py +268 -0
- tau/tui/components/__init__.py +0 -0
- tau/tui/components/autocomplete_manager.py +267 -0
- tau/tui/components/autocomplete_picker.py +143 -0
- tau/tui/components/box.py +90 -0
- tau/tui/components/command_palette.py +144 -0
- tau/tui/components/dynamic_border.py +19 -0
- tau/tui/components/file_picker.py +233 -0
- tau/tui/components/image.py +181 -0
- tau/tui/components/inline_selector.py +71 -0
- tau/tui/components/layout.py +1194 -0
- tau/tui/components/message_list.py +692 -0
- tau/tui/components/modal.py +97 -0
- tau/tui/components/model_palette.py +204 -0
- tau/tui/components/picker_overlay.py +174 -0
- tau/tui/components/prompt_overlay.py +236 -0
- tau/tui/components/resume_modal.py +372 -0
- tau/tui/components/select_list.py +222 -0
- tau/tui/components/settings_modal.py +274 -0
- tau/tui/components/settings_schema.py +203 -0
- tau/tui/components/spinner.py +119 -0
- tau/tui/components/text_input.py +396 -0
- tau/tui/components/text_prompt.py +82 -0
- tau/tui/components/tree_select_list.py +580 -0
- tau/tui/components/trust_screen.py +97 -0
- tau/tui/diff.py +114 -0
- tau/tui/fuzzy.py +99 -0
- tau/tui/input.py +496 -0
- tau/tui/input_handler.py +716 -0
- tau/tui/keybindings.py +87 -0
- tau/tui/markdown.py +286 -0
- tau/tui/message_renderers.py +31 -0
- tau/tui/overlay.py +326 -0
- tau/tui/renderer.py +378 -0
- tau/tui/terminal.py +499 -0
- tau/tui/theme.py +148 -0
- tau/tui/tui.py +544 -0
- tau/tui/ui_context.py +768 -0
- tau/tui/utils.py +20 -0
- tau/utils/__init__.py +0 -0
- tau/utils/http_proxy.py +221 -0
- tau/utils/image_processing.py +172 -0
- tau/utils/secrets.py +59 -0
- tau/utils/version_check.py +60 -0
- tau_coding_agent-0.1.0.dist-info/METADATA +177 -0
- tau_coding_agent-0.1.0.dist-info/RECORD +283 -0
- tau_coding_agent-0.1.0.dist-info/WHEEL +5 -0
- tau_coding_agent-0.1.0.dist-info/entry_points.txt +2 -0
- tau_coding_agent-0.1.0.dist-info/licenses/LICENSE +21 -0
- tau_coding_agent-0.1.0.dist-info/top_level.txt +1 -0
tau/auth/storage.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from tau.auth.types import LockResult
|
|
3
|
+
from filelock import FileLock
|
|
4
|
+
from typing import Callable, Awaitable
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AuthStorage(ABC):
|
|
9
|
+
"""Abstract storage backend for auth credentials."""
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def with_lock(self, fn: Callable[[str | None], LockResult]) -> LockResult:
|
|
13
|
+
"""Execute fn with exclusive access to storage."""
|
|
14
|
+
pass
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
async def with_lock_async(self, fn: Callable[[str | None], Awaitable[LockResult]]) -> LockResult:
|
|
18
|
+
"""Execute async fn with exclusive access to storage."""
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class FileAuthStorage(AuthStorage):
|
|
23
|
+
"""File-based storage backend with locking."""
|
|
24
|
+
|
|
25
|
+
def __init__(self, store_path: Path):
|
|
26
|
+
"""Initialize file storage at the given path."""
|
|
27
|
+
self.store_path = store_path
|
|
28
|
+
self.lock_path = store_path.with_suffix(".lock")
|
|
29
|
+
self._ensure_parent_dir()
|
|
30
|
+
self._ensure_file_exists()
|
|
31
|
+
|
|
32
|
+
def _ensure_parent_dir(self) -> None:
|
|
33
|
+
"""Create parent directory if it doesn't exist."""
|
|
34
|
+
self.store_path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
35
|
+
|
|
36
|
+
def _ensure_file_exists(self) -> None:
|
|
37
|
+
"""Create storage file if it doesn't exist."""
|
|
38
|
+
if not self.store_path.exists():
|
|
39
|
+
self.store_path.write_text("{}", encoding="utf-8")
|
|
40
|
+
self.store_path.chmod(0o600)
|
|
41
|
+
|
|
42
|
+
def with_lock(self, fn: Callable[[str | None], LockResult]) -> LockResult:
|
|
43
|
+
"""Execute fn with exclusive access to storage."""
|
|
44
|
+
with FileLock(self.lock_path):
|
|
45
|
+
current = self.store_path.read_text(encoding="utf-8") if self.store_path.exists() else None
|
|
46
|
+
result = fn(current)
|
|
47
|
+
if result.next is not None:
|
|
48
|
+
self.store_path.write_text(result.next, encoding="utf-8")
|
|
49
|
+
self.store_path.chmod(0o600)
|
|
50
|
+
return result
|
|
51
|
+
|
|
52
|
+
async def with_lock_async(self, fn: Callable[[str | None], Awaitable[LockResult]]) -> LockResult:
|
|
53
|
+
"""Execute async fn with exclusive access to storage."""
|
|
54
|
+
with FileLock(self.lock_path):
|
|
55
|
+
current = self.store_path.read_text(encoding="utf-8") if self.store_path.exists() else None
|
|
56
|
+
result = await fn(current)
|
|
57
|
+
if result.next is not None:
|
|
58
|
+
self.store_path.write_text(result.next, encoding="utf-8")
|
|
59
|
+
self.store_path.chmod(0o600)
|
|
60
|
+
return result
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class InMemoryAuthStorage(AuthStorage):
|
|
64
|
+
"""In-memory storage backend for testing."""
|
|
65
|
+
|
|
66
|
+
def __init__(self):
|
|
67
|
+
"""Initialize empty in-memory storage."""
|
|
68
|
+
self._value: str | None = None
|
|
69
|
+
|
|
70
|
+
def with_lock(self, fn: Callable[[str | None], LockResult]) -> LockResult:
|
|
71
|
+
"""Execute fn with exclusive access to memory storage."""
|
|
72
|
+
result = fn(self._value)
|
|
73
|
+
if result.next is not None:
|
|
74
|
+
self._value = result.next
|
|
75
|
+
return result
|
|
76
|
+
|
|
77
|
+
async def with_lock_async(self, fn: Callable[[str | None], Awaitable[LockResult]]) -> LockResult:
|
|
78
|
+
"""Execute async fn with exclusive access to memory storage."""
|
|
79
|
+
result = await fn(self._value)
|
|
80
|
+
if result.next is not None:
|
|
81
|
+
self._value = result.next
|
|
82
|
+
return result
|
tau/auth/types.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from tau.inference.types import AuthType
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Generic, TypeVar, Optional, Literal
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@dataclass
|
|
7
|
+
class OAuthCredential:
|
|
8
|
+
"""OAuth 2.0 credential with access and refresh tokens."""
|
|
9
|
+
type: AuthType = field(default_factory=lambda: AuthType.OAuth, init=False)
|
|
10
|
+
access: str = ""
|
|
11
|
+
refresh: str = ""
|
|
12
|
+
expires: int = 0 # Unix timestamp in milliseconds
|
|
13
|
+
extra: dict[str, str] = field(default_factory=dict)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class APICredential:
|
|
18
|
+
"""API key credential."""
|
|
19
|
+
type: AuthType = field(default_factory=lambda: AuthType.ApiKey, init=False)
|
|
20
|
+
key: str = ""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
AuthCredential = OAuthCredential | APICredential
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class AuthStatus:
|
|
28
|
+
"""Authentication status and source information."""
|
|
29
|
+
configured: bool
|
|
30
|
+
source: Optional[Literal["stored", "runtime", "env"]] = None
|
|
31
|
+
label: Optional[str] = None
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
T = TypeVar('T')
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class LockResult(Generic[T]):
|
|
39
|
+
"""Result of a locked operation with next continuation."""
|
|
40
|
+
result: T
|
|
41
|
+
next: str | None = None
|
tau/builtins/__init__.py
ADDED
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from tau.commands.types import CommandInfo
|
|
4
|
+
from tau.builtins.commands.session import cmd_new, cmd_fork
|
|
5
|
+
from tau.builtins.commands.reload import cmd_reload
|
|
6
|
+
from tau.builtins.commands.compact import cmd_compact
|
|
7
|
+
from tau.builtins.commands.clear import cmd_clear
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_builtin_commands() -> list[CommandInfo]:
|
|
11
|
+
"""Get the list of builtin slash commands."""
|
|
12
|
+
return [
|
|
13
|
+
CommandInfo(
|
|
14
|
+
name="new",
|
|
15
|
+
description="Start a fresh session.",
|
|
16
|
+
call=cmd_new,
|
|
17
|
+
),
|
|
18
|
+
CommandInfo(
|
|
19
|
+
name="fork",
|
|
20
|
+
description="Branch the session tree at a given entry ID.",
|
|
21
|
+
call=cmd_fork,
|
|
22
|
+
argument_hint="<entry_id>",
|
|
23
|
+
required_arg_names=["entry_id"],
|
|
24
|
+
),
|
|
25
|
+
CommandInfo(
|
|
26
|
+
name="reload",
|
|
27
|
+
description="Reload extensions, themes, and prompt appends.",
|
|
28
|
+
call=cmd_reload,
|
|
29
|
+
),
|
|
30
|
+
CommandInfo(
|
|
31
|
+
name="compact",
|
|
32
|
+
description="Summarise and compact the current session context.",
|
|
33
|
+
call=cmd_compact,
|
|
34
|
+
argument_hint="<custom_instruction>",
|
|
35
|
+
),
|
|
36
|
+
CommandInfo(
|
|
37
|
+
name="clear",
|
|
38
|
+
description="Clear the message list.",
|
|
39
|
+
call=cmd_clear,
|
|
40
|
+
),
|
|
41
|
+
]
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from tau.commands.registry import CommandRegistry
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def cmd_clear(reg: CommandRegistry, _args: list[str]) -> None:
|
|
10
|
+
"""Clear the message list."""
|
|
11
|
+
if reg.runtime is None:
|
|
12
|
+
return
|
|
13
|
+
from tau.extensions.context import ExtensionContext
|
|
14
|
+
ctx = ExtensionContext.from_runtime(reg.runtime)
|
|
15
|
+
if ctx.ui is not None:
|
|
16
|
+
ctx.ui.clear_messages()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from tau.commands.registry import CommandRegistry
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def cmd_compact(reg: CommandRegistry, args: list[str]) -> None:
|
|
10
|
+
"""Manually compact the current session context."""
|
|
11
|
+
if reg.runtime is None or reg.runtime.agent is None:
|
|
12
|
+
return
|
|
13
|
+
|
|
14
|
+
sm = reg.runtime.settings_manager
|
|
15
|
+
if sm is not None and not sm.is_compaction_enabled():
|
|
16
|
+
from tau.extensions.context import ExtensionContext
|
|
17
|
+
ctx = ExtensionContext.from_runtime(reg.runtime)
|
|
18
|
+
if ctx.ui is not None:
|
|
19
|
+
ctx.ui.notify("Compaction is disabled. Enable it in /settings → Compaction.")
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
custom_instructions = " ".join(args).strip() or None
|
|
23
|
+
did_compact = await reg.runtime.agent.compact(custom_instructions=custom_instructions)
|
|
24
|
+
if not did_compact:
|
|
25
|
+
from tau.extensions.context import ExtensionContext
|
|
26
|
+
ctx = ExtensionContext.from_runtime(reg.runtime)
|
|
27
|
+
if ctx.ui is not None:
|
|
28
|
+
ctx.ui.notify("Nothing to compact — conversation is too short to summarize.")
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from tau.commands.registry import CommandRegistry
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def cmd_reload(reg: CommandRegistry, _args: list[str]) -> None:
|
|
10
|
+
"""Reload extensions, themes, and prompt appends."""
|
|
11
|
+
if reg.runtime is None:
|
|
12
|
+
return
|
|
13
|
+
result = await reg.runtime.reload_extensions()
|
|
14
|
+
|
|
15
|
+
from tau.extensions.context import ExtensionContext
|
|
16
|
+
ctx = ExtensionContext.from_runtime(reg.runtime)
|
|
17
|
+
if ctx.ui is None:
|
|
18
|
+
return
|
|
19
|
+
|
|
20
|
+
n = len(result.extensions)
|
|
21
|
+
ext_word = "extension" if n == 1 else "extensions"
|
|
22
|
+
if result.errors:
|
|
23
|
+
e = len(result.errors)
|
|
24
|
+
err_word = "error" if e == 1 else "errors"
|
|
25
|
+
ctx.ui.notify(f"Reloaded {n} {ext_word} with {e} {err_word}.")
|
|
26
|
+
else:
|
|
27
|
+
ctx.ui.notify(f"Reloaded {n} {ext_word}.")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from tau.commands.registry import CommandRegistry
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def cmd_new(reg: CommandRegistry, _args: list[str]) -> None:
|
|
10
|
+
"""Start a new session."""
|
|
11
|
+
if reg.runtime is not None:
|
|
12
|
+
await reg.runtime.new_session()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
async def cmd_fork(reg: CommandRegistry, args: list[str]) -> None:
|
|
16
|
+
"""Fork the session at a given entry ID."""
|
|
17
|
+
if reg.runtime is None:
|
|
18
|
+
return
|
|
19
|
+
await reg.runtime.fork_session(args[0])
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Built-in footer status extension — git branch left, model/context right."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from .git import GitBadge
|
|
5
|
+
from .model import ModelBadge
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register(tau: object) -> None:
|
|
9
|
+
from tau.tui.component import Row
|
|
10
|
+
|
|
11
|
+
git_badge = GitBadge()
|
|
12
|
+
model_badge = ModelBadge()
|
|
13
|
+
row = Row([(git_badge, "left"), (model_badge, "right")]) # type: ignore[arg-type]
|
|
14
|
+
|
|
15
|
+
def _request_render(ctx: object) -> None:
|
|
16
|
+
layout = getattr(ctx, "_layout", None)
|
|
17
|
+
if layout is not None:
|
|
18
|
+
layout._tui.request_render()
|
|
19
|
+
|
|
20
|
+
@tau.on("tui_ready")
|
|
21
|
+
def on_ready(event, ctx):
|
|
22
|
+
ctx._layout.footer.add_child(row)
|
|
23
|
+
git_badge.update(str(ctx.cwd))
|
|
24
|
+
model_badge.update_from_ctx(ctx)
|
|
25
|
+
_request_render(ctx)
|
|
26
|
+
|
|
27
|
+
@tau.on("session_start")
|
|
28
|
+
def on_session_start(event, ctx):
|
|
29
|
+
if ctx.has_ui:
|
|
30
|
+
git_badge.update(str(ctx.cwd))
|
|
31
|
+
model_badge.update_from_ctx(ctx)
|
|
32
|
+
_request_render(ctx)
|
|
33
|
+
|
|
34
|
+
@tau.on("model_select")
|
|
35
|
+
def on_model_select(event, ctx):
|
|
36
|
+
if not ctx.has_ui:
|
|
37
|
+
return
|
|
38
|
+
model = getattr(event, "model", None)
|
|
39
|
+
if model is not None:
|
|
40
|
+
model_badge.set_model(
|
|
41
|
+
getattr(model, "id", "") or "",
|
|
42
|
+
getattr(model, "provider", "") or "",
|
|
43
|
+
bool(getattr(model, "thinking", False)),
|
|
44
|
+
)
|
|
45
|
+
# The new model usually has a different context window, so the usage %
|
|
46
|
+
# changes even though the token count didn't — refresh it immediately
|
|
47
|
+
# instead of waiting for the next turn.
|
|
48
|
+
model_badge.update_context_from_ctx(ctx)
|
|
49
|
+
_request_render(ctx)
|
|
50
|
+
|
|
51
|
+
@tau.on("thinking_level_select")
|
|
52
|
+
def on_thinking_level_select(event, ctx):
|
|
53
|
+
if not ctx.has_ui:
|
|
54
|
+
return
|
|
55
|
+
model_badge.set_thinking_level(getattr(event, "level", None))
|
|
56
|
+
_request_render(ctx)
|
|
57
|
+
|
|
58
|
+
@tau.on("settled")
|
|
59
|
+
def on_settled(event, ctx):
|
|
60
|
+
if ctx.has_ui:
|
|
61
|
+
git_badge.update(str(ctx.cwd))
|
|
62
|
+
model_badge.update_context_from_ctx(ctx)
|
|
63
|
+
_request_render(ctx)
|
|
64
|
+
|
|
65
|
+
@tau.on("message_end")
|
|
66
|
+
def on_message_end(event, ctx):
|
|
67
|
+
if ctx.has_ui:
|
|
68
|
+
git_badge.update(str(ctx.cwd))
|
|
69
|
+
model_badge.update_context_from_ctx(ctx)
|
|
70
|
+
_request_render(ctx)
|
|
71
|
+
|
|
72
|
+
@tau.on("compaction_end")
|
|
73
|
+
def on_compaction_end(event, ctx):
|
|
74
|
+
if ctx.has_ui:
|
|
75
|
+
model_badge.update_context_from_ctx(ctx)
|
|
76
|
+
_request_render(ctx)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"""Git branch badge component."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from .utils import read_branch, shorten_home
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class GitBadge:
|
|
8
|
+
"""Renders ``~/path (branch)`` for the footer Row left slot."""
|
|
9
|
+
|
|
10
|
+
def __init__(self) -> None:
|
|
11
|
+
self._text = ""
|
|
12
|
+
|
|
13
|
+
def update(self, cwd: str) -> None:
|
|
14
|
+
branch = read_branch(cwd)
|
|
15
|
+
display = shorten_home(cwd)
|
|
16
|
+
self._text = f"{display} ({branch})" if branch else display
|
|
17
|
+
|
|
18
|
+
def render(self, width: int) -> list[str]: # noqa: ARG002
|
|
19
|
+
from tau.tui.ansi import DIM, RESET
|
|
20
|
+
return [DIM + self._text + RESET]
|
|
21
|
+
|
|
22
|
+
def handle_input(self, event: object) -> bool: # noqa: ARG002
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
def invalidate(self) -> None:
|
|
26
|
+
pass
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"""Model + context-usage badge component."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ModelBadge:
|
|
6
|
+
"""Renders ``(provider) model ∙ Level|context%`` for the footer Row right slot.
|
|
7
|
+
|
|
8
|
+
The ``∙ Level`` segment only appears when the active model supports
|
|
9
|
+
extended thinking and a thinking level is set.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
def __init__(self) -> None:
|
|
13
|
+
self._provider = ""
|
|
14
|
+
self._model = ""
|
|
15
|
+
self._tokens = 0
|
|
16
|
+
self._context_window = 0
|
|
17
|
+
self._thinking = False
|
|
18
|
+
self._thinking_level = ""
|
|
19
|
+
|
|
20
|
+
def set_model(self, model_id: str, provider: str, thinking: bool = False) -> None:
|
|
21
|
+
self._model = model_id
|
|
22
|
+
self._provider = provider
|
|
23
|
+
self._thinking = thinking
|
|
24
|
+
|
|
25
|
+
def set_thinking_level(self, level: object) -> None:
|
|
26
|
+
self._thinking_level = str(getattr(level, "value", level) or "")
|
|
27
|
+
|
|
28
|
+
def set_context(self, tokens: int, context_window: int) -> None:
|
|
29
|
+
self._tokens = tokens
|
|
30
|
+
self._context_window = context_window
|
|
31
|
+
|
|
32
|
+
def update_from_ctx(self, ctx: object) -> None:
|
|
33
|
+
self.set_model(
|
|
34
|
+
getattr(ctx, "model_id", "") or "",
|
|
35
|
+
getattr(ctx, "provider_id", "") or "",
|
|
36
|
+
bool(getattr(ctx, "model_thinking", False)),
|
|
37
|
+
)
|
|
38
|
+
settings = getattr(ctx, "settings", None)
|
|
39
|
+
if settings is not None:
|
|
40
|
+
self.set_thinking_level(settings.get_thinking_level())
|
|
41
|
+
self.update_context_from_ctx(ctx)
|
|
42
|
+
|
|
43
|
+
def update_context_from_ctx(self, ctx: object) -> None:
|
|
44
|
+
usage = getattr(ctx, "get_context_usage", lambda: None)()
|
|
45
|
+
if usage is not None:
|
|
46
|
+
tokens = usage.get("tokens") or 0
|
|
47
|
+
window = usage.get("context_window") or 0
|
|
48
|
+
self.set_context(tokens, window)
|
|
49
|
+
|
|
50
|
+
def render(self, width: int) -> list[str]: # noqa: ARG002
|
|
51
|
+
from tau.tui.ansi import DIM, RESET
|
|
52
|
+
if not self._provider and not self._model:
|
|
53
|
+
return []
|
|
54
|
+
|
|
55
|
+
left = f"({self._provider}) {self._model}" if self._provider else self._model
|
|
56
|
+
if self._thinking and self._thinking_level and self._thinking_level != "off":
|
|
57
|
+
left += f" ∙ {self._thinking_level.title()}"
|
|
58
|
+
|
|
59
|
+
if self._context_window > 0 and self._tokens > 0:
|
|
60
|
+
pct = self._tokens / self._context_window * 100
|
|
61
|
+
label = f"{pct:.1f}%" if pct < 1 else f"{int(round(pct))}%"
|
|
62
|
+
return [DIM + f"{left}|{label}" + RESET]
|
|
63
|
+
return [DIM + left + RESET]
|
|
64
|
+
|
|
65
|
+
def handle_input(self, event: object) -> bool: # noqa: ARG002
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
def invalidate(self) -> None:
|
|
69
|
+
pass
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Shared helpers for the status extension."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def read_branch(cwd: object) -> str:
|
|
6
|
+
"""Return the current git branch name for the given directory, or ''."""
|
|
7
|
+
import os
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
try:
|
|
10
|
+
path = Path(str(cwd))
|
|
11
|
+
for candidate in [path, *path.parents]:
|
|
12
|
+
git = candidate / ".git"
|
|
13
|
+
if git.is_dir():
|
|
14
|
+
head = git / "HEAD"
|
|
15
|
+
elif git.is_file():
|
|
16
|
+
content = git.read_text(encoding="utf-8").strip()
|
|
17
|
+
if not content.startswith("gitdir: "):
|
|
18
|
+
continue
|
|
19
|
+
gitdir = os.path.normpath(
|
|
20
|
+
os.path.join(str(candidate), content[8:].strip())
|
|
21
|
+
)
|
|
22
|
+
head = Path(gitdir) / "HEAD"
|
|
23
|
+
else:
|
|
24
|
+
continue
|
|
25
|
+
if not head.is_file():
|
|
26
|
+
continue
|
|
27
|
+
text = head.read_text(encoding="utf-8").strip()
|
|
28
|
+
if text.startswith("ref: refs/heads/"):
|
|
29
|
+
return text[len("ref: refs/heads/"):]
|
|
30
|
+
return text[:7]
|
|
31
|
+
return ""
|
|
32
|
+
except OSError:
|
|
33
|
+
return ""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def shorten_home(path: str) -> str:
|
|
37
|
+
"""Replace the home directory prefix with ``~``."""
|
|
38
|
+
import os
|
|
39
|
+
home = os.path.expanduser("~")
|
|
40
|
+
if path == home:
|
|
41
|
+
return "~"
|
|
42
|
+
if path.startswith(home + os.sep):
|
|
43
|
+
return "~" + path[len(home):]
|
|
44
|
+
return path
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Built-in header extension — shows app name and version above the message list."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def register(tau: object) -> None:
|
|
6
|
+
from tau.settings.paths import get_app_name, get_app_version
|
|
7
|
+
from tau.tui.ansi import BOLD, CYAN, DIM, RESET
|
|
8
|
+
from tau.tui.component import StaticComponent
|
|
9
|
+
|
|
10
|
+
def _build() -> StaticComponent:
|
|
11
|
+
name = BOLD + CYAN + get_app_name() + RESET
|
|
12
|
+
version = DIM + f"v{get_app_version()}" + RESET
|
|
13
|
+
return StaticComponent([f"{name} {version}"])
|
|
14
|
+
|
|
15
|
+
@tau.on("tui_ready")
|
|
16
|
+
def on_ready(event, ctx):
|
|
17
|
+
if ctx.has_ui:
|
|
18
|
+
ctx.ui.set_header(_build())
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from tau.inference.model.types import Cost, Model, Modality
|
|
2
|
+
|
|
3
|
+
_TEXT = [Modality.Text]
|
|
4
|
+
_AUDIO = [Modality.Audio]
|
|
5
|
+
|
|
6
|
+
_OPENAI_VOICES = ["alloy", "ash", "ballad", "coral", "echo", "fable", "nova", "onyx", "sage", "shimmer", "verse"]
|
|
7
|
+
_GROQ_VOICES = ["autumn", "diana", "hannah", "austin", "daniel", "troy"]
|
|
8
|
+
|
|
9
|
+
models = [
|
|
10
|
+
# OpenAI TTS
|
|
11
|
+
Model(id="tts-1", name="TTS-1", provider="openai", cost=Cost(input=15.0), input=_TEXT, output=_AUDIO, api="openai-audio", voices=_OPENAI_VOICES),
|
|
12
|
+
Model(id="tts-1-hd", name="TTS-1 HD", provider="openai", cost=Cost(input=30.0), input=_TEXT, output=_AUDIO, api="openai-audio", voices=_OPENAI_VOICES),
|
|
13
|
+
Model(id="gpt-4o-mini-tts", name="GPT-4o Mini TTS", provider="openai", cost=Cost(input=0.60), input=_TEXT, output=_AUDIO, api="openai-audio", voices=_OPENAI_VOICES),
|
|
14
|
+
# OpenAI STT
|
|
15
|
+
Model(id="whisper-1", name="Whisper 1", provider="openai", cost=Cost(input=0.006), input=_AUDIO, output=_TEXT, api="openai-audio"),
|
|
16
|
+
Model(id="gpt-4o-transcribe", name="GPT-4o Transcribe", provider="openai", cost=Cost(input=2.5), input=_AUDIO, output=_TEXT, api="openai-audio"),
|
|
17
|
+
Model(id="gpt-4o-mini-transcribe", name="GPT-4o Mini Transcribe", provider="openai", cost=Cost(input=0.003), input=_AUDIO, output=_TEXT, api="openai-audio"),
|
|
18
|
+
# Google Gemini TTS
|
|
19
|
+
Model(id="gemini-2.5-flash-preview-tts", name="Gemini 2.5 Flash TTS", provider="google", cost=Cost(input=0.50, output=10.0), input=_TEXT, output=_AUDIO, api="gemini-audio"),
|
|
20
|
+
Model(id="gemini-2.5-pro-preview-tts", name="Gemini 2.5 Pro TTS", provider="google", cost=Cost(input=2.00, output=16.0), input=_TEXT, output=_AUDIO, api="gemini-audio"),
|
|
21
|
+
Model(id="gemini-3.1-flash-tts-preview", name="Gemini 3.1 Flash TTS", provider="google", cost=Cost(input=0.10, output=1.00), input=_TEXT, output=_AUDIO, api="gemini-audio"),
|
|
22
|
+
# OpenRouter speech models
|
|
23
|
+
Model(id="openai/gpt-audio", name="GPT Audio", provider="openrouter", cost=Cost(input=2.50, output=10.00), input=_TEXT, output=_AUDIO, api="openai-audio"),
|
|
24
|
+
Model(id="openai/gpt-audio-mini", name="GPT Audio Mini", provider="openrouter", cost=Cost(input=0.60, output=2.40), input=_TEXT, output=_AUDIO, api="openai-audio"),
|
|
25
|
+
Model(id="openai/gpt-4o-audio-preview", name="GPT-4o Audio", provider="openrouter", cost=Cost(input=2.50, output=10.00), input=_TEXT, output=_AUDIO, api="openai-audio"),
|
|
26
|
+
Model(id="google/lyria-3-pro-preview", name="Lyria 3 Pro Preview", provider="openrouter", cost=Cost(), input=_TEXT, output=_AUDIO, api="openai-audio"),
|
|
27
|
+
Model(id="google/lyria-3-clip-preview", name="Lyria 3 Clip Preview", provider="openrouter", cost=Cost(), input=_TEXT, output=_AUDIO, api="openai-audio"),
|
|
28
|
+
# Sarvam AI
|
|
29
|
+
Model(id="bulbul:v3", name="Bulbul v3", provider="sarvam", cost=Cost(), input=_TEXT, output=_AUDIO, api="sarvam-audio"),
|
|
30
|
+
Model(id="saarika:v2.5", name="Saarika v2.5", provider="sarvam", cost=Cost(), input=_AUDIO, output=_TEXT, api="sarvam-audio"),
|
|
31
|
+
Model(id="saaras:v3", name="Saaras v3", provider="sarvam", cost=Cost(), input=_AUDIO, output=_TEXT, api="sarvam-audio"),
|
|
32
|
+
# ElevenLabs
|
|
33
|
+
Model(id="eleven_multilingual_v2", name="Eleven Multilingual v2", provider="elevenlabs", cost=Cost(input=0.30), input=_TEXT, output=_AUDIO, api="elevenlabs-audio"),
|
|
34
|
+
Model(id="eleven_flash_v2_5", name="Eleven Flash v2.5", provider="elevenlabs", cost=Cost(input=0.08), input=_TEXT, output=_AUDIO, api="elevenlabs-audio"),
|
|
35
|
+
Model(id="eleven_turbo_v2_5", name="Eleven Turbo v2.5", provider="elevenlabs", cost=Cost(input=0.15), input=_TEXT, output=_AUDIO, api="elevenlabs-audio"),
|
|
36
|
+
Model(id="scribe_v1", name="Scribe v1", provider="elevenlabs", cost=Cost(input=0.40), input=_AUDIO, output=_TEXT, api="elevenlabs-audio"),
|
|
37
|
+
Model(id="scribe_v2", name="Scribe v2", provider="elevenlabs", cost=Cost(input=0.40), input=_AUDIO, output=_TEXT, api="elevenlabs-audio"),
|
|
38
|
+
# Groq TTS
|
|
39
|
+
Model(id="canopylabs/orpheus-v1-english", name="Orpheus v1 English", provider="groq", cost=Cost(), input=_TEXT, output=_AUDIO, api="openai-audio", voices=_GROQ_VOICES, tts_format="wav"),
|
|
40
|
+
# Groq STT
|
|
41
|
+
Model(id="whisper-large-v3", name="Whisper Large v3", provider="groq", cost=Cost(input=0.111), input=_AUDIO, output=_TEXT, api="openai-audio"),
|
|
42
|
+
Model(id="whisper-large-v3-turbo", name="Whisper Large v3 Turbo", provider="groq", cost=Cost(input=0.04), input=_AUDIO, output=_TEXT, api="openai-audio"),
|
|
43
|
+
]
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from tau.inference.model.types import Cost, Model, Modality
|
|
2
|
+
|
|
3
|
+
_TEXT = [Modality.Text]
|
|
4
|
+
_IMAGE = [Modality.Image]
|
|
5
|
+
_TEXT_IMAGE = [Modality.Text, Modality.Image]
|
|
6
|
+
_TEXT_IMAGE_OUT = [Modality.Text, Modality.Image]
|
|
7
|
+
|
|
8
|
+
models = [
|
|
9
|
+
# OpenAI DALL-E
|
|
10
|
+
Model(id="dall-e-3", name="DALL-E 3", provider="openai", cost=Cost(input=40.0), input=_TEXT, output=_IMAGE, api="openai-image"),
|
|
11
|
+
Model(id="dall-e-2", name="DALL-E 2", provider="openai", cost=Cost(input=20.0), input=_TEXT, output=_IMAGE, api="openai-image"),
|
|
12
|
+
# Together AI
|
|
13
|
+
Model(id="black-forest-labs/FLUX.1-schnell-Free", name="FLUX.1 Schnell Free", provider="together", cost=Cost(), input=_TEXT, output=_IMAGE, api="openai-image"),
|
|
14
|
+
Model(id="black-forest-labs/FLUX.1-schnell", name="FLUX.1 Schnell", provider="together", cost=Cost(input=0.053), input=_TEXT, output=_IMAGE, api="openai-image"),
|
|
15
|
+
Model(id="black-forest-labs/FLUX.1-dev", name="FLUX.1 Dev", provider="together", cost=Cost(input=0.35), input=_TEXT, output=_IMAGE, api="openai-image"),
|
|
16
|
+
Model(id="black-forest-labs/FLUX.1.1-pro", name="FLUX.1.1 Pro", provider="together", cost=Cost(input=0.40), input=_TEXT, output=_IMAGE, api="openai-image"),
|
|
17
|
+
# Fireworks AI
|
|
18
|
+
Model(id="accounts/fireworks/models/flux-1-schnell-fp8", name="FLUX.1 Schnell FP8", provider="fireworks", cost=Cost(input=0.053), input=_TEXT, output=_IMAGE, api="openai-image"),
|
|
19
|
+
Model(id="accounts/fireworks/models/flux-1-dev-fp8", name="FLUX.1 Dev FP8", provider="fireworks", cost=Cost(input=0.35), input=_TEXT, output=_IMAGE, api="openai-image"),
|
|
20
|
+
# Black Forest Labs FLUX via OpenRouter
|
|
21
|
+
Model(id="black-forest-labs/flux-2-flex", name="FLUX.2 Flex", provider="openrouter", cost=Cost(), input=_TEXT_IMAGE, output=_IMAGE),
|
|
22
|
+
Model(id="black-forest-labs/flux-2-klein", name="FLUX.2 Klein 4B", provider="openrouter", cost=Cost(), input=_TEXT_IMAGE, output=_IMAGE),
|
|
23
|
+
Model(id="black-forest-labs/flux-2-max", name="FLUX.2 Max", provider="openrouter", cost=Cost(), input=_TEXT_IMAGE, output=_IMAGE),
|
|
24
|
+
Model(id="black-forest-labs/flux-2-pro", name="FLUX.2 Pro", provider="openrouter", cost=Cost(), input=_TEXT_IMAGE, output=_IMAGE),
|
|
25
|
+
# Google Imagen (native gemini-image API)
|
|
26
|
+
Model(id="imagen-3.0-generate-002", name="Imagen 3", provider="google", cost=Cost(input=0.04), input=_TEXT, output=_IMAGE, api="gemini-image"),
|
|
27
|
+
Model(id="imagen-3.0-fast-generate-001", name="Imagen 3 Fast", provider="google", cost=Cost(input=0.02), input=_TEXT, output=_IMAGE, api="gemini-image"),
|
|
28
|
+
# Google Gemini Image via OpenRouter
|
|
29
|
+
Model(id="google/gemini-2.5-flash-image-generation", name="Gemini 2.5 Flash Image", provider="openrouter", cost=Cost(input=0.30, output=2.50), input=_TEXT_IMAGE, output=_TEXT_IMAGE_OUT),
|
|
30
|
+
Model(id="google/gemini-3-pro-image-generation-preview", name="Gemini 3 Pro Image Preview", provider="openrouter", cost=Cost(input=2.00, output=12.00), input=_TEXT_IMAGE, output=_TEXT_IMAGE_OUT),
|
|
31
|
+
Model(id="google/gemini-3.1-flash-image-generation-preview", name="Gemini 3.1 Flash Image Preview", provider="openrouter", cost=Cost(input=0.50, output=3.00), input=_TEXT_IMAGE, output=_TEXT_IMAGE_OUT),
|
|
32
|
+
# OpenAI GPT Image via OpenRouter
|
|
33
|
+
Model(id="openai/gpt-5-image", name="GPT-5 Image", provider="openrouter", cost=Cost(input=10.00, output=10.00), input=_TEXT_IMAGE, output=_TEXT_IMAGE_OUT),
|
|
34
|
+
Model(id="openai/gpt-5-image-mini", name="GPT-5 Image Mini", provider="openrouter", cost=Cost(input=2.50, output=2.00), input=_TEXT_IMAGE, output=_TEXT_IMAGE_OUT),
|
|
35
|
+
Model(id="openai/gpt-5.4-image-2", name="GPT-5.4 Image 2", provider="openrouter", cost=Cost(input=8.00, output=15.00), input=_TEXT_IMAGE, output=_TEXT_IMAGE_OUT),
|
|
36
|
+
# ByteDance
|
|
37
|
+
Model(id="bytedance/seedream-4.5", name="Seedream 4.5", provider="openrouter", cost=Cost(), input=_TEXT_IMAGE, output=_IMAGE),
|
|
38
|
+
# Sourceful Riverflow via OpenRouter
|
|
39
|
+
Model(id="sourceful/riverflow-v2", name="Riverflow V2", provider="openrouter", cost=Cost(), input=_TEXT_IMAGE, output=_IMAGE),
|
|
40
|
+
Model(id="sourceful/riverflow-v2-turbo", name="Riverflow V2 Turbo", provider="openrouter", cost=Cost(), input=_TEXT_IMAGE, output=_IMAGE),
|
|
41
|
+
Model(id="sourceful/riverflow-v2-max", name="Riverflow V2 Max", provider="openrouter", cost=Cost(), input=_TEXT_IMAGE, output=_IMAGE),
|
|
42
|
+
Model(id="sourceful/riverflow-v2-pro", name="Riverflow V2 Pro", provider="openrouter", cost=Cost(), input=_TEXT_IMAGE, output=_IMAGE),
|
|
43
|
+
]
|