Skip to content

Commit 10f4a22

Browse files
committed
update
1 parent 5d6f66e commit 10f4a22

File tree

3 files changed

+161
-114
lines changed

3 files changed

+161
-114
lines changed

.vscode/settings.json

Lines changed: 0 additions & 3 deletions
This file was deleted.

code365scripts.openai/Private/Assistant/OpenAIClient.ps1 renamed to code365scripts.openai/Types/OpenAIClient.ps1

Lines changed: 159 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class OpenAIClient {
5959
return Invoke-RestMethod -Method $method -Uri $url -Headers $this.headers
6060
}
6161
else {
62-
return Invoke-RestMethod -Method $method -Uri $url -Headers $this.headers -Body ($body | ConvertTo-Json)
62+
return Invoke-RestMethod -Method $method -Uri $url -Headers $this.headers -Body ($body | ConvertTo-Json -Depth 10)
6363
}
6464
}
6565

@@ -73,16 +73,30 @@ class AssistantResource {
7373
[OpenAIClient]$Client
7474
[System.Management.Automation.HiddenAttribute()]
7575
[string]$urifragment
76+
[System.Management.Automation.HiddenAttribute()]
77+
[string]$objTypeName
7678

77-
AssistantResource([OpenAIClient]$client, [string]$urifragment) {
79+
AssistantResource([OpenAIClient]$client, [string]$urifragment, [string]$objTypeName) {
7880
$this.Client = $client
7981
$this.urifragment = $urifragment
82+
$this.objTypeName = $objTypeName
8083
}
8184
[psobject[]]list() {
85+
86+
if ($this.objTypeName) {
87+
return $this.Client.web($this.urifragment).data | ForEach-Object {
88+
New-Object -TypeName $this.objTypeName -ArgumentList $_
89+
}
90+
}
91+
8292
return $this.Client.web($this.urifragment).data
8393
}
8494

8595
[psobject]get([string]$id) {
96+
if ($this.objTypeName) {
97+
return New-Object -TypeName $this.objTypeName -ArgumentList $this.Client.web("$($this.urifragment)/$id")
98+
}
99+
86100
return $this.Client.web("$($this.urifragment)/$id")
87101
}
88102

@@ -91,12 +105,123 @@ class AssistantResource {
91105
}
92106

93107
[psobject]create([hashtable]$body) {
108+
if ($this.objTypeName) {
109+
return New-Object -TypeName $this.objTypeName -ArgumentList $this.Client.web("$($this.urifragment)", "POST", $body)
110+
}
94111
return $this.Client.web("$($this.urifragment)", "POST", $body)
95112
}
113+
114+
[psobject]create() {
115+
return $this.create(@{})
116+
}
117+
118+
119+
[void]clear() {
120+
# warn user this is very dangerous action, it will remove all the instance, and ask for confirmation
121+
$confirm = Read-Host "Are you sure you want to remove all the instances? (yes/no)"
122+
if ($confirm -ne "yes" -and $confirm -ne "y") {
123+
return
124+
}
125+
# get all the instances and remove it
126+
$this.list() | ForEach-Object {
127+
$this.delete($_.id)
128+
}
129+
}
130+
}
131+
132+
class File:AssistantResource {
133+
File([OpenAIClient]$client): base($client, "files", $null) {}
134+
135+
[psobject]create([hashtable]$body) {
136+
if ($body.files) {
137+
$files = $body.files
138+
return $this.upload($files)
139+
}
140+
141+
throw "The body must contain 'files' key."
142+
}
143+
144+
145+
[System.Management.Automation.HiddenAttribute()]
146+
[psobject]upload([string[]]$fullname) {
147+
$url = "{0}{1}" -f $this.Client.baseUri, $this.urifragment
148+
if ($this.Client.baseUri -match "azure") {
149+
$url = "{0}?api-version=2024-05-01-preview" -f $url
150+
}
151+
152+
$result = @()
153+
154+
foreach ($file in $fullname) {
155+
Write-Host "process file: $file"
156+
# Define the purpose (e.g., "assistants", "vision", "batch", or "fine-tune")
157+
$purpose = "assistants"
158+
# Create a new web request
159+
$request = [System.Net.WebRequest]::Create($url)
160+
$request.Method = "POST"
161+
162+
# add the item of headers to request.Headers
163+
$this.Client.headers.GetEnumerator() | Where-Object {
164+
$_.Key -ne "Content-Type"
165+
} | ForEach-Object {
166+
$request.Headers.Add($_.Key, $_.Value)
167+
}
168+
169+
# Create a boundary for the multipart/form-data content
170+
$boundary = [System.Guid]::NewGuid().ToString()
171+
172+
# Set the content type and boundary
173+
$request.ContentType = "multipart/form-data; boundary=$boundary"
174+
175+
$name = "{0}-{1}" -f (Get-FileHash $file).Hash, (Split-Path $file -Leaf)
176+
177+
# Create the request body
178+
$body = @"
179+
--$boundary
180+
Content-Disposition: form-data; name="file"; filename="$name"
181+
Content-Type: application/octet-stream
182+
183+
$(Get-Content -Path $file)
184+
185+
--$boundary
186+
Content-Disposition: form-data; name="purpose"
187+
188+
$purpose
189+
--$boundary--
190+
"@
191+
192+
193+
# Convert the body to bytes
194+
$bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($body)
195+
196+
# Set the content length
197+
$request.ContentLength = $bodyBytes.Length
198+
199+
# Get the request stream and write the body
200+
$requestStream = $request.GetRequestStream()
201+
$requestStream.Write($bodyBytes, 0, $bodyBytes.Length)
202+
$requestStream.Close()
203+
204+
# Get the response
205+
$response = $request.GetResponse()
206+
207+
# Read the response content
208+
$responseStream = $response.GetResponseStream()
209+
$reader = [System.IO.StreamReader]::new($responseStream)
210+
$responseContent = $reader.ReadToEnd()
211+
$reader.Close()
212+
213+
# Print the response content
214+
$result += ($responseContent | ConvertFrom-Json)
215+
}
216+
return $result
217+
}
218+
219+
96220
}
97221

98222
class Assistant:AssistantResource {
99-
Assistant([OpenAIClient]$client): base($client, "assistants") {}
223+
Assistant([OpenAIClient]$client): base($client, "assistants", "AssistantObject") {}
224+
100225

101226
<#
102227
.SYNOPSIS
@@ -106,7 +231,7 @@ class Assistant:AssistantResource {
106231
.PARAMETER body
107232
The body must contain 'name', 'model', and 'instructions' keys. But it can also contain 'config', 'vector_store_ids', 'functions', and 'files' keys.
108233
#>
109-
[psobject]create([hashtable]$body) {
234+
[AssistantObject]create([hashtable]$body) {
110235
if ($body.name -and $body.model -and $body.instructions) {
111236
$vector_store_ids = $body.vector_store_ids
112237
$functions = $body.functions
@@ -123,7 +248,8 @@ class Assistant:AssistantResource {
123248

124249
$body.Add("tool_resources", @{
125250
"file_search" = @{
126-
"vector_stores" = @(@{
251+
"vector_stores" = @(
252+
@{
127253
file_ids = @($file_ids)
128254
})
129255
}
@@ -165,14 +291,40 @@ class Assistant:AssistantResource {
165291
$body.Remove("functions")
166292
$body.Remove("config")
167293

168-
return $this.Client.web("$($this.urifragment)", "POST", $body)
294+
return [AssistantObject]::new($this.Client.web("$($this.urifragment)", "POST", $body))
169295
}
170296

171297
throw "The body must contain 'name' and 'model', 'instructions' keys."
172298
}
173299
}
300+
301+
302+
class AssistantResourceObject {
303+
AssistantResourceObject([psobject]$data) {
304+
# check all the properties and assign it to the object
305+
$data.PSObject.Properties | ForEach-Object {
306+
$this | Add-Member -MemberType NoteProperty -Name $_.Name -Value $_.Value
307+
}
308+
}
309+
}
310+
311+
class AssistantObject:AssistantResourceObject {
312+
AssistantObject([psobject]$data):base($data) {}
313+
[void]chat() {
314+
Write-Host "chat with the assistant : $($this.id)"
315+
}
316+
}
317+
318+
319+
class ThreadObject:AssistantResourceObject {
320+
ThreadObject([psobject]$data):base($data) {}
321+
}
322+
class Thread:AssistantResource {
323+
Thread([OpenAIClient]$client): base($client, "threads", "ThreadObject") {}
324+
}
325+
174326
class Vector_store:AssistantResource {
175-
Vector_store([OpenAIClient]$client): base($client, "vector_stores") {}
327+
Vector_store([OpenAIClient]$client): base($client, "vector_stores", $null) {}
176328

177329
[psobject]create([hashtable]$body) {
178330
<#
@@ -197,96 +349,4 @@ class Vector_store:AssistantResource {
197349

198350
throw "The body must contain 'name', 'file_ids', and 'days_to_expire' keys."
199351
}
200-
}
201-
class File:AssistantResource {
202-
File([OpenAIClient]$client): base($client, "files") {}
203-
204-
[psobject]create([hashtable]$body) {
205-
if ($body.files) {
206-
$files = $body.files
207-
return $this.upload($files)
208-
}
209-
210-
throw "The body must contain 'files' key."
211-
}
212-
213-
214-
[System.Management.Automation.HiddenAttribute()]
215-
[psobject]upload([string[]]$fullname) {
216-
$url = "{0}{1}" -f $this.Client.baseUri, $this.urifragment
217-
if ($this.Client.baseUri -match "azure") {
218-
$url = "{0}?api-version=2024-05-01-preview" -f $url
219-
}
220-
221-
$result = @()
222-
223-
foreach ($file in $fullname) {
224-
Write-Host "process file: $file"
225-
# Define the purpose (e.g., "assistants", "vision", "batch", or "fine-tune")
226-
$purpose = "assistants"
227-
# Create a new web request
228-
$request = [System.Net.WebRequest]::Create($url)
229-
$request.Method = "POST"
230-
231-
# add the item of headers to request.Headers
232-
$this.Client.headers.GetEnumerator() | Where-Object {
233-
$_.Key -ne "Content-Type"
234-
} | ForEach-Object {
235-
$request.Headers.Add($_.Key, $_.Value)
236-
}
237-
238-
# Create a boundary for the multipart/form-data content
239-
$boundary = [System.Guid]::NewGuid().ToString()
240-
241-
# Set the content type and boundary
242-
$request.ContentType = "multipart/form-data; boundary=$boundary"
243-
244-
$name = "{0}-{1}" -f (Get-FileHash $file).Hash, (Split-Path $file -Leaf)
245-
246-
# Create the request body
247-
$body = @"
248-
--$boundary
249-
Content-Disposition: form-data; name="file"; filename="$name"
250-
Content-Type: application/octet-stream
251-
252-
$(Get-Content -Path $file)
253-
254-
--$boundary
255-
Content-Disposition: form-data; name="purpose"
256-
257-
$purpose
258-
--$boundary--
259-
"@
260-
261-
262-
# Convert the body to bytes
263-
$bodyBytes = [System.Text.Encoding]::UTF8.GetBytes($body)
264-
265-
# Set the content length
266-
$request.ContentLength = $bodyBytes.Length
267-
268-
# Get the request stream and write the body
269-
$requestStream = $request.GetRequestStream()
270-
$requestStream.Write($bodyBytes, 0, $bodyBytes.Length)
271-
$requestStream.Close()
272-
273-
# Get the response
274-
$response = $request.GetResponse()
275-
276-
# Read the response content
277-
$responseStream = $response.GetResponseStream()
278-
$reader = [System.IO.StreamReader]::new($responseStream)
279-
$responseContent = $reader.ReadToEnd()
280-
$reader.Close()
281-
282-
# Print the response content
283-
$result += ($responseContent | ConvertFrom-Json)
284-
}
285-
return $result
286-
}
287-
288-
289-
}
290-
class Thread:AssistantResource {
291-
Thread([OpenAIClient]$client): base($client, "threads") {}
292352
}

code365scripts.openai/code365scripts.openai.psm1

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,14 @@
11
Import-LocalizedData -FileName "resources.psd1" -BindingVariable "resources"
22

3-
foreach ($directory in @('Public', 'Private')) {
3+
4+
foreach ($directory in @('Types','Public', 'Private')) {
45

56
$path = Join-Path -Path $PSScriptRoot -ChildPath $directory
67
if (Test-Path $path) {
78
Get-ChildItem -Path $path -Filter "*.ps1" -File -Recurse | ForEach-Object { . $_.FullName }
89
}
910
}
1011

11-
12-
# check if the "$home\.openai-powershell\profile.ps1" exists, if so, source it, otherwise create it and append some code
13-
# $profilePath = "$home\.openai-powershell\profile.ps1"
14-
# if (Test-Path $profilePath) {
15-
# . $profilePath
16-
# }
17-
# else {
18-
# New-Item -Path $profilePath -ItemType File -Force
19-
# Add-Content -Path $profilePath -Value (Get-Content "$PSScriptRoot\private\profile.ps1" -Raw)
20-
# }
21-
2212
# check if the "$home\.openai-powershell\profile.json" exists, if so, read the file, and register a ArgumentCompleter for New-ChatCompletions, and New-ChatGPTConversation functions, the parameter name is environment
2313

2414
$profilePath = "$($env:USERPROFILE)\.openai-powershell\profile.json"

0 commit comments

Comments
 (0)