arize-phoenix 10.0.4__py3-none-any.whl → 12.28.1__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.
- {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/METADATA +124 -72
- arize_phoenix-12.28.1.dist-info/RECORD +499 -0
- {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/WHEEL +1 -1
- {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/IP_NOTICE +1 -1
- phoenix/__generated__/__init__.py +0 -0
- phoenix/__generated__/classification_evaluator_configs/__init__.py +20 -0
- phoenix/__generated__/classification_evaluator_configs/_document_relevance_classification_evaluator_config.py +17 -0
- phoenix/__generated__/classification_evaluator_configs/_hallucination_classification_evaluator_config.py +17 -0
- phoenix/__generated__/classification_evaluator_configs/_models.py +18 -0
- phoenix/__generated__/classification_evaluator_configs/_tool_selection_classification_evaluator_config.py +17 -0
- phoenix/__init__.py +5 -4
- phoenix/auth.py +39 -2
- phoenix/config.py +1763 -91
- phoenix/datetime_utils.py +120 -2
- phoenix/db/README.md +595 -25
- phoenix/db/bulk_inserter.py +145 -103
- phoenix/db/engines.py +140 -33
- phoenix/db/enums.py +3 -12
- phoenix/db/facilitator.py +302 -35
- phoenix/db/helpers.py +1000 -65
- phoenix/db/iam_auth.py +64 -0
- phoenix/db/insertion/dataset.py +135 -2
- phoenix/db/insertion/document_annotation.py +9 -6
- phoenix/db/insertion/evaluation.py +2 -3
- phoenix/db/insertion/helpers.py +17 -2
- phoenix/db/insertion/session_annotation.py +176 -0
- phoenix/db/insertion/span.py +15 -11
- phoenix/db/insertion/span_annotation.py +3 -4
- phoenix/db/insertion/trace_annotation.py +3 -4
- phoenix/db/insertion/types.py +50 -20
- phoenix/db/migrations/versions/01a8342c9cdf_add_user_id_on_datasets.py +40 -0
- phoenix/db/migrations/versions/0df286449799_add_session_annotations_table.py +105 -0
- phoenix/db/migrations/versions/272b66ff50f8_drop_single_indices.py +119 -0
- phoenix/db/migrations/versions/58228d933c91_dataset_labels.py +67 -0
- phoenix/db/migrations/versions/699f655af132_experiment_tags.py +57 -0
- phoenix/db/migrations/versions/735d3d93c33e_add_composite_indices.py +41 -0
- phoenix/db/migrations/versions/a20694b15f82_cost.py +196 -0
- phoenix/db/migrations/versions/ab513d89518b_add_user_id_on_dataset_versions.py +40 -0
- phoenix/db/migrations/versions/d0690a79ea51_users_on_experiments.py +40 -0
- phoenix/db/migrations/versions/deb2c81c0bb2_dataset_splits.py +139 -0
- phoenix/db/migrations/versions/e76cbd66ffc3_add_experiments_dataset_examples.py +87 -0
- phoenix/db/models.py +669 -56
- phoenix/db/pg_config.py +10 -0
- phoenix/db/types/model_provider.py +4 -0
- phoenix/db/types/token_price_customization.py +29 -0
- phoenix/db/types/trace_retention.py +23 -15
- phoenix/experiments/evaluators/utils.py +3 -3
- phoenix/experiments/functions.py +160 -52
- phoenix/experiments/tracing.py +2 -2
- phoenix/experiments/types.py +1 -1
- phoenix/inferences/inferences.py +1 -2
- phoenix/server/api/auth.py +38 -7
- phoenix/server/api/auth_messages.py +46 -0
- phoenix/server/api/context.py +100 -4
- phoenix/server/api/dataloaders/__init__.py +79 -5
- phoenix/server/api/dataloaders/annotation_configs_by_project.py +31 -0
- phoenix/server/api/dataloaders/annotation_summaries.py +60 -8
- phoenix/server/api/dataloaders/average_experiment_repeated_run_group_latency.py +50 -0
- phoenix/server/api/dataloaders/average_experiment_run_latency.py +17 -24
- phoenix/server/api/dataloaders/cache/two_tier_cache.py +1 -2
- phoenix/server/api/dataloaders/dataset_dataset_splits.py +52 -0
- phoenix/server/api/dataloaders/dataset_example_revisions.py +0 -1
- phoenix/server/api/dataloaders/dataset_example_splits.py +40 -0
- phoenix/server/api/dataloaders/dataset_examples_and_versions_by_experiment_run.py +47 -0
- phoenix/server/api/dataloaders/dataset_labels.py +36 -0
- phoenix/server/api/dataloaders/document_evaluation_summaries.py +2 -2
- phoenix/server/api/dataloaders/document_evaluations.py +6 -9
- phoenix/server/api/dataloaders/experiment_annotation_summaries.py +88 -34
- phoenix/server/api/dataloaders/experiment_dataset_splits.py +43 -0
- phoenix/server/api/dataloaders/experiment_error_rates.py +21 -28
- phoenix/server/api/dataloaders/experiment_repeated_run_group_annotation_summaries.py +77 -0
- phoenix/server/api/dataloaders/experiment_repeated_run_groups.py +57 -0
- phoenix/server/api/dataloaders/experiment_runs_by_experiment_and_example.py +44 -0
- phoenix/server/api/dataloaders/last_used_times_by_generative_model_id.py +35 -0
- phoenix/server/api/dataloaders/latency_ms_quantile.py +40 -8
- phoenix/server/api/dataloaders/record_counts.py +37 -10
- phoenix/server/api/dataloaders/session_annotations_by_session.py +29 -0
- phoenix/server/api/dataloaders/span_cost_by_span.py +24 -0
- phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_generative_model.py +56 -0
- phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_project_session.py +57 -0
- phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_span.py +43 -0
- phoenix/server/api/dataloaders/span_cost_detail_summary_entries_by_trace.py +56 -0
- phoenix/server/api/dataloaders/span_cost_details_by_span_cost.py +27 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_experiment.py +57 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_experiment_repeated_run_group.py +64 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_experiment_run.py +58 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_generative_model.py +55 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_project.py +152 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_project_session.py +56 -0
- phoenix/server/api/dataloaders/span_cost_summary_by_trace.py +55 -0
- phoenix/server/api/dataloaders/span_costs.py +29 -0
- phoenix/server/api/dataloaders/table_fields.py +2 -2
- phoenix/server/api/dataloaders/token_prices_by_model.py +30 -0
- phoenix/server/api/dataloaders/trace_annotations_by_trace.py +27 -0
- phoenix/server/api/dataloaders/types.py +29 -0
- phoenix/server/api/exceptions.py +11 -1
- phoenix/server/api/helpers/dataset_helpers.py +5 -1
- phoenix/server/api/helpers/playground_clients.py +1243 -292
- phoenix/server/api/helpers/playground_registry.py +2 -2
- phoenix/server/api/helpers/playground_spans.py +8 -4
- phoenix/server/api/helpers/playground_users.py +26 -0
- phoenix/server/api/helpers/prompts/conversions/aws.py +83 -0
- phoenix/server/api/helpers/prompts/conversions/google.py +103 -0
- phoenix/server/api/helpers/prompts/models.py +205 -22
- phoenix/server/api/input_types/{SpanAnnotationFilter.py → AnnotationFilter.py} +22 -14
- phoenix/server/api/input_types/ChatCompletionInput.py +6 -2
- phoenix/server/api/input_types/CreateProjectInput.py +27 -0
- phoenix/server/api/input_types/CreateProjectSessionAnnotationInput.py +37 -0
- phoenix/server/api/input_types/DatasetFilter.py +17 -0
- phoenix/server/api/input_types/ExperimentRunSort.py +237 -0
- phoenix/server/api/input_types/GenerativeCredentialInput.py +9 -0
- phoenix/server/api/input_types/GenerativeModelInput.py +5 -0
- phoenix/server/api/input_types/ProjectSessionSort.py +161 -1
- phoenix/server/api/input_types/PromptFilter.py +14 -0
- phoenix/server/api/input_types/PromptVersionInput.py +52 -1
- phoenix/server/api/input_types/SpanSort.py +44 -7
- phoenix/server/api/input_types/TimeBinConfig.py +23 -0
- phoenix/server/api/input_types/UpdateAnnotationInput.py +34 -0
- phoenix/server/api/input_types/UserRoleInput.py +1 -0
- phoenix/server/api/mutations/__init__.py +10 -0
- phoenix/server/api/mutations/annotation_config_mutations.py +8 -8
- phoenix/server/api/mutations/api_key_mutations.py +19 -23
- phoenix/server/api/mutations/chat_mutations.py +154 -47
- phoenix/server/api/mutations/dataset_label_mutations.py +243 -0
- phoenix/server/api/mutations/dataset_mutations.py +21 -16
- phoenix/server/api/mutations/dataset_split_mutations.py +351 -0
- phoenix/server/api/mutations/experiment_mutations.py +2 -2
- phoenix/server/api/mutations/export_events_mutations.py +3 -3
- phoenix/server/api/mutations/model_mutations.py +210 -0
- phoenix/server/api/mutations/project_mutations.py +49 -10
- phoenix/server/api/mutations/project_session_annotations_mutations.py +158 -0
- phoenix/server/api/mutations/project_trace_retention_policy_mutations.py +8 -4
- phoenix/server/api/mutations/prompt_label_mutations.py +74 -65
- phoenix/server/api/mutations/prompt_mutations.py +65 -129
- phoenix/server/api/mutations/prompt_version_tag_mutations.py +11 -8
- phoenix/server/api/mutations/span_annotations_mutations.py +15 -10
- phoenix/server/api/mutations/trace_annotations_mutations.py +14 -10
- phoenix/server/api/mutations/trace_mutations.py +47 -3
- phoenix/server/api/mutations/user_mutations.py +66 -41
- phoenix/server/api/queries.py +768 -293
- phoenix/server/api/routers/__init__.py +2 -2
- phoenix/server/api/routers/auth.py +154 -88
- phoenix/server/api/routers/ldap.py +229 -0
- phoenix/server/api/routers/oauth2.py +369 -106
- phoenix/server/api/routers/v1/__init__.py +24 -4
- phoenix/server/api/routers/v1/annotation_configs.py +23 -31
- phoenix/server/api/routers/v1/annotations.py +481 -17
- phoenix/server/api/routers/v1/datasets.py +395 -81
- phoenix/server/api/routers/v1/documents.py +142 -0
- phoenix/server/api/routers/v1/evaluations.py +24 -31
- phoenix/server/api/routers/v1/experiment_evaluations.py +19 -8
- phoenix/server/api/routers/v1/experiment_runs.py +337 -59
- phoenix/server/api/routers/v1/experiments.py +479 -48
- phoenix/server/api/routers/v1/models.py +7 -0
- phoenix/server/api/routers/v1/projects.py +18 -49
- phoenix/server/api/routers/v1/prompts.py +54 -40
- phoenix/server/api/routers/v1/sessions.py +108 -0
- phoenix/server/api/routers/v1/spans.py +1091 -81
- phoenix/server/api/routers/v1/traces.py +132 -78
- phoenix/server/api/routers/v1/users.py +389 -0
- phoenix/server/api/routers/v1/utils.py +3 -7
- phoenix/server/api/subscriptions.py +305 -88
- phoenix/server/api/types/Annotation.py +90 -23
- phoenix/server/api/types/ApiKey.py +13 -17
- phoenix/server/api/types/AuthMethod.py +1 -0
- phoenix/server/api/types/ChatCompletionSubscriptionPayload.py +1 -0
- phoenix/server/api/types/CostBreakdown.py +12 -0
- phoenix/server/api/types/Dataset.py +226 -72
- phoenix/server/api/types/DatasetExample.py +88 -18
- phoenix/server/api/types/DatasetExperimentAnnotationSummary.py +10 -0
- phoenix/server/api/types/DatasetLabel.py +57 -0
- phoenix/server/api/types/DatasetSplit.py +98 -0
- phoenix/server/api/types/DatasetVersion.py +49 -4
- phoenix/server/api/types/DocumentAnnotation.py +212 -0
- phoenix/server/api/types/Experiment.py +264 -59
- phoenix/server/api/types/ExperimentComparison.py +5 -10
- phoenix/server/api/types/ExperimentRepeatedRunGroup.py +155 -0
- phoenix/server/api/types/ExperimentRepeatedRunGroupAnnotationSummary.py +9 -0
- phoenix/server/api/types/ExperimentRun.py +169 -65
- phoenix/server/api/types/ExperimentRunAnnotation.py +158 -39
- phoenix/server/api/types/GenerativeModel.py +245 -3
- phoenix/server/api/types/GenerativeProvider.py +70 -11
- phoenix/server/api/types/{Model.py → InferenceModel.py} +1 -1
- phoenix/server/api/types/ModelInterface.py +16 -0
- phoenix/server/api/types/PlaygroundModel.py +20 -0
- phoenix/server/api/types/Project.py +1278 -216
- phoenix/server/api/types/ProjectSession.py +188 -28
- phoenix/server/api/types/ProjectSessionAnnotation.py +187 -0
- phoenix/server/api/types/ProjectTraceRetentionPolicy.py +1 -1
- phoenix/server/api/types/Prompt.py +119 -39
- phoenix/server/api/types/PromptLabel.py +42 -25
- phoenix/server/api/types/PromptVersion.py +11 -8
- phoenix/server/api/types/PromptVersionTag.py +65 -25
- phoenix/server/api/types/ServerStatus.py +6 -0
- phoenix/server/api/types/Span.py +167 -123
- phoenix/server/api/types/SpanAnnotation.py +189 -42
- phoenix/server/api/types/SpanCostDetailSummaryEntry.py +10 -0
- phoenix/server/api/types/SpanCostSummary.py +10 -0
- phoenix/server/api/types/SystemApiKey.py +65 -1
- phoenix/server/api/types/TokenPrice.py +16 -0
- phoenix/server/api/types/TokenUsage.py +3 -3
- phoenix/server/api/types/Trace.py +223 -51
- phoenix/server/api/types/TraceAnnotation.py +149 -50
- phoenix/server/api/types/User.py +137 -32
- phoenix/server/api/types/UserApiKey.py +73 -26
- phoenix/server/api/types/node.py +10 -0
- phoenix/server/api/types/pagination.py +11 -2
- phoenix/server/app.py +290 -45
- phoenix/server/authorization.py +38 -3
- phoenix/server/bearer_auth.py +34 -24
- phoenix/server/cost_tracking/cost_details_calculator.py +196 -0
- phoenix/server/cost_tracking/cost_model_lookup.py +179 -0
- phoenix/server/cost_tracking/helpers.py +68 -0
- phoenix/server/cost_tracking/model_cost_manifest.json +3657 -830
- phoenix/server/cost_tracking/regex_specificity.py +397 -0
- phoenix/server/cost_tracking/token_cost_calculator.py +57 -0
- phoenix/server/daemons/__init__.py +0 -0
- phoenix/server/daemons/db_disk_usage_monitor.py +214 -0
- phoenix/server/daemons/generative_model_store.py +103 -0
- phoenix/server/daemons/span_cost_calculator.py +99 -0
- phoenix/server/dml_event.py +17 -0
- phoenix/server/dml_event_handler.py +5 -0
- phoenix/server/email/sender.py +56 -3
- phoenix/server/email/templates/db_disk_usage_notification.html +19 -0
- phoenix/server/email/types.py +11 -0
- phoenix/server/experiments/__init__.py +0 -0
- phoenix/server/experiments/utils.py +14 -0
- phoenix/server/grpc_server.py +11 -11
- phoenix/server/jwt_store.py +17 -15
- phoenix/server/ldap.py +1449 -0
- phoenix/server/main.py +26 -10
- phoenix/server/oauth2.py +330 -12
- phoenix/server/prometheus.py +66 -6
- phoenix/server/rate_limiters.py +4 -9
- phoenix/server/retention.py +33 -20
- phoenix/server/session_filters.py +49 -0
- phoenix/server/static/.vite/manifest.json +55 -51
- phoenix/server/static/assets/components-BreFUQQa.js +6702 -0
- phoenix/server/static/assets/{index-E0M82BdE.js → index-CTQoemZv.js} +140 -56
- phoenix/server/static/assets/pages-DBE5iYM3.js +9524 -0
- phoenix/server/static/assets/vendor-BGzfc4EU.css +1 -0
- phoenix/server/static/assets/vendor-DCE4v-Ot.js +920 -0
- phoenix/server/static/assets/vendor-codemirror-D5f205eT.js +25 -0
- phoenix/server/static/assets/vendor-recharts-V9cwpXsm.js +37 -0
- phoenix/server/static/assets/vendor-shiki-Do--csgv.js +5 -0
- phoenix/server/static/assets/vendor-three-CmB8bl_y.js +3840 -0
- phoenix/server/templates/index.html +40 -6
- phoenix/server/thread_server.py +1 -2
- phoenix/server/types.py +14 -4
- phoenix/server/utils.py +74 -0
- phoenix/session/client.py +56 -3
- phoenix/session/data_extractor.py +5 -0
- phoenix/session/evaluation.py +14 -5
- phoenix/session/session.py +45 -9
- phoenix/settings.py +5 -0
- phoenix/trace/attributes.py +80 -13
- phoenix/trace/dsl/helpers.py +90 -1
- phoenix/trace/dsl/query.py +8 -6
- phoenix/trace/projects.py +5 -0
- phoenix/utilities/template_formatters.py +1 -1
- phoenix/version.py +1 -1
- arize_phoenix-10.0.4.dist-info/RECORD +0 -405
- phoenix/server/api/types/Evaluation.py +0 -39
- phoenix/server/cost_tracking/cost_lookup.py +0 -255
- phoenix/server/static/assets/components-DULKeDfL.js +0 -4365
- phoenix/server/static/assets/pages-Cl0A-0U2.js +0 -7430
- phoenix/server/static/assets/vendor-WIZid84E.css +0 -1
- phoenix/server/static/assets/vendor-arizeai-Dy-0mSNw.js +0 -649
- phoenix/server/static/assets/vendor-codemirror-DBtifKNr.js +0 -33
- phoenix/server/static/assets/vendor-oB4u9zuV.js +0 -905
- phoenix/server/static/assets/vendor-recharts-D-T4KPz2.js +0 -59
- phoenix/server/static/assets/vendor-shiki-BMn4O_9F.js +0 -5
- phoenix/server/static/assets/vendor-three-C5WAXd5r.js +0 -2998
- phoenix/utilities/deprecation.py +0 -31
- {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/entry_points.txt +0 -0
- {arize_phoenix-10.0.4.dist-info → arize_phoenix-12.28.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,54 +1,70 @@
|
|
|
1
1
|
import gzip
|
|
2
2
|
import zlib
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Optional
|
|
4
4
|
|
|
5
|
-
from fastapi import APIRouter, BackgroundTasks, Header, HTTPException, Query
|
|
6
|
-
from google.protobuf.json_format import MessageToJson
|
|
5
|
+
from fastapi import APIRouter, BackgroundTasks, Depends, Header, HTTPException, Path, Query
|
|
7
6
|
from google.protobuf.message import DecodeError
|
|
8
7
|
from opentelemetry.proto.collector.trace.v1.trace_service_pb2 import (
|
|
9
8
|
ExportTraceServiceRequest,
|
|
10
9
|
ExportTraceServiceResponse,
|
|
11
10
|
)
|
|
12
11
|
from pydantic import Field
|
|
13
|
-
from sqlalchemy import
|
|
12
|
+
from sqlalchemy import delete, select
|
|
14
13
|
from starlette.concurrency import run_in_threadpool
|
|
15
14
|
from starlette.datastructures import State
|
|
16
15
|
from starlette.requests import Request
|
|
17
|
-
from starlette.responses import
|
|
18
|
-
from starlette.status import (
|
|
19
|
-
HTTP_404_NOT_FOUND,
|
|
20
|
-
HTTP_415_UNSUPPORTED_MEDIA_TYPE,
|
|
21
|
-
HTTP_422_UNPROCESSABLE_ENTITY,
|
|
22
|
-
)
|
|
16
|
+
from starlette.responses import Response
|
|
23
17
|
from strawberry.relay import GlobalID
|
|
24
18
|
|
|
25
19
|
from phoenix.db import models
|
|
26
|
-
from phoenix.db.
|
|
27
|
-
from phoenix.db.insertion.
|
|
20
|
+
from phoenix.db.helpers import SupportedSQLDialect
|
|
21
|
+
from phoenix.db.insertion.helpers import as_kv, insert_on_conflict
|
|
22
|
+
from phoenix.server.api.routers.v1.annotations import TraceAnnotationData
|
|
23
|
+
from phoenix.server.api.types.node import from_global_id_with_expected_type
|
|
24
|
+
from phoenix.server.authorization import is_not_locked
|
|
28
25
|
from phoenix.server.bearer_auth import PhoenixUser
|
|
29
|
-
from phoenix.server.dml_event import TraceAnnotationInsertEvent
|
|
26
|
+
from phoenix.server.dml_event import SpanDeleteEvent, TraceAnnotationInsertEvent
|
|
27
|
+
from phoenix.server.prometheus import SPAN_QUEUE_REJECTIONS
|
|
30
28
|
from phoenix.trace.otel import decode_otlp_span
|
|
31
29
|
from phoenix.utilities.project import get_project_name
|
|
32
30
|
|
|
33
31
|
from .models import V1RoutesBaseModel
|
|
34
|
-
from .utils import
|
|
32
|
+
from .utils import (
|
|
33
|
+
RequestBody,
|
|
34
|
+
ResponseBody,
|
|
35
|
+
add_errors_to_responses,
|
|
36
|
+
)
|
|
35
37
|
|
|
36
38
|
router = APIRouter(tags=["traces"])
|
|
37
39
|
|
|
38
40
|
|
|
41
|
+
def is_not_at_capacity(request: Request) -> None:
|
|
42
|
+
if request.app.state.span_queue_is_full():
|
|
43
|
+
SPAN_QUEUE_REJECTIONS.inc()
|
|
44
|
+
raise HTTPException(
|
|
45
|
+
detail="Server is at capacity and cannot process more requests",
|
|
46
|
+
status_code=503,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
39
50
|
@router.post(
|
|
40
51
|
"/traces",
|
|
52
|
+
dependencies=[Depends(is_not_locked), Depends(is_not_at_capacity)],
|
|
41
53
|
operation_id="addTraces",
|
|
42
54
|
summary="Send traces",
|
|
43
55
|
responses=add_errors_to_responses(
|
|
44
56
|
[
|
|
45
57
|
{
|
|
46
|
-
"status_code":
|
|
58
|
+
"status_code": 415,
|
|
47
59
|
"description": (
|
|
48
60
|
"Unsupported content type (only `application/x-protobuf` is supported)"
|
|
49
61
|
),
|
|
50
62
|
},
|
|
51
|
-
{"status_code":
|
|
63
|
+
{"status_code": 422, "description": "Invalid request body"},
|
|
64
|
+
{
|
|
65
|
+
"status_code": 503,
|
|
66
|
+
"description": "Server is at capacity and cannot process more requests",
|
|
67
|
+
},
|
|
52
68
|
]
|
|
53
69
|
),
|
|
54
70
|
openapi_extra={
|
|
@@ -66,16 +82,16 @@ async def post_traces(
|
|
|
66
82
|
background_tasks: BackgroundTasks,
|
|
67
83
|
content_type: Optional[str] = Header(default=None),
|
|
68
84
|
content_encoding: Optional[str] = Header(default=None),
|
|
69
|
-
) ->
|
|
85
|
+
) -> Response:
|
|
70
86
|
if content_type != "application/x-protobuf":
|
|
71
87
|
raise HTTPException(
|
|
72
88
|
detail=f"Unsupported content type: {content_type}",
|
|
73
|
-
status_code=
|
|
89
|
+
status_code=415,
|
|
74
90
|
)
|
|
75
91
|
if content_encoding and content_encoding not in ("gzip", "deflate"):
|
|
76
92
|
raise HTTPException(
|
|
77
93
|
detail=f"Unsupported content encoding: {content_encoding}",
|
|
78
|
-
status_code=
|
|
94
|
+
status_code=415,
|
|
79
95
|
)
|
|
80
96
|
body = await request.body()
|
|
81
97
|
if content_encoding == "gzip":
|
|
@@ -88,59 +104,22 @@ async def post_traces(
|
|
|
88
104
|
except DecodeError:
|
|
89
105
|
raise HTTPException(
|
|
90
106
|
detail="Request body is invalid ExportTraceServiceRequest",
|
|
91
|
-
status_code=
|
|
107
|
+
status_code=422,
|
|
92
108
|
)
|
|
93
109
|
background_tasks.add_task(_add_spans, req, request.state)
|
|
94
|
-
return JSONResponse(MessageToJson(ExportTraceServiceResponse()))
|
|
95
110
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
111
|
+
# "The server MUST use the same Content-Type in the response as it received in the request"
|
|
112
|
+
response_message = ExportTraceServiceResponse()
|
|
113
|
+
response_bytes = response_message.SerializeToString()
|
|
114
|
+
return Response(
|
|
115
|
+
content=response_bytes,
|
|
116
|
+
media_type="application/x-protobuf",
|
|
117
|
+
status_code=200,
|
|
102
118
|
)
|
|
103
119
|
|
|
104
120
|
|
|
105
|
-
class
|
|
106
|
-
|
|
107
|
-
name: str = Field(description="The name of the annotation")
|
|
108
|
-
annotator_kind: Literal["LLM", "HUMAN"] = Field(
|
|
109
|
-
description="The kind of annotator used for the annotation"
|
|
110
|
-
)
|
|
111
|
-
result: Optional[TraceAnnotationResult] = Field(
|
|
112
|
-
default=None, description="The result of the annotation"
|
|
113
|
-
)
|
|
114
|
-
metadata: Optional[dict[str, Any]] = Field(
|
|
115
|
-
default=None, description="Metadata for the annotation"
|
|
116
|
-
)
|
|
117
|
-
identifier: str = Field(
|
|
118
|
-
default="",
|
|
119
|
-
description=(
|
|
120
|
-
"The identifier of the annotation. "
|
|
121
|
-
"If provided, the annotation will be updated if it already exists."
|
|
122
|
-
),
|
|
123
|
-
)
|
|
124
|
-
|
|
125
|
-
def as_precursor(self, *, user_id: Optional[int] = None) -> Precursors.TraceAnnotation:
|
|
126
|
-
return Precursors.TraceAnnotation(
|
|
127
|
-
self.trace_id,
|
|
128
|
-
models.TraceAnnotation(
|
|
129
|
-
name=self.name,
|
|
130
|
-
annotator_kind=self.annotator_kind,
|
|
131
|
-
score=self.result.score if self.result else None,
|
|
132
|
-
label=self.result.label if self.result else None,
|
|
133
|
-
explanation=self.result.explanation if self.result else None,
|
|
134
|
-
metadata_=self.metadata or {},
|
|
135
|
-
identifier=self.identifier,
|
|
136
|
-
source="APP",
|
|
137
|
-
user_id=user_id,
|
|
138
|
-
),
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
class AnnotateTracesRequestBody(RequestBody[list[TraceAnnotation]]):
|
|
143
|
-
data: list[TraceAnnotation] = Field(description="The trace annotations to be upserted")
|
|
121
|
+
class AnnotateTracesRequestBody(RequestBody[list[TraceAnnotationData]]):
|
|
122
|
+
data: list[TraceAnnotationData] = Field(description="The trace annotations to be upserted")
|
|
144
123
|
|
|
145
124
|
|
|
146
125
|
class InsertedTraceAnnotation(V1RoutesBaseModel):
|
|
@@ -153,17 +132,15 @@ class AnnotateTracesResponseBody(ResponseBody[list[InsertedTraceAnnotation]]):
|
|
|
153
132
|
|
|
154
133
|
@router.post(
|
|
155
134
|
"/trace_annotations",
|
|
135
|
+
dependencies=[Depends(is_not_locked)],
|
|
156
136
|
operation_id="annotateTraces",
|
|
157
137
|
summary="Create trace annotations",
|
|
158
|
-
responses=add_errors_to_responses(
|
|
159
|
-
[{"status_code": HTTP_404_NOT_FOUND, "description": "Trace not found"}]
|
|
160
|
-
),
|
|
161
|
-
include_in_schema=False,
|
|
138
|
+
responses=add_errors_to_responses([{"status_code": 404, "description": "Trace not found"}]),
|
|
162
139
|
)
|
|
163
140
|
async def annotate_traces(
|
|
164
141
|
request: Request,
|
|
165
142
|
request_body: AnnotateTracesRequestBody,
|
|
166
|
-
sync: bool = Query(default=
|
|
143
|
+
sync: bool = Query(default=False, description="If true, fulfill request synchronously."),
|
|
167
144
|
) -> AnnotateTracesResponseBody:
|
|
168
145
|
if not request_body.data:
|
|
169
146
|
return AnnotateTracesResponseBody(data=[])
|
|
@@ -174,15 +151,17 @@ async def annotate_traces(
|
|
|
174
151
|
|
|
175
152
|
precursors = [d.as_precursor(user_id=user_id) for d in request_body.data]
|
|
176
153
|
if not sync:
|
|
177
|
-
await request.state.
|
|
154
|
+
await request.state.enqueue_annotations(*precursors)
|
|
178
155
|
return AnnotateTracesResponseBody(data=[])
|
|
179
156
|
|
|
180
157
|
trace_ids = {p.trace_id for p in precursors}
|
|
181
158
|
async with request.app.state.db() as session:
|
|
182
159
|
existing_traces = {
|
|
183
|
-
|
|
184
|
-
async for
|
|
185
|
-
select(models.Trace
|
|
160
|
+
trace_id: id_
|
|
161
|
+
async for trace_id, id_ in await session.stream(
|
|
162
|
+
select(models.Trace.trace_id, models.Trace.id).filter(
|
|
163
|
+
models.Trace.trace_id.in_(trace_ids)
|
|
164
|
+
)
|
|
186
165
|
)
|
|
187
166
|
}
|
|
188
167
|
|
|
@@ -190,13 +169,19 @@ async def annotate_traces(
|
|
|
190
169
|
if missing_trace_ids:
|
|
191
170
|
raise HTTPException(
|
|
192
171
|
detail=f"Traces with IDs {', '.join(missing_trace_ids)} do not exist.",
|
|
193
|
-
status_code=
|
|
172
|
+
status_code=404,
|
|
194
173
|
)
|
|
195
174
|
inserted_ids = []
|
|
175
|
+
dialect = SupportedSQLDialect(session.bind.dialect.name)
|
|
196
176
|
for p in precursors:
|
|
197
177
|
values = dict(as_kv(p.as_insertable(existing_traces[p.trace_id]).row))
|
|
198
178
|
trace_annotation_id = await session.scalar(
|
|
199
|
-
|
|
179
|
+
insert_on_conflict(
|
|
180
|
+
values,
|
|
181
|
+
dialect=dialect,
|
|
182
|
+
table=models.TraceAnnotation,
|
|
183
|
+
unique_by=("name", "trace_rowid", "identifier"),
|
|
184
|
+
).returning(models.TraceAnnotation.id)
|
|
200
185
|
)
|
|
201
186
|
inserted_ids.append(trace_annotation_id)
|
|
202
187
|
request.state.event_queue.put(TraceAnnotationInsertEvent(tuple(inserted_ids)))
|
|
@@ -214,4 +199,73 @@ async def _add_spans(req: ExportTraceServiceRequest, state: State) -> None:
|
|
|
214
199
|
for scope_span in resource_spans.scope_spans:
|
|
215
200
|
for otlp_span in scope_span.spans:
|
|
216
201
|
span = await run_in_threadpool(decode_otlp_span, otlp_span)
|
|
217
|
-
await state.
|
|
202
|
+
await state.enqueue_span(span, project_name)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@router.delete(
|
|
206
|
+
"/traces/{trace_identifier}",
|
|
207
|
+
operation_id="deleteTrace",
|
|
208
|
+
summary="Delete a trace by identifier",
|
|
209
|
+
description=(
|
|
210
|
+
"Delete an entire trace by its identifier. The identifier can be either:\n"
|
|
211
|
+
"1. A Relay node ID (base64-encoded)\n"
|
|
212
|
+
"2. An OpenTelemetry trace_id (hex string)\n\n"
|
|
213
|
+
"This will permanently remove all spans in the trace and their associated data."
|
|
214
|
+
),
|
|
215
|
+
responses=add_errors_to_responses([404]),
|
|
216
|
+
status_code=204, # No Content for successful deletion
|
|
217
|
+
)
|
|
218
|
+
async def delete_trace(
|
|
219
|
+
request: Request,
|
|
220
|
+
trace_identifier: str = Path(
|
|
221
|
+
description="The trace identifier: either a relay GlobalID or OpenTelemetry trace_id"
|
|
222
|
+
),
|
|
223
|
+
) -> None:
|
|
224
|
+
"""
|
|
225
|
+
Delete a trace by identifier (relay GlobalID or OpenTelemetry trace_id).
|
|
226
|
+
|
|
227
|
+
This endpoint will:
|
|
228
|
+
1. Delete the trace by identifier (relay GlobalID or OpenTelemetry trace_id)
|
|
229
|
+
2. Get project_id from the deletion for cache invalidation
|
|
230
|
+
3. Trigger cache invalidation events
|
|
231
|
+
4. Return 204 No Content on success
|
|
232
|
+
|
|
233
|
+
Note: This deletes the entire trace, including all spans, which maintains data consistency
|
|
234
|
+
and avoids orphaned spans or inconsistent cached cumulative fields.
|
|
235
|
+
"""
|
|
236
|
+
async with request.app.state.db() as session:
|
|
237
|
+
# Try to parse as GlobalID first, then fall back to trace_id
|
|
238
|
+
try:
|
|
239
|
+
trace_rowid = from_global_id_with_expected_type(
|
|
240
|
+
GlobalID.from_id(trace_identifier),
|
|
241
|
+
"Trace",
|
|
242
|
+
)
|
|
243
|
+
# Delete by database rowid
|
|
244
|
+
delete_stmt = (
|
|
245
|
+
delete(models.Trace)
|
|
246
|
+
.where(models.Trace.id == trace_rowid)
|
|
247
|
+
.returning(models.Trace.project_rowid)
|
|
248
|
+
)
|
|
249
|
+
error_detail = f"Trace with relay ID '{trace_identifier}' not found"
|
|
250
|
+
except Exception:
|
|
251
|
+
# Delete by OpenTelemetry trace_id
|
|
252
|
+
delete_stmt = (
|
|
253
|
+
delete(models.Trace)
|
|
254
|
+
.where(models.Trace.trace_id == trace_identifier)
|
|
255
|
+
.returning(models.Trace.project_rowid)
|
|
256
|
+
)
|
|
257
|
+
error_detail = f"Trace with trace_id '{trace_identifier}' not found"
|
|
258
|
+
|
|
259
|
+
project_id = await session.scalar(delete_stmt)
|
|
260
|
+
|
|
261
|
+
if project_id is None:
|
|
262
|
+
raise HTTPException(
|
|
263
|
+
status_code=404,
|
|
264
|
+
detail=error_detail,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
# Trigger cache invalidation event
|
|
268
|
+
request.state.event_queue.put(SpanDeleteEvent((project_id,)))
|
|
269
|
+
|
|
270
|
+
# Return 204 No Content (successful deletion with no response body)
|
|
271
|
+
return None
|