Skip to content

Commit

Permalink
Override media_type when using from_url() Fixes #1436 (#1444)
Browse files Browse the repository at this point in the history
<!-- ELLIPSIS_HIDDEN -->


> [!IMPORTANT]
> Add optional `media_type` parameter to `from_url()` for `Audio` and
`Image` classes in Python, Ruby, and TypeScript, and update
documentation accordingly.
> 
>   - **Behavior**:
> - Add optional `media_type` parameter to `from_url()` in `BamlAudioPy`
and `BamlImagePy` in `audio.rs` and `image.rs`.
> - Update `from_url()` in `media.rs` for Ruby to accept `media_type`.
> - Modify `fromUrl()` in `native.d.ts` and `audio.rs` and `image.rs`
for TypeScript to include `mediaType`.
>   - **Documentation**:
> - Add `media.mdx` and `media.mdx` to document `Image` and `Audio`
usage.
> - Update `multi-modal.mdx` to reflect changes in `from_url()` method.
>     - Update `typebuilder.mdx` to include new TypeBuilder methods.
>     - Update `docs.yml` to include new documentation paths.
> 
> <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 e7a3d2a. It will automatically
update as commits are pushed.</sup>


<!-- ELLIPSIS_HIDDEN -->
  • Loading branch information
hellovai authored Feb 13, 2025
1 parent 1d481f1 commit c913f09
Show file tree
Hide file tree
Showing 11 changed files with 269 additions and 28 deletions.
5 changes: 2 additions & 3 deletions docs/old/docs/calling-baml/multi-modal.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,10 @@ import { Image } from "@boundaryml/baml"
let res = await b.TestImageInput(
Image.fromBase64('image/png', image_b64),
)

```

```ruby Ruby (beta)
we're working on it!
# we're working on it!
```

</CodeBlocks>
Expand Down Expand Up @@ -95,6 +94,6 @@ import { Audio } from "@boundaryml/baml"
```

