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