diff --git a/engine/language-client-codegen/src/typescript/templates/client.ts.j2 b/engine/language-client-codegen/src/typescript/templates/client.ts.j2 new file mode 100644 index 000000000..75f1a7090 --- /dev/null +++ b/engine/language-client-codegen/src/typescript/templates/client.ts.j2 @@ -0,0 +1,80 @@ +import { BamlRuntime, FunctionResult, BamlCtxManager, BamlStream, Image } from "@boundaryml/baml" +import { + {%- for t in types %}{{ t }}{% if !loop.last %}, {% endif %}{% endfor -%} +} from "./types" +import TypeBuilder from "./type_builder" + +export type RecursivePartialNull = T extends object + ? { + [P in keyof T]?: RecursivePartialNull; + } + : T | null; + +export class BamlClient { + private runtime: BamlRuntime + private ctx_manager: BamlCtxManager + private stream_client: BamlStreamClient + + constructor(runtime: BamlRuntime, ctx_manager: BamlCtxManager) { + this.runtime = runtime + this.ctx_manager = ctx_manager + this.stream_client = new BamlStreamClient(runtime, ctx_manager) + } + + get stream() { + return this.stream_client + } + + {% for fn in funcs %} + async {{ fn.name }}( + {% for (name, optional, type) in fn.args -%} + {{name}}{% if optional %}?{% endif %}: {{type}}, + {%- endfor %} + __baml_options__?: { tb?: TypeBuilder } + ): Promise<{{fn.return_type}}> { + const raw = await this.runtime.callFunction( + "{{fn.name}}", + { + {% for (name, optional, type) in fn.args -%} + "{{name}}": {{name}}{% if optional %}?? null{% endif %}{% if !loop.last %},{% endif %} + {%- endfor %} + }, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + return raw.parsed() as {{fn.return_type}} + } + {% endfor %} +} + +class BamlStreamClient { + constructor(private runtime: BamlRuntime, private ctx_manager: BamlCtxManager) {} + + {% for fn in funcs %} + {{ fn.name }}( + {% for (name, optional, type) in fn.args -%} + {{name}}{% if optional %}?{% endif %}: {{type}}, + {%- endfor %} + __baml_options__?: { tb?: TypeBuilder } + ): BamlStream, {{ fn.return_type }}> { + const raw = this.runtime.streamFunction( + "{{fn.name}}", + { + {% for (name, optional, type) in fn.args -%} + "{{name}}": {{name}}{% if optional %} ?? null{% endif %}{% if !loop.last %},{% endif %} + {%- endfor %} + }, + undefined, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + return new BamlStream, {{ fn.return_type }}>( + raw, + (a): a is RecursivePartialNull<{{ fn.return_type }}> => a, + (a): a is {{ fn.return_type }} => a, + this.ctx_manager.get(), + __baml_options__?.tb?.__tb(), + ) + } + {% endfor %} +} \ No newline at end of file diff --git a/engine/language-client-codegen/src/typescript/templates/type_builder.ts.j2 b/engine/language-client-codegen/src/typescript/templates/type_builder.ts.j2 new file mode 100644 index 000000000..703d0173c --- /dev/null +++ b/engine/language-client-codegen/src/typescript/templates/type_builder.ts.j2 @@ -0,0 +1,67 @@ +import { FieldType } from '@boundaryml/baml/native' +import { TypeBuilder as _TypeBuilder, EnumBuilder, ClassBuilder } from '@boundaryml/baml/type_builder' + +export default class TypeBuilder { + private tb: _TypeBuilder; + {% for cls in classes %}{% if cls.dynamic %} + {{cls.name}}: ClassBuilder<'{{cls.name}}' + {%- for (name, _, _) in cls.fields %}{% if loop.first %}, {%endif%}"{{name}}"{% if !loop.last %} | {% endif %}{% endfor -%} + >; + {% endif %}{% endfor %} + {% for enum in enums %}{% if enum.dynamic %} + {{enum.name}}: EnumBuilder<'{{enum.name}}'{%- for value in enum.values %}{% if loop.first %}, {%endif%}"{{value}}"{% if !loop.last %} | {% endif %}{% endfor -%}>; + {% endif %}{% endfor %} + + constructor() { + this.tb = new _TypeBuilder({ + classes: new Set([ + {% for cls in classes %}"{{cls.name}}",{% endfor %} + ]), + enums: new Set([ + {% for enum in enums %}"{{enum.name}}",{% endfor %} + ]) + }); + {% for cls in classes %}{% if cls.dynamic %} + this.{{cls.name}} = this.tb.classBuilder("{{cls.name}}", [ + {% for (name, _, _) in cls.fields %}"{{name}}",{% endfor %} + ]); + {% endif %}{% endfor %} + {% for enum in enums %}{% if enum.dynamic %} + this.{{enum.name}} = this.tb.enumBuilder("{{enum.name}}", [ + {% for value in enum.values %}"{{value}}",{% endfor %} + ]); + {% endif %}{% endfor %} + } + + __tb() { + return this.tb._tb(); + } + + string(): FieldType { + return this.tb.string() + } + + int(): FieldType { + return this.tb.int() + } + + float(): FieldType { + return this.tb.float() + } + + bool(): FieldType { + return this.tb.bool() + } + + list(type: FieldType): FieldType { + return this.tb.list(type) + } + + addClass(name: Name): ClassBuilder { + return this.tb.addClass(name); + } + + addEnum(name: Name): EnumBuilder { + return this.tb.addEnum(name); + } +} diff --git a/engine/language-client-codegen/src/typescript/templates/types.ts.j2 b/engine/language-client-codegen/src/typescript/templates/types.ts.j2 new file mode 100644 index 000000000..f560952ce --- /dev/null +++ b/engine/language-client-codegen/src/typescript/templates/types.ts.j2 @@ -0,0 +1,20 @@ +import { Image } from "@boundaryml/baml" + +{%- for enum in enums %} +export enum {{enum.name}} { + {%- for value in enum.values %} + {{ value }} = "{{ value }}", + {%- endfor %} +} +{% endfor %} + +{%- for cls in classes %} +export interface {{cls.name}} { + {%- for (name, optional, type) in cls.fields %} + {{name}}{% if optional %}?{% endif %}: {{type}} + {%- endfor %} + {% if cls.dynamic %} + [key: string]: any; + {%- endif %} +} +{% endfor %} \ No newline at end of file