local-openai2anthropic 0.1.0__py3-none-any.whl → 0.3.6__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.
@@ -3,7 +3,7 @@
3
3
  local-openai2anthropic: A proxy server that converts Anthropic Messages API to OpenAI API.
4
4
  """
5
5
 
6
- __version__ = "0.1.0"
6
+ __version__ = "0.3.6"
7
7
 
8
8
  from local_openai2anthropic.protocol import (
9
9
  AnthropicError,
@@ -0,0 +1,7 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """Allow running as python -m local_openai2anthropic"""
3
+
4
+ from local_openai2anthropic.main import main
5
+
6
+ if __name__ == "__main__":
7
+ main()
@@ -3,44 +3,137 @@
3
3
  Configuration settings for the proxy server.
4
4
  """
5
5
 
6
+ import sys
6
7
  from functools import lru_cache
8
+ from pathlib import Path
7
9
  from typing import Optional
8
10
 
9
- from pydantic_settings import BaseSettings, SettingsConfigDict
11
+ from pydantic import BaseModel, ConfigDict
10
12
 
11
13
 
12
- class Settings(BaseSettings):
13
- """Application settings loaded from environment variables."""
14
-
15
- model_config = SettingsConfigDict(
16
- env_prefix="OA2A_", # OpenAI-to-Anthropic prefix
17
- env_file=".env",
18
- env_file_encoding="utf-8",
19
- case_sensitive=False,
20
- )
21
-
14
+ def get_config_dir() -> Path:
15
+ """Get platform-specific config directory.
16
+
17
+ Returns:
18
+ Path to the config directory (~/.oa2a)
19
+ """
20
+ return Path.home() / ".oa2a"
21
+
22
+
23
+ def get_config_file() -> Path:
24
+ """Get config file path.
25
+
26
+ Returns:
27
+ Path to the config file (~/.oa2a/config.toml)
28
+ """
29
+ return get_config_dir() / "config.toml"
30
+
31
+
32
+ def create_default_config() -> bool:
33
+ """Create default config file if not exists.
34
+
35
+ Returns:
36
+ True if a new config file was created, False if it already exists
37
+ """
38
+ config_file = get_config_file()
39
+ if config_file.exists():
40
+ return False
41
+
42
+ config_dir = get_config_dir()
43
+ config_dir.mkdir(parents=True, exist_ok=True)
44
+
45
+ # Set restrictive permissions (0o600) for the config directory on Unix-like systems
46
+ if sys.platform != "win32":
47
+ config_dir.chmod(0o700)
48
+
49
+ default_config = """# OA2A Configuration File
50
+ # Place this file at ~/.oa2a/config.toml
51
+
52
+ # OpenAI API Configuration
53
+ openai_api_key = ""
54
+ openai_base_url = "https://api.openai.com/v1"
55
+ openai_org_id = ""
56
+ openai_project_id = ""
57
+
58
+ # Server Configuration
59
+ host = "0.0.0.0"
60
+ port = 8080
61
+ request_timeout = 300.0
62
+
63
+ # API Key for authenticating requests to this server (optional)
64
+ api_key = ""
65
+
66
+ # CORS settings
67
+ cors_origins = ["*"]
68
+ cors_credentials = true
69
+ cors_methods = ["*"]
70
+ cors_headers = ["*"]
71
+
72
+ # Logging
73
+ log_level = "INFO"
74
+ log_dir = "" # Empty uses platform-specific default
75
+
76
+ # Tavily Web Search Configuration
77
+ tavily_api_key = ""
78
+ tavily_timeout = 30.0
79
+ tavily_max_results = 5
80
+ websearch_max_uses = 5
81
+ """
82
+ config_file.write_text(default_config, encoding="utf-8")
83
+
84
+ # Set restrictive permissions (0o600) for the config file on Unix-like systems
85
+ if sys.platform != "win32":
86
+ config_file.chmod(0o600)
87
+
88
+ return True
89
+
90
+
91
+ def load_config_from_file() -> dict:
92
+ """Load configuration from TOML file.
93
+
94
+ Returns:
95
+ Dictionary containing configuration values, empty dict if file doesn't exist
96
+ """
97
+ if sys.version_info >= (3, 11):
98
+ import tomllib
99
+ else:
100
+ import tomli as tomllib
101
+
102
+ config_file = get_config_file()
103
+ if not config_file.exists():
104
+ return {}
105
+ with open(config_file, "rb") as f:
106
+ return tomllib.load(f)
107
+
108
+
109
+ class Settings(BaseModel):
110
+ """Application settings loaded from config file."""
111
+
112
+ model_config = ConfigDict(extra="ignore")
113
+
22
114
  # OpenAI API Configuration
23
- openai_api_key: str
115
+ openai_api_key: Optional[str] = None
24
116
  openai_base_url: str = "https://api.openai.com/v1"
25
117
  openai_org_id: Optional[str] = None
26
118
  openai_project_id: Optional[str] = None
27
-
119
+
28
120
  # Server Configuration
29
121
  host: str = "0.0.0.0"
30
122
  port: int = 8080
31
123
  request_timeout: float = 300.0 # 5 minutes
32
-
124
+
33
125
  # API Key for authenticating requests to this server (optional)
34
126
  api_key: Optional[str] = None
35
-
127
+
36
128
  # CORS settings
37
129
  cors_origins: list[str] = ["*"]
38
130
  cors_credentials: bool = True
39
131
  cors_methods: list[str] = ["*"]
40
132
  cors_headers: list[str] = ["*"]
41
-
133
+
42
134
  # Logging
43
135
  log_level: str = "INFO"
136
+ log_dir: str = "" # Empty means use platform-specific default
44
137
 
45
138
  # Tavily Web Search Configuration
46
139
  tavily_api_key: Optional[str] = None
@@ -60,8 +153,29 @@ class Settings(BaseSettings):
60
153
  headers["OpenAI-Project"] = self.openai_project_id
61
154
  return headers
62
155
 
156
+ @classmethod
157
+ def from_toml(cls) -> "Settings":
158
+ """Load settings from TOML config file.
159
+
160
+ Returns:
161
+ Settings instance populated from config file
162
+ """
163
+ config_data = load_config_from_file()
164
+ return cls(**config_data)
165
+
63
166
 
64
167
  @lru_cache
65
168
  def get_settings() -> Settings:
66
- """Get cached settings instance."""
67
- return Settings()
169
+ """Get cached settings instance.
170
+
171
+ Creates default config file if it doesn't exist and notifies the user.
172
+
173
+ Returns:
174
+ Settings instance loaded from config file
175
+ """
176
+ created = create_default_config()
177
+ if created:
178
+ config_file = get_config_file()
179
+ print(f"Created default config file: {config_file}")
180
+ print("Please edit it to add your API keys and settings.")
181
+ return Settings.from_toml()