Skip to content

Commit 3fdbfd0

Browse files
authored
[feat] Adding support for pydantic v1 (#1968)
<!-- ELLIPSIS_HIDDEN --> > [!IMPORTANT] > Adds support for Pydantic v1 by introducing a new generator output type and updating templates and logic to handle both Pydantic versions. > > - **Behavior**: > - Adds support for Pydantic v1 by introducing `GeneratorOutputType::PythonPydanticV1`. > - Updates `generate()` in `python/mod.rs` to accept `legacy_pydantic` flag. > - Modifies `parse_py_type()` in `parse_py_type.rs` to check Pydantic version and adjust field extraction logic. > - **Templates**: > - Updates `partial_types.py.j2` and `types.py.j2` to conditionally use `ConfigDict` or `Config` based on Pydantic version. > - Adjusts class definitions to use `GenericModel` for Pydantic v1. > - **Misc**: > - Changes `client_type` from `Option<GeneratorOutputType>` to `GeneratorOutputType` in `GeneratorArgs`. > - Updates `generators.baml` to include a new generator for Pydantic v1. > - Adds integration tests for Pydantic v1 in `python-v1/tests/test_typebuilder.py`. > > <sup>This description was created by </sup>[<img alt="Ellipsis" src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=BoundaryML%2Fbaml&utm_source=github&utm_medium=referral)<sup> for ec7cd42. You can [customize](https://app.ellipsis.dev/BoundaryML/settings/summaries) this summary. It will automatically update as commits are pushed.</sup> <!-- ELLIPSIS_HIDDEN -->
1 parent 5d45612 commit 3fdbfd0

56 files changed

Lines changed: 57625 additions & 71 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎engine/baml-lib/baml-types/src/generator.rs‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ pub enum GeneratorOutputType {
1717
#[strum(serialize = "python/pydantic")]
1818
PythonPydantic,
1919

20+
#[strum(serialize = "python/pydantic/v1")]
21+
PythonPydanticV1,
22+
2023
#[strum(serialize = "typescript")]
2124
Typescript,
2225

@@ -43,6 +46,9 @@ impl GeneratorOutputType {
4346
// Due to legacy reasons, PythonPydantic and Typescript default to async
4447
// DO NOT CHANGE THIS DEFAULT EVER OR YOU WILL BREAK EXISTING USERS
4548
Self::PythonPydantic => GeneratorDefaultClientMode::Async,
49+
// mimic legacy version
50+
Self::PythonPydanticV1 => GeneratorDefaultClientMode::Async,
51+
4652
Self::Typescript => GeneratorDefaultClientMode::Async,
4753
Self::TypescriptReact => GeneratorDefaultClientMode::Async,
4854
Self::RubySorbet => GeneratorDefaultClientMode::Sync,
@@ -55,6 +61,7 @@ impl GeneratorOutputType {
5561
match self {
5662
Self::OpenApi => GeneratorDefaultClientMode::Sync,
5763
Self::PythonPydantic => GeneratorDefaultClientMode::Sync,
64+
Self::PythonPydanticV1 => GeneratorDefaultClientMode::Sync,
5865
Self::Typescript => GeneratorDefaultClientMode::Async,
5966
Self::TypescriptReact => GeneratorDefaultClientMode::Async,
6067
Self::RubySorbet => GeneratorDefaultClientMode::Sync,

‎engine/baml-runtime/src/cli/generate.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl GenerateArgs {
5050
// this has no meaning
5151
GeneratorDefaultClientMode::Sync
5252
}
53-
internal_baml_core::configuration::GeneratorOutputType::PythonPydantic => {
53+
internal_baml_core::configuration::GeneratorOutputType::PythonPydantic | internal_baml_core::configuration::GeneratorOutputType::PythonPydanticV1 => {
5454
// TODO: Consider changing this default to sync
5555
GeneratorDefaultClientMode::Async
5656
}
@@ -82,7 +82,7 @@ impl GenerateArgs {
8282
false,
8383
default_client_mode,
8484
vec![],
85-
None,
85+
client_type,
8686
if matches!(
8787
client_type,
8888
internal_baml_core::configuration::GeneratorOutputType::Go

‎engine/baml-runtime/src/cli/init.rs‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl InitArgs {
8484
"Created new BAML project in {} for {}",
8585
baml_src.display(),
8686
match output_type {
87-
GeneratorOutputType::PythonPydantic => "Python clients".to_string(),
87+
GeneratorOutputType::PythonPydanticV1 | GeneratorOutputType::PythonPydantic => "Python clients".to_string(),
8888
GeneratorOutputType::Typescript => "TypeScript clients".to_string(),
8989
GeneratorOutputType::RubySorbet => "Ruby clients".to_string(),
9090
GeneratorOutputType::OpenApi => match &self.openapi_client_type {
@@ -98,7 +98,7 @@ impl InitArgs {
9898
baml_log::info!(
9999
"Follow instructions at https://docs.boundaryml.com/docs/get-started/quickstart/{}",
100100
match output_type {
101-
GeneratorOutputType::PythonPydantic => "python",
101+
GeneratorOutputType::PythonPydanticV1 | GeneratorOutputType::PythonPydantic => "python",
102102
GeneratorOutputType::Typescript => "typescript",
103103
GeneratorOutputType::RubySorbet => "ruby",
104104
GeneratorOutputType::OpenApi => "openapi",
@@ -121,6 +121,7 @@ fn generate_main_baml_content(
121121
| GeneratorOutputType::RubySorbet
122122
| GeneratorOutputType::Go => "".to_string(),
123123
GeneratorOutputType::PythonPydantic
124+
| GeneratorOutputType::PythonPydanticV1
124125
| GeneratorOutputType::Typescript
125126
| GeneratorOutputType::TypescriptReact => format!(
126127
r#"

‎engine/baml-runtime/src/cli/serve/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,7 @@ Tip: test that the server is up using `curl http://localhost:{}/_debug/ping`
571571
true,
572572
GeneratorDefaultClientMode::Sync,
573573
Vec::new(),
574-
None,
574+
GeneratorOutputType::OpenApi,
575575
None,
576576
None,
577577
)

‎engine/baml-runtime/src/lib.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,7 @@ impl BamlRuntime {
924924
no_version_check,
925925
generator.default_client_mode(),
926926
generator.on_generate.clone(),
927-
Some(generator.output_type),
927+
generator.output_type,
928928
generator.client_package_name.clone(),
929929
generator.module_format,
930930
)?,

‎engine/language_client_codegen/src/go/mod.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ class Foo {
278278
true,
279279
GeneratorDefaultClientMode::Async,
280280
Vec::new(),
281-
Some(GeneratorOutputType::Go),
281+
GeneratorOutputType::Go,
282282
Some("example.com/integ-tests".to_string()),
283283
None,
284284
)

‎engine/language_client_codegen/src/lib.rs‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ pub struct GeneratorArgs {
3838
on_generate: Vec<String>,
3939

4040
// The type of client to generate
41-
client_type: Option<GeneratorOutputType>,
41+
client_type: GeneratorOutputType,
4242
client_package_name: Option<String>,
4343

4444
// For TS generators, we can choose between CJS and ESM module formats
@@ -64,7 +64,7 @@ impl GeneratorArgs {
6464
no_version_check: bool,
6565
default_client_mode: GeneratorDefaultClientMode,
6666
on_generate: Vec<String>,
67-
client_type: Option<GeneratorOutputType>,
67+
client_type: GeneratorOutputType,
6868
client_package_name: Option<String>,
6969
module_format: Option<ModuleFormat>,
7070
) -> Result<Self> {
@@ -207,7 +207,8 @@ impl GenerateClient for GeneratorOutputType {
207207
// Generate files
208208
let files = match self {
209209
GeneratorOutputType::OpenApi => openapi::generate(ir, gen),
210-
GeneratorOutputType::PythonPydantic => python::generate(ir, gen),
210+
GeneratorOutputType::PythonPydantic => python::generate(ir, gen, false),
211+
GeneratorOutputType::PythonPydanticV1 => python::generate(ir, gen, true),
211212
GeneratorOutputType::RubySorbet => ruby::generate(ir, gen),
212213
GeneratorOutputType::Typescript => typescript::generate(ir, gen),
213214
GeneratorOutputType::TypescriptReact => typescript::generate(ir, gen),

‎engine/language_client_codegen/src/python/generate_types.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use internal_baml_core::ir::{
1616
pub(crate) struct PythonTypes<'ir> {
1717
enums: Vec<PythonEnum<'ir>>,
1818
classes: Vec<PythonClass<'ir>>,
19+
is_pydantic_2: bool,
1920
structural_recursive_alias_cycles: Vec<PythonTypeAlias<'ir>>,
2021
}
2122

@@ -51,6 +52,7 @@ struct PythonTypeAlias<'ir> {
5152
#[template(path = "partial_types.py.j2", escape = "none")]
5253
pub(crate) struct PythonStreamTypes<'ir> {
5354
partial_classes: Vec<PartialPythonClass<'ir>>,
55+
is_pydantic_2: bool,
5456
structural_recursive_alias_cycles: Vec<PythonTypeAlias<'ir>>,
5557
}
5658

@@ -68,11 +70,12 @@ impl<'ir> TryFrom<(&'ir IntermediateRepr, &'_ crate::GeneratorArgs)> for PythonT
6870
type Error = anyhow::Error;
6971

7072
fn try_from(
71-
(ir, _): (&'ir IntermediateRepr, &'_ crate::GeneratorArgs),
73+
(ir, gen): (&'ir IntermediateRepr, &'_ crate::GeneratorArgs),
7274
) -> Result<PythonTypes<'ir>> {
7375
Ok(PythonTypes {
7476
enums: ir.walk_enums().map(PythonEnum::from).collect::<Vec<_>>(),
7577
classes: ir.walk_classes().map(PythonClass::from).collect::<Vec<_>>(),
78+
is_pydantic_2: matches!(gen.client_type, baml_types::GeneratorOutputType::PythonPydantic),
7679
structural_recursive_alias_cycles: {
7780
let mut cycles = ir
7881
.walk_alias_cycles()
@@ -160,12 +163,13 @@ impl<'ir> From<Walker<'ir, (&'ir String, &'ir FieldType)>> for PythonTypeAlias<'
160163
impl<'ir> TryFrom<(&'ir IntermediateRepr, &'_ crate::GeneratorArgs)> for PythonStreamTypes<'ir> {
161164
type Error = anyhow::Error;
162165

163-
fn try_from((ir, _): (&'ir IntermediateRepr, &'_ crate::GeneratorArgs)) -> Result<Self> {
166+
fn try_from((ir, gen): (&'ir IntermediateRepr, &'_ crate::GeneratorArgs)) -> Result<Self> {
164167
Ok(Self {
165168
partial_classes: ir
166169
.walk_classes()
167170
.map(PartialPythonClass::from)
168171
.collect::<Vec<_>>(),
172+
is_pydantic_2: matches!(gen.client_type, baml_types::GeneratorOutputType::PythonPydantic),
169173
structural_recursive_alias_cycles: {
170174
let mut cycles = ir
171175
.walk_alias_cycles()

‎engine/language_client_codegen/src/python/mod.rs‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ struct InlinedBaml {
119119
pub(crate) fn generate(
120120
ir: &IntermediateRepr,
121121
generator: &crate::GeneratorArgs,
122+
legacy_pydantic: bool,
122123
) -> Result<IndexMap<PathBuf, String>> {
123124
let mut collector = FileCollector::<PythonLanguageFeatures>::new();
124125

@@ -587,7 +588,7 @@ client<llm> GPT35 {
587588
true,
588589
GeneratorDefaultClientMode::Async,
589590
Vec::new(),
590-
Some(GeneratorOutputType::PythonPydantic),
591+
GeneratorOutputType::PythonPydantic,
591592
None,
592593
None,
593594
)
@@ -600,7 +601,7 @@ client<llm> GPT35 {
600601
fn generate_streaming_python() {
601602
let ir = mk_ir();
602603
let generator_args = mk_gen();
603-
let res = generate(&ir, &generator_args).unwrap();
604+
let res = generate(&ir, &generator_args, false).unwrap();
604605
let partial_types = res.get(&PathBuf::from("partial_types.py")).unwrap();
605606
let async_client = res.get(&PathBuf::from("async_client.py")).unwrap();
606607
//eprintln!("{}", partial_types);

‎engine/language_client_codegen/src/python/templates/async_client.py.j2‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
from typing import Any, Dict, List, Optional, TypeVar, Union, TypedDict, Type, Literal, cast
2-
from typing_extensions import NotRequired
1+
from typing import Any, Dict, List, Optional, TypeVar, Union, TypedDict, Type, cast
2+
from typing_extensions import NotRequired, Literal
33
import pprint
44

55
import baml_py

0 commit comments

Comments
 (0)