arize-phoenix 11.7.0__py3-none-any.whl → 11.8.0__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.
Potentially problematic release.
This version of arize-phoenix might be problematic. Click here for more details.
- {arize_phoenix-11.7.0.dist-info → arize_phoenix-11.8.0.dist-info}/METADATA +14 -2
- {arize_phoenix-11.7.0.dist-info → arize_phoenix-11.8.0.dist-info}/RECORD +32 -31
- phoenix/config.py +33 -0
- phoenix/datetime_utils.py +112 -1
- phoenix/db/helpers.py +156 -1
- phoenix/server/api/auth.py +28 -6
- phoenix/server/api/dataloaders/span_cost_summary_by_experiment.py +6 -7
- phoenix/server/api/exceptions.py +6 -0
- phoenix/server/api/input_types/TimeBinConfig.py +23 -0
- phoenix/server/api/routers/oauth2.py +19 -2
- phoenix/server/api/types/CostBreakdown.py +4 -7
- phoenix/server/api/types/Project.py +341 -73
- phoenix/server/app.py +7 -3
- phoenix/server/authorization.py +27 -2
- phoenix/server/cost_tracking/cost_details_calculator.py +22 -16
- phoenix/server/daemons/span_cost_calculator.py +2 -8
- phoenix/server/email/sender.py +2 -1
- phoenix/server/email/templates/db_disk_usage_notification.html +3 -0
- phoenix/server/static/.vite/manifest.json +36 -36
- phoenix/server/static/assets/{components-J3qjrjBf.js → components-5M9nebi4.js} +344 -263
- phoenix/server/static/assets/{index-CEObsQf_.js → index-OU2WTnGN.js} +11 -11
- phoenix/server/static/assets/{pages-CW1UdBht.js → pages-DF8rqxJ4.js} +451 -444
- phoenix/server/static/assets/{vendor-BnPh9i9e.js → vendor-Bl7CyFDw.js} +147 -147
- phoenix/server/static/assets/{vendor-arizeai-Cr9o_Iu_.js → vendor-arizeai-B_viEUUA.js} +18 -480
- phoenix/server/static/assets/{vendor-codemirror-k3zCIjlN.js → vendor-codemirror-vlcH1_iR.js} +1 -1
- phoenix/server/static/assets/{vendor-recharts-BdblEuGB.js → vendor-recharts-C9cQu72o.js} +25 -25
- phoenix/server/static/assets/{vendor-shiki-DPtuv2M4.js → vendor-shiki-BsknB7bv.js} +1 -1
- phoenix/version.py +1 -1
- {arize_phoenix-11.7.0.dist-info → arize_phoenix-11.8.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-11.7.0.dist-info → arize_phoenix-11.8.0.dist-info}/entry_points.txt +0 -0
- {arize_phoenix-11.7.0.dist-info → arize_phoenix-11.8.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-11.7.0.dist-info → arize_phoenix-11.8.0.dist-info}/licenses/LICENSE +0 -0
phoenix/server/authorization.py
CHANGED
|
@@ -25,6 +25,7 @@ Usage:
|
|
|
25
25
|
from fastapi import HTTPException, Request
|
|
26
26
|
from fastapi import status as fastapi_status
|
|
27
27
|
|
|
28
|
+
from phoenix.config import get_env_support_email
|
|
28
29
|
from phoenix.server.bearer_auth import PhoenixUser
|
|
29
30
|
|
|
30
31
|
|
|
@@ -54,9 +55,33 @@ def require_admin(request: Request) -> None:
|
|
|
54
55
|
|
|
55
56
|
|
|
56
57
|
def is_not_locked(request: Request) -> None:
|
|
58
|
+
"""
|
|
59
|
+
FastAPI dependency to ensure database operations are not locked due to insufficient storage.
|
|
60
|
+
|
|
61
|
+
This dependency checks if data insertion and update operations are disabled due to
|
|
62
|
+
storage capacity limits. When storage thresholds are exceeded, it raises an HTTP 507
|
|
63
|
+
error with actionable guidance for users.
|
|
64
|
+
|
|
65
|
+
Usage:
|
|
66
|
+
Add as a dependency to any route that modifies data:
|
|
67
|
+
|
|
68
|
+
@router.post("/create-data", dependencies=[Depends(is_not_locked)])
|
|
69
|
+
async def create_data(...):
|
|
70
|
+
...
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
HTTPException: HTTP 507 Insufficient Storage when database operations are locked.
|
|
74
|
+
The error includes guidance on resolving storage issues and support contact
|
|
75
|
+
information if configured.
|
|
76
|
+
"""
|
|
57
77
|
if request.app.state.db.should_not_insert_or_update:
|
|
78
|
+
detail = (
|
|
79
|
+
"Database operations are disabled due to insufficient storage. "
|
|
80
|
+
"Please delete old data or increase storage."
|
|
81
|
+
)
|
|
82
|
+
if support_email := get_env_support_email():
|
|
83
|
+
detail += f" Need help? Contact us at {support_email}"
|
|
58
84
|
raise HTTPException(
|
|
59
85
|
status_code=fastapi_status.HTTP_507_INSUFFICIENT_STORAGE,
|
|
60
|
-
detail=
|
|
61
|
-
"records are currently not allowed.",
|
|
86
|
+
detail=detail,
|
|
62
87
|
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from itertools import chain
|
|
2
|
-
from typing import Any, Iterable, Mapping
|
|
2
|
+
from typing import Any, Iterable, Mapping, Optional
|
|
3
3
|
|
|
4
4
|
from typing_extensions import TypeAlias
|
|
5
5
|
|
|
@@ -60,7 +60,7 @@ class SpanCostDetailsCalculator:
|
|
|
60
60
|
for p in prices
|
|
61
61
|
if p.is_prompt
|
|
62
62
|
}
|
|
63
|
-
if "input" not in self._prompt:
|
|
63
|
+
if self._prompt and "input" not in self._prompt:
|
|
64
64
|
raise ValueError("Token prices for prompt must include an 'input' token type")
|
|
65
65
|
|
|
66
66
|
# Create calculators for completion token types (is_prompt=False)
|
|
@@ -69,7 +69,7 @@ class SpanCostDetailsCalculator:
|
|
|
69
69
|
for p in prices
|
|
70
70
|
if not p.is_prompt
|
|
71
71
|
}
|
|
72
|
-
if "output" not in self._completion:
|
|
72
|
+
if self._completion and "output" not in self._completion:
|
|
73
73
|
raise ValueError("Token prices for completion must include an 'output' token type")
|
|
74
74
|
|
|
75
75
|
def calculate_details(
|
|
@@ -112,6 +112,9 @@ class SpanCostDetailsCalculator:
|
|
|
112
112
|
"""
|
|
113
113
|
prompt_details: dict[_TokenType, models.SpanCostDetail] = {}
|
|
114
114
|
completion_details: dict[_TokenType, models.SpanCostDetail] = {}
|
|
115
|
+
calculator: Optional[TokenCostCalculator]
|
|
116
|
+
cost: Optional[float]
|
|
117
|
+
cost_per_token: Optional[float]
|
|
115
118
|
|
|
116
119
|
# Phase 1: Process detailed token counts from span attributes
|
|
117
120
|
for is_prompt, prefix, calculators, results in (
|
|
@@ -128,18 +131,19 @@ class SpanCostDetailsCalculator:
|
|
|
128
131
|
tokens = max(0, int(token_count))
|
|
129
132
|
|
|
130
133
|
# Calculate cost using specific calculator or fallback to default
|
|
134
|
+
calculator = None
|
|
135
|
+
calculator_key = "input" if is_prompt else "output"
|
|
131
136
|
if token_type in calculators:
|
|
132
137
|
# Use specific calculator for this token type
|
|
133
138
|
calculator = calculators[token_type]
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
# "output" for completions
|
|
137
|
-
key = "input" if is_prompt else "output"
|
|
138
|
-
calculator = calculators[key]
|
|
139
|
-
cost = calculator.calculate_cost(attributes, tokens)
|
|
139
|
+
elif calculator_key in calculators:
|
|
140
|
+
calculator = calculators[calculator_key]
|
|
140
141
|
|
|
141
|
-
|
|
142
|
-
cost_per_token =
|
|
142
|
+
cost = None
|
|
143
|
+
cost_per_token = None
|
|
144
|
+
if calculator:
|
|
145
|
+
cost = calculator.calculate_cost(attributes, tokens)
|
|
146
|
+
cost_per_token = cost / tokens if tokens else None
|
|
143
147
|
|
|
144
148
|
detail = models.SpanCostDetail(
|
|
145
149
|
token_type=token_type,
|
|
@@ -171,11 +175,13 @@ class SpanCostDetailsCalculator:
|
|
|
171
175
|
if tokens <= 0:
|
|
172
176
|
continue
|
|
173
177
|
|
|
174
|
-
# Calculate cost using
|
|
175
|
-
cost =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
178
|
+
# Calculate cost using calculator if available
|
|
179
|
+
cost = None
|
|
180
|
+
cost_per_token = None
|
|
181
|
+
if token_type in calculators:
|
|
182
|
+
calculator = calculators[token_type]
|
|
183
|
+
cost = calculator.calculate_cost(attributes, tokens)
|
|
184
|
+
cost_per_token = cost / tokens if tokens else None
|
|
179
185
|
|
|
180
186
|
detail = models.SpanCostDetail(
|
|
181
187
|
token_type=token_type,
|
|
@@ -5,7 +5,6 @@ from asyncio import sleep
|
|
|
5
5
|
from datetime import datetime
|
|
6
6
|
from typing import Any, Mapping, NamedTuple, Optional
|
|
7
7
|
|
|
8
|
-
from sqlalchemy import inspect
|
|
9
8
|
from typing_extensions import TypeAlias
|
|
10
9
|
|
|
11
10
|
from phoenix.db import models
|
|
@@ -84,18 +83,13 @@ class SpanCostCalculator(DaemonTask):
|
|
|
84
83
|
start_time=start_time,
|
|
85
84
|
attributes=attributes,
|
|
86
85
|
)
|
|
87
|
-
if
|
|
88
|
-
return None
|
|
89
|
-
if not isinstance(inspect(cost_model).attrs.token_prices.loaded_value, list):
|
|
90
|
-
return None
|
|
91
|
-
|
|
92
|
-
calculator = SpanCostDetailsCalculator(cost_model.token_prices)
|
|
86
|
+
calculator = SpanCostDetailsCalculator(cost_model.token_prices if cost_model else [])
|
|
93
87
|
details = calculator.calculate_details(attributes)
|
|
94
88
|
if not details:
|
|
95
89
|
return None
|
|
96
90
|
|
|
97
91
|
cost = models.SpanCost(
|
|
98
|
-
model_id=cost_model.id,
|
|
92
|
+
model_id=cost_model.id if cost_model else None,
|
|
99
93
|
span_start_time=start_time,
|
|
100
94
|
)
|
|
101
95
|
for detail in details:
|
phoenix/server/email/sender.py
CHANGED
|
@@ -8,7 +8,7 @@ from anyio import to_thread
|
|
|
8
8
|
from jinja2 import Environment, FileSystemLoader, select_autoescape
|
|
9
9
|
from typing_extensions import TypeAlias
|
|
10
10
|
|
|
11
|
-
from phoenix.config import get_env_root_url
|
|
11
|
+
from phoenix.config import get_env_root_url, get_env_support_email
|
|
12
12
|
|
|
13
13
|
EMAIL_TEMPLATE_FOLDER = Path(__file__).parent / "templates"
|
|
14
14
|
|
|
@@ -96,6 +96,7 @@ class SimpleEmailSender:
|
|
|
96
96
|
current_usage_gibibytes=current_usage_gibibytes,
|
|
97
97
|
allocated_storage_gibibytes=allocated_storage_gibibytes,
|
|
98
98
|
notification_threshold_percentage=notification_threshold_percentage,
|
|
99
|
+
support_email=get_env_support_email(),
|
|
99
100
|
)
|
|
100
101
|
|
|
101
102
|
msg = EmailMessage()
|
|
@@ -12,5 +12,8 @@
|
|
|
12
12
|
<p><strong>Usage Percentage:</strong> {{ ((current_usage_gibibytes / allocated_storage_gibibytes) * 100)|round(1) }}%</p>
|
|
13
13
|
<p><strong>Notification Threshold:</strong> {{ notification_threshold_percentage }}%</p>
|
|
14
14
|
<p>Please consider removing old data or increasing your storage allocation to prevent interruption.</p>
|
|
15
|
+
{% if support_email %}
|
|
16
|
+
<p>If you need assistance, please contact support at <a id="support-email" href="mailto:{{ support_email }}">{{ support_email }}</a>.</p>
|
|
17
|
+
{% endif %}
|
|
15
18
|
</body>
|
|
16
19
|
</html>
|
|
@@ -1,28 +1,28 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_components-
|
|
3
|
-
"file": "assets/components-
|
|
2
|
+
"_components-5M9nebi4.js": {
|
|
3
|
+
"file": "assets/components-5M9nebi4.js",
|
|
4
4
|
"name": "components",
|
|
5
5
|
"imports": [
|
|
6
|
-
"_vendor-
|
|
7
|
-
"_pages-
|
|
8
|
-
"_vendor-arizeai-
|
|
9
|
-
"_vendor-codemirror-
|
|
6
|
+
"_vendor-Bl7CyFDw.js",
|
|
7
|
+
"_pages-DF8rqxJ4.js",
|
|
8
|
+
"_vendor-arizeai-B_viEUUA.js",
|
|
9
|
+
"_vendor-codemirror-vlcH1_iR.js",
|
|
10
10
|
"_vendor-three-C5WAXd5r.js"
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
|
-
"_pages-
|
|
14
|
-
"file": "assets/pages-
|
|
13
|
+
"_pages-DF8rqxJ4.js": {
|
|
14
|
+
"file": "assets/pages-DF8rqxJ4.js",
|
|
15
15
|
"name": "pages",
|
|
16
16
|
"imports": [
|
|
17
|
-
"_vendor-
|
|
18
|
-
"_vendor-arizeai-
|
|
19
|
-
"_components-
|
|
20
|
-
"_vendor-codemirror-
|
|
21
|
-
"_vendor-recharts-
|
|
17
|
+
"_vendor-Bl7CyFDw.js",
|
|
18
|
+
"_vendor-arizeai-B_viEUUA.js",
|
|
19
|
+
"_components-5M9nebi4.js",
|
|
20
|
+
"_vendor-codemirror-vlcH1_iR.js",
|
|
21
|
+
"_vendor-recharts-C9cQu72o.js"
|
|
22
22
|
]
|
|
23
23
|
},
|
|
24
|
-
"_vendor-
|
|
25
|
-
"file": "assets/vendor-
|
|
24
|
+
"_vendor-Bl7CyFDw.js": {
|
|
25
|
+
"file": "assets/vendor-Bl7CyFDw.js",
|
|
26
26
|
"name": "vendor",
|
|
27
27
|
"imports": [
|
|
28
28
|
"_vendor-three-C5WAXd5r.js"
|
|
@@ -35,33 +35,33 @@
|
|
|
35
35
|
"file": "assets/vendor-WIZid84E.css",
|
|
36
36
|
"src": "_vendor-WIZid84E.css"
|
|
37
37
|
},
|
|
38
|
-
"_vendor-arizeai-
|
|
39
|
-
"file": "assets/vendor-arizeai-
|
|
38
|
+
"_vendor-arizeai-B_viEUUA.js": {
|
|
39
|
+
"file": "assets/vendor-arizeai-B_viEUUA.js",
|
|
40
40
|
"name": "vendor-arizeai",
|
|
41
41
|
"imports": [
|
|
42
|
-
"_vendor-
|
|
42
|
+
"_vendor-Bl7CyFDw.js"
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
|
-
"_vendor-codemirror-
|
|
46
|
-
"file": "assets/vendor-codemirror-
|
|
45
|
+
"_vendor-codemirror-vlcH1_iR.js": {
|
|
46
|
+
"file": "assets/vendor-codemirror-vlcH1_iR.js",
|
|
47
47
|
"name": "vendor-codemirror",
|
|
48
48
|
"imports": [
|
|
49
|
-
"_vendor-
|
|
50
|
-
"_vendor-shiki-
|
|
49
|
+
"_vendor-Bl7CyFDw.js",
|
|
50
|
+
"_vendor-shiki-BsknB7bv.js"
|
|
51
51
|
]
|
|
52
52
|
},
|
|
53
|
-
"_vendor-recharts-
|
|
54
|
-
"file": "assets/vendor-recharts-
|
|
53
|
+
"_vendor-recharts-C9cQu72o.js": {
|
|
54
|
+
"file": "assets/vendor-recharts-C9cQu72o.js",
|
|
55
55
|
"name": "vendor-recharts",
|
|
56
56
|
"imports": [
|
|
57
|
-
"_vendor-
|
|
57
|
+
"_vendor-Bl7CyFDw.js"
|
|
58
58
|
]
|
|
59
59
|
},
|
|
60
|
-
"_vendor-shiki-
|
|
61
|
-
"file": "assets/vendor-shiki-
|
|
60
|
+
"_vendor-shiki-BsknB7bv.js": {
|
|
61
|
+
"file": "assets/vendor-shiki-BsknB7bv.js",
|
|
62
62
|
"name": "vendor-shiki",
|
|
63
63
|
"imports": [
|
|
64
|
-
"_vendor-
|
|
64
|
+
"_vendor-Bl7CyFDw.js"
|
|
65
65
|
]
|
|
66
66
|
},
|
|
67
67
|
"_vendor-three-C5WAXd5r.js": {
|
|
@@ -69,19 +69,19 @@
|
|
|
69
69
|
"name": "vendor-three"
|
|
70
70
|
},
|
|
71
71
|
"index.tsx": {
|
|
72
|
-
"file": "assets/index-
|
|
72
|
+
"file": "assets/index-OU2WTnGN.js",
|
|
73
73
|
"name": "index",
|
|
74
74
|
"src": "index.tsx",
|
|
75
75
|
"isEntry": true,
|
|
76
76
|
"imports": [
|
|
77
|
-
"_vendor-
|
|
78
|
-
"_vendor-arizeai-
|
|
79
|
-
"_pages-
|
|
80
|
-
"_components-
|
|
77
|
+
"_vendor-Bl7CyFDw.js",
|
|
78
|
+
"_vendor-arizeai-B_viEUUA.js",
|
|
79
|
+
"_pages-DF8rqxJ4.js",
|
|
80
|
+
"_components-5M9nebi4.js",
|
|
81
81
|
"_vendor-three-C5WAXd5r.js",
|
|
82
|
-
"_vendor-codemirror-
|
|
83
|
-
"_vendor-shiki-
|
|
84
|
-
"_vendor-recharts-
|
|
82
|
+
"_vendor-codemirror-vlcH1_iR.js",
|
|
83
|
+
"_vendor-shiki-BsknB7bv.js",
|
|
84
|
+
"_vendor-recharts-C9cQu72o.js"
|
|
85
85
|
]
|
|
86
86
|
}
|
|
87
87
|
}
|