Skip to content

agent-config: update rust sdk to expose agent config#2825

Merged
mschuwalow merged 12 commits intomainfrom
agent-config-rust-sdk
Feb 26, 2026
Merged

agent-config: update rust sdk to expose agent config#2825
mschuwalow merged 12 commits intomainfrom
agent-config-rust-sdk

Conversation

@mschuwalow
Copy link
Contributor

@mschuwalow mschuwalow commented Feb 20, 2026

resolves #2805

had to partially implement the discussed #[autoinject] parameter to make it behave the way I want.

Final syntax looks like this:

    #[derive(ConfigSchema)]
    #[allow(unused)]
    struct ConfigAgentConfigNested {
        foo: String,
        bar: i32,
        nested_secret: Secret<bool>,
    }

    #[derive(ConfigSchema)]
    #[allow(unused)]
    struct ConfigAgentConfig {
        url: String,
        port: u32,
        nested: ConfigAgentConfigNested,
        api_key: Secret<String>,
    }

    #[agent_definition]
    trait ConfigAgent: BaseAgent {
        fn new(#[autoinject] config: golem_rust::agentic::Config<ConfigAgentConfig>) -> Self;
    }

    struct ConfigAgentImpl;

    #[agent_implementation]
    impl ConfigAgent for ConfigAgentImpl {
        fn new(#[autoinject] _config: golem_rust::agentic::Config<ConfigAgentConfig>) -> Self {
            Self
        }
    }

The rules here are as follows:

  • non-struct types with schemas (including derived ones using #[derive(Schema)]) map to leaf nodes of configuration (impls of ConfigField trait).
  • For struct types a #[derive(ConfigSchema)] annotation is required that teaches the system how to recurse into the config fields. It also allows using the type as a top-level config type (impls of ConfigSchema trait)

There is a new AutoInjectable trait (currently only implemented for ConfigSchema, but should also be implemented for Principal), that should be used together with the autoinject annotation.

The annotation macros in the example above produce the following code:

// ConfigAgentConfigNested

impl crate::agentic::ConfigSchema for ConfigAgentConfigNested {
    fn describe_config() -> Vec<crate::agentic::ConfigEntry> {
        let mut config_entries = Vec::new();
        {
            let mut collected_entries =
                <String as crate::agentic::ConfigField>::collect_entries(&["foo".to_string()]);
            config_entries.append(&mut collected_entries);
        }
        {
            let mut collected_entries =
                <i32 as crate::agentic::ConfigField>::collect_entries(&["bar".to_string()]);
            config_entries.append(&mut collected_entries);
        }
        {
            let mut collected_entries =
                <Secret<bool> as crate::agentic::ConfigField>::collect_entries(&[
                    "nested_secret".to_string()
                ]);
            config_entries.append(&mut collected_entries);
        }
        config_entries
    }
    fn load(path: &[String]) -> Result<Self, String> {
        Ok(Self {
            foo: {
                let mut field_path = path.to_vec();
                field_path.push("foo".to_string());
                <String as crate::agentic::ConfigField>::load(&field_path)?
            },
            bar: {
                let mut field_path = path.to_vec();
                field_path.push("bar".to_string());
                <i32 as crate::agentic::ConfigField>::load(&field_path)?
            },
            nested_secret: {
                let mut field_path = path.to_vec();
                field_path.push("nested_secret".to_string());
                <Secret<bool> as crate::agentic::ConfigField>::load(&field_path)?
            },
        })
    }
}

impl crate::agentic::ConfigField for ConfigAgentConfigNested {
    const IS_SHARED: bool = false;
    fn collect_entries(path_prefix: &[String]) -> Vec<crate::agentic::ConfigEntry> {
        let mut config_entries = <Self as crate::agentic::ConfigSchema>::describe_config();
        for config_entry in config_entries.iter_mut() {
            let mut key = path_prefix.to_vec();
            key.append(&mut config_entry.key);
            config_entry.key = key;
        }
        config_entries
    }
    fn load(path: &[String]) -> Result<Self, String> {
        <Self as crate::agentic::ConfigSchema>::load(path)
    }
}

// ConfigAgentConfig

impl crate::agentic::ConfigSchema for ConfigAgentConfig {
    fn describe_config() -> Vec<crate::agentic::ConfigEntry> {
        let mut config_entries = Vec::new();
        {
            let mut collected_entries =
                <String as crate::agentic::ConfigField>::collect_entries(&["url".to_string()]);
            config_entries.append(&mut collected_entries);
        }
        {
            let mut collected_entries =
                <u32 as crate::agentic::ConfigField>::collect_entries(&["port".to_string()]);
            config_entries.append(&mut collected_entries);
        }
        {
            let mut collected_entries =
                <ConfigAgentConfigNested as crate::agentic::ConfigField>::collect_entries(&[
                    "nested".to_string(),
                ]);
            config_entries.append(&mut collected_entries);
        }
        {
            let mut collected_entries =
                <Secret<String> as crate::agentic::ConfigField>::collect_entries(&[
                    "api_key".to_string()
                ]);
            config_entries.append(&mut collected_entries);
        }
        config_entries
    }
    fn load(path: &[String]) -> Result<Self, String> {
        Ok(Self {
            url: {
                let mut field_path = path.to_vec();
                field_path.push("url".to_string());
                <String as crate::agentic::ConfigField>::load(&field_path)?
            },
            port: {
                let mut field_path = path.to_vec();
                field_path.push("port".to_string());
                <u32 as crate::agentic::ConfigField>::load(&field_path)?
            },
            nested: {
                let mut field_path = path.to_vec();
                field_path.push("nested".to_string());
                <ConfigAgentConfigNested as crate::agentic::ConfigField>::load(&field_path)?
            },
            api_key: {
                let mut field_path = path.to_vec();
                field_path.push("api_key".to_string());
                <Secret<String> as crate::agentic::ConfigField>::load(&field_path)?
            },
        })
    }
}

impl crate::agentic::ConfigField for ConfigAgentConfig {
    const IS_SHARED: bool = false;
    fn collect_entries(path_prefix: &[String]) -> Vec<crate::agentic::ConfigEntry> {
        let mut config_entries = <Self as crate::agentic::ConfigSchema>::describe_config();
        for config_entry in config_entries.iter_mut() {
            let mut key = path_prefix.to_vec();
            key.append(&mut config_entry.key);
            config_entry.key = key;
        }
        config_entries
    }
    fn load(path: &[String]) -> Result<Self, String> {
        <Self as crate::agentic::ConfigSchema>::load(path)
    }
}

// agent definition

#[allow(async_fn_in_trait)]
trait ConfigAgent: BaseAgent {
    fn new(config: golem_rust::agentic::Config<ConfigAgentConfig>) -> Self;

    async fn load_snapshot(&mut self, _bytes: Vec<u8>) -> Result<(), String> {
        Err("load_snapshot not implemented".to_string())
    }
    async fn save_snapshot(&self) -> Result<Vec<u8>, String> {
        Err("save_snapshot not implemented".to_string())
    }
    fn __register_agent_type() {
        let agent_type = golem_rust::agentic::ExtendedAgentType {
            type_name: "ConfigAgent".to_string(),
            description: "".to_string(),
            methods: alloc::vec::Vec::new(),
            dependencies: alloc::vec::Vec::new(),
            constructor: {
                let constructor_data_schema = {
                    {
                        let mut constructor_multi_modal_inputs = alloc::vec::Vec::new();
                        let mut constructor_default_inputs = alloc::vec::Vec::new();
                        if !constructor_multi_modal_inputs.is_empty() {
                            golem_rust::agentic::ExtendedDataSchema::Multimodal(
                                constructor_multi_modal_inputs,
                            )
                        } else {
                            golem_rust::agentic::ExtendedDataSchema::Tuple(
                                constructor_default_inputs,
                            )
                        }
                    }
                };
                golem_rust::agentic::ExtendedAgentConstructor {
                    name: Some("new".to_string()),
                    description: "".to_string(),
                    prompt_hint: None,
                    input_schema: constructor_data_schema,
                }
            },
            mode: golem_rust::golem_agentic::golem::agent::common::AgentMode::Durable,
            http_mount: None,
            snapshotting: golem_rust::golem_agentic::golem::agent::common::Snapshotting::Disabled,
            config: {
                let mut all_configs = Vec::new();
                {
                    let mut config =  <golem_rust::agentic::Config<ConfigAgentConfig>as ::golem_rust::agentic::AutoInjectable> ::config_entries();
                    all_configs.append(&mut config);
                }
                all_configs
            },
        };
        let principal_input_parameters = agent_type.principal_params_in_constructor();
        if let Some(http_mount) = &agent_type.http_mount {
            golem_rust::agentic::validate_http_mount(
                &agent_type.type_name,
                &http_mount,
                &agent_type.constructor.to_agent_constructor(),
                &principal_input_parameters,
            )
            .expect("HTTP mount validation failed");
        }
        for method in &agent_type.methods {
            golem_rust::agentic::validate_http_endpoint(
                &agent_type.type_name,
                method,
                agent_type.http_mount.as_ref(),
            )
            .expect("Agent method HTTP endpoint validation failed");
        }
        golem_rust::agentic::register_agent_type(
            golem_rust::agentic::AgentTypeName(agent_type.type_name.to_string()),
            agent_type,
        );
    }
}
pub struct ConfigAgentClient {
    agent_id: golem_rust::golem_wasm::AgentId,
    wasm_rpc: golem_rust::golem_wasm::WasmRpc,
}
impl ConfigAgentClient {
    pub fn get() -> ConfigAgentClient {
        let agent_type =
            golem_rust::golem_agentic::golem::agent::host::get_agent_type("ConfigAgent")
                .expect("Internal Error: Agent type not registered");
        let mut structured_values = alloc::vec::Vec::new();
        let data_value = if structured_values.is_empty() {
            golem_rust::golem_agentic::golem::agent::common::DataValue::Tuple(alloc::vec::Vec::new())
        } else {
            match &structured_values[0] {
                golem_rust::agentic::StructuredValue::Default(_) => {
                    let element_values = structured_values.into_iter().map(|vt|{
                        if let golem_rust::agentic::StructuredValue::Default(ev) = vt {
                            ev
                        }else {
                            {
                                core::panicking::panic_fmt(core::const_format_args!("constructor parameter type mismatch. Expected default, found multimodal"));
                            };
                        }
                    }).collect:: <Vec<golem_rust::golem_agentic::golem::agent::common::ElementValue>>();
                    golem_rust::golem_agentic::golem::agent::common::DataValue::Tuple(
                        element_values,
                    )
                }
                golem_rust::agentic::StructuredValue::Multimodal(_) => {
                    let multimodal_result = structured_values
                        .remove(0)
                        .get_multimodal_value()
                        .expect(
                        "Constructor parameter type mismatch. Expected multimodal, found default",
                    );
                    golem_rust::golem_agentic::golem::agent::common::DataValue::Multimodal(
                        multimodal_result,
                    )
                }
                golem_rust::agentic::StructuredValue::AutoInjected(_) => {
                    {
                        core::panicking::panic_fmt(core::const_format_args!("Internal Error: Trying to convert principal parameter to data value in RPC call"));
                    };
                }
            }
        };
        let agent_id_string = golem_rust::golem_agentic::golem::agent::host::make_agent_id(
            "ConfigAgent",
            &data_value,
            None,
        )
        .expect("Internal Error: Failed to make agent id");
        let agent_id = golem_rust::golem_wasm::AgentId {
            agent_id: agent_id_string,
            component_id: agent_type.implemented_by.clone(),
        };
        let wasm_rpc = golem_rust::golem_wasm::WasmRpc::new(&agent_id);
        ConfigAgentClient {
            agent_id: agent_id,
            wasm_rpc: wasm_rpc,
        }
    }
    pub fn new_phantom() -> ConfigAgentClient {
        let agent_type =
            golem_rust::golem_agentic::golem::agent::host::get_agent_type("ConfigAgent")
                .expect("Internal Error: Agent type not registered");
        let mut structured_values = alloc::vec::Vec::new();
        let data_value = if structured_values.is_empty() {
            golem_rust::golem_agentic::golem::agent::common::DataValue::Tuple(alloc::vec::Vec::new())
        } else {
            match &structured_values[0] {
                golem_rust::agentic::StructuredValue::Default(_) => {
                    let element_values = structured_values.into_iter().map(|vt|{
                        if let golem_rust::agentic::StructuredValue::Default(ev) = vt {
                            ev
                        }else {
                            {
                                core::panicking::panic_fmt(core::const_format_args!("constructor parameter type mismatch. Expected default, found multimodal"));
                            };
                        }
                    }).collect:: <Vec<golem_rust::golem_agentic::golem::agent::common::ElementValue>>();
                    golem_rust::golem_agentic::golem::agent::common::DataValue::Tuple(
                        element_values,
                    )
                }
                golem_rust::agentic::StructuredValue::Multimodal(_) => {
                    let multimodal_result = structured_values
                        .remove(0)
                        .get_multimodal_value()
                        .expect(
                        "Constructor parameter type mismatch. Expected multimodal, found default",
                    );
                    golem_rust::golem_agentic::golem::agent::common::DataValue::Multimodal(
                        multimodal_result,
                    )
                }
                golem_rust::agentic::StructuredValue::AutoInjected(_) => {
                    {
                        core::panicking::panic_fmt(core::const_format_args!("Internal Error: Trying to convert principal parameter to data value in RPC call"));
                    };
                }
            }
        };
        let agent_id_string = golem_rust::golem_agentic::golem::agent::host::make_agent_id(
            "ConfigAgent",
            &data_value,
            Some(golem_rust::Uuid::new_v4().into()),
        )
        .expect("Internal Error: Failed to make agent id");
        let agent_id = golem_rust::golem_wasm::AgentId {
            agent_id: agent_id_string,
            component_id: agent_type.implemented_by.clone(),
        };
        let wasm_rpc = golem_rust::golem_wasm::WasmRpc::new(&agent_id);
        ConfigAgentClient {
            agent_id: agent_id,
            wasm_rpc: wasm_rpc,
        }
    }
    pub fn get_phantom(phantom_id: golem_rust::Uuid) -> ConfigAgentClient {
        let agent_type =
            golem_rust::golem_agentic::golem::agent::host::get_agent_type("ConfigAgent")
                .expect("Internal Error: Agent type not registered");
        let mut structured_values = alloc::vec::Vec::new();
        let data_value = if structured_values.is_empty() {
            golem_rust::golem_agentic::golem::agent::common::DataValue::Tuple(alloc::vec::Vec::new())
        } else {
            match &structured_values[0] {
                golem_rust::agentic::StructuredValue::Default(_) => {
                    let element_values = structured_values.into_iter().map(|vt|{
                        if let golem_rust::agentic::StructuredValue::Default(ev) = vt {
                            ev
                        }else {
                            {
                                core::panicking::panic_fmt(core::const_format_args!("constructor parameter type mismatch. Expected default, found multimodal"));
                            };
                        }
                    }).collect:: <Vec<golem_rust::golem_agentic::golem::agent::common::ElementValue>>();
                    golem_rust::golem_agentic::golem::agent::common::DataValue::Tuple(
                        element_values,
                    )
                }
                golem_rust::agentic::StructuredValue::Multimodal(_) => {
                    let multimodal_result = structured_values
                        .remove(0)
                        .get_multimodal_value()
                        .expect(
                        "Constructor parameter type mismatch. Expected multimodal, found default",
                    );
                    golem_rust::golem_agentic::golem::agent::common::DataValue::Multimodal(
                        multimodal_result,
                    )
                }
                golem_rust::agentic::StructuredValue::AutoInjected(_) => {
                    {
                        core::panicking::panic_fmt(core::const_format_args!("Internal Error: Trying to convert principal parameter to data value in RPC call"));
                    };
                }
            }
        };
        let agent_id_string = golem_rust::golem_agentic::golem::agent::host::make_agent_id(
            "ConfigAgent",
            &data_value,
            Some(phantom_id.into()),
        )
        .expect("Internal Error: Failed to make agent id");
        let agent_id = golem_rust::golem_wasm::AgentId {
            agent_id: agent_id_string,
            component_id: agent_type.implemented_by.clone(),
        };
        let wasm_rpc = golem_rust::golem_wasm::WasmRpc::new(&agent_id);
        ConfigAgentClient {
            agent_id: agent_id,
            wasm_rpc: wasm_rpc,
        }
    }
    pub fn phantom_id(&self) -> Option<golem_rust::Uuid> {
        let (_, _, phantom_id) =
            golem_rust::golem_agentic::golem::agent::host::parse_agent_id(&self.agent_id.agent_id)
                .unwrap();
        phantom_id.map(|id| id.into())
    }
    pub fn get_agent_id(&self) -> String {
        self.agent_id.agent_id.clone()
    }
}

// agent impl

impl ConfigAgent for ConfigAgentImpl {
    fn new(_config: golem_rust::agentic::Config<ConfigAgentConfig>) -> Self {
        Self
    }
}

impl golem_rust::agentic::BaseAgent for ConfigAgentImpl {
    fn get_agent_id(&self) -> String {
        golem_rust::agentic::get_agent_id().agent_id
    }
    #[allow(
        elided_named_lifetimes,
        clippy::async_yields_async,
        clippy::diverging_sub_expression,
        clippy::let_unit_value,
        clippy::needless_arbitrary_self_type,
        clippy::no_effect_underscore_binding,
        clippy::shadow_same,
        clippy::type_complexity,
        clippy::type_repetition_in_bounds,
        clippy::used_underscore_binding
    )]
    fn invoke<'life0, 'async_trait>(
        &'life0 mut self,
        method_name: String,
        input: golem_rust::golem_agentic::golem::agent::common::DataValue,
        principal: golem_rust::golem_agentic::golem::agent::common::Principal,
    ) -> ::core::pin::Pin<
        Box<
            dyn ::core::future::Future<
                    Output = Result<
                        golem_rust::golem_agentic::golem::agent::common::DataValue,
                        golem_rust::golem_agentic::golem::agent::common::AgentError,
                    >,
                > + 'async_trait,
        >,
    >
    where
        'life0: 'async_trait,
        Self: 'async_trait,
    {
        Box::pin(async move {
            if let ::core::option::Option::Some(__ret) = ::core::option::Option::None::<
                Result<
                    golem_rust::golem_agentic::golem::agent::common::DataValue,
                    golem_rust::golem_agentic::golem::agent::common::AgentError,
                >,
            > {
                #[allow(unreachable_code)]
                return __ret;
            }
            let mut __self = self;
            let method_name = method_name;
            let input = input;
            let principal = principal;
            let __ret: Result<
                golem_rust::golem_agentic::golem::agent::common::DataValue,
                golem_rust::golem_agentic::golem::agent::common::AgentError,
            > = {
                match method_name.as_str() {
                    _ => Err(golem_rust::agentic::invalid_method_error(method_name)),
                }
            };
            #[allow(unreachable_code)]
            __ret
        })
    }
    fn get_definition(&self) -> golem_rust::golem_agentic::golem::agent::common::AgentType {
        golem_rust::agentic::get_agent_type_by_name(&golem_rust::agentic::AgentTypeName(
            "ConfigAgent".to_string(),
        ))
        .expect("Agent definition not found")
    }
    #[allow(
        elided_named_lifetimes,
        clippy::async_yields_async,
        clippy::diverging_sub_expression,
        clippy::let_unit_value,
        clippy::needless_arbitrary_self_type,
        clippy::no_effect_underscore_binding,
        clippy::shadow_same,
        clippy::type_complexity,
        clippy::type_repetition_in_bounds,
        clippy::used_underscore_binding
    )]
    fn load_snapshot_base<'life0, 'async_trait>(
        &'life0 mut self,
        bytes: Vec<u8>,
    ) -> ::core::pin::Pin<Box<dyn ::core::future::Future<Output = Result<(), String>> + 'async_trait>>
    where
        'life0: 'async_trait,
        Self: 'async_trait,
    {
        Box::pin(async move {
            if let ::core::option::Option::Some(__ret) =
                ::core::option::Option::None::<Result<(), String>>
            {
                #[allow(unreachable_code)]
                return __ret;
            }
            let mut __self = self;
            let bytes = bytes;
            let __ret: Result<(), String> = {
                use golem_rust::agentic::snapshot_auto::SnapshotLoadFallback;
                let mut helper = golem_rust::agentic::snapshot_auto::LoadHelper(__self);
                helper.snapshot_load(&bytes)
            };
            #[allow(unreachable_code)]
            __ret
        })
    }
    #[allow(
        elided_named_lifetimes,
        clippy::async_yields_async,
        clippy::diverging_sub_expression,
        clippy::let_unit_value,
        clippy::needless_arbitrary_self_type,
        clippy::no_effect_underscore_binding,
        clippy::shadow_same,
        clippy::type_complexity,
        clippy::type_repetition_in_bounds,
        clippy::used_underscore_binding
    )]
    fn save_snapshot_base<'life0, 'async_trait>(
        &'life0 self,
    ) -> ::core::pin::Pin<
        Box<
            dyn ::core::future::Future<Output = Result<golem_rust::agentic::SnapshotData, String>>
                + 'async_trait,
        >,
    >
    where
        'life0: 'async_trait,
        Self: 'async_trait,
    {
        Box::pin(async move {
            if let ::core::option::Option::Some(__ret) =
                ::core::option::Option::None::<Result<golem_rust::agentic::SnapshotData, String>>
            {
                #[allow(unreachable_code)]
                return __ret;
            }
            let __self = self;
            let __ret: Result<golem_rust::agentic::SnapshotData, String> = {
                use golem_rust::agentic::snapshot_auto::SnapshotSaveFallback;
                let helper = golem_rust::agentic::snapshot_auto::SaveHelper(__self);
                helper.snapshot_save()
            };
            #[allow(unreachable_code)]
            __ret
        })
    }
}
struct __ConfigAgentInitiator;

