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
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.

View File

@@ -3,6 +3,8 @@
import incubaid.herolib.clients.openai
import incubaid.herolib.core.playcmds
//to set the API key, either set it here, or set the OPENAI_API_KEY environment variable
playcmds.run(
heroscript: '
!!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
}

View File

@@ -10,6 +10,7 @@ import incubaid.herolib.clients.meilisearch
import incubaid.herolib.clients.mycelium
import incubaid.herolib.clients.mycelium_rpc
import incubaid.herolib.clients.openai
import incubaid.herolib.clients.openrouter
import incubaid.herolib.clients.postgresql_client
import incubaid.herolib.clients.qdrant
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.zola
import incubaid.herolib.virt.hetznermanager
import incubaid.herolib.virt.kubernetes
pub fn run_all(args_ PlayArgs) ! {
mut args := args_
@@ -64,6 +66,7 @@ pub fn run_all(args_ PlayArgs) ! {
mycelium.play(mut plbook)!
mycelium_rpc.play(mut plbook)!
openai.play(mut plbook)!
openrouter.play(mut plbook)!
postgresql_client.play(mut plbook)!
qdrant.play(mut plbook)!
rclone.play(mut plbook)!
@@ -101,4 +104,5 @@ pub fn run_all(args_ PlayArgs) ! {
traefik.play(mut plbook)!
zola.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 {
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
action_names_to_try := [
@@ -28,6 +29,10 @@ fn decode_struct[T](_ T, data string) !T {
'configure.${obj_name}',
'${obj_name}.define',
'${obj_name}.configure',
'define.${obj_name2}',
'configure.${obj_name2}',
'${obj_name2}.define',
'${obj_name2}.configure',
]
mut found_action_name := ''
@@ -47,7 +52,7 @@ fn decode_struct[T](_ T, data string) !T {
}
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 {

View File

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

View File

@@ -9,36 +9,36 @@ import os
// Execute kubectl command with proper error handling
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} '
}
if !k.kubeconfig_path.is_empty() {
if k.kubeconfig_path.len > 0 {
cmd += '--kubeconfig=${k.kubeconfig_path} '
}
if !k.config.context.is_empty() {
if k.config.context.len > 0 {
cmd += '--context=${k.config.context} '
}
cmd += args.command
console.print_debug("executing: ${cmd}")
console.print_debug('executing: ${cmd}')
job := osal.exec(
cmd: cmd
timeout: args.timeout
retry: args.retry
cmd: cmd
timeout: args.timeout
retry: args.retry
raise_error: false
)!
return KubectlResult{
exit_code: job.exit_code
stdout: job.output
stderr: job.error
success: job.exit_code == 0
stdout: job.output
stderr: job.error
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}')
}
version_data := json.decode(map[string]interface{}, result.stdout)!
server_version := version_data['serverVersion'] or { return error('No serverVersion') }
println(result.stdout)
// Get node count
nodes_result := k.kubectl_exec(command: 'get nodes -o json')!
nodes_count := if nodes_result.success {
nodes_data := json.decode(map[string]interface{}, nodes_result.stdout)!
items := nodes_data['items'] or { []interface{}{} }
items.len
} else {
0
}
$dbg;
// version_data := json.decode(map[string]interface{}, result.stdout)!
// server_version := version_data['serverVersion'] or { return error('No serverVersion') }
// Get namespace count
ns_result := k.kubectl_exec(command: 'get namespaces -o json')!
ns_count := if ns_result.success {
ns_data := json.decode(map[string]interface{}, ns_result.stdout)!
items := ns_data['items'] or { []interface{}{} }
items.len
} else {
0
}
// // Get node count
// nodes_result := k.kubectl_exec(command: 'get nodes -o json')!
// nodes_count := if nodes_result.success {
// nodes_data := json.decode(map[string]interface{}, nodes_result.stdout)!
// items := nodes_data['items'] or { []interface{}{} }
// items.len
// } else {
// 0
// }
// Get running pods count
pods_result := k.kubectl_exec(command: 'get pods --all-namespaces -o json')!
pods_count := if pods_result.success {
pods_data := json.decode(map[string]interface{}, pods_result.stdout)!
items := pods_data['items'] or { []interface{}{} }
items.len
} else {
0
}
// // Get namespace count
// ns_result := k.kubectl_exec(command: 'get namespaces -o json')!
// ns_count := if ns_result.success {
// ns_data := json.decode(map[string]interface{}, ns_result.stdout)!
// items := ns_data['items'] or { []interface{}{} }
// 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
}
// // Get running pods count
// pods_result := k.kubectl_exec(command: 'get pods --all-namespaces -o json')!
// pods_count := if pods_result.success {
// pods_data := json.decode(map[string]interface{}, pods_result.stdout)!
// items := pods_data['items'] or { []interface{}{} }
// 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.)
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')!
if !result.success {
return error('Failed to get pods: ${result.stderr}')
}
data := json.decode(map[string]interface{}, result.stdout)!
items := data['items'] or { []interface{}{} }
return items as []map[string]interface{}
println(result.stdout)
$dbg;
// 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')!
if !result.success {
return error('Failed to get deployments: ${result.stderr}')
}
data := json.decode(map[string]interface{}, result.stdout)!
items := data['items'] or { []interface{}{} }
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_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')!
if !result.success {
return error('Failed to get services: ${result.stderr}')
}
data := json.decode(map[string]interface{}, result.stdout)!
items := data['items'] or { []interface{}{} }
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')
}
// Apply YAML file
@@ -157,7 +167,7 @@ pub fn (mut k KubeClient) apply_yaml(yaml_path string) !KubectlResult {
// Validate before applying
validation := yaml_validate(yaml_path)!
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}')!
@@ -204,11 +214,11 @@ pub fn (mut k KubeClient) logs(pod_name string, namespace string, follow bool) !
// Exec into container
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}'
if !container.is_empty() {
mut cmd := 'exec -it ${pod_name} -n ${namespace}'
if container.len > 0 {
cmd += ' -c ${container}'
}
cmd += ' -- ${cmd_args.join(" ")}'
cmd += ' -- ${cmd_args.join(' ')}'
result := k.kubectl_exec(command: cmd, timeout: 300)!
if !result.success {
@@ -228,4 +238,4 @@ pub fn (mut k KubeClient) watch_deployment(name string, namespace string, timeou
cmd := 'rollout status deployment/${name} -n ${namespace} --timeout=${timeout_seconds}s'
result := k.kubectl_exec(command: cmd, timeout: timeout_seconds + 10)!
return result.success
}
}

View File

@@ -11,16 +11,13 @@ const default = true
@[heap]
pub struct KubeClient {
pub mut:
name string = 'default'
name string = 'default'
kubeconfig_path string
config KubeConfig
connected bool
api_version string = 'v1'
kubectl_path string = 'kubectl'
cache_enabled bool = true
cache_ttl_seconds int = 300
cache_enabled bool = true
cache_ttl_seconds int = 300
}
// your checking & initialization code if needed
@@ -41,7 +38,7 @@ fn configure() ! {
/////////////NORMALLY NO NEED TO TOUCH
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)!
return obj
}

View File

@@ -1,14 +1,8 @@
module kubernetes
import incubaid.herolib.data.paramsparser
import incubaid.herolib.data.encoderhero
import incubaid.herolib.data.ourjson
import os
pub const version = '1.0.0'
const singleton = false
const default = true
// K8s API Version and Kind tracking
@[params]
pub struct K8sMetadata {
@@ -166,7 +160,6 @@ pub mut:
insecure_skip_tls_verify bool
}
// Validation result for YAML files
pub struct K8sValidationResult {
pub mut:
@@ -186,31 +179,3 @@ pub mut:
running_pods int
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)!
}