ash-98 commited on
Commit
d48ac80
·
1 Parent(s): be23f76

Initial Commit

Browse files
Files changed (7) hide show
  1. .gitattributes copy +35 -0
  2. .gitignore +1 -0
  3. .streamlit/config.toml +5 -0
  4. README copy.md +14 -0
  5. app.py +193 -0
  6. requirements.txt +82 -0
  7. utils.py +144 -0
.gitattributes copy ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1 @@
 
 
1
+ Dockerfile
.streamlit/config.toml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ [theme]
2
+ primaryColor="#01d2fc"
3
+ backgroundColor="#252040"
4
+ secondaryBackgroundColor="#262626"
5
+ textColor="#f4f4f4"
README copy.md ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Cal Test
3
+ emoji: 🌍
4
+ colorFrom: purple
5
+ colorTo: blue
6
+ sdk: streamlit
7
+ sdk_version: 1.44.0
8
+ app_file: app.py
9
+ pinned: false
10
+ license: apache-2.0
11
+ python_version: 3.13
12
+ ---
13
+
14
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,193 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import asyncio
3
+ import tokonomics
4
+ from utils import create_model_hierarchy
5
+
6
+ st.set_page_config(page_title="LLM Pricing App", layout="wide")
7
+
8
+ # --------------------------
9
+ # Async Data Loading Function
10
+ # --------------------------
11
+ async def load_data():
12
+ """Simulate loading data asynchronously."""
13
+ AVAILABLE_MODELS = await tokonomics.get_available_models()
14
+ hierarchy = create_model_hierarchy(AVAILABLE_MODELS)
15
+ FILTERED_MODELS = []
16
+ MODEL_PRICING = {}
17
+ PROVIDERS = list(hierarchy.keys())
18
+ for provider in PROVIDERS:
19
+ for model_family in hierarchy[provider]:
20
+ for model_version in hierarchy[provider][model_family].keys():
21
+ for region in hierarchy[provider][model_family][model_version]:
22
+ model_id = hierarchy[provider][model_family][model_version][region]
23
+ MODEL_PRICING[model_id] = await tokonomics.get_model_costs(model_id)
24
+ FILTERED_MODELS.append(model_id)
25
+ return FILTERED_MODELS, MODEL_PRICING, PROVIDERS
26
+
27
+ # --------------------------
28
+ # Provider Change Function
29
+ # --------------------------
30
+ def provider_change(provider, selected_type, all_types=["text", "vision", "video", "image"]):
31
+ """Filter models based on the selected provider and type."""
32
+ all_models = st.session_state.get("models", [])
33
+ new_models = []
34
+ others = [a_type for a_type in all_types if selected_type != a_type]
35
+ for model_name in all_models:
36
+ if provider in model_name:
37
+ if selected_type in model_name:
38
+ new_models.append(model_name)
39
+ elif any(other in model_name for other in others):
40
+ continue
41
+ else:
42
+ new_models.append(model_name)
43
+ return new_models if new_models else all_models
44
+
45
+ # --------------------------
46
+ # Estimate Cost Function (Updated)
47
+ # --------------------------
48
+ def estimate_cost(num_alerts, input_size, output_size, model_id):
49
+ pricing = st.session_state.get("pricing", {})
50
+ cost_token = pricing.get(model_id)
51
+ if not cost_token:
52
+ return "NA"
53
+ input_tokens = round(input_size * 1.3)
54
+ output_tokens = round(output_size * 1.3)
55
+ price_day = cost_token.get("input_cost_per_token", 0) * input_tokens + \
56
+ cost_token.get("output_cost_per_token", 0) * output_tokens
57
+ price_total = price_day * num_alerts
58
+ return f"""## Estimated Cost:
59
+
60
+ Day Price: {price_total:0.2f} USD
61
+ Month Price: {price_total * 31:0.2f} USD
62
+ Year Price: {price_total * 365:0.2f} USD
63
+ """
64
+
65
+ # --------------------------
66
+ # Load Data into Session State (only once)
67
+ # --------------------------
68
+ if "data_loaded" not in st.session_state:
69
+ with st.spinner("Loading pricing data..."):
70
+ models, pricing, providers = asyncio.run(load_data())
71
+ st.session_state["models"] = models
72
+ st.session_state["pricing"] = pricing
73
+ st.session_state["providers"] = providers
74
+ st.session_state["data_loaded"] = True
75
+
76
+ # --------------------------
77
+ # Sidebar
78
+ # --------------------------
79
+ with st.sidebar:
80
+ st.image("https://cdn.prod.website-files.com/630f558f2a15ca1e88a2f774/631f1436ad7a0605fecc5e15_Logo.svg",
81
+ use_container_width=True)
82
+ st.markdown(
83
+ """ Visit: [https://www.priam.ai](https://www.priam.ai)
84
+ """
85
+ )
86
+ st.divider()
87
+ st.sidebar.title("LLM Pricing Calculator")
88
+
89
+ # --------------------------
90
+ # Main Content Layout (Model Selection Tab)
91
+ # --------------------------
92
+ tab1, tab2 = st.tabs(["Model Selection", "About"])
93
+
94
+ with tab1:
95
+ st.header("LLM Pricing App")
96
+
97
+ # --- Row 1: Provider/Type and Model Selection ---
98
+ col_left, col_right = st.columns(2)
99
+ with col_left:
100
+ selected_provider = st.selectbox(
101
+ "Select a provider",
102
+ st.session_state["providers"],
103
+ index=st.session_state["providers"].index("azure") if "azure" in st.session_state["providers"] else 0
104
+ )
105
+ selected_type = st.radio("Select type", options=["text", "image"], index=0)
106
+
107
+ with col_right:
108
+ # Filter models based on the selected provider and type
109
+ filtered_models = provider_change(selected_provider, selected_type)
110
+
111
+ if filtered_models:
112
+ # Force "gpt-4-turbo" as default if available; otherwise, default to the first model.
113
+ default_model = "o1" if "o1" in filtered_models else filtered_models[0]
114
+ selected_model = st.selectbox(
115
+ "Select a model",
116
+ options=filtered_models,
117
+ index=filtered_models.index(default_model)
118
+ )
119
+ else:
120
+ selected_model = None
121
+ st.write("No models available")
122
+
123
+ # --- Row 2: Alert Stats ---
124
+ col1, col2, col3 = st.columns(3)
125
+ with col1:
126
+ num_alerts = st.number_input(
127
+ "Security Alerts Per Day",
128
+ value=100,
129
+ min_value=1,
130
+ step=1,
131
+ help="Number of security alerts to analyze daily"
132
+ )
133
+ with col2:
134
+ input_size = st.number_input(
135
+ "Alert Content Size (characters)",
136
+ value=1000,
137
+ min_value=1,
138
+ step=1,
139
+ help="Include logs, metadata, and context per alert"
140
+ )
141
+ with col3:
142
+ output_size = st.number_input(
143
+ "Analysis Output Size (characters)",
144
+ value=500,
145
+ min_value=1,
146
+ step=1,
147
+ help="Expected length of security analysis and recommendations"
148
+ )
149
+
150
+ # --- Row 3: Buttons ---
151
+ btn_col1, btn_col2 = st.columns(2)
152
+ with btn_col1:
153
+ if st.button("Estimate"):
154
+ if selected_model:
155
+ st.session_state["result"] = estimate_cost(num_alerts, input_size, output_size, selected_model)
156
+ else:
157
+ st.session_state["result"] = "No model selected."
158
+ with btn_col2:
159
+ if st.button("Refresh Pricing Data"):
160
+ with st.spinner("Refreshing pricing data..."):
161
+ models, pricing, providers = asyncio.run(load_data())
162
+ st.session_state["models"] = models
163
+ st.session_state["pricing"] = pricing
164
+ st.session_state["providers"] = providers
165
+ st.success("Pricing data refreshed!")
166
+
167
+ st.divider()
168
+ # --- Display Results ---
169
+ st.markdown("### Results")
170
+ if "result" in st.session_state:
171
+ st.write(st.session_state["result"])
172
+ else:
173
+ st.write("Use the buttons above to estimate costs.")
174
+
175
+ # --- Clear Button Below Results ---
176
+ if st.button("Clear"):
177
+ st.session_state.pop("result", None)
178
+ st.rerun()
179
+
180
+ with tab2:
181
+ st.markdown(
182
+ """
183
+ ## About This App
184
+
185
+ This is based on the tokonomics package.
186
+
187
+ - The app downloads the latest pricing from the LiteLLM repository.
188
+ - Using simple maths to estimate the total tokens.
189
+ - Version 0.1
190
+
191
+ Website: [https://www.priam.ai](https://www.priam.ai)
192
+ """
193
+ )
requirements.txt ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Core dependencies
2
+ requests
3
+ tokonomics
4
+ aiofiles==23.2.1
5
+ altair==5.5.0
6
+ annotated-types==0.7.0
7
+ anyenv==0.4.11
8
+ anyio==4.9.0
9
+ appdirs==1.4.4
10
+ attrs==25.3.0
11
+ audioop-lts==0.2.1
12
+ blinker==1.9.0
13
+ cachetools==5.5.2
14
+ certifi==2025.1.31
15
+ charset-normalizer==3.4.1
16
+ click==8.1.8
17
+ fastapi==0.115.12
18
+ ffmpy==0.5.0
19
+ filelock==3.18.0
20
+ fsspec==2025.3.0
21
+ gitdb==4.0.12
22
+ GitPython==3.1.44
23
+ gradio==5.23.0
24
+ gradio_client==1.8.0
25
+ groovy==0.1.2
26
+ h11==0.14.0
27
+ hishel==0.1.1
28
+ httpcore==1.0.7
29
+ httpx==0.28.1
30
+ huggingface-hub==0.29.3
31
+ idna==3.10
32
+ Jinja2==3.1.6
33
+ jsonschema==4.23.0
34
+ jsonschema-specifications==2024.10.1
35
+ markdown-it-py==3.0.0
36
+ MarkupSafe==3.0.2
37
+ mdurl==0.1.2
38
+ narwhals==1.32.0
39
+ numpy==2.2.4
40
+ orjson==3.10.16
41
+ packaging==24.2
42
+ pandas==2.2.3
43
+ pillow==11.1.0
44
+ platformdirs==4.3.7
45
+ protobuf==5.29.4
46
+ pyarrow==19.0.1
47
+ pydantic==2.10.6
48
+ pydantic_core==2.27.2
49
+ pydeck==0.9.1
50
+ pydub==0.25.1
51
+ Pygments==2.19.1
52
+ python-dateutil==2.9.0.post0
53
+ python-dotenv==1.0.1
54
+ python-multipart==0.0.20
55
+ pytz==2025.2
56
+ PyYAML==6.0.2
57
+ referencing==0.36.2
58
+ requests==2.32.3
59
+ rich==13.9.4
60
+ rpds-py==0.23.1
61
+ ruff==0.11.2
62
+ safehttpx==0.1.6
63
+ semantic-version==2.10.0
64
+ shellingham==1.5.4
65
+ six==1.17.0
66
+ smmap==5.0.2
67
+ sniffio==1.3.1
68
+ starlette==0.46.1
69
+ streamlit==1.44.0
70
+ tenacity==9.0.0
71
+ tokonomics==0.3.9
72
+ toml==0.10.2
73
+ tomlkit==0.13.2
74
+ tornado==6.4.2
75
+ tqdm==4.67.1
76
+ typer==0.15.2
77
+ typing_extensions==4.12.2
78
+ tzdata==2025.2
79
+ urllib3==2.3.0
80
+ uvicorn==0.34.0
81
+ watchdog==6.0.0
82
+ websockets==15.0.1
utils.py ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import List,Dict
2
+ import re
3
+
4
+ def parse_model_entries(model_entries: List[str]) -> List[Dict[str, str]]:
5
+ """
6
+ Parse a list of model entries into structured dictionaries with provider, model name, version, region, and type.
7
+
8
+ Args:
9
+ model_entries: List of model entry strings as found in models.txt
10
+
11
+ Returns:
12
+ List of dictionaries with parsed model information containing keys:
13
+ - provider: Name of the provider (e.g., 'azure', 'openai', 'anthropic', etc.)
14
+ - model_name: Base name of the model
15
+ - version: Version of the model (if available)
16
+ - region: Deployment region (if available)
17
+ - model_type: Type of the model (text, image, audio based on pattern analysis)
18
+ """
19
+ parsed_models = []
20
+
21
+ # Common provider prefixes to identify
22
+ known_providers = [
23
+ 'azure', 'bedrock', 'anthropic', 'openai', 'cohere', 'google',
24
+ 'mistral', 'meta', 'amazon', 'ai21', 'anyscale', 'stability',
25
+ 'cloudflare', 'databricks', 'cerebras', 'assemblyai'
26
+ ]
27
+
28
+ # Image-related keywords to identify image models
29
+ image_indicators = ['dall-e', 'stable-diffusion', 'image', 'canvas', 'x-', 'steps']
30
+
31
+ # Audio-related keywords to identify audio models
32
+ audio_indicators = ['whisper', 'tts', 'audio', 'voice']
33
+
34
+ for entry in model_entries:
35
+ model_info = {
36
+ 'provider': '',
37
+ 'model_name': '',
38
+ 'version': '',
39
+ 'region': '',
40
+ 'model_type': 'text' # Default to text
41
+ }
42
+
43
+ # Check for image models
44
+ if any(indicator in entry.lower() for indicator in image_indicators):
45
+ model_info['model_type'] = 'image'
46
+
47
+ # Check for audio models
48
+ elif any(indicator in entry.lower() for indicator in audio_indicators):
49
+ model_info['model_type'] = 'audio'
50
+
51
+ # Parse the entry based on common patterns
52
+ parts = entry.split('/')
53
+
54
+ # Handle region and provider extraction
55
+ if len(parts) >= 2:
56
+ # Extract provider from the beginning (common pattern)
57
+ if parts[0].lower() in known_providers:
58
+ model_info['provider'] = parts[0].lower()
59
+
60
+ # For bedrock and azure, the region is often the next part
61
+ if parts[0].lower() in ['bedrock', 'azure'] and len(parts) >= 3:
62
+ # Skip commitment parts if present
63
+ if 'commitment' not in parts[1]:
64
+ model_info['region'] = parts[1]
65
+
66
+ # The last part typically contains the model name and possibly version
67
+ model_with_version = parts[-1]
68
+ else:
69
+ # For single-part entries
70
+ model_with_version = entry
71
+
72
+ # Extract provider from model name if not already set
73
+ if not model_info['provider']:
74
+ # Look for known providers within the model name
75
+ for provider in known_providers:
76
+ if provider in model_with_version.lower() or f'{provider}.' in model_with_version.lower():
77
+ model_info['provider'] = provider
78
+ # Remove provider prefix if it exists at the beginning
79
+ if model_with_version.lower().startswith(f'{provider}.'):
80
+ model_with_version = model_with_version[len(provider) + 1:]
81
+ break
82
+
83
+ # Extract version information
84
+ version_match = re.search(r'[:.-]v(\d+(?:\.\d+)*(?:-\d+)?|\d+)(?::\d+)?$', model_with_version)
85
+ if version_match:
86
+ model_info['version'] = version_match.group(1)
87
+ # Remove version from model name
88
+ model_name = model_with_version[:version_match.start()]
89
+ else:
90
+ # Look for date-based versions like 2024-08-06
91
+ date_match = re.search(r'-(\d{4}-\d{2}-\d{2})$', model_with_version)
92
+ if date_match:
93
+ model_info['version'] = date_match.group(1)
94
+ model_name = model_with_version[:date_match.start()]
95
+ else:
96
+ model_name = model_with_version
97
+
98
+ # Clean up model name by removing trailing/leading separators
99
+ model_info['model_name'] = model_name.strip('.-:')
100
+
101
+ parsed_models.append(model_info)
102
+
103
+ return parsed_models
104
+
105
+
106
+ def create_model_hierarchy(model_entries: List[str]) -> Dict[str, Dict[str, Dict[str, Dict[str, str]]]]:
107
+ """
108
+ Organize model entries into a nested dictionary structure by provider, model, version, and region.
109
+
110
+ Args:
111
+ model_entries: List of model entry strings as found in models.txt
112
+
113
+ Returns:
114
+ Nested dictionary with the structure:
115
+ Provider -> Model -> Version -> Region = full model string
116
+ If region or version is None, they are replaced with "NA".
117
+ """
118
+ # Parse the model entries to get structured information
119
+ parsed_models = parse_model_entries(model_entries)
120
+
121
+ # Create the nested dictionary structure
122
+ hierarchy = {}
123
+
124
+ for i, model_info in enumerate(parsed_models):
125
+ provider = model_info['provider'] if model_info['provider'] else 'unknown'
126
+ model_name = model_info['model_name']
127
+ version = model_info['version'] if model_info['version'] else 'NA'
128
+ # For Azure models, always use 'NA' as region since they are globally available
129
+ region = 'NA' if provider == 'azure' else (model_info['region'] if model_info['region'] else 'NA')
130
+
131
+ # Initialize nested dictionaries if they don't exist
132
+ if provider not in hierarchy:
133
+ hierarchy[provider] = {}
134
+
135
+ if model_name not in hierarchy[provider]:
136
+ hierarchy[provider][model_name] = {}
137
+
138
+ if version not in hierarchy[provider][model_name]:
139
+ hierarchy[provider][model_name][version] = {}
140
+
141
+ # Store the full model string at the leaf node
142
+ hierarchy[provider][model_name][version][region] = model_entries[i]
143
+
144
+ return hierarchy