sarveshpatel commited on
Commit
a07ae97
·
verified ·
1 Parent(s): a234ba5

Create app/config.py

Browse files
Files changed (1) hide show
  1. app/config.py +121 -0
app/config.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Application configuration using pydantic-settings."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from enum import Enum
7
+ from pathlib import Path
8
+ from typing import Optional
9
+
10
+ from pydantic import Field, field_validator
11
+ from pydantic_settings import BaseSettings
12
+
13
+
14
+ class EnvType(str, Enum):
15
+ CONDA = "conda"
16
+ VENV = "venv"
17
+ VENV_UV = "venv-uv"
18
+
19
+
20
+ class Settings(BaseSettings):
21
+ """Application settings loaded from environment variables."""
22
+
23
+ # Server settings
24
+ app_name: str = "MCP Code Executor"
25
+ debug: bool = False
26
+ host: str = "0.0.0.0"
27
+ port: int = 7860
28
+
29
+ # Code storage
30
+ code_storage_dir: str = Field(
31
+ default="/app/code_storage",
32
+ description="Directory where generated code files are stored",
33
+ )
34
+
35
+ # Environment configuration
36
+ env_type: EnvType = Field(default=EnvType.VENV_UV, description="Python environment type")
37
+ conda_env_name: Optional[str] = Field(default=None, description="Conda environment name")
38
+ venv_path: Optional[str] = Field(default=None, description="Path to virtualenv")
39
+ uv_venv_path: Optional[str] = Field(
40
+ default="/app/executor_venv", description="Path to UV virtualenv"
41
+ )
42
+
43
+ # Execution settings
44
+ execution_timeout: int = Field(
45
+ default=120, description="Maximum execution time in seconds", ge=5, le=600
46
+ )
47
+ max_concurrent_executions: int = Field(
48
+ default=20, description="Maximum concurrent code executions", ge=1, le=100
49
+ )
50
+ max_output_size: int = Field(
51
+ default=1_048_576, description="Maximum output size in bytes (1MB)", ge=1024
52
+ )
53
+ max_file_size: int = Field(
54
+ default=10_485_760, description="Maximum file size in bytes (10MB)", ge=1024
55
+ )
56
+
57
+ # HF Space
58
+ hf_space: bool = Field(default=False, description="Running on HuggingFace Space")
59
+
60
+ @field_validator("code_storage_dir")
61
+ @classmethod
62
+ def ensure_storage_dir_exists(cls, v: str) -> str:
63
+ path = Path(v)
64
+ path.mkdir(parents=True, exist_ok=True)
65
+ return str(path.resolve())
66
+
67
+ def get_python_executable(self) -> str:
68
+ """Get the Python executable path for the configured environment."""
69
+ if self.env_type == EnvType.CONDA:
70
+ if not self.conda_env_name:
71
+ raise ValueError("CONDA_ENV_NAME must be set when ENV_TYPE is conda")
72
+ return f"conda run -n {self.conda_env_name} python"
73
+ elif self.env_type == EnvType.VENV:
74
+ if not self.venv_path:
75
+ raise ValueError("VENV_PATH must be set when ENV_TYPE is venv")
76
+ return str(Path(self.venv_path) / "bin" / "python")
77
+ elif self.env_type == EnvType.VENV_UV:
78
+ if not self.uv_venv_path:
79
+ raise ValueError("UV_VENV_PATH must be set when ENV_TYPE is venv-uv")
80
+ return str(Path(self.uv_venv_path) / "bin" / "python")
81
+ raise ValueError(f"Unknown environment type: {self.env_type}")
82
+
83
+ def get_pip_command(self) -> list[str]:
84
+ """Get the pip install command for the configured environment."""
85
+ if self.env_type == EnvType.CONDA:
86
+ return ["conda", "install", "-n", self.conda_env_name, "-y"]
87
+ elif self.env_type == EnvType.VENV:
88
+ pip_path = str(Path(self.venv_path) / "bin" / "pip")
89
+ return [pip_path, "install"]
90
+ elif self.env_type == EnvType.VENV_UV:
91
+ return ["uv", "pip", "install", "--python", str(Path(self.uv_venv_path) / "bin" / "python")]
92
+ raise ValueError(f"Unknown environment type: {self.env_type}")
93
+
94
+ model_config = {
95
+ "env_prefix": "",
96
+ "case_sensitive": False,
97
+ "env_file": ".env",
98
+ "env_file_encoding": "utf-8",
99
+ }
100
+
101
+
102
+ # Singleton settings instance
103
+ _settings: Optional[Settings] = None
104
+
105
+
106
+ def get_settings() -> Settings:
107
+ """Get or create the settings singleton."""
108
+ global _settings
109
+ if _settings is None:
110
+ _settings = Settings()
111
+ return _settings
112
+
113
+
114
+ def update_settings(**kwargs) -> Settings:
115
+ """Update settings with new values (for dynamic configuration)."""
116
+ global _settings
117
+ current = get_settings()
118
+ new_data = current.model_dump()
119
+ new_data.update(kwargs)
120
+ _settings = Settings(**new_data)
121
+ return _settings