arize-phoenix 4.35.2__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.

Files changed (104) hide show
  1. {arize_phoenix-4.35.2.dist-info → arize_phoenix-5.0.0.dist-info}/METADATA +10 -12
  2. {arize_phoenix-4.35.2.dist-info → arize_phoenix-5.0.0.dist-info}/RECORD +92 -79
  3. phoenix/__init__.py +86 -0
  4. phoenix/auth.py +275 -14
  5. phoenix/config.py +369 -27
  6. phoenix/db/alembic.ini +0 -34
  7. phoenix/db/engines.py +27 -10
  8. phoenix/db/enums.py +20 -0
  9. phoenix/db/facilitator.py +112 -0
  10. phoenix/db/insertion/dataset.py +0 -1
  11. phoenix/db/insertion/types.py +1 -1
  12. phoenix/db/migrate.py +3 -3
  13. phoenix/db/migrations/env.py +0 -7
  14. phoenix/db/migrations/versions/cd164e83824f_users_and_tokens.py +157 -0
  15. phoenix/db/models.py +145 -60
  16. phoenix/experiments/evaluators/code_evaluators.py +9 -3
  17. phoenix/experiments/functions.py +1 -4
  18. phoenix/inferences/fixtures.py +0 -1
  19. phoenix/inferences/inferences.py +0 -1
  20. phoenix/logging/__init__.py +3 -0
  21. phoenix/logging/_config.py +90 -0
  22. phoenix/logging/_filter.py +6 -0
  23. phoenix/logging/_formatter.py +69 -0
  24. phoenix/metrics/__init__.py +0 -1
  25. phoenix/otel/settings.py +4 -4
  26. phoenix/server/api/README.md +28 -0
  27. phoenix/server/api/auth.py +32 -0
  28. phoenix/server/api/context.py +50 -2
  29. phoenix/server/api/dataloaders/__init__.py +4 -0
  30. phoenix/server/api/dataloaders/user_roles.py +30 -0
  31. phoenix/server/api/dataloaders/users.py +33 -0
  32. phoenix/server/api/exceptions.py +7 -0
  33. phoenix/server/api/mutations/__init__.py +0 -2
  34. phoenix/server/api/mutations/api_key_mutations.py +104 -86
  35. phoenix/server/api/mutations/dataset_mutations.py +8 -8
  36. phoenix/server/api/mutations/experiment_mutations.py +2 -2
  37. phoenix/server/api/mutations/export_events_mutations.py +3 -3
  38. phoenix/server/api/mutations/project_mutations.py +3 -3
  39. phoenix/server/api/mutations/span_annotations_mutations.py +4 -4
  40. phoenix/server/api/mutations/trace_annotations_mutations.py +4 -4
  41. phoenix/server/api/mutations/user_mutations.py +282 -42
  42. phoenix/server/api/openapi/schema.py +2 -2
  43. phoenix/server/api/queries.py +48 -39
  44. phoenix/server/api/routers/__init__.py +11 -0
  45. phoenix/server/api/routers/auth.py +284 -0
  46. phoenix/server/api/routers/embeddings.py +26 -0
  47. phoenix/server/api/routers/oauth2.py +456 -0
  48. phoenix/server/api/routers/v1/__init__.py +38 -16
  49. phoenix/server/api/routers/v1/datasets.py +0 -1
  50. phoenix/server/api/types/ApiKey.py +11 -0
  51. phoenix/server/api/types/AuthMethod.py +9 -0
  52. phoenix/server/api/types/User.py +48 -4
  53. phoenix/server/api/types/UserApiKey.py +35 -1
  54. phoenix/server/api/types/UserRole.py +7 -0
  55. phoenix/server/app.py +105 -34
  56. phoenix/server/bearer_auth.py +161 -0
  57. phoenix/server/email/__init__.py +0 -0
  58. phoenix/server/email/sender.py +26 -0
  59. phoenix/server/email/templates/__init__.py +0 -0
  60. phoenix/server/email/templates/password_reset.html +19 -0
  61. phoenix/server/email/types.py +11 -0
  62. phoenix/server/grpc_server.py +6 -0
  63. phoenix/server/jwt_store.py +504 -0
  64. phoenix/server/main.py +61 -30
  65. phoenix/server/oauth2.py +51 -0
  66. phoenix/server/prometheus.py +20 -0
  67. phoenix/server/rate_limiters.py +191 -0
  68. phoenix/server/static/.vite/manifest.json +31 -31
  69. phoenix/server/static/assets/{components-Dte7_KRd.js → components-REunxTt6.js} +348 -286
  70. phoenix/server/static/assets/index-DAPJxlCw.js +101 -0
  71. phoenix/server/static/assets/{pages-CnTvEGEN.js → pages-1VrMk2pW.js} +559 -291
  72. phoenix/server/static/assets/{vendor-BC3OPQuM.js → vendor-B5IC0ivG.js} +5 -5
  73. phoenix/server/static/assets/{vendor-arizeai-NjB3cZzD.js → vendor-arizeai-aFbT4kl1.js} +2 -2
  74. phoenix/server/static/assets/{vendor-codemirror-gE_JCOgX.js → vendor-codemirror-BEGorXSV.js} +1 -1
  75. phoenix/server/static/assets/{vendor-recharts-BXLYwcXF.js → vendor-recharts-6nUU7gU_.js} +1 -1
  76. phoenix/server/telemetry.py +2 -2
  77. phoenix/server/templates/index.html +1 -0
  78. phoenix/server/types.py +157 -1
  79. phoenix/services.py +0 -1
  80. phoenix/session/client.py +7 -3
  81. phoenix/session/evaluation.py +0 -1
  82. phoenix/session/session.py +0 -1
  83. phoenix/settings.py +9 -0
  84. phoenix/trace/exporter.py +0 -1
  85. phoenix/trace/fixtures.py +0 -2
  86. phoenix/utilities/client.py +16 -0
  87. phoenix/utilities/logging.py +9 -1
  88. phoenix/utilities/re.py +3 -3
  89. phoenix/version.py +1 -1
  90. phoenix/db/migrations/future_versions/README.md +0 -4
  91. phoenix/db/migrations/future_versions/cd164e83824f_users_and_tokens.py +0 -293
  92. phoenix/db/migrations/versions/.gitignore +0 -1
  93. phoenix/server/api/mutations/auth.py +0 -18
  94. phoenix/server/api/mutations/auth_mutations.py +0 -65
  95. phoenix/server/static/assets/index-fq1-hCK4.js +0 -100
  96. phoenix/trace/langchain/__init__.py +0 -3
  97. phoenix/trace/langchain/instrumentor.py +0 -35
  98. phoenix/trace/llama_index/__init__.py +0 -3
  99. phoenix/trace/llama_index/callback.py +0 -103
  100. phoenix/trace/openai/__init__.py +0 -3
  101. phoenix/trace/openai/instrumentor.py +0 -31
  102. {arize_phoenix-4.35.2.dist-info → arize_phoenix-5.0.0.dist-info}/WHEEL +0 -0
  103. {arize_phoenix-4.35.2.dist-info → arize_phoenix-5.0.0.dist-info}/licenses/IP_NOTICE +0 -0
  104. {arize_phoenix-4.35.2.dist-info → arize_phoenix-5.0.0.dist-info}/licenses/LICENSE +0 -0
