wip: add tests for openai client

This commit is contained in:
2024-12-29 18:01:27 +02:00
parent df63231db5
commit 3c9f86613d
11 changed files with 266 additions and 28 deletions

View File

@@ -110,3 +110,41 @@ fn (mut f OpenAI) create_audio_request(args AudioArgs, endpoint string) !AudioRe
} }
return json.decode(AudioResponse, r.body)! return json.decode(AudioResponse, r.body)!
} }
@[params]
pub struct CreateSpeechArgs {
pub:
model ModelType = .tts_1
input string @[required]
voice Voice = .alloy
response_format AudioFormat = .mp3
speed f32 = 1.0
output_path string @[required]
}
pub struct CreateSpeechRequest {
pub:
model string
input string
voice string
response_format string
speed f32
}
pub fn (mut f OpenAI) create_speech(args CreateSpeechArgs) ! {
mut output_file := os.open_file(args.output_path, 'w+')!
req := CreateSpeechRequest{
model: modelname_str(args.model)
input: args.input
voice: voice_str(args.voice)
response_format: audio_format_str(args.response_format)
speed: args.speed
}
data := json.encode(req)
mut conn := f.connection()!
r := conn.post_json_str(prefix: 'audio/speech', data: data)!
output_file.write(r.bytes())!
}

View File

@@ -0,0 +1,105 @@
module openai
import os
fn test_chat_completion() {
key := os.getenv('OPENAI_API_KEY')
heroscript := '!!openai.configure api_key: "${key}"'
play(heroscript: heroscript)!
mut client := get()!
res := client.chat_completion(.gpt_4o_2024_08_06, Messages{
messages: [
Message{
role: .user
content: 'Say these words exactly as i write them with no punctuation: AI is getting out of hand'
},
]
})!
assert res.choices.len == 1
assert res.choices[0].message.content == 'AI is getting out of hand'
}
fn test_embeddings() {
key := os.getenv('OPENAI_API_KEY')
heroscript := '!!openai.configure api_key: "${key}"'
play(heroscript: heroscript)!
mut client := get()!
res := client.create_embeddings(
input: ['The food was delicious and the waiter..']
model: .text_embedding_ada
)!
assert res.data.len == 1
assert res.data[0].embedding.len == 1536
}
fn test_files() {
key := os.getenv('OPENAI_API_KEY')
heroscript := '!!openai.configure api_key: "${key}"'
play(heroscript: heroscript)!
mut client := get()!
uploaded_file := client.upload_file(
filepath: '${os.dir(@FILE) + '/testdata/testfile.txt'}'
purpose: .assistants
)!
assert uploaded_file.filename == 'testfile.txt'
assert uploaded_file.purpose == 'assistants'
got_file := client.get_file(uploaded_file.id)!
assert got_file == uploaded_file
uploaded_file2 := client.upload_file(
filepath: '${os.dir(@FILE) + '/testdata/testfile2.txt'}'
purpose: .assistants
)!
assert uploaded_file2.filename == 'testfile2.txt'
assert uploaded_file2.purpose == 'assistants'
mut got_list := client.list_files()!
assert got_list.data.len >= 2 // there could be other older files
mut ids := []string{}
for file in got_list.data {
ids << file.id
}
assert uploaded_file.id in ids
assert uploaded_file2.id in ids
for file in got_list.data {
client.delete_file(file.id)!
}
got_list = client.list_files()!
assert got_list.data.len == 0
}
fn test_audio() {
key := os.getenv('OPENAI_API_KEY')
heroscript := '!!openai.configure api_key: "${key}"'
play(heroscript: heroscript)!
mut client := get()!
// create speech
client.create_speech(
input: 'the quick brown fox jumps over the lazy dog'
output_path: '/tmp/output.mp3'
)!
assert os.exists('/tmp/output.mp3')
}

View File

@@ -63,8 +63,10 @@ pub fn (mut f OpenAI) chat_completion(model_type ModelType, msgs Messages) !Chat
m.messages << mr m.messages << mr
} }
data := json.encode(m) data := json.encode(m)
println('data: ${data}')
mut conn := f.connection()! mut conn := f.connection()!
r := conn.post_json_str(prefix: 'chat/completions', data: data)! r := conn.post_json_str(prefix: 'chat/completions', data: data)!
println('res: ${r}')
res := json.decode(ChatCompletion, r)! res := json.decode(ChatCompletion, r)!
return res return res

View File

@@ -11,7 +11,14 @@ const jsonl_mime_type = 'text/jsonl'
pub struct FileUploadArgs { pub struct FileUploadArgs {
pub: pub:
filepath string filepath string
purpose string purpose FilePurpose
}
pub enum FilePurpose {
assistants
vision
batch
fine_tuning
} }
pub struct File { pub struct File {
@@ -51,7 +58,7 @@ pub fn (mut f OpenAI) upload_file(args FileUploadArgs) !File {
'file': [file_data] 'file': [file_data]
} }
form: { form: {
'purpose': args.purpose 'purpose': file_purpose_str(args.purpose)
} }
} }
@@ -93,3 +100,21 @@ pub fn (mut f OpenAI) get_file_content(file_id string) !string {
r := conn.get(prefix: 'files/' + file_id + '/content')! r := conn.get(prefix: 'files/' + file_id + '/content')!
return r return r
} }
// returns the purpose of the file in string format
fn file_purpose_str(purpose FilePurpose) string {
return match purpose {
.assistants {
'assistants'
}
.vision {
'vision'
}
.batch {
'batch'
}
.fine_tuning {
'fine_tuning'
}
}
}

