cost-katana 1.0.1__py3-none-any.whl → 1.0.2__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.
- cost_katana/__init__.py +16 -14
- cost_katana/cli.py +581 -119
- cost_katana/client.py +298 -75
- cost_katana/config.py +82 -85
- cost_katana/exceptions.py +19 -1
- cost_katana/models.py +110 -111
- {cost_katana-1.0.1.dist-info → cost_katana-1.0.2.dist-info}/METADATA +5 -4
- cost_katana-1.0.2.dist-info/RECORD +12 -0
- cost_katana-1.0.1.dist-info/RECORD +0 -12
- {cost_katana-1.0.1.dist-info → cost_katana-1.0.2.dist-info}/WHEEL +0 -0
- {cost_katana-1.0.1.dist-info → cost_katana-1.0.2.dist-info}/entry_points.txt +0 -0
- {cost_katana-1.0.1.dist-info → cost_katana-1.0.2.dist-info}/licenses/LICENSE +0 -0
- {cost_katana-1.0.1.dist-info → cost_katana-1.0.2.dist-info}/top_level.txt +0 -0
cost_katana/config.py
CHANGED
@@ -5,13 +5,14 @@ Configuration management for Cost Katana
|
|
5
5
|
import json
|
6
6
|
import os
|
7
7
|
from typing import Dict, Any, Optional
|
8
|
-
from dataclasses import dataclass, asdict
|
8
|
+
from dataclasses import dataclass, asdict, fields
|
9
9
|
from pathlib import Path
|
10
10
|
|
11
|
+
|
11
12
|
@dataclass
|
12
13
|
class Config:
|
13
14
|
"""Configuration class for Cost Katana client"""
|
14
|
-
|
15
|
+
|
15
16
|
api_key: Optional[str] = None
|
16
17
|
base_url: str = "https://cost-katana-backend.store"
|
17
18
|
timeout: int = 30
|
@@ -26,35 +27,35 @@ class Config:
|
|
26
27
|
enable_failover: bool = True
|
27
28
|
cost_limit_per_request: Optional[float] = None
|
28
29
|
cost_limit_per_day: Optional[float] = None
|
29
|
-
|
30
|
+
|
30
31
|
def __post_init__(self):
|
31
32
|
"""Load from environment variables if not set"""
|
32
33
|
if not self.api_key:
|
33
|
-
self.api_key = os.getenv(
|
34
|
-
|
34
|
+
self.api_key = os.getenv("API_KEY")
|
35
|
+
|
35
36
|
# Override with environment variables if they exist
|
36
|
-
if os.getenv(
|
37
|
-
self.base_url = os.getenv(
|
38
|
-
if os.getenv(
|
39
|
-
self.default_model = os.getenv(
|
40
|
-
if os.getenv(
|
41
|
-
self.timeout = int(os.getenv(
|
42
|
-
|
37
|
+
if os.getenv("COST_KATANA_BASE_URL"):
|
38
|
+
self.base_url = os.getenv("COST_KATANA_BASE_URL")
|
39
|
+
if os.getenv("COST_KATANA_DEFAULT_MODEL"):
|
40
|
+
self.default_model = os.getenv("COST_KATANA_DEFAULT_MODEL")
|
41
|
+
if os.getenv("COST_KATANA_TIMEOUT"):
|
42
|
+
self.timeout = int(os.getenv("COST_KATANA_TIMEOUT"))
|
43
|
+
|
43
44
|
@classmethod
|
44
|
-
def from_file(cls, config_path: str) ->
|
45
|
+
def from_file(cls, config_path: str) -> "Config":
|
45
46
|
"""
|
46
47
|
Load configuration from JSON file.
|
47
|
-
|
48
|
+
|
48
49
|
Args:
|
49
50
|
config_path: Path to JSON configuration file
|
50
|
-
|
51
|
+
|
51
52
|
Returns:
|
52
53
|
Config instance
|
53
|
-
|
54
|
+
|
54
55
|
Example config.json:
|
55
56
|
{
|
56
57
|
"api_key": "dak_your_key_here",
|
57
|
-
"base_url": "https://
|
58
|
+
"base_url": "https://cost-katana-backend.store",
|
58
59
|
"default_model": "claude-3-sonnet",
|
59
60
|
"default_temperature": 0.3,
|
60
61
|
"cost_limit_per_day": 100.0,
|
@@ -70,54 +71,56 @@ class Config:
|
|
70
71
|
}
|
71
72
|
}
|
72
73
|
"""
|
73
|
-
|
74
|
-
|
75
|
-
if not
|
74
|
+
config_path_obj = Path(config_path).expanduser()
|
75
|
+
|
76
|
+
if not config_path_obj.exists():
|
76
77
|
raise FileNotFoundError(f"Configuration file not found: {config_path}")
|
77
|
-
|
78
|
+
|
78
79
|
try:
|
79
|
-
with open(
|
80
|
+
with open(config_path_obj, "r", encoding="utf-8") as f:
|
80
81
|
data = json.load(f)
|
81
82
|
except json.JSONDecodeError as e:
|
82
83
|
raise ValueError(f"Invalid JSON in config file: {e}")
|
83
|
-
|
84
|
+
|
84
85
|
# Extract known fields
|
85
|
-
config_fields = {
|
86
|
-
|
87
|
-
}
|
88
|
-
|
86
|
+
config_fields = {field.name for field in fields(cls)}
|
87
|
+
|
89
88
|
config_data = {k: v for k, v in data.items() if k in config_fields}
|
90
89
|
config = cls(**config_data)
|
91
|
-
|
90
|
+
|
92
91
|
# Store additional data
|
93
|
-
|
94
|
-
|
92
|
+
setattr(
|
93
|
+
config,
|
94
|
+
"_extra_data",
|
95
|
+
{k: v for k, v in data.items() if k not in config_fields},
|
96
|
+
)
|
97
|
+
|
95
98
|
return config
|
96
|
-
|
99
|
+
|
97
100
|
def to_dict(self) -> Dict[str, Any]:
|
98
101
|
"""Convert config to dictionary"""
|
99
102
|
result = asdict(self)
|
100
|
-
|
103
|
+
|
101
104
|
# Add extra data if it exists
|
102
|
-
if hasattr(self,
|
105
|
+
if hasattr(self, "_extra_data"):
|
103
106
|
result.update(self._extra_data)
|
104
|
-
|
107
|
+
|
105
108
|
return result
|
106
|
-
|
109
|
+
|
107
110
|
def save(self, config_path: str):
|
108
111
|
"""Save configuration to JSON file"""
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
with open(
|
112
|
+
config_path_obj = Path(config_path).expanduser()
|
113
|
+
config_path_obj.parent.mkdir(parents=True, exist_ok=True)
|
114
|
+
|
115
|
+
with open(config_path_obj, "w", encoding="utf-8") as f:
|
113
116
|
json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
|
114
|
-
|
117
|
+
|
115
118
|
def get_provider_config(self, provider: str) -> Dict[str, Any]:
|
116
119
|
"""Get configuration for a specific provider"""
|
117
|
-
if hasattr(self,
|
118
|
-
return self._extra_data[
|
120
|
+
if hasattr(self, "_extra_data") and "providers" in self._extra_data:
|
121
|
+
return self._extra_data["providers"].get(provider, {})
|
119
122
|
return {}
|
120
|
-
|
123
|
+
|
121
124
|
def get_model_mapping(self, model_name: str) -> str:
|
122
125
|
"""
|
123
126
|
Map user-friendly model names to internal model IDs.
|
@@ -128,54 +131,48 @@ class Config:
|
|
128
131
|
# Based on actual models available from Cost Katana Backend
|
129
132
|
default_mappings = {
|
130
133
|
# Amazon Nova models (primary recommendation)
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
134
|
+
"nova-micro": "amazon.nova-micro-v1:0",
|
135
|
+
"nova-lite": "amazon.nova-lite-v1:0",
|
136
|
+
"nova-pro": "amazon.nova-pro-v1:0",
|
137
|
+
"fast": "amazon.nova-micro-v1:0",
|
138
|
+
"balanced": "amazon.nova-lite-v1:0",
|
139
|
+
"powerful": "amazon.nova-pro-v1:0",
|
138
140
|
# Anthropic Claude models
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
141
|
+
"claude-3-haiku": "anthropic.claude-3-haiku-20240307-v1:0",
|
142
|
+
"claude-3-sonnet": "anthropic.claude-3-sonnet-20240229-v1:0",
|
143
|
+
"claude-3-opus": "anthropic.claude-3-opus-20240229-v1:0",
|
144
|
+
"claude-3.5-haiku": "anthropic.claude-3-5-haiku-20241022-v1:0",
|
145
|
+
"claude-3.5-sonnet": "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
146
|
+
"claude": "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
146
147
|
# Meta Llama models
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
148
|
+
"llama-3.1-8b": "meta.llama3-1-8b-instruct-v1:0",
|
149
|
+
"llama-3.1-70b": "meta.llama3-1-70b-instruct-v1:0",
|
150
|
+
"llama-3.1-405b": "meta.llama3-1-405b-instruct-v1:0",
|
151
|
+
"llama-3.2-1b": "meta.llama3-2-1b-instruct-v1:0",
|
152
|
+
"llama-3.2-3b": "meta.llama3-2-3b-instruct-v1:0",
|
153
153
|
# Mistral models
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
154
|
+
"mistral-7b": "mistral.mistral-7b-instruct-v0:2",
|
155
|
+
"mixtral-8x7b": "mistral.mixtral-8x7b-instruct-v0:1",
|
156
|
+
"mistral-large": "mistral.mistral-large-2402-v1:0",
|
158
157
|
# Cohere models
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
158
|
+
"command": "cohere.command-text-v14",
|
159
|
+
"command-light": "cohere.command-light-text-v14",
|
160
|
+
"command-r": "cohere.command-r-v1:0",
|
161
|
+
"command-r-plus": "cohere.command-r-plus-v1:0",
|
164
162
|
# AI21 models
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
163
|
+
"jamba": "ai21.jamba-instruct-v1:0",
|
164
|
+
"j2-ultra": "ai21.j2-ultra-v1",
|
165
|
+
"j2-mid": "ai21.j2-mid-v1",
|
169
166
|
# Backwards compatibility aliases
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
167
|
+
"gemini-2.0-flash": "amazon.nova-lite-v1:0", # Map to similar performance
|
168
|
+
"gemini-pro": "amazon.nova-pro-v1:0",
|
169
|
+
"gpt-4": "anthropic.claude-3-5-sonnet-20241022-v2:0",
|
170
|
+
"gpt-3.5-turbo": "anthropic.claude-3-haiku-20240307-v1:0",
|
174
171
|
}
|
175
|
-
|
172
|
+
|
176
173
|
# Check for custom mappings in config
|
177
|
-
if hasattr(self,
|
178
|
-
custom_mappings = self._extra_data[
|
174
|
+
if hasattr(self, "_extra_data") and "model_mappings" in self._extra_data:
|
175
|
+
custom_mappings = self._extra_data["model_mappings"]
|
179
176
|
default_mappings.update(custom_mappings)
|
180
|
-
|
181
|
-
return default_mappings.get(model_name, model_name)
|
177
|
+
|
178
|
+
return default_mappings.get(model_name, model_name)
|
cost_katana/exceptions.py
CHANGED
@@ -2,38 +2,56 @@
|
|
2
2
|
Custom exceptions for Cost Katana
|
3
3
|
"""
|
4
4
|
|
5
|
+
|
5
6
|
class CostKatanaError(Exception):
|
6
7
|
"""Base exception for Cost Katana errors"""
|
8
|
+
|
7
9
|
pass
|
8
10
|
|
11
|
+
|
9
12
|
class AuthenticationError(CostKatanaError):
|
10
13
|
"""Raised when authentication fails"""
|
14
|
+
|
11
15
|
pass
|
12
16
|
|
17
|
+
|
13
18
|
class ModelNotAvailableError(CostKatanaError):
|
14
19
|
"""Raised when requested model is not available"""
|
20
|
+
|
15
21
|
pass
|
16
22
|
|
23
|
+
|
17
24
|
class RateLimitError(CostKatanaError):
|
18
25
|
"""Raised when rate limit is exceeded"""
|
26
|
+
|
19
27
|
pass
|
20
28
|
|
29
|
+
|
21
30
|
class CostLimitExceededError(CostKatanaError):
|
22
31
|
"""Raised when cost limits are exceeded"""
|
32
|
+
|
23
33
|
pass
|
24
34
|
|
35
|
+
|
25
36
|
class ConversationNotFoundError(CostKatanaError):
|
26
37
|
"""Raised when conversation is not found"""
|
38
|
+
|
27
39
|
pass
|
28
40
|
|
41
|
+
|
29
42
|
class InvalidConfigurationError(CostKatanaError):
|
30
43
|
"""Raised when configuration is invalid"""
|
44
|
+
|
31
45
|
pass
|
32
46
|
|
47
|
+
|
33
48
|
class NetworkError(CostKatanaError):
|
34
49
|
"""Raised when network requests fail"""
|
50
|
+
|
35
51
|
pass
|
36
52
|
|
53
|
+
|
37
54
|
class ModelTimeoutError(CostKatanaError):
|
38
55
|
"""Raised when model request times out"""
|
39
|
-
|
56
|
+
|
57
|
+
pass
|