1 """Config flow for Google Generative AI Conversation integration."""
3 from __future__
import annotations
5 from collections.abc
import Mapping
6 from functools
import partial
8 from types
import MappingProxyType
11 from google.ai
import generativelanguage_v1beta
12 from google.api_core.client_options
import ClientOptions
13 from google.api_core.exceptions
import ClientError, GoogleAPIError
14 import google.generativeai
as genai
15 import voluptuous
as vol
39 CONF_DANGEROUS_BLOCK_THRESHOLD,
40 CONF_HARASSMENT_BLOCK_THRESHOLD,
41 CONF_HATE_BLOCK_THRESHOLD,
45 CONF_SEXUAL_BLOCK_THRESHOLD,
50 RECOMMENDED_CHAT_MODEL,
51 RECOMMENDED_HARM_BLOCK_THRESHOLD,
52 RECOMMENDED_MAX_TOKENS,
53 RECOMMENDED_TEMPERATURE,
58 _LOGGER = logging.getLogger(__name__)
60 STEP_API_DATA_SCHEMA = vol.Schema(
62 vol.Required(CONF_API_KEY): str,
66 RECOMMENDED_OPTIONS = {
67 CONF_RECOMMENDED:
True,
68 CONF_LLM_HASS_API: llm.LLM_API_ASSIST,
69 CONF_PROMPT: llm.DEFAULT_INSTRUCTIONS_PROMPT,
74 """Validate the user input allows us to connect.
76 Data has the keys from STEP_USER_DATA_SCHEMA with values provided by the user.
78 client = generativelanguage_v1beta.ModelServiceAsyncClient(
79 client_options=ClientOptions(api_key=data[CONF_API_KEY])
81 await client.list_models(timeout=5.0)
85 """Handle a config flow for Google Generative AI Conversation."""
90 self, user_input: dict[str, Any] |
None =
None
91 ) -> ConfigFlowResult:
92 """Handle the initial step."""
93 errors: dict[str, str] = {}
94 if user_input
is not None:
97 except GoogleAPIError
as err:
98 if isinstance(err, ClientError)
and err.reason ==
"API_KEY_INVALID":
99 errors[
"base"] =
"invalid_auth"
101 errors[
"base"] =
"cannot_connect"
103 _LOGGER.exception(
"Unexpected exception")
104 errors[
"base"] =
"unknown"
112 title=
"Google Generative AI",
114 options=RECOMMENDED_OPTIONS,
118 data_schema=STEP_API_DATA_SCHEMA,
119 description_placeholders={
120 "api_key_url":
"https://aistudio.google.com/app/apikey"
126 self, user_input: dict[str, Any] |
None =
None
127 ) -> ConfigFlowResult:
128 """Handle the initial step."""
132 self, entry_data: Mapping[str, Any]
133 ) -> ConfigFlowResult:
134 """Handle configuration by re-auth."""
138 self, user_input: dict[str, Any] |
None =
None
139 ) -> ConfigFlowResult:
140 """Dialog that informs the user that reauth is required."""
141 if user_input
is not None:
146 step_id=
"reauth_confirm",
147 description_placeholders={
148 CONF_NAME: reauth_entry.title,
149 CONF_API_KEY: reauth_entry.data.get(CONF_API_KEY,
""),
155 config_entry: ConfigEntry,
157 """Create the options flow."""
162 """Google Generative AI config flow options handler."""
164 def __init__(self, config_entry: ConfigEntry) ->
None:
165 """Initialize options flow."""
167 CONF_RECOMMENDED,
False
171 self, user_input: dict[str, Any] |
None =
None
172 ) -> ConfigFlowResult:
173 """Manage the options."""
176 if user_input
is not None:
178 if user_input[CONF_LLM_HASS_API] ==
"none":
179 user_input.pop(CONF_LLM_HASS_API)
186 CONF_RECOMMENDED: user_input[CONF_RECOMMENDED],
187 CONF_PROMPT: user_input[CONF_PROMPT],
188 CONF_LLM_HASS_API: user_input[CONF_LLM_HASS_API],
194 data_schema=vol.Schema(schema),
200 options: dict[str, Any] | MappingProxyType[str, Any],
202 """Return a schema for Google Generative AI completion options."""
203 hass_apis: list[SelectOptionDict] = [
214 for api
in llm.async_get_apis(hass)
221 "suggested_value": options.get(
222 CONF_PROMPT, llm.DEFAULT_INSTRUCTIONS_PROMPT
228 description={
"suggested_value": options.get(CONF_LLM_HASS_API)},
232 CONF_RECOMMENDED, default=options.get(CONF_RECOMMENDED,
False)
236 if options.get(CONF_RECOMMENDED):
239 api_models = await hass.async_add_executor_job(partial(genai.list_models))
243 label=api_model.display_name,
244 value=api_model.name,
246 for api_model
in sorted(api_models, key=
lambda x: x.display_name)
248 api_model.name !=
"models/gemini-1.0-pro"
249 and "vision" not in api_model.name
250 and "generateContent" in api_model.supported_generation_methods
254 harm_block_thresholds: list[SelectOptionDict] = [
261 value=
"BLOCK_ONLY_HIGH",
265 value=
"BLOCK_MEDIUM_AND_ABOVE",
269 value=
"BLOCK_LOW_AND_ABOVE",
274 mode=SelectSelectorMode.DROPDOWN, options=harm_block_thresholds
282 description={
"suggested_value": options.get(CONF_CHAT_MODEL)},
283 default=RECOMMENDED_CHAT_MODEL,
289 description={
"suggested_value": options.get(CONF_TEMPERATURE)},
290 default=RECOMMENDED_TEMPERATURE,
294 description={
"suggested_value": options.get(CONF_TOP_P)},
295 default=RECOMMENDED_TOP_P,
299 description={
"suggested_value": options.get(CONF_TOP_K)},
300 default=RECOMMENDED_TOP_K,
304 description={
"suggested_value": options.get(CONF_MAX_TOKENS)},
305 default=RECOMMENDED_MAX_TOKENS,
308 CONF_HARASSMENT_BLOCK_THRESHOLD,
310 "suggested_value": options.get(CONF_HARASSMENT_BLOCK_THRESHOLD)
312 default=RECOMMENDED_HARM_BLOCK_THRESHOLD,
313 ): harm_block_thresholds_selector,
315 CONF_HATE_BLOCK_THRESHOLD,
316 description={
"suggested_value": options.get(CONF_HATE_BLOCK_THRESHOLD)},
317 default=RECOMMENDED_HARM_BLOCK_THRESHOLD,
318 ): harm_block_thresholds_selector,
320 CONF_SEXUAL_BLOCK_THRESHOLD,
322 "suggested_value": options.get(CONF_SEXUAL_BLOCK_THRESHOLD)
324 default=RECOMMENDED_HARM_BLOCK_THRESHOLD,
325 ): harm_block_thresholds_selector,
327 CONF_DANGEROUS_BLOCK_THRESHOLD,
329 "suggested_value": options.get(CONF_DANGEROUS_BLOCK_THRESHOLD)
331 default=RECOMMENDED_HARM_BLOCK_THRESHOLD,
332 ): harm_block_thresholds_selector,
OptionsFlow async_get_options_flow(ConfigEntry config_entry)
ConfigFlowResult async_step_reauth_confirm(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_reauth(self, Mapping[str, Any] entry_data)
ConfigFlowResult async_step_api(self, dict[str, Any]|None user_input=None)
ConfigFlowResult async_step_user(self, dict[str, Any]|None user_input=None)
None __init__(self, ConfigEntry config_entry)
ConfigFlowResult async_step_init(self, dict[str, Any]|None user_input=None)
last_rendered_recommended
ConfigEntry _get_reauth_entry(self)
ConfigFlowResult async_create_entry(self, *str title, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None, Mapping[str, Any]|None options=None)
ConfigFlowResult async_update_reload_and_abort(self, ConfigEntry entry, *str|None|UndefinedType unique_id=UNDEFINED, str|UndefinedType title=UNDEFINED, Mapping[str, Any]|UndefinedType data=UNDEFINED, Mapping[str, Any]|UndefinedType data_updates=UNDEFINED, Mapping[str, Any]|UndefinedType options=UNDEFINED, str|UndefinedType reason=UNDEFINED, bool reload_even_if_entry_is_unchanged=True)
ConfigFlowResult async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
ConfigEntry config_entry(self)
None config_entry(self, ConfigEntry value)
_FlowResultT async_show_form(self, *str|None step_id=None, vol.Schema|None data_schema=None, dict[str, str]|None errors=None, Mapping[str, str]|None description_placeholders=None, bool|None last_step=None, str|None preview=None)
_FlowResultT async_create_entry(self, *str|None title=None, Mapping[str, Any] data, str|None description=None, Mapping[str, str]|None description_placeholders=None)
dict google_generative_ai_config_option_schema(HomeAssistant hass, dict[str, Any]|MappingProxyType[str, Any] options)
None validate_input(HomeAssistant hass, dict[str, Any] data)