Skip to content

tdfs4ds.configure

tdfs4ds.configure

Interactive, non-destructive configuration of tdfs4ds.

:func:configure_interactively walks through the settings that make the AI-facing half of tdfs4ds work -- the database connection, the instruct model, the embedding model and the vector store -- and records the answers in an existing .env line by line.

How it differs from :func:tdfs4ds.generate_ai_studio_env:

  • That one is specific to Teradata AI Studio and rewrites the whole file. This one works anywhere and goes through :func:tdfs4ds.patch_dotenv, so comments, ordering, commented-out alternatives and variables it knows nothing about survive. Only values that actually changed are touched.
  • Variables already present in the file are the defaults of the questions; Enter keeps them. A secret is shown as set (hidden, N chars).

Principles, each one learned the hard way elsewhere in this package:

  • Ask the endpoint, do not make the user type a model name. Models are listed with the provider's own mechanism -- HTTP /models for the OpenAI-compatible family, boto3 for Bedrock (foundation models and inference profiles, because many models can only be invoked through a profile id).
  • A key is asked for only when it is needed. Cloud roles, profiles and keyless gateways work without one, so the wizard first tries without and asks only when the provider answers with an authentication failure.
  • A backend that cannot work is not written. The Teradata Vector Store may be missing, unlicensed or unreachable; that is detected and local Chroma is offered instead.
  • Checks describe the file, not the session. They run through :class:tdfs4ds.ai_studio._Staged, which clears managed variables the file does not set.
  • Secrets are written only after an explicit yes, in a separate patch_dotenv(..., allow_secrets=True) call, and never printed.

Nothing here runs at import time.

PLACEHOLDER module-attribute

PLACEHOLDER = '<set-this>'

logger module-attribute

logger = getLogger(__name__)

SECTIONS module-attribute

SECTIONS = ('connection', 'instruct', 'embedding', 'vector_store')

PROVIDERS module-attribute

PROVIDERS = ('openai', 'openai-compatible', 'vllm', 'litellm', 'azure', 'bedrock', 'aws', 'gcp', 'mistralai')

_BEDROCK_TRANSPORTS module-attribute

_BEDROCK_TRANSPORTS = ('converse', 'openai-compatible')

_BEDROCK_AUTH module-attribute

_BEDROCK_AUTH = ('ambient role', 'api key')

_HTTP_PROVIDERS module-attribute

_HTTP_PROVIDERS = ('openai', 'vllm', 'openai-compatible', 'azure', 'mistralai')

_UNLISTABLE module-attribute

_UNLISTABLE = ('azure', 'gcp', 'litellm')

_PROVIDER_FIELDS module-attribute

_PROVIDER_FIELDS = {'openai': (('URL', 'API base URL (Enter for api.openai.com)', False),), 'openai-compatible': (('URL', 'API base URL', True),), 'vllm': (('URL', 'API base URL', True),), 'mistralai': (('URL', 'API base URL (Enter for the default)', False),), 'litellm': (('URL', 'LiteLLM proxy URL (Enter to use provider env vars)', False),), 'azure': (('URL', 'Azure endpoint, https://<name>.openai.azure.com/', True), ('AZURE_DEPLOYMENT', 'deployment name (Enter = model name)', False), ('AZURE_API_VERSION', 'API version', False)), 'bedrock': (('REGION', 'AWS region, e.g. us-east-1', True),), 'aws': (('REGION', 'AWS region, e.g. us-east-1', True),), 'gcp': (('GCP_PROJECT', 'GCP project id', True), ('GCP_LOCATION', 'GCP location', False))}

_GENERIC_NO_IDENTITY module-attribute

_GENERIC_NO_IDENTITY = ('unauthorized', 'invalid api key', 'incorrect api key', 'invalid_api_key', 'authentication', 'api_key client option must be set')

_STATUS module-attribute

_STATUS = compile('(error code|status code|http|status)[: ]+(401|403)\\b', I)

_FOREIGN_ENV module-attribute

_FOREIGN_ENV = ('AWS_BEARER_TOKEN_BEDROCK', 'AWS_DEFAULT_REGION', 'AWS_REGION', 'AZURE_OPENAI_API_KEY', 'AZURE_OPENAI_ENDPOINT', 'OPENAI_API_VERSION', 'GOOGLE_CLOUD_PROJECT', 'CLOUD_ML_REGION')

_VS_AUTH_ATTRS module-attribute

_VS_AUTH_ATTRS = ('VECTOR_STORE_AUTH_BASE_URL', 'VECTOR_STORE_AUTH_USERNAME', 'VECTOR_STORE_AUTH_PASSWORD', 'VECTOR_STORE_AUTH_AUTH_TOKEN', 'VECTOR_STORE_AUTH_PAT_TOKEN', 'VECTOR_STORE_AUTH_PEM_FILE')

