arize-phoenix 4.36.0__py3-none-any.whl → 5.0.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-4.36.0.dist-info → arize_phoenix-5.0.0.dist-info}/METADATA +10 -12
- {arize_phoenix-4.36.0.dist-info → arize_phoenix-5.0.0.dist-info}/RECORD +68 -59
- phoenix/__init__.py +86 -0
- phoenix/auth.py +275 -14
- phoenix/config.py +277 -25
- phoenix/db/enums.py +20 -0
- phoenix/db/facilitator.py +112 -0
- phoenix/db/migrations/versions/cd164e83824f_users_and_tokens.py +157 -0
- phoenix/db/models.py +145 -60
- phoenix/experiments/evaluators/code_evaluators.py +9 -3
- phoenix/experiments/functions.py +1 -4
- phoenix/server/api/README.md +28 -0
- phoenix/server/api/auth.py +32 -0
- phoenix/server/api/context.py +50 -2
- phoenix/server/api/dataloaders/__init__.py +4 -0
- phoenix/server/api/dataloaders/user_roles.py +30 -0
- phoenix/server/api/dataloaders/users.py +33 -0
- phoenix/server/api/exceptions.py +7 -0
- phoenix/server/api/mutations/__init__.py +0 -2
- phoenix/server/api/mutations/api_key_mutations.py +104 -86
- phoenix/server/api/mutations/dataset_mutations.py +8 -8
- phoenix/server/api/mutations/experiment_mutations.py +2 -2
- phoenix/server/api/mutations/export_events_mutations.py +3 -3
- phoenix/server/api/mutations/project_mutations.py +3 -3
- phoenix/server/api/mutations/span_annotations_mutations.py +4 -4
- phoenix/server/api/mutations/trace_annotations_mutations.py +4 -4
- phoenix/server/api/mutations/user_mutations.py +282 -42
- phoenix/server/api/openapi/schema.py +2 -2
- phoenix/server/api/queries.py +48 -39
- phoenix/server/api/routers/__init__.py +11 -0
- phoenix/server/api/routers/auth.py +284 -0
- phoenix/server/api/routers/embeddings.py +26 -0
- phoenix/server/api/routers/oauth2.py +456 -0
- phoenix/server/api/routers/v1/__init__.py +38 -16
- phoenix/server/api/types/ApiKey.py +11 -0
- phoenix/server/api/types/AuthMethod.py +9 -0
- phoenix/server/api/types/User.py +48 -4
- phoenix/server/api/types/UserApiKey.py +35 -1
- phoenix/server/api/types/UserRole.py +7 -0
- phoenix/server/app.py +103 -31
- phoenix/server/bearer_auth.py +161 -0
- phoenix/server/email/__init__.py +0 -0
- phoenix/server/email/sender.py +26 -0
- phoenix/server/email/templates/__init__.py +0 -0
- phoenix/server/email/templates/password_reset.html +19 -0
- phoenix/server/email/types.py +11 -0
- phoenix/server/grpc_server.py +6 -0
- phoenix/server/jwt_store.py +504 -0
- phoenix/server/main.py +40 -9
- phoenix/server/oauth2.py +51 -0
- phoenix/server/prometheus.py +20 -0
- phoenix/server/rate_limiters.py +191 -0
- phoenix/server/static/.vite/manifest.json +31 -31
- phoenix/server/static/assets/{components-Dte7_KRd.js → components-REunxTt6.js} +348 -286
- phoenix/server/static/assets/index-DAPJxlCw.js +101 -0
- phoenix/server/static/assets/{pages-CnTvEGEN.js → pages-1VrMk2pW.js} +559 -291
- phoenix/server/static/assets/{vendor-BC3OPQuM.js → vendor-B5IC0ivG.js} +5 -5
- phoenix/server/static/assets/{vendor-arizeai-NjB3cZzD.js → vendor-arizeai-aFbT4kl1.js} +2 -2
- phoenix/server/static/assets/{vendor-codemirror-gE_JCOgX.js → vendor-codemirror-BEGorXSV.js} +1 -1
- phoenix/server/static/assets/{vendor-recharts-BXLYwcXF.js → vendor-recharts-6nUU7gU_.js} +1 -1
- phoenix/server/templates/index.html +1 -0
- phoenix/server/types.py +157 -1
- phoenix/session/client.py +7 -2
- phoenix/utilities/client.py +16 -0
- phoenix/version.py +1 -1
- phoenix/db/migrations/future_versions/README.md +0 -4
- phoenix/db/migrations/future_versions/cd164e83824f_users_and_tokens.py +0 -293
- phoenix/db/migrations/versions/.gitignore +0 -1
- phoenix/server/api/mutations/auth.py +0 -18
- phoenix/server/api/mutations/auth_mutations.py +0 -65
- phoenix/server/static/assets/index-fq1-hCK4.js +0 -100
- phoenix/trace/langchain/__init__.py +0 -3
- phoenix/trace/langchain/instrumentor.py +0 -34
- phoenix/trace/llama_index/__init__.py +0 -3
- phoenix/trace/llama_index/callback.py +0 -102
- phoenix/trace/openai/__init__.py +0 -3
- phoenix/trace/openai/instrumentor.py +0 -30
- {arize_phoenix-4.36.0.dist-info → arize_phoenix-5.0.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-4.36.0.dist-info → arize_phoenix-5.0.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-4.36.0.dist-info → arize_phoenix-5.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
"""users and tokens
|
|
2
|
-
|
|
3
|
-
Revision ID: cd164e83824f
|
|
4
|
-
Revises: 10460e46d750
|
|
5
|
-
Create Date: 2024-08-01 18:36:52.157604
|
|
6
|
-
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from datetime import datetime, timezone
|
|
10
|
-
from typing import Any, Dict, List, Optional, Sequence, TypedDict, Union
|
|
11
|
-
|
|
12
|
-
import sqlalchemy as sa
|
|
13
|
-
from alembic import op
|
|
14
|
-
from sqlalchemy import (
|
|
15
|
-
JSON,
|
|
16
|
-
TIMESTAMP,
|
|
17
|
-
CheckConstraint,
|
|
18
|
-
Dialect,
|
|
19
|
-
ForeignKey,
|
|
20
|
-
MetaData,
|
|
21
|
-
TypeDecorator,
|
|
22
|
-
func,
|
|
23
|
-
insert,
|
|
24
|
-
)
|
|
25
|
-
from sqlalchemy.dialects import postgresql
|
|
26
|
-
from sqlalchemy.ext.asyncio.engine import AsyncConnection
|
|
27
|
-
from sqlalchemy.ext.compiler import compiles
|
|
28
|
-
from sqlalchemy.orm import (
|
|
29
|
-
DeclarativeBase,
|
|
30
|
-
Mapped,
|
|
31
|
-
mapped_column,
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
from phoenix.datetime_utils import normalize_datetime
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class JSONB(JSON):
|
|
38
|
-
# See https://docs.sqlalchemy.org/en/20/core/custom_types.html
|
|
39
|
-
__visit_name__ = "JSONB"
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@compiles(JSONB, "sqlite") # type: ignore
|
|
43
|
-
def _(*args: Any, **kwargs: Any) -> str:
|
|
44
|
-
# See https://docs.sqlalchemy.org/en/20/core/custom_types.html
|
|
45
|
-
return "JSONB"
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
JSON_ = (
|
|
49
|
-
JSON()
|
|
50
|
-
.with_variant(
|
|
51
|
-
postgresql.JSONB(), # type: ignore
|
|
52
|
-
"postgresql",
|
|
53
|
-
)
|
|
54
|
-
.with_variant(
|
|
55
|
-
JSONB(),
|
|
56
|
-
"sqlite",
|
|
57
|
-
)
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class JsonDict(TypeDecorator[Dict[str, Any]]):
|
|
62
|
-
# See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
|
|
63
|
-
cache_ok = True
|
|
64
|
-
impl = JSON_
|
|
65
|
-
|
|
66
|
-
def process_bind_param(self, value: Optional[Dict[str, Any]], _: Dialect) -> Dict[str, Any]:
|
|
67
|
-
return value if isinstance(value, dict) else {}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
class JsonList(TypeDecorator[List[Any]]):
|
|
71
|
-
# See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
|
|
72
|
-
cache_ok = True
|
|
73
|
-
impl = JSON_
|
|
74
|
-
|
|
75
|
-
def process_bind_param(self, value: Optional[List[Any]], _: Dialect) -> List[Any]:
|
|
76
|
-
return value if isinstance(value, list) else []
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
class UtcTimeStamp(TypeDecorator[datetime]):
|
|
80
|
-
# See # See https://docs.sqlalchemy.org/en/20/core/custom_types.html
|
|
81
|
-
cache_ok = True
|
|
82
|
-
impl = TIMESTAMP(timezone=True)
|
|
83
|
-
|
|
84
|
-
def process_bind_param(self, value: Optional[datetime], _: Dialect) -> Optional[datetime]:
|
|
85
|
-
return normalize_datetime(value)
|
|
86
|
-
|
|
87
|
-
def process_result_value(self, value: Optional[Any], _: Dialect) -> Optional[datetime]:
|
|
88
|
-
return normalize_datetime(value, timezone.utc)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
class ExperimentRunOutput(TypedDict, total=False):
|
|
92
|
-
task_output: Any
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
class Base(DeclarativeBase):
|
|
96
|
-
# Enforce best practices for naming constraints
|
|
97
|
-
# https://alembic.sqlalchemy.org/en/latest/naming.html#integration-of-naming-conventions-into-operations-autogenerate
|
|
98
|
-
metadata = MetaData(
|
|
99
|
-
naming_convention={
|
|
100
|
-
"ix": "ix_%(table_name)s_%(column_0_N_name)s",
|
|
101
|
-
"uq": "uq_%(table_name)s_%(column_0_N_name)s",
|
|
102
|
-
"ck": "ck_%(table_name)s_`%(constraint_name)s`",
|
|
103
|
-
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
|
104
|
-
"pk": "pk_%(table_name)s",
|
|
105
|
-
}
|
|
106
|
-
)
|
|
107
|
-
type_annotation_map = {
|
|
108
|
-
Dict[str, Any]: JsonDict,
|
|
109
|
-
List[Dict[str, Any]]: JsonList,
|
|
110
|
-
ExperimentRunOutput: JsonDict,
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
class UserRole(Base):
|
|
115
|
-
__tablename__ = "user_roles"
|
|
116
|
-
id: Mapped[int] = mapped_column(primary_key=True)
|
|
117
|
-
name: Mapped[str] = mapped_column(unique=True)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
class User(Base):
|
|
121
|
-
__tablename__ = "users"
|
|
122
|
-
id: Mapped[int] = mapped_column(primary_key=True)
|
|
123
|
-
user_role_id: Mapped[int] = mapped_column(
|
|
124
|
-
ForeignKey("user_roles.id"),
|
|
125
|
-
index=True,
|
|
126
|
-
)
|
|
127
|
-
username: Mapped[Optional[str]] = mapped_column(nullable=True, unique=True, index=True)
|
|
128
|
-
email: Mapped[str] = mapped_column(nullable=False, unique=True, index=True)
|
|
129
|
-
auth_method: Mapped[str] = mapped_column(
|
|
130
|
-
CheckConstraint("auth_method IN ('LOCAL')", name="valid_auth_method")
|
|
131
|
-
)
|
|
132
|
-
password_hash: Mapped[Optional[str]]
|
|
133
|
-
reset_password: Mapped[bool]
|
|
134
|
-
created_at: Mapped[datetime] = mapped_column(UtcTimeStamp, server_default=func.now())
|
|
135
|
-
updated_at: Mapped[datetime] = mapped_column(
|
|
136
|
-
UtcTimeStamp, server_default=func.now(), onupdate=func.now()
|
|
137
|
-
)
|
|
138
|
-
deleted_at: Mapped[Optional[datetime]] = mapped_column(UtcTimeStamp)
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
# revision identifiers, used by Alembic.
|
|
142
|
-
revision: str = "cd164e83824f"
|
|
143
|
-
down_revision: Union[str, None] = "3be8647b87d8"
|
|
144
|
-
branch_labels: Union[str, Sequence[str], None] = None
|
|
145
|
-
depends_on: Union[str, Sequence[str], None] = None
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
async def insert_roles_and_users(connection: AsyncConnection) -> None:
|
|
149
|
-
"""
|
|
150
|
-
Populates the `user_roles` table and adds a system user and initial admin
|
|
151
|
-
user to the `users` table.
|
|
152
|
-
"""
|
|
153
|
-
await connection.execute(
|
|
154
|
-
insert(UserRole).values([{"name": "SYSTEM"}, {"name": "ADMIN"}, {"name": "MEMBER"}])
|
|
155
|
-
)
|
|
156
|
-
system_user_role_id = sa.select(UserRole.id).where(UserRole.name == "SYSTEM").scalar_subquery()
|
|
157
|
-
admin_user_role_id = sa.select(UserRole.id).where(UserRole.name == "ADMIN").scalar_subquery()
|
|
158
|
-
await connection.execute(
|
|
159
|
-
insert(User).values(
|
|
160
|
-
[
|
|
161
|
-
{
|
|
162
|
-
"user_role_id": system_user_role_id,
|
|
163
|
-
"username": None,
|
|
164
|
-
"email": "system@localhost",
|
|
165
|
-
"auth_method": "LOCAL",
|
|
166
|
-
"password_hash": None,
|
|
167
|
-
"reset_password": False,
|
|
168
|
-
},
|
|
169
|
-
{
|
|
170
|
-
"user_role_id": admin_user_role_id,
|
|
171
|
-
"username": "admin",
|
|
172
|
-
"email": "admin@localhost",
|
|
173
|
-
"auth_method": "LOCAL",
|
|
174
|
-
"password_hash": None, # todo: replace this with the hashed PHOENIX_SECRET
|
|
175
|
-
"reset_password": True,
|
|
176
|
-
},
|
|
177
|
-
]
|
|
178
|
-
)
|
|
179
|
-
)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
def upgrade() -> None:
|
|
183
|
-
op.create_table(
|
|
184
|
-
"user_roles",
|
|
185
|
-
sa.Column("id", sa.Integer, primary_key=True),
|
|
186
|
-
sa.Column(
|
|
187
|
-
"name",
|
|
188
|
-
sa.String,
|
|
189
|
-
nullable=False,
|
|
190
|
-
unique=True,
|
|
191
|
-
),
|
|
192
|
-
)
|
|
193
|
-
op.create_table(
|
|
194
|
-
"users",
|
|
195
|
-
sa.Column("id", sa.Integer, primary_key=True),
|
|
196
|
-
sa.Column(
|
|
197
|
-
"user_role_id",
|
|
198
|
-
sa.Integer,
|
|
199
|
-
sa.ForeignKey("user_roles.id"),
|
|
200
|
-
nullable=False,
|
|
201
|
-
index=True,
|
|
202
|
-
),
|
|
203
|
-
sa.Column("username", sa.String, nullable=True, unique=True, index=True),
|
|
204
|
-
sa.Column("email", sa.String, nullable=False, unique=True, index=True),
|
|
205
|
-
sa.Column(
|
|
206
|
-
"auth_method",
|
|
207
|
-
sa.String,
|
|
208
|
-
sa.CheckConstraint("auth_method IN ('LOCAL')", "valid_auth_method"),
|
|
209
|
-
nullable=False,
|
|
210
|
-
),
|
|
211
|
-
sa.Column("password_hash", sa.String, nullable=True),
|
|
212
|
-
sa.Column("reset_password", sa.Boolean, nullable=False),
|
|
213
|
-
sa.Column(
|
|
214
|
-
"created_at",
|
|
215
|
-
sa.TIMESTAMP(timezone=True),
|
|
216
|
-
nullable=False,
|
|
217
|
-
server_default=sa.func.now(),
|
|
218
|
-
),
|
|
219
|
-
sa.Column(
|
|
220
|
-
"updated_at",
|
|
221
|
-
sa.TIMESTAMP(timezone=True),
|
|
222
|
-
nullable=False,
|
|
223
|
-
server_default=sa.func.now(),
|
|
224
|
-
onupdate=sa.func.now(),
|
|
225
|
-
),
|
|
226
|
-
sa.Column(
|
|
227
|
-
"deleted_at",
|
|
228
|
-
sa.TIMESTAMP(timezone=True),
|
|
229
|
-
nullable=True,
|
|
230
|
-
),
|
|
231
|
-
)
|
|
232
|
-
op.create_table(
|
|
233
|
-
"api_keys",
|
|
234
|
-
sa.Column("id", sa.Integer, primary_key=True),
|
|
235
|
-
sa.Column(
|
|
236
|
-
"user_id",
|
|
237
|
-
sa.Integer,
|
|
238
|
-
sa.ForeignKey("users.id"),
|
|
239
|
-
nullable=False,
|
|
240
|
-
index=True,
|
|
241
|
-
),
|
|
242
|
-
sa.Column("name", sa.String, nullable=False),
|
|
243
|
-
sa.Column("description", sa.String, nullable=True),
|
|
244
|
-
sa.Column(
|
|
245
|
-
"created_at",
|
|
246
|
-
sa.TIMESTAMP(timezone=True),
|
|
247
|
-
nullable=False,
|
|
248
|
-
server_default=sa.func.now(),
|
|
249
|
-
),
|
|
250
|
-
sa.Column(
|
|
251
|
-
"expires_at",
|
|
252
|
-
sa.TIMESTAMP(timezone=True),
|
|
253
|
-
nullable=True,
|
|
254
|
-
),
|
|
255
|
-
)
|
|
256
|
-
op.create_table(
|
|
257
|
-
"audit_api_keys",
|
|
258
|
-
sa.Column("id", sa.Integer, primary_key=True),
|
|
259
|
-
sa.Column(
|
|
260
|
-
"api_key_id",
|
|
261
|
-
sa.Integer,
|
|
262
|
-
sa.ForeignKey("api_keys.id"),
|
|
263
|
-
nullable=False,
|
|
264
|
-
index=True,
|
|
265
|
-
),
|
|
266
|
-
sa.Column(
|
|
267
|
-
"user_id",
|
|
268
|
-
sa.Integer,
|
|
269
|
-
sa.ForeignKey("users.id"),
|
|
270
|
-
nullable=False,
|
|
271
|
-
index=True,
|
|
272
|
-
),
|
|
273
|
-
sa.Column(
|
|
274
|
-
"action",
|
|
275
|
-
sa.String,
|
|
276
|
-
sa.CheckConstraint("action IN ('CREATE', 'DELETE')", "valid_action"),
|
|
277
|
-
nullable=False,
|
|
278
|
-
),
|
|
279
|
-
sa.Column(
|
|
280
|
-
"created_at",
|
|
281
|
-
sa.TIMESTAMP(timezone=True),
|
|
282
|
-
nullable=False,
|
|
283
|
-
server_default=sa.func.now(),
|
|
284
|
-
),
|
|
285
|
-
)
|
|
286
|
-
op.run_async(insert_roles_and_users)
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
def downgrade() -> None:
|
|
290
|
-
op.drop_table("audit_api_keys")
|
|
291
|
-
op.drop_table("api_keys")
|
|
292
|
-
op.drop_table("users")
|
|
293
|
-
op.drop_table("user_roles")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
cd164e83824f_users_and_tokens.py
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
|
-
from strawberry import Info
|
|
4
|
-
from strawberry.permission import BasePermission
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class IsAuthenticated(BasePermission):
|
|
8
|
-
message = "User is not authenticated"
|
|
9
|
-
|
|
10
|
-
async def has_permission(self, source: Any, info: Info, **kwargs: Any) -> bool:
|
|
11
|
-
return not info.context.read_only
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class HasSecret(BasePermission):
|
|
15
|
-
message = "Application secret is not set"
|
|
16
|
-
|
|
17
|
-
async def has_permission(self, source: Any, info: Info, **kwargs: Any) -> bool:
|
|
18
|
-
return info.context.secret is not None
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
from datetime import timedelta
|
|
3
|
-
|
|
4
|
-
import strawberry
|
|
5
|
-
from sqlalchemy import select
|
|
6
|
-
from strawberry.types import Info
|
|
7
|
-
|
|
8
|
-
from phoenix.auth import is_valid_password
|
|
9
|
-
from phoenix.db import models
|
|
10
|
-
from phoenix.server.api.context import Context
|
|
11
|
-
from phoenix.server.api.exceptions import Unauthorized
|
|
12
|
-
from phoenix.server.api.mutations.auth import HasSecret
|
|
13
|
-
|
|
14
|
-
PHOENIX_ACCESS_TOKEN_COOKIE_NAME = "phoenix-access-token"
|
|
15
|
-
PHOENIX_ACCESS_TOKEN_COOKIE_MAX_AGE_IN_SECONDS = int(timedelta(days=31).total_seconds())
|
|
16
|
-
FAILED_LOGIN_MESSAGE = "login failed"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@strawberry.input
|
|
20
|
-
class LoginMutationInput:
|
|
21
|
-
email: str
|
|
22
|
-
password: str
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@strawberry.type
|
|
26
|
-
class AuthMutationMixin:
|
|
27
|
-
@strawberry.mutation(permission_classes=[HasSecret]) # type: ignore
|
|
28
|
-
async def login(
|
|
29
|
-
self,
|
|
30
|
-
info: Info[Context, None],
|
|
31
|
-
input: LoginMutationInput,
|
|
32
|
-
) -> None:
|
|
33
|
-
async with info.context.db() as session:
|
|
34
|
-
if (
|
|
35
|
-
user := await session.scalar(
|
|
36
|
-
select(models.User).where(models.User.email == input.email)
|
|
37
|
-
)
|
|
38
|
-
) is None or (password_hash := user.password_hash) is None:
|
|
39
|
-
raise Unauthorized(FAILED_LOGIN_MESSAGE)
|
|
40
|
-
secret = info.context.get_secret()
|
|
41
|
-
loop = asyncio.get_running_loop()
|
|
42
|
-
if not await loop.run_in_executor(
|
|
43
|
-
executor=None,
|
|
44
|
-
func=lambda: is_valid_password(
|
|
45
|
-
password=input.password, salt=secret, password_hash=password_hash
|
|
46
|
-
),
|
|
47
|
-
):
|
|
48
|
-
raise Unauthorized(FAILED_LOGIN_MESSAGE)
|
|
49
|
-
response = info.context.get_response()
|
|
50
|
-
response.set_cookie(
|
|
51
|
-
key=PHOENIX_ACCESS_TOKEN_COOKIE_NAME,
|
|
52
|
-
value="token", # todo: compute access token
|
|
53
|
-
secure=True,
|
|
54
|
-
httponly=True,
|
|
55
|
-
samesite="strict",
|
|
56
|
-
max_age=PHOENIX_ACCESS_TOKEN_COOKIE_MAX_AGE_IN_SECONDS,
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
@strawberry.mutation(permission_classes=[HasSecret]) # type: ignore
|
|
60
|
-
async def logout(
|
|
61
|
-
self,
|
|
62
|
-
info: Info[Context, None],
|
|
63
|
-
) -> None:
|
|
64
|
-
response = info.context.get_response()
|
|
65
|
-
response.delete_cookie(key=PHOENIX_ACCESS_TOKEN_COOKIE_NAME)
|
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
import{r as d,j as e,d5 as P,v as s,F,R as v,w as E,aQ as S,d6 as L,d7 as R,d8 as a,d9 as w,da as z,b as A,db as j}from"./vendor-BC3OPQuM.js";import{S as C,j as k,Z as $,U as _,t as I,a4 as O}from"./vendor-arizeai-NjB3cZzD.js";import{E as T,L as D,a as N,P as G,h as M,M as U,b as m,D as B,d as q,c as J,e as K,f as W,g as H,T as Q,p as V,i as g,j as Y,k as Z,l as u,m as X,n as h,o as b,q as ee,r as re,s as ae,t as te,v as oe,A as ne,S as se,F as le}from"./pages-CnTvEGEN.js";import{b9 as ie,d as ce,R as de,ba as pe,bb as me}from"./components-Dte7_KRd.js";import"./vendor-three-DwGkEfCM.js";import"./vendor-recharts-BXLYwcXF.js";import"./vendor-codemirror-gE_JCOgX.js";(function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const t of document.querySelectorAll('link[rel="modulepreload"]'))c(t);new MutationObserver(t=>{for(const o of t)if(o.type==="childList")for(const l of o.addedNodes)l.tagName==="LINK"&&l.rel==="modulepreload"&&c(l)}).observe(document,{childList:!0,subtree:!0});function i(t){const o={};return t.integrity&&(o.integrity=t.integrity),t.referrerPolicy&&(o.referrerPolicy=t.referrerPolicy),t.crossOrigin==="use-credentials"?o.credentials="include":t.crossOrigin==="anonymous"?o.credentials="omit":o.credentials="same-origin",o}function c(t){if(t.ep)return;t.ep=!0;const o=i(t);fetch(t.href,o)}})();const f="arize-phoenix-feature-flags",p={__CLEAR__:!0};function ge(){const r=localStorage.getItem(f);if(!r)return p;try{const n=JSON.parse(r);return Object.assign({},p,n)}catch{return p}}const x=d.createContext(null);function ue(){const r=v.useContext(x);if(r===null)throw new Error("useFeatureFlags must be used within a FeatureFlagsProvider");return r}function he(r){const[n,i]=d.useState(ge()),c=t=>{localStorage.setItem(f,JSON.stringify(t)),i(t)};return e(x.Provider,{value:{featureFlags:n,setFeatureFlags:c},children:e(be,{children:r.children})})}function be(r){const{children:n}=r,{featureFlags:i,setFeatureFlags:c}=ue(),[t,o]=d.useState(!1);return P("ctrl+shift+f",()=>o(!0)),s(F,{children:[n,e(_,{type:"modal",isDismissable:!0,onDismiss:()=>o(!1),children:t&&e(C,{title:"Feature Flags",children:e(k,{height:"size-1000",padding:"size-100",children:Object.keys(i).map(l=>e($,{isSelected:i[l],onChange:y=>c({...i,[l]:y}),children:l},l))})})})]})}function fe(){return e(S,{styles:r=>E`
|
|
2
|
-
body {
|
|
3
|
-
background-color: var(--ac-global-color-grey-75);
|
|
4
|
-
color: var(--ac-global-text-color-900);
|
|
5
|
-
font-family: "Roboto";
|
|
6
|
-
font-size: ${r.typography.sizes.medium.fontSize}px;
|
|
7
|
-
margin: 0;
|
|
8
|
-
#root,
|
|
9
|
-
#root > div[data-overlay-container="true"],
|
|
10
|
-
#root > div[data-overlay-container="true"] > .ac-theme {
|
|
11
|
-
height: 100vh;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/* Remove list styling */
|
|
16
|
-
ul {
|
|
17
|
-
display: block;
|
|
18
|
-
list-style-type: none;
|
|
19
|
-
margin-block-start: none;
|
|
20
|
-
margin-block-end: 0;
|
|
21
|
-
padding-inline-start: 0;
|
|
22
|
-
margin-block-start: 0;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/* A reset style for buttons */
|
|
26
|
-
.button--reset {
|
|
27
|
-
background: none;
|
|
28
|
-
border: none;
|
|
29
|
-
padding: 0;
|
|
30
|
-
}
|
|
31
|
-
/* this css class is added to html via modernizr @see modernizr.js */
|
|
32
|
-
.no-hiddenscroll {
|
|
33
|
-
/* Works on Firefox */
|
|
34
|
-
* {
|
|
35
|
-
scrollbar-width: thin;
|
|
36
|
-
scrollbar-color: var(--ac-global-color-grey-300)
|
|
37
|
-
var(--ac-global-color-grey-400);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/* Works on Chrome, Edge, and Safari */
|
|
41
|
-
*::-webkit-scrollbar {
|
|
42
|
-
width: 14px;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
*::-webkit-scrollbar-track {
|
|
46
|
-
background: var(--ac-global-color-grey-100);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
*::-webkit-scrollbar-thumb {
|
|
50
|
-
background-color: var(--ac-global-color-grey-75);
|
|
51
|
-
border-radius: 8px;
|
|
52
|
-
border: 1px solid var(--ac-global-color-grey-300);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
:root {
|
|
57
|
-
--px-blue-color: ${r.colors.arizeBlue};
|
|
58
|
-
|
|
59
|
-
--px-flex-gap-sm: ${r.spacing.margin4}px;
|
|
60
|
-
--px-flex-gap-sm: ${r.spacing.margin8}px;
|
|
61
|
-
|
|
62
|
-
--px-section-background-color: ${r.colors.gray500};
|
|
63
|
-
|
|
64
|
-
/* An item is a typically something in a list */
|
|
65
|
-
--px-item-background-color: ${r.colors.gray800};
|
|
66
|
-
--px-item-border-color: ${r.colors.gray600};
|
|
67
|
-
|
|
68
|
-
--px-spacing-sm: ${r.spacing.padding4}px;
|
|
69
|
-
--px-spacing-med: ${r.spacing.padding8}px;
|
|
70
|
-
--px-spacing-lg: ${r.spacing.padding16}px;
|
|
71
|
-
|
|
72
|
-
--px-border-radius-med: ${r.borderRadius.medium}px;
|
|
73
|
-
|
|
74
|
-
--px-font-size-sm: ${r.typography.sizes.small.fontSize}px;
|
|
75
|
-
--px-font-size-med: ${r.typography.sizes.medium.fontSize}px;
|
|
76
|
-
--px-font-size-lg: ${r.typography.sizes.large.fontSize}px;
|
|
77
|
-
|
|
78
|
-
--px-gradient-bar-height: 8px;
|
|
79
|
-
|
|
80
|
-
--px-nav-collapsed-width: 45px;
|
|
81
|
-
--px-nav-expanded-width: 200px;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
.ac-theme--dark {
|
|
85
|
-
--px-primary-color: #9efcfd;
|
|
86
|
-
--px-primary-color--transparent: rgb(158, 252, 253, 0.2);
|
|
87
|
-
--px-reference-color: #baa1f9;
|
|
88
|
-
--px-reference-color--transparent: #baa1f982;
|
|
89
|
-
--px-corpus-color: #92969c;
|
|
90
|
-
--px-corpus-color--transparent: #92969c63;
|
|
91
|
-
}
|
|
92
|
-
.ac-theme--light {
|
|
93
|
-
--px-primary-color: #00add0;
|
|
94
|
-
--px-primary-color--transparent: rgba(0, 173, 208, 0.2);
|
|
95
|
-
--px-reference-color: #4500d9;
|
|
96
|
-
--px-reference-color--transparent: rgba(69, 0, 217, 0.2);
|
|
97
|
-
--px-corpus-color: #92969c;
|
|
98
|
-
--px-corpus-color--transparent: #92969c63;
|
|
99
|
-
}
|
|
100
|
-
`})}const xe=L(R(s(a,{path:"/",errorElement:e(T,{}),children:[e(a,{path:"/login",element:e(D,{})}),s(a,{element:e(N,{}),children:[e(a,{path:"/profile",handle:{crumb:()=>"profile"},element:e(G,{})}),e(a,{index:!0,loader:M}),s(a,{path:"/model",handle:{crumb:()=>"model"},element:e(U,{}),children:[e(a,{index:!0,element:e(m,{})}),e(a,{element:e(m,{}),children:e(a,{path:"dimensions",children:e(a,{path:":dimensionId",element:e(B,{}),loader:q})})}),e(a,{path:"embeddings",children:e(a,{path:":embeddingDimensionId",element:e(J,{}),loader:K,handle:{crumb:r=>r.embedding.name}})})]}),s(a,{path:"/projects",handle:{crumb:()=>"projects"},element:e(W,{}),children:[e(a,{index:!0,element:e(H,{})}),s(a,{path:":projectId",element:e(Q,{}),loader:V,handle:{crumb:r=>r.project.name},children:[e(a,{index:!0,element:e(g,{})}),e(a,{element:e(g,{}),children:e(a,{path:"traces/:traceId",element:e(Y,{})})})]})]}),s(a,{path:"/datasets",handle:{crumb:()=>"datasets"},children:[e(a,{index:!0,element:e(Z,{})}),s(a,{path:":datasetId",loader:u,handle:{crumb:r=>r.dataset.name},children:[s(a,{element:e(X,{}),loader:u,children:[e(a,{index:!0,element:e(h,{}),loader:b}),e(a,{path:"experiments",element:e(h,{}),loader:b}),e(a,{path:"examples",element:e(ee,{}),loader:re,children:e(a,{path:":exampleId",element:e(ae,{})})})]}),e(a,{path:"compare",handle:{crumb:()=>"compare"},loader:te,element:e(oe,{})})]})]}),e(a,{path:"/apis",element:e(ne,{}),handle:{crumb:()=>"APIs"}}),e(a,{path:"/settings",element:e(se,{}),handle:{crumb:()=>"Settings"}})]})]})),{basename:window.Config.basename});function ye(){return e(w,{router:xe})}function Pe(){return e(le,{children:e(ie,{children:e(Fe,{})})})}function Fe(){const{theme:r}=ce();return e(O,{theme:r,children:e(z,{theme:I,children:s(A.RelayEnvironmentProvider,{environment:de,children:[e(fe,{}),e(he,{children:e(pe,{children:e(d.Suspense,{children:e(me,{children:e(ye,{})})})})})]})})})}const ve=document.getElementById("root"),Ee=j.createRoot(ve);Ee.render(e(Pe,{}));
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from importlib.metadata import PackageNotFoundError
|
|
3
|
-
from importlib.util import find_spec
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
from openinference.instrumentation.langchain import LangChainInstrumentor as Instrumentor
|
|
7
|
-
from openinference.semconv.resource import ResourceAttributes
|
|
8
|
-
from opentelemetry.sdk import trace as trace_sdk
|
|
9
|
-
from opentelemetry.sdk.resources import Resource
|
|
10
|
-
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
|
|
11
|
-
|
|
12
|
-
from phoenix.config import get_env_project_name
|
|
13
|
-
from phoenix.trace.exporter import _OpenInferenceExporter
|
|
14
|
-
|
|
15
|
-
logger = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
__all__ = ("LangChainInstrumentor",)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class LangChainInstrumentor(Instrumentor):
|
|
21
|
-
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
22
|
-
if find_spec("langchain_core") is None:
|
|
23
|
-
raise PackageNotFoundError(
|
|
24
|
-
"Missing `langchain-core`. Install with `pip install langchain-core`."
|
|
25
|
-
)
|
|
26
|
-
super().__init__()
|
|
27
|
-
|
|
28
|
-
def instrument(self) -> None:
|
|
29
|
-
tracer_provider = trace_sdk.TracerProvider(
|
|
30
|
-
resource=Resource({ResourceAttributes.PROJECT_NAME: get_env_project_name()}),
|
|
31
|
-
span_limits=trace_sdk.SpanLimits(max_attributes=10_000),
|
|
32
|
-
)
|
|
33
|
-
tracer_provider.add_span_processor(SimpleSpanProcessor(_OpenInferenceExporter()))
|
|
34
|
-
super().instrument(skip_dep_check=True, tracer_provider=tracer_provider)
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from importlib.metadata import PackageNotFoundError, version
|
|
3
|
-
from typing import Any, Optional, Tuple
|
|
4
|
-
|
|
5
|
-
from openinference.semconv.resource import ResourceAttributes
|
|
6
|
-
from opentelemetry import trace as trace_api
|
|
7
|
-
from opentelemetry.sdk import trace as trace_sdk
|
|
8
|
-
from opentelemetry.sdk.resources import Resource
|
|
9
|
-
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
|
|
10
|
-
|
|
11
|
-
from phoenix.config import get_env_project_name
|
|
12
|
-
from phoenix.trace.errors import IncompatibleLibraryVersionError
|
|
13
|
-
from phoenix.trace.exporter import _OpenInferenceExporter
|
|
14
|
-
|
|
15
|
-
logger = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
LLAMA_INDEX_MODERN_VERSION = (0, 10, 0)
|
|
18
|
-
INSTRUMENTATION_MODERN_VERSION = (1, 0, 0)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def _check_instrumentation_compatibility() -> bool:
|
|
22
|
-
llama_index_version_str = _get_version_if_installed("llama-index")
|
|
23
|
-
llama_index_installed = llama_index_version_str is not None
|
|
24
|
-
llama_index_core_version_str = _get_version_if_installed("llama-index-core")
|
|
25
|
-
llama_index_core_installed = modern_llama_index_installed = (
|
|
26
|
-
llama_index_core_version_str is not None
|
|
27
|
-
)
|
|
28
|
-
instrumentation_version_str = version("openinference-instrumentation-llama-index")
|
|
29
|
-
instrumentation_version = _parse_semantic_version(instrumentation_version_str)
|
|
30
|
-
|
|
31
|
-
if not llama_index_installed and not llama_index_core_installed:
|
|
32
|
-
raise PackageNotFoundError(
|
|
33
|
-
"Missing `llama_index`. "
|
|
34
|
-
"Install with `pip install llama-index` or "
|
|
35
|
-
"`pip install llama-index-core` for a minimal installation."
|
|
36
|
-
)
|
|
37
|
-
elif modern_llama_index_installed and instrumentation_version < INSTRUMENTATION_MODERN_VERSION:
|
|
38
|
-
raise IncompatibleLibraryVersionError(
|
|
39
|
-
f"llama-index-core v{llama_index_core_version_str} is not compatible with "
|
|
40
|
-
f"openinference-instrumentation-llama-index v{instrumentation_version_str}. "
|
|
41
|
-
"Please upgrade openinference-instrumentation-llama-index to at least 1.0.0 via "
|
|
42
|
-
"`pip install 'openinference-instrumentation-llama-index>=1.0.0'`."
|
|
43
|
-
)
|
|
44
|
-
elif (
|
|
45
|
-
llama_index_installed
|
|
46
|
-
and llama_index_version_str
|
|
47
|
-
and _parse_semantic_version(llama_index_version_str) < LLAMA_INDEX_MODERN_VERSION
|
|
48
|
-
and instrumentation_version >= INSTRUMENTATION_MODERN_VERSION
|
|
49
|
-
):
|
|
50
|
-
raise IncompatibleLibraryVersionError(
|
|
51
|
-
f"llama-index v{llama_index_version_str} is not compatible with "
|
|
52
|
-
f"openinference-instrumentation-llama-index v{instrumentation_version_str}. "
|
|
53
|
-
"Please either migrate llama-index to at least 0.10.0 or downgrade "
|
|
54
|
-
"openinference-instrumentation-llama-index via "
|
|
55
|
-
"`pip install 'openinference-instrumentation-llama-index<1.0.0'`."
|
|
56
|
-
)
|
|
57
|
-
return True
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def _get_version_if_installed(package_name: str) -> Optional[str]:
|
|
61
|
-
"""
|
|
62
|
-
Gets the version of the package if it is installed, otherwise, returns None.
|
|
63
|
-
"""
|
|
64
|
-
try:
|
|
65
|
-
return version(package_name)
|
|
66
|
-
except PackageNotFoundError:
|
|
67
|
-
return None
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
def _parse_semantic_version(semver_string: str) -> Tuple[int, ...]:
|
|
71
|
-
"""
|
|
72
|
-
Parse a semantic version string into a tuple of integers.
|
|
73
|
-
"""
|
|
74
|
-
return tuple(map(int, semver_string.split(".")[:3]))
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
if _check_instrumentation_compatibility():
|
|
78
|
-
from openinference.instrumentation.llama_index._callback import (
|
|
79
|
-
OpenInferenceTraceCallbackHandler as _OpenInferenceTraceCallbackHandler,
|
|
80
|
-
)
|
|
81
|
-
from openinference.instrumentation.llama_index.version import (
|
|
82
|
-
__version__,
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class OpenInferenceTraceCallbackHandler(_OpenInferenceTraceCallbackHandler):
|
|
87
|
-
"""Callback handler for storing LLM application trace data in OpenInference format.
|
|
88
|
-
OpenInference is an open standard for capturing and storing AI model
|
|
89
|
-
inferences. It enables production LLMapp servers to seamlessly integrate
|
|
90
|
-
with LLM observability solutions such as Arize and Phoenix.
|
|
91
|
-
|
|
92
|
-
For more information on the specification, see
|
|
93
|
-
https://github.com/Arize-ai/openinference
|
|
94
|
-
"""
|
|
95
|
-
|
|
96
|
-
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
97
|
-
tracer_provider = trace_sdk.TracerProvider(
|
|
98
|
-
resource=Resource({ResourceAttributes.PROJECT_NAME: get_env_project_name()}),
|
|
99
|
-
span_limits=trace_sdk.SpanLimits(max_attributes=10_000),
|
|
100
|
-
)
|
|
101
|
-
tracer_provider.add_span_processor(SimpleSpanProcessor(_OpenInferenceExporter()))
|
|
102
|
-
super().__init__(trace_api.get_tracer(__name__, __version__, tracer_provider))
|
phoenix/trace/openai/__init__.py
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from importlib.metadata import PackageNotFoundError
|
|
3
|
-
from importlib.util import find_spec
|
|
4
|
-
from typing import Any
|
|
5
|
-
|
|
6
|
-
from openinference.instrumentation.openai import OpenAIInstrumentor as Instrumentor
|
|
7
|
-
from openinference.semconv.resource import ResourceAttributes
|
|
8
|
-
from opentelemetry.sdk import trace as trace_sdk
|
|
9
|
-
from opentelemetry.sdk.resources import Resource
|
|
10
|
-
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
|
|
11
|
-
|
|
12
|
-
from phoenix.config import get_env_project_name
|
|
13
|
-
from phoenix.trace.exporter import _OpenInferenceExporter
|
|
14
|
-
|
|
15
|
-
logger = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class OpenAIInstrumentor(Instrumentor):
|
|
19
|
-
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
20
|
-
if find_spec("openai") is None:
|
|
21
|
-
raise PackageNotFoundError("Missing `openai`. Install with `pip install openai`.")
|
|
22
|
-
super().__init__()
|
|
23
|
-
|
|
24
|
-
def instrument(self) -> None:
|
|
25
|
-
tracer_provider = trace_sdk.TracerProvider(
|
|
26
|
-
resource=Resource({ResourceAttributes.PROJECT_NAME: get_env_project_name()}),
|
|
27
|
-
span_limits=trace_sdk.SpanLimits(max_attributes=10_000),
|
|
28
|
-
)
|
|
29
|
-
tracer_provider.add_span_processor(SimpleSpanProcessor(_OpenInferenceExporter()))
|
|
30
|
-
super().instrument(skip_dep_check=True, tracer_provider=tracer_provider)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|