View File

@@ -1,6 +1,7 @@
module openai module openai
pub enum ModelType { pub enum ModelType {
gpt_4o_2024_08_06
gpt_3_5_turbo gpt_3_5_turbo
gpt_4 gpt_4
gpt_4_0613 gpt_4_0613
@@ -10,16 +11,17 @@ pub enum ModelType {
gpt_3_5_turbo_16k gpt_3_5_turbo_16k
gpt_3_5_turbo_16k_0613 gpt_3_5_turbo_16k_0613
whisper_1 whisper_1
tts_1
} }
fn modelname_str(e ModelType) string { fn modelname_str(e ModelType) string {
if e == .gpt_4 {
return 'gpt-4'
}
if e == .gpt_3_5_turbo {
return 'gpt-3.5-turbo'
}
return match e { return match e {
.tts_1 {
'tts-1'
}
.gpt_4o_2024_08_06 {
'gpt-4o-2024-08-06'
}
.gpt_4 { .gpt_4 {
'gpt-4' 'gpt-4'
} }
@@ -73,3 +75,79 @@ fn roletype_str(x RoleType) string {
} }
} }
} }
pub enum Voice {
alloy
ash
coral
echo
fable
onyx
nova
sage
shimmer
}
fn voice_str(x Voice) string {
return match x {
.alloy {
'alloy'
}
.ash {
'ash'
}
.coral {
'coral'
}
.echo {
'echo'
}
.fable {
'fable'
}
.onyx {
'onyx'
}
.nova {
'nova'
}
.sage {
'sage'
}
.shimmer {
'shimmer'
}
}
}
pub enum AudioFormat {
mp3
opus
aac
flac
wav
pcm
}
fn audio_format_str(x AudioFormat) string {
return match x {
.mp3 {
'mp3'
}
.opus {
'opus'
}
.aac {
'aac'
}
.flac {
'flac'
}
.wav {
'wav'
}
.pcm {
'pcm'
}
}
}

View File

@@ -1,7 +1,7 @@
module openai module openai
import freeflowuniverse.crystallib.core.base import freeflowuniverse.herolib.core.base
import freeflowuniverse.crystallib.core.playbook import freeflowuniverse.herolib.core.playbook
__global ( __global (
openai_global map[string]&OpenAI openai_global map[string]&OpenAI

View File

@@ -22,8 +22,8 @@ pub fn heroscript_default() !string {
pub struct OpenAI { pub struct OpenAI {
pub mut: pub mut:
name string = 'default' name string = 'default'
key string @[secret] api_key string @[secret]
conn ?&httpconnection.HTTPConnection conn ?&httpconnection.HTTPConnection
} }
@@ -32,7 +32,7 @@ fn cfg_play(p paramsparser.Params) ! {
// THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above // THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE WITH struct above
mut mycfg := OpenAI{ mut mycfg := OpenAI{
name: p.get_default('name', 'default')! name: p.get_default('name', 'default')!
key: p.get('key')! api_key: p.get('api_key')!
} }
set(mycfg)! set(mycfg)!
} }
@@ -47,15 +47,13 @@ pub fn (mut client OpenAI) connection() !&httpconnection.HTTPConnection {
mut c := client.conn or { mut c := client.conn or {
mut c2 := httpconnection.new( mut c2 := httpconnection.new(
name: 'openaiconnection_${client.name}' name: 'openaiconnection_${client.name}'
url: 'https://openrouter.ai/api/v1' url: 'https://api.openai.com/v1'
cache: false cache: false
retry: 0
)! )!
c2 c2
} }
c.default_header.set(.authorization, 'Bearer ${client.key}') c.default_header.set(.authorization, 'Bearer ${client.api_key}')
c.default_header.set(.content_type, 'application/json')
client.conn = c client.conn = c
return c return c

View File

@@ -1,14 +1,12 @@
# openai # openai
To get started To get started
```vlang ```vlang
import freeflowuniverse.crystallib.clients. openai import freeflowuniverse.herolib.clients. openai
mut client:= openai.get()! mut client:= openai.get()!
@@ -21,12 +19,9 @@ client...
## example heroscript ## example heroscript
```hero ```hero
!!openai.configure !!openai.configure
secret: '...' secret: '...'
host: 'localhost' host: 'localhost'
port: 8888 port: 8888
``` ```

View File

@@ -1,5 +0,0 @@
module ourdb
fn test_basic_operations() {
// mut client := get()
}

View File

@@ -0,0 +1 @@
hello world

View File

@@ -0,0 +1 @@
testfile2 content