arize-phoenix 4.36.0__py3-none-any.whl → 5.1.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 (81) hide show
  1. {arize_phoenix-4.36.0.dist-info → arize_phoenix-5.1.0.dist-info}/METADATA +10 -12
  2. {arize_phoenix-4.36.0.dist-info → arize_phoenix-5.1.0.dist-info}/RECORD +69 -60
  3. phoenix/__init__.py +86 -0
  4. phoenix/auth.py +275 -14
  5. phoenix/config.py +277 -25
  6. phoenix/db/enums.py +20 -0
  7. phoenix/db/facilitator.py +112 -0
  8. phoenix/db/migrations/versions/cd164e83824f_users_and_tokens.py +157 -0
  9. phoenix/db/models.py +145 -60
  10. phoenix/experiments/evaluators/code_evaluators.py +9 -3
  11. phoenix/experiments/functions.py +1 -4
  12. phoenix/server/api/README.md +28 -0
  13. phoenix/server/api/auth.py +32 -0
  14. phoenix/server/api/context.py +50 -2
  15. phoenix/server/api/dataloaders/__init__.py +4 -0
  16. phoenix/server/api/dataloaders/user_roles.py +30 -0
  17. phoenix/server/api/dataloaders/users.py +33 -0
  18. phoenix/server/api/exceptions.py +7 -0
  19. phoenix/server/api/mutations/__init__.py +0 -2
  20. phoenix/server/api/mutations/api_key_mutations.py +104 -86
  21. phoenix/server/api/mutations/dataset_mutations.py +8 -8
  22. phoenix/server/api/mutations/experiment_mutations.py +2 -2
  23. phoenix/server/api/mutations/export_events_mutations.py +3 -3
  24. phoenix/server/api/mutations/project_mutations.py +3 -3
  25. phoenix/server/api/mutations/span_annotations_mutations.py +4 -4
  26. phoenix/server/api/mutations/trace_annotations_mutations.py +4 -4
  27. phoenix/server/api/mutations/user_mutations.py +282 -42
  28. phoenix/server/api/openapi/schema.py +2 -2
  29. phoenix/server/api/queries.py +48 -39
  30. phoenix/server/api/routers/__init__.py +11 -0
  31. phoenix/server/api/routers/auth.py +284 -0
  32. phoenix/server/api/routers/embeddings.py +26 -0
  33. phoenix/server/api/routers/oauth2.py +456 -0
  34. phoenix/server/api/routers/v1/__init__.py +38 -16
  35. phoenix/server/api/types/ApiKey.py +11 -0
  36. phoenix/server/api/types/AuthMethod.py +9 -0
  37. phoenix/server/api/types/User.py +48 -4
  38. phoenix/server/api/types/UserApiKey.py +35 -1
  39. phoenix/server/api/types/UserRole.py +7 -0
  40. phoenix/server/app.py +103 -31
  41. phoenix/server/bearer_auth.py +161 -0
  42. phoenix/server/email/__init__.py +0 -0
  43. phoenix/server/email/sender.py +26 -0
  44. phoenix/server/email/templates/__init__.py +0 -0
  45. phoenix/server/email/templates/password_reset.html +19 -0
  46. phoenix/server/email/types.py +11 -0
  47. phoenix/server/grpc_server.py +6 -0
  48. phoenix/server/jwt_store.py +504 -0
  49. phoenix/server/main.py +40 -9
  50. phoenix/server/oauth2.py +51 -0
  51. phoenix/server/prometheus.py +20 -0
  52. phoenix/server/rate_limiters.py +191 -0
  53. phoenix/server/static/.vite/manifest.json +31 -31
  54. phoenix/server/static/assets/{components-Dte7_KRd.js → components-REunxTt6.js} +348 -286
  55. phoenix/server/static/assets/index-DAPJxlCw.js +101 -0
  56. phoenix/server/static/assets/{pages-CnTvEGEN.js → pages-1VrMk2pW.js} +559 -291
  57. phoenix/server/static/assets/{vendor-BC3OPQuM.js → vendor-B5IC0ivG.js} +5 -5
  58. phoenix/server/static/assets/{vendor-arizeai-NjB3cZzD.js → vendor-arizeai-aFbT4kl1.js} +2 -2
  59. phoenix/server/static/assets/{vendor-codemirror-gE_JCOgX.js → vendor-codemirror-BEGorXSV.js} +1 -1
  60. phoenix/server/static/assets/{vendor-recharts-BXLYwcXF.js → vendor-recharts-6nUU7gU_.js} +1 -1
  61. phoenix/server/templates/index.html +1 -0
  62. phoenix/server/types.py +157 -1
  63. phoenix/session/client.py +7 -2
  64. phoenix/trace/fixtures.py +24 -0
  65. phoenix/utilities/client.py +16 -0
  66. phoenix/version.py +1 -1
  67. phoenix/db/migrations/future_versions/README.md +0 -4
  68. phoenix/db/migrations/future_versions/cd164e83824f_users_and_tokens.py +0 -293
  69. phoenix/db/migrations/versions/.gitignore +0 -1
  70. phoenix/server/api/mutations/auth.py +0 -18
  71. phoenix/server/api/mutations/auth_mutations.py +0 -65
  72. phoenix/server/static/assets/index-fq1-hCK4.js +0 -100
  73. phoenix/trace/langchain/__init__.py +0 -3
  74. phoenix/trace/langchain/instrumentor.py +0 -34
  75. phoenix/trace/llama_index/__init__.py +0 -3
  76. phoenix/trace/llama_index/callback.py +0 -102
  77. phoenix/trace/openai/__init__.py +0 -3
  78. phoenix/trace/openai/instrumentor.py +0 -30
  79. {arize_phoenix-4.36.0.dist-info → arize_phoenix-5.1.0.dist-info}/WHEEL +0 -0
  80. {arize_phoenix-4.36.0.dist-info → arize_phoenix-5.1.0.dist-info}/licenses/IP_NOTICE +0 -0
  81. {arize_phoenix-4.36.0.dist-info → arize_phoenix-5.1.0.dist-info}/licenses/LICENSE +0 -0
@@ -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
 
phoenix/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "4.36.0"
1
+ __version__ = "5.1.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,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,3 +0,0 @@
1
- from .callback import OpenInferenceTraceCallbackHandler
2
-
3
- __all__ = ["OpenInferenceTraceCallbackHandler"]
@@ -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))
@@ -1,3 +0,0 @@
1
- from .instrumentor import OpenAIInstrumentor
2
-
3
- __all__ = ["OpenAIInstrumentor"]