Skip to content

.env file

tdfs4ds reads .env files at import time without touching os.environ. Only variables starting with TDFS4DS_ are picked up — your other tooling stays unaffected.

Search order

  1. ./.env (current working directory)
  2. ~/.tdfs4ds/.env (user-wide)

You can point at a custom file with load_config(dotenv_path='/path/.env').

Example

# Feature store identity
TDFS4DS_SCHEMA=MY_DATABASE
# Optional: dedicated database for VIEWS (leave unset to co-locate them with tables in SCHEMA)
# TDFS4DS_SCHEMA_VIEW=MY_VIEWS_DATABASE
TDFS4DS_DATA_DOMAIN=MY_PROJECT
TDFS4DS_DISPLAY_LOGS=true
TDFS4DS_STORE_FEATURE=MERGE
TDFS4DS_VARCHAR_SIZE=1024

# LLM (used by genai + agent + query optimizer)
TDFS4DS_INSTRUCT_MODEL_PROVIDER=openai
TDFS4DS_INSTRUCT_MODEL_MODEL=gpt-4o
TDFS4DS_INSTRUCT_MODEL_URL=
TDFS4DS_INSTRUCT_MODEL_API_KEY=sk-...

# Embeddings (LangChain — used by both Chroma and Teradata backends)
TDFS4DS_EMBEDDING_MODEL_PROVIDER=vllm
TDFS4DS_EMBEDDING_MODEL_URL=https://api.example.com/v1/e5
TDFS4DS_EMBEDDING_MODEL_API_KEY=...
TDFS4DS_EMBEDDING_MODEL_MODEL=text-embedding-3-small
TDFS4DS_EMBEDDING_MODEL_DIM=1536

# Vector store backend
TDFS4DS_VECTOR_STORE_BACKEND=chroma                 # or 'teradata'

# Chroma backend
TDFS4DS_CHROMA_MODE=local
TDFS4DS_CHROMA_PATH=./tdfs4ds_chroma

# Teradata backend (opt-in TeradataAI in-DB embedding — leave blank to keep LangChain)
TDFS4DS_TERADATA_VECTOR_STORE_PREFIX=tdfs4ds
TDFS4DS_TERADATA_AI_API_TYPE=
TDFS4DS_TERADATA_AI_MODEL_NAME=
TDFS4DS_TERADATA_AI_REGION=
TDFS4DS_TERADATA_AI_API_BASE=
TDFS4DS_TERADATA_AI_ACCESS_KEY=

Quoted values and export KEY=VALUE syntax are both supported.

Always .gitignore your .env

Add .env to .gitignore to keep API keys out of source control. tdfs4ds.connect does not redact tokens from logs.

Setting it up interactively (any platform)

configure_interactively() asks for the database connection, the instruct model, the embedding model and the vector store, and updates your existing .env in place. Comments, ordering and variables it does not know about are kept; only values that changed are rewritten.

import tdfs4ds

tdfs4ds.configure_interactively('.env')
tdfs4ds.configure_interactively('.env', sections=('instruct',))   # one part only
  • Existing values are the defaults. Press Enter to keep them. A secret is shown as set (hidden, N chars).
  • Models come from the endpoint. OpenAI-compatible endpoints are listed over HTTP; Bedrock is listed with boto3, including inference profiles (many models can only be invoked through a profile id such as us.anthropic.…). For Azure, Vertex and LiteLLM there is no list call, so you type the name and the live check confirms it.
  • An API key is asked for only when needed. The wizard tries without one first, so a Bedrock role or profile, or a keyless gateway, works as is. It asks for a key only after an authentication failure.
  • The Teradata Vector Store may not be available. If its auth or service check fails, the wizard offers local Chroma and does not write the Teradata settings.
  • Secrets are written only if you agree (write_secrets='ask' by default, with no as the default answer). Otherwise they stay in the session and the wizard lists the variables to set yourself.

To change one value without any questions, use tdfs4ds.patch_dotenv(path, {...}). generate_ai_studio_env() below is different: it rewrites the whole file.

Generating it on Teradata AI Studio

