lexsi-ds-agent / docs /MODELING_TOOLS.md
adityatannaarya's picture
added utils and reorganized
e68dffb
|
Raw
History Blame Contribute Delete
12.6 kB

A newer version of the Gradio SDK is available: 6.22.0

Upgrade

Modeling Tools - added by Aditya

This document describes the modeling-tools layer of the Lexsi DS Agent v0: the three production tools owned by Aditya per the design doc Β§4 (train_tabular_model, predict, explain_prediction) plus the supporting test infrastructure. It assumes you've read the project README and the v0 design doc.

For the rest of the agent (data connectors, NL→SQL, KG retrieval, summarization), see Bhavish's tools under lexsi_ds/agent/tools/.


TL;DR

# Run the full modeling flow offline β€” no Lexsi auth required
python scripts/demo_modeling_offline.py

# Run the test suite (47 hermetic modeling tests + 32 PKDD-gated tests)
python -m pytest tests/ -q

# Bundle a zip for review
bash scripts/package.sh

What was delivered

1. Three production tools (live SDK)

Each replaces a not_implemented stub with a full implementation against the lexsi-sdk==0.1.46 surface. Argument schemas match the design doc Β§4 contract; downstream observation shapes match what summarize_result expects in cache.

File What it does Key SDK call
lexsi_ds/agent/tools/train_tabular_model.py Uploads a context DataFrame + trains a model in one shot. Defaults to XGBoost; supports TabPFN / OrionMSP / TabICL / TabDPT / OrionBix / Mitra / ContextTab (requires compute_type). TabularProject.upload_data(data, tag, config=ProjectConfig, compute_type, tunning_strategy)
lexsi_ds/agent/tools/predict.py Uploads the predict DataFrame under a sibling tag, runs batch inference, surfaces top-K by probability. TabularProject.model_inference(tag, model_name, pod)
lexsi_ds/agent/tools/explain_prediction.py Per-case XAI for the top-K cases (or explicit case_ids). Reads shap_feature_importance directly, calls xai_summary and xai_similar_cases opt-in. Handles both flat and multiclass-nested SHAP dicts. TabularProject.case_predict(unique_identifier, tag, model_name, xai)

2. Offline test infrastructure

The three tools talk to Lexsi over the network. To run them in tests and demos without an SDK token, this delivery includes a faithful test double that mirrors the SDK contract in executable form.

File Purpose
lexsi_ds/agent/testing/__init__.py Re-exports FakeTabularProject, FakeCaseTabular, make_offline_ctx.
lexsi_ds/agent/testing/fakes.py FakeTabularProject mirrors upload_data's two-branch behavior (first-call sets project config + trains; subsequent calls just store tags), foundation-model compute_type check, model_inference, case_predict. FakeCaseTabular mirrors the live class field-for-field for the subset explain_prediction reads. Same role StubLLMClient plays for text. If the SDK ever changes a kwarg name, this file is the canary β€” tests break before production does.

3. Production bootstrap

File Purpose
lexsi_ds/agent/lexsi_bootstrap.py bootstrap_lexsi_handles() β€” env-driven login + project resolution. Mirrors the LexsiTextClient.from_env pattern. Returns LexsiHandles{org, workspace, tab_project, text_project}. Asserts isinstance(tab_project, TabularProject) so wrong-modality misconfiguration fails at boot, not at first tool call.

4. Test suite

File Tests What it covers
tests/test_train_tabular_model.py 15 XGBoost / TabPFN / sample_percentage happy paths; missing_df, bad_target, empty_target, missing_compute_type, no_tab_project error branches; entity-column resolution, tag minting, ProjectConfig 12-key shape, active-model resolution.
tests/test_predict.py 13 Happy path, explicit model_id, top_k bounding, null-target stripping; missing_df, empty_predict_df, no_model_id, no_tab_project; column-name normalization, top-K helpers.
tests/test_explain_prediction.py 15 Auto / explicit case_ids, include_similar, summary caching; no_model_id, no_tab_project, no_predict_tag, no_cases, all_cases_failed; SHAP top-k for flat / nested-multiclass / empty / garbage dicts.
tests/test_modeling_e2e.py 4 Full 3-step flow with cache handoff; predict-without-train fails clean; explain-without-predict fails clean; registry membership.
tests/conftest.py (fixtures) Adds offline fixtures: loan_context_df, loan_predict_df, fake_tab_project, ctx_with_fake, ctx_offline, seed_sql_result.

5. Demo + packaging

File Purpose
scripts/demo_modeling_offline.py Runnable end-to-end demo: train β†’ predict β†’ explain against FakeTabularProject. Flags: --rows, --predict-rows, --top-k, --model-type, --compute-type, --seed, --lexsi (swap fake for real SDK), -v.
scripts/package.sh Produces dist/lexsi_ds_agent-modeling.zip excluding caches / sessions / runs / PKDD source. OUT= env overrides the output path.

Architectural decisions

  1. predict uses batch model_inference; explain_prediction uses per-row case_predict. The per-case XAI path is needed only for the K rows we actually want to explain. Running case_predict on the full predict_df would cost K Γ— the inference time and the bias-monitor / policy hooks would fire K times for no extra information.

  2. No external polling. The live upload_data already calls poll_events internally and blocks until training is done. The agent loop layers wall-clock guards via daemon threads so a stuck backend cannot hang the loop:

    Env var Default Affects
    LEXSI_TRAIN_TIMEOUT_S 900 (15 min) upload_data train call
    LEXSI_PREDICT_TIMEOUT_S 600 (10 min) upload_data predict upload, model_inference
    LEXSI_CASE_TIMEOUT_S 60 per-case case_predict
    LEXSI_XAI_BUDGET_S 300 (5 min) total across all explained cases
  3. Offline-runnable via FakeTabularProject mirroring the live SDK surface, exactly the way StubLLMClient mirrors LexsiTextClient. Pins the SDK contract in executable form so contract drift breaks tests, not production.


Cache contract

This is what links the three tools together and what summarize_result reads in the final step.

After train_tabular_model β†’ predict β†’ explain_prediction:

Cache key Type Set by
last_model_id str (the live Lexsi model_name) train
last_train_tag str train
last_train_df_label str train
last_model_task dict{target_column, entity_column, task_type, model_type} train
last_predict_tag str predict
last_predict_df_label str predict
last_predictions pd.DataFrame (input cols + Prediction + Probability) predict
last_prediction_columns dict{pred, prob, entity} predict
last_xai list[dict] (one entry per case: case_id, pred_value, pred_category, shap_top, shap_full, summary, optionally similar_preview) explain
last_xai_failures list[dict] explain

How to run

Offline (no auth, no compute, no network)

python scripts/demo_modeling_offline.py --rows 100 --predict-rows 25 --top-k 5

Runs all three tools against FakeTabularProject and prints the cache state. The SHAP drivers in the demo are deterministic per seed (not per case) because the fake uses a single seeded RNG β€” the live Lexsi backend produces real per-case SHAP.

Against real Lexsi

export SDK_ACCESS_TOKEN=...                  # from app.lexsi.ai/sdk
export LEXSI_ORG_NAME=personal               # or your org
export LEXSI_WORKSPACE_NAME=your_ws
export LEXSI_TABULAR_PROJECT_NAME=your_proj

python scripts/demo_modeling_offline.py --lexsi --model-type XGBoost

# foundation model:
python scripts/demo_modeling_offline.py --lexsi --model-type TabPFN --compute-type small

Wiring into the live agent loop

from lexsi_ds.agent.context import AgentContext
from lexsi_ds.agent.lexsi_bootstrap import bootstrap_lexsi_handles
from lexsi_ds.llm.client import factory as llm_factory

handles = bootstrap_lexsi_handles()
ctx = AgentContext(
    dataset=...,                        # from Bhavish's connect_datalake / load_pkdd_handle()
    run_id=...,
    org=handles.org,
    text_project=handles.text_project,
    tab_project=handles.tab_project,
    llm=llm_factory("lexsi"),
)
# Three modeling tools are already in the registry; the planner can call them by name.

How to test

python -m pytest tests/                       # all 47 modeling tests + 32 PKDD-gated
python -m pytest tests/test_modeling_e2e.py   # end-to-end only
python -m pytest tests/ -k "happy_path"       # happy paths only
python -m pytest tests/ -k "error or no_"     # error branches only

All modeling tests are hermetic β€” no network, no DuckDB, no Lexsi auth. Bhavish's PKDD-gated tests skip cleanly when the DuckDB isn't bootstrapped.


SDK cross-verification

Every SDK call site was cross-verified against lexsi-sdk==0.1.46 via introspection of installed signatures + reading source bodies for xai_summary, xai_similar_cases, and Workspace.project.

Verified call sites

Call site Live signature in lexsi-sdk==0.1.46 Result
TabularProject.upload_data(data, tag, config, compute_type, tunning_strategy) matches exactly (preserving the SDK's tunning_strategy typo, not tuning_strategy) βœ…
TabularProject.model_inference(tag, model_name, pod) matches βœ…
TabularProject.case_predict(unique_identifier, tag, model_name, xai) matches βœ…
TabularProject.active_model() / .models() returns pd.DataFrame with model_name column β€” matches my reads βœ…
CaseTabular field reads (shap_feature_importance, pred_value, pred_category, summary, similar_cases_data, model_name, data_id, unique_identifier) all present βœ…
CaseTabular.xai_summary() / .xai_similar_cases() source confirmed β€” xai_summary returns string (caches on self.summary); xai_similar_cases returns pd.DataFrame | str; my code handles both βœ…
Login chain: xai.login β†’ xai.organization β†’ Organization.workspace β†’ Workspace.project matches; Workspace.project() is modality-dispatching βœ…
ProjectConfig TypedDict shape exact 12-key match βœ…

Known limitations / deliberately deferred

  • No bias-monitor / risk-policy hookup. The SDK exposes these on case_predict(..., risk_policies=True); not wired in v0 because design doc scope is risk scoring and explanation, not policy enforcement. Single arg passthrough when needed.
  • No PEFT / fine-tune path. Foundation models default to tunning_strategy="inference" (zero-shot) to keep the demo path fast. PEFT / base-ft requires peft_config / tunning_config β€” the SDK supports them; the tool args don't expose them yet. Easy follow-on if needed.
  • Single active model assumed. _resolve_active_model_name falls back to the most recent row in .models() if .active_model() is empty, but doesn't model multiple concurrent active models. The live SDK enforces a single-active-model invariant, so this should be fine.
  • Fake SHAP is deterministic per project, not per case. In the offline demo, top-K cases show the same SHAP drivers because the fake uses a single seeded RNG. The live Lexsi backend produces per-case SHAP β€” this is a cosmetic limitation of the test double, not the production tools.

File index β€” exactly what was added or changed

NEW    lexsi_ds/agent/lexsi_bootstrap.py
NEW    lexsi_ds/agent/testing/__init__.py
NEW    lexsi_ds/agent/testing/fakes.py
REPL   lexsi_ds/agent/tools/train_tabular_model.py    (stub β†’ production)
REPL   lexsi_ds/agent/tools/predict.py                (stub β†’ production)
REPL   lexsi_ds/agent/tools/explain_prediction.py     (stub β†’ production)
NEW    tests/__init__.py
MERGE  tests/conftest.py                              (added offline fixtures alongside existing PKDD fixtures)
NEW    tests/test_train_tabular_model.py              (15 tests)
NEW    tests/test_predict.py                          (13 tests)
NEW    tests/test_explain_prediction.py               (15 tests)
NEW    tests/test_modeling_e2e.py                     (4 tests)
NEW    scripts/demo_modeling_offline.py
NEW    scripts/package.sh
NEW    HANDOFF.md                                     (delivery handoff note)
NEW    MODELING_TOOLS.md                              (this file)