sandboxy 0.0.6__tar.gz → 0.0.7__tar.gz

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 (131) hide show
  1. {sandboxy-0.0.6 → sandboxy-0.0.7}/PKG-INFO +1 -1
  2. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/components/ResultDisplay.tsx +4 -2
  3. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/lib/api.ts +2 -0
  4. {sandboxy-0.0.6 → sandboxy-0.0.7}/pyproject.toml +1 -1
  5. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/api/app.py +13 -0
  6. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/api/routes/local.py +12 -2
  7. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/cli/main.py +3 -1
  8. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/providers/__init__.py +2 -0
  9. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/providers/registry.py +26 -5
  10. sandboxy-0.0.6/sandboxy/ui/dist/assets/index-BZFjoK-_.js → sandboxy-0.0.7/sandboxy/ui/dist/assets/index-DfKu4uXt.js +1 -1
  11. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/ui/dist/index.html +1 -1
  12. {sandboxy-0.0.6 → sandboxy-0.0.7}/.env.example +0 -0
  13. {sandboxy-0.0.6 → sandboxy-0.0.7}/.github/workflows/ci.yml +0 -0
  14. {sandboxy-0.0.6 → sandboxy-0.0.7}/.github/workflows/publish.yml +0 -0
  15. {sandboxy-0.0.6 → sandboxy-0.0.7}/.gitignore +0 -0
  16. {sandboxy-0.0.6 → sandboxy-0.0.7}/CONTRIBUTING.md +0 -0
  17. {sandboxy-0.0.6 → sandboxy-0.0.7}/LICENSE +0 -0
  18. {sandboxy-0.0.6 → sandboxy-0.0.7}/Makefile +0 -0
  19. {sandboxy-0.0.6 → sandboxy-0.0.7}/README.md +0 -0
  20. {sandboxy-0.0.6 → sandboxy-0.0.7}/docs/yaml-tools.md +0 -0
  21. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/index.html +0 -0
  22. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/package-lock.json +0 -0
  23. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/package.json +0 -0
  24. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/postcss.config.js +0 -0
  25. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/App.tsx +0 -0
  26. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/components/Layout.tsx +0 -0
  27. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/components/ModelSelector.tsx +0 -0
  28. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/hooks/useScenarioBuilder.ts +0 -0
  29. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/hooks/useScenarioRun.ts +0 -0
  30. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/hooks/useToolBuilder.ts +0 -0
  31. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/index.css +0 -0
  32. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/main.tsx +0 -0
  33. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/pages/BuilderPage.tsx +0 -0
  34. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/pages/DashboardPage.tsx +0 -0
  35. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/pages/DatasetPage.tsx +0 -0
  36. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/pages/ResultsPage.tsx +0 -0
  37. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/pages/RunPage.tsx +0 -0
  38. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/pages/ScenarioDetailPage.tsx +0 -0
  39. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/src/pages/ToolBuilderPage.tsx +0 -0
  40. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/tailwind.config.js +0 -0
  41. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/tsconfig.json +0 -0
  42. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/tsconfig.node.json +0 -0
  43. {sandboxy-0.0.6 → sandboxy-0.0.7}/local-ui/vite.config.ts +0 -0
  44. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/__init__.py +0 -0
  45. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/agents/__init__.py +0 -0
  46. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/agents/base.py +0 -0
  47. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/agents/llm_prompt.py +0 -0
  48. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/agents/loader.py +0 -0
  49. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/api/__init__.py +0 -0
  50. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/api/routes/__init__.py +0 -0
  51. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/api/routes/agents.py +0 -0
  52. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/api/routes/providers.py +0 -0
  53. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/api/routes/tools.py +0 -0
  54. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/cli/__init__.py +0 -0
  55. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/cli/type_detector.py +0 -0
  56. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/config.py +0 -0
  57. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/core/__init__.py +0 -0
  58. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/core/async_runner.py +0 -0
  59. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/core/mdl_parser.py +0 -0
  60. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/core/runner.py +0 -0
  61. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/core/safe_eval.py +0 -0
  62. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/core/state.py +0 -0
  63. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/datasets/__init__.py +0 -0
  64. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/datasets/loader.py +0 -0
  65. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/datasets/runner.py +0 -0
  66. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/errors.py +0 -0
  67. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/local/context.py +0 -0
  68. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/local/results.py +0 -0
  69. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/logging.py +0 -0
  70. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/mcp/__init__.py +0 -0
  71. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/mcp/client.py +0 -0
  72. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/mcp/wrapper.py +0 -0
  73. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/mlflow/__init__.py +0 -0
  74. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/mlflow/artifacts.py +0 -0
  75. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/mlflow/config.py +0 -0
  76. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/mlflow/exporter.py +0 -0
  77. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/mlflow/metrics.py +0 -0
  78. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/mlflow/tags.py +0 -0
  79. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/mlflow/tracing.py +0 -0
  80. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/providers/anthropic_provider.py +0 -0
  81. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/providers/base.py +0 -0
  82. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/providers/config.py +0 -0
  83. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/providers/http_client.py +0 -0
  84. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/providers/local.py +0 -0
  85. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/providers/openai_provider.py +0 -0
  86. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/providers/openrouter.py +0 -0
  87. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/scenarios/__init__.py +0 -0
  88. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/scenarios/comparison.py +0 -0
  89. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/scenarios/loader.py +0 -0
  90. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/scenarios/runner.py +0 -0
  91. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/scenarios/unified.py +0 -0
  92. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/session/__init__.py +0 -0
  93. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/session/manager.py +0 -0
  94. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/tools/__init__.py +0 -0
  95. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/tools/base.py +0 -0
  96. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/tools/loader.py +0 -0
  97. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/tools/yaml_tools.py +0 -0
  98. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/ui/__init__.py +0 -0
  99. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/ui/dist/assets/index-Qf7gGJk_.css +0 -0
  100. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/utils/__init__.py +0 -0
  101. {sandboxy-0.0.6 → sandboxy-0.0.7}/sandboxy/utils/time.py +0 -0
  102. {sandboxy-0.0.6 → sandboxy-0.0.7}/scenarios/customer_service.yml +0 -0
  103. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/__init__.py +0 -0
  104. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/conftest.py +0 -0
  105. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/factories.py +0 -0
  106. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/integration/__init__.py +0 -0
  107. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/integration/api/__init__.py +0 -0
  108. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/integration/test_mlflow_integration.py +0 -0
  109. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/mocks/__init__.py +0 -0
  110. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/mocks/providers.py +0 -0
  111. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/__init__.py +0 -0
  112. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/agents/__init__.py +0 -0
  113. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/agents/test_base.py +0 -0
  114. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/agents/test_llm_prompt.py +0 -0
  115. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/agents/test_loader.py +0 -0
  116. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/core/__init__.py +0 -0
  117. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/core/test_async_runner.py +0 -0
  118. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/core/test_mdl_parser.py +0 -0
  119. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/core/test_runner.py +0 -0
  120. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/core/test_safe_eval.py +0 -0
  121. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/core/test_state.py +0 -0
  122. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/mlflow/__init__.py +0 -0
  123. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/mlflow/test_artifacts.py +0 -0
  124. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/mlflow/test_config.py +0 -0
  125. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/mlflow/test_metrics.py +0 -0
  126. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/mlflow/test_tags.py +0 -0
  127. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/providers/test_openrouter.py +0 -0
  128. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/tools/__init__.py +0 -0
  129. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/tools/test_base.py +0 -0
  130. {sandboxy-0.0.6 → sandboxy-0.0.7}/tests/unit/tools/test_loader.py +0 -0
  131. {sandboxy-0.0.6 → sandboxy-0.0.7}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sandboxy
