arize-phoenix 4.14.1__py3-none-any.whl → 4.16.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.14.1.dist-info → arize_phoenix-4.16.0.dist-info}/METADATA +5 -3
- {arize_phoenix-4.14.1.dist-info → arize_phoenix-4.16.0.dist-info}/RECORD +81 -71
- phoenix/db/bulk_inserter.py +131 -5
- phoenix/db/engines.py +2 -1
- phoenix/db/helpers.py +23 -1
- phoenix/db/insertion/constants.py +2 -0
- phoenix/db/insertion/document_annotation.py +157 -0
- phoenix/db/insertion/helpers.py +13 -0
- phoenix/db/insertion/span_annotation.py +144 -0
- phoenix/db/insertion/trace_annotation.py +144 -0
- phoenix/db/insertion/types.py +261 -0
- phoenix/experiments/functions.py +3 -2
- phoenix/experiments/types.py +3 -3
- phoenix/server/api/context.py +7 -9
- phoenix/server/api/dataloaders/__init__.py +2 -0
- phoenix/server/api/dataloaders/average_experiment_run_latency.py +3 -3
- phoenix/server/api/dataloaders/dataset_example_revisions.py +2 -4
- phoenix/server/api/dataloaders/dataset_example_spans.py +2 -4
- phoenix/server/api/dataloaders/document_evaluation_summaries.py +2 -4
- phoenix/server/api/dataloaders/document_evaluations.py +2 -4
- phoenix/server/api/dataloaders/document_retrieval_metrics.py +2 -4
- phoenix/server/api/dataloaders/evaluation_summaries.py +2 -4
- phoenix/server/api/dataloaders/experiment_annotation_summaries.py +2 -4
- phoenix/server/api/dataloaders/experiment_error_rates.py +2 -4
- phoenix/server/api/dataloaders/experiment_run_counts.py +2 -4
- phoenix/server/api/dataloaders/experiment_sequence_number.py +2 -4
- phoenix/server/api/dataloaders/latency_ms_quantile.py +2 -3
- phoenix/server/api/dataloaders/min_start_or_max_end_times.py +2 -4
- phoenix/server/api/dataloaders/project_by_name.py +3 -3
- phoenix/server/api/dataloaders/record_counts.py +2 -4
- phoenix/server/api/dataloaders/span_annotations.py +2 -4
- phoenix/server/api/dataloaders/span_dataset_examples.py +36 -0
- phoenix/server/api/dataloaders/span_descendants.py +2 -4
- phoenix/server/api/dataloaders/span_evaluations.py +2 -4
- phoenix/server/api/dataloaders/span_projects.py +3 -3
- phoenix/server/api/dataloaders/token_counts.py +2 -4
- phoenix/server/api/dataloaders/trace_evaluations.py +2 -4
- phoenix/server/api/dataloaders/trace_row_ids.py +2 -4
- phoenix/server/api/input_types/SpanAnnotationSort.py +17 -0
- phoenix/server/api/input_types/TraceAnnotationSort.py +17 -0
- phoenix/server/api/mutations/span_annotations_mutations.py +8 -3
- phoenix/server/api/mutations/trace_annotations_mutations.py +8 -3
- phoenix/server/api/openapi/main.py +18 -2
- phoenix/server/api/openapi/schema.py +12 -12
- phoenix/server/api/routers/v1/__init__.py +36 -83
- phoenix/server/api/routers/v1/datasets.py +515 -509
- phoenix/server/api/routers/v1/evaluations.py +164 -73
- phoenix/server/api/routers/v1/experiment_evaluations.py +68 -91
- phoenix/server/api/routers/v1/experiment_runs.py +98 -155
- phoenix/server/api/routers/v1/experiments.py +132 -181
- phoenix/server/api/routers/v1/pydantic_compat.py +78 -0
- phoenix/server/api/routers/v1/spans.py +164 -203
- phoenix/server/api/routers/v1/traces.py +134 -159
- phoenix/server/api/routers/v1/utils.py +95 -0
- phoenix/server/api/types/Span.py +27 -3
- phoenix/server/api/types/Trace.py +21 -4
- phoenix/server/api/utils.py +4 -4
- phoenix/server/app.py +172 -192
- phoenix/server/grpc_server.py +2 -2
- phoenix/server/main.py +5 -9
- phoenix/server/static/.vite/manifest.json +31 -31
- phoenix/server/static/assets/components-Ci5kMOk5.js +1175 -0
- phoenix/server/static/assets/{index-CQgXRwU0.js → index-BQG5WVX7.js} +2 -2
- phoenix/server/static/assets/{pages-hdjlFZhO.js → pages-BrevprVW.js} +451 -275
- phoenix/server/static/assets/{vendor-DPvSDRn3.js → vendor-CP0b0YG0.js} +2 -2
- phoenix/server/static/assets/{vendor-arizeai-CkvPT67c.js → vendor-arizeai-DTbiPGp6.js} +27 -27
- phoenix/server/static/assets/vendor-codemirror-DtdPDzrv.js +15 -0
- phoenix/server/static/assets/{vendor-recharts-5jlNaZuF.js → vendor-recharts-A0DA1O99.js} +1 -1
- phoenix/server/thread_server.py +2 -2
- phoenix/server/types.py +18 -0
- phoenix/session/client.py +5 -3
- phoenix/session/session.py +2 -2
- phoenix/trace/dsl/filter.py +2 -6
- phoenix/trace/fixtures.py +17 -23
- phoenix/trace/utils.py +23 -0
- phoenix/utilities/client.py +116 -0
- phoenix/utilities/project.py +1 -1
- phoenix/version.py +1 -1
- phoenix/server/api/routers/v1/dataset_examples.py +0 -178
- phoenix/server/openapi/docs.py +0 -221
- phoenix/server/static/assets/components-DeS0YEmv.js +0 -1142
- phoenix/server/static/assets/vendor-codemirror-Cqwpwlua.js +0 -12
- {arize_phoenix-4.14.1.dist-info → arize_phoenix-4.16.0.dist-info}/WHEEL +0 -0
- {arize_phoenix-4.14.1.dist-info → arize_phoenix-4.16.0.dist-info}/licenses/IP_NOTICE +0 -0
- {arize_phoenix-4.14.1.dist-info → arize_phoenix-4.16.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
from datetime import timezone
|
|
2
|
-
from typing import Any, AsyncIterator, Dict, List
|
|
1
|
+
from datetime import datetime, timezone
|
|
2
|
+
from typing import Any, AsyncIterator, Dict, List, Literal, Optional
|
|
3
3
|
|
|
4
|
+
from fastapi import APIRouter, HTTPException, Query
|
|
5
|
+
from pydantic import Field
|
|
4
6
|
from sqlalchemy import select
|
|
5
7
|
from starlette.requests import Request
|
|
6
|
-
from starlette.responses import
|
|
8
|
+
from starlette.responses import Response, StreamingResponse
|
|
7
9
|
from starlette.status import HTTP_404_NOT_FOUND, HTTP_422_UNPROCESSABLE_ENTITY
|
|
8
10
|
from strawberry.relay import GlobalID
|
|
9
11
|
|
|
@@ -11,94 +13,83 @@ from phoenix.config import DEFAULT_PROJECT_NAME
|
|
|
11
13
|
from phoenix.datetime_utils import normalize_datetime
|
|
12
14
|
from phoenix.db import models
|
|
13
15
|
from phoenix.db.helpers import SupportedSQLDialect
|
|
14
|
-
from phoenix.db.insertion.helpers import insert_on_conflict
|
|
15
|
-
from phoenix.
|
|
16
|
-
from phoenix.server.api.
|
|
17
|
-
from phoenix.trace.dsl import SpanQuery
|
|
16
|
+
from phoenix.db.insertion.helpers import as_kv, insert_on_conflict
|
|
17
|
+
from phoenix.db.insertion.types import Precursors
|
|
18
|
+
from phoenix.server.api.routers.utils import df_to_bytes
|
|
19
|
+
from phoenix.trace.dsl import SpanQuery as SpanQuery_
|
|
20
|
+
|
|
21
|
+
from .pydantic_compat import V1RoutesBaseModel
|
|
22
|
+
from .utils import RequestBody, ResponseBody, add_errors_to_responses
|
|
18
23
|
|
|
19
24
|
DEFAULT_SPAN_LIMIT = 1000
|
|
20
25
|
|
|
26
|
+
router = APIRouter(tags=["traces"], include_in_schema=False)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class SpanQuery(V1RoutesBaseModel):
|
|
30
|
+
select: Optional[Dict[str, Any]] = None
|
|
31
|
+
filter: Optional[Dict[str, Any]] = None
|
|
32
|
+
explode: Optional[Dict[str, Any]] = None
|
|
33
|
+
concat: Optional[Dict[str, Any]] = None
|
|
34
|
+
rename: Optional[Dict[str, Any]] = None
|
|
35
|
+
index: Optional[Dict[str, Any]] = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class QuerySpansRequestBody(V1RoutesBaseModel):
|
|
39
|
+
queries: List[SpanQuery]
|
|
40
|
+
start_time: Optional[datetime] = None
|
|
41
|
+
end_time: Optional[datetime] = None
|
|
42
|
+
limit: int = DEFAULT_SPAN_LIMIT
|
|
43
|
+
root_spans_only: Optional[bool] = None
|
|
44
|
+
project_name: Optional[str] = Field(
|
|
45
|
+
default=None,
|
|
46
|
+
description=(
|
|
47
|
+
"The name of the project to query. "
|
|
48
|
+
"This parameter has been deprecated, use the project_name query parameter instead."
|
|
49
|
+
),
|
|
50
|
+
deprecated=True,
|
|
51
|
+
)
|
|
52
|
+
stop_time: Optional[datetime] = Field(
|
|
53
|
+
default=None,
|
|
54
|
+
description=(
|
|
55
|
+
"An upper bound on the time to query for. "
|
|
56
|
+
"This parameter has been deprecated, use the end_time parameter instead."
|
|
57
|
+
),
|
|
58
|
+
deprecated=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
21
61
|
|
|
22
62
|
# TODO: Add property details to SpanQuery schema
|
|
23
|
-
|
|
24
|
-
""
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
required: true
|
|
38
|
-
content:
|
|
39
|
-
application/json:
|
|
40
|
-
schema:
|
|
41
|
-
type: object
|
|
42
|
-
properties:
|
|
43
|
-
queries:
|
|
44
|
-
type: array
|
|
45
|
-
items:
|
|
46
|
-
type: object
|
|
47
|
-
properties:
|
|
48
|
-
select:
|
|
49
|
-
type: object
|
|
50
|
-
filter:
|
|
51
|
-
type: object
|
|
52
|
-
explode:
|
|
53
|
-
type: object
|
|
54
|
-
concat:
|
|
55
|
-
type: object
|
|
56
|
-
rename:
|
|
57
|
-
type: object
|
|
58
|
-
index:
|
|
59
|
-
type: object
|
|
60
|
-
start_time:
|
|
61
|
-
type: string
|
|
62
|
-
format: date-time
|
|
63
|
-
end_time:
|
|
64
|
-
type: string
|
|
65
|
-
format: date-time
|
|
66
|
-
nullable: true
|
|
67
|
-
limit:
|
|
68
|
-
type: integer
|
|
69
|
-
nullable: true
|
|
70
|
-
default: 1000
|
|
71
|
-
root_spans_only:
|
|
72
|
-
type: boolean
|
|
73
|
-
nullable: true
|
|
74
|
-
responses:
|
|
75
|
-
200:
|
|
76
|
-
description: Success
|
|
77
|
-
403:
|
|
78
|
-
description: Forbidden
|
|
79
|
-
404:
|
|
80
|
-
description: Not found
|
|
81
|
-
422:
|
|
82
|
-
description: Request body is invalid
|
|
83
|
-
"""
|
|
84
|
-
payload = await request.json()
|
|
85
|
-
queries = payload.pop("queries", [])
|
|
63
|
+
@router.post(
|
|
64
|
+
"/spans",
|
|
65
|
+
operation_id="querySpans",
|
|
66
|
+
summary="Query spans with query DSL",
|
|
67
|
+
responses=add_errors_to_responses([HTTP_404_NOT_FOUND, HTTP_422_UNPROCESSABLE_ENTITY]),
|
|
68
|
+
)
|
|
69
|
+
async def query_spans_handler(
|
|
70
|
+
request: Request,
|
|
71
|
+
request_body: QuerySpansRequestBody,
|
|
72
|
+
project_name: Optional[str] = Query(
|
|
73
|
+
default=None, description="The project name to get evaluations from"
|
|
74
|
+
),
|
|
75
|
+
) -> Response:
|
|
76
|
+
queries = request_body.queries
|
|
86
77
|
project_name = (
|
|
87
|
-
|
|
78
|
+
project_name
|
|
88
79
|
or request.query_params.get("project-name") # for backward compatibility
|
|
89
80
|
or request.headers.get(
|
|
90
81
|
"project-name"
|
|
91
82
|
) # read from headers/payload for backward-compatibility
|
|
92
|
-
or
|
|
83
|
+
or request_body.project_name
|
|
93
84
|
or DEFAULT_PROJECT_NAME
|
|
94
85
|
)
|
|
95
|
-
end_time =
|
|
86
|
+
end_time = request_body.end_time or request_body.stop_time
|
|
96
87
|
try:
|
|
97
|
-
span_queries = [
|
|
88
|
+
span_queries = [SpanQuery_.from_dict(query.dict()) for query in queries]
|
|
98
89
|
except Exception as e:
|
|
99
|
-
|
|
90
|
+
raise HTTPException(
|
|
91
|
+
detail=f"Invalid query: {e}",
|
|
100
92
|
status_code=HTTP_422_UNPROCESSABLE_ENTITY,
|
|
101
|
-
content=f"Invalid query: {e}",
|
|
102
93
|
)
|
|
103
94
|
async with request.app.state.db() as session:
|
|
104
95
|
results = []
|
|
@@ -108,19 +99,19 @@ async def query_spans_handler(request: Request) -> Response:
|
|
|
108
99
|
query,
|
|
109
100
|
project_name=project_name,
|
|
110
101
|
start_time=normalize_datetime(
|
|
111
|
-
|
|
102
|
+
request_body.start_time,
|
|
112
103
|
timezone.utc,
|
|
113
104
|
),
|
|
114
105
|
end_time=normalize_datetime(
|
|
115
|
-
|
|
106
|
+
end_time,
|
|
116
107
|
timezone.utc,
|
|
117
108
|
),
|
|
118
|
-
limit=
|
|
119
|
-
root_spans_only=
|
|
109
|
+
limit=request_body.limit,
|
|
110
|
+
root_spans_only=request_body.root_spans_only,
|
|
120
111
|
)
|
|
121
112
|
)
|
|
122
113
|
if not results:
|
|
123
|
-
|
|
114
|
+
raise HTTPException(status_code=HTTP_404_NOT_FOUND)
|
|
124
115
|
|
|
125
116
|
async def content() -> AsyncIterator[bytes]:
|
|
126
117
|
for result in results:
|
|
@@ -132,134 +123,104 @@ async def query_spans_handler(request: Request) -> Response:
|
|
|
132
123
|
)
|
|
133
124
|
|
|
134
125
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
- private
|
|
145
|
-
requestBody:
|
|
146
|
-
description: List of span annotations to be inserted
|
|
147
|
-
required: true
|
|
148
|
-
content:
|
|
149
|
-
application/json:
|
|
150
|
-
schema:
|
|
151
|
-
type: object
|
|
152
|
-
properties:
|
|
153
|
-
data:
|
|
154
|
-
type: array
|
|
155
|
-
items:
|
|
156
|
-
type: object
|
|
157
|
-
properties:
|
|
158
|
-
span_id:
|
|
159
|
-
type: string
|
|
160
|
-
description: The ID of the span being annotated
|
|
161
|
-
name:
|
|
162
|
-
type: string
|
|
163
|
-
description: The name of the annotation
|
|
164
|
-
annotator_kind:
|
|
165
|
-
type: string
|
|
166
|
-
description: The kind of annotator used for the annotation ("LLM" or "HUMAN")
|
|
167
|
-
result:
|
|
168
|
-
type: object
|
|
169
|
-
description: The result of the annotation
|
|
170
|
-
properties:
|
|
171
|
-
label:
|
|
172
|
-
type: string
|
|
173
|
-
description: The label assigned by the annotation
|
|
174
|
-
score:
|
|
175
|
-
type: number
|
|
176
|
-
format: float
|
|
177
|
-
description: The score assigned by the annotation
|
|
178
|
-
explanation:
|
|
179
|
-
type: string
|
|
180
|
-
description: Explanation of the annotation result
|
|
181
|
-
error:
|
|
182
|
-
type: string
|
|
183
|
-
description: Optional error message if the annotation encountered an error
|
|
184
|
-
metadata:
|
|
185
|
-
type: object
|
|
186
|
-
description: Metadata for the annotation
|
|
187
|
-
additionalProperties:
|
|
188
|
-
type: string
|
|
189
|
-
required:
|
|
190
|
-
- span_id
|
|
191
|
-
- name
|
|
192
|
-
- annotator_kind
|
|
193
|
-
responses:
|
|
194
|
-
200:
|
|
195
|
-
description: Span annotations inserted successfully
|
|
196
|
-
content:
|
|
197
|
-
application/json:
|
|
198
|
-
schema:
|
|
199
|
-
type: object
|
|
200
|
-
properties:
|
|
201
|
-
data:
|
|
202
|
-
type: array
|
|
203
|
-
items:
|
|
204
|
-
type: object
|
|
205
|
-
properties:
|
|
206
|
-
id:
|
|
207
|
-
type: string
|
|
208
|
-
description: The ID of the inserted span annotation
|
|
209
|
-
404:
|
|
210
|
-
description: Span not found
|
|
211
|
-
"""
|
|
212
|
-
payload: List[Dict[str, Any]] = (await request.json()).get("data", [])
|
|
213
|
-
span_gids = [GlobalID.from_id(annotation["span_id"]) for annotation in payload]
|
|
214
|
-
|
|
215
|
-
resolved_span_ids = []
|
|
216
|
-
for span_gid in span_gids:
|
|
217
|
-
try:
|
|
218
|
-
resolved_span_ids.append(from_global_id_with_expected_type(span_gid, "Span"))
|
|
219
|
-
except ValueError:
|
|
220
|
-
return Response(
|
|
221
|
-
content="Span with ID {span_gid} does not exist",
|
|
222
|
-
status_code=HTTP_404_NOT_FOUND,
|
|
223
|
-
)
|
|
126
|
+
@router.get("/spans", include_in_schema=False, deprecated=True)
|
|
127
|
+
async def get_spans_handler(
|
|
128
|
+
request: Request,
|
|
129
|
+
request_body: QuerySpansRequestBody,
|
|
130
|
+
project_name: Optional[str] = Query(
|
|
131
|
+
default=None, description="The project name to get evaluations from"
|
|
132
|
+
),
|
|
133
|
+
) -> Response:
|
|
134
|
+
return await query_spans_handler(request, request_body, project_name)
|
|
224
135
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
136
|
+
|
|
137
|
+
class SpanAnnotationResult(V1RoutesBaseModel):
|
|
138
|
+
label: Optional[str] = Field(default=None, description="The label assigned by the annotation")
|
|
139
|
+
score: Optional[float] = Field(default=None, description="The score assigned by the annotation")
|
|
140
|
+
explanation: Optional[str] = Field(
|
|
141
|
+
default=None, description="Explanation of the annotation result"
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class SpanAnnotation(V1RoutesBaseModel):
|
|
146
|
+
span_id: str = Field(description="OpenTelemetry Span ID (hex format w/o 0x prefix)")
|
|
147
|
+
name: str = Field(description="The name of the annotation")
|
|
148
|
+
annotator_kind: Literal["LLM", "HUMAN"] = Field(
|
|
149
|
+
description="The kind of annotator used for the annotation"
|
|
150
|
+
)
|
|
151
|
+
result: Optional[SpanAnnotationResult] = Field(
|
|
152
|
+
default=None, description="The result of the annotation"
|
|
153
|
+
)
|
|
154
|
+
metadata: Optional[Dict[str, Any]] = Field(
|
|
155
|
+
default=None, description="Metadata for the annotation"
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def as_precursor(self) -> Precursors.SpanAnnotation:
|
|
159
|
+
return Precursors.SpanAnnotation(
|
|
160
|
+
self.span_id,
|
|
161
|
+
models.SpanAnnotation(
|
|
162
|
+
name=self.name,
|
|
163
|
+
annotator_kind=self.annotator_kind,
|
|
164
|
+
score=self.result.score if self.result else None,
|
|
165
|
+
label=self.result.label if self.result else None,
|
|
166
|
+
explanation=self.result.explanation if self.result else None,
|
|
167
|
+
metadata_=self.metadata or {},
|
|
168
|
+
),
|
|
228
169
|
)
|
|
229
|
-
existing_span_ids = {span.id for span in spans.scalars()}
|
|
230
170
|
|
|
231
|
-
|
|
171
|
+
|
|
172
|
+
class AnnotateSpansRequestBody(RequestBody[List[SpanAnnotation]]):
|
|
173
|
+
data: List[SpanAnnotation]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class InsertedSpanAnnotation(V1RoutesBaseModel):
|
|
177
|
+
id: str = Field(description="The ID of the inserted span annotation")
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class AnnotateSpansResponseBody(ResponseBody[List[InsertedSpanAnnotation]]):
|
|
181
|
+
pass
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@router.post(
|
|
185
|
+
"/span_annotations",
|
|
186
|
+
operation_id="annotateSpans",
|
|
187
|
+
summary="Create or update span annotations",
|
|
188
|
+
responses=add_errors_to_responses(
|
|
189
|
+
[{"status_code": HTTP_404_NOT_FOUND, "description": "Span not found"}]
|
|
190
|
+
),
|
|
191
|
+
response_description="Span annotations inserted successfully",
|
|
192
|
+
)
|
|
193
|
+
async def annotate_spans(
|
|
194
|
+
request: Request,
|
|
195
|
+
request_body: AnnotateSpansRequestBody,
|
|
196
|
+
sync: bool = Query(default=True, description="If true, fulfill request synchronously."),
|
|
197
|
+
) -> AnnotateSpansResponseBody:
|
|
198
|
+
precursors = [d.as_precursor() for d in request_body.data]
|
|
199
|
+
if not sync:
|
|
200
|
+
await request.state.enqueue(*precursors)
|
|
201
|
+
return AnnotateSpansResponseBody(data=[])
|
|
202
|
+
|
|
203
|
+
span_ids = {p.span_id for p in precursors}
|
|
204
|
+
async with request.app.state.db() as session:
|
|
205
|
+
existing_spans = {
|
|
206
|
+
span.span_id: span.id
|
|
207
|
+
async for span in await session.stream_scalars(
|
|
208
|
+
select(models.Span).filter(models.Span.span_id.in_(span_ids))
|
|
209
|
+
)
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
missing_span_ids = span_ids - set(existing_spans.keys())
|
|
232
213
|
if missing_span_ids:
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
]
|
|
236
|
-
return Response(
|
|
237
|
-
content=f"Spans with IDs {', '.join(missing_span_gids)} do not exist.",
|
|
214
|
+
raise HTTPException(
|
|
215
|
+
detail=f"Spans with IDs {', '.join(missing_span_ids)} do not exist.",
|
|
238
216
|
status_code=HTTP_404_NOT_FOUND,
|
|
239
217
|
)
|
|
240
218
|
|
|
241
219
|
inserted_annotations = []
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
annotator_kind = annotation["annotator_kind"]
|
|
247
|
-
result = annotation.get("result")
|
|
248
|
-
label = result.get("label") if result else None
|
|
249
|
-
score = result.get("score") if result else None
|
|
250
|
-
explanation = result.get("explanation") if result else None
|
|
251
|
-
metadata = annotation.get("metadata") or {}
|
|
252
|
-
|
|
253
|
-
values = dict(
|
|
254
|
-
span_rowid=span_id,
|
|
255
|
-
name=name,
|
|
256
|
-
label=label,
|
|
257
|
-
score=score,
|
|
258
|
-
explanation=explanation,
|
|
259
|
-
annotator_kind=annotator_kind,
|
|
260
|
-
metadata_=metadata,
|
|
261
|
-
)
|
|
262
|
-
dialect = SupportedSQLDialect(session.bind.dialect.name)
|
|
220
|
+
|
|
221
|
+
dialect = SupportedSQLDialect(session.bind.dialect.name)
|
|
222
|
+
for p in precursors:
|
|
223
|
+
values = dict(as_kv(p.as_insertable(existing_spans[p.span_id]).row))
|
|
263
224
|
span_annotation_id = await session.scalar(
|
|
264
225
|
insert_on_conflict(
|
|
265
226
|
values,
|
|
@@ -269,7 +230,7 @@ async def annotate_spans(request: Request) -> Response:
|
|
|
269
230
|
).returning(models.SpanAnnotation.id)
|
|
270
231
|
)
|
|
271
232
|
inserted_annotations.append(
|
|
272
|
-
|
|
233
|
+
InsertedSpanAnnotation(id=str(GlobalID("SpanAnnotation", str(span_annotation_id))))
|
|
273
234
|
)
|
|
274
235
|
|
|
275
|
-
return
|
|
236
|
+
return AnnotateSpansResponseBody(data=inserted_annotations)
|