vtx-coding-agent 0.1.1__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 (117) hide show
  1. vtx/__init__.py +63 -0
  2. vtx/async_utils.py +40 -0
  3. vtx/builtin_skills/github/SKILL.md +139 -0
  4. vtx/builtin_skills/init/SKILL.md +74 -0
  5. vtx/builtin_skills/review/SKILL.md +73 -0
  6. vtx/builtin_skills/skill-builder/SKILL.md +133 -0
  7. vtx/cli.py +90 -0
  8. vtx/config.py +741 -0
  9. vtx/context/__init__.py +15 -0
  10. vtx/context/_xml.py +8 -0
  11. vtx/context/agent_mds.py +128 -0
  12. vtx/context/git.py +64 -0
  13. vtx/context/loader.py +41 -0
  14. vtx/context/skills.py +423 -0
  15. vtx/core/__init__.py +47 -0
  16. vtx/core/compaction.py +89 -0
  17. vtx/core/errors.py +17 -0
  18. vtx/core/handoff.py +51 -0
  19. vtx/core/scratchpad.py +54 -0
  20. vtx/core/types.py +197 -0
  21. vtx/defaults/__init__.py +0 -0
  22. vtx/defaults/config.yml +53 -0
  23. vtx/diff_display.py +12 -0
  24. vtx/events.py +224 -0
  25. vtx/gh_cli.py +82 -0
  26. vtx/git_branch.py +90 -0
  27. vtx/headless.py +127 -0
  28. vtx/llm/__init__.py +93 -0
  29. vtx/llm/base.py +217 -0
  30. vtx/llm/context_length.py +150 -0
  31. vtx/llm/dynamic_models.py +735 -0
  32. vtx/llm/model_fetcher.py +279 -0
  33. vtx/llm/models.py +78 -0
  34. vtx/llm/oauth/__init__.py +59 -0
  35. vtx/llm/oauth/copilot.py +358 -0
  36. vtx/llm/oauth/dynamic.py +236 -0
  37. vtx/llm/oauth/openai.py +400 -0
  38. vtx/llm/phase_parser.py +270 -0
  39. vtx/llm/provider.yaml +280 -0
  40. vtx/llm/provider_catalog.py +230 -0
  41. vtx/llm/providers/__init__.py +45 -0
  42. vtx/llm/providers/anthropic_sdk.py +256 -0
  43. vtx/llm/providers/mock.py +249 -0
  44. vtx/llm/providers/openai_sdk.py +246 -0
  45. vtx/llm/providers/sanitize.py +14 -0
  46. vtx/llm/sdk/__init__.py +13 -0
  47. vtx/llm/sdk/anthropic.py +382 -0
  48. vtx/llm/sdk/base.py +82 -0
  49. vtx/llm/sdk/openai.py +344 -0
  50. vtx/llm/tool_parser.py +161 -0
  51. vtx/loop.py +272 -0
  52. vtx/notify.py +109 -0
  53. vtx/permissions.py +114 -0
  54. vtx/prompts/__init__.py +45 -0
  55. vtx/prompts/builder.py +86 -0
  56. vtx/prompts/env.py +58 -0
  57. vtx/prompts/identity.py +166 -0
  58. vtx/prompts/tooling.py +36 -0
  59. vtx/py.typed +0 -0
  60. vtx/runtime.py +580 -0
  61. vtx/session.py +868 -0
  62. vtx/sounds/completion.wav +0 -0
  63. vtx/sounds/error.wav +0 -0
  64. vtx/sounds/permission.wav +0 -0
  65. vtx/themes.py +1104 -0
  66. vtx/tools/__init__.py +68 -0
  67. vtx/tools/_read_image.py +106 -0
  68. vtx/tools/_tool_utils.py +90 -0
  69. vtx/tools/base.py +36 -0
  70. vtx/tools/bash.py +371 -0
  71. vtx/tools/edit.py +261 -0
  72. vtx/tools/find.py +132 -0
  73. vtx/tools/read.py +238 -0
  74. vtx/tools/skill.py +278 -0
  75. vtx/tools/web.py +238 -0
  76. vtx/tools/write.py +88 -0
  77. vtx/tools_manager.py +216 -0
  78. vtx/turn.py +789 -0
  79. vtx/ui/__init__.py +0 -0
  80. vtx/ui/agent_runner.py +417 -0
  81. vtx/ui/app.py +665 -0
  82. vtx/ui/app_protocol.py +29 -0
  83. vtx/ui/autocomplete.py +440 -0
  84. vtx/ui/blocks.py +735 -0
  85. vtx/ui/chat.py +613 -0
  86. vtx/ui/clipboard.py +59 -0
  87. vtx/ui/commands/__init__.py +100 -0
  88. vtx/ui/commands/auth.py +306 -0
  89. vtx/ui/commands/base.py +122 -0
  90. vtx/ui/commands/models.py +144 -0
  91. vtx/ui/commands/sessions.py +388 -0
  92. vtx/ui/commands/settings.py +286 -0
  93. vtx/ui/completion_ui.py +313 -0
  94. vtx/ui/export.py +703 -0
  95. vtx/ui/floating_list.py +370 -0
  96. vtx/ui/formatting.py +287 -0
  97. vtx/ui/input.py +760 -0
  98. vtx/ui/latex.py +349 -0
  99. vtx/ui/launch.py +108 -0
  100. vtx/ui/path_complete.py +228 -0
  101. vtx/ui/prompt_history.py +102 -0
  102. vtx/ui/queue_ui.py +141 -0
  103. vtx/ui/selection_mode.py +18 -0
  104. vtx/ui/session_ui.py +235 -0
  105. vtx/ui/startup.py +124 -0
  106. vtx/ui/styles.py +327 -0
  107. vtx/ui/tool_output.py +34 -0
  108. vtx/ui/tree.py +437 -0
  109. vtx/ui/welcome.py +51 -0
  110. vtx/ui/widgets.py +558 -0
  111. vtx/update_check.py +49 -0
  112. vtx/version.py +22 -0
  113. vtx_coding_agent-0.1.1.dist-info/METADATA +259 -0
  114. vtx_coding_agent-0.1.1.dist-info/RECORD +117 -0
  115. vtx_coding_agent-0.1.1.dist-info/WHEEL +4 -0
  116. vtx_coding_agent-0.1.1.dist-info/entry_points.txt +2 -0
  117. vtx_coding_agent-0.1.1.dist-info/licenses/LICENSE +201 -0