phoenix/settings.py CHANGED
@@ -1,5 +1,8 @@
1
+ import logging
1
2
  from dataclasses import dataclass, field
2
3
 
4
+ from phoenix.config import LoggingMode
5
+
3
6
 
4
7
  @dataclass
5
8
  class _Settings:
@@ -7,6 +10,12 @@ class _Settings:
7
10
 
8
11
  # By default, don't log migrations
9
12
  log_migrations: bool = field(default=False)
13
+ # By default, Phoenix does not configure its loggers and acts as a library
14
+ logging_mode: LoggingMode = field(default=LoggingMode.DEFAULT)
15
+ # By default, log level is INFO
16
+ logging_level: int = field(default=logging.INFO)
17
+ # By default, log level is WARNING
18
+ db_logging_level: int = field(default=logging.WARNING)
10
19
 
11
20
 
12
21
  # Singleton instance of the settings
phoenix/trace/exporter.py CHANGED
@@ -20,7 +20,6 @@ from phoenix.config import (
20
20
  )
21
21
 
22
22
  logger = logging.getLogger(__name__)
23
- logger.addHandler(logging.NullHandler())
24
23
 
25
24
  END_OF_QUEUE = None # sentinel value for queue termination
26
25
 
phoenix/trace/fixtures.py CHANGED
@@ -38,8 +38,6 @@ from phoenix.trace.utils import (
38
38
  )
39
39
 
40
40
  logger = logging.getLogger(__name__)
41
- logger.setLevel(logging.INFO)
42
- logger.addHandler(logging.NullHandler())
43
41
 
44
42
 
45
43
  class EvaluationResultSchema(NamedTuple):
@@ -3,6 +3,8 @@ from typing import Any
3
3
 
4
4
  import httpx
5
5
 
6
+ from phoenix.config import get_env_client_headers, get_env_phoenix_api_key
7
+
6
8
  PHOENIX_SERVER_VERSION_HEADER = "x-phoenix-server-version"
7
9
 
8
10
 
