feat: Passwordless cross-device authentication

- Arkitektur: docs/auth/passwordless-architecture.md
- Backend: iom/quixzoom-auth-service/ (FastAPI + Redis)
- Webb: quixzoom-market-pages/se/login/ (QR-kod + polling)
- App: iom/quixzoom-app/src/features/auth/ (push + deep links)

Flöde: QR-kod → app-godkännande → webb-inloggad
This commit is contained in:
Bernt
2026-07-07 07:11:50 +00:00
parent 4aa984ad74
commit 6989a98d75
61843 changed files with 5491611 additions and 872231 deletions
+9
View File
@@ -0,0 +1,9 @@
export declare function playAudio(input: NodeJS.ReadableStream | Response | File): Promise<void>;
type RecordAudioOptions = {
signal?: AbortSignal;
device?: number;
timeout?: number;
};
export declare function recordAudio(options?: RecordAudioOptions): Promise<File>;
export {};
//# sourceMappingURL=audio.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"audio.d.mts","sourceRoot":"","sources":["../src/helpers/audio.ts"],"names":[],"mappings":"AA0DA,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG,QAAQ,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ7F;AAED,KAAK,kBAAkB,GAAG;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAiEF,wBAAsB,WAAW,CAAC,OAAO,GAAE,kBAAuB,iBAQjE"}
+9
View File
@@ -0,0 +1,9 @@
export declare function playAudio(input: NodeJS.ReadableStream | Response | File): Promise<void>;
type RecordAudioOptions = {
signal?: AbortSignal;
device?: number;
timeout?: number;
};
export declare function recordAudio(options?: RecordAudioOptions): Promise<File>;
export {};
//# sourceMappingURL=audio.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../src/helpers/audio.ts"],"names":[],"mappings":"AA0DA,wBAAsB,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,cAAc,GAAG,QAAQ,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ7F;AAED,KAAK,kBAAkB,GAAG;IACxB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAiEF,wBAAsB,WAAW,CAAC,OAAO,GAAE,kBAAuB,iBAQjE"}
+122
View File
@@ -0,0 +1,122 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.playAudio = playAudio;
exports.recordAudio = recordAudio;
const node_child_process_1 = require("node:child_process");
const node_stream_1 = require("node:stream");
const node_process_1 = require("node:process");
const uploads_1 = require("../internal/uploads.js");
const DEFAULT_SAMPLE_RATE = 24000;
const DEFAULT_CHANNELS = 1;
const isNode = Boolean(node_process_1.versions?.node);
const recordingProviders = {
win32: 'dshow',
darwin: 'avfoundation',
linux: 'alsa',
aix: 'alsa',
android: 'alsa',
freebsd: 'alsa',
haiku: 'alsa',
sunos: 'alsa',
netbsd: 'alsa',
openbsd: 'alsa',
cygwin: 'dshow',
};
function isResponse(stream) {
return typeof stream.body !== 'undefined';
}
function isFile(stream) {
(0, uploads_1.checkFileSupport)();
return stream instanceof File;
}
async function nodejsPlayAudio(stream) {
return new Promise((resolve, reject) => {
try {
const ffplay = (0, node_child_process_1.spawn)('ffplay', ['-autoexit', '-nodisp', '-i', 'pipe:0']);
if (isResponse(stream)) {
stream.body.pipe(ffplay.stdin);
}
else if (isFile(stream)) {
node_stream_1.Readable.from(stream.stream()).pipe(ffplay.stdin);
}
else {
stream.pipe(ffplay.stdin);
}
ffplay.on('close', (code) => {
if (code !== 0) {
reject(new Error(`ffplay process exited with code ${code}`));
}
resolve();
});
}
catch (error) {
reject(error);
}
});
}
async function playAudio(input) {
if (isNode) {
return nodejsPlayAudio(input);
}
throw new Error('Play audio is not supported in the browser yet. Check out https://npm.im/wavtools as an alternative.');
}
function nodejsRecordAudio({ signal, device, timeout } = {}) {
(0, uploads_1.checkFileSupport)();
return new Promise((resolve, reject) => {
const data = [];
const provider = recordingProviders[node_process_1.platform];
try {
const ffmpeg = (0, node_child_process_1.spawn)('ffmpeg', [
'-f',
provider,
'-i',
`:${device ?? 0}`, // default audio input device; adjust as needed
'-ar',
DEFAULT_SAMPLE_RATE.toString(),
'-ac',
DEFAULT_CHANNELS.toString(),
'-f',
'wav',
'pipe:1',
], {
stdio: ['ignore', 'pipe', 'pipe'],
});
ffmpeg.stdout.on('data', (chunk) => {
data.push(chunk);
});
ffmpeg.on('error', (error) => {
console.error(error);
reject(error);
});
ffmpeg.on('close', (code) => {
returnData();
});
function returnData() {
const audioBuffer = Buffer.concat(data);
const audioFile = new File([audioBuffer], 'audio.wav', { type: 'audio/wav' });
resolve(audioFile);
}
if (typeof timeout === 'number' && timeout > 0) {
const internalSignal = AbortSignal.timeout(timeout);
internalSignal.addEventListener('abort', () => {
ffmpeg.kill('SIGTERM');
});
}
if (signal) {
signal.addEventListener('abort', () => {
ffmpeg.kill('SIGTERM');
});
}
}
catch (error) {
reject(error);
}
});
}
async function recordAudio(options = {}) {
if (isNode) {
return nodejsRecordAudio(options);
}
throw new Error('Record audio is not supported in the browser. Check out https://npm.im/wavtools as an alternative.');
}
//# sourceMappingURL=audio.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/helpers/audio.ts"],"names":[],"mappings":"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"}
+118
View File
@@ -0,0 +1,118 @@
import { spawn } from 'node:child_process';
import { Readable } from 'node:stream';
import { platform, versions } from 'node:process';
import { checkFileSupport } from "../internal/uploads.mjs";
const DEFAULT_SAMPLE_RATE = 24000;
const DEFAULT_CHANNELS = 1;
const isNode = Boolean(versions?.node);
const recordingProviders = {
win32: 'dshow',
darwin: 'avfoundation',
linux: 'alsa',
aix: 'alsa',
android: 'alsa',
freebsd: 'alsa',
haiku: 'alsa',
sunos: 'alsa',
netbsd: 'alsa',
openbsd: 'alsa',
cygwin: 'dshow',
};
function isResponse(stream) {
return typeof stream.body !== 'undefined';
}
function isFile(stream) {
checkFileSupport();
return stream instanceof File;
}
async function nodejsPlayAudio(stream) {
return new Promise((resolve, reject) => {
try {
const ffplay = spawn('ffplay', ['-autoexit', '-nodisp', '-i', 'pipe:0']);
if (isResponse(stream)) {
stream.body.pipe(ffplay.stdin);
}
else if (isFile(stream)) {
Readable.from(stream.stream()).pipe(ffplay.stdin);
}
else {
stream.pipe(ffplay.stdin);
}
ffplay.on('close', (code) => {
if (code !== 0) {
reject(new Error(`ffplay process exited with code ${code}`));
}
resolve();
});
}
catch (error) {
reject(error);
}
});
}
export async function playAudio(input) {
if (isNode) {
return nodejsPlayAudio(input);
}
throw new Error('Play audio is not supported in the browser yet. Check out https://npm.im/wavtools as an alternative.');
}
function nodejsRecordAudio({ signal, device, timeout } = {}) {
checkFileSupport();
return new Promise((resolve, reject) => {
const data = [];
const provider = recordingProviders[platform];
try {
const ffmpeg = spawn('ffmpeg', [
'-f',
provider,
'-i',
`:${device ?? 0}`, // default audio input device; adjust as needed
'-ar',
DEFAULT_SAMPLE_RATE.toString(),
'-ac',
DEFAULT_CHANNELS.toString(),
'-f',
'wav',
'pipe:1',
], {
stdio: ['ignore', 'pipe', 'pipe'],
});
ffmpeg.stdout.on('data', (chunk) => {
data.push(chunk);
});
ffmpeg.on('error', (error) => {
console.error(error);
reject(error);
});
ffmpeg.on('close', (code) => {
returnData();
});
function returnData() {
const audioBuffer = Buffer.concat(data);
const audioFile = new File([audioBuffer], 'audio.wav', { type: 'audio/wav' });
resolve(audioFile);
}
if (typeof timeout === 'number' && timeout > 0) {
const internalSignal = AbortSignal.timeout(timeout);
internalSignal.addEventListener('abort', () => {
ffmpeg.kill('SIGTERM');
});
}
if (signal) {
signal.addEventListener('abort', () => {
ffmpeg.kill('SIGTERM');
});
}
}
catch (error) {
reject(error);
}
});
}
export async function recordAudio(options = {}) {
if (isNode) {
return nodejsRecordAudio(options);
}
throw new Error('Record audio is not supported in the browser. Check out https://npm.im/wavtools as an alternative.');
}
//# sourceMappingURL=audio.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"audio.mjs","sourceRoot":"","sources":["../src/helpers/audio.ts"],"names":[],"mappings":"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"}
+73
View File
@@ -0,0 +1,73 @@
import { ResponseFormatJSONSchema } from "../resources/index.mjs";
import * as z3 from 'zod/v3';
import * as z4 from 'zod/v4';
import { AutoParseableResponseFormat, AutoParseableTextFormat, AutoParseableTool } from "../lib/parser.mjs";
import { AutoParseableResponseTool } from "../lib/ResponsesParser.mjs";
import { type ResponseFormatTextJSONSchemaConfig } from "../resources/responses/responses.mjs";
type InferZodType<T> = T extends z4.ZodType ? z4.infer<T> : T extends z3.ZodType ? z3.infer<T> : never;
/**
* Creates a chat completion `JSONSchema` response format object from
* the given Zod schema.
*
* If this is passed to the `.parse()`, `.stream()` or `.runTools()`
* chat completion methods then the response message will contain a
* `.parsed` property that is the result of parsing the content with
* the given Zod object.
*
* ```ts
* const completion = await client.chat.completions.parse({
* model: 'gpt-4o-2024-08-06',
* messages: [
* { role: 'system', content: 'You are a helpful math tutor.' },
* { role: 'user', content: 'solve 8x + 31 = 2' },
* ],
* response_format: zodResponseFormat(
* z.object({
* steps: z.array(z.object({
* explanation: z.string(),
* answer: z.string(),
* })),
* final_answer: z.string(),
* }),
* 'math_answer',
* ),
* });
* const message = completion.choices[0]?.message;
* if (message?.parsed) {
* console.log(message.parsed);
* console.log(message.parsed.final_answer);
* }
* ```
*
* This can be passed directly to the `.create()` method but will not
* result in any automatic parsing, you'll have to parse the response yourself.
*/
export declare function zodResponseFormat<ZodInput extends z3.ZodType | z4.ZodType>(zodObject: ZodInput, name: string, props?: Omit<ResponseFormatJSONSchema.JSONSchema, 'schema' | 'strict' | 'name'>): AutoParseableResponseFormat<InferZodType<ZodInput>>;
export declare function zodTextFormat<ZodInput extends z3.ZodType | z4.ZodType>(zodObject: ZodInput, name: string, props?: Omit<ResponseFormatTextJSONSchemaConfig, 'schema' | 'type' | 'strict' | 'name'>): AutoParseableTextFormat<InferZodType<ZodInput>>;
/**
* Creates a chat completion `function` tool that can be invoked
* automatically by the chat completion `.runTools()` method or automatically
* parsed by `.parse()` / `.stream()`.
*/
export declare function zodFunction<Parameters extends z3.ZodType | z4.ZodType>(options: {
name: string;
parameters: Parameters;
function?: ((args: InferZodType<Parameters>) => unknown | Promise<unknown>) | undefined;
description?: string | undefined;
}): AutoParseableTool<{
arguments: Parameters;
name: string;
function: (args: InferZodType<Parameters>) => unknown;
}>;
export declare function zodResponsesFunction<Parameters extends z3.ZodType | z4.ZodType>(options: {
name: string;
parameters: Parameters;
function?: ((args: InferZodType<Parameters>) => unknown | Promise<unknown>) | undefined;
description?: string | undefined;
}): AutoParseableResponseTool<{
arguments: Parameters;
name: string;
function: (args: InferZodType<Parameters>) => unknown;
}>;
export {};
//# sourceMappingURL=zod.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"zod.d.mts","sourceRoot":"","sources":["../src/helpers/zod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,+BAA2B;AAC9D,OAAO,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC7B,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,iBAAiB,EAIlB,0BAAsB;AAEvB,OAAO,EAAE,yBAAyB,EAA6B,mCAA+B;AAC9F,OAAO,EAAE,KAAK,kCAAkC,EAAE,6CAAyC;AAI3F,KAAK,YAAY,CAAC,CAAC,IACjB,CAAC,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,GAChC,CAAC,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,GAClC,KAAK,CAAC;AAwCV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,EACxE,SAAS,EAAE,QAAQ,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,IAAI,CAAC,wBAAwB,CAAC,UAAU,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC,GAC9E,2BAA2B,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAarD;AAED,wBAAgB,aAAa,CAAC,QAAQ,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,EACpE,SAAS,EAAE,QAAQ,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,IAAI,CAAC,kCAAkC,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC,GACtF,uBAAuB,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAWjD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,UAAU,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACxF,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,GAAG,iBAAiB,CAAC;IACpB,SAAS,EAAE,UAAU,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;CACvD,CAAC,CAoBD;AAED,wBAAgB,oBAAoB,CAAC,UAAU,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,UAAU,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACxF,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,GAAG,yBAAyB,CAAC;IAC5B,SAAS,EAAE,UAAU,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;CACvD,CAAC,CAiBD"}
+73
View File
@@ -0,0 +1,73 @@
import { ResponseFormatJSONSchema } from "../resources/index.js";
import * as z3 from 'zod/v3';
import * as z4 from 'zod/v4';
import { AutoParseableResponseFormat, AutoParseableTextFormat, AutoParseableTool } from "../lib/parser.js";
import { AutoParseableResponseTool } from "../lib/ResponsesParser.js";
import { type ResponseFormatTextJSONSchemaConfig } from "../resources/responses/responses.js";
type InferZodType<T> = T extends z4.ZodType ? z4.infer<T> : T extends z3.ZodType ? z3.infer<T> : never;
/**
* Creates a chat completion `JSONSchema` response format object from
* the given Zod schema.
*
* If this is passed to the `.parse()`, `.stream()` or `.runTools()`
* chat completion methods then the response message will contain a
* `.parsed` property that is the result of parsing the content with
* the given Zod object.
*
* ```ts
* const completion = await client.chat.completions.parse({
* model: 'gpt-4o-2024-08-06',
* messages: [
* { role: 'system', content: 'You are a helpful math tutor.' },
* { role: 'user', content: 'solve 8x + 31 = 2' },
* ],
* response_format: zodResponseFormat(
* z.object({
* steps: z.array(z.object({
* explanation: z.string(),
* answer: z.string(),
* })),
* final_answer: z.string(),
* }),
* 'math_answer',
* ),
* });
* const message = completion.choices[0]?.message;
* if (message?.parsed) {
* console.log(message.parsed);
* console.log(message.parsed.final_answer);
* }
* ```
*
* This can be passed directly to the `.create()` method but will not
* result in any automatic parsing, you'll have to parse the response yourself.
*/
export declare function zodResponseFormat<ZodInput extends z3.ZodType | z4.ZodType>(zodObject: ZodInput, name: string, props?: Omit<ResponseFormatJSONSchema.JSONSchema, 'schema' | 'strict' | 'name'>): AutoParseableResponseFormat<InferZodType<ZodInput>>;
export declare function zodTextFormat<ZodInput extends z3.ZodType | z4.ZodType>(zodObject: ZodInput, name: string, props?: Omit<ResponseFormatTextJSONSchemaConfig, 'schema' | 'type' | 'strict' | 'name'>): AutoParseableTextFormat<InferZodType<ZodInput>>;
/**
* Creates a chat completion `function` tool that can be invoked
* automatically by the chat completion `.runTools()` method or automatically
* parsed by `.parse()` / `.stream()`.
*/
export declare function zodFunction<Parameters extends z3.ZodType | z4.ZodType>(options: {
name: string;
parameters: Parameters;
function?: ((args: InferZodType<Parameters>) => unknown | Promise<unknown>) | undefined;
description?: string | undefined;
}): AutoParseableTool<{
arguments: Parameters;
name: string;
function: (args: InferZodType<Parameters>) => unknown;
}>;
export declare function zodResponsesFunction<Parameters extends z3.ZodType | z4.ZodType>(options: {
name: string;
parameters: Parameters;
function?: ((args: InferZodType<Parameters>) => unknown | Promise<unknown>) | undefined;
description?: string | undefined;
}): AutoParseableResponseTool<{
arguments: Parameters;
name: string;
function: (args: InferZodType<Parameters>) => unknown;
}>;
export {};
//# sourceMappingURL=zod.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"zod.d.ts","sourceRoot":"","sources":["../src/helpers/zod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,8BAA2B;AAC9D,OAAO,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC7B,OAAO,EACL,2BAA2B,EAC3B,uBAAuB,EACvB,iBAAiB,EAIlB,yBAAsB;AAEvB,OAAO,EAAE,yBAAyB,EAA6B,kCAA+B;AAC9F,OAAO,EAAE,KAAK,kCAAkC,EAAE,4CAAyC;AAI3F,KAAK,YAAY,CAAC,CAAC,IACjB,CAAC,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,GAChC,CAAC,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,GAClC,KAAK,CAAC;AAwCV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,EACxE,SAAS,EAAE,QAAQ,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,IAAI,CAAC,wBAAwB,CAAC,UAAU,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC,GAC9E,2BAA2B,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAarD;AAED,wBAAgB,aAAa,CAAC,QAAQ,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,EACpE,SAAS,EAAE,QAAQ,EACnB,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,IAAI,CAAC,kCAAkC,EAAE,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC,GACtF,uBAAuB,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAWjD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,UAAU,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE;IAC/E,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,UAAU,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACxF,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,GAAG,iBAAiB,CAAC;IACpB,SAAS,EAAE,UAAU,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;CACvD,CAAC,CAoBD;AAED,wBAAgB,oBAAoB,CAAC,UAAU,SAAS,EAAE,CAAC,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE;IACxF,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,UAAU,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,YAAY,CAAC,UAAU,CAAC,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IACxF,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC,GAAG,yBAAyB,CAAC;IAC5B,SAAS,EAAE,UAAU,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,IAAI,EAAE,YAAY,CAAC,UAAU,CAAC,KAAK,OAAO,CAAC;CACvD,CAAC,CAiBD"}
+135
View File
@@ -0,0 +1,135 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.zodResponseFormat = zodResponseFormat;
exports.zodTextFormat = zodTextFormat;
exports.zodFunction = zodFunction;
exports.zodResponsesFunction = zodResponsesFunction;
const tslib_1 = require("../internal/tslib.js");
const z4 = tslib_1.__importStar(require("zod/v4"));
const parser_1 = require("../lib/parser.js");
const zod_to_json_schema_1 = require("../_vendor/zod-to-json-schema/index.js");
const ResponsesParser_1 = require("../lib/ResponsesParser.js");
const transform_1 = require("../lib/transform.js");
function zodV3ToJsonSchema(schema, options) {
return (0, zod_to_json_schema_1.zodToJsonSchema)(schema, {
openaiStrictMode: true,
name: options.name,
nameStrategy: 'duplicate-ref',
$refStrategy: 'extract-to-root',
nullableStrategy: 'property',
});
}
function zodV4ToJsonSchema(schema) {
return (0, transform_1.toStrictJsonSchema)(z4.toJSONSchema(schema, {
target: 'draft-7',
override: ({ zodSchema, jsonSchema }) => {
const def = zodSchema._zod.def;
if (def.type === 'union' && 'discriminator' in def && Array.isArray(jsonSchema.oneOf)) {
if (jsonSchema.anyOf !== undefined) {
throw new Error('Zod discriminated union generated both `anyOf` and `oneOf`, which cannot be represented in an OpenAI strict schema');
}
// Discriminator values are mutually exclusive, so anyOf preserves the
// union while staying inside the API's supported JSON Schema subset.
jsonSchema.anyOf = jsonSchema.oneOf;
delete jsonSchema.oneOf;
}
},
}));
}
function isZodV4(zodObject) {
return '_zod' in zodObject;
}
/**
* Creates a chat completion `JSONSchema` response format object from
* the given Zod schema.
*
* If this is passed to the `.parse()`, `.stream()` or `.runTools()`
* chat completion methods then the response message will contain a
* `.parsed` property that is the result of parsing the content with
* the given Zod object.
*
* ```ts
* const completion = await client.chat.completions.parse({
* model: 'gpt-4o-2024-08-06',
* messages: [
* { role: 'system', content: 'You are a helpful math tutor.' },
* { role: 'user', content: 'solve 8x + 31 = 2' },
* ],
* response_format: zodResponseFormat(
* z.object({
* steps: z.array(z.object({
* explanation: z.string(),
* answer: z.string(),
* })),
* final_answer: z.string(),
* }),
* 'math_answer',
* ),
* });
* const message = completion.choices[0]?.message;
* if (message?.parsed) {
* console.log(message.parsed);
* console.log(message.parsed.final_answer);
* }
* ```
*
* This can be passed directly to the `.create()` method but will not
* result in any automatic parsing, you'll have to parse the response yourself.
*/
function zodResponseFormat(zodObject, name, props) {
return (0, parser_1.makeParseableResponseFormat)({
type: 'json_schema',
json_schema: {
...props,
name,
strict: true,
schema: isZodV4(zodObject) ? zodV4ToJsonSchema(zodObject) : zodV3ToJsonSchema(zodObject, { name }),
},
}, (content) => zodObject.parse(JSON.parse(content)));
}
function zodTextFormat(zodObject, name, props) {
return (0, parser_1.makeParseableTextFormat)({
type: 'json_schema',
...props,
name,
strict: true,
schema: isZodV4(zodObject) ? zodV4ToJsonSchema(zodObject) : zodV3ToJsonSchema(zodObject, { name }),
}, (content) => zodObject.parse(JSON.parse(content)));
}
/**
* Creates a chat completion `function` tool that can be invoked
* automatically by the chat completion `.runTools()` method or automatically
* parsed by `.parse()` / `.stream()`.
*/
function zodFunction(options) {
// @ts-expect-error TODO
return (0, parser_1.makeParseableTool)({
type: 'function',
function: {
name: options.name,
parameters: isZodV4(options.parameters) ?
zodV4ToJsonSchema(options.parameters)
: zodV3ToJsonSchema(options.parameters, { name: options.name }),
strict: true,
...(options.description ? { description: options.description } : undefined),
},
}, {
callback: options.function,
parser: (args) => options.parameters.parse(JSON.parse(args)),
});
}
function zodResponsesFunction(options) {
return (0, ResponsesParser_1.makeParseableResponseTool)({
type: 'function',
name: options.name,
parameters: isZodV4(options.parameters) ?
zodV4ToJsonSchema(options.parameters)
: zodV3ToJsonSchema(options.parameters, { name: options.name }),
strict: true,
...(options.description ? { description: options.description } : undefined),
}, {
callback: options.function,
parser: (args) => options.parameters.parse(JSON.parse(args)),
});
}
//# sourceMappingURL=zod.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"zod.js","sourceRoot":"","sources":["../src/helpers/zod.ts"],"names":[],"mappings":"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"}
+128
View File
@@ -0,0 +1,128 @@
import * as z4 from 'zod/v4';
import { makeParseableResponseFormat, makeParseableTextFormat, makeParseableTool, } from "../lib/parser.mjs";
import { zodToJsonSchema as _zodToJsonSchema } from "../_vendor/zod-to-json-schema/index.mjs";
import { makeParseableResponseTool } from "../lib/ResponsesParser.mjs";
import { toStrictJsonSchema } from "../lib/transform.mjs";
function zodV3ToJsonSchema(schema, options) {
return _zodToJsonSchema(schema, {
openaiStrictMode: true,
name: options.name,
nameStrategy: 'duplicate-ref',
$refStrategy: 'extract-to-root',
nullableStrategy: 'property',
});
}
function zodV4ToJsonSchema(schema) {
return toStrictJsonSchema(z4.toJSONSchema(schema, {
target: 'draft-7',
override: ({ zodSchema, jsonSchema }) => {
const def = zodSchema._zod.def;
if (def.type === 'union' && 'discriminator' in def && Array.isArray(jsonSchema.oneOf)) {
if (jsonSchema.anyOf !== undefined) {
throw new Error('Zod discriminated union generated both `anyOf` and `oneOf`, which cannot be represented in an OpenAI strict schema');
}
// Discriminator values are mutually exclusive, so anyOf preserves the
// union while staying inside the API's supported JSON Schema subset.
jsonSchema.anyOf = jsonSchema.oneOf;
delete jsonSchema.oneOf;
}
},
}));
}
function isZodV4(zodObject) {
return '_zod' in zodObject;
}
/**
* Creates a chat completion `JSONSchema` response format object from
* the given Zod schema.
*
* If this is passed to the `.parse()`, `.stream()` or `.runTools()`
* chat completion methods then the response message will contain a
* `.parsed` property that is the result of parsing the content with
* the given Zod object.
*
* ```ts
* const completion = await client.chat.completions.parse({
* model: 'gpt-4o-2024-08-06',
* messages: [
* { role: 'system', content: 'You are a helpful math tutor.' },
* { role: 'user', content: 'solve 8x + 31 = 2' },
* ],
* response_format: zodResponseFormat(
* z.object({
* steps: z.array(z.object({
* explanation: z.string(),
* answer: z.string(),
* })),
* final_answer: z.string(),
* }),
* 'math_answer',
* ),
* });
* const message = completion.choices[0]?.message;
* if (message?.parsed) {
* console.log(message.parsed);
* console.log(message.parsed.final_answer);
* }
* ```
*
* This can be passed directly to the `.create()` method but will not
* result in any automatic parsing, you'll have to parse the response yourself.
*/
export function zodResponseFormat(zodObject, name, props) {
return makeParseableResponseFormat({
type: 'json_schema',
json_schema: {
...props,
name,
strict: true,
schema: isZodV4(zodObject) ? zodV4ToJsonSchema(zodObject) : zodV3ToJsonSchema(zodObject, { name }),
},
}, (content) => zodObject.parse(JSON.parse(content)));
}
export function zodTextFormat(zodObject, name, props) {
return makeParseableTextFormat({
type: 'json_schema',
...props,
name,
strict: true,
schema: isZodV4(zodObject) ? zodV4ToJsonSchema(zodObject) : zodV3ToJsonSchema(zodObject, { name }),
}, (content) => zodObject.parse(JSON.parse(content)));
}
/**
* Creates a chat completion `function` tool that can be invoked
* automatically by the chat completion `.runTools()` method or automatically
* parsed by `.parse()` / `.stream()`.
*/
export function zodFunction(options) {
// @ts-expect-error TODO
return makeParseableTool({
type: 'function',
function: {
name: options.name,
parameters: isZodV4(options.parameters) ?
zodV4ToJsonSchema(options.parameters)
: zodV3ToJsonSchema(options.parameters, { name: options.name }),
strict: true,
...(options.description ? { description: options.description } : undefined),
},
}, {
callback: options.function,
parser: (args) => options.parameters.parse(JSON.parse(args)),
});
}
export function zodResponsesFunction(options) {
return makeParseableResponseTool({
type: 'function',
name: options.name,
parameters: isZodV4(options.parameters) ?
zodV4ToJsonSchema(options.parameters)
: zodV3ToJsonSchema(options.parameters, { name: options.name }),
strict: true,
...(options.description ? { description: options.description } : undefined),
}, {
callback: options.function,
parser: (args) => options.parameters.parse(JSON.parse(args)),
});
}
//# sourceMappingURL=zod.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"zod.mjs","sourceRoot":"","sources":["../src/helpers/zod.ts"],"names":[],"mappings":"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"}