Skip to content

Commit 410d410

Browse files
committed
tests passing
1 parent 337d05e commit 410d410

File tree

2 files changed

+46
-94
lines changed

2 files changed

+46
-94
lines changed

src/helpers/beta/zod.ts

Lines changed: 1 addition & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,7 @@
1-
import { transformJSONSchema } from '../..//lib/transform-json-schema';
21
import type { infer as zodInfer, ZodType } from 'zod';
32
import * as z from 'zod';
4-
import { OpenAIError } from '../../core/error';
53
import type { BetaRunnableTool, Promisable } from '../../lib/beta/BetaRunnableTool';
6-
import type { AutoParseableBetaOutputFormat } from '../../lib/beta-parser';
7-
import { FunctionTool } from '../../resources/beta';
8-
// import { AutoParseableBetaOutputFormat } from '../../lib/beta-parser';
9-
// import { BetaRunnableTool, Promisable } from '../../lib/tools/BetaRunnableTool';
10-
// import { BetaToolResultContentBlockParam } from '../../resources/beta';
11-
/**
12-
* Creates a JSON schema output format object from the given Zod schema.
13-
*
14-
* If this is passed to the `.parse()` method then the response message will contain a
15-
* `.parsed` property that is the result of parsing the content with the given Zod object.
16-
*
17-
* This can be passed directly to the `.create()` method but will not
18-
* result in any automatic parsing, you'll have to parse the response yourself.
19-
*/
20-
export function betaZodOutputFormat<ZodInput extends ZodType>(
21-
zodObject: ZodInput,
22-
): AutoParseableBetaOutputFormat<zodInfer<ZodInput>> {
23-
let jsonSchema = z.toJSONSchema(zodObject, { reused: 'ref' });
24-
25-
jsonSchema = transformJSONSchema(jsonSchema);
26-
27-
return {
28-
type: 'json_schema',
29-
schema: {
30-
...jsonSchema,
31-
},
32-
parse: (content) => {
33-
const output = zodObject.safeParse(JSON.parse(content));
34-
35-
if (!output.success) {
36-
throw new OpenAIError(
37-
`Failed to parse structured output: ${output.error.message} cause: ${output.error.issues}`,
38-
);
39-
}
40-
41-
return output.data;
42-
},
43-
};
44-
}
4+
import type { FunctionTool } from '../../resources/beta';
455

466
/**
477
* Creates a tool using the provided Zod schema that can be passed
@@ -78,34 +38,3 @@ export function betaZodTool<InputSchema extends ZodType>(options: {
7838
parse: (args: unknown) => options.inputSchema.parse(args) as zodInfer<InputSchema>,
7939
};
8040
}
81-
82-
// /**
83-
// * Creates a tool using the provided Zod schema that can be passed
84-
// * into the `.toolRunner()` method. The Zod schema will automatically be
85-
// * converted into JSON Schema when passed to the API. The provided function's
86-
// * input arguments will also be validated against the provided schema.
87-
// */
88-
// export function betaZodTool<InputSchema extends ZodType>(options: {
89-
// name: string;
90-
// inputSchema: InputSchema;
91-
// description: string;
92-
// run: (args: zodInfer<InputSchema>) => Promisable<string | Array<BetaToolResultContentBlockParam>>;
93-
// }): BetaRunnableTool<zodInfer<InputSchema>> {
94-
// const jsonSchema = z.toJSONSchema(options.inputSchema, { reused: 'ref' });
95-
96-
// if (jsonSchema.type !== 'object') {
97-
// throw new Error(`Zod schema for tool "${options.name}" must be an object, but got ${jsonSchema.type}`);
98-
// }
99-
100-
// // TypeScript doesn't narrow the type after the runtime check, so we need to assert it
101-
// const objectSchema = jsonSchema as typeof jsonSchema & { type: 'object' };
102-
103-
// return {
104-
// type: 'custom',
105-
// name: options.name,
106-
// input_schema: objectSchema,
107-
// description: options.description,
108-
// run: options.run,
109-
// parse: (args: unknown) => options.inputSchema.parse(args) as zodInfer<InputSchema>,
110-
// };
111-
// }

