wip: add tests for openai client
This commit is contained in:
@@ -110,3 +110,41 @@ fn (mut f OpenAI) create_audio_request(args AudioArgs, endpoint string) !AudioRe
|
||||
}
|
||||
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())!
|
||||
}
|
||||
|
||||
105
lib/clients/openai/client_test.v
Normal file
105
lib/clients/openai/client_test.v
Normal 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')
|
||||
}
|
||||
@@ -63,8 +63,10 @@ pub fn (mut f OpenAI) chat_completion(model_type ModelType, msgs Messages) !Chat
|
||||
m.messages << mr
|
||||
}
|
||||
data := json.encode(m)
|
||||
println('data: ${data}')
|
||||
mut conn := f.connection()!
|
||||
r := conn.post_json_str(prefix: 'chat/completions', data: data)!
|
||||
println('res: ${r}')
|
||||
|
||||
res := json.decode(ChatCompletion, r)!
|
||||
return res
|
||||
|
||||
@@ -11,7 +11,14 @@ const jsonl_mime_type = 'text/jsonl'
|
||||
pub struct FileUploadArgs {
|
||||
pub:
|
||||
filepath string
|
||||
purpose string
|
||||
purpose FilePurpose
|
||||
}
|
||||
|
||||
pub enum FilePurpose {
|
||||
assistants
|
||||
vision
|
||||
batch
|
||||
fine_tuning
|
||||
}
|
||||
|
||||
pub struct File {
|
||||
@@ -51,7 +58,7 @@ pub fn (mut f OpenAI) upload_file(args FileUploadArgs) !File {
|
||||
'file': [file_data]
|
||||
}
|
||||
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')!
|
||||
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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
module openai
|
||||
|
||||
pub enum ModelType {
|
||||
gpt_4o_2024_08_06
|
||||
gpt_3_5_turbo
|
||||
gpt_4
|
||||
gpt_4_0613
|
||||
@@ -10,16 +11,17 @@ pub enum ModelType {
|
||||
gpt_3_5_turbo_16k
|
||||
gpt_3_5_turbo_16k_0613
|
||||
whisper_1
|
||||
tts_1
|
||||
}
|
||||
|
||||
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 {
|
||||
.tts_1 {
|
||||
'tts-1'
|
||||
}
|
||||
.gpt_4o_2024_08_06 {
|
||||
'gpt-4o-2024-08-06'
|
||||
}
|
||||
.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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module openai
|
||||
|
||||
import freeflowuniverse.crystallib.core.base
|
||||
import freeflowuniverse.crystallib.core.playbook
|
||||
import freeflowuniverse.herolib.core.base
|
||||
import freeflowuniverse.herolib.core.playbook
|
||||
|
||||
__global (
|
||||
openai_global map[string]&OpenAI
|
||||
|
||||
@@ -23,7 +23,7 @@ pub fn heroscript_default() !string {
|
||||
pub struct OpenAI {
|
||||
pub mut:
|
||||
name string = 'default'
|
||||
key string @[secret]
|
||||
api_key string @[secret]
|
||||
|
||||
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
|
||||
mut mycfg := OpenAI{
|
||||
name: p.get_default('name', 'default')!
|
||||
key: p.get('key')!
|
||||
api_key: p.get('api_key')!
|
||||
}
|
||||
set(mycfg)!
|
||||
}
|
||||
@@ -47,15 +47,13 @@ pub fn (mut client OpenAI) connection() !&httpconnection.HTTPConnection {
|
||||
mut c := client.conn or {
|
||||
mut c2 := httpconnection.new(
|
||||
name: 'openaiconnection_${client.name}'
|
||||
url: 'https://openrouter.ai/api/v1'
|
||||
url: 'https://api.openai.com/v1'
|
||||
cache: false
|
||||
retry: 0
|
||||
)!
|
||||
c2
|
||||
}
|
||||
|
||||
c.default_header.set(.authorization, 'Bearer ${client.key}')
|
||||
c.default_header.set(.content_type, 'application/json')
|
||||
c.default_header.set(.authorization, 'Bearer ${client.api_key}')
|
||||
|
||||
client.conn = c
|
||||
return c
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
# openai
|
||||
|
||||
|
||||
|
||||
To get started
|
||||
|
||||
```vlang
|
||||
|
||||
|
||||
|
||||
import freeflowuniverse.crystallib.clients. openai
|
||||
import freeflowuniverse.herolib.clients. openai
|
||||
|
||||
mut client:= openai.get()!
|
||||
|
||||
@@ -21,12 +19,9 @@ client...
|
||||
|
||||
## example heroscript
|
||||
|
||||
|
||||
```hero
|
||||
!!openai.configure
|
||||
secret: '...'
|
||||
host: 'localhost'
|
||||
port: 8888
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
module ourdb
|
||||
|
||||
fn test_basic_operations() {
|
||||
// mut client := get()
|
||||
}
|
||||
1
lib/clients/openai/testdata/testfile.txt
vendored
Normal file
1
lib/clients/openai/testdata/testfile.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
hello world
|
||||
1
lib/clients/openai/testdata/testfile2.txt
vendored
Normal file
1
lib/clients/openai/testdata/testfile2.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
testfile2 content
|
||||
Reference in New Issue
Block a user