impl golem_rust::agentic::AgentInitiator for __ConfigAgentInitiator {
    #[allow(
        elided_named_lifetimes,
        clippy::async_yields_async,
        clippy::diverging_sub_expression,
        clippy::let_unit_value,
        clippy::needless_arbitrary_self_type,
        clippy::no_effect_underscore_binding,
        clippy::shadow_same,
        clippy::type_complexity,
        clippy::type_repetition_in_bounds,
        clippy::used_underscore_binding
    )]
    fn initiate<'life0, 'async_trait>(
        &'life0 self,
        params: golem_rust::golem_agentic::golem::agent::common::DataValue,
        principal: golem_rust::golem_agentic::golem::agent::common::Principal,
    ) -> ::core::pin::Pin<
        Box<
            dyn ::core::future::Future<
                    Output = Result<
                        (),
                        golem_rust::golem_agentic::golem::agent::common::AgentError,
                    >,
                > + 'async_trait,
        >,
    >
    where
        'life0: 'async_trait,
        Self: 'async_trait,
    {
        Box::pin(async move {
            if let ::core::option::Option::Some(__ret) = ::core::option::Option::None::<
                Result<(), golem_rust::golem_agentic::golem::agent::common::AgentError>,
            > {
                #[allow(unreachable_code)]
                return __ret;
            }
            let __self = self;
            let params = params;
            let principal = principal;
            let __ret: Result<(), golem_rust::golem_agentic::golem::agent::common::AgentError> = {
                let mut input_param_index = 0;
                let _config:golem_rust::agentic::Config<ConfigAgentConfig>  =  <golem_rust::agentic::Config<ConfigAgentConfig>as ::golem_rust::agentic::AutoInjectable> ::autoinject().map_err(|err|golem_rust::agentic::internal_error(alloc::__export::must_use({
                    alloc::fmt::format(alloc::__export::format_args!("Failed loading config of type {}: {}",err,stringify!(golem_rust::agentic::Config<ConfigAgentConfig>)))
                })))? ;
                let agent_instance = Box::new(<ConfigAgentImpl>::new(_config));
                let agent_id = golem_rust::bindings::golem::api::host::get_self_metadata().agent_id;
                golem_rust::agentic::register_agent_instance(
                    golem_rust::agentic::ResolvedAgent::new(agent_instance),
                );
                Ok(())
            };
            #[allow(unreachable_code)]
            __ret
        })
    }
}
#[allow(unused)]
fn __register_agent_initiator_configagent() {
    #[allow(unsafe_code)]
    {
        #[allow(unsafe_code)]
        #[cfg_attr(
            any(
                target_os = "linux",
                target_os = "android",
                target_os = "freebsd",
                target_os = "netbsd",
                target_os = "openbsd",
                target_os = "dragonfly",
                target_os = "illumos",
                target_os = "haiku",
                target_family = "wasm"
            ),
            link_section = ".init_array"
        )]
        #[cfg_attr(target_vendor = "apple", link_section = "__DATA,__mod_init_func")]
        #[cfg_attr(windows, link_section = ".CRT$XCU")]
        #[used]
        #[allow(non_upper_case_globals, non_snake_case)]
        #[doc(hidden)]
        static f: extern "C" fn() -> usize = {
            #[cfg(not(clippy))]
            #[cfg_attr(
                any(target_os = "linux", target_os = "android"),
                link_section = ".text.startup"
            )]
            #[allow(non_snake_case)]
            extern "C" fn f() -> usize {
                unsafe {
                    __register_agent_initiator_configagent();
                    0
                }
            };
            #[cfg(clippy)]
            #[allow(non_snake_case)]
            extern "C" fn f() -> usize {
                unsafe {
                    __register_agent_initiator_configagent();
                    0
                }
            };
            #[cfg(not(any(
                target_os = "linux",
                target_os = "android",
                target_os = "freebsd",
                target_os = "netbsd",
                target_os = "openbsd",
                target_os = "dragonfly",
                target_os = "illumos",
                target_os = "haiku",
                target_vendor = "apple",
                target_family = "wasm",
                windows
            )))]
            compile_error!("#[ctor]/#[dtor] is not supported on the current target");
            f
        };;
        #[cfg(not(any(
            target_os = "linux",
            target_os = "android",
            target_os = "freebsd",
            target_os = "netbsd",
            target_os = "openbsd",
            target_os = "dragonfly",
            target_os = "illumos",
            target_os = "haiku",
            target_vendor = "apple",
            target_family = "wasm",
            windows
        )))]
        compile_error!("#[ctor]/#[dtor] is not supported on the current target");
    }
    #[cfg(target_family = "wasm")]
    {
        static __CTOR__INITILIZED: core::sync::atomic::AtomicBool =
            core::sync::atomic::AtomicBool::new(false);
        if __CTOR__INITILIZED.swap(true, core::sync::atomic::Ordering::Relaxed) {
            return;
        }
    }
    {
        ConfigAgentImpl::__register_agent_type();
        golem_rust::agentic::register_agent_initiator(
            &"ConfigAgent",
            std::sync::Arc::new(__ConfigAgentInitiator),
        );
    }
}

