shotgun-sh 0.2.11.dev7__py3-none-any.whl → 0.2.23.dev1__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.

Potentially problematic release.


This version of shotgun-sh might be problematic. Click here for more details.

Files changed (51) hide show
  1. shotgun/agents/agent_manager.py +25 -11
  2. shotgun/agents/config/README.md +89 -0
  3. shotgun/agents/config/__init__.py +10 -1
  4. shotgun/agents/config/manager.py +287 -32
  5. shotgun/agents/config/models.py +26 -1
  6. shotgun/agents/config/provider.py +27 -0
  7. shotgun/agents/config/streaming_test.py +119 -0
  8. shotgun/agents/error/__init__.py +11 -0
  9. shotgun/agents/error/models.py +19 -0
  10. shotgun/agents/history/token_counting/anthropic.py +8 -0
  11. shotgun/agents/runner.py +230 -0
  12. shotgun/build_constants.py +1 -1
  13. shotgun/cli/context.py +43 -0
  14. shotgun/cli/error_handler.py +24 -0
  15. shotgun/cli/export.py +34 -34
  16. shotgun/cli/plan.py +34 -34
  17. shotgun/cli/research.py +17 -9
  18. shotgun/cli/specify.py +20 -19
  19. shotgun/cli/tasks.py +34 -34
  20. shotgun/exceptions.py +323 -0
  21. shotgun/llm_proxy/__init__.py +17 -0
  22. shotgun/llm_proxy/client.py +215 -0
  23. shotgun/llm_proxy/models.py +137 -0
  24. shotgun/logging_config.py +42 -0
  25. shotgun/main.py +2 -0
  26. shotgun/posthog_telemetry.py +18 -25
  27. shotgun/prompts/agents/partials/common_agent_system_prompt.j2 +3 -2
  28. shotgun/sdk/codebase.py +14 -3
  29. shotgun/sentry_telemetry.py +140 -2
  30. shotgun/settings.py +5 -0
  31. shotgun/tui/app.py +35 -10
  32. shotgun/tui/screens/chat/chat_screen.py +192 -91
  33. shotgun/tui/screens/chat/codebase_index_prompt_screen.py +62 -11
  34. shotgun/tui/screens/chat_screen/command_providers.py +3 -2
  35. shotgun/tui/screens/chat_screen/hint_message.py +76 -1
  36. shotgun/tui/screens/chat_screen/history/chat_history.py +37 -2
  37. shotgun/tui/screens/directory_setup.py +45 -41
  38. shotgun/tui/screens/feedback.py +10 -3
  39. shotgun/tui/screens/github_issue.py +11 -2
  40. shotgun/tui/screens/model_picker.py +8 -1
  41. shotgun/tui/screens/pipx_migration.py +12 -6
  42. shotgun/tui/screens/provider_config.py +25 -8
  43. shotgun/tui/screens/shotgun_auth.py +0 -10
  44. shotgun/tui/screens/welcome.py +32 -0
  45. shotgun/tui/widgets/widget_coordinator.py +3 -2
  46. shotgun_sh-0.2.23.dev1.dist-info/METADATA +472 -0
  47. {shotgun_sh-0.2.11.dev7.dist-info → shotgun_sh-0.2.23.dev1.dist-info}/RECORD +50 -42
  48. shotgun_sh-0.2.11.dev7.dist-info/METADATA +0 -130
  49. {shotgun_sh-0.2.11.dev7.dist-info → shotgun_sh-0.2.23.dev1.dist-info}/WHEEL +0 -0
  50. {shotgun_sh-0.2.11.dev7.dist-info → shotgun_sh-0.2.23.dev1.dist-info}/entry_points.txt +0 -0
  51. {shotgun_sh-0.2.11.dev7.dist-info → shotgun_sh-0.2.23.dev1.dist-info}/licenses/LICENSE +0 -0
shotgun/cli/research.py CHANGED
@@ -11,7 +11,10 @@ from shotgun.agents.research import (
11
11
  create_research_agent,
12
12
  run_research_agent,
13
13
  )
14
+ from shotgun.cli.error_handler import print_agent_error
15
+ from shotgun.exceptions import ErrorNotPickedUpBySentry
14
16
  from shotgun.logging_config import get_logger
17
+ from shotgun.posthog_telemetry import track_event
15
18
 
16
19
  app = typer.Typer(
17
20
  name="research", help="Perform research with agentic loops", no_args_is_help=True
@@ -59,8 +62,6 @@ async def async_research(
59
62
  ) -> None:
60
63
  """Async wrapper for research process."""
61
64
  # Track research command usage
62
- from shotgun.posthog_telemetry import track_event
63
-
64
65
  track_event(
65
66
  "research_command",
66
67
  {
@@ -75,11 +76,18 @@ async def async_research(
75
76
  # Create the research agent with deps and provider
76
77
  agent, deps = await create_research_agent(agent_runtime_options, provider)
77
78
 
78
- # Start research process
79
+ # Start research process with error handling
79
80
  logger.info("🔬 Starting research...")
80
- result = await run_research_agent(agent, query, deps)
81
-
82
- # Display results
83
- print("✅ Research Complete!")
84
- print("📋 Findings:")
85
- print(result.output)
81
+ try:
82
+ result = await run_research_agent(agent, query, deps)
83
+ # Display results
84
+ print("✅ Research Complete!")
85
+ print("📋 Findings:")
86
+ print(result.output)
87
+ except ErrorNotPickedUpBySentry as e:
88
+ # All user-actionable errors - display with plain text
89
+ print_agent_error(e)
90
+ except Exception as e:
91
+ # Unexpected errors that weren't wrapped (shouldn't happen)
92
+ logger.exception("Unexpected error in research command")
93
+ print(f"⚠️ An unexpected error occurred: {str(e)}")
shotgun/cli/specify.py CHANGED
@@ -11,6 +11,8 @@ from shotgun.agents.specify import (
11
11
  create_specify_agent,
12
12
  run_specify_agent,
13
13
  )
14
+ from shotgun.cli.error_handler import print_agent_error
15
+ from shotgun.exceptions import ErrorNotPickedUpBySentry
14
16
  from shotgun.logging_config import get_logger
15
17
 
16
18
  app = typer.Typer(
@@ -44,26 +46,25 @@ def specify(
44
46
 
45
47
  logger.info("📝 Specification Requirement: %s", requirement)
46
48
 
47
- try:
48
- # Create agent dependencies
49
- agent_runtime_options = AgentRuntimeOptions(
50
- interactive_mode=not non_interactive
51
- )
49
+ # Create agent dependencies
50
+ agent_runtime_options = AgentRuntimeOptions(interactive_mode=not non_interactive)
52
51
 
53
- # Create the specify agent with deps and provider
54
- agent, deps = asyncio.run(create_specify_agent(agent_runtime_options, provider))
52
+ # Create the specify agent with deps and provider
53
+ agent, deps = asyncio.run(create_specify_agent(agent_runtime_options, provider))
55
54
 
56
- # Start specification process
57
- logger.info("📋 Starting specification generation...")
58
- result = asyncio.run(run_specify_agent(agent, requirement, deps))
55
+ # Start specification process with error handling
56
+ logger.info("📋 Starting specification generation...")
59
57
 
60
- # Display results
61
- logger.info("✅ Specification Complete!")
62
- logger.info("📋 Results:")
63
- logger.info("%s", result.output)
58
+ async def async_specify() -> None:
59
+ try:
60
+ result = await run_specify_agent(agent, requirement, deps)
61
+ logger.info("✅ Specification Complete!")
62
+ logger.info("📋 Results:")
63
+ logger.info("%s", result.output)
64
+ except ErrorNotPickedUpBySentry as e:
65
+ print_agent_error(e)
66
+ except Exception as e:
67
+ logger.exception("Unexpected error in specify command")
68
+ print(f"⚠️ An unexpected error occurred: {str(e)}")
64
69
 
65
- except Exception as e:
66
- logger.error("❌ Error during specification: %s", str(e))
67
- import traceback
68
-
69
- logger.debug("Full traceback:\n%s", traceback.format_exc())
70
+ asyncio.run(async_specify())
shotgun/cli/tasks.py CHANGED
@@ -11,7 +11,10 @@ from shotgun.agents.tasks import (
11
11
  create_tasks_agent,
12
12
  run_tasks_agent,
13
13
  )
14
+ from shotgun.cli.error_handler import print_agent_error
15
+ from shotgun.exceptions import ErrorNotPickedUpBySentry
14
16
  from shotgun.logging_config import get_logger
17
+ from shotgun.posthog_telemetry import track_event
15
18
 
16
19
  app = typer.Typer(name="tasks", help="Generate task lists with agentic approach")
17
20
  logger = get_logger(__name__)
@@ -42,37 +45,34 @@ def tasks(
42
45
 
43
46
  logger.info("📋 Task Creation Instruction: %s", instruction)
44
47
 
45
- try:
46
- # Track tasks command usage
47
- from shotgun.posthog_telemetry import track_event
48
-
49
- track_event(
50
- "tasks_command",
51
- {
52
- "non_interactive": non_interactive,
53
- "provider": provider.value if provider else "default",
54
- },
55
- )
56
-
57
- # Create agent dependencies
58
- agent_runtime_options = AgentRuntimeOptions(
59
- interactive_mode=not non_interactive
60
- )
61
-
62
- # Create the tasks agent with deps and provider
63
- agent, deps = asyncio.run(create_tasks_agent(agent_runtime_options, provider))
64
-
65
- # Start task creation process
66
- logger.info("🎯 Starting task creation...")
67
- result = asyncio.run(run_tasks_agent(agent, instruction, deps))
68
-
69
- # Display results
70
- logger.info("✅ Task Creation Complete!")
71
- logger.info("📋 Results:")
72
- logger.info("%s", result.output)
73
-
74
- except Exception as e:
75
- logger.error("❌ Error during task creation: %s", str(e))
76
- import traceback
77
-
78
- logger.debug("Full traceback:\n%s", traceback.format_exc())
48
+ # Track tasks command usage
49
+ track_event(
50
+ "tasks_command",
51
+ {
52
+ "non_interactive": non_interactive,
53
+ "provider": provider.value if provider else "default",
54
+ },
55
+ )
56
+
57
+ # Create agent dependencies
58
+ agent_runtime_options = AgentRuntimeOptions(interactive_mode=not non_interactive)
59
+
60
+ # Create the tasks agent with deps and provider
61
+ agent, deps = asyncio.run(create_tasks_agent(agent_runtime_options, provider))
62
+
63
+ # Start task creation process with error handling
64
+ logger.info("🎯 Starting task creation...")
65
+
66
+ async def async_tasks() -> None:
67
+ try:
68
+ result = await run_tasks_agent(agent, instruction, deps)
69
+ logger.info(" Task Creation Complete!")
70
+ logger.info("📋 Results:")
71
+ logger.info("%s", result.output)
72
+ except ErrorNotPickedUpBySentry as e:
73
+ print_agent_error(e)
74
+ except Exception as e:
75
+ logger.exception("Unexpected error in tasks command")
76
+ print(f"⚠️ An unexpected error occurred: {str(e)}")
77
+
78
+ asyncio.run(async_tasks())
shotgun/exceptions.py CHANGED
@@ -1,13 +1,57 @@
1
1
  """General exceptions for Shotgun application."""
2
2
 
3
+ from shotgun.utils import get_shotgun_home
4
+
5
+ # Shotgun Account signup URL for BYOK users
6
+ SHOTGUN_SIGNUP_URL = "https://shotgun.sh"
7
+ SHOTGUN_CONTACT_EMAIL = "contact@shotgun.sh"
8
+
3
9
 
4
10
  class ErrorNotPickedUpBySentry(Exception): # noqa: N818
5
11
  """Base for user-actionable errors that shouldn't be sent to Sentry.
6
12
 
7
13
  These errors represent expected user conditions requiring action
8
14
  rather than bugs that need tracking.
15
+
16
+ All subclasses should implement to_markdown() and to_plain_text() methods
17
+ for consistent error message formatting.
9
18
  """
10
19
 
20
+ def to_markdown(self) -> str:
21
+ """Generate markdown-formatted error message for TUI.
22
+
23
+ Subclasses should override this method.
24
+ """
25
+ return f"⚠️ {str(self)}"
26
+
27
+ def to_plain_text(self) -> str:
28
+ """Generate plain text error message for CLI.
29
+
30
+ Subclasses should override this method.
31
+ """
32
+ return f"⚠️ {str(self)}"
33
+
34
+
35
+ # ============================================================================
36
+ # User Action Required Errors
37
+ # ============================================================================
38
+
39
+
40
+ class AgentCancelledException(ErrorNotPickedUpBySentry):
41
+ """Raised when user cancels an agent operation."""
42
+
43
+ def __init__(self) -> None:
44
+ """Initialize the exception."""
45
+ super().__init__("Operation cancelled by user")
46
+
47
+ def to_markdown(self) -> str:
48
+ """Generate markdown-formatted error message for TUI."""
49
+ return "⚠️ Operation cancelled by user"
50
+
51
+ def to_plain_text(self) -> str:
52
+ """Generate plain text error message for CLI."""
53
+ return "⚠️ Operation cancelled by user"
54
+
11
55
 
12
56
  class ContextSizeLimitExceeded(ErrorNotPickedUpBySentry):
13
57
  """Raised when conversation context exceeds the model's limits.
@@ -30,3 +74,282 @@ class ContextSizeLimitExceeded(ErrorNotPickedUpBySentry):
30
74
  super().__init__(
31
75
  f"Context too large for {model_name} (limit: {max_tokens:,} tokens)"
32
76
  )
77
+
78
+ def to_markdown(self) -> str:
79
+ """Generate markdown-formatted error message for TUI."""
80
+ return (
81
+ f"⚠️ **Context too large for {self.model_name}**\n\n"
82
+ f"Your conversation history exceeds this model's limit ({self.max_tokens:,} tokens).\n\n"
83
+ f"**Choose an action:**\n\n"
84
+ f"1. Switch to a larger model (`Ctrl+P` → Change Model)\n"
85
+ f"2. Switch to a larger model, compact (`/compact`), then switch back to {self.model_name}\n"
86
+ f"3. Clear conversation (`/clear`)\n"
87
+ )
88
+
89
+ def to_plain_text(self) -> str:
90
+ """Generate plain text error message for CLI."""
91
+ return (
92
+ f"⚠️ Context too large for {self.model_name}\n\n"
93
+ f"Your conversation history exceeds this model's limit ({self.max_tokens:,} tokens).\n\n"
94
+ f"Choose an action:\n"
95
+ f"1. Switch to a larger model\n"
96
+ f"2. Switch to a larger model, compact, then switch back to {self.model_name}\n"
97
+ f"3. Clear conversation\n"
98
+ )
99
+
100
+
101
+ # ============================================================================
102
+ # Shotgun Account Errors (show contact email in TUI)
103
+ # ============================================================================
104
+
105
+
106
+ class ShotgunAccountException(ErrorNotPickedUpBySentry):
107
+ """Base class for Shotgun Account service errors.
108
+
109
+ TUI will check isinstance() of this class to show contact email UI.
110
+ """
111
+
112
+
113
+ class BudgetExceededException(ShotgunAccountException):
114
+ """Raised when Shotgun Account budget has been exceeded.
115
+
116
+ This is a user-actionable error - they need to contact support
117
+ to increase their budget limit. This is a temporary exception
118
+ until self-service budget increases are implemented.
119
+ """
120
+
121
+ def __init__(
122
+ self,
123
+ current_cost: float | None = None,
124
+ max_budget: float | None = None,
125
+ message: str | None = None,
126
+ ):
127
+ """Initialize the exception.
128
+
129
+ Args:
130
+ current_cost: Current total spend/cost (optional)
131
+ max_budget: Maximum budget limit (optional)
132
+ message: Optional custom error message from API
133
+ """
134
+ self.current_cost = current_cost
135
+ self.max_budget = max_budget
136
+ self.api_message = message
137
+
138
+ if message:
139
+ error_msg = message
140
+ elif current_cost is not None and max_budget is not None:
141
+ error_msg = f"Budget exceeded: ${current_cost:.2f} / ${max_budget:.2f}"
142
+ else:
143
+ error_msg = "Budget exceeded"
144
+
145
+ super().__init__(error_msg)
146
+
147
+ def to_markdown(self) -> str:
148
+ """Generate markdown-formatted error message for TUI.
149
+
150
+ Note: TUI will detect ShotgunAccountException and automatically
151
+ show email contact UI component.
152
+ """
153
+ return (
154
+ "⚠️ **Your Shotgun Account budget has been exceeded!**\n\n"
155
+ "Your account has reached its spending limit and cannot process more requests.\n\n"
156
+ "**Need help?** Contact us for budget increase.\n\n"
157
+ "• Self-service budget increases are coming soon!\n\n"
158
+ f"_Error details: {str(self)}_"
159
+ )
160
+
161
+ def to_plain_text(self) -> str:
162
+ """Generate plain text error message for CLI."""
163
+ return (
164
+ "⚠️ Your Shotgun Account budget has been exceeded!\n\n"
165
+ "Your account has reached its spending limit and cannot process more requests.\n\n"
166
+ f"Need help? Contact: {SHOTGUN_CONTACT_EMAIL}\n\n"
167
+ "• Self-service budget increases are coming soon!\n\n"
168
+ f"Error details: {str(self)}"
169
+ )
170
+
171
+
172
+ class ShotgunServiceOverloadException(ShotgunAccountException):
173
+ """Raised when Shotgun Account AI service is overloaded."""
174
+
175
+ def __init__(self, message: str | None = None):
176
+ """Initialize the exception.
177
+
178
+ Args:
179
+ message: Optional custom error message from API
180
+ """
181
+ super().__init__(message or "Service temporarily overloaded")
182
+
183
+ def to_markdown(self) -> str:
184
+ """Generate markdown-formatted error message for TUI."""
185
+ return "⚠️ The AI service is temporarily overloaded. Please wait a moment and try again."
186
+
187
+ def to_plain_text(self) -> str:
188
+ """Generate plain text error message for CLI."""
189
+ return "⚠️ The AI service is temporarily overloaded. Please wait a moment and try again."
190
+
191
+
192
+ class ShotgunRateLimitException(ShotgunAccountException):
193
+ """Raised when Shotgun Account rate limit is reached."""
194
+
195
+ def __init__(self, message: str | None = None):
196
+ """Initialize the exception.
197
+
198
+ Args:
199
+ message: Optional custom error message from API
200
+ """
201
+ super().__init__(message or "Rate limit reached")
202
+
203
+ def to_markdown(self) -> str:
204
+ """Generate markdown-formatted error message for TUI."""
205
+ return "⚠️ Rate limit reached. Please wait before trying again."
206
+
207
+ def to_plain_text(self) -> str:
208
+ """Generate plain text error message for CLI."""
209
+ return "⚠️ Rate limit reached. Please wait before trying again."
210
+
211
+
212
+ # ============================================================================
213
+ # BYOK (Bring Your Own Key) API Errors
214
+ # ============================================================================
215
+
216
+
217
+ class BYOKAPIException(ErrorNotPickedUpBySentry):
218
+ """Base class for BYOK API errors.
219
+
220
+ All BYOK errors suggest using Shotgun Account to avoid the issue.
221
+ """
222
+
223
+ def __init__(self, message: str, specific_error: str = "API error"):
224
+ """Initialize the exception.
225
+
226
+ Args:
227
+ message: The error message from the API
228
+ specific_error: Human-readable error type label
229
+ """
230
+ self.api_message = message
231
+ self.specific_error = specific_error
232
+ super().__init__(message)
233
+
234
+ def to_markdown(self) -> str:
235
+ """Generate markdown-formatted error message for TUI."""
236
+ return (
237
+ f"⚠️ **{self.specific_error}**: {self.api_message}\n\n"
238
+ f"_This could be avoided with a [Shotgun Account]({SHOTGUN_SIGNUP_URL})._"
239
+ )
240
+
241
+ def to_plain_text(self) -> str:
242
+ """Generate plain text error message for CLI."""
243
+ return (
244
+ f"⚠️ {self.specific_error}: {self.api_message}\n\n"
245
+ f"This could be avoided with a Shotgun Account: {SHOTGUN_SIGNUP_URL}"
246
+ )
247
+
248
+
249
+ class BYOKRateLimitException(BYOKAPIException):
250
+ """Raised when BYOK user hits rate limit."""
251
+
252
+ def __init__(self, message: str):
253
+ """Initialize the exception.
254
+
255
+ Args:
256
+ message: The error message from the API
257
+ """
258
+ super().__init__(message, specific_error="Rate limit reached")
259
+
260
+
261
+ class BYOKQuotaBillingException(BYOKAPIException):
262
+ """Raised when BYOK user has quota or billing issues."""
263
+
264
+ def __init__(self, message: str):
265
+ """Initialize the exception.
266
+
267
+ Args:
268
+ message: The error message from the API
269
+ """
270
+ super().__init__(message, specific_error="Quota or billing issue")
271
+
272
+
273
+ class BYOKAuthenticationException(BYOKAPIException):
274
+ """Raised when BYOK authentication fails."""
275
+
276
+ def __init__(self, message: str):
277
+ """Initialize the exception.
278
+
279
+ Args:
280
+ message: The error message from the API
281
+ """
282
+ super().__init__(message, specific_error="Authentication error")
283
+
284
+
285
+ class BYOKServiceOverloadException(BYOKAPIException):
286
+ """Raised when BYOK service is overloaded."""
287
+
288
+ def __init__(self, message: str):
289
+ """Initialize the exception.
290
+
291
+ Args:
292
+ message: The error message from the API
293
+ """
294
+ super().__init__(message, specific_error="Service overloaded")
295
+
296
+
297
+ class BYOKGenericAPIException(BYOKAPIException):
298
+ """Raised for generic BYOK API errors."""
299
+
300
+ def __init__(self, message: str):
301
+ """Initialize the exception.
302
+
303
+ Args:
304
+ message: The error message from the API
305
+ """
306
+ super().__init__(message, specific_error="API error")
307
+
308
+
309
+ # ============================================================================
310
+ # Generic Errors
311
+ # ============================================================================
312
+
313
+
314
+ class GenericAPIStatusException(ErrorNotPickedUpBySentry):
315
+ """Raised for generic API status errors that don't fit other categories."""
316
+
317
+ def __init__(self, message: str):
318
+ """Initialize the exception.
319
+
320
+ Args:
321
+ message: The error message from the API
322
+ """
323
+ self.api_message = message
324
+ super().__init__(message)
325
+
326
+ def to_markdown(self) -> str:
327
+ """Generate markdown-formatted error message for TUI."""
328
+ return f"⚠️ AI service error: {self.api_message}"
329
+
330
+ def to_plain_text(self) -> str:
331
+ """Generate plain text error message for CLI."""
332
+ return f"⚠️ AI service error: {self.api_message}"
333
+
334
+
335
+ class UnknownAgentException(ErrorNotPickedUpBySentry):
336
+ """Raised for unknown/unclassified agent errors."""
337
+
338
+ def __init__(self, original_exception: Exception):
339
+ """Initialize the exception.
340
+
341
+ Args:
342
+ original_exception: The original exception that was caught
343
+ """
344
+ self.original_exception = original_exception
345
+ super().__init__(str(original_exception))
346
+
347
+ def to_markdown(self) -> str:
348
+ """Generate markdown-formatted error message for TUI."""
349
+ log_path = get_shotgun_home() / "logs" / "shotgun.log"
350
+ return f"⚠️ An error occurred: {str(self.original_exception)}\n\nCheck logs at {log_path}"
351
+
352
+ def to_plain_text(self) -> str:
353
+ """Generate plain text error message for CLI."""
354
+ log_path = get_shotgun_home() / "logs" / "shotgun.log"
355
+ return f"⚠️ An error occurred: {str(self.original_exception)}\n\nCheck logs at {log_path}"
@@ -1,5 +1,6 @@
1
1
  """LiteLLM proxy client utilities and configuration."""
2
2
 
3
+ from .client import LiteLLMProxyClient, get_budget_info
3
4
  from .clients import (
4
5
  create_anthropic_proxy_provider,
5
6
  create_litellm_provider,
@@ -9,6 +10,14 @@ from .constants import (
9
10
  LITELLM_PROXY_BASE_URL,
10
11
  LITELLM_PROXY_OPENAI_BASE,
11
12
  )
13
+ from .models import (
14
+ BudgetInfo,
15
+ BudgetSource,
16
+ KeyInfoData,
17
+ KeyInfoResponse,
18
+ TeamInfoData,
19
+ TeamInfoResponse,
20
+ )
12
21
 
13
22
  __all__ = [
14
23
  "LITELLM_PROXY_BASE_URL",
@@ -16,4 +25,12 @@ __all__ = [
16
25
  "LITELLM_PROXY_OPENAI_BASE",
17
26
  "create_litellm_provider",
18
27
  "create_anthropic_proxy_provider",
28
+ "LiteLLMProxyClient",
29
+ "get_budget_info",
30
+ "BudgetInfo",
31
+ "BudgetSource",
32
+ "KeyInfoData",
33
+ "KeyInfoResponse",
34
+ "TeamInfoData",
35
+ "TeamInfoResponse",
19
36
  ]