.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¶
./.env(current working directory)~/.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 asus.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:
- 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. - 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 asNone— correct for a gateway that needs none, and honest rather than a fake key that fails as a 401 elsewhere. - 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. - The configuration is checked before the write (
check=True, the default): the values are staged into the session,describe_config()andcheck_configuration()run against them, and the session is put back. Underauth='jwt'the database logon is attempted too —check_configurationleaves the database connection alone by design, but this function writes that credential, and a refusal there surfaces much later as anError 8017naming nothing you set. - Gaps or failing checks block the write until you confirm. Non-interactive
callers get nothing written and the preview returned;
force=Truewrites anyway. Either way the return value shows exactly what the file would have contained. - 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:
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:
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
#-- soPASSWORD=secret # prodauthenticates withsecret, aspython-dotenvdoes. A#with nothing before it stays in the value (abc#defisabc#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
$VARexpansion.TDFS4DS_VECTOR_STORE_AUTH_AUTH_TOKEN=$OAUTH_ACCESS_TOKENis read by tdfs4ds as that literal string. It works only when something else interpolates first --python-dotenv'sload_dotenv()does, a shell or a Docker--env-filedoes. If you rely on tdfs4ds's own auto-discovery, write the resolved value (which is whatgenerate_ai_studio_envdoes).