Skip to content

Commit 5d6f66e

Browse files
committed
upate assistant script
1 parent 0d2ee9b commit 5d6f66e

14 files changed

+320
-432
lines changed

code365scripts.openai/Public/Assistant/Get-ThreadMessage.ps1 renamed to code365scripts.openai/Private/Assistant/Get-ThreadMessage.ps1

File renamed without changes.

code365scripts.openai/Public/Assistant/New-Assistant.ps1 renamed to code365scripts.openai/Private/Assistant/New-Assistant.ps1

File renamed without changes.

code365scripts.openai/Public/Assistant/New-AssistantConversation.ps1 renamed to code365scripts.openai/Private/Assistant/New-AssistantConversation.ps1

File renamed without changes.

code365scripts.openai/Public/Assistant/New-ThreadMessage.ps1 renamed to code365scripts.openai/Private/Assistant/New-ThreadMessage.ps1

File renamed without changes.

code365scripts.openai/Public/Assistant/New-ThreadRun.ps1 renamed to code365scripts.openai/Private/Assistant/New-ThreadRun.ps1

File renamed without changes.
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
class OpenAIClient {
2+
[System.Management.Automation.HiddenAttribute()]
3+
[string]$apikey
4+
[System.Management.Automation.HiddenAttribute()]
5+
[string]$baseUri
6+
[System.Management.Automation.HiddenAttribute()]
7+
[string]$model
8+
[System.Management.Automation.HiddenAttribute()]
9+
[hashtable]$headers
10+
[System.Management.Automation.HiddenAttribute()]
11+
[string]$apiVersion
12+
13+
[Assistant]$assistants
14+
[Vector_store]$vector_stores
15+
[File]$files
16+
[Thread]$threads
17+
18+
OpenAIClient([string]$apiKey, [string]$baseUri, [string]$model, [string]$apiVersion) {
19+
$this.apikey = $apiKey
20+
$this.baseUri = $baseUri
21+
$this.model = $model
22+
$this.apiVersion = $apiVersion
23+
24+
$this.init()
25+
}
26+
[System.Management.Automation.HiddenAttribute()]
27+
[void]init() {
28+
# check the apikey, endpoint and model, if empty, then return error
29+
$this.headers = @{
30+
"Content-Type" = "application/json"
31+
"OpenAI-Beta" = "assistants=v2"
32+
}
33+
34+
if ($this.baseUri -match "azure") {
35+
$this.headers.Add("api-key", $this.apikey)
36+
$this.baseUri = $this.baseUri + "openai/"
37+
}
38+
else {
39+
$this.headers.Add("Authorization", "Bearer $($this.apikey)")
40+
$this.apiVersion = ""
41+
}
42+
43+
$this.assistants = [Assistant]::new($this)
44+
$this.vector_stores = [Vector_store]::new($this)
45+
$this.files = [File]::new($this)
46+
$this.threads = [Thread]::new($this)
47+
}
48+
49+
[psobject]web(
50+
[string]$urifragment,
51+
[string]$method = "GET",
52+
[psobject]$body = $null) {
53+
$url = "{0}{1}" -f $this.baseUri, $urifragment
54+
if ($this.apiVersion -ne "") {
55+
$url = "{0}?api-version={1}" -f $url, $this.apiVersion
56+
}
57+
58+
if ($method -eq "GET" -or $null -eq $body) {
59+
return Invoke-RestMethod -Method $method -Uri $url -Headers $this.headers
60+
}
61+
else {
62+
return Invoke-RestMethod -Method $method -Uri $url -Headers $this.headers -Body ($body | ConvertTo-Json)
63+
}
64+
}
65+
66+
[psobject]web($urifragment) {
67+
return $this.web($urifragment, "GET", @{})
68+
}
69+
}
70+
71+
class AssistantResource {
72+
[System.Management.Automation.HiddenAttribute()]
73+
[OpenAIClient]$Client
74+
[System.Management.Automation.HiddenAttribute()]
75+
[string]$urifragment
76+
77+
AssistantResource([OpenAIClient]$client, [string]$urifragment) {
78+
$this.Client = $client
79+
$this.urifragment = $urifragment
80+
}
81+
[psobject[]]list() {
82+
return $this.Client.web($this.urifragment).data
83+
}
84+
85+
[psobject]get([string]$id) {
86+
return $this.Client.web("$($this.urifragment)/$id")
87+
}
88+
89+
[psobject]delete([string]$id) {
90+
return $this.Client.web("$($this.urifragment)/$id", "DELETE", @{})
91+
}
92+
93+
[psobject]create([hashtable]$body) {
94+
return $this.Client.web("$($this.urifragment)", "POST", $body)
95+
}
96+
}
97+
98+
class Assistant:AssistantResource {
99+
Assistant([OpenAIClient]$client): base($client, "assistants") {}
100+
101+
<#
102+
.SYNOPSIS
103+
Create a new assistant
104+
.DESCRIPTION
105+
Create a new assistant with the given name, model, and instructions.
106+
.PARAMETER body
107+
The body must contain 'name', 'model', and 'instructions' keys. But it can also contain 'config', 'vector_store_ids', 'functions', and 'files' keys.
108+
#>
109+
[psobject]create([hashtable]$body) {
110+
if ($body.name -and $body.model -and $body.instructions) {
111+
$vector_store_ids = $body.vector_store_ids
112+
$functions = $body.functions
113+
$files = $body.files
114+
$config = $body.config
115+
116+
if ($files) {
117+
# upload the files and create new vector store
118+
$file_ids = $this.Client.files.create(@{ "files" = $files }) | Select-Object -ExpandProperty id
119+
$body.Add("tools", @(
120+
@{
121+
"type" = "file_search"
122+
}))
123+
124+
$body.Add("tool_resources", @{
125+
"file_search" = @{
126+
"vector_stores" = @(@{
127+
file_ids = @($file_ids)
128+
})
129+
}
130+
})
131+
}
132+
133+
if ($vector_store_ids -and $vector_store_ids.Count -gt 0) {
134+
$body.Add("tool_resources", @{
135+
"file_search" = @{
136+
"vector_store_ids" = @($vector_store_ids)
137+
}
138+
})
139+
140+
$body.Add("tools", @(
141+
@{
142+
"type" = "file_search"
143+
}))
144+
}
145+
146+
if ($functions -and $functions.Count -gt 0) {
147+
148+
if ($null -eq $body.tools) {
149+
$body.Add("tools", @())
150+
}
151+
152+
$functions | ForEach-Object {
153+
$func = Get-FunctionJson -functionName $_
154+
$body.tools += $func
155+
}
156+
}
157+
158+
if ($config) {
159+
Merge-Hashtable -table1 $body -table2 $config
160+
}
161+
162+
# remove files, vector_store_ids, functions, and config from the body
163+
$body.Remove("files")
164+
$body.Remove("vector_store_ids")
165+
$body.Remove("functions")
166+
$body.Remove("config")
167+
168+
return $this.Client.web("$($this.urifragment)", "POST", $body)
169+
}
170+
171+
throw "The body must contain 'name' and 'model', 'instructions' keys."
172+
}
173+
}
174+
class Vector_store:AssistantResource {
175+
Vector_store([OpenAIClient]$client): base($client, "vector_stores") {}
176+
177+
[psobject]create([hashtable]$body) {
178+
<#
179+
.SYNOPSIS
180+
Create a new vector store
181+
.DESCRIPTION
182+
Create a new vector store with the given name, file_ids, and days_to_expire.
183+
.PARAMETER body
184+
The body must contain 'name', 'file_ids', and 'days_to_expire' keys.
185+
#>
186+
187+
# check if the body contains name, file_ids, and days_to_expire
188+
if ($body.name -and $body.file_ids -and $body.days_to_expire) {
189+
#replace the days_to_expire with expires_after
190+
$body.expires_after = @{
191+
"days" = $body.days_to_expire
192+
"anchor" = "last_active_at"
193+
}
194+
$body.Remove("days_to_expire")
195+
return $this.Client.web("$($this.urifragment)", "POST", $body)
196+
}
197+
198+
throw "The body must contain 'name', 'file_ids', and 'days_to_expire' keys."
199+
}
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") {}
292+
}

0 commit comments

Comments
 (0)