This commit is contained in:
2025-03-31 09:32:58 +02:00
parent b9e5d14b48
commit 9fcdcc3aff
19 changed files with 383 additions and 399 deletions

70
examples/aiexamples/groq.vsh Executable file
View File

@@ -0,0 +1,70 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
module main
import freeflowuniverse.herolib.clients.openai
import os
fn test1(mut client openai.OpenAI)!{
// Create a chat completion request
res := client.chat_completion(msgs:openai.Messages{
messages: [
openai.Message{
role: .user
content: 'What are the key differences between Groq and other AI inference providers?'
},
]
})!
// Print the response
println('\nGroq AI Response:')
println('==================')
println(res.choices[0].message.content)
println('\nUsage Statistics:')
println('Prompt tokens: ${res.usage.prompt_tokens}')
println('Completion tokens: ${res.usage.completion_tokens}')
println('Total tokens: ${res.usage.total_tokens}')
}
fn test2(mut client openai.OpenAI)!{
// Create a chat completion request
res := client.chat_completion(
model:"deepseek-r1-distill-llama-70b",
msgs:openai.Messages{
messages: [
openai.Message{
role: .user
content: 'A story of 10 lines?'
},
]
})!
println('\nGroq AI Response:')
println('==================')
println(res.choices[0].message.content)
println('\nUsage Statistics:')
println('Prompt tokens: ${res.usage.prompt_tokens}')
println('Completion tokens: ${res.usage.completion_tokens}')
println('Total tokens: ${res.usage.total_tokens}')
}
println('
TO USE:
export AIKEY=\'gsk_...\'
export AIURL=\'https://api.groq.com/openai/v1\'
export AIMODEL=\'llama-3.3-70b-versatile\'
')
mut client:=openai.get(name:"test")!
println(client)
// test1(mut client)!
test2(mut client)!

View File

@@ -1,69 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.clients.jina
import freeflowuniverse.herolib.osal
import os
// Example of using the Jina client
fn main() {
// Set environment variable for testing
// In production, you would set this in your environment
// osal.env_set(key: 'JINAKEY', value: 'your-api-key')
// Check if JINAKEY environment variable exists
if !osal.env_exists('JINAKEY') {
println('JINAKEY environment variable not set. Please set it before running this example.')
exit(1)
}
// Create a Jina client instance
mut client := jina.get(name: 'default')!
println('Jina client initialized successfully.')
// Example: Create embeddings
model := 'jina-embeddings-v3'
texts := ['Hello, world!', 'How are you doing?']
println('Creating embeddings for texts: ${texts}')
result := client.create_embeddings(texts, model, 'retrieval.query')!
println('Embeddings created successfully.')
println('Model: ${result['model']}')
println('Data count: ${result['data'].arr().len}')
// Example: List classifiers
println('\nListing classifiers:')
classifiers := client.list_classifiers() or {
println('Failed to list classifiers: ${err}')
return
}
println('Classifiers retrieved successfully.')
// Example: Create a classifier
println('\nTraining a classifier:')
examples := [
jina.TrainingExample{
text: 'This movie was great!'
label: 'positive'
},
jina.TrainingExample{
text: 'I did not like this movie.'
label: 'negative'
},
jina.TrainingExample{
text: 'The movie was okay.'
label: 'neutral'
},
]
training_result := client.train(examples, model, 'private') or {
println('Failed to train classifier: ${err}')
return
}
println('Classifier trained successfully.')
println('Classifier ID: ${training_result['classifier_id']}')
}

View File

@@ -1,9 +1,13 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run #!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import freeflowuniverse.herolib.clients.qdrant import freeflowuniverse.herolib.clients.qdrant
import freeflowuniverse.herolib.installers.db.qdrant as qdrant_installer
import freeflowuniverse.herolib.core.httpconnection import freeflowuniverse.herolib.core.httpconnection
import rand import rand
mut i:=qdrant_installer.get()!
i.install()!
// 1. Get the qdrant client // 1. Get the qdrant client
mut qdrant_client := qdrant.get()! mut qdrant_client := qdrant.get()!

View File

@@ -1 +0,0 @@
export GROQ_API_KEY="your-groq-api-key-here"

View File

@@ -1,64 +0,0 @@
# Groq AI Client Example
This example demonstrates how to use Groq's AI API with the herolib OpenAI client. Groq provides API compatibility with OpenAI's client libraries, allowing you to leverage Groq's fast inference speeds with minimal changes to your existing code.
## Prerequisites
- V programming language installed
- A Groq API key (get one from [Groq's website](https://console.groq.com/keys))
## Setup
1. Copy the `.env.example` file to `.env`:
```bash
cp .env.example .env
```
2. Edit the `.env` file and replace `your-groq-api-key-here` with your actual Groq API key.
3. Load the environment variables:
```bash
source .env
```
## Running the Example
Execute the script with:
```bash
v run groq_client.vsh
```
Or make it executable first:
```bash
chmod +x groq_client.vsh
./groq_client.vsh
```
## How It Works
The example uses the existing OpenAI client from herolib but configures it to use Groq's API endpoint:
1. It retrieves the Groq API key from the environment variables
2. Configures the OpenAI client with the Groq API key
3. Overrides the default OpenAI URL with Groq's API URL (`https://api.groq.com/openai/v1`)
4. Sends a chat completion request to Groq's API
5. Displays the response
## Supported Models
Groq supports various models including:
- llama2-70b-4096
- mixtral-8x7b-32768
- gemma-7b-it
For a complete and up-to-date list of supported models, refer to the [Groq API documentation](https://console.groq.com/docs/models).
## Notes
- The example uses the `gpt_3_5_turbo` enum from the OpenAI client, but Groq will automatically map this to an appropriate model on their end.
- For production use, you may want to explicitly specify one of Groq's supported models.

View File

@@ -1,46 +0,0 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
module main
import freeflowuniverse.herolib.clients.openai
import os
fn main() {
// Get API key from environment variable
key := os.getenv('GROQ_API_KEY')
if key == '' {
println('Error: GROQ_API_KEY environment variable not set')
println('Please set it by running: source .env')
exit(1)
}
// Get the configured client
mut client := openai.OpenAI{
name: 'groq'
api_key: key
server_url: 'https://api.groq.com/openai/v1'
}
// Define the model and message for chat completion
// Note: Use a model that Groq supports, like llama2-70b-4096 or mixtral-8x7b-32768
model := 'qwen-2.5-coder-32b'
// Create a chat completion request
res := client.chat_completion(model, openai.Messages{
messages: [
openai.Message{
role: .user
content: 'What are the key differences between Groq and other AI inference providers?'
},
]
})!
// Print the response
println('\nGroq AI Response:')
println('==================')
println(res.choices[0].message.content)
println('\nUsage Statistics:')
println('Prompt tokens: ${res.usage.prompt_tokens}')
println('Completion tokens: ${res.usage.completion_tokens}')
println('Total tokens: ${res.usage.total_tokens}')
}

View File

@@ -446,9 +446,12 @@ v-install() {
v-analyzer() { v-analyzer() {
set -ex
# Install v-analyzer if requested # Install v-analyzer if requested
if [ "$INSTALL_ANALYZER" = true ]; then if [ "$INSTALL_ANALYZER" = true ]; then
echo "Installing v-analyzer..." echo "Installing v-analyzer..."
cd /tmp
v download -RD https://raw.githubusercontent.com/vlang/v-analyzer/main/install.vsh v download -RD https://raw.githubusercontent.com/vlang/v-analyzer/main/install.vsh
# Check if v-analyzer bin directory exists # Check if v-analyzer bin directory exists
@@ -517,10 +520,7 @@ if [ "$RESET" = true ] || ! command_exists v; then
v-install v-install
# Only install v-analyzer if not in GitHub Actions environment
if ! is_github_actions; then
v-analyzer
fi
fi fi
@@ -534,6 +534,10 @@ fi
if [ "$INSTALL_ANALYZER" = true ]; then if [ "$INSTALL_ANALYZER" = true ]; then
# Only install v-analyzer if not in GitHub Actions environment
if ! is_github_actions; then
v-analyzer
fi
echo "Run 'source ~/.bashrc' or 'source ~/.zshrc' to update PATH for v-analyzer" echo "Run 'source ~/.bashrc' or 'source ~/.zshrc' to update PATH for v-analyzer"
fi fi

View File

@@ -114,7 +114,7 @@ fn (mut f OpenAI) create_audio_request(args AudioArgs, endpoint string) !AudioRe
@[params] @[params]
pub struct CreateSpeechArgs { pub struct CreateSpeechArgs {
pub: pub:
model ModelType = .tts_1 model string = "tts_1"
input string @[required] input string @[required]
voice Voice = .alloy voice Voice = .alloy
response_format AudioFormat = .mp3 response_format AudioFormat = .mp3
@@ -135,7 +135,7 @@ pub fn (mut f OpenAI) create_speech(args CreateSpeechArgs) ! {
mut output_file := os.open_file(args.output_path, 'w+')! mut output_file := os.open_file(args.output_path, 'w+')!
req := CreateSpeechRequest{ req := CreateSpeechRequest{
model: modelname_str(args.model) model: args.model
input: args.input input: args.input
voice: voice_str(args.voice) voice: voice_str(args.voice)
response_format: audio_format_str(args.response_format) response_format: audio_format_str(args.response_format)

View File

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

View File

@@ -48,13 +48,25 @@ mut:
messages []MessageRaw messages []MessageRaw
} }
@[params]
pub struct CompletionArgs{
pub mut:
model string
msgs Messages
}
// creates a new chat completion given a list of messages // creates a new chat completion given a list of messages
// each message consists of message content and the role of the author // each message consists of message content and the role of the author
pub fn (mut f OpenAI) chat_completion(model_type string, msgs Messages) !ChatCompletion { pub fn (mut f OpenAI) chat_completion(args_ CompletionArgs) !ChatCompletion {
mut m := ChatMessagesRaw{ mut args:=args_
model: model_type if args.model==""{
args.model = f.model_default
} }
for msg in msgs.messages { mut m := ChatMessagesRaw{
model: args.model
}
for msg in args.msgs.messages {
mr := MessageRaw{ mr := MessageRaw{
role: roletype_str(msg.role) role: roletype_str(msg.role)
content: msg.content content: msg.content
@@ -62,10 +74,10 @@ pub fn (mut f OpenAI) chat_completion(model_type string, msgs Messages) !ChatCom
m.messages << mr m.messages << mr
} }
data := json.encode(m) data := json.encode(m)
println('data: ${data}') // 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}') // println('res: ${r}')
res := json.decode(ChatCompletion, r)! res := json.decode(ChatCompletion, r)!
return res return res

View File

@@ -1,57 +1,5 @@
module openai module openai
pub enum ModelType {
gpt_4o_2024_08_06
gpt_3_5_turbo
gpt_4
gpt_4_0613
gpt_4_32k
gpt_4_32k_0613
gpt_3_5_turbo_0613
gpt_3_5_turbo_16k
gpt_3_5_turbo_16k_0613
whisper_1
tts_1
}
fn modelname_str(e ModelType) string {
return match e {
.tts_1 {
'tts-1'
}
.gpt_4o_2024_08_06 {
'gpt-4o-2024-08-06'
}
.gpt_4 {
'gpt-4'
}
.gpt_3_5_turbo {
'gpt-3.5-turbo'
}
.gpt_4_0613 {
'gpt-4-0613'
}
.gpt_4_32k {
'gpt-4-32k'
}
.gpt_4_32k_0613 {
'gpt-4-32k-0613'
}
.gpt_3_5_turbo_0613 {
'gpt-3.5-turbo-0613'
}
.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'
}
}
}
pub enum RoleType { pub enum RoleType {
system system
user user

View File

@@ -0,0 +1,112 @@
module openai
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook
import freeflowuniverse.herolib.ui.console
__global (
openai_global map[string]&OpenAI
openai_default string
)
/////////FACTORY
@[params]
pub struct ArgsGet {
pub mut:
name string
}
fn args_get(args_ ArgsGet) ArgsGet {
mut args := args_
if args.name == '' {
args.name = 'default'
}
return args
}
pub fn get(args_ ArgsGet) !&OpenAI {
mut context := base.context()!
mut args := args_get(args_)
mut obj := OpenAI{name:args.name}
if args.name !in openai_global {
if !exists(args)! {
set(obj)!
} else {
heroscript := context.hero_config_get('openai', args.name)!
mut obj_ := heroscript_loads(heroscript)!
set_in_mem(obj_)!
}
}
return openai_global[args.name] or {
println(openai_global)
// bug if we get here because should be in globals
panic('could not get config for openai with name, is bug, was for: ${args.name}')
}
}
// register the config for the future
pub fn set(o OpenAI) ! {
set_in_mem(o)!
mut context := base.context()!
heroscript := heroscript_dumps(o)!
context.hero_config_set('openai', o.name, heroscript)!
}
// does the config exists?
pub fn exists(args_ ArgsGet) !bool {
mut context := base.context()!
mut args := args_get(args_)
return context.hero_config_exists('openai', args.name)
}
pub fn delete(args_ ArgsGet) ! {
mut args := args_get(args_)
mut context := base.context()!
context.hero_config_delete('openai', args.name)!
if args.name in openai_global {
// del openai_global[args.name]
}
}
// only sets in mem, does not set as config
fn set_in_mem(o OpenAI) ! {
mut o2 := obj_init(o)!
openai_global[o.name] = &o2
openai_default = o.name
}
@[params]
pub struct PlayArgs {
pub mut:
heroscript string // if filled in then plbook will be made out of it
plbook ?playbook.PlayBook
reset bool
}
pub fn play(args_ PlayArgs) ! {
mut args := args_
mut plbook := args.plbook or { playbook.new(text: args.heroscript)! }
mut install_actions := plbook.find(filter: 'openai.configure')!
if install_actions.len > 0 {
for install_action in install_actions {
heroscript := install_action.heroscript()
mut obj2 := heroscript_loads(heroscript)!
set(obj2)!
}
}
}
// switch instance to be used for openai
pub fn switch(name string) {
openai_default = name
}
// helpers
@[params]
pub struct DefaultConfigArgs {
instance string = 'default'
}

View File

@@ -0,0 +1,93 @@
module openai
import freeflowuniverse.herolib.data.encoderhero
import freeflowuniverse.herolib.core.httpconnection
import os
pub const version = '0.0.0'
const singleton = false
const default = true
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
// @[heap]
// pub struct OpenAIBase {
// pub mut:
// name string = 'default'
// api_key string
// url string
// model_default string
// }
@[heap]
pub struct OpenAI {
pub mut:
name string = 'default'
api_key string
url string
model_default string
conn ?&httpconnection.HTTPConnection @[skip; str: skip]
}
// your checking & initialization code if needed
fn obj_init(mycfg_ OpenAI) !OpenAI {
mut mycfg := mycfg_
if mycfg.api_key==""{
mut k:=os.getenv('AIKEY')
if k != ""{
mycfg.api_key = k
k=os.getenv('AIURL')
if k != ""{
mycfg.url = k
}else{
return error("found AIKEY in env, but not AIURL")
}
k=os.getenv('AIMODEL')
if k != ""{
mycfg.model_default = k
}
return mycfg
}
mycfg.url = "https://api.openai.com/v1/models"
k=os.getenv('OPENAI_API_KEY')
if k != ""{
mycfg.api_key = k
return mycfg
}
k=os.getenv('OPENROUTER_API_KEY')
if k != ""{
mycfg.api_key = k
mycfg.url = "https://openrouter.ai/api/v1"
return mycfg
}
}
return mycfg
}
pub fn (mut client OpenAI) connection() !&httpconnection.HTTPConnection {
mut c := client.conn or {
mut c2 := httpconnection.new(
name: 'openaiconnection_${client.name}'
url: client.url
cache: false
retry: 20
)!
c2
}
c.default_header.set(.authorization, 'Bearer ${client.api_key}')
client.conn = c
return c
}
/////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_dumps(obj OpenAI) !string {
return encoderhero.encode[OpenAI](obj)!
}
pub fn heroscript_loads(heroscript string) !OpenAI {
mut obj := encoderhero.decode[OpenAI](heroscript)!
return obj
}

View File

@@ -1,67 +0,0 @@
module openai
import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.herolib.core.httpconnection
import os
pub const version = '1.14.3'
const singleton = false
const default = true
// TODO: THIS IS EXAMPLE CODE AND NEEDS TO BE CHANGED IN LINE TO STRUCT BELOW, IS STRUCTURED AS HEROSCRIPT
pub fn heroscript_default() !string {
heroscript := "
!!openai.configure
name:'openai'
api_key: ${os.getenv('OPENAI_API_KEY')}
"
return heroscript
}
// THIS THE THE SOURCE OF THE INFORMATION OF THIS FILE, HERE WE HAVE THE CONFIG OBJECT CONFIGURED AND MODELLED
pub struct OpenAI {
pub mut:
name string = 'default'
api_key string @[secret]
server_url string
conn ?&httpconnection.HTTPConnection
}
// 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')!
// api_key: p.get('api_key')!
// }
// set(mycfg)!
// }
fn obj_init(obj_ OpenAI) !OpenAI {
// never call get here, only thing we can do here is work on object itself
mut obj := obj_
return obj
}
pub fn (mut client OpenAI) connection() !&httpconnection.HTTPConnection {
server_url := if client.server_url != '' {
client.server_url
} else {
'https://api.openai.com/v1'
}
mut c := client.conn or {
mut c2 := httpconnection.new(
name: 'openaiconnection_${client.name}'
url: server_url
cache: false
retry: 20
)!
c2
}
c.default_header.set(.authorization, 'Bearer ${client.api_key}')
client.conn = c
return c
}

View File

@@ -6,7 +6,7 @@ To get started
import freeflowuniverse.herolib.clients. openai import freeflowuniverse.herolib.clients.openai
mut client:= openai.get()! mut client:= openai.get()!
@@ -20,8 +20,5 @@ client...
## example heroscript ## example heroscript
```hero ```hero
!!openai.configure !!openai.configure key
secret: '...'
host: 'localhost'
port: 8888
``` ```

View File

@@ -39,7 +39,7 @@ fn args_get (args_ ArgsGet) ArgsGet {
pub fn get(args_ ArgsGet) !&${args.classname} { pub fn get(args_ ArgsGet) !&${args.classname} {
mut context:=base.context()! mut context:=base.context()!
mut args := args_get(args_) mut args := args_get(args_)
mut obj := ${args.classname}{} mut obj := ${args.classname}{name:args.name}
if !(args.name in ${args.name}_global) { if !(args.name in ${args.name}_global) {
if ! exists(args)!{ if ! exists(args)!{
set(obj)! set(obj)!

View File

@@ -43,22 +43,3 @@ config := mcp.ServerConfiguration{
mut server := mcp.new_server(handlers, config)! mut server := mcp.new_server(handlers, config)!
server.start()! server.start()!
``` ```
## Development Tools
The module includes several development tools accessible through the `v_do` directory:
- **test**: Run tests for V files
- **run**: Execute V files
- **compile**: Compile V files
- **vet**: Perform static analysis on V files
## Dependencies
- `freeflowuniverse.herolib.schemas.jsonrpc`: For JSON-RPC communication
- `x.json2`: For JSON serialization/deserialization
- Standard V libraries: `time`, `os`, `log`
## License
This module is part of the HeroLib project. See the project's license for more information.

View File

@@ -56,6 +56,15 @@ pub fn env_get(key string) !string {
return os.environ()[key]! return os.environ()[key]!
} }
pub fn env_exists(key string) !bool {
k:=os.environ()
if key in k{
return true
}
return false
}
// Returns the requested environment variable if it exists or returns the provided default value if it does not // Returns the requested environment variable if it exists or returns the provided default value if it does not
pub fn env_get_default(key string, def string) string { pub fn env_get_default(key string, def string) string {
return os.environ()[key] or { return def } return os.environ()[key] or { return def }