_VS_MECHANISMS module-attribute

_VS_MECHANISMS = ('basic', 'jwt', 'pat')

_Ctx

Bases: object

What is in the file, what the user has changed, what was found.

path instance-attribute

path = path

enabled instance-attribute

enabled = enabled

current instance-attribute

current = {}

values instance-attribute

values = {}

checks instance-attribute

checks = []

skipped instance-attribute

skipped = []

__init__

__init__(path, enabled)

get

get(attr)

The value that is in force: a pending change, else the file's.

set

set(attr, value)

Record a change. An answer equal to the file's records nothing.

forget

forget(*attrs)

effective

effective()

_stage

Bases: object

Apply the effective configuration to the session, then put it back.

__init__

__init__(ctx, extra=None)

__enter__

__enter__()

__exit__

__exit__(*exc_info)

_is_placeholder

_is_placeholder(value)

True when value is a generator placeholder rather than a real value.

_is_secret

_is_secret(attr)

True when attr names a credential that must never be written in clear.

_parse_dotenv

_parse_dotenv(path)

Parse path as a .env file and return a {ATTR: raw_value} dict for every TDFS4DS_* variable found.

Handles: - KEY=VALUE - KEY="VALUE" / KEY='VALUE' - export KEY=VALUE - # comments (whole-line and trailing) and blank lines

Trailing-comment rule, matching python-dotenv so a file behaves the same whichever loader reads it: in an unquoted value, whitespace followed by # starts a comment and is dropped. A # with no whitespace before it is part of the value (PASSWORD=abc#def is abc#def), and quoting keeps everything verbatim (PASSWORD="abc #def") — which is how to write a value that genuinely contains " #".

This used to keep the comment: PASSWORD=secret # prod became the literal secret # prod, which then failed authentication somewhere far away from the file that caused it.

patch_dotenv

patch_dotenv(path, updates, *, comment=None, allow_secrets=False)

Set individual TDFS4DS_* variables in an existing .env, in place.

:func:export_config and :func:tdfs4ds.generate_ai_studio_env both rewrite the whole file, which loses its comments, its ordering and the commented-out alternatives they deliberately put there. This changes one line at a time, so a probed value can be recorded into a file a human maintains.

It is line-based rather than parse-and-rewrite because :func:_parse_dotenv is lossy for this purpose — it drops comments, ordering and quoting, so it cannot serve as the read side of a read-modify-write.