@@ -15,6 +17,13 @@ class VersionedClient(httpx.Client):
15
17
  from phoenix import __version__ as phoenix_version
16
18
 
17
19
  super().__init__(*args, **kwargs)
20
+
21
+ if env_headers := get_env_client_headers():
22
+ self.headers.update(env_headers)
23
+ if "authorization" not in [k.lower() for k in self.headers]:
24
+ if api_key := get_env_phoenix_api_key():
25
+ self.headers["Authorization"] = f"Bearer {api_key}"
26
+
18
27
  self._client_phoenix_version = phoenix_version
19
28
  self._warned_on_minor_version_mismatch = False
20
29
 
@@ -72,6 +81,13 @@ class VersionedAsyncClient(httpx.AsyncClient):
72
81
  from phoenix import __version__ as phoenix_version
73
82
 
74
83
  super().__init__(*args, **kwargs)
84
+
85
+ if env_headers := get_env_client_headers():
86
+ self.headers.update(env_headers)
87
+ if "authorization" not in [k.lower() for k in self.headers]:
88
+ if api_key := get_env_phoenix_api_key():
89
+ self.headers["Authorization"] = f"Bearer {api_key}"
90
+
75
91
  self._client_phoenix_version = phoenix_version
76
92
  self._warned_on_minor_version_mismatch = False
77
93
 
@@ -1,6 +1,6 @@
1
1
  # A collection of printing and logging utilities
2
2
 
3
- from typing import Any
3
+ from typing import Any, List
4
4
 
5
5
  from tqdm.auto import tqdm
6
6
 
@@ -8,3 +8,11 @@ from tqdm.auto import tqdm
8
8
  def printif(condition: bool, *args: Any, **kwargs: Any) -> None:
9
9
  if condition:
10
10
  tqdm.write(*args, **kwargs)
11
+
12
+
13
+ def log_a_list(list_of_str: List[str], join_word: str) -> str:
14
+ if list_of_str is None or len(list_of_str) == 0:
15
+ return ""
16
+ if len(list_of_str) == 1:
17
+ return list_of_str[0]
18
+ return f"{', '.join(map(str, list_of_str[:-1]))} {join_word} {list_of_str[-1]}"
phoenix/utilities/re.py CHANGED
@@ -1,9 +1,9 @@
1
- from logging import getLogger
1
+ import logging
2
2
  from re import compile, split
3
3
  from typing import Dict, List
4
4
  from urllib.parse import unquote
5
5
 
6
- _logger = getLogger(__name__)
6
+ logger = logging.getLogger(__name__)
7
7
 
8
8
  # Optional whitespace
9
9
  _OWS = r"[ \t]*"
@@ -35,7 +35,7 @@ def parse_env_headers(s: str) -> Dict[str, str]:
35
35
  continue
36
36
  match = _HEADER_PATTERN.fullmatch(header.strip())
37
37
  if not match:
38
- _logger.warning(
38
+ logger.warning(
39
39
  "Header format invalid! Header values in environment variables must be "
40
40
  "URL encoded: %s",
41
41
  header,
phoenix/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "4.35.2"
1
+ __version__ = "5.0.0"
@@ -1,4 +0,0 @@
1
- # Future Migrations
2
-
3
- This folder contains future migrations for unreleased features that are under active development. Do not run these migrations unless you are a Phoenix developer.
4
-
@@ -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,3 +0,0 @@
1
- from phoenix.trace.langchain.instrumentor import LangChainInstrumentor
2
-
3
- __all__ = ("LangChainInstrumentor",)
@@ -1,35 +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
- logger.addHandler(logging.NullHandler())
17
-
18
- __all__ = ("LangChainInstrumentor",)
19
-
20
-
21
- class LangChainInstrumentor(Instrumentor):
22
- def __init__(self, *args: Any, **kwargs: Any) -> None:
23
- if find_spec("langchain_core") is None:
24
- raise PackageNotFoundError(
25
- "Missing `langchain-core`. Install with `pip install langchain-core`."
26
- )
27
- super().__init__()
28
-
29
- def instrument(self) -> None:
30
- tracer_provider = trace_sdk.TracerProvider(
31
- resource=Resource({ResourceAttributes.PROJECT_NAME: get_env_project_name()}),
32
- span_limits=trace_sdk.SpanLimits(max_attributes=10_000),
33
- )
34
- tracer_provider.add_span_processor(SimpleSpanProcessor(_OpenInferenceExporter()))
35
- super().instrument(skip_dep_check=True, tracer_provider=tracer_provider)
@@ -1,3 +0,0 @@
1
- from .callback import OpenInferenceTraceCallbackHandler
2
-
3
- __all__ = ["OpenInferenceTraceCallbackHandler"]