This commit is contained in:
2025-10-29 07:53:34 +04:00
parent 4222dac72e
commit c5f1d39958
18 changed files with 138 additions and 110 deletions

View File

@@ -0,0 +1,42 @@
#!/usr/bin/env -S v -n -w -gc none -cc tcc -d use_openssl -enable-globals run
import incubaid.herolib.clients.openai
import incubaid.herolib.core.playcmds
playcmds.run(
heroscript: '
!!openai.configure name:"default"
url:"https://openrouter.ai/api/v1"
model_default:"gpt-oss-120b"
'
reset: false
)!
// Get the client instance
mut client := openai.get() or {
eprintln('Failed to get client: ${err}')
return
}
println(client.list_models()!)
println('Sending message to OpenRouter...\n')
// Simple hello message
response := client.chat_completion(
model: 'qwen/qwen-2.5-coder-32b-instruct'
message: 'Say hello in a friendly way!'
temperature: 0.7
max_completion_tokens: 100
) or {
eprintln('Failed to get completion: ${err}')
return
}
println('Response from AI:')
println(''.repeat(50))
println(response.result)
println(''.repeat(50))
println('\nTokens used: ${response.usage.total_tokens}')
println(' - Prompt: ${response.usage.prompt_tokens}')
println(' - Completion: ${response.usage.completion_tokens}')

View File

@@ -2,7 +2,7 @@
## Overview ## Overview
This folder contains **three example scripts** demonstrating the usage of the OpenRouter V client (`herolib.clients.openrouter`). This folder contains **example scripts** demonstrating the usage of the OpenRouter V client (`herolib.clients.openrouter`).
* **Goal:** Show how to send messages to OpenRouter models, run a **two-model pipeline** for code enhancement, and illustrate multi-model usage. * **Goal:** Show how to send messages to OpenRouter models, run a **two-model pipeline** for code enhancement, and illustrate multi-model usage.

View File

@@ -3,6 +3,8 @@
import incubaid.herolib.clients.openai import incubaid.herolib.clients.openai
import incubaid.herolib.core.playcmds import incubaid.herolib.core.playcmds
//to set the API key, either set it here, or set the OPENAI_API_KEY environment variable
playcmds.run( playcmds.run(
heroscript: ' heroscript: '
!!openai.configure name: "default" key: "" url: "https://openrouter.ai/api/v1" model_default: "gpt-oss-120b" !!openai.configure name: "default" key: "" url: "https://openrouter.ai/api/v1" model_default: "gpt-oss-120b"

View File

@@ -46,6 +46,9 @@ fn obj_init(mycfg_ OpenAI) !OpenAI {
} }
} }
} }
if mycfg.api_key == '' {
return error('OpenAI client "${mycfg.name}" missing api_key')
}
return mycfg return mycfg
} }

View File