On AI Studio you should not write this file by hand. The platform publishes most of it already, so generate_ai_studio_env() derives what it can, asks for the rest and writes the file:

import tdfs4ds

tdfs4ds.detect_ai_studio()                  # what the platform exposes
preview = tdfs4ds.generate_ai_studio_env('.env')
print(preview)                              # secrets shown as "set (hidden, N chars)"

16 of the 22 variables need no input: 9 are derived from a platform variable and 7 are constant for AI Studio.

Target Source
TD_USERNAME, TD_DATABASE the …/teradata_username claim inside OAUTH_ACCESS_TOKEN
INSTRUCT_MODEL_URL TD_LITELLM_BASE_URL
VECTOR_STORE_AUTH_BASE_URL TD_VECTORSTORE_BASE_URL, minus its /data-insights suffix
INSTRUCT_MODEL_VERIFY_SSL TD_SSL_VERIFY
MCP_SERVER_URL https://{JH_FQDN}/one-td/mcp
the password, the API key, the instruct model asked -- no platform source exists

The printed preview hides secret values while the file on disk receives the real ones, so the output is safe to keep in a saved notebook.

It asks, checks, and only then writes

The order is deliberate. A .env that does not work is worse than no .env, because it looks finished:

  1. Blank answers are asked again, with the reason the value is needed. What is still unanswered is reported as a gap rather than quietly written as <set-this> — that placeholder loads as the literal string and fails much later, somewhere that names neither the variable nor this function.
  2. Two answers are handled for you. The instruct model is chosen from the list the endpoint itself serves, defaulting to a Ministral 14B when one is offered. No API key is written as None, which loads as None — correct for a gateway that needs none, and honest rather than a fake key that fails as a 401 elsewhere.
  3. Two questions are asked, not defaulted. How everything authenticates (password or jwt) and which route LLM calls take — neither is derivable, and both change what the file costs you. The platform publishes exactly one LiteLLM address and it is http://, so deriving the route means always choosing plaintext and never saying so; the generated file would show a URL and nothing about the API key travelling in clear.
  4. The configuration is checked before the write (check=True, the default): the values are staged into the session, describe_config() and check_configuration() run against them, and the session is put back. Under auth='jwt' the database logon is attempted too — check_configuration leaves the database connection alone by design, but this function writes that credential, and a refusal there surfaces much later as an Error 8017 naming nothing you set.
  5. Gaps or failing checks block the write until you confirm. Non-interactive callers get nothing written and the preview returned; force=True writes anyway. Either way the return value shows exactly what the file would have contained.
  6. A successful write ends with load_config(), so the session is configured when the call returns — no separate step to forget.

Add pin=True to record what the JSON-constraints check discovers (typically STRUCTURED_OUTPUT_METHOD) into the file just written.

tdfs4ds.generate_ai_studio_env('.env', pin=True)          # ask, check, write, record
tdfs4ds.generate_ai_studio_env('.env', check=False, force=True)   # just write it

The Enterprise MCP gateway needs its own token

The one-td gateway authenticates against the platform's identity provider, not the database. Sending MCP_USERNAME/MCP_PASSWORD there returns the database's own Error 8017, The UserId, Password or Account is invalid — which reads like a wrong password and is really the wrong kind of credential.

Most of the time you already have one: the session's OAUTH_ACCESS_TOKEN is issued by the same identity provider, to the same OAuth client (onetd-<SITE>) and with the same scope the device flow requests, so the generator uses it and writes MCP_SERVER_HEADERS. It expires about two hours after issue, and then:

token = tdfs4ds.generate_mcp_token()     # device flow: prints a URL and a code

That is an OAuth 2.0 Device Authorization Grant. The identity provider, the client id and the device endpoint are all read from what the platform publishes (IDP_BASE_URL, IDP_CLIENT_ID, TD_KEYCLOAK_BASE_URL) rather than guessed — which IdP a site uses cannot be inferred from its hostname, and a site whose FQDN says preprod may well be on the staging provider.

Two tokens, and only one is a database credential

