Skip to content

Commit 342f0cb

Browse files
author
Andrei Bratu
committed
More ergonomic typing on decorators
1 parent 6204723 commit 342f0cb

5 files changed

Lines changed: 61 additions & 81 deletions

File tree

src/decorators/flow.ts

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,32 +15,14 @@ import {
1515
export function flowUtilityFactory<I, O>(
1616
client: HumanloopClient,
1717
opentelemetryTracer: Tracer,
18-
callable: (
19-
args: I extends Record<string, unknown> & {
20-
messages?: ChatMessage[];
21-
}
22-
? I
23-
: never,
24-
) => O,
18+
callable: (args: I) => O,
2519
path: string,
2620
attributes?: Record<string, unknown>,
27-
): (args: I) => Promise<O | undefined> & {
28-
file: {
29-
type: string;
30-
version: { attributes?: Record<string, unknown> };
31-
callable: (args: I) => Promise<O | undefined>;
32-
};
33-
} {
21+
) {
3422
const flowKernel = { attributes: attributes || {} };
3523
const fileType = "flow";
3624

37-
const wrappedFunction = async (
38-
inputs: I extends Record<string, unknown> & {
39-
messages?: ChatMessage[];
40-
}
41-
? I
42-
: never,
43-
) => {
25+
const wrappedFunction = async (inputs: I) => {
4426
return HL_CONTEXT.with(
4527
setDecoratorContext({
4628
path: path,
@@ -139,7 +121,6 @@ export function flowUtilityFactory<I, O>(
139121
);
140122
};
141123

142-
// @ts-ignore
143124
return Object.assign(wrappedFunction, {
144125
file: {
145126
path: path,

src/decorators/tool.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { ReadableSpan } from "@opentelemetry/sdk-trace-node";
33

44
import { ToolKernelRequest } from "../api/types/ToolKernelRequest";
55
import { getEvaluationContext, getTraceId } from "../context";
6-
import { File as EvalRunFile } from "../evals/types";
76
import { NestedDict, jsonifyIfNotString, writeToOpenTelemetrySpan } from "../otel";
87
import {
98
HUMANLOOP_FILE_KEY,
@@ -30,7 +29,6 @@ export function toolUtilityFactory<I, O>(
3029
path: string,
3130
): (inputs: I) => O & {
3231
jsonSchema: Record<string, unknown>;
33-
file: EvalRunFile<I, O>;
3432
} {
3533
const fileType = "tool";
3634

@@ -96,11 +94,6 @@ export function toolUtilityFactory<I, O>(
9694
// @ts-ignore Adding jsonSchema property to utility-wrapped function
9795
return Object.assign(wrappedFunction, {
9896
jsonSchema: version.function || {},
99-
decorator: {
100-
type: fileType,
101-
path: path,
102-
version,
103-
},
10497
});
10598
}
10699

src/evals/run.ts

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -117,11 +117,17 @@ async function pMap<T, O>(
117117
return result;
118118
}
119119

120-
function callableIsHumanloopUtility<I, O>(file: File<I, O>): boolean {
120+
function callableIsHumanloopUtility<
121+
I extends Record<string, unknown> & { messages?: any[] },
122+
O,
123+
>(file: File<I, O>): boolean {
121124
return file.callable !== undefined && "decorator" in file.callable;
122125
}
123126

124-
function fileOrFileInsideHLUtility<I, O>(file: File<I, O>): File<I, O> {
127+
function fileOrFileInsideHLUtility<
128+
I extends Record<string, unknown> & { messages?: any[] },
129+
O,
130+
>(file: File<I, O>): File<I, O> {
125131
if (callableIsHumanloopUtility(file)) {
126132
// @ts-ignore
127133
const innerFile: File<I, O> = file.callable!.file! as File<I, O>;
@@ -171,7 +177,9 @@ function fileOrFileInsideHLUtility<I, O>(file: File<I, O>): File<I, O> {
171177
}
172178
}
173179

174-
function getFileType<I, O>(file: File<I, O>): FileType {
180+
function getFileType<I extends Record<string, unknown> & { messages?: any[] }, O>(
181+
file: File<I, O>,
182+
): FileType {
175183
// Determine the `type` of the `file` to Evaluate - if not `type` provided, default to `flow`
176184
try {
177185
const type_ = file.type as FileType;
@@ -188,7 +196,7 @@ function getFileType<I, O>(file: File<I, O>): FileType {
188196
}
189197
}
190198

191-
function getFileCallable<I, O>(
199+
function getFileCallable<I extends Record<string, unknown> & { messages?: any[] }, O>(
192200
file: File<I, O>,
193201
type_: FileType,
194202
): File<I, O>["callable"] {
@@ -208,7 +216,10 @@ function getFileCallable<I, O>(
208216
return function_;
209217
}
210218

211-
export async function runEval<I, O>(
219+
export async function runEval<
220+
I extends Record<string, unknown> & { messages?: any[] },
221+
O,
222+
>(
212223
client: HumanloopClient,
213224
file: File<I, O>,
214225
dataset: Dataset,
@@ -519,10 +530,10 @@ export async function runEval<I, O>(
519530
return checks;
520531
}
521532

522-
async function callFunction<I, O>(
523-
callable: File<I, O>["callable"],
524-
datapoint: DatapointResponse,
525-
): Promise<string> {
533+
async function callFunction<
534+
I extends Record<string, unknown> & { messages?: any[] },
535+
O,
536+
>(callable: File<I, O>["callable"], datapoint: DatapointResponse): Promise<string> {
526537
const datapointDict = { ...datapoint };
527538
let output;
528539
if (callable === undefined) {
@@ -551,7 +562,7 @@ async function callFunction<I, O>(
551562
return output;
552563
}
553564

554-
async function upsertFile<I, O>({
565+
async function upsertFile<I extends Record<string, unknown> & { messages?: any[] }, O>({
555566
file,
556567
type,
557568
client,
@@ -688,7 +699,10 @@ async function getNewRun({
688699
return { evaluation, run };
689700
}
690701

691-
async function upsertLocalEvaluators<I, O>({
702+
async function upsertLocalEvaluators<
703+
I extends Record<string, unknown> & { messages?: any[] },
704+
O,
705+
>({
692706
evaluators,
693707
callable,
694708
type,

src/evals/types.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ interface Identifiers {
4747
path?: string;
4848
}
4949

50-
export interface File<I, O> extends Identifiers {
50+
export interface File<I extends Record<string, unknown> & { messages?: any[] }, O>
51+
extends Identifiers {
5152
/** The type of File this callable relates to on Humanloop. */
5253
type?: "flow" | "prompt";
5354
/** The contents uniquely define the version of the File on Humanloop. */
@@ -59,9 +60,7 @@ export interface File<I, O> extends Identifiers {
5960
* If messages are defined in your Dataset, then
6061
* `output = callable(datapoint.inputs, messages=datapoint.messages)`.
6162
*/
62-
callable?: I extends Record<string, unknown> & { messages?: ChatMessage[] }
63-
? (args: I) => O
64-
: never;
63+
callable?: (args: I) => O;
6564
}
6665

6766
export interface Dataset extends Identifiers {

src/humanloop.client.ts

Lines changed: 30 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
import { Tracer } from "@opentelemetry/api";
2-
import {
3-
Instrumentation,
4-
registerInstrumentations,
5-
} from "@opentelemetry/instrumentation";
62
import { resourceFromAttributes } from "@opentelemetry/resources";
73
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
84
import { AnthropicInstrumentation } from "@traceloop/instrumentation-anthropic";
@@ -102,7 +98,7 @@ class ExtendedEvaluations extends BaseEvaluations {
10298
* @param evaluators - List of evaluators to be. Can be ran on Humanloop if specified only by path, or locally if a callable is provided.
10399
* @param concurrency - Number of datapoints to process in parallel.
104100
*/
105-
async run<I, O>({
101+
async run<I extends Record<string, unknown> & { messages?: any[] }, O>({
106102
file,
107103
dataset,
108104
name,
@@ -122,14 +118,7 @@ class ExtendedEvaluations extends BaseEvaluations {
122118
)[];
123119
concurrency?: number;
124120
}): Promise<EvaluatorCheck[]> {
125-
return runEval<I, O>(
126-
this._client,
127-
file,
128-
dataset,
129-
name,
130-
evaluators,
131-
concurrency,
132-
);
121+
return runEval(this._client, file, dataset, name, evaluators, concurrency);
133122
}
134123
}
135124

@@ -293,7 +282,7 @@ export class HumanloopClient extends BaseHumanloopClient {
293282
}
294283

295284
// Check if user has passed the LLM provider instrumentors
296-
private assertProviders() {
285+
private assertAtLeastOneProviderModuleSet() {
297286
const userDidNotPassProviders = Object.values(this.instrumentProviders).every(
298287
(provider) => !provider,
299288
);
@@ -406,20 +395,18 @@ ${RESET}`,
406395
* @param path - The path to the Prompt.
407396
*/
408397
public prompt<I, O>(args: {
409-
callable: I extends Record<string, unknown> & { messages?: ChatMessage[] }
410-
? (args: I) => O
411-
: () => O;
398+
callable: I extends never ? () => O : (args: I) => O;
412399
path: string;
413-
}): I extends Record<string, unknown>
414-
? (
415-
args: I,
416-
) => O extends Promise<infer R>
400+
}): I extends never
401+
? () => O extends Promise<infer R>
417402
? Promise<R | undefined>
418403
: Promise<O | undefined>
419-
: () => O extends Promise<infer R>
404+
: (
405+
args: I,
406+
) => O extends Promise<infer R>
420407
? Promise<R | undefined>
421408
: Promise<O | undefined> {
422-
this.assertProviders();
409+
this.assertAtLeastOneProviderModuleSet();
423410
// @ts-ignore
424411
return promptDecoratorFactory(args.path, args.callable);
425412
}
@@ -457,16 +444,20 @@ ${RESET}`,
457444
* @param version - The JSON Schema of the Tool's inputs and outputs, plus the optional Humanloop fields `attributes and `setupValues`. See API reference for details.
458445
*/
459446
public tool<I, O>(args: {
460-
callable: I extends Record<string, unknown> ? (args: I) => O : () => O;
447+
callable: I extends never
448+
? () => O
449+
: I extends Record<string, any>
450+
? (args: I) => O
451+
: never;
461452
path: string;
462453
version: ToolKernelRequest;
463-
}): I extends Record<string, unknown>
464-
? (
465-
args: I,
466-
) => O extends Promise<infer R>
454+
}): I extends never
455+
? () => O extends Promise<infer R>
467456
? Promise<R | undefined>
468457
: Promise<O | undefined>
469-
: () => O extends Promise<infer R>
458+
: (
459+
args: I,
460+
) => O extends Promise<infer R>
470461
? Promise<R | undefined>
471462
: Promise<O | undefined> {
472463
// @ts-ignore
@@ -543,18 +534,20 @@ ${RESET}`,
543534
path,
544535
attributes,
545536
}: {
546-
callable: I extends Record<string, unknown> & { messages?: ChatMessage[] }
547-
? ((args: I) => O) | (() => O)
548-
: never;
537+
callable: I extends never
538+
? () => O
539+
: I extends Record<string, any> & { messages?: ChatMessage[] }
540+
? (args: I) => O
541+
: never;
549542
path: string;
550543
attributes?: Record<string, unknown>;
551-
}): I extends Record<string, unknown> & { messages?: ChatMessage[] }
552-
? (
553-
args: I,
554-
) => O extends Promise<infer R>
544+
}): I extends never
545+
? () => O extends Promise<infer R>
555546
? Promise<R | undefined>
556547
: Promise<O | undefined>
557-
: () => O extends Promise<infer R>
548+
: (
549+
args: I,
550+
) => O extends Promise<infer R>
558551
? Promise<R | undefined>
559552
: Promise<O | undefined> {
560553
// @ts-ignore

0 commit comments

Comments
 (0)