Skip to content

Commit cd25160

Browse files
committed
Add S3 support for video processing in Converse API
1 parent be6e009 commit cd25160

10 files changed

Lines changed: 583 additions & 28 deletions

.vscode/settings.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"luma",
4545
"mixtral",
4646
"Mogh",
47+
"mybucket",
4748
"notcontains",
4849
"notlike",
4950
"OTBS",
@@ -55,6 +56,7 @@
5556
"starships",
5657
"Stroustrup",
5758
"toolspec",
59+
"tooluse",
5860
"webp",
5961
"whitespaces",
6062
"Worf",

docs/Invoke-ConverseAPI.md

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,19 @@ Invoke-ConverseAPI -ModelID <String> [-Message <String>] [-DocumentPath <String[
3939
[-SessionToken <String>] [<CommonParameters>]
4040
```
4141

42+
### MessageS3VideoSet
43+
44+
```powershell
45+
Invoke-ConverseAPI -ModelID <String> [-Message <String>] -S3Location <String> [-S3BucketOwner <String>]
46+
[-ReturnFullObject] [-NoContextPersist] [-MaxTokens <Int32>] [-StopSequences <String[]>]
47+
[-Temperature <Single>] [-TopP <Single>] [-SystemPrompt <String>] [-Tools <PSObject[]>] [-ToolChoice <String>]
48+
[-ToolName <String>] [-GuardrailID <String>] [-GuardrailVersion <String>] [-GuardrailTrace <String>]
49+
[-AdditionalModelRequestField <PSObject>] [-AdditionalModelResponseFieldPath <String[]>] [-AccessKey <String>]
50+
[-Credential <AWSCredentials>] [-EndpointUrl <String>] [-NetworkCredential <PSCredential>]
51+
[-ProfileLocation <String>] [-ProfileName <String>] [-Region <Object>] [-SecretKey <String>]
52+
[-SessionToken <String>] [<CommonParameters>]
53+
```
54+
4255
### MessageVideoSet
4356

4457
```powershell
@@ -192,6 +205,23 @@ The model will describe the video in the video file.
192205

193206
### EXAMPLE 8
194207

208+
```powershell
209+
$invokeConverseAPISplat = @{
210+
Message = 'Please describe the video in the attached video.'
211+
S3Location = 's3://mybucket/myvideo.mp4'
212+
ModelID = 'amazon.nova-pro-v1:0'
213+
ReturnFullObject = $true
214+
Credential = $awsCredential
215+
Region = 'us-west-2'
216+
}
217+
Invoke-ConverseAPI @invokeConverseAPISplat
218+
```
219+
220+
Sends a video vision message to the on-demand specified model via the Converse API.
221+
The model will describe the video in the S3 location.
222+
223+
### EXAMPLE 9
224+
195225
```powershell
196226
$invokeConverseAPISplat = @{
197227
Message = 'Provide a one sentence summary of the document.'
@@ -206,7 +236,7 @@ Invoke-ConverseAPI @invokeConverseAPISplat
206236
Sends a document message to the on-demand specified model via the Converse API.
207237
The model will provide a one sentence summary of the document.
208238

209-
### EXAMPLE 9
239+
### EXAMPLE 10
210240

211241
```powershell
212242
$tools = [PSCustomObject]@{
@@ -241,7 +271,7 @@ A tool is provided to answer the user's question.
241271
Additional parameters are provided to require the use of the tool and to specify the tool to use.
242272
This will prompt the model to return a tool-based response.
243273

244-
### EXAMPLE 10
274+
### EXAMPLE 11
245275

246276
```powershell
247277
$tools = [PSCustomObject]@{
@@ -307,7 +337,7 @@ The message to be sent to the model.
307337
308338
```yaml
309339
Type: String
310-
Parameter Sets: MessageOnlySet, MessageDocumentSet, MessageVideoSet, MessageImageSet
340+
Parameter Sets: MessageOnlySet, MessageDocumentSet, MessageS3VideoSet, MessageVideoSet, MessageImageSet
311341
Aliases:
312342

313343
Required: False
@@ -351,6 +381,38 @@ Accept pipeline input: False
351381
Accept wildcard characters: False
352382
```
353383
384+
### -S3Location
385+
386+
The location of a video object in an Amazon S3 bucket.
387+
388+
```yaml
389+
Type: String
390+
Parameter Sets: MessageS3VideoSet
391+
Aliases:
392+
393+
Required: True
394+
Position: Named
395+
Default value: None
396+
Accept pipeline input: False
397+
Accept wildcard characters: False
398+
```
399+
400+
### -S3BucketOwner
401+
402+
If the bucket belongs to another AWS account, specify that accounts ID.
403+
404+
```yaml
405+
Type: String
406+
Parameter Sets: MessageS3VideoSet
407+
Aliases:
408+
409+
Required: False
410+
Position: Named
411+
Default value: None
412+
Accept pipeline input: False
413+
Accept wildcard characters: False
414+
```
415+
354416
### -DocumentPath
355417
356418
File path to local document.

src/Tests/Unit/Private/Format-ConverseAPI.Tests.ps1

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,24 @@ InModuleScope 'pwshBedrock' {
157157
} | Should -Throw
158158
} #it
159159

160+
It 'should throw if Get-S3Extension returns null' {
161+
Mock -CommandName Get-S3Extension -MockWith {
162+
return $null
163+
} #endMock
164+
{
165+
$formatConverseAPISplat = @{
166+
Role = 'user'
167+
Message = 'Live long and prosper!'
168+
ModelID = 'Converse'
169+
S3Location = 's3://my-bucket/path/to/video'
170+
}
171+
Format-ConverseAPI @formatConverseAPISplat
172+
} | Should -Throw
173+
} #it
174+
160175
} #context_Error
161176

162177
Context 'Success' {
163-
164178
BeforeEach {
165179
Reset-ModelContext -AllModels -Force
166180
Mock -CommandName Get-Item -MockWith {
@@ -169,6 +183,9 @@ InModuleScope 'pwshBedrock' {
169183
Mock -CommandName Convert-MediaToMemoryStream -MockWith {
170184
[System.IO.MemoryStream]::new()
171185
} #endMock
186+
Mock -CommandName Get-S3Extension -MockWith {
187+
return 'mp4'
188+
} #endMock
172189
$toolResult = [PSCustomObject]@{
173190
ToolUseId = 'tooluse_ihA1_9blR3S1QJixGq5gwg'
174191
Content = [PSCustomObject]@{
@@ -331,7 +348,49 @@ InModuleScope 'pwshBedrock' {
331348
$result.Content.Video | Should -BeOfType 'Amazon.BedrockRuntime.Model.VideoBlock'
332349
$result.Content.Video.Source | Should -BeOfType 'Amazon.BedrockRuntime.Model.VideoSource'
333350
$result.Content.Video.Format.Value | Should -BeExactly 'mp4'
334-
$result.Content.Video.Source.Bytes | Should -BeOfType 'System.IO.MemoryStream'
351+
$result.Content.Video.Source.Bytes | Should -BeOfType 'System.IO.MemoryStream' } #it
352+
353+
It 'should return a Amazon.BedrockRuntime.Model.VideoBlock with the expected values if S3Location is provided' {
354+
$formatConverseAPISplat = @{
355+
Role = 'user'
356+
ModelID = 'Converse'
357+
S3Location = 's3://my-bucket/path/to/video.mp4'
358+
}
359+
$result = Format-ConverseAPI @formatConverseAPISplat
360+
$result | Should -BeOfType 'Amazon.BedrockRuntime.Model.Message'
361+
$result.Content.Video | Should -BeOfType 'Amazon.BedrockRuntime.Model.VideoBlock'
362+
$result.Content.Video.Source | Should -BeOfType 'Amazon.BedrockRuntime.Model.VideoSource'
363+
$result.Content.Video.Format.Value | Should -BeExactly 'mp4'
364+
$result.Content.Video.Source.S3Location | Should -BeOfType 'Amazon.BedrockRuntime.Model.S3Location'
365+
$result.Content.Video.Source.S3Location.Uri | Should -BeExactly 's3://my-bucket/path/to/video.mp4'
366+
} #it
367+
368+
It 'should include S3BucketOwner in the S3Location if provided' {
369+
$formatConverseAPISplat = @{
370+
Role = 'user'
371+
ModelID = 'Converse'
372+
S3Location = 's3://my-bucket/path/to/video.mp4'
373+
S3BucketOwner = '123456789012'
374+
}
375+
$result = Format-ConverseAPI @formatConverseAPISplat
376+
$result | Should -BeOfType 'Amazon.BedrockRuntime.Model.Message'
377+
$result.Content.Video | Should -BeOfType 'Amazon.BedrockRuntime.Model.VideoBlock'
378+
$result.Content.Video.Source.S3Location.BucketOwner | Should -BeExactly '123456789012'
379+
} #it
380+
381+
It 'should convert jpg to jpeg in S3Location' {
382+
Mock -CommandName Get-S3Extension -MockWith {
383+
return 'jpg'
384+
} #endMock
385+
$formatConverseAPISplat = @{
386+
Role = 'user'
387+
ModelID = 'Converse'
388+
S3Location = 's3://my-bucket/path/to/image.jpg'
389+
}
390+
$result = Format-ConverseAPI @formatConverseAPISplat
391+
$result | Should -BeOfType 'Amazon.BedrockRuntime.Model.Message'
392+
$result.Content.Video | Should -BeOfType 'Amazon.BedrockRuntime.Model.VideoBlock'
393+
$result.Content.Video.Format.Value | Should -BeExactly 'jpeg'
335394
} #it
336395

337396
It 'should return the expected results if multiple media is provided' {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
BeforeDiscovery {
2+
Set-Location -Path $PSScriptRoot
3+
$ModuleName = 'pwshBedrock'
4+
$PathToManifest = [System.IO.Path]::Combine('..', '..', '..', $ModuleName, "$ModuleName.psd1")
5+
$script:assetPath = [System.IO.Path]::Combine('..', 'assets')
6+
#if the module is already in memory, remove it
7+
Get-Module $ModuleName -ErrorAction SilentlyContinue | Remove-Module -Force
8+
Import-Module $PathToManifest -Force
9+
}
10+
11+
12+
InModuleScope 'pwshBedrock' {
13+
Describe 'Get-S3Extension Private Function Tests' -Tag Unit {
14+
BeforeAll {
15+
$WarningPreference = 'SilentlyContinue'
16+
$ErrorActionPreference = 'SilentlyContinue'
17+
$validS3Uri = 's3://my-bucket/path/to/file.png'
18+
$validS3UriWithParams = 's3://my-bucket/path/to/file.jpg?param1=value1&param2=value2'
19+
$invalidS3Uri = 'not-a-uri'
20+
$s3UriWithoutExtension = 's3://my-bucket/path/to/file'
21+
$emptyS3Uri = ''
22+
} #beforeAll
23+
24+
Context 'Error' {
25+
26+
It 'Should throw when S3Location is empty' {
27+
$result = { Get-S3Extension -S3Location $emptyS3Uri }
28+
$result | Should -Throw
29+
} #it
30+
31+
It 'Should return $null when S3Location is invalid' {
32+
$result = Get-S3Extension -S3Location $invalidS3Uri
33+
$result | Should -BeNullOrEmpty
34+
} #it
35+
36+
It 'Should return $null when S3Location has no file extension' {
37+
$result = Get-S3Extension -S3Location $s3UriWithoutExtension
38+
$result | Should -BeNullOrEmpty
39+
} #it
40+
} #context_Error
41+
42+
Context 'Success' {
43+
It 'Should return "png" for a valid S3 URI with PNG extension' {
44+
$result = Get-S3Extension -S3Location $validS3Uri
45+
$result | Should -Be 'png'
46+
} #it
47+
48+
It 'Should return "jpg" for a valid S3 URI with JPG extension and query parameters' {
49+
$result = Get-S3Extension -S3Location $validS3UriWithParams
50+
$result | Should -Be 'jpg'
51+
} #it
52+
53+
It 'Should extract extensions from different S3 URIs correctly' {
54+
$testCases = @(
55+
@{ Uri = 's3://my-bucket/images/photo.jpeg'; Expected = 'jpeg' }
56+
@{ Uri = 's3://my-bucket/documents/report.pdf'; Expected = 'pdf' }
57+
@{ Uri = 's3://my-bucket/videos/movie.mp4'; Expected = 'mp4' }
58+
@{ Uri = 's3://my-bucket/nested/path/to/file.txt'; Expected = 'txt' }
59+
)
60+
61+
foreach ($test in $testCases) {
62+
$result = Get-S3Extension -S3Location $test.Uri
63+
$result | Should -Be $test.Expected
64+
}
65+
} #it
66+
} #context_Success
67+
68+
} #describe_Get-S3Extension
69+
} #inModule

src/Tests/Unit/Private/Test-ConverseAPIVideo.Tests.ps1

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,26 @@ InModuleScope 'pwshBedrock' {
9595
$result | Should -Not -BeNullOrEmpty
9696
} #it
9797

98+
It 'Should return true for supported extension <_>' -ForEach $supportedMediaExtensions {
99+
Mock -CommandName Write-Verbose {}
100+
$result = Test-ConverseAPIVideo -Extension $_
101+
$result | Should -Be $true
102+
} #it
103+
104+
It 'Should return true for supported extension <_> in lowercase' -ForEach $supportedMediaExtensions {
105+
Mock -CommandName Write-Verbose {}
106+
$extensionLower = $_.ToLower()
107+
$result = Test-ConverseAPIVideo -Extension $extensionLower
108+
$result | Should -Be $true
109+
} #it
110+
111+
It 'Should return false for unsupported extension' {
112+
Mock -CommandName Write-Warning {}
113+
$extension = 'txt'
114+
$result = Test-ConverseAPIVideo -Extension $extension
115+
$result | Should -Be $false
116+
} #it
117+
98118
} #context_Success
99119

100120
} #describe_Test-ConverseAPIVideo

0 commit comments

Comments
 (0)