🔨 feat: Use x-strict attribute in OpenAPI Actions for Strict Function Definition (#4639)

* feat: manage an 'x-strict': true attribute in openapi specs for assistants which generates function calls with stric attribute

* fix typo and lint errors

---------

Co-authored-by: Olivier Schiavo <olivier.schiavo@wengo.com>
This commit is contained in:
owengo 2025-02-10 22:02:21 +01:00 committed by GitHub
parent aea055b597
commit d844e56c50
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 18 additions and 3 deletions

View file

@ -17,6 +17,7 @@ export type ParametersSchema = {
type: string; type: string;
properties: Record<string, Reference | Schema>; properties: Record<string, Reference | Schema>;
required: string[]; required: string[];
additionalProperties?: boolean;
}; };
export type OpenAPISchema = OpenAPIV3.SchemaObject & export type OpenAPISchema = OpenAPIV3.SchemaObject &
@ -128,24 +129,33 @@ export class FunctionSignature {
name: string; name: string;
description: string; description: string;
parameters: ParametersSchema; parameters: ParametersSchema;
strict: boolean;
constructor(name: string, description: string, parameters: ParametersSchema) { constructor(name: string, description: string, parameters: ParametersSchema, strict?: boolean) {
this.name = name; this.name = name;
this.description = description; this.description = description;
this.parameters = parameters; this.parameters = parameters;
this.strict = strict ?? false;
} }
toObjectTool(): FunctionTool { toObjectTool(): FunctionTool {
const parameters = {
...this.parameters,
additionalProperties: this.strict ? false : undefined,
};
return { return {
type: Tools.function, type: Tools.function,
function: { function: {
name: this.name, name: this.name,
description: this.description, description: this.description,
parameters: this.parameters, parameters,
strict: this.strict,
}, },
}; };
} }
} }
class RequestConfig { class RequestConfig {
constructor( constructor(
readonly domain: string, readonly domain: string,
@ -383,12 +393,15 @@ export function openapiToFunction(
for (const [method, operation] of Object.entries(methods as OpenAPIV3.PathsObject)) { for (const [method, operation] of Object.entries(methods as OpenAPIV3.PathsObject)) {
const operationObj = operation as OpenAPIV3.OperationObject & { const operationObj = operation as OpenAPIV3.OperationObject & {
'x-openai-isConsequential'?: boolean; 'x-openai-isConsequential'?: boolean;
} & {
'x-strict'?: boolean
}; };
// Operation ID is used as the function name // Operation ID is used as the function name
const defaultOperationId = `${method}_${path}`; const defaultOperationId = `${method}_${path}`;
const operationId = operationObj.operationId || sanitizeOperationId(defaultOperationId); const operationId = operationObj.operationId || sanitizeOperationId(defaultOperationId);
const description = operationObj.summary || operationObj.description || ''; const description = operationObj.summary || operationObj.description || '';
const isStrict = operationObj['x-strict'] ?? false;
const parametersSchema: OpenAPISchema = { const parametersSchema: OpenAPISchema = {
type: 'object', type: 'object',
@ -428,7 +441,7 @@ export function openapiToFunction(
} }
} }
const functionSignature = new FunctionSignature(operationId, description, parametersSchema); const functionSignature = new FunctionSignature(operationId, description, parametersSchema, isStrict);
functionSignatures.push(functionSignature); functionSignatures.push(functionSignature);
const actionRequest = new ActionRequest( const actionRequest = new ActionRequest(

View file

@ -38,6 +38,8 @@ export type FunctionTool = {
description: string; description: string;
name: string; name: string;
parameters: Record<string, unknown>; parameters: Record<string, unknown>;
strict?: boolean;
additionalProperties?: boolean; // must be false if strict is true https://platform.openai.com/docs/guides/structured-outputs/some-type-specific-keywords-are-not-yet-supported
}; };
}; };