@@ -10,6 +10,7 @@ import incubaid.herolib.clients.meilisearch
import incubaid.herolib.clients.mycelium import incubaid.herolib.clients.mycelium
import incubaid.herolib.clients.mycelium_rpc import incubaid.herolib.clients.mycelium_rpc
import incubaid.herolib.clients.openai import incubaid.herolib.clients.openai
import incubaid.herolib.clients.openrouter
import incubaid.herolib.clients.postgresql_client import incubaid.herolib.clients.postgresql_client
import incubaid.herolib.clients.qdrant import incubaid.herolib.clients.qdrant
import incubaid.herolib.clients.rclone import incubaid.herolib.clients.rclone
@@ -47,6 +48,7 @@ import incubaid.herolib.installers.web.tailwind4
import incubaid.herolib.installers.web.traefik import incubaid.herolib.installers.web.traefik
import incubaid.herolib.installers.web.zola import incubaid.herolib.installers.web.zola
import incubaid.herolib.virt.hetznermanager import incubaid.herolib.virt.hetznermanager
import incubaid.herolib.virt.kubernetes
pub fn run_all(args_ PlayArgs) ! { pub fn run_all(args_ PlayArgs) ! {
mut args := args_ mut args := args_
@@ -64,6 +66,7 @@ pub fn run_all(args_ PlayArgs) ! {
mycelium.play(mut plbook)! mycelium.play(mut plbook)!
mycelium_rpc.play(mut plbook)! mycelium_rpc.play(mut plbook)!
openai.play(mut plbook)! openai.play(mut plbook)!
openrouter.play(mut plbook)!
postgresql_client.play(mut plbook)! postgresql_client.play(mut plbook)!
qdrant.play(mut plbook)! qdrant.play(mut plbook)!
rclone.play(mut plbook)! rclone.play(mut plbook)!
@@ -101,4 +104,5 @@ pub fn run_all(args_ PlayArgs) ! {
traefik.play(mut plbook)! traefik.play(mut plbook)!
zola.play(mut plbook)! zola.play(mut plbook)!
hetznermanager.play(mut plbook)! hetznermanager.play(mut plbook)!
kubernetes.play(mut plbook)!
} }

View File

@@ -21,6 +21,7 @@ fn decode_struct[T](_ T, data string) !T {
$if T is $struct { $if T is $struct {
obj_name := texttools.snake_case(T.name.all_after_last('.')) obj_name := texttools.snake_case(T.name.all_after_last('.'))
obj_name2 := texttools.name_fix(T.name.all_after_last('.'))
// Define possible action name formats to try // Define possible action name formats to try
action_names_to_try := [ action_names_to_try := [
@@ -28,6 +29,10 @@ fn decode_struct[T](_ T, data string) !T {
'configure.${obj_name}', 'configure.${obj_name}',
'${obj_name}.define', '${obj_name}.define',
'${obj_name}.configure', '${obj_name}.configure',
'define.${obj_name2}',
'configure.${obj_name2}',
'${obj_name2}.define',
'${obj_name2}.configure',
] ]
mut found_action_name := '' mut found_action_name := ''
@@ -47,7 +52,7 @@ fn decode_struct[T](_ T, data string) !T {
} }
if found_action_name == '' { if found_action_name == '' {
return error('Data does not contain expected action format for ${obj_name}\nData: ${data}') return error('Data does not contain expected action format for obj:${obj_name} or obj:${obj_name2}\nData: ${data}')
} }
if actions.len > 1 { if actions.len > 1 {

View File

@@ -54,6 +54,6 @@ fn destroy() ! {
// No cleanup needed for kubectl wrapper // No cleanup needed for kubectl wrapper
} }
fn configure() ! { // fn configure() ! {
console.print_debug('Kubernetes client configured') // console.print_debug('Kubernetes client configured')
} // }

View File

@@ -9,36 +9,36 @@ import os
// Execute kubectl command with proper error handling // Execute kubectl command with proper error handling
pub fn (mut k KubeClient) kubectl_exec(args KubectlExecArgs) !KubectlResult { pub fn (mut k KubeClient) kubectl_exec(args KubectlExecArgs) !KubectlResult {
mut cmd := '${k.kubectl_path} ' mut cmd := 'kubectl'
if !k.config.namespace.is_empty() { if k.config.namespace.len > 0 {
cmd += '--namespace=${k.config.namespace} ' cmd += '--namespace=${k.config.namespace} '
} }
if !k.kubeconfig_path.is_empty() { if k.kubeconfig_path.len > 0 {
cmd += '--kubeconfig=${k.kubeconfig_path} ' cmd += '--kubeconfig=${k.kubeconfig_path} '
} }
if !k.config.context.is_empty() { if k.config.context.len > 0 {
cmd += '--context=${k.config.context} ' cmd += '--context=${k.config.context} '
} }
cmd += args.command cmd += args.command
console.print_debug("executing: ${cmd}") console.print_debug('executing: ${cmd}')
job := osal.exec( job := osal.exec(
cmd: cmd cmd: cmd
timeout: args.timeout timeout: args.timeout
retry: args.retry retry: args.retry
raise_error: false raise_error: false
)! )!
return KubectlResult{ return KubectlResult{
exit_code: job.exit_code exit_code: job.exit_code
stdout: job.output stdout: job.output
stderr: job.error stderr: job.error
success: job.exit_code == 0 success: job.exit_code == 0
} }
} }
@@ -76,80 +76,90 @@ pub fn (mut k KubeClient) cluster_info() !ClusterInfo {
return error('Failed to get cluster version: ${result.stderr}') return error('Failed to get cluster version: ${result.stderr}')
} }
version_data := json.decode(map[string]interface{}, result.stdout)! println(result.stdout)
server_version := version_data['serverVersion'] or { return error('No serverVersion') }
// Get node count $dbg;
nodes_result := k.kubectl_exec(command: 'get nodes -o json')! // version_data := json.decode(map[string]interface{}, result.stdout)!
nodes_count := if nodes_result.success { // server_version := version_data['serverVersion'] or { return error('No serverVersion') }
nodes_data := json.decode(map[string]interface{}, nodes_result.stdout)!
items := nodes_data['items'] or { []interface{}{} }
items.len
} else {
0
}
// Get namespace count // // Get node count
ns_result := k.kubectl_exec(command: 'get namespaces -o json')! // nodes_result := k.kubectl_exec(command: 'get nodes -o json')!
ns_count := if ns_result.success { // nodes_count := if nodes_result.success {
ns_data := json.decode(map[string]interface{}, ns_result.stdout)! // nodes_data := json.decode(map[string]interface{}, nodes_result.stdout)!
items := ns_data['items'] or { []interface{}{} } // items := nodes_data['items'] or { []interface{}{} }
items.len // items.len
} else { // } else {
0 // 0
} // }
// Get running pods count // // Get namespace count
pods_result := k.kubectl_exec(command: 'get pods --all-namespaces -o json')! // ns_result := k.kubectl_exec(command: 'get namespaces -o json')!
pods_count := if pods_result.success { // ns_count := if ns_result.success {
pods_data := json.decode(map[string]interface{}, pods_result.stdout)! // ns_data := json.decode(map[string]interface{}, ns_result.stdout)!
items := pods_data['items'] or { []interface{}{} } // items := ns_data['items'] or { []interface{}{} }
items.len // items.len
} else { // } else {
0 // 0
} // }
return ClusterInfo{ // // Get running pods count
version: 'v1.0.0' // pods_result := k.kubectl_exec(command: 'get pods --all-namespaces -o json')!
nodes: nodes_count // pods_count := if pods_result.success {
namespaces: ns_count // pods_data := json.decode(map[string]interface{}, pods_result.stdout)!
running_pods: pods_count // items := pods_data['items'] or { []interface{}{} }
api_server: k.config.api_server // items.len
} // } else {
// 0
// }
// return ClusterInfo{
// version: 'v1.0.0'
// nodes: nodes_count
// namespaces: ns_count
// running_pods: pods_count
// api_server: k.config.api_server
// }
return ClusterInfo{}
} }
// Get resources (Pods, Deployments, Services, etc.) // Get resources (Pods, Deployments, Services, etc.)
pub fn (mut k KubeClient) get_pods(namespace string) ![]map[string]interface{} { pub fn (mut k KubeClient) get_pods(namespace string) ! {
result := k.kubectl_exec(command: 'get pods -n ${namespace} -o json')! result := k.kubectl_exec(command: 'get pods -n ${namespace} -o json')!
if !result.success { if !result.success {
return error('Failed to get pods: ${result.stderr}') return error('Failed to get pods: ${result.stderr}')
} }
data := json.decode(map[string]interface{}, result.stdout)! println(result.stdout)
items := data['items'] or { []interface{}{} } $dbg;
return items as []map[string]interface{} // data := json.decode(map[string]interface{}, result.stdout)!
// items := data['items'] or { []interface{}{} }
// return items as []map[string]interface{}
panic('Not implemented')
} }
pub fn (mut k KubeClient) get_deployments(namespace string) ![]map[string]interface{} { pub fn (mut k KubeClient) get_deployments(namespace string) ! {
result := k.kubectl_exec(command: 'get deployments -n ${namespace} -o json')! result := k.kubectl_exec(command: 'get deployments -n ${namespace} -o json')!
if !result.success { if !result.success {
return error('Failed to get deployments: ${result.stderr}') return error('Failed to get deployments: ${result.stderr}')
} }
data := json.decode(map[string]interface{}, result.stdout)! // data := json.decode(map[string]interface{}, result.stdout)!
items := data['items'] or { []interface{}{} } // items := data['items'] or { []interface{}{} }
return items as []map[string]interface{} // return items as []map[string]interface{}
panic('Not implemented')
} }
pub fn (mut k KubeClient) get_services(namespace string) ![]map[string]interface{} { pub fn (mut k KubeClient) get_services(namespace string) ! {
result := k.kubectl_exec(command: 'get services -n ${namespace} -o json')! result := k.kubectl_exec(command: 'get services -n ${namespace} -o json')!
if !result.success { if !result.success {
return error('Failed to get services: ${result.stderr}') return error('Failed to get services: ${result.stderr}')
} }
data := json.decode(map[string]interface{}, result.stdout)! // data := json.decode(map[string]interface{}, result.stdout)!
items := data['items'] or { []interface{}{} } // items := data['items'] or { []interface{}{} }
return items as []map[string]interface{} // return items as []map[string]interface{}
panic('Not implemented')
} }
// Apply YAML file // Apply YAML file
@@ -157,7 +167,7 @@ pub fn (mut k KubeClient) apply_yaml(yaml_path string) !KubectlResult {
// Validate before applying // Validate before applying
validation := yaml_validate(yaml_path)! validation := yaml_validate(yaml_path)!
if !validation.valid { if !validation.valid {
return error('YAML validation failed: ${validation.errors.join(", ")}') return error('YAML validation failed: ${validation.errors.join(', ')}')
} }
result := k.kubectl_exec(command: 'apply -f ${yaml_path}')! result := k.kubectl_exec(command: 'apply -f ${yaml_path}')!
@@ -204,11 +214,11 @@ pub fn (mut k KubeClient) logs(pod_name string, namespace string, follow bool) !
// Exec into container // Exec into container
pub fn (mut k KubeClient) exec_pod(pod_name string, namespace string, container string, cmd_args []string) !string { pub fn (mut k KubeClient) exec_pod(pod_name string, namespace string, container string, cmd_args []string) !string {
cmd := 'exec -it ${pod_name} -n ${namespace}' mut cmd := 'exec -it ${pod_name} -n ${namespace}'
if !container.is_empty() { if container.len > 0 {
cmd += ' -c ${container}' cmd += ' -c ${container}'
} }
cmd += ' -- ${cmd_args.join(" ")}' cmd += ' -- ${cmd_args.join(' ')}'
result := k.kubectl_exec(command: cmd, timeout: 300)! result := k.kubectl_exec(command: cmd, timeout: 300)!
if !result.success { if !result.success {

View File

@@ -11,16 +11,13 @@ const default = true
@[heap] @[heap]
pub struct KubeClient { pub struct KubeClient {
pub mut: pub mut:
name string = 'default'
name string = 'default' name string = 'default'
kubeconfig_path string kubeconfig_path string
config KubeConfig config KubeConfig
connected bool connected bool
api_version string = 'v1' api_version string = 'v1'
kubectl_path string = 'kubectl' cache_enabled bool = true
cache_enabled bool = true cache_ttl_seconds int = 300
cache_ttl_seconds int = 300
} }
// your checking & initialization code if needed // your checking & initialization code if needed
@@ -41,7 +38,7 @@ fn configure() ! {
/////////////NORMALLY NO NEED TO TOUCH /////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_loads(heroscript string) !KubeClient { pub fn heroscript_loads(heroscript string) !KubeClient {
//TODO: will have to be implemented manual // TODO: will have to be implemented manual
mut obj := encoderhero.decode[KubeClient](heroscript)! mut obj := encoderhero.decode[KubeClient](heroscript)!
return obj return obj
} }

View File

@@ -1,14 +1,8 @@
module kubernetes module kubernetes
import incubaid.herolib.data.paramsparser
import incubaid.herolib.data.encoderhero import incubaid.herolib.data.encoderhero
import incubaid.herolib.data.ourjson
import os import os
pub const version = '1.0.0'
const singleton = false
const default = true
// K8s API Version and Kind tracking // K8s API Version and Kind tracking
@[params] @[params]
pub struct K8sMetadata { pub struct K8sMetadata {
@@ -166,7 +160,6 @@ pub mut:
insecure_skip_tls_verify bool insecure_skip_tls_verify bool
} }
// Validation result for YAML files // Validation result for YAML files
pub struct K8sValidationResult { pub struct K8sValidationResult {
pub mut: pub mut:
@@ -186,31 +179,3 @@ pub mut:
running_pods int running_pods int
api_server string api_server string
} }
// Initialization
fn obj_init(mut cfg KubeClient) !KubeClient {
// Resolve kubeconfig path
if cfg.kubeconfig_path.is_empty() {
home := os.home_dir()
cfg.kubeconfig_path = '${home}/.kube/config'
}
// Ensure kubeconfig exists
if !os.path_exists(cfg.kubeconfig_path) {
return error('kubeconfig not found at ${cfg.kubeconfig_path}')
}
cfg.config.kubeconfig_path = cfg.kubeconfig_path
return cfg
}
fn configure() ! {
// Configure any defaults or environment-specific settings
}
pub fn heroscript_loads(heroscript string) !KubeClient {
// TODO: this will have to be redone, because its much more complicated now, need to define a nice play processors
mut obj := encoderhero.decode[KubeClient](heroscript)!
return obj_init(obj)!
}