tests/utils/mock-fetch.ts

Lines changed: 45 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,70 @@
1-
import { Fetch, RequestInfo, RequestInit } from 'openai/internal/builtin-types';
1+
import { type Fetch, type RequestInfo, type RequestInit, type Response } from 'openai/internal/builtin-types';
22
import { PassThrough } from 'stream';
33

4+
/**
5+
* Creates a mock `fetch` function and a `handleRequest` function for intercepting `fetch` calls.
6+
*
7+
* You call `handleRequest` with a callback function that handles the next `fetch` call.
8+
* It returns a Promise that:
9+
* - waits for the next call to `fetch`
10+
* - calls the callback with the `fetch` arguments
11+
* - resolves `fetch` with the callback output
12+
*/
413
export function mockFetch(): {
514
fetch: Fetch;
615
handleRequest: (handle: Fetch) => void;
716
handleStreamEvents: (events: any[]) => void;
817
handleMessageStreamEvents: (iter: AsyncIterable<any>) => void;
918
} {
10-
const queue: Promise<typeof fetch>[] = [];
11-
const readResolvers: ((handler: typeof fetch) => void)[] = [];
19+
const fetchQueue: ((handler: typeof fetch) => void)[] = [];
20+
const handlerQueue: Promise<typeof fetch>[] = [];
1221

13-
let index = 0;
22+
const enqueueHandler = () => {
23+
handlerQueue.push(
24+
new Promise<typeof fetch>((resolve) => {
25+
fetchQueue.push((handle: typeof fetch) => {
26+
enqueueHandler();
27+
resolve(handle);
28+
});
29+
}),
30+
);
31+
};
32+
enqueueHandler();
1433

1534
async function fetch(req: string | RequestInfo, init?: RequestInit): Promise<Response> {
16-
const idx = index++;
17-
if (!queue[idx]) {
18-
queue.push(new Promise((resolve) => readResolvers.push(resolve)));
19-
}
20-
21-
const handler = await queue[idx]!;
35+
const handler = await handlerQueue.shift();
36+
if (!handler) throw new Error('expected handler to be defined');
37+
const signal = init?.signal;
38+
if (!signal) return await handler(req, init);
2239
return await Promise.race([
2340
handler(req, init),
24-
new Promise<Response>((_resolve, reject) => {
25-
if (init?.signal?.aborted) {
26-
// @ts-ignore
41+
new Promise<Response>((resolve, reject) => {
42+
if (signal.aborted) {
43+
// @ts-ignore does exist in Node
2744
reject(new DOMException('The user aborted a request.', 'AbortError'));
2845
return;
2946
}
30-
init?.signal?.addEventListener('abort', (_e) => {
31-
// @ts-ignore
47+
signal.addEventListener('abort', (e) => {
48+
// @ts-ignore does exist in Node
3249
reject(new DOMException('The user aborted a request.', 'AbortError'));
3350
});
3451
}),
3552
]);
3653
}
3754

38-
function handleRequest(handler: typeof fetch): void {
39-
if (readResolvers.length) {
40-
const resolver = readResolvers.shift()!;
41-
resolver(handler);
42-
return;
43-
}
44-
queue.push(Promise.resolve(handler));
55+
function handleRequest(handle: typeof fetch): Promise<void> {
56+
return new Promise<void>((resolve, reject) => {
57+
fetchQueue.shift()?.(async (req, init) => {
58+
try {
59+
return await handle(req, init);
60+
} catch (err) {
61+
reject(err);
62+
return err as any;
63+
} finally {
64+
resolve();
65+
}
66+
});
67+
});
4568
}
4669

4770
function handleStreamEvents(events: any[]) {

0 commit comments

Comments
 (0)