Which line is changed, and why:

  • The last uncommented occurrence. _parse_dotenv assigns as it reads, so on a duplicate the last one is what takes effect; patching any other would leave the file saying one thing and meaning another.
  • Otherwise the first commented-out slot (#TDFS4DS_X=) is uncommented in place. A generated file offers such slots precisely so that turning one on is an edit.
  • Otherwise appended, preceded by comment if given.

A commented slot is never uncommented while a live line exists — a generated file carries commented alternatives next to the live choice (two MCP_SERVER_URL lines, for instance), and enabling one of those would silently switch the setting to the alternative.

Parameters

path : str or Path An existing .env. Creating one is :func:export_config's or :func:tdfs4ds.generate_ai_studio_env's job, not this function's, so a missing file raises rather than being conjured half-populated. updates : dict {'STRUCTURED_OUTPUT_METHOD': 'function_calling'} — keys with or without the TDFS4DS_ prefix. Values are rendered the way :func:_coerce reads them back, so None is written as the literal None (which loads as None), not as an empty line. comment : str, optional Provenance note, written as a trailing comment on the line itself. Say where the value came from and when: a pinned setting that was right in one session is wrong in another, and a stale pin should be visible rather than silent.

Trailing rather than above, because the whole line is rewritten on each
call — so re-pinning replaces the note instead of stacking a new one on
every run. It is dropped for a value that has to be quoted: the parser
takes a quoted value verbatim, so a trailing comment would become part
of it. Newlines are folded to "; " for the same reason.

allow_secrets : bool, default False Required to write a credential. A general-purpose file patcher must not be the thing that puts a password into a file someone later commits, so by default a name :func:_is_secret recognises is refused.

Returns

dict {'updated': [...], 'added': [...], 'unchanged': [...]} of full TDFS4DS_* names. Nothing is written when everything is unchanged.

Raises

ValueError path does not exist, a key is not a registered variable (it would only earn an "unknown .env variable" warning on load), or it is a credential without allow_secrets.

_is_missing_credentials

_is_missing_credentials(text)

True when nothing authenticated the call: a key (or a role) is missing.

_is_not_permitted

_is_not_permitted(text)

True when the caller is known but this action or model is refused.

_needs_key

_needs_key(ctx, kind, text)

Should the wizard ask for an API key because of this error?

Yes for a missing identity. For a refusal: no when the provider's identity is an environment role (Bedrock, Vertex) -- it was recognised and denied, a key is not the answer -- and, for a keyless HTTP gateway, only when no key is held yet (a bare 403 there usually means "send one").

_full_error

_full_error(exc)

The whole error, cause chain included. Never the first line only: the half that names the fault is often the later one.

_ascii

_ascii(text)

_say

_say(text='')

_hidden

_hidden(value)

_show

_show(attr, value)

A value as it may be printed: secrets are never printed.

_no_api_key

_no_api_key()

_prompts

_prompts()

_ask_text

_ask_text(ctx, label, attr, *, default=None, required=False, why=None)

Ask for a plain value; the file's value is the default.

_ask_secret

_ask_secret(ctx, label, attr, *, required=False, why=None)

Ask for a secret. Enter keeps the one already set; it is never echoed.

_choose

_choose(ctx, label, options, default)

Pick from options by number or name; anything else keeps default.

_confirm

_confirm(ctx, question, default)

_run_check

_run_check(ctx, check_name, extra=None)

Run one diagnostics._check_* live, against the effective config.

_report_check

_report_check(ctx, result)

Print one check result in full and remember it.

_bedrock_inference_profiles

_bedrock_inference_profiles(region)

Inference-profile ids. Many on-demand models (Claude, Nova) can only be invoked through a profile id such as us.anthropic.<model>, never through the bare foundation-model id, so listing foundation models alone would offer ids that fail at the first call.

_list_model_ids

_list_model_ids(ctx, kind, extra=None)

Models the configured provider serves: (ids, error_text_or_None).

Uses the package's own listing code (list_instruct_models / list_embedding_models) so the proxy and TLS settings apply, and adds Bedrock inference profiles for instruct models. Any failure is returned, in full, for the caller to show -- never raised.

_pick_model

_pick_model(ctx, kind, ids, attr)

Choose from the models the endpoint serves; the file's value leads.

_logon_kwargs

_logon_kwargs(ctx)

_section_connection

_section_connection(ctx)

_model_prefix

_model_prefix(kind)

_provider_for

_provider_for(ctx, kind)

The provider in force for kind (embedding falls back to instruct).

_ensure_key

_ensure_key(ctx, kind, ids_error)

Ask for an API key after the provider rejected a keyless attempt.

_provider_settings

_provider_settings(ctx, kind)

Provider, then only the fields that provider needs.

_bedrock_transport

_bedrock_transport(ctx, prefix)

Converse (the default) or Bedrock's OpenAI-compatible endpoint, and how that endpoint authenticates: an API key, or the environment's role.

_keyless_extra

_keyless_extra(ctx, kind, provider)

For the OpenAI-compatible family a client cannot be built without a key, so the keyless attempt carries the conventional placeholder.

_section_model

_section_model(ctx, kind)

_type_models

_type_models(ctx, kind, model_attr)

Type the model, or several candidates to try (comma-separated).

The list was unavailable, so the call is the only judge of what works. Candidates are probed with one real call each; the first that answers wins (or the user chooses among several). A model id, an inference-profile id (eu./us./apac. prefix) and an ARN are all accepted.

_profile_variants

_profile_variants(model, region)

Inference-profile spellings of a bare Bedrock model id, region first.

_probe_candidates

_probe_candidates(ctx, kind, model_attr, candidates)

Try each candidate with a live call; return the one to use.

_live_model_check

_live_model_check(ctx, kind, key_attr)

_teradata_vector_store

_teradata_vector_store(ctx)

Configure and verify the Teradata Vector Store. True when it works.

_section_vector_store

_section_vector_store(ctx)

_is_secret_value

_is_secret_value(attr, value)

_review_and_write

_review_and_write(ctx, write_secrets)

_merge

_merge(report, part)

_reload

_reload(ctx)

Make the session use what was just written, and say what still wins.

configure_interactively

configure_interactively(path='.env', *, sections=SECTIONS, check=True, write_secrets='ask', prompt=True)

Configure tdfs4ds by answering questions; update path in place.

Existing TDFS4DS_* lines are the defaults and are edited in place, so comments, ordering and unrelated variables survive. A missing file is created. Only values that changed are written.

Parameters

path : str or Path, default '.env' The file to update. sections : iterable of str Any of 'connection', 'instruct', 'embedding', 'vector_store'. Default: all, in that order. check : bool, default True Run the live checks (model call, vector-store service). With False only the questions are asked and model lists are not fetched. write_secrets : {'ask', 'never', 'always'}, default 'ask' Whether API keys, passwords and tokens may be written to the file. 'ask' asks once, with the default answer no. prompt : bool, default True False (or no terminal) asks nothing and writes nothing.

Returns

dict {'path', 'updated', 'added', 'unchanged', 'skipped', 'checks'} -- variable names and statuses only, never a value.

_skip_checks

_skip_checks(ctx, name)

check=False: ask the plain questions, fetch and call nothing.