UNPKG

@arizeai/phoenix-client

Version:
127 lines 4.52 kB
import { SemanticConventions } from "@arizeai/openinference-semantic-conventions"; import { createClient } from "../client.js"; import { getSpans } from "../spans/getSpans.js"; import { getSession } from "./getSession.js"; const MAX_TRACE_IDS_PER_BATCH = 50; /** * Get the turns (root span I/O) for a session. * * Returns input/output extracted from root spans for each trace, along with * the full root span. Turns are ordered by trace start_time. * * **Note:** A "turn" is derived from a trace's root span. For input/output to appear, * the root span must have `input.value` and `output.value` attributes set * (per OpenInference semantic conventions). This typically requires instrumentation * that records these attributes on the top-level span. * * @experimental this function is experimental and may change in the future * * @example * ```ts * import { getSessionTurns } from "@arizeai/phoenix-client/sessions"; * * const turns = await getSessionTurns({ sessionId: "my-session" }); * for (const turn of turns) { * console.log(`[${turn.startTime}] Input: ${turn.input?.value}`); * console.log(`[${turn.startTime}] Output: ${turn.output?.value}`); * } * ``` */ export async function getSessionTurns({ client: _client, sessionId, }) { const client = _client ?? createClient(); // getSession already calls ensureSessionsApi internally const session = await getSession({ client, sessionId }); const traces = session.traces; if (traces.length === 0) { return []; } const projectId = session.projectId; const traceInfo = new Map(traces.map((t) => [t.traceId, t])); const allTraceIds = [...traceInfo.keys()]; // Fetch root spans in batches const rootSpansByTrace = new Map(); for (let i = 0; i < allTraceIds.length; i += MAX_TRACE_IDS_PER_BATCH) { const traceIdBatch = allTraceIds.slice(i, i + MAX_TRACE_IDS_PER_BATCH); const spans = await getAllRootSpansForBatch({ client, projectId, traceIdBatch, }); for (const span of spans) { const traceId = span.context.trace_id; if (!rootSpansByTrace.has(traceId)) { rootSpansByTrace.set(traceId, span); } } } return buildSessionTurns({ allTraceIds, traceInfo, rootSpansByTrace }); } /** * Fetch all root spans for a batch of trace IDs, handling pagination. */ async function getAllRootSpansForBatch({ client, projectId, traceIdBatch, }) { const allSpans = []; let cursor = null; do { const result = await getSpans({ client, project: { projectId }, traceIds: traceIdBatch, parentId: null, limit: traceIdBatch.length, ...(cursor ? { cursor } : {}), }); allSpans.push(...result.spans); cursor = result.nextCursor; } while (cursor != null); return allSpans; } /** * Extract a SessionTurnIO from span attributes for a given prefix. */ function extractIO({ attrs, valueKey, mimeTypeKey, }) { const value = attrs[valueKey]; if (value == null) return undefined; const io = { value: String(value) }; const mimeType = attrs[mimeTypeKey]; if (mimeType != null) { io.mimeType = String(mimeType); } return io; } /** * Build session turns from trace info and root spans, ordered by start_time. */ function buildSessionTurns({ allTraceIds, traceInfo, rootSpansByTrace, }) { const turns = []; for (const traceId of allTraceIds) { const info = traceInfo.get(traceId); if (!info) continue; const turn = { traceId, startTime: info.startTime, endTime: info.endTime, }; const rootSpan = rootSpansByTrace.get(traceId); if (rootSpan) { turn.rootSpan = rootSpan; const attrs = rootSpan.attributes ?? {}; turn.input = extractIO({ attrs, valueKey: SemanticConventions.INPUT_VALUE, mimeTypeKey: SemanticConventions.INPUT_MIME_TYPE, }); turn.output = extractIO({ attrs, valueKey: SemanticConventions.OUTPUT_VALUE, mimeTypeKey: SemanticConventions.OUTPUT_MIME_TYPE, }); } turns.push(turn); } turns.sort((a, b) => a.startTime.localeCompare(b.startTime)); return turns; } //# sourceMappingURL=getSessionTurns.js.map