vtx/events.py ADDED
@@ -0,0 +1,224 @@
1
+ import asyncio
2
+ from dataclasses import dataclass, field
3
+ from typing import Any, Literal
4
+
5
+ from .core.types import AssistantMessage, FileChanges, StopReason, ToolResultMessage, Usage
6
+ from .permissions import ApprovalResponse
7
+
8
+ # =================================================================================================
9
+ # Agent Lifecycle Events
10
+ # =================================================================================================
11
+
12
+
13
+ @dataclass
14
+ class AgentStartEvent:
15
+ type: Literal["agent_start"] = "agent_start"
16
+
17
+
18
+ @dataclass
19
+ class AgentEndEvent:
20
+ type: Literal["agent_end"] = "agent_end"
21
+ stop_reason: StopReason = StopReason.STOP
22
+ total_turns: int = 0
23
+ total_usage: Usage | None = None
24
+
25
+
26
+ # =================================================================================================
27
+ # Turn Lifecycle Events
28
+ # =================================================================================================
29
+
30
+
31
+ @dataclass
32
+ class TurnStartEvent:
33
+ type: Literal["turn_start"] = "turn_start"
34
+ turn: int = 0
35
+
36
+
37
+ @dataclass
38
+ class TurnEndEvent:
39
+ type: Literal["turn_end"] = "turn_end"
40
+ turn: int = 0
41
+ assistant_message: AssistantMessage | None = None
42
+ tool_results: list[ToolResultMessage] = field(default_factory=list)
43
+ stop_reason: StopReason = StopReason.STOP
44
+ tool_call_count: int = 0
45
+
46
+
47
+ # =================================================================================================
48
+ # Content Streaming Events
49
+ # =================================================================================================
50
+
51
+
52
+ @dataclass
53
+ class ThinkingStartEvent:
54
+ type: Literal["thinking_start"] = "thinking_start"
55
+
56
+
57
+ @dataclass
58
+ class ThinkingDeltaEvent:
59
+ type: Literal["thinking_delta"] = "thinking_delta"
60
+ delta: str = ""
61
+
62
+
63
+ @dataclass
64
+ class ThinkingEndEvent:
65
+ type: Literal["thinking_end"] = "thinking_end"
66
+ thinking: str = ""
67
+ signature: str | None = None
68
+
69
+
70
+ @dataclass
71
+ class TextStartEvent:
72
+ type: Literal["text_start"] = "text_start"
73
+
74
+
75
+ @dataclass
76
+ class TextDeltaEvent:
77
+ type: Literal["text_delta"] = "text_delta"
78
+ delta: str = ""
79
+
80
+
81
+ @dataclass
82
+ class TextEndEvent:
83
+ type: Literal["text_end"] = "text_end"
84
+ text: str = ""
85
+
86
+
87
+ # =================================================================================================
88
+ # Tool Events
89
+ # =================================================================================================
90
+
91
+
92
+ @dataclass
93
+ class ToolStartEvent:
94
+ type: Literal["tool_start"] = "tool_start"
95
+ tool_call_id: str = ""
96
+ tool_name: str = ""
97
+
98
+
99
+ @dataclass
100
+ class ToolArgsDeltaEvent:
101
+ type: Literal["tool_args_delta"] = "tool_args_delta"
102
+ tool_call_id: str = ""
103
+ delta: str = ""
104
+
105
+
106
+ @dataclass
107
+ class ToolArgsTokenUpdateEvent:
108
+ type: Literal["tool_args_token_update"] = "tool_args_token_update"
109
+ tool_call_id: str = ""
110
+ tool_name: str = ""
111
+ token_count: int = 0
112
+
113
+
114
+ @dataclass
115
+ class ToolEndEvent:
116
+ type: Literal["tool_end"] = "tool_end"
117
+ tool_call_id: str = ""
118
+ tool_name: str = ""
119
+ arguments: dict[str, Any] = field(default_factory=dict)
120
+ display: str = "" # Formatted display string from tool.format_call()
121
+
122
+
123
+ @dataclass
124
+ class ToolResultEvent:
125
+ type: Literal["tool_result"] = "tool_result"
126
+ tool_call_id: str = ""
127
+ tool_name: str = ""
128
+ result: ToolResultMessage | None = None
129
+ file_changes: FileChanges | None = None
130
+
131
+
132
+ @dataclass
133
+ class ToolApprovalEvent:
134
+ type: Literal["tool_approval"] = "tool_approval"
135
+ tool_call_id: str = ""
136
+ tool_name: str = ""
137
+ display: str = ""
138
+ future: asyncio.Future[ApprovalResponse] | None = None
139
+
140
+
141
+ # =================================================================================================
142
+ # Compaction Events
143
+ # =================================================================================================
144
+
145
+
146
+ @dataclass
147
+ class CompactionStartEvent:
148
+ type: Literal["compaction_start"] = "compaction_start"
149
+
150
+
151
+ @dataclass
152
+ class CompactionEndEvent:
153
+ type: Literal["compaction_end"] = "compaction_end"
154
+ tokens_before: int = 0
155
+ aborted: bool = False
156
+ reason: str = "" # why compaction aborted, empty on success
157
+
158
+
159
+ # =================================================================================================
160
+ # Other Events
161
+ # =================================================================================================
162
+
163
+
164
+ @dataclass
165
+ class RetryEvent:
166
+ type: Literal["retry"] = "retry"
167
+ attempt: int = 0
168
+ total_attempts: int = 3
169
+ delay: float = 0.0
170
+ error: str = ""
171
+
172
+
173
+ @dataclass
174
+ class ErrorEvent:
175
+ type: Literal["error"] = "error"
176
+ error: str = ""
177
+
178
+
179
+ @dataclass
180
+ class WarningEvent:
181
+ type: Literal["warning"] = "warning"
182
+ warning: str = ""
183
+
184
+
185
+ @dataclass
186
+ class InterruptedEvent:
187
+ type: Literal["interrupted"] = "interrupted"
188
+ message: str = "Interrupted by user"
189
+
190
+
191
+ # =================================================================================================
192
+ # Union Types
193
+ # =================================================================================================
194
+
195
+ # Events yielded by run_single_turn (turn.py)
196
+ StreamEvent = (
197
+ ThinkingStartEvent
198
+ | ThinkingDeltaEvent
199
+ | ThinkingEndEvent
200
+ | TextStartEvent
201
+ | TextDeltaEvent
202
+ | TextEndEvent
203
+ | ToolStartEvent
204
+ | ToolArgsDeltaEvent
205
+ | ToolArgsTokenUpdateEvent
206
+ | ToolEndEvent
207
+ | ToolResultEvent
208
+ | ToolApprovalEvent
209
+ | RetryEvent
210
+ | TurnEndEvent
211
+ | ErrorEvent
212
+ | WarningEvent
213
+ | InterruptedEvent
214
+ )
215
+
216
+ # All events yielded by Agent.run() (loop.py)
217
+ Event = (
218
+ AgentStartEvent
219
+ | AgentEndEvent
220
+ | TurnStartEvent
221
+ | CompactionStartEvent
222
+ | CompactionEndEvent
223
+ | StreamEvent
224
+ )
vtx/gh_cli.py ADDED
@@ -0,0 +1,82 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import subprocess
5
+ import time
6
+ from dataclasses import dataclass
7
+
8
+ from vtx import AVAILABLE_BINARIES
9
+
10
+
11
+ @dataclass(frozen=True)
12
+ class PullRequest:
13
+ number: int
14
+ branch: str
15
+ title: str
16
+
17
+ def chat_reference(self) -> str:
18
+ description = _single_line_description(self.title)
19
+ return f'PR#{self.number} {self.branch} "{description}"'
20
+
21
+
22
+ def _single_line_description(description: str) -> str:
23
+ lines = description.splitlines()
24
+ first_line = lines[0].strip() if lines else ""
25
+ hidden_count = len(lines) - 1
26
+ if hidden_count <= 0:
27
+ return first_line
28
+ return f"{first_line} ... ({hidden_count} lines hidden)"
29
+
30
+
31
+ _CACHE_TTL_SECONDS = 30.0
32
+ _cached_cwd: str | None = None
33
+ _cached_at: float = 0.0
34
+ _cached_prs: list[PullRequest] = []
35
+
36
+
37
+ def is_available() -> bool:
38
+ return "gh" in AVAILABLE_BINARIES
39
+
40
+
41
+ def list_pull_requests(cwd: str = ".") -> list[PullRequest]:
42
+ global _cached_at, _cached_cwd, _cached_prs
43
+
44
+ if not is_available():
45
+ return []
46
+
47
+ now = time.monotonic()
48
+ if _cached_cwd == cwd and now - _cached_at < _CACHE_TTL_SECONDS:
49
+ return _cached_prs
50
+
51
+ try:
52
+ result = subprocess.run(
53
+ ["gh", "pr", "list", "--json", "number,headRefName,title", "--limit", "50"],
54
+ cwd=cwd,
55
+ capture_output=True,
56
+ text=True,
57
+ timeout=2.0,
58
+ )
59
+ except Exception:
60
+ return []
61
+
62
+ if result.returncode != 0:
63
+ return []
64
+
65
+ try:
66
+ raw_prs = json.loads(result.stdout)
67
+ except json.JSONDecodeError:
68
+ return []
69
+
70
+ prs = [
71
+ PullRequest(
72
+ number=int(pr["number"]),
73
+ branch=str(pr.get("headRefName") or ""),
74
+ title=str(pr.get("title") or ""),
75
+ )
76
+ for pr in raw_prs
77
+ if "number" in pr
78
+ ]
79
+ _cached_cwd = cwd
80
+ _cached_at = now
81
+ _cached_prs = prs
82
+ return prs
vtx/git_branch.py ADDED
@@ -0,0 +1,90 @@
1
+ import os
2
+ import subprocess
3
+ from dataclasses import dataclass
4
+
5
+
6
+ @dataclass(frozen=True)
7
+ class GitPaths:
8
+ repo_dir: str
9
+ common_git_dir: str
10
+ head_path: str
11
+
12
+
13
+ def find_git_paths(cwd: str) -> GitPaths | None:
14
+ """Find git metadata paths for regular repos and worktrees."""
15
+ directory = os.path.abspath(cwd)
16
+ while True:
17
+ git_path = os.path.join(directory, ".git")
18
+ if os.path.exists(git_path):
19
+ try:
20
+ if os.path.isfile(git_path):
21
+ with open(git_path, encoding="utf-8") as f:
22
+ content = f.read().strip()
23
+ if content.startswith("gitdir: "):
24
+ git_dir = os.path.abspath(
25
+ os.path.join(directory, content.removeprefix("gitdir: ").strip())
26
+ )
27
+ head_path = os.path.join(git_dir, "HEAD")
28
+ if not os.path.exists(head_path):
29
+ return None
30
+ common_dir_path = os.path.join(git_dir, "commondir")
31
+ if os.path.exists(common_dir_path):
32
+ with open(common_dir_path, encoding="utf-8") as f:
33
+ common_dir = f.read().strip()
34
+ common_git_dir = os.path.abspath(os.path.join(git_dir, common_dir))
35
+ else:
36
+ common_git_dir = git_dir
37
+ return GitPaths(directory, common_git_dir, head_path)
38
+ elif os.path.isdir(git_path):
39
+ head_path = os.path.join(git_path, "HEAD")
40
+ if not os.path.exists(head_path):
41
+ return None
42
+ return GitPaths(directory, git_path, head_path)
43
+ except OSError:
44
+ return None
45
+
46
+ parent = os.path.dirname(directory)
47
+ if parent == directory:
48
+ return None
49
+ directory = parent
50
+
51
+
52
+ def _resolve_branch_with_git(repo_dir: str) -> str | None:
53
+ try:
54
+ result = subprocess.run(
55
+ ["git", "--no-optional-locks", "symbolic-ref", "--quiet", "--short", "HEAD"],
56
+ cwd=repo_dir,
57
+ capture_output=True,
58
+ text=True,
59
+ timeout=1,
60
+ check=False,
61
+ )
62
+ except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
63
+ return None
64
+
65
+ if result.returncode != 0:
66
+ return None
67
+ branch = result.stdout.strip()
68
+ return branch or None
69
+
70
+
71
+ def resolve_git_branch(cwd: str) -> str:
72
+ """Resolve current git branch, returning an empty string outside git repos."""
73
+ git_paths = find_git_paths(cwd)
74
+ if git_paths is None:
75
+ return ""
76
+
77
+ try:
78
+ with open(git_paths.head_path, encoding="utf-8") as f:
79
+ content = f.read().strip()
80
+ except OSError:
81
+ return ""
82
+
83
+ prefix = "ref: refs/heads/"
84
+ if content.startswith(prefix):
85
+ branch = content.removeprefix(prefix)
86
+ if branch == ".invalid":
87
+ return _resolve_branch_with_git(git_paths.repo_dir) or "detached"
88
+ return branch
89
+
90
+ return "detached"
vtx/headless.py ADDED
@@ -0,0 +1,127 @@
1
+ import os
2
+ import sys
3
+ from collections.abc import AsyncIterator
4
+ from typing import TextIO
5
+
6
+ from vtx import config, get_config
7
+ from vtx.config import get_last_selected
8
+
9
+ from .core.types import StopReason, TextContent
10
+ from .events import AgentEndEvent, ErrorEvent, Event, ToolApprovalEvent, TurnEndEvent
11
+ from .llm.base import AuthMode
12
+ from .permissions import ApprovalResponse
13
+ from .runtime import ConversationRuntime
14
+ from .tools import DEFAULT_TOOLS, get_tools
15
+
16
+ _EXIT_CODES = {StopReason.STOP: 0, StopReason.ERROR: 1, StopReason.LENGTH: 3}
17
+
18
+
19
+ def _exit_code(stop: StopReason) -> int:
20
+ return _EXIT_CODES.get(stop, 1)
21
+
22
+
23
+ def resolve_prompt(prompt_arg: str, *, stdin: TextIO) -> str:
24
+ if prompt_arg == "-":
25
+ return stdin.read().strip()
26
+ return prompt_arg.strip()
27
+
28
+
29
+ async def render_run(
30
+ events: AsyncIterator[Event], *, out: TextIO | None = None, err: TextIO | None = None
31
+ ) -> StopReason:
32
+ out = sys.stdout if out is None else out
33
+ err = sys.stderr if err is None else err
34
+ final_text = ""
35
+ stop = StopReason.ERROR
36
+ async for event in events:
37
+ match event:
38
+ case TurnEndEvent(assistant_message=msg) if msg is not None:
39
+ text = "".join(p.text for p in msg.content if isinstance(p, TextContent)).strip()
40
+ if text:
41
+ final_text = text
42
+ case AgentEndEvent(stop_reason=stop_reason):
43
+ stop = stop_reason
44
+ case ErrorEvent(error=error):
45
+ print(f"error: {error}", file=err)
46
+ case ToolApprovalEvent(tool_name=tool_name, future=future) if future is not None:
47
+ future.set_result(ApprovalResponse.DENY)
48
+ print(
49
+ f"error: {tool_name!r} requires approval, denied (non-interactive mode)",
50
+ file=err,
51
+ )
52
+ case _:
53
+ pass
54
+ if stop == StopReason.STOP and final_text:
55
+ print(final_text, file=out)
56
+ return stop
57
+
58
+
59
+ async def run_headless(
60
+ *,
61
+ prompt_arg: str,
62
+ model: str | None,
63
+ provider: str | None,
64
+ api_key: str | None,
65
+ base_url: str | None,
66
+ openai_compat_auth_mode: AuthMode | None,
67
+ anthropic_compat_auth_mode: AuthMode | None,
68
+ ) -> int:
69
+ prompt = resolve_prompt(prompt_arg, stdin=sys.stdin)
70
+ if not prompt:
71
+ print("error: empty prompt", file=sys.stderr)
72
+ return 2
73
+
74
+ cfg = get_config()
75
+ previous_permission_mode = cfg.permissions.mode
76
+ # Headless can't show approval prompts; force auto in-memory for this run only.
77
+ cfg.permissions.mode = "auto"
78
+
79
+ try:
80
+ last_selected = get_last_selected()
81
+ initial_model = model or last_selected.model_id or config.llm.default_model
82
+ initial_provider = (
83
+ provider
84
+ if provider is not None
85
+ else (
86
+ last_selected.provider
87
+ if last_selected.model_id
88
+ else (config.llm.default_provider if model is None else None)
89
+ )
90
+ )
91
+ base = base_url or config.llm.default_base_url or None
92
+ thinking = last_selected.thinking_level or config.llm.default_thinking_level
93
+ openai_auth = openai_compat_auth_mode or config.llm.auth.openai_compat
94
+ anthropic_auth = anthropic_compat_auth_mode or config.llm.auth.anthropic_compat
95
+
96
+ tools = get_tools(DEFAULT_TOOLS)
97
+
98
+ runtime = ConversationRuntime(
99
+ cwd=os.getcwd(),
100
+ model=initial_model,
101
+ model_provider=initial_provider,
102
+ api_key=api_key,
103
+ base_url=base,
104
+ thinking_level=thinking,
105
+ tools=tools,
106
+ openai_compat_auth_mode=openai_auth,
107
+ anthropic_compat_auth_mode=anthropic_auth,
108
+ )
109
+
110
+ try:
111
+ init = runtime.initialize()
112
+ if init.provider_error:
113
+ print(f"error: {init.provider_error}", file=sys.stderr)
114
+ return 2
115
+
116
+ agent = runtime.prepare_for_run()
117
+ except Exception as e:
118
+ print(f"error: {e}", file=sys.stderr)
119
+ return 2
120
+
121
+ if agent is None:
122
+ print("error: agent initialization failed", file=sys.stderr)
123
+ return 2
124
+
125
+ return _exit_code(await render_run(agent.run(prompt)))
126
+ finally:
127
+ cfg.permissions.mode = previous_permission_mode
vtx/llm/__init__.py ADDED
@@ -0,0 +1,93 @@
1
+ from .base import DEFAULT_THINKING_LEVELS, BaseProvider, LLMStream, ProviderConfig
2
+ from .dynamic_models import (
3
+ DYNAMIC_PROVIDERS,
4
+ DynamicModelEntry,
5
+ DynamicProviderConfig,
6
+ find_dynamic_model,
7
+ get_all_models_with_dynamic,
8
+ get_dynamic_models,
9
+ get_dynamic_provider,
10
+ get_dynamic_provider_headers,
11
+ get_provider_models,
12
+ refresh_all_providers,
13
+ refresh_provider,
14
+ register_dynamic_provider,
15
+ )
16
+ from .models import (
17
+ ApiType,
18
+ Model,
19
+ get_all_models,
20
+ get_max_tokens,
21
+ get_model,
22
+ get_models_by_provider,
23
+ )
24
+ from .oauth import (
25
+ clear_api_key,
26
+ clear_openai_credentials,
27
+ get_dynamic_api_key,
28
+ get_provider_status,
29
+ get_valid_openai_credentials,
30
+ has_api_key,
31
+ is_copilot_logged_in,
32
+ is_openai_logged_in,
33
+ load_api_key,
34
+ load_openai_credentials,
35
+ openai_login,
36
+ save_api_key,
37
+ )
38
+ from .oauth import clear_credentials as clear_copilot_credentials
39
+ from .oauth import get_valid_token as get_copilot_token
40
+ from .oauth import load_credentials as load_copilot_credentials
41
+ from .oauth import login as copilot_login
42
+ from .provider_catalog import ProviderInfo, detect_provider_from_env, list_providers
43
+ from .provider_catalog import get as get_provider_info
44
+ from .providers import PROVIDER_API_BY_NAME, get_provider_class, resolve_provider_api_type
45
+
46
+ __all__ = [
47
+ "DEFAULT_THINKING_LEVELS",
48
+ "DYNAMIC_PROVIDERS",
49
+ "PROVIDER_API_BY_NAME",
50
+ "ApiType",
51
+ "BaseProvider",
52
+ "DynamicModelEntry",
53
+ "DynamicProviderConfig",
54
+ "LLMStream",
55
+ "Model",
56
+ "ProviderConfig",
57
+ "ProviderInfo",
58
+ "clear_api_key",
59
+ "clear_copilot_credentials",
60
+ "clear_openai_credentials",
61
+ "copilot_login",
62
+ "detect_provider_from_env",
63
+ "find_dynamic_model",
64
+ "get_all_models",
65
+ "get_all_models_with_dynamic",
66
+ "get_copilot_token",
67
+ "get_dynamic_api_key",
68
+ "get_dynamic_models",
69
+ "get_dynamic_provider",
70
+ "get_dynamic_provider_headers",
71
+ "get_max_tokens",
72
+ "get_model",
73
+ "get_models_by_provider",
74
+ "get_openai_token",
75
+ "get_provider_class",
76
+ "get_provider_info",
77
+ "get_provider_models",
78
+ "get_provider_status",
79
+ "get_valid_openai_credentials",
80
+ "has_api_key",
81
+ "is_copilot_logged_in",
82
+ "is_openai_logged_in",
83
+ "list_providers",
84
+ "load_api_key",
85
+ "load_copilot_credentials",
86
+ "load_openai_credentials",
87
+ "openai_login",
88
+ "refresh_all_providers",
89
+ "refresh_provider",
90
+ "register_dynamic_provider",
91
+ "resolve_provider_api_type",
92
+ "save_api_key",
93
+ ]