|
1 | | -import hash from "stable-hash"; |
| 1 | +import * as contextApi from "@opentelemetry/api"; |
| 2 | +import { |
| 3 | + HUMANLOOP_CONTEXT_EVALUATION, |
| 4 | + HUMANLOOP_CONTEXT_PROMPT, |
| 5 | + HUMANLOOP_CONTEXT_TRACE_ID, |
| 6 | +} from "otel/constants"; |
| 7 | + |
| 8 | +export function getTraceId(): string | undefined { |
| 9 | + const key = contextApi.createContextKey(HUMANLOOP_CONTEXT_TRACE_ID); |
| 10 | + const value = contextApi.context.active().getValue(key); |
| 11 | + return (value || undefined) as string | undefined; |
| 12 | +} |
2 | 13 |
|
3 | | -import { FlowLogRequest, PromptLogRequest } from "../api"; |
4 | | -import { DatapointResponse } from "../api"; |
5 | | -import { Humanloop } from "../index"; |
6 | | -import { Version } from "./types"; |
| 14 | +export function setTraceId(flowLogId: string): contextApi.Context { |
| 15 | + const key = contextApi.createContextKey(HUMANLOOP_CONTEXT_TRACE_ID); |
| 16 | + return contextApi.context.active().setValue(key, flowLogId); |
| 17 | +} |
7 | 18 |
|
8 | | -type EvaluationContextState = { |
9 | | - fileId?: string; |
10 | | - path?: string; |
11 | | - uploadCallback: (logId: string, datapoint: DatapointResponse) => void; |
12 | | - evaluatedVersion?: Version; |
| 19 | +export type PromptContext = { |
| 20 | + path: string; |
| 21 | + template?: string; |
13 | 22 | }; |
14 | 23 |
|
15 | | -type EvaluationContextKey = { |
16 | | - inputs: Record<string, unknown> | undefined; |
17 | | - messages: Humanloop.ChatMessage[] | undefined; |
18 | | -}; |
| 24 | +export function setPromptContext(promptContext: PromptContext): contextApi.Context { |
| 25 | + const key = contextApi.createContextKey(HUMANLOOP_CONTEXT_PROMPT); |
| 26 | + return contextApi.context.active().setValue(key, promptContext); |
| 27 | +} |
19 | 28 |
|
20 | | -type EvaluationContextValue = { |
21 | | - runId: string; |
| 29 | +export function getPromptContext(): PromptContext | undefined { |
| 30 | + const key = contextApi.createContextKey(HUMANLOOP_CONTEXT_PROMPT); |
| 31 | + return (contextApi.context.active().getValue(key) || undefined) as |
| 32 | + | PromptContext |
| 33 | + | undefined; |
| 34 | +} |
| 35 | + |
| 36 | +export type EvaluationContext = { |
22 | 37 | sourceDatapointId: string; |
23 | | - uploadCallback: (logId: string) => void; |
| 38 | + runId: string; |
| 39 | + callback: (log_id: string) => void; |
| 40 | + fileId: string; |
| 41 | + path: string; |
24 | 42 | }; |
25 | 43 |
|
26 | | -class EvaluationContext { |
27 | | - private state?: EvaluationContextState; |
28 | | - private static instance: EvaluationContext; |
29 | | - private inputMappings: Map<string, EvaluationContextValue[]> = new Map(); |
30 | | - |
31 | | - private constructor() {} |
32 | | - |
33 | | - public static getInstance(): EvaluationContext { |
34 | | - if (!EvaluationContext.instance) { |
35 | | - EvaluationContext.instance = new EvaluationContext(); |
36 | | - } |
37 | | - return EvaluationContext.instance; |
38 | | - } |
39 | | - |
40 | | - public setState(state: EvaluationContextState): void { |
41 | | - this.state = state; |
42 | | - } |
43 | | - |
44 | | - public getState(): Omit<EvaluationContextState, "uploadCallback"> | undefined { |
45 | | - return this.state === undefined |
46 | | - ? this.state |
47 | | - : { |
48 | | - fileId: this.state.fileId, |
49 | | - path: this.state.path, |
50 | | - evaluatedVersion: this.state.evaluatedVersion, |
51 | | - }; |
52 | | - } |
53 | | - |
54 | | - public addDatapoint(datapoint: DatapointResponse, runId: string): void { |
55 | | - if (this.state === undefined) { |
56 | | - throw new Error("EvaluationContext state is not set"); |
57 | | - } |
58 | | - const key = hash({ inputs: datapoint.inputs, messages: datapoint.messages }); |
59 | | - |
60 | | - if (!this.inputMappings.has(key)) { |
61 | | - this.inputMappings.set(key, []); |
62 | | - } |
63 | | - this.inputMappings.get(key)!.push({ |
64 | | - runId, |
65 | | - sourceDatapointId: datapoint.id, |
66 | | - uploadCallback: (logId: string) => |
67 | | - this.state!.uploadCallback(logId, datapoint), |
68 | | - }); |
69 | | - } |
70 | | - |
71 | | - public getDatapoint(key: EvaluationContextKey): EvaluationContextValue { |
72 | | - if (key.inputs !== undefined && "inputs" in key.inputs) { |
73 | | - key = { ...key, inputs: key.inputs.inputs as Record<string, unknown> }; |
74 | | - } |
75 | | - const mappings = this.inputMappings.get(hash(key)); |
76 | | - if (!mappings || mappings.length === 0) { |
77 | | - throw new Error( |
78 | | - `No input mappings found for: ${JSON.stringify(key)}. Try using peekDatapoint() first.`, |
79 | | - ); |
80 | | - } |
81 | | - return mappings.pop()!; |
82 | | - } |
83 | | - |
84 | | - public peekDatapoint(key: EvaluationContextKey): boolean { |
85 | | - const mappings = this.inputMappings.get(hash(key)); |
86 | | - return mappings !== undefined && mappings.length > 0; |
87 | | - } |
88 | | - |
89 | | - public isEvaluatedFile(args: FlowLogRequest | PromptLogRequest) { |
90 | | - return ( |
91 | | - this.state && |
92 | | - (this.state.fileId === args.id || this.state.path === args.path) |
93 | | - ); |
94 | | - } |
| 44 | +export function setEvaluationContext( |
| 45 | + evaluationContext: EvaluationContext, |
| 46 | +): contextApi.Context { |
| 47 | + const key = contextApi.createContextKey(HUMANLOOP_CONTEXT_EVALUATION); |
| 48 | + return contextApi.context.active().setValue(key, evaluationContext); |
95 | 49 | } |
96 | 50 |
|
97 | | -export const evaluationContext = EvaluationContext.getInstance(); |
| 51 | +export function getEvaluationContext(): EvaluationContext | undefined { |
| 52 | + const key = contextApi.createContextKey(HUMANLOOP_CONTEXT_EVALUATION); |
| 53 | + return (contextApi.context.active().getValue(key) || undefined) as |
| 54 | + | EvaluationContext |
| 55 | + | undefined; |
| 56 | +} |
0 commit comments