@mschuwalow mschuwalow self-assigned this Feb 20, 2026
Base automatically changed from agent-config-update-wit to main February 24, 2026 16:12
@mschuwalow mschuwalow force-pushed the agent-config-rust-sdk branch from 9fbc0b3 to 8c7ad7d Compare February 24, 2026 20:54
@mschuwalow mschuwalow requested a review from afsalthaj February 24, 2026 20:54
@mschuwalow mschuwalow force-pushed the agent-config-rust-sdk branch from 8c7ad7d to 3d171b4 Compare February 24, 2026 21:13
@mschuwalow mschuwalow marked this pull request as ready for review February 24, 2026 21:26

#[diagnostic::on_unimplemented(
message = "Only autoinjectable types are allowed to be annotated with the autoinject attribute. Supported\n\
types are `golem_rust::agentic::Config`"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: is it not going to be confusing that we have to mark config parameters with autoinject, but we don't have to principals?

Copy link
Contributor Author

@mschuwalow mschuwalow Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plan is to have the same for principal in 1.5 misc #2830.

This is just already partially implemented for config as I couldn't get the behavior I want otherwise.

}

#[derive(ConfigSchema)]
#[allow(unused)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor thing:

Will user always end up needing to write this unused thing? Isn't it actually used in the next few lines.?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just related to the config fields not being used in the tests. No unused needed in real code.

@mschuwalow mschuwalow force-pushed the agent-config-rust-sdk branch from 24349f0 to 7fca5af Compare February 26, 2026 12:11
@github-actions
Copy link

github-actions bot commented Feb 26, 2026

✅ All contributors have signed the CLA.
Posted by the CLA Assistant Lite bot.

@afsalthaj
Copy link
Contributor

I have read the CLA Document and I hereby sign the CLA

github-actions bot added a commit that referenced this pull request Feb 26, 2026
@mschuwalow mschuwalow changed the title Agent config rust sdk agent-config: update rust sdk to expose agent config Feb 26, 2026
@mschuwalow mschuwalow merged commit d6c92a2 into main Feb 26, 2026
21 of 24 checks passed
@mschuwalow mschuwalow deleted the agent-config-rust-sdk branch February 26, 2026 16:40
@github-actions github-actions bot locked and limited conversation to collaborators Feb 26, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement agent configuration in the rust sdk

3 participants