diff --git a/docs/docs/get-started/quickstart/openapi.mdx b/docs/docs/get-started/quickstart/openapi.mdx index b81f2c178..4977675f6 100644 --- a/docs/docs/get-started/quickstart/openapi.mdx +++ b/docs/docs/get-started/quickstart/openapi.mdx @@ -306,6 +306,16 @@ try { + + + If you're using `cargo watch -- cargo build` and seeing build failures because it can't find + the generated `baml_client`, try increasing the delay on `cargo watch` to 1 second like so: + + ```bash + cargo watch --delay 1 -- cargo build + ``` + + ```toml Cargo.toml [dependencies] baml-client = { path = "./baml_client" } diff --git a/engine/baml-runtime/src/cli/init.rs b/engine/baml-runtime/src/cli/init.rs index d3c96dbaf..95f6cbb91 100644 --- a/engine/baml-runtime/src/cli/init.rs +++ b/engine/baml-runtime/src/cli/init.rs @@ -137,6 +137,10 @@ fn generate_main_baml_content( ); let openapi_generate_command = match openapi_client_type { + Some("go") => format!( + "{} --additional-properties enumClassPrefix=true,isGoSubmodule=true,packageName=baml_client,withGoMod=false", + openapi_generate_command + ), Some("java") => format!( "{} --additional-properties generateApiTests=false,generateModelTests=false && cd ../baml_client && mvn clean install", openapi_generate_command @@ -145,6 +149,10 @@ fn generate_main_baml_content( "{} --additional-properties composerPackageName=boundaryml/baml-client", openapi_generate_command ), + Some("ruby") => format!( + "{} --additional-properties gemName=baml_client", + openapi_generate_command + ), Some("rust") => format!( "{} --additional-properties packageName=baml-client", openapi_generate_command @@ -171,7 +179,7 @@ fn generate_main_baml_content( // 'baml-cli generate' will run this after generating openapi.yaml, to generate your OpenAPI client // This command will be run from within $output_dir {}"#, - openapi_generate_command + openapi_generate_command.trim_start() ); vec![ diff --git a/engine/language_client_codegen/src/openapi.rs b/engine/language_client_codegen/src/openapi.rs index afe23a6b9..7ad24b035 100644 --- a/engine/language_client_codegen/src/openapi.rs +++ b/engine/language_client_codegen/src/openapi.rs @@ -70,6 +70,46 @@ impl Serialize for OpenApiSchema<'_> { &self, serializer: S, ) -> core::result::Result { + let baml_image_schema = TypeSpecWithMeta { + meta: TypeMetadata { + title: Some("BamlImage".to_string()), + r#enum: None, + r#const: None, + nullable: false, + }, + type_spec: TypeSpec::Inline(TypeDef::Class { + properties: vec![ + ( + "base64".to_string(), + TypeSpecWithMeta { + meta: TypeMetadata { + title: None, + r#enum: None, + r#const: None, + nullable: false, + }, + type_spec: TypeSpec::Inline(TypeDef::String), + }, + ), + ( + "media_type".to_string(), + TypeSpecWithMeta { + meta: TypeMetadata { + title: None, + r#enum: None, + r#const: None, + nullable: true, + }, + type_spec: TypeSpec::Inline(TypeDef::String), + }, + ), + ] + .into_iter() + .collect(), + required: vec!["base64".to_string()], + additional_properties: false, + }), + }; let schemas = match self .schemas .iter() @@ -106,7 +146,7 @@ impl Serialize for OpenApiSchema<'_> { ]) .collect::>(), "components": { - "requestBodies": self.paths.iter().map(|(p)| { + "requestBodies": self.paths.iter().map(|p| { (p.function_name, json!({ "required": true, "content": { @@ -130,9 +170,9 @@ impl Serialize for OpenApiSchema<'_> { }, "media_type": { "type": "string", - "nullable": true, }, - } + }, + "required": ["base64"], }, { "type": "object", @@ -143,9 +183,9 @@ impl Serialize for OpenApiSchema<'_> { }, "media_type": { "type": "string", - "nullable": true, }, - } + }, + "required": ["url"], } ], }), @@ -163,9 +203,9 @@ impl Serialize for OpenApiSchema<'_> { }, "media_type": { "type": "string", - "nullable": true, }, - } + }, + "required": ["base64"], }, { "type": "object", @@ -176,9 +216,9 @@ impl Serialize for OpenApiSchema<'_> { }, "media_type": { "type": "string", - "nullable": true, }, - } + }, + "required": ["url"], } ], }), @@ -293,8 +333,16 @@ impl<'ir> TryFrom>> for OpenApiMethodDef<'ir> { function_name, request_body: TypeSpecWithMeta { meta: TypeMetadata { - // title: Some(format!("{}Request", function_name)), - title: None, + // We _deliberately_ set this, even though OpenAPI doesn't require it, + // because some generators will de-duplicate names of generated types + // based on type shape + // + // For example, the Golang generator will use "ClassifyMessageRequest" as the + // request type for b.GetOrderInfo if they both have (input: string) as their + // function arg signature (I think the Java generator too?) + // + // title: None, + title: Some(format!("{}Request", function_name)), r#enum: None, r#const: None, nullable: false, @@ -468,7 +516,9 @@ impl<'ir> ToTypeReferenceInTypeDefinition<'ir> for FieldType { TypeValue::Float => TypeSpec::Inline(TypeDef::Float), // TODO: should this support "format: int64"? TypeValue::Int => TypeSpec::Inline(TypeDef::Int), - TypeValue::Null => anyhow::bail!("OpenAPI does not support null literals"), + TypeValue::Null => anyhow::bail!( + "BAML<->OpenAPI only allows nulls in unions, not as a literal" + ), TypeValue::String => TypeSpec::Inline(TypeDef::String), TypeValue::Media(BamlMediaType::Audio) => TypeSpec::Ref { r#ref: format!("#/components/schemas/BamlAudio"), @@ -479,7 +529,7 @@ impl<'ir> ToTypeReferenceInTypeDefinition<'ir> for FieldType { }, }, FieldType::Union(inner) => { - let (null_types, nonnull_types): (Vec<_>, Vec<_>) = + let (_nulls, nonnull_types): (Vec<_>, Vec<_>) = inner.into_iter().partition(|t| t.is_null()); // dbg!(&null_types); @@ -487,17 +537,16 @@ impl<'ir> ToTypeReferenceInTypeDefinition<'ir> for FieldType { let one_of = nonnull_types .iter() .map(|t| t.to_type_spec(ir)) - .collect::>()?; + .collect::>>()?; + if one_of.is_empty() { + anyhow::bail!("BAML<->OpenAPI unions must have at least one non-null type") + } TypeSpecWithMeta { meta: TypeMetadata { title: None, r#enum: None, r#const: None, - nullable: if nonnull_types.is_empty() { - false - } else { - true - }, + nullable: false, }, type_spec: TypeSpec::Union { one_of }, } @@ -507,8 +556,8 @@ impl<'ir> ToTypeReferenceInTypeDefinition<'ir> for FieldType { } FieldType::Optional(inner) => { let mut type_spec = inner.to_type_spec(ir)?; - type_spec.meta.nullable = true; - // TODO: if type_spec is of an enum, add "null" to the list of values + // TODO: if type_spec is of an enum, consider adding "null" to the list of values + // something i saw suggested doing this type_spec } }) diff --git a/integ-tests/openapi/baml_client/openapi.yaml b/integ-tests/openapi/baml_client/openapi.yaml index 1d8303d0f..aa854e441 100644 --- a/integ-tests/openapi/baml_client/openapi.yaml +++ b/integ-tests/openapi/baml_client/openapi.yaml @@ -95,7 +95,6 @@ paths: application/json: schema: title: CustomTaskResponse - nullable: true oneOf: - $ref: '#/components/schemas/BookOrder' - $ref: '#/components/schemas/FlightConfirmation' @@ -300,7 +299,6 @@ paths: application/json: schema: title: FnClassOptionalOutputResponse - nullable: true $ref: '#/components/schemas/ClassOptionalOutput' operationId: FnClassOptionalOutput /call/FnClassOptionalOutput2: @@ -314,7 +312,6 @@ paths: application/json: schema: title: FnClassOptionalOutput2Response - nullable: true $ref: '#/components/schemas/ClassOptionalOutput2' operationId: FnClassOptionalOutput2 /call/FnEnumListOutput: @@ -544,7 +541,6 @@ paths: title: OptionalTest_FunctionResponse type: array items: - nullable: true $ref: '#/components/schemas/OptionalTest_ReturnType' operationId: OptionalTest_Function /call/PromptTestClaude: @@ -1056,6 +1052,7 @@ components: content: application/json: schema: + title: AaaSamOutputFormatRequest type: object properties: recipe: @@ -1068,6 +1065,7 @@ components: content: application/json: schema: + title: AudioInputRequest type: object properties: aud: @@ -1080,6 +1078,7 @@ components: content: application/json: schema: + title: ClassifyMessageRequest type: object properties: input: @@ -1092,6 +1091,7 @@ components: content: application/json: schema: + title: ClassifyMessage2Request type: object properties: input: @@ -1104,6 +1104,7 @@ components: content: application/json: schema: + title: ClassifyMessage3Request type: object properties: input: @@ -1116,6 +1117,7 @@ components: content: application/json: schema: + title: CustomTaskRequest type: object properties: input: @@ -1128,6 +1130,7 @@ components: content: application/json: schema: + title: DescribeImageRequest type: object properties: img: @@ -1140,6 +1143,7 @@ components: content: application/json: schema: + title: DescribeImage2Request type: object properties: classWithImage: @@ -1155,6 +1159,7 @@ components: content: application/json: schema: + title: DescribeImage3Request type: object properties: classWithImage: @@ -1170,6 +1175,7 @@ components: content: application/json: schema: + title: DescribeImage4Request type: object properties: classWithImage: @@ -1185,6 +1191,7 @@ components: content: application/json: schema: + title: DummyOutputFunctionRequest type: object properties: input: @@ -1197,6 +1204,7 @@ components: content: application/json: schema: + title: DynamicFuncRequest type: object properties: input: @@ -1209,6 +1217,7 @@ components: content: application/json: schema: + title: DynamicInputOutputRequest type: object properties: input: @@ -1221,6 +1230,7 @@ components: content: application/json: schema: + title: DynamicListInputOutputRequest type: object properties: input: @@ -1235,6 +1245,7 @@ components: content: application/json: schema: + title: ExpectFailureRequest type: object properties: {} required: [] @@ -1244,6 +1255,7 @@ components: content: application/json: schema: + title: ExtractNamesRequest type: object properties: input: @@ -1256,6 +1268,7 @@ components: content: application/json: schema: + title: ExtractPeopleRequest type: object properties: text: @@ -1268,6 +1281,7 @@ components: content: application/json: schema: + title: ExtractReceiptInfoRequest type: object properties: email: @@ -1280,12 +1294,12 @@ components: content: application/json: schema: + title: ExtractResumeRequest type: object properties: resume: type: string img: - nullable: true $ref: '#/components/schemas/BamlImage' required: - resume @@ -1295,6 +1309,7 @@ components: content: application/json: schema: + title: ExtractResume2Request type: object properties: resume: @@ -1307,6 +1322,7 @@ components: content: application/json: schema: + title: FnClassOptionalOutputRequest type: object properties: input: @@ -1319,6 +1335,7 @@ components: content: application/json: schema: + title: FnClassOptionalOutput2Request type: object properties: input: @@ -1331,6 +1348,7 @@ components: content: application/json: schema: + title: FnEnumListOutputRequest type: object properties: input: @@ -1343,6 +1361,7 @@ components: content: application/json: schema: + title: FnEnumOutputRequest type: object properties: input: @@ -1355,10 +1374,10 @@ components: content: application/json: schema: + title: FnNamedArgsSingleStringOptionalRequest type: object properties: myString: - nullable: true type: string required: [] additionalProperties: false @@ -1367,6 +1386,7 @@ components: content: application/json: schema: + title: FnOutputBoolRequest type: object properties: input: @@ -1379,6 +1399,7 @@ components: content: application/json: schema: + title: FnOutputClassRequest type: object properties: input: @@ -1391,6 +1412,7 @@ components: content: application/json: schema: + title: FnOutputClassListRequest type: object properties: input: @@ -1403,6 +1425,7 @@ components: content: application/json: schema: + title: FnOutputClassNestedRequest type: object properties: input: @@ -1415,6 +1438,7 @@ components: content: application/json: schema: + title: FnOutputClassWithEnumRequest type: object properties: input: @@ -1427,6 +1451,7 @@ components: content: application/json: schema: + title: FnOutputStringListRequest type: object properties: input: @@ -1439,6 +1464,7 @@ components: content: application/json: schema: + title: FnTestAliasedEnumOutputRequest type: object properties: input: @@ -1451,6 +1477,7 @@ components: content: application/json: schema: + title: FnTestClassAliasRequest type: object properties: input: @@ -1463,6 +1490,7 @@ components: content: application/json: schema: + title: FnTestNamedArgsSingleEnumRequest type: object properties: myArg: @@ -1475,6 +1503,7 @@ components: content: application/json: schema: + title: GetDataTypeRequest type: object properties: text: @@ -1487,6 +1516,7 @@ components: content: application/json: schema: + title: GetOrderInfoRequest type: object properties: email: @@ -1499,6 +1529,7 @@ components: content: application/json: schema: + title: GetQueryRequest type: object properties: query: @@ -1511,6 +1542,7 @@ components: content: application/json: schema: + title: MyFuncRequest type: object properties: input: @@ -1523,6 +1555,7 @@ components: content: application/json: schema: + title: OptionalTest_FunctionRequest type: object properties: input: @@ -1535,6 +1568,7 @@ components: content: application/json: schema: + title: PromptTestClaudeRequest type: object properties: input: @@ -1547,6 +1581,7 @@ components: content: application/json: schema: + title: PromptTestClaudeChatRequest type: object properties: input: @@ -1559,6 +1594,7 @@ components: content: application/json: schema: + title: PromptTestClaudeChatNoSystemRequest type: object properties: input: @@ -1571,6 +1607,7 @@ components: content: application/json: schema: + title: PromptTestOpenAIRequest type: object properties: input: @@ -1583,6 +1620,7 @@ components: content: application/json: schema: + title: PromptTestOpenAIChatRequest type: object properties: input: @@ -1595,6 +1633,7 @@ components: content: application/json: schema: + title: PromptTestOpenAIChatNoSystemRequest type: object properties: input: @@ -1607,6 +1646,7 @@ components: content: application/json: schema: + title: PromptTestStreamingRequest type: object properties: input: @@ -1619,6 +1659,7 @@ components: content: application/json: schema: + title: SchemaDescriptionsRequest type: object properties: input: @@ -1631,6 +1672,7 @@ components: content: application/json: schema: + title: TestAnthropicRequest type: object properties: input: @@ -1643,6 +1685,7 @@ components: content: application/json: schema: + title: TestAnthropicShorthandRequest type: object properties: input: @@ -1655,6 +1698,7 @@ components: content: application/json: schema: + title: TestAwsRequest type: object properties: input: @@ -1667,6 +1711,7 @@ components: content: application/json: schema: + title: TestAzureRequest type: object properties: input: @@ -1679,6 +1724,7 @@ components: content: application/json: schema: + title: TestCachingRequest type: object properties: input: @@ -1691,6 +1737,7 @@ components: content: application/json: schema: + title: TestFallbackClientRequest type: object properties: {} required: [] @@ -1700,6 +1747,7 @@ components: content: application/json: schema: + title: TestFallbackToShorthandRequest type: object properties: input: @@ -1712,6 +1760,7 @@ components: content: application/json: schema: + title: TestFnNamedArgsSingleBoolRequest type: object properties: myBool: @@ -1724,6 +1773,7 @@ components: content: application/json: schema: + title: TestFnNamedArgsSingleClassRequest type: object properties: myArg: @@ -1736,6 +1786,7 @@ components: content: application/json: schema: + title: TestFnNamedArgsSingleEnumListRequest type: object properties: myArg: @@ -1750,6 +1801,7 @@ components: content: application/json: schema: + title: TestFnNamedArgsSingleFloatRequest type: object properties: myFloat: @@ -1762,6 +1814,7 @@ components: content: application/json: schema: + title: TestFnNamedArgsSingleIntRequest type: object properties: myInt: @@ -1774,6 +1827,7 @@ components: content: application/json: schema: + title: TestFnNamedArgsSingleMapStringToClassRequest type: object properties: myMap: @@ -1788,6 +1842,7 @@ components: content: application/json: schema: + title: TestFnNamedArgsSingleMapStringToMapRequest type: object properties: myMap: @@ -1804,6 +1859,7 @@ components: content: application/json: schema: + title: TestFnNamedArgsSingleMapStringToStringRequest type: object properties: myMap: @@ -1818,6 +1874,7 @@ components: content: application/json: schema: + title: TestFnNamedArgsSingleStringRequest type: object properties: myString: @@ -1830,6 +1887,7 @@ components: content: application/json: schema: + title: TestFnNamedArgsSingleStringArrayRequest type: object properties: myStringArray: @@ -1844,6 +1902,7 @@ components: content: application/json: schema: + title: TestFnNamedArgsSingleStringListRequest type: object properties: myArg: @@ -1858,6 +1917,7 @@ components: content: application/json: schema: + title: TestGeminiRequest type: object properties: input: @@ -1870,6 +1930,7 @@ components: content: application/json: schema: + title: TestImageInputRequest type: object properties: img: @@ -1882,6 +1943,7 @@ components: content: application/json: schema: + title: TestImageInputAnthropicRequest type: object properties: img: @@ -1894,6 +1956,7 @@ components: content: application/json: schema: + title: TestImageListInputRequest type: object properties: imgs: @@ -1908,6 +1971,7 @@ components: content: application/json: schema: + title: TestMulticlassNamedArgsRequest type: object properties: myArg: @@ -1923,6 +1987,7 @@ components: content: application/json: schema: + title: TestOllamaRequest type: object properties: input: @@ -1935,6 +2000,7 @@ components: content: application/json: schema: + title: TestOpenAILegacyProviderRequest type: object properties: input: @@ -1947,6 +2013,7 @@ components: content: application/json: schema: + title: TestOpenAIShorthandRequest type: object properties: input: @@ -1959,6 +2026,7 @@ components: content: application/json: schema: + title: TestRetryConstantRequest type: object properties: {} required: [] @@ -1968,6 +2036,7 @@ components: content: application/json: schema: + title: TestRetryExponentialRequest type: object properties: {} required: [] @@ -1977,6 +2046,7 @@ components: content: application/json: schema: + title: TestVertexRequest type: object properties: input: @@ -1989,10 +2059,10 @@ components: content: application/json: schema: + title: UnionTest_FunctionRequest type: object properties: input: - nullable: true oneOf: - type: string - type: boolean @@ -2009,7 +2079,8 @@ components: type: string media_type: type: string - nullable: true + required: + - base64 - type: object title: BamlImageUrl properties: @@ -2017,7 +2088,8 @@ components: type: string media_type: type: string - nullable: true + required: + - url BamlAudio: oneOf: - type: object @@ -2027,7 +2099,8 @@ components: type: string media_type: type: string - nullable: true + required: + - base64 - type: object title: BamlAudioUrl properties: @@ -2035,7 +2108,8 @@ components: type: string media_type: type: string - nullable: true + required: + - url Category: enum: - Refund @@ -2140,7 +2214,6 @@ components: type: object properties: prop4: - nullable: true type: string required: [] additionalProperties: false @@ -2176,13 +2249,10 @@ components: type: object properties: prop1: - nullable: true type: string prop2: - nullable: true type: string prop3: - nullable: true $ref: '#/components/schemas/Blah' required: [] additionalProperties: false @@ -2204,15 +2274,12 @@ components: type: object properties: bookOrder: - nullable: true oneOf: - $ref: '#/components/schemas/BookOrder' flightConfirmation: - nullable: true oneOf: - $ref: '#/components/schemas/FlightConfirmation' groceryReceipt: - nullable: true oneOf: - $ref: '#/components/schemas/GroceryReceipt' required: [] @@ -2274,7 +2341,6 @@ components: items: type: string graduation_date: - nullable: true type: string required: - institution @@ -2351,7 +2417,6 @@ components: items: type: array items: - nullable: true oneOf: - type: string - type: integer @@ -2407,11 +2472,9 @@ components: type: object properties: prop3: - nullable: true oneOf: - type: string prop4: - nullable: true oneOf: - type: string prop20: @@ -2423,11 +2486,9 @@ components: type: object properties: prop11: - nullable: true oneOf: - type: string prop12: - nullable: true oneOf: - type: string required: [] @@ -2447,15 +2508,12 @@ components: type: object properties: omega_1: - nullable: true $ref: '#/components/schemas/OptionalTest_Prop1' omega_2: - nullable: true type: string omega_3: type: array items: - nullable: true $ref: '#/components/schemas/OptionalTest_CategoryType' required: - omega_3 @@ -2466,10 +2524,8 @@ components: order_status: $ref: '#/components/schemas/OrderStatus' tracking_number: - nullable: true type: string estimated_arrival_date: - nullable: true type: string required: - order_status @@ -2478,10 +2534,8 @@ components: type: object properties: name: - nullable: true type: string hair_color: - nullable: true $ref: '#/components/schemas/Color' required: [] additionalProperties: false @@ -2489,12 +2543,10 @@ components: type: object properties: amount: - nullable: true oneOf: - type: integer - type: number unit: - nullable: true type: string required: - amount @@ -2505,7 +2557,6 @@ components: dataType: $ref: '#/components/schemas/DataType' value: - nullable: true oneOf: - $ref: '#/components/schemas/Resume' - $ref: '#/components/schemas/Event' @@ -2521,7 +2572,6 @@ components: items: $ref: '#/components/schemas/ReceiptItem' total_cost: - nullable: true type: number required: - items @@ -2532,7 +2582,6 @@ components: name: type: string description: - nullable: true type: string quantity: type: integer @@ -2586,22 +2635,18 @@ components: type: object properties: prop1: - nullable: true oneOf: - type: string prop2: - nullable: true oneOf: - $ref: '#/components/schemas/Nested' - type: string prop5: type: array items: - nullable: true oneOf: - type: string prop6: - nullable: true oneOf: - type: string - type: array @@ -2610,20 +2655,16 @@ components: nested_attrs: type: array items: - nullable: true oneOf: - type: string - $ref: '#/components/schemas/Nested' parens: - nullable: true oneOf: - type: string other_group: - nullable: true oneOf: - type: string - - nullable: true - oneOf: + - oneOf: - type: integer - type: string required: @@ -2637,17 +2678,14 @@ components: type: object properties: dateRange: - nullable: true type: integer location: type: array items: type: string jobTitle: - nullable: true $ref: '#/components/schemas/WithReasoning' company: - nullable: true $ref: '#/components/schemas/WithReasoning' description: type: array @@ -2656,7 +2694,6 @@ components: tags: type: array items: - nullable: true oneOf: - $ref: '#/components/schemas/Tag' - type: string @@ -2738,19 +2775,16 @@ components: type: object properties: prop1: - nullable: true oneOf: - type: string - type: boolean prop2: type: array items: - nullable: true oneOf: - type: number - type: boolean prop3: - nullable: true oneOf: - type: array items: