tdfs4ds.diagnostics¶
tdfs4ds.diagnostics
¶
Configuration self-check for the AI-facing parts of tdfs4ds.
tdfs4ds.check_configuration() exercises each configured piece — Vector
Store authentication, the Vector Store service itself, the instruct (LLM)
model, and the embedding model — and reports, per check, whether it works.
When something is missing or wrong it returns a concrete fix hint, in the
same spirit as the prescriptive error messages used elsewhere in the package
(_NOT_CONFIGURED_MSG, ACC_6881_HINT, logger_safe('error', …)):
the point is to name the setting to change, not to hand back a traceback.
Design rules:
- Never raises. Every check is individually wrapped; a check that blows up
is reported as
failedwith its error text, and the remaining checks still run. - Never reveals a secret. Only environment-variable names, URLs, model names and booleans about presence ever reach the result or the printed output. Tokens, keys and passwords do not.
- Reuses, never reimplements. The clients are built by the very same
functions the rest of the package uses (
build_llm,get_embeddings,get_vector_store_embedding,configure_vector_store_auth), so a check that passes means the real code path works — not merely that a parallel reimplementation of it does.
The Teradata database connection itself is not opened here: it is external
to the platform and opened by the caller (in-cluster, typically
tdml.create_context(host=…, logmech='JWT', logdata=…)). When a session is
open, two checks use it (since 0.3.1.222): temporal (VALIDTIME) tables work
for the user, and the session query band can be set and DBQL read back. Where
a check needs a live session, a missing one surfaces as that check's hint.
_TERADATA_PACKAGES
module-attribute
¶
_TERADATAML_MISMATCH_TRIGGERS
module-attribute
¶
_TERADATAML_MISMATCH_HINT
module-attribute
¶
_TERADATAML_MISMATCH_HINT = 'teradataml and teradatagenai are installed but are not a matching pair: this teradatagenai imports a teradataml internal that this teradataml no longer has (seen with teradataml 20.0.0.12 + teradatagenai 20.0.0.9). Nothing is missing, so reinstalling the latest versions does not help. If a package above is marked \'user site ~/.local\', it shadows the environment\'s own copy: remove it there (`%pip uninstall -y teradataml teradatagenai langchain-teradata teradatasqlalchemy` once: pip removes the copy found first, the user site one) so the environment\'s own pair is used. Otherwise install a pair that works together into the running interpreter, for example `%pip install "teradataml==20.0.0.10" "teradatagenai==20.0.0.7"` (verified to import together). Restart the kernel afterwards.'
_LOAD_CONFIG_HINT
module-attribute
¶
_LOAD_CONFIG_HINT = "Call tdfs4ds.load_config() — these variables are set in the environment but never reached the module configuration. tdfs4ds reads TDFS4DS_* into tdfs4ds.<ATTR> once, during `import tdfs4ds`; anything exported (or loaded from a .env) AFTER the import is not picked up automatically. This also happens when the .env sits outside the process's working directory — pass tdfs4ds.load_config(dotenv_path='/path/to/.env'). Note the Vector Store auth variables are exempt: they are read live from os.environ, which is why that check can pass while these do not."
_AUTH_FALLBACK_HINT
module-attribute
¶
_AUTH_FALLBACK_HINT = "teradataml.set_auth_token() was reached but rejected the configuration. Re-check the values behind the variables above (URL reachable? token still valid — an injected OAuth/JWT access token expires?), and see the connect()/setup() log for the 'Vector Store auth ... failed' warning line, which carries the underlying reason."
_CREDENTIAL_REJECTED_HINT
module-attribute
¶
_CREDENTIAL_REJECTED_HINT = 'The service is reachable and enabled — it answered, then the DATABASE refused the credential it presented for you (SQLState 28000). Do NOT go looking at licensing. The usual cause is an EXPIRED token: teradataml stores a ready-made JWT without validating it (the validation call is commented out in its own source), so the auth check above and the health probe both pass with a stale one — this stage is the first that actually uses it. Re-read the current token into TDFS4DS_VECTOR_STORE_AUTH_AUTH_TOKEN and re-run setup()/connect(). If the credential is fresh, check the Basic username/password, then that the database user may log on the way the service logs on for it (set_auth_token requires the privilege to log in with a NULL password — see GRANT LOGON).'
_SERVICE_HINTS
module-attribute
¶
_SERVICE_HINTS = [(('28000', 'userid, password or account is invalid', 'invalid authorization'), _CREDENTIAL_REJECTED_HINT), (('502', '503', '504', 'bad gateway', 'gateway time'), 'Transient gateway error — the Vector Store REST gateway returned a raw 5xx. This usually clears within seconds; retry. tdfs4ds already retries this class internally (_retry_if_transient_gateway_error).'), (('no scheme supplied', 'invalid url', 'none/api'), "The Vector Store client has no base URL — session auth was never established. Fix the 'Vector Store auth' check above first (TDFS4DS_VECTOR_STORE_AUTH_BASE_URL + one credential group); everything else here depends on it."), (('not enabled', 'not licensed', '404', '<html'), 'The Vector Store REST endpoint answered, but not as a Vector Store service. Check that the Enterprise Vector Store feature is licensed and activated on this Vantage instance, and that BASE_URL points at the Vector Store service (in-cluster this is typically http://vectorstore.vectorstore.svc.cluster.local:8000).'), (_TERADATAML_MISMATCH_TRIGGERS, _TERADATAML_MISMATCH_HINT), (('teradatagenai.vector_store', 'teradatagenai.collection', 'teradatagenai.'), "'teradatagenai' imports, but without its vector_store.collection module — so the build being imported is NOT the one you think. Check the origin reported above against the version you expect: in a notebook, `!pip freeze` resolves pip through PATH while the kernel imports from its own sys.executable, so they routinely disagree. Install into the running interpreter (`%pip install -U teradatagenai langchain-teradata` in a notebook, note the % not !), and check for a stray teradatagenai.py/directory shadowing it in the working directory."), (('teradatagenai',), "The 'teradatagenai' package is missing. Install it with `pip install teradatagenai` (it normally arrives transitively with 'langchain-teradata')."), (('connection', 'timed out', 'timeout', 'name or service not known', 'getaddrinfo'), 'Cannot reach TDFS4DS_VECTOR_STORE_AUTH_BASE_URL. Check the URL and that this process can route to it — a cluster-local service name only resolves from inside the cluster.')]
_SERVICE_FALLBACK_HINT
module-attribute
¶
_SERVICE_FALLBACK_HINT = "CollectionManager().health() failed. Verify TDFS4DS_VECTOR_STORE_AUTH_BASE_URL points at a running Vector Store service and that the auth check above passed — or set tdfs4ds.VECTOR_STORE_BACKEND = 'chroma' to use the bundled local index instead."
_REDIRECT_TRIGGERS
module-attribute
¶
_REDIRECT_TRIGGERS = ('error code: 301', 'error code: 302', 'error code: 303', 'error code: 307', 'error code: 308', 'model_dump', "'str' object has no attribute")
_REDIRECT_HINT
module-attribute
¶
_REDIRECT_HINT = "The endpoint did not answer as an OpenAI-compatible API: it either redirected (3xx) or returned a SUCCESS status with a non-JSON body, which the OpenAI SDK hands back as raw text for LangChain to choke on. Both mean something in front of the gateway — an ingress, an SSO login page, an error page — replied instead of the model. This is NOT a TLS or model problem. Check INSTRUCT_MODEL_URL's path (the SDK appends /chat/completions to it) and the redirect target reported above; if that target is a login page, the ingress wants its own credentials and a route that bypasses it (a cluster-local service URL) is the fix."
_INSTRUCT_HINTS
module-attribute
¶
_INSTRUCT_HINTS = [(_REDIRECT_TRIGGERS, _REDIRECT_HINT), (('certificate', 'certificate_verify_failed', 'ssl:', 'sslerror', 'self signed', 'self-signed'), "TLS verification failed — the endpoint's certificate is not trusted by the system CA store, which is normal for an internal host. Set tdfs4ds.INSTRUCT_MODEL_CA_BUNDLE to the issuing CA's PEM file (verification stays on), or as a last resort INSTRUCT_MODEL_VERIFY_SSL=False, which sends the API key over an unauthenticated connection."), (('401', '403', 'unauthorized', 'forbidden', 'invalid api key', 'incorrect api key'), 'The endpoint rejected the credential. Check tdfs4ds.INSTRUCT_MODEL_API_KEY (or TDFS4DS_INSTRUCT_MODEL_API_KEY) — for Bedrock this is the bearer token, and AWS credentials additionally come from AWS_PROFILE / ~/.aws / the IAM role.'), (('404', 'model not found', 'does not exist', 'unknown model', 'no such model'), 'The provider does not recognise tdfs4ds.INSTRUCT_MODEL_MODEL. Call tdfs4ds.list_instruct_models() to see what this endpoint actually serves.'), (('api_key client option', 'missing credentials', 'openai_api_key'), "No API key reached the client. For an OpenAI-compatible gateway set tdfs4ds.INSTRUCT_MODEL_API_KEY; if the gateway checks no key at all it still needs a non-empty value, because the OpenAI SDK refuses to build a client without one — write EMPTY (vLLM's convention). Note that 'none', 'null' and an empty string all load as Python None and will fail this way."), (('422', 'unprocessable', 'guided_json', 'response_format'), "Structured-output rejection (HTTP 422). A LiteLLM/Mistral-style gateway does not accept vLLM's guided_json. Set tdfs4ds.INSTRUCT_MODEL_PROVIDER = 'mistralai' (or 'openai-compatible') rather than 'vllm' for such a gateway."), (('the tool', 'at tool', 'tool name', 'toolspec', 'tools.0', 'invalid tool'), "The endpoint rejected the structured-output TOOL NAME, not the model's ability to produce typed output. On the function_calling path the Pydantic schema's class name is sent as the tool name, and Bedrock-backed routes require it to start with a letter and contain only letters, digits and underscores — a leading underscore is refused. Rename the schema class passed to with_structured_output() so it is letter-initial."), (('proxy', 'socks', 'tunnel'), "The outbound proxy failed. Check tdfs4ds.INSTRUCT_MODEL_PROXY — the scheme selects the protocol ('http://host:3128' or 'socks5://host:1080'); socks5 needs the httpx[socks] extra."), (('connection', 'timed out', 'timeout', 'name or service not known', 'getaddrinfo', 'connect call failed', 'max retries'), 'Cannot reach the model endpoint. Check tdfs4ds.INSTRUCT_MODEL_URL, and set tdfs4ds.INSTRUCT_MODEL_PROXY if outbound traffic must go through a proxy.'), (('region', 'could not connect to the endpoint url', 'no region'), "Region not resolved. Bedrock needs tdfs4ds.INSTRUCT_MODEL_REGION (e.g. 'us-east-1'); GCP Vertex needs INSTRUCT_MODEL_GCP_PROJECT + INSTRUCT_MODEL_GCP_LOCATION."), (('credential', 'could not automatically determine', 'default credentials'), 'Cloud credentials were not found. Bedrock: AWS_PROFILE / ~/.aws/credentials / IAM role. GCP: GOOGLE_APPLICATION_CREDENTIALS, or `gcloud auth application-default login`.'), (('langchain_community', 'langchain-community', 'chatlitellm'), "If tdfs4ds.INSTRUCT_MODEL_URL points at a LiteLLM PROXY (a server, typically :4000), set INSTRUCT_MODEL_PROVIDER='openai-compatible' instead of 'litellm' and keep the same URL, key and model: the proxy speaks the OpenAI API, so ChatOpenAI talks to it natively with no extra packages. The 'litellm' provider is for the other topology — the LiteLLM Python SDK in-process, no proxy, model strings like 'bedrock/amazon.nova-pro-v1:0' — and needs `pip install langchain-community litellm`, neither of which tdfs4ds depends on (recent langchain-community releases have also dropped ChatLiteLLM)."), (('no module named', 'import'), 'A provider package is missing — install the LangChain integration for tdfs4ds.INSTRUCT_MODEL_PROVIDER (openai/vllm/openai-compatible → langchain-openai, bedrock → langchain-aws, gcp → langchain-google-vertexai, mistralai → langchain-mistralai, litellm → langchain-community + litellm).')]
_INSTRUCT_FALLBACK_HINT
module-attribute
¶
_INSTRUCT_FALLBACK_HINT = 'Re-check tdfs4ds.INSTRUCT_MODEL_PROVIDER / _MODEL / _URL / _API_KEY (and _REGION for Bedrock, _GCP_PROJECT + _GCP_LOCATION for Vertex). Without a working instruct model, documentation, query optimization and the consumer agent are all unavailable.'
_EMBEDDING_HINTS
module-attribute
¶
_EMBEDDING_HINTS = [(('input should be a valid string',), 'The known tiktoken-array bug: langchain_openai sent token arrays instead of strings. Build embeddings only via tdfs4ds.agent.embedding.get_embeddings(), which sets check_embedding_ctx_length=False for non-OpenAI endpoints (TEI / vLLM / LiteLLM).'), (('401', '403', 'unauthorized', 'forbidden', 'invalid api key'), 'The endpoint rejected the credential. Check tdfs4ds.EMBEDDING_MODEL_API_KEY — it falls back to INSTRUCT_MODEL_API_KEY when unset, which is often not the right key for a separate embedding endpoint.'), (('404', 'model not found', 'does not exist', 'unknown model'), 'The provider does not recognise tdfs4ds.EMBEDDING_MODEL_MODEL. Call tdfs4ds.list_embedding_models() to see what this endpoint serves.'), (_TERADATAML_MISMATCH_TRIGGERS, _TERADATAML_MISMATCH_HINT), (('teradatagenai',), "tdfs4ds.TERADATA_AI_API_TYPE is set but 'teradatagenai' is not installed. Either `pip install teradatagenai`, or unset TERADATA_AI_API_TYPE to fall back to LangChain embeddings."), (("passed to argument 'api_type'", 'invalid value(s)'), "The 'teradatagenai' being imported does not accept this TERADATA_AI_API_TYPE; its permitted values are listed in the error above. If that list is missing 'td_hosted' (and 'litellm'/'guardrails'), the imported build is older than it appears — compare the origin reported above with `pip freeze`, since in a notebook `!pip` and the kernel resolve to different environments. Install into the running interpreter (`%pip install -U teradatagenai`, % not !). Otherwise pick a listed value and supply that provider's credentials."), (('api_type', 'model_name', 'access_key', 'region'), "TeradataAI rejected its arguments. For api_type='td_hosted' only api_type and model_name are needed (no key, no region); 'aws' additionally needs TERADATA_AI_REGION + TERADATA_AI_ACCESS_KEY, 'nim' needs TERADATA_AI_API_BASE."), (('auth', 'token', 'set_auth_token'), "In-database embedding authenticates through the active Teradata session. Make sure tdml.create_context() succeeded and the 'Vector Store auth' check above passed."), (('certificate', 'certificate_verify_failed', 'ssl:', 'sslerror', 'self signed', 'self-signed'), "TLS verification failed for the EMBEDDING endpoint. Note the asymmetry: INSTRUCT_MODEL_VERIFY_SSL / _CA_BUNDLE are instruct-only by design, so there is no embedding equivalent to set - an embedding client is deliberately never affected by the instruct model's TLS posture. The fixes are to install the issuing CA in the system trust store, to point tdfs4ds.EMBEDDING_MODEL_URL at a route with a publicly trusted certificate, or to route it through tdfs4ds.EMBEDDING_MODEL_PROXY."), (('connection', 'timed out', 'timeout', 'getaddrinfo', 'max retries'), 'Cannot reach the embedding endpoint. Check tdfs4ds.EMBEDDING_MODEL_URL (it falls back to INSTRUCT_MODEL_URL) and tdfs4ds.EMBEDDING_MODEL_PROXY.')]
_EMBEDDING_FALLBACK_HINT
module-attribute
¶
_EMBEDDING_FALLBACK_HINT = 'Re-check tdfs4ds.EMBEDDING_MODEL_PROVIDER / _MODEL / _URL / _API_KEY / _DIM. Each falls back to the matching INSTRUCT_MODEL_* value when unset, which is a common source of surprise when the two models live behind different endpoints.'
_EXPIRED_TOKEN_HINT
module-attribute
¶
_EXPIRED_TOKEN_HINT = "This is a bearer token, and it has a lifetime — typically about two hours on AI Studio. Nothing is misconfigured: mint a fresh one and write it back into %s. In a notebook that is `%%gen_token` followed by `tdfs4ds.generate_ai_studio_env('.env', token=user_token)`, which refreshes every component that authenticates with it. If you would rather the file kept working, `auth='password'` does not expire."
_OLD_SERVICE_HINT
module-attribute
¶
_OLD_SERVICE_HINT = "The Teradata Vector Store SERVICE (not the teradatagenai client package) is version {found}; tdfs4ds needs {minimum} or newer. This is a server-side component of the Vantage system — upgrading it is a platform/admin action, not a pip install, so ask whoever administers this instance to update the Vector Store service. Expect indexing to misbehave until then (build_vector_index may fail or silently index nothing) even though the service is up and your credential is accepted. To keep working meanwhile, set tdfs4ds.VECTOR_STORE_BACKEND = 'chroma' for a local index."
_AWS_DENIED
module-attribute
¶
_AWS_DENIED = ('accessdenied', 'access denied', 'not authorized to perform', 'is not authorized', 'explicit deny', 'forbidden')
_AWS_NO_IDENTITY
module-attribute
¶
_AWS_NO_IDENTITY = ('unable to locate credentials', 'nocredentialserror', 'no credentials', 'could not automatically determine', 'unrecognizedclientexception', 'invalidsignatureexception', 'security token included in the request is invalid', 'expiredtoken', 'signature we calculated does not match')
_TRANSPORT_HINTS
module-attribute
¶
_TRANSPORT_HINTS = [(('mismatch',), 'The certificate is valid but was issued for a different host, so the name in tdfs4ds.INSTRUCT_MODEL_URL is not the one the endpoint answers for. Use the hostname the certificate names (reported above) rather than an IP address or an internal alias.'), (('expired',), "The endpoint's certificate has EXPIRED. Nothing in tdfs4ds can work around that -- it has to be renewed on the server. Until it is, tdfs4ds.INSTRUCT_MODEL_VERIFY_SSL=False lets calls through, at the cost of sending the API key to an unauthenticated peer."), (('untrusted',), "The certificate is not trusted by the CA store in use, which is normal for an internal host. Set tdfs4ds.INSTRUCT_MODEL_CA_BUNDLE to the issuing CA's PEM file -- verification stays ON, which is the difference between this and INSTRUCT_MODEL_VERIFY_SSL=False."), (('unreachable',), 'Nothing answered on the TLS port. Check the host and port in tdfs4ds.INSTRUCT_MODEL_URL, and whether this machine has egress to it (in-cluster a service URL usually works where the public ingress does not).')]
_PROBE_SCHEMA
module-attribute
¶
_PROBE_SCHEMA = {'type': 'object', 'properties': {'colour': {'type': 'string'}, 'count': {'type': 'integer'}}, 'required': ['colour', 'count']}
_PROBE_QUESTION
module-attribute
¶
_MCP_DEFAULT_QUESTION
module-attribute
¶
_LANGCHAIN_PACKAGES
module-attribute
¶
_MCP_HINTS
module-attribute
¶
_MCP_HINTS = [(("from 'mcp.", "from 'mcp'", "from 'langchain_mcp_adapters"), 'The MCP client packages of this Python do not match: mcp 2.x removed names that langchain-mcp-adapters before 0.3.2 imports. Run `%pip install --user "mcp>=1.24,<2" "langchain-mcp-adapters>=0.3.2"` in the Python that runs the agent, then restart it.'), (("no module named 'langchain", "no module named 'langgraph"), 'The langchain packages of this Python do not match (typically langchain-core 0.x under 1.x packages; tdfs4ds needs langchain-core 1.x). Run `%pip install -U langchain-core langgraph langchain-mcp-adapters langchain-openai` in the Python that runs the agent, then restart it.'), (('8017', 'userid, password', 'password or account', 'logon', 'not authorized'), "The MCP server reached Teradata and the DATABASE rejected a logon (Error 8017) — so the URL, TLS and the Authorization header are all fine. WHICH credential was refused depends on what was sent, and the check's detail line above says which. With BASIC, it forwarded tdfs4ds.MCP_USERNAME/_PASSWORD (these take precedence over TDFS4DS_TD_USERNAME/_PASSWORD); compare them against a credential you know works, since a value that merely LOOKS right can be a leftover '<set-this>' placeholder, or carry a trailing .env comment or stray whitespace. With CUSTOM HEADERS (a Bearer token), the token was ACCEPTED — an invalid one is refused earlier and differently, as 'Invalid JWT token' — so this 8017 is the SERVER's own database logon on your behalf. tdfs4ds sent no password, so no tdfs4ds setting will fix it: the gateway itself has to be able to log the token's identity on to Teradata. Try Basic with a known-good password to see whether the server accepts that route instead. Either way, a credential that works with tdml.create_context(logmech='TD2') can still fail here if the server logs on with a different mechanism (LDAP), since the same name and password are then checked against a different directory."), (('401', '403', 'unauthorized', 'forbidden', 'authentication'), 'The MCP server rejected the credentials. For the Enterprise edition set tdfs4ds.MCP_SERVER_EDITION=\'enterprise\' (which sends HTTP Basic built from TDFS4DS_TD_USERNAME/_PASSWORD) and override with TDFS4DS_MCP_USERNAME/_PASSWORD if the MCP account differs from the database one. For any other scheme set TDFS4DS_MCP_SERVER_HEADERS to the exact headers, e.g. {"Authorization": "Bearer ..."}.'), (('certificate', 'ssl', 'tls', 'self signed', 'self-signed', 'certificate_verify_failed'), "TLS verification failed — the server's certificate is not trusted by the system CA store, which is normal for an internal host. Point tdfs4ds.MCP_SERVER_CA_BUNDLE at your internal CA's PEM file. (Verification is never disabled; supplying the trust anchor is the fix.)"), (('connect', 'getaddrinfo', 'name or service not known', 'refused', 'unreachable', 'max retries'), 'Cannot reach tdfs4ds.MCP_SERVER_URL. Check the URL and that this process can route to it — a cluster-local service name only resolves from inside the cluster.'), (('timeout', 'timed out'), 'The MCP server did not answer in time. Raise tdfs4ds.MCP_TIMEOUT, or check whether the server is healthy.'), (('404', '405', '406', 'not found', 'not acceptable', 'method not allowed', 'text/event-stream', 'missing session', 'session id'), "The endpoint answered, but not as an MCP endpoint for this transport. Switch tdfs4ds.MCP_SERVER_TRANSPORT between 'streamable_http' and 'sse' (a server usually serves one of them, sometimes on different paths) and check the URL path."), (('langchain_mcp_adapters', 'no module named'), 'The MCP client library is missing: `pip install langchain-mcp-adapters`.')]
_MCP_FALLBACK_HINT
module-attribute
¶
_MCP_FALLBACK_HINT = "Check tdfs4ds.MCP_SERVER_URL, MCP_SERVER_TRANSPORT ('streamable_http' or 'sse') and MCP_SERVER_EDITION. MCP is optional — the consumer agent works without it, minus the data-query skills."
_MCP_TOOLCALL_HINTS
module-attribute
¶
_MCP_TOOLCALL_HINTS = [(('contextwindowexceeded', 'context window', 'context length', 'maximum context', 'too many tokens', 'prompt is too long'), "The tool SCHEMAS alone overflow the model's context window before your question is even considered — the Enterprise MCP edition exposes ~132 tools, several times what the Community one does. tdfs4ds caps how many are bound via tdfs4ds.MCP_MAX_TOOLS (keeping the ones most relevant to the question); lower it, or configure an instruct model with a larger context window. This is NOT a tool-calling support problem."), (('tool', 'function', 'tool_choice', 'tools', '400', 'unsupported'), 'The MCP server is reachable and its tools were listed, so this is the MODEL side: the configured instruct model (or the gateway in front of it) does not support tool calling. Check tdfs4ds.INSTRUCT_MODEL_MODEL — behind a LiteLLM proxy, tool support depends on the underlying model, not on the proxy.'), (('401', '403', 'unauthorized', 'invalid api key'), 'The model endpoint rejected the credential during the tool probe — check tdfs4ds.INSTRUCT_MODEL_API_KEY. (The MCP server itself answered fine.)')]
_MCP_TOOLCALL_FALLBACK_HINT
module-attribute
¶
_MCP_TOOLCALL_FALLBACK_HINT = 'The MCP tools were listed successfully but the model could not complete a tool-calling round trip. That isolates the problem to the model side, not to MCP connectivity or authentication.'
_NO_CONTEXT_HINT
module-attribute
¶
_NO_CONTEXT_HINT = 'Open the database session first — tdml.create_context(...) or tdfs4ds.connect(...) — then run the check again.'
_BROKEN_SESSION_HINT
module-attribute
¶
_BROKEN_SESSION_HINT = "The database never answered: the error comes from this client's session, which is broken (typically concurrent use of teradataml's single connection, or a reconnect under it), so this check says nothing about the feature. Reconnect (tdfs4ds.reconnect_teradata(), or the app's Connect page) and run the check again."
_BROKEN_SESSION_MARKERS
module-attribute
¶
_BROKEN_SESSION_MARKERS = ("object has no attribute 'cursor'", 'is not a valid connection pool handle')
_CHECKS
module-attribute
¶
_CHECKS = (_check_config_loaded, _check_database_temporal, _check_database_query_band, _check_vector_store_auth, _check_vector_store_service, _check_llm_transport, _check_instruct_model, _check_json_constraints, _check_embedding_model, _check_mcp_server, _check_tracing)
_SELFTEST_HINT
module-attribute
¶
_SELFTEST_HINT = "The Vector Store answered every read-only probe but failed on the write path. The per-step breakdown is under this check's 'selftest' key, and a standalone reproducer containing NO credentials is at report['checks'][...]['selftest']['repro'] — write it to a file and attach it to a support ticket. If the service version above is below the supported minimum, upgrade it first: that is a platform/admin action."
_SELFTEST_OPT_IN_HINT
module-attribute
¶
_SELFTEST_OPT_IN_HINT = 'Run it with tdfs4ds.check_configuration(selftest=True), or on its own with tdfs4ds.selftest_vector_store(). It is opt-in because it is the only check that WRITES: it creates a throwaway table and vector-store collection, embeds a few sentences, retrieves them, changes one, refreshes, and drops both again. The checks above are all read-only, and a read-only probe cannot see the write-path failures that actually break indexing.'
_ASCII_TRANSLITERATION
module-attribute
¶
_ASCII_TRANSLITERATION = maketrans({'—': '-', '–': '-', '…': '...', '→': '->', '’': "'", '‘': "'", '“': '"', '”': '"'})
_flatten_exception
¶
Render an exception, unwrapping ExceptionGroup down to its leaves.
An ExceptionGroup stringifies as "unhandled errors in a TaskGroup (1
sub-exception)" — a count, not a cause — and the real error is only reachable
through .exceptions. Anything running under an anyio task group (the MCP
client, notably) raises one, so without this every distinct failure — expired
certificate, 401, wrong transport, DNS — collapses into the same
uninformative string and no hint can match it.
Leaves are rendered as Type: message so the class name is available for
matching too (SSLCertVerificationError carries the diagnosis even when
its message does not).
Group detection is duck-typed rather than isinstance(exc,
BaseExceptionGroup): that builtin only exists from Python 3.11, and on
older interpreters anyio raises the exceptiongroup backport's own class
instead.
_short
¶
First line of text, truncated — errors are often multi-page.
Exceptions are flattened first, so a task-group wrapper never hides the
cause from the caller or from :func:_match_hint.
_match_hint
¶
Return the first hint in table whose trigger substrings appear in error_text.
table is a list of (triggers, hint) where triggers is a tuple of
lowercase substrings — any one matching is enough. An unrecognised error
still gets fallback, so a failure is never reported without direction.
_package_origin
¶
Report where package actually imports from, and under which interpreter.
This is the evidence that separates "installed but too old" from "a
different environment than the one you inspected" — two failures that look
identical in the traceback and have opposite fixes. Confirmed live: a
notebook reported teradatagenai==20.0.0.7 via !pip freeze (a version
that does support td_hosted and ships vector_store.collection) while
its kernel imported an older build lacking both, because !pip resolves
through PATH and the kernel through its own sys.executable.
_teradata_package_versions
¶
Installed versions of the Teradata client packages and where each sits.
Read from the distribution metadata, so it works when the import itself
fails. A copy in the user site (~/.local) is flagged: it comes first on
sys.path and shadows the environment's own copy, which is how a
%pip install --user in one kernel breaks every other environment.
_is_empty
¶
Is value an attribute that was never populated?
Deliberately NOT not value: False and 0 are legitimate, loaded
configuration values — every _BOOL_VARS member set to false
(DISPLAY_LOGS, DEBUG_MODE, USE_VOLATILE_TABLE,
BUILD_DATASET_AT_UPLOAD, INSTRUCT_MODEL_VERIFY_SSL) plus
EMBEDDING_BATCH_DELAY=0 and SCHEDULING_MAX_WORKERS=0. A truthiness
test reports all of those as never applied, which is how this function came
to exist.
_env_attr_status
¶
Yield (env_var_name, status) for every TDFS4DS_* set in the
environment, comparing it against the module attribute it should have
populated.
Status is one of :data:APPLIED, :data:UNLOADED or :data:DIVERGED, and
the comparison is against the coerced expected value — _coerce is
what load_config itself applies, so 'false' is compared as False
rather than as a non-empty string.
A variable whose coercion fails is skipped: nothing can be said about what the attribute should have become.
_unloaded_env_vars
¶
Return the TDFS4DS_* variable names that are set in the environment
but whose module attribute is still empty.
This is the load-ordering trap: module attributes are a snapshot taken at
import, so a variable exported afterwards is invisible to every check that
reads tdfs4ds.<ATTR> — while the Vector Store auth check, which reads
os.environ directly, still sees it. Reporting "not configured" for a
variable the user demonstrably set would send them looking in the wrong
place.
With no attrs, scans every variable config.py accepts.
_diverged_env_vars
¶
Return the TDFS4DS_* names whose environment value differs from a
module attribute that does hold something.
Distinct from :func:_unloaded_env_vars, and reported without failing the
run: a variable can legitimately differ because the user overrode the
attribute in-session on purpose. But it also catches the quieter half of
the load-ordering trap — an attribute sitting at its default (say
VECTOR_STORE_BACKEND='chroma', or INSTRUCT_MODEL_VERIFY_SSL=True)
while the environment asks for something else is not "unset", so nothing
else would notice.
Names only, never values: this set includes credential variables.
_check_config_loaded
¶
Catch the load-ordering trap before any check blames a missing setting.
Runs first, because every check below reads tdfs4ds.<ATTR>: if the
environment never made it into the module config, they all report "not
configured" about variables the user did set.
_token_state
¶
(state, note) for a bearer token, never raising.
Thin wrapper so every check words expiry the same way and none of them has
to import the reader. See :mod:tdfs4ds.utils.tokens for why the signature
is not verified.
_health_version
¶
Pull the service version out of a health() payload, tolerating shape.
health(return_type='json') has been observed as a mapping with a
version key and as a one-row list of such mappings (it also renders as
a one-row table of status/platform/commit/version). Anything unrecognised
returns None, which is reported as "not verified" rather than failing the
check — a diagnostic must not invent a problem out of a payload shape it
does not recognise.
_parse_version
¶
'2.0.5' -> (2, 0, 5). None when text holds no leading numeric
version, so an unparseable value never fails the check.
_collection_count
¶
Describe the list() result without assuming its shape.
The return type varies with the client build and return_type; the count
is a nicety, the point of the call is that it did not raise.
_safe_url
¶
Strip credentials and query string from url before it is displayed.
An SSO redirect carries tokens in its query string, and a base URL can carry userinfo — neither belongs in a report.
_is_normalisation
¶
True when a redirect only tidies the URL rather than diverting it.
A gateway commonly 301s /v1 to /v1/, and http to https. Calling
that "interception" would raise an alarm on a perfectly healthy endpoint —
which matters now that this probe runs on every check rather than only
after an error had already implicated a redirect.
Only scheme, host and path are compared, each normalised for a trailing
slash. A same-host redirect to a DIFFERENT path (/chat/completions ->
/login) is not normalisation and is still reported.
_redirect_probe
¶
Describe what actually answers at INSTRUCT_MODEL_URL, or None.
Sends no credential: the point is to see whether something in front of
the gateway intercepts the request, and an unauthenticated GET is enough for
that. Redirects are deliberately NOT followed, so the Location is
visible rather than the page it leads to.
Honours the configured proxy and TLS settings, so it exercises the same path the model call took. Never raises — a probe that crashes the diagnostic would be worse than no probe.
_instruct_failure
¶
Build the FAILED result for an instruct-model error.
The redirect probe used to run from here, gated on this error's hint being
the redirect one. It no longer does: _check_llm_transport runs it
unconditionally and earlier, so by the time this is reached the report
already says whether something intercepted the request — and running it
again would ask the same question twice per failing run.
_bedrock_auth_hint
¶
A hint that tells a ROLE user what to fix, instead of asking for a key.
The generic 401/403 hint says "check INSTRUCT_MODEL_API_KEY", which is wrong
advice for the very setup this exists for: an IAM role has no key to check.
Only fires for the Bedrock routes, so every other provider's message is
unchanged. None otherwise.
_certificate_summary
¶
Pull subject CN, issuer CN and expiry out of a parsed certificate dict.
getpeercert() returns this shape only for a certificate the handshake
actually validated, which is why the untrusted path below has to work
harder to say anything at all.
_peek_certificate
¶
Read an UNTRUSTED certificate's identity, best effort.
Needed because the useful sentence for an internal endpoint is "issued by
getpeercert() returns nothing.
ssl._ssl._test_decode_cert is the only certificate parser in the
standard library (the public surface exposes PEM/DER conversion but no
parse), so this uses it deliberately and defensively: it is private, and
every failure here degrades to reporting the verification error alone.
Naming the issuer is a convenience, never the diagnosis.
_tls_probe
¶
Describe what TLS the instruct endpoint presents. Never raises.
Uses the configured trust (INSTRUCT_MODEL_CA_BUNDLE, or the system
store) so the verdict is about the connection tdfs4ds really makes, not an
idealised one.
Returns a dict whose status is one of:
ok
Verified. Carries the certificate identity and days to expiry -- the
thing nothing in this package reported before, and the one failure that
is entirely predictable in advance.
insecure
Handshake succeeded with verification switched off.
untrusted / mismatch / expired
Verification failed, split by cause because the fixes differ: a trust
anchor, the URL's host, or a renewal.
unreachable
Nothing answered on the TLS port.
plain
http:// -- no TLS at all.
skipped
Not probed; reason says why (no URL, or a proxy is in the path).
_check_llm_transport
¶
What the network does to an instruct-model call, before any credential.
Runs FIRST among the model checks so its answer frames theirs: a TLS failure, an expiring certificate or an ingress answering instead of the gateway all surface here as themselves, rather than as a puzzling model error two checks later.
Deliberately credential-free. It also absorbs the redirect probe, which used to run only when an error message had already matched the redirect hint -- and which silently discarded TLS errors, so the one thing it could have reported about a certificate never appeared.
Scoped to the instruct endpoint. INSTRUCT_MODEL_VERIFY_SSL /
_CA_BUNDLE reach only the OpenAI-family clients; Bedrock and Vertex
constrain and secure their transport through their own SDKs.
_resolve_proxy_name
¶
Name the variable a configured proxy came from, never its URL.
A proxy URL can carry credentials in its userinfo, and the variable name is what the reader needs in order to change it.
_probe_documentation_constraint
¶
Exercise the documentation path's structured output, once.
This path is entirely separate from the consumer agent's. It branches on
INSTRUCT_MODEL_PROVIDER to choose a wire parameter — guided_json for
vllm, response_format for the OpenAI family, format= for Ollama
— and, if the backend refuses that parameter, retries once
unconstrained. So a provider whose syntax this endpoint rejects leaves
documentation running on prompt-guided JSON, at strictly lower reliability,
with nothing anywhere reporting it. Nothing probed it until now.
Returns {"status", "mechanism", "detail", "error"} where status is
enforced / relayed / degraded / unconstrained / error.
_pin_settings
¶
Apply recommend to the session and, when asked, to a .env.
Nothing is changed unless pin asks for it. A diagnostic that silently
rewrote configuration would be a trap: INSTRUCT_MODEL_PROVIDER in
particular flows on into export_config and into a deployed pod's
config.yaml, so a reader running a check would be shipping a value they
never chose.
Never raises: a check that cannot record its finding must still report it.
_tool_calling_verdict
¶
What is actually known about TOOL CALLING on this endpoint.
Returns a short sentence, or None when nothing measured it — an
unprobed capability must not be reported as either working or broken.
Evidence in order of directness: the wire sweep asked for a tool call explicitly; failing that, either path having used tool calling is proof; the agent's fallback list is the weakest but still real.
_check_json_constraints
¶
Can this endpoint return typed JSON, and by which syntax?
Answering a plain prompt proves nothing about it. The failure this exists for happens only on the typed path: the consumer agent's very first step is intent classification, and it dies on a gateway that relays the requested response format without enforcing it while every other check stays green. Same split as the MCP check's two stages — listing tools proves nothing about calling them.
Two independent paths are probed, because tdfs4ds has two, keyed on different settings:
- the agent + query optimizer path, langchain's
with_structured_output(STRUCTURED_OUTPUT_METHOD); - the documentation path, a provider-branched wire parameter
(
INSTRUCT_MODEL_PROVIDER), whose silent unconstrained fallback is exactly the kind of degradation a green report used to hide.
Only if one of those reports trouble does it run the nine-request
wire-level sweep (:func:tdfs4ds.probe_json_constraints), which identifies
the serving stack from the accept/reject pattern. A healthy endpoint pays
nothing for it.
_embedding_hint
¶
The embedding hint, plus the one mistake a Bedrock user makes by default.
EMBEDDING_MODEL_MODEL defaults to an OpenAI model name, and the embedding
provider falls back to the instruct provider - so choosing Bedrock for the
LLM silently asks Bedrock for text-embedding-3-small.
langchain_mismatch_hint
¶
The fix for No module named 'langchain_core.messages.content' and the
like: the langchain packages of this Python are from different generations
(a 0.x langchain-core under 1.x packages). Names the versions installed;
None when the error is not a missing langchain module.
mcp_version_mismatch_hint
¶
The fix for cannot import name 'RequestContext' from 'mcp.shared.context':
mcp 2.x under a langchain-mcp-adapters that predates it (< 0.3.2, which caps
mcp below 2). Names the installed versions.
_run_coroutine
¶
Run an async factory to completion from sync code, notebook included.
Mirrors the bridge in skill_mcp_tools: a worker thread owns the event
loop, so this works whether or not the caller already has one running
(Jupyter always does).
_mcp_tool_probe
¶
Ask question through a minimal agent over the MCP tools.
Returns (answer, tools_called).
Deliberately NOT routed through consumer_agent: that agent answers
business questions about the feature store, so a server-administration
question would test its classifier rather than the MCP tool path, and could
fail for reasons unrelated to MCP. This wants the narrowest circuit —
model → tool call → answer — built with the same
create_react_agent the real skill uses.
_never_reached_database
¶
The failure is a broken client session (matched on the messages seen live, never on the absence of a database marker: an unknown error stays the feature's).
_check_database_temporal
¶
Temporal (VALIDTIME) tables work for this user, in this session.
Every catalog and storage table of tdfs4ds is a VALIDTIME table. Proven
with a session-only volatile table (no perm space, nothing left behind):
create it, insert a row, read it CURRENT and NONSEQUENCED, drop it.
_check_database_query_band
¶
The session query band can be set, and DBQL can be read back.
run() tags every query of a process execution with a query band so the
follow-up rows can be joined to DBQL (tdfs4ds.followup_resource_usage).
That needs (1) SET QUERY_BAND — checked by setting a pair, reading it
back and restoring the session band exactly; (2) SELECT on
DBC.QryLogV; (3) DBQL logging for this user. Only (1) can fail the
check: without (2) or (3) runs work, the join is just empty — reported with
the grant / logging statement to ask for.
_check_tracing
¶
Agent tracing (Opik): off, or on and able to build a tracer.
Never sends anything and never shows a setting's value: it reports whether
opik is importable and whether its own configuration loads, and to which
kind of target (cloud / self-hosted) it would send.
_check_vector_store_selftest
¶
Wrap :func:selftest_vector_store as a check row.
Always present in the report, even when not requested. An earlier version
appended the row only when selftest=True, which made the feature
undiscoverable: a passing report gave the reader no way to learn that
everything it just verified was read-only, nor that a deeper check existed.
Opt-in is about not writing without being asked — not about hiding.
Deliberately thin otherwise: the self-test owns its own never-raises and
cleanup contracts, and its detailed report travels intact under
selftest rather than being flattened into one line.
check_configuration
¶
check_configuration(*, live=True, display=True, mcp_question=None, selftest=False, pin=False, progress=None)
Validate the tdfs4ds AI configuration and report what to fix.
Runs eight checks, in dependency order (plus an opt-in ninth, selftest):
- Config load — every check below reads
tdfs4ds.<ATTR>, whichconfig.pypopulates once duringimport tdfs4ds. A variable exported after that import (or a.envoutside the working directory) never reaches the module config, so this reports whichTDFS4DS_*variables are set in the environment but not applied — otherwise every check below blames a setting the user did make. 0b. Temporal (VALIDTIME) — with an open database session: a session-only VALIDTIME table is created, written, readCURRENTandNONSEQUENCED, and dropped. Every tdfs4ds table is temporal. 0c. Query band & DBQL — the session query band can be set (and the caller's band is restored), and DBQL can be read with logging active for this user: whatfollowup_resource_usageneeds to join process executions to their resource usage. Only a refusedSET QUERY_BANDfails; missing DBQL access or logging is reported with the statement to ask the DBA for. - Vector Store auth — resolves and applies
teradataml.set_auth_tokenfrom theTDFS4DS_VECTOR_STORE_AUTH_*variables (JWT / PAT / Basic / OAuth). Skipped when nothing is configured and the backend is not'teradata'. - Vector Store service —
CollectionManager().health()(liveness), then.list()(the first call that actually spends the credential), then the service version against :data:_VECTOR_STORE_MIN_VERSION: an older service is up and accepts the credential, and only misbehaves once something writes, so the version is the one place that is visible in advance. Only whenVECTOR_STORE_BACKEND='teradata'. - LLM transport (TLS) — credential-free, and before the model on
purpose: completes a TLS handshake with
INSTRUCT_MODEL_URLusing the configured trust, reports the certificate's identity and days to expiry, separates a hostname mismatch from an untrusted issuer from an expired certificate (three different fixes), and says whether something in front of the gateway answers instead of it. Instruct-only, becauseINSTRUCT_MODEL_VERIFY_SSL/_CA_BUNDLEare. - Instruct model — builds the client with
build_llm()and sends a hello-world prompt: can it answer at all. - JSON constraints — typed output, which a plain reply proves nothing
about. Probes both paths, because they are independent: the agent /
query-optimizer path (
STRUCTURED_OUTPUT_METHOD) and the documentation path (INSTRUCT_MODEL_PROVIDER), whose silent retry unconstrained is invisible to every other check. Only if one reports trouble does it run the nine-request wire-level sweep that identifies the serving stack; the full sweep travels under the check'sjson_sweepkey. Seepinto record the answer. - Embedding model — with the Teradata backend and
TERADATA_AI_API_TYPEset, checks only thatTeradataAI(...)instantiates (in-database embedding needs nothing more from us); otherwise embeds a hello-world string and verifies the returned vector length againstEMBEDDING_MODEL_DIM. -
MCP server (when
MCP_SERVER_URLis set) — two stages: list the tools (proving reachability, auth and TLS), then drive them with a minimal agent (proving the instruct model can actually call tools, which listing them does not). A failure in the second stage isolates the problem to the model side. -
Vector Store self-test (
selftest=Trueonly) — the checks above are all read-only, and every Vector Store failure this package has hit in the field is invisible to a read-only probe. This one writes: it embeds a handful of sentences into a throwaway collection, retrieves them, changes one, refreshes, retrieves again, and cleans up — see :func:tdfs4ds.selftest_vector_store, whose full per-step report and support reproducer are returned under the check'sselftestkey.
The Teradata database connection is not opened here — it is external to the platform and owned by the caller; the two database checks are skipped (with a hint) when no session is open.
Parameters¶
live : bool, default True
Make the outbound calls (auth handshake, health probe, one LLM prompt,
one embedding). False builds the clients and resolves the
configuration but calls nothing — for CI or an offline sanity pass.
display : bool, default True
Print an aligned summary, with the fix hint under each check that did
not pass.
mcp_question : str, optional
Question sent to the MCP tool-calling probe. Defaults to asking for the
list of available databases — answerable by both the Community and
Enterprise editions. The agent's tool guards apply to a custom question
too, so a probe reaching for feature-store internals is refused exactly
as it would be in the agent.
selftest : bool, default False
Run the write-path Vector Store self-test as a ninth check. Off by
default because it is the only part of this function that creates
and drops database objects; everything else is read-only. Ignored when
live=False or the backend is not 'teradata'.
pin : bool or str, default False
Record what the JSON-constraints check discovers. False reports the
setting and changes nothing. True applies it to the session and
writes it into the first .env on config.py's search path via
:func:tdfs4ds.patch_dotenv, stamped with the endpoint and date it was
probed against — so the value is not re-derived every session, and a
stale pin is visible rather than silent. A string pins into that file
instead.
Worth knowing before using it: ``STRUCTURED_OUTPUT_METHOD`` is
classified as ordinary config by the AgentOps packager, so a pinned
value travels into a deployed pod's ``config.yaml`` — right when the
pod shares this gateway, wrong when it does not.
Returns¶
dict
{"ok": bool, "version": str, "live": bool, "checks": [...]} where
each check is {"name", "label", "status", "detail", "hint", "error"}
and status is 'ok' / 'failed' / 'skipped'. ok is
True when no check failed (a skipped check does not fail the run).
progress(event, data) (optional) is called with "start" and
{"name", "label", "index", "total"} before each check, then with
"result" and the check's row once it is done, so a UI can show each row
as it arrives instead of waiting for the slowest check. A failing hook is
logged and ignored: the report never depends on it.
Notes¶
Never raises, and never returns or prints a secret — only variable names, URLs, model names and presence flags.
Examples¶
import tdfs4ds report = tdfs4ds.check_configuration() # doctest: +SKIP report["ok"] # doctest: +SKIP True tdfs4ds.check_configuration(live=False) # doctest: +SKIP
_stream_handles_unicode
¶
True when stream can encode the emoji + typography this module emits.
_print_report
¶
Print the summary, degrading rather than raising on any console.
Displaying a report must never be the thing that breaks a diagnostic run. A console that cannot encode the emoji/typography (a legacy Windows code page) gets ASCII markers and transliterated punctuation instead of a UnicodeEncodeError; anything still un-encodable after that — odd bytes echoed back inside a provider's error text, say — is replaced, not raised.
title names the report; it defaults to this module's own, so existing
callers are unaffected. It exists so the AgentOps deployment checks
(:mod:tdfs4ds.agentops.preflight, :mod:tdfs4ds.agentops.smoke) render
through this formatter instead of growing a second copy of it.
_format_report
¶
Render report as an aligned plain-text summary (also used by the UI).