Skip to content

Commit 77892d5

Browse files
author
Andrei Bratu
committed
more flow decorator tests
1 parent 564d019 commit 77892d5

File tree

1 file changed

+211
-0
lines changed

1 file changed

+211
-0
lines changed

tests/integration/decorators.test.ts

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,215 @@ describe("decorators", () => {
6565
throw error;
6666
}
6767
});
68+
69+
it("should create logs with proper tracing when using prompt in flow decorator", async () => {
70+
// Track resources for cleanup
71+
let flowId: string | null = null;
72+
let promptId: string | null = null;
73+
74+
try {
75+
// Create test flow and prompt paths
76+
const flowPath = `${testSetup.sdkTestDir.path}/test_flow`;
77+
const promptPath = `${testSetup.sdkTestDir.path}/test_prompt`;
78+
79+
// Create the prompt
80+
const promptResponse = await testSetup.humanloopClient.prompts.upsert({
81+
path: promptPath,
82+
provider: "openai",
83+
model: "gpt-4o-mini",
84+
temperature: 0,
85+
});
86+
promptId = promptResponse.id;
87+
88+
// Define the flow callable function with the correct type signature
89+
const flowCallable = async (question: {
90+
question: string;
91+
}): Promise<string> => {
92+
const response = await testSetup.humanloopClient.prompts.call({
93+
path: promptPath,
94+
messages: [{ role: "user", content: question.question }],
95+
providerApiKeys: { openai: testSetup.openaiApiKey },
96+
});
97+
98+
const output = response.logs?.[0]?.output;
99+
expect(output).not.toBeNull();
100+
return output || "";
101+
};
102+
103+
// Apply the flow decorator
104+
const myFlow = testSetup.humanloopClient.flow({
105+
path: flowPath,
106+
callable: flowCallable,
107+
});
108+
109+
// Call the flow with the expected input format
110+
const result = await myFlow({
111+
question: "What is the capital of the France?",
112+
});
113+
expect(result?.toLowerCase()).toContain("paris");
114+
115+
// Wait for logs to be created
116+
await new Promise((resolve) => setTimeout(resolve, 5000));
117+
118+
// Verify prompt logs
119+
const promptRetrieveResponse =
120+
await testSetup.humanloopClient.files.retrieveByPath({
121+
path: promptPath,
122+
});
123+
expect(promptRetrieveResponse).not.toBeNull();
124+
const promptLogsResponse = await testSetup.humanloopClient.logs.list({
125+
fileId: promptRetrieveResponse.id,
126+
page: 1,
127+
size: 50,
128+
});
129+
expect(promptLogsResponse.data.length).toBe(1);
130+
const promptLog = promptLogsResponse.data[0];
131+
132+
// Verify flow logs
133+
const flowRetrieveResponse =
134+
await testSetup.humanloopClient.files.retrieveByPath({
135+
path: flowPath,
136+
});
137+
expect(flowRetrieveResponse).not.toBeNull();
138+
flowId = flowRetrieveResponse.id;
139+
const flowLogsResponse = await testSetup.humanloopClient.logs.list({
140+
fileId: flowRetrieveResponse.id,
141+
page: 1,
142+
size: 50,
143+
});
144+
expect(flowLogsResponse.data.length).toBe(1);
145+
const flowLog = flowLogsResponse.data[0];
146+
147+
// Verify tracing between logs
148+
expect(promptLog.traceParentId).toBe(flowLog.id);
149+
} finally {
150+
// Clean up resources
151+
if (flowId) {
152+
await testSetup.humanloopClient.flows.delete(flowId);
153+
}
154+
if (promptId) {
155+
await testSetup.humanloopClient.prompts.delete(promptId);
156+
}
157+
}
158+
});
159+
160+
it("should log exceptions when using the flow decorator", async () => {
161+
// Track resources for cleanup
162+
let flowId: string | null = null;
163+
164+
try {
165+
// Create test flow path
166+
const flowPath = `${testSetup.sdkTestDir.path}/test_flow_log_error`;
167+
168+
// Define a flow callable that throws an error
169+
const flowCallable = async ({
170+
question,
171+
}: {
172+
question: string;
173+
}): Promise<string> => {
174+
throw new Error("This is a test exception");
175+
};
176+
177+
// Apply the flow decorator
178+
const myFlow = testSetup.humanloopClient.flow({
179+
path: flowPath,
180+
callable: flowCallable,
181+
});
182+
183+
// Call the flow and expect it to throw
184+
try {
185+
await myFlow({ question: "test" });
186+
// If we get here, the test should fail
187+
fail("Expected flow to throw an error but it didn't");
188+
} catch (error) {
189+
// Expected error
190+
expect(error).toBeDefined();
191+
}
192+
193+
// Wait for logs to be created
194+
await new Promise((resolve) => setTimeout(resolve, 5000));
195+
196+
// Verify flow logs
197+
const flowRetrieveResponse =
198+
await testSetup.humanloopClient.files.retrieveByPath({
199+
path: flowPath,
200+
});
201+
expect(flowRetrieveResponse).not.toBeNull();
202+
flowId = flowRetrieveResponse.id;
203+
204+
const flowLogsResponse = await testSetup.humanloopClient.logs.list({
205+
fileId: flowRetrieveResponse.id,
206+
page: 1,
207+
size: 50,
208+
});
209+
expect(flowLogsResponse.data.length).toBe(1);
210+
211+
const flowLog = flowLogsResponse.data[0];
212+
expect(flowLog.error).not.toBeUndefined();
213+
expect(flowLog.output).toBeUndefined();
214+
} finally {
215+
// Clean up resources
216+
if (flowId) {
217+
await testSetup.humanloopClient.flows.delete(flowId);
218+
}
219+
}
220+
});
221+
222+
it("should populate outputMessage when flow returns chat message format", async () => {
223+
// Track resources for cleanup
224+
let flowId: string | null = null;
225+
226+
try {
227+
// Create test flow path
228+
const flowPath = `${testSetup.sdkTestDir.path}/test_flow_log_output_message`;
229+
230+
// Define a flow callable that returns a chat message format
231+
const flowCallable = async ({ question }: { question: string }) => {
232+
return {
233+
role: "user",
234+
content: question,
235+
};
236+
};
237+
238+
// Apply the flow decorator
239+
const myFlow = testSetup.humanloopClient.flow({
240+
path: flowPath,
241+
callable: flowCallable,
242+
});
243+
244+
// Call the flow and check the returned message
245+
const result = await myFlow({
246+
question: "What is the capital of the France?",
247+
});
248+
expect(result?.content.toLowerCase()).toContain("france");
249+
250+
// Wait for logs to be created
251+
await new Promise((resolve) => setTimeout(resolve, 5000));
252+
253+
// Verify flow logs
254+
const flowRetrieveResponse =
255+
await testSetup.humanloopClient.files.retrieveByPath({
256+
path: flowPath,
257+
});
258+
expect(flowRetrieveResponse).not.toBeNull();
259+
flowId = flowRetrieveResponse.id;
260+
261+
const flowLogsResponse = await testSetup.humanloopClient.logs.list({
262+
fileId: flowRetrieveResponse.id,
263+
page: 1,
264+
size: 50,
265+
});
266+
expect(flowLogsResponse.data.length).toBe(1);
267+
268+
const flowLog = flowLogsResponse.data[0];
269+
expect(flowLog.outputMessage).not.toBeUndefined();
270+
expect(flowLog.output).toBeUndefined();
271+
expect(flowLog.error).toBeUndefined();
272+
} finally {
273+
// Clean up resources
274+
if (flowId) {
275+
await testSetup.humanloopClient.flows.delete(flowId);
276+
}
277+
}
278+
});
68279
});

0 commit comments

Comments
 (0)