This is the trap behind most Error 8017s on AI Studio. The platform hands out two tokens that look alike:

token authenticates against is a database credential
OAUTH_ACCESS_TOKEN (the session's) the platform's own services not everywhere
%gen_token → user_token Vantage yes — it is what the notebooks pass to create_context(logmech='JWT')

Under auth='jwt' one token becomes three credentials: the database logon, the Vector Store, and the MCP bearer. Where the platform token is not database-capable, all three fail — and the gateway's failure is the confusing one, because it opens a Teradata session on your behalf, so a refusal arrives as the database's Error 8017 rather than a rejected bearer.

Pass the right one and every component gets it:

%gen_token
tdfs4ds.generate_ai_studio_env('.env', token=user_token)

mcp_token= overrides the gateway's alone, for a site where the gateway and the database want different tokens. The logon is attempted before the file is written, so a refused token is reported instead of discovered later.

auth='jwt' writes no password at all, but the token expires about two hours after it was issued, so the file then has to be regenerated. Password mode stays valid indefinitely. The variant you do not pick is written into the file commented out, as are slots for INSTRUCT_MODEL_CA_BUNDLE and STRUCTURED_OUTPUT_METHOD — uncomment either, or let check_configuration(pin=True) fill the second one in.

Which route the LLM calls take

tdfs4ds.generate_ai_studio_env('.env', llm='ingress')   # https, one hop more
tdfs4ds.generate_ai_studio_env('.env', llm='incluster') # http, what the platform publishes

Left unset, this is asked. The in-cluster service speaks plaintext only — there is no TLS on that port — so it is a choice of route, not of whether to encrypt the one you have. 'incluster' is the shortest path and the one a deployed AgentOps pod takes, but the API key and every prompt travel in clear; 'ingress' (https://{JH_FQDN}/one-td/litellm, the same one-td pattern as the MCP gateway) adds TLS at the cost of a hop. Whichever you do not choose is written commented out. On an http:// URL INSTRUCT_MODEL_VERIFY_SSL is omitted entirely: there is no TLS to verify.

On an https:// URL the generator handshakes with the endpoint rather than inheriting TD_SSL_VERIFY. That variable is a platform-wide hint — in practice about the in-cluster services and their own certificates — and copying it onto a public ingress leaves the connection encrypted but not authenticated, which is the half that does not protect an API key. If the certificate verifies against the system trust store, verification stays on and the file says the hint was not copied; if it does not, INSTRUCT_MODEL_VERIFY_SSL=false is written to keep the endpoint usable, with INSTRUCT_MODEL_CA_BUNDLE named as the better fix. If the endpoint cannot be reached the platform hint is used, unchanged — "could not tell" is never treated as "it is fine". The probe sends no credential.

Off AI Studio

The function still runs, but it can derive nothing and its constants (TD_HOST=dbc, VECTOR_STORE_BACKEND=teradata, …) are AI Studio assumptions -- the generated file labels them as such. Elsewhere, prefer export_config(fmt='env', source='module'), which writes a .env from any working session on any platform.

Quoting, comments and $VAR

Three rules of the parser, each of which has bitten someone:

  • A trailing comment is stripped from an unquoted value when whitespace precedes the # -- so PASSWORD=secret # prod authenticates with secret, as python-dotenv does. A # with nothing before it stays in the value (abc#def is abc#def).
  • Quoting makes a value verbatim, which is the only way to keep a literal " #": TDFS4DS_MCP_PASSWORD="abc #def". There is no escape syntax inside quotes.
  • There is no $VAR expansion. TDFS4DS_VECTOR_STORE_AUTH_AUTH_TOKEN=$OAUTH_ACCESS_TOKEN is read by tdfs4ds as that literal string. It works only when something else interpolates first -- python-dotenv's load_dotenv() does, a shell or a Docker --env-file does. If you rely on tdfs4ds's own auto-discovery, write the resolved value (which is what generate_ai_studio_env does).

Reloading

tdfs4ds.load_config()                       # default search paths
tdfs4ds.load_config(dotenv_path='./.env.prod')  # explicit override