```ruby Ruby (beta)
we're working on it!
# we're working on it!
```
</CodeBlocks>
5 changes: 3 additions & 2 deletions engine/language_client_python/src/types/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ crate::lang_wrapper!(BamlAudioPy, baml_types::BamlMedia);
#[pymethods]
impl BamlAudioPy {
#[staticmethod]
fn from_url(url: String) -> Self {
#[pyo3(signature = (url, media_type = None))]
fn from_url(url: String, media_type: Option<String>) -> Self {
BamlAudioPy {
inner: baml_types::BamlMedia::url(baml_types::BamlMediaType::Audio, url, None),
inner: baml_types::BamlMedia::url(baml_types::BamlMediaType::Audio, url, media_type),
}
}

Expand Down
5 changes: 3 additions & 2 deletions engine/language_client_python/src/types/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,10 @@ crate::lang_wrapper!(BamlImagePy, baml_types::BamlMedia);
#[pymethods]
impl BamlImagePy {
#[staticmethod]
fn from_url(url: String) -> Self {
#[pyo3(signature = (url, media_type = None))]
fn from_url(url: String, media_type: Option<String>) -> Self {
BamlImagePy {
inner: baml_types::BamlMedia::url(baml_types::BamlMediaType::Image, url, None),
inner: baml_types::BamlMedia::url(baml_types::BamlMediaType::Image, url, media_type),
}
}

Expand Down
14 changes: 7 additions & 7 deletions engine/language_client_ruby/ext/ruby_ffi/src/types/media.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ pub(crate) struct Image {
}

impl Image {
pub fn from_url(url: String) -> Self {
pub fn from_url(url: String, media_type: Option<String>) -> Self {
Self {
inner: BamlMedia::url(BamlMediaType::Image, url, None),
inner: BamlMedia::url(BamlMediaType::Image, url, media_type),
}
}

Expand All @@ -26,7 +26,7 @@ impl Image {

pub fn define_in_ruby(module: &RModule) -> Result<()> {
let cls = module.define_class("Image", class::object())?;
cls.define_singleton_method("from_url", function!(Image::from_url, 1))?;
cls.define_singleton_method("from_url", function!(Image::from_url, 2))?;
cls.define_singleton_method("from_base64", function!(Image::from_base64, 2))?;

Ok(())
Expand All @@ -45,20 +45,20 @@ pub(crate) struct Audio {
}

impl Audio {
pub fn from_url(url: String) -> Self {
pub fn from_url(url: String, media_type: Option<String>) -> Self {
Self {
inner: BamlMedia::url(BamlMediaType::Audio, url, None),
inner: BamlMedia::url(BamlMediaType::Audio, url, media_type),
}
}
pub fn from_base64(media_type: String, base64: String) -> Self {
Self {
inner: BamlMedia::base64(BamlMediaType::Image, base64, Some(media_type)),
inner: BamlMedia::base64(BamlMediaType::Audio, base64, Some(media_type)),
}
}

pub fn define_in_ruby(module: &RModule) -> Result<()> {
let cls = module.define_class("Audio", class::object())?;
cls.define_singleton_method("from_url", function!(Audio::from_url, 1))?;
cls.define_singleton_method("from_url", function!(Audio::from_url, 2))?;
cls.define_singleton_method("from_base64", function!(Audio::from_base64, 2))?;

Ok(())
Expand Down
4 changes: 2 additions & 2 deletions engine/language_client_typescript/native.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* auto-generated by NAPI-RS */
/* eslint-disable */
export declare class BamlAudio {
static fromUrl(url: string): BamlAudio
static fromUrl(url: string, mediaType?: string | undefined | null): BamlAudio
static fromBase64(mediaType: string, base64: string): BamlAudio
isUrl(): boolean
asUrl(): string
Expand All @@ -10,7 +10,7 @@ export declare class BamlAudio {
}

export declare class BamlImage {
static fromUrl(url: string): BamlImage
static fromUrl(url: string, mediaType?: string | undefined | null): BamlImage
static fromBase64(mediaType: string, base64: string): BamlImage
isUrl(): boolean
asUrl(): string
Expand Down
4 changes: 2 additions & 2 deletions engine/language_client_typescript/src/types/audio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ crate::lang_wrapper!(BamlAudio, baml_types::BamlMedia);
#[napi]
impl BamlAudio {
#[napi(ts_return_type = "BamlAudio")]
pub fn from_url(url: String) -> External<BamlAudio> {
pub fn from_url(url: String, media_type: Option<String>) -> External<BamlAudio> {
let aud = BamlAudio {
inner: baml_types::BamlMedia::url(baml_types::BamlMediaType::Audio, url, None),
inner: baml_types::BamlMedia::url(baml_types::BamlMediaType::Audio, url, media_type),
};
External::new(aud)
}
Expand Down
4 changes: 2 additions & 2 deletions engine/language_client_typescript/src/types/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ crate::lang_wrapper!(BamlImage, baml_types::BamlMedia);
#[napi]
impl BamlImage {
#[napi(ts_return_type = "BamlImage")]
pub fn from_url(url: String) -> External<BamlImage> {
pub fn from_url(url: String, media_type: Option<String>) -> External<BamlImage> {
let img = BamlImage {
inner: baml_types::BamlMedia::url(baml_types::BamlMediaType::Image, url, None),
inner: baml_types::BamlMedia::url(baml_types::BamlMediaType::Image, url, media_type),
};
External::new(img)
}
Expand Down
58 changes: 58 additions & 0 deletions fern/03-reference/baml/media.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
---
title: Image / Audio
---

Media values as denoted more specifically in BAML.

| Baml Type |
| --- |
| `image` |
| `audio` |

Both `image` and `audio` values values can be:

- A URL
- A base64 encoded string
- A file path

For usage in Python / Typescript / etc, see [baml_client > media](/ref/baml-client/media).

## Usage as a URL

```baml {2,13-15,22-25,32-34}
// Pass in an image type
function DescribeImage(image: image) -> string {
client "openai/gpt-4o-mini"
prompt #"
Describe the image.
{{ image }}
"#
}
test ImageDescriptionFromURL {
functions [DescribeImage]
args {
image {
url "https://upload.wikimedia.org/wikipedia/en/4/4d/Shrek_%28character%29.png"
}
}
}
test ImageDescriptionFromBase64 {
functions [DescribeImage]
args {
image {
media_type "image/png"
base64 "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x/AAzmH+UlvRkwAAAAASUVORK5CYII="
}
}
}
test ImageDescriptionFromFile {
functions [DescribeImage]
args {
image {
file "./shrek.png"
}
}
}
107 changes: 107 additions & 0 deletions fern/03-reference/baml_client/media.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
---
title: Image / Audio
---

Image / Audio values to BAML functions can by created in client libraries. This document explains how to use these functions both at compile time and runtime to handle multimedia data. For more details, refer to [image types](/ref/baml/types#image) and [audio types](/ref/baml/types#audio).

## Images

BAML functions that accept an image input utilize the Image helper, which supports creating Image objects from URLs or Base64 encoded data.

### Image Creation Methods


`Image.from_url(url: string, media_type: string | null)`: Creates an Image object from the specified URL. (optionally specify the media type, otherwise it will be inferred from the URL)

`Image.from_base64(mime_type: string, data: string)`: Creates an Image object using Base64 encoded data along with the given MIME type.

### Usage


<CodeBlocks>
```python {6,11}
from baml_py import Image
from baml_client import b

async def test_image_input():
# Create an Image from a URL
img = Image.from_url("https://upload.wikimedia.org/wikipedia/en/4/4d/Shrek_%28character%29.png")
res = await b.TestImageInput(img=img)

# Create an Image from Base64 data
image_b64 = "iVB0xyz..."
img = Image.from_base64("image/png", image_b64)
res = await b.TestImageInput(img=img)
```

```typescript
import { b } from '../baml_client'
import { Image } from "@boundaryml/baml"

// Create an Image from a URL
let res = await b.TestImageInput(
Image.fromUrl('https://upload.wikimedia.org/wikipedia/en/4/4d/Shrek_%28character%29.png')
)

// Create an Image from Base64 data
const image_b64 = "iVB0xyz..."
res = await b.TestImageInput(
Image.fromBase64('image/png', image_b64)
)
```

```ruby
# Ruby implementation is in development.
```
</CodeBlocks>
## Audio

BAML functions that accept audio inputs utilize the Audio helper. Similar to images, you can create Audio objects from URLs or Base64 encoded data.

### Audio Creation Methods

• Audio.from_url(url: string): Creates an Audio object from the specified URL.

• Audio.from_base64(mime_type: string, data: string): Creates an Audio object using Base64 encoded audio data with the provided MIME type.

### Usage

**Python**
```python
from baml_py import Audio
from baml_client import b

async def run():
# Create an Audio object from a URL
res = await b.TestAudioInput(
audio=Audio.from_url("https://actions.google.com/sounds/v1/emergency/beeper_emergency_call.ogg")
)

# Create an Audio object from Base64 data
b64 = "iVB0xyz..."
res = await b.TestAudioInput(
audio=Audio.from_base64("audio/ogg", b64)
)
```

**TypeScript**
```typescript
import { b } from '../baml_client'
import { Audio } from "@boundaryml/baml"

// Create an Audio object from a URL
let res = await b.TestAudioInput(
Audio.fromUrl('https://actions.google.com/sounds/v1/emergency/beeper_emergency_call.ogg')
)

// Create an Audio object from Base64 data
const audio_base64 = "iVB0xyz..."
res = await b.TestAudioInput(
Audio.fromBase64('audio/ogg', audio_base64)
)
```

**Ruby (beta)**
```ruby
# Ruby implementation is in development.
```
84 changes: 76 additions & 8 deletions fern/03-reference/baml_client/typebuilder.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -203,14 +203,82 @@ tb.User.add_property("address", address.type)

TypeBuilder provides methods for building different kinds of types:

| Method | Description | Example |
|--------|-------------|---------|
| `string()` | Creates a string type | `tb.string()` |
| `int()` | Creates an integer type | `tb.int()` |
| `float()` | Creates a float type | `tb.float()` |
| `bool()` | Creates a boolean type | `tb.bool()` |
| `list()` | Makes a type into a list | `tb.string().list()` |
| `optional()` | Makes a type optional | `tb.string().optional()` |
| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `string()` | `FieldType` | Creates a string type | `tb.string()` |
| `int()` | `FieldType` | Creates an integer type | `tb.int()` |
| `float()` | `FieldType` | Creates a float type | `tb.float()` |
| `bool()` | `FieldType` | Creates a boolean type | `tb.bool()` |
| `list(type: FieldType)` | `FieldType` | Makes a type into a list | `tb.list(tb.string())` |
| `union(types: FieldType[])` | `FieldType` | Creates a union of types | `tb.union([tb.string(), tb.int()])` |
| `add_class(name: string)` | `ClassBuilder` | Creates a new class | `tb.add_class("User")` |
| `add_enum(name: string)` | `EnumBuilder` | Creates a new enum | `tb.add_enum("Category")` |


In addition to the methods above, all types marked with `@@dynamic` will also appear in the TypeBuilder.

```baml {4}
class User {
name string
age int
@@dynamic // Allow adding more properties
}
```

```python {2}
tb = TypeBuilder()
tb.User.add_property("email", tb.string())
```

### FieldType

`FieldType` is a type that represents a field in a type. It can be used to add descriptions, constraints, and other metadata to a field.

| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `list()` | `FieldType` | Makes a type into a list | `tb.string().list()` |
| `optional()` | `FieldType` | Makes a type optional | `tb.string().optional()` |

### ClassBuilder

`ClassBuilder` is a type that represents a class in a type. It can be used to add properties to a class.

| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `add_property(name: string, type: FieldType)` | `ClassPropertyBuilder` | Adds a property to the class | `my_cls.add_property("email", tb.string())` |
| `description(description: string)` | `ClassBuilder` | Adds a description to the class | `my_cls.description("A user class")` |
| `type()` | `FieldType` | Returns the type of the class | `my_cls.type()` |

### ClassPropertyBuilder

`ClassPropertyBuilder` is a type that represents a property in a class. It can be used to add descriptions, constraints, and other metadata to a property.


| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `description(description: string)` | `ClassPropertyBuilder` | Adds a description to the property | `my_prop.description("An email address")` |
| `alias(alias: string)` | `ClassPropertyBuilder` | Adds the alias attribute to the property | `my_prop.alias("email_address")` |

### EnumBuilder

`EnumBuilder` is a type that represents an enum in a type. It can be used to add values to an enum.

| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `add_value(value: string)` | `EnumValueBuilder` | Adds a value to the enum | `my_enum.add_value("VALUE1")` |
| `description(description: string)` | `EnumBuilder` | Adds a description to the enum value | `my_enum.description("A value in the enum")` |
| `type()` | `FieldType` | Returns the type of the enum | `my_enum.type()` |

### EnumValueBuilder

`EnumValueBuilder` is a type that represents a value in an enum. It can be used to add descriptions, constraints, and other metadata to a value.

| Method | Returns | Description | Example |
|--------|---------|-------------|---------|
| `description(description: string)` | `EnumValueBuilder` | Adds a description to the enum value | `my_value.description("A value in the enum")` |
| `alias(alias: string)` | `EnumValueBuilder` | Adds the alias attribute to the enum value | `my_value.alias("VALUE1")` |
| `skip()` | `EnumValueBuilder` | Adds the skip attribute to the enum value | `my_value.skip()` |


## Adding Descriptions

Expand Down
Loading

0 comments on commit c913f09

Please sign in to comment.