3
- Version: 0.0.6
3
+ Version: 0.0.7
4
4
  Summary: Open-source agent simulation and benchmarking platform
5
5
  Project-URL: Homepage, https://github.com/sandboxy-ai/sandboxy
6
6
  Project-URL: Repository, https://github.com/sandboxy-ai/sandboxy
@@ -2,8 +2,10 @@ import { useState } from 'react'
2
2
  import { CheckCircle, XCircle, Trophy, Clock, Zap, DollarSign, Hash, MessageSquare, ChevronDown, ChevronRight, Eye, X, AlertCircle } from 'lucide-react'
3
3
  import { RunScenarioResponse, CompareModelsResponse } from '../lib/api'
4
4
 
5
- export function formatCost(cost: number | null | undefined): string {
5
+ export function formatCost(cost: number | null | undefined, isLocal?: boolean): string {
6
6
  if (cost === null || cost === undefined) return '-'
7
+ // Local models have cost = 0 or is_local flag
8
+ if (isLocal === true || cost === 0) return 'Local'
7
9
  if (cost < 0.0001) return '<$0.0001'
8
10
  if (cost < 0.01) return `$${cost.toFixed(4)}`
9
11
  return `$${cost.toFixed(3)}`
@@ -73,7 +75,7 @@ export function SingleRunResult({ result }: { result: RunScenarioResponse }) {
73
75
  <DollarSign className="w-4 h-4" />
74
76
  Cost
75
77
  </div>
76
- <div className="text-2xl font-bold text-emerald-300">{formatCost(result.cost_usd)}</div>
78
+ <div className="text-2xl font-bold text-emerald-300">{formatCost(result.cost_usd, result.is_local)}</div>
77
79
  </div>
78
80
  </div>
79
81
 
@@ -110,6 +110,8 @@ export interface RunScenarioResponse {
110
110
  output_tokens: number
111
111
  cost_usd: number | null
112
112
  error: string | null
113
+ is_local?: boolean
114
+ provider_name?: string
113
115
  }
114
116
 
115
117
  export interface CompareModelsRequest {
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sandboxy"
7
- version = "0.0.6"
7
+ version = "0.0.7"
8
8
  description = "Open-source agent simulation and benchmarking platform"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -36,6 +36,19 @@ def create_local_app(
36
36
  root_dir: Working directory for scenarios/tools/agents.
37
37
  local_ui_path: Path to local UI static files.
38
38
  """
39
+ # Load .env from root_dir to pick up API keys from the project directory
40
+ # This is important when root_dir differs from cwd
41
+ env_file = root_dir / ".env"
42
+ if env_file.exists():
43
+ load_dotenv(env_file, override=True)
44
+ logger.info(f"Loaded environment from {env_file}")
45
+
46
+ # Reset provider registry so it picks up the newly loaded API keys
47
+ from sandboxy.providers.registry import reset_registry
48
+
49
+ reset_registry()
50
+ logger.info("Provider registry reset after loading project .env")
51
+
39
52
  from sandboxy.local.context import LocalContext, set_local_context
40
53
 
41
54
  ctx = LocalContext(root_dir=root_dir)
@@ -493,7 +493,10 @@ async def run_scenario(request: RunScenarioRequest) -> RunScenarioResponse:
493
493
  experiment_name=mlflow_config.experiment,
494
494
  )
495
495
 
496
- with mlflow_run_context(mlflow_config, run_name=request.model) as run_id:
496
+ # Short run name - just the model (scenario is in experiment name)
497
+ run_name = request.model.split("/")[-1] if "/" in request.model else request.model
498
+
499
+ with mlflow_run_context(mlflow_config, run_name=run_name) as run_id:
497
500
  result = await runner.run(
498
501
  scenario=spec,
499
502
  model=request.model,
@@ -633,12 +636,17 @@ async def compare_models(request: CompareModelsRequest) -> CompareModelsResponse
633
636
  scenario_name=spec.name,
634
637
  )
635
638
  exporter = MLflowExporter(config)
639
+
640
+ # Short run name - just the model
641
+ run_name = result.model.split("/")[-1] if "/" in result.model else result.model
642
+
636
643
  exporter.export(
637
644
  result=result.to_dict(),
638
645
  scenario_path=scenario_path,
639
646
  scenario_name=spec.name,
640
647
  scenario_id=spec.id,
641
648
  agent_name=result.model,
649
+ run_name=run_name,
642
650
  )
643
651
  except ImportError:
644
652
  logger.warning("MLflow not installed, skipping export")
@@ -1513,7 +1521,9 @@ async def run_with_dataset(request: RunDatasetRequest) -> RunDatasetResponse:
1513
1521
  if mlflow_config and mlflow_config.enabled:
1514
1522
  from sandboxy.mlflow import mlflow_run_context
1515
1523
 
1516
- run_name = f"{request.model}-{request.dataset_id}"
1524
+ # Short run name - model + dataset
1525
+ model_short = request.model.split("/")[-1] if "/" in request.model else request.model
1526
+ run_name = f"{model_short} ({request.dataset_id})"
1517
1527
  with mlflow_run_context(mlflow_config, run_name=run_name) as run_id:
1518
1528
  result = await run_dataset_benchmark()
1519
1529
 
@@ -833,7 +833,9 @@ def scenario(
833
833
  )
834
834
 
835
835
  # Start run, execute scenario, then log metrics - all connected
836
- with mlflow_run_context(mlflow_config, run_name=model_id) as run_id:
836
+ # Short run name - just the model
837
+ run_name = model_id.split("/")[-1] if "/" in model_id else model_id
838
+ with mlflow_run_context(mlflow_config, run_name=run_name) as run_id:
837
839
  runner = ScenarioRunner(scenario=spec, agent=agent)
838
840
  result = runner.run(max_turns=max_turns)
839
841
 
@@ -40,6 +40,7 @@ from sandboxy.providers.registry import (
40
40
  get_provider,
41
41
  get_registry,
42
42
  reload_local_providers,
43
+ reset_registry,
43
44
  )
44
45
 
45
46
  __all__ = [
@@ -53,6 +54,7 @@ __all__ = [
53
54
  "get_provider",
54
55
  "get_registry",
55
56
  "reload_local_providers",
57
+ "reset_registry",
56
58
  # Local provider
57
59
  "LocalProvider",
58
60
  "LocalProviderConnectionError",
@@ -154,23 +154,34 @@ class ProviderRegistry:
154
154
  if "/" in model_id:
155
155
  provider_name, model_name = model_id.split("/", 1)
156
156
 
157
- # Check for local provider first (e.g., "ollama/llama3")
157
+ # Priority 1: Check for LOCAL provider with matching name
158
+ # Local providers take precedence over cloud providers
158
159
  if provider_name in self.providers:
159
160
  provider = self.providers[provider_name]
160
- # Verify it's a local provider or supports the model
161
- if hasattr(provider, "config") or provider.supports_model(model_id):
161
+ # Only use if it's a local provider (has config attribute)
162
+ if hasattr(provider, "config"):
162
163
  return provider
163
164
 
164
- # OpenRouter format (e.g., "openai/gpt-4o")
165
+ # Priority 2: OpenRouter for provider/model format (e.g., "openai/gpt-4o")
165
166
  if "openrouter" in self.providers:
166
167
  return self.providers["openrouter"]
167
168
 
168
- # Fallback to direct provider if prefix matches
169
+ # Priority 3: Fallback to direct cloud provider if available
169
170
  if provider_name == "openai" and "openai" in self.providers:
170
171
  return self.providers["openai"]
171
172
  if provider_name == "anthropic" and "anthropic" in self.providers:
172
173
  return self.providers["anthropic"]
173
174
 
175
+ # No valid provider found for prefixed model - raise clear error
176
+ available = list(self.providers.keys())
177
+ raise ProviderError(
178
+ f"No provider available for model '{model_id}'. "
179
+ f"The '{provider_name}/' prefix requires OpenRouter (set OPENROUTER_API_KEY) "
180
+ f"or a local provider named '{provider_name}'. "
181
+ f"Available providers: {available}",
182
+ provider="registry",
183
+ )
184
+
174
185
  # No prefix - use direct providers
175
186
  model_lower = model_id.lower()
176
187
 
@@ -280,6 +291,16 @@ def get_registry() -> ProviderRegistry:
280
291
  return _registry
281
292
 
282
293
 
294
+ def reset_registry() -> None:
295
+ """Reset the global provider registry.
296
+
297
+ Forces re-initialization on next get_registry() call.
298
+ Useful after loading new environment variables.
299
+ """
300
+ global _registry
301
+ _registry = None
302
+
303
+
283
304
  def get_provider(model_id: str) -> BaseProvider:
284
305
  """Get a provider for a model (convenience function).
285
306