Spaces:
Sleeping
Sleeping
Simplified input for modelfit_chat
Browse files
main.py
CHANGED
|
@@ -160,6 +160,34 @@ async def _fetch_url_to_records(data_url: str) -> list:
|
|
| 160 |
df[col] = df[col].astype(str)
|
| 161 |
return df.to_dict(orient="records")
|
| 162 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
async def _forward(path: str, method: str = "GET", json_body=None, user_token: str | None = None):
|
| 164 |
"""
|
| 165 |
Forward request to the PRIVATE Space:
|
|
@@ -554,32 +582,37 @@ async def modelforecast(req: Request):
|
|
| 554 |
async def modelfit_chat(
|
| 555 |
req: Request,
|
| 556 |
data_url: str = Body(..., embed=True),
|
| 557 |
-
id_col: str = Body(..., embed=True),
|
| 558 |
-
time_col: str = Body(..., embed=True),
|
| 559 |
-
y: str = Body(..., embed=True),
|
| 560 |
-
lag_y: object = Body(None, embed=True),
|
| 561 |
-
lagged_features: object = Body({}, embed=True),
|
| 562 |
-
current_features: object = Body([], embed=True),
|
| 563 |
-
filter_by_significance: bool = Body(False, embed=True),
|
| 564 |
-
meanvar_test: bool = Body(False, embed=True),
|
| 565 |
-
signif: object = Body(0.05, embed=True),
|
| 566 |
):
|
| 567 |
-
"""
|
|
|
|
|
|
|
|
|
|
|
|
|
| 568 |
user_token = _extract_user_token(req)
|
| 569 |
if not user_token:
|
| 570 |
raise HTTPException(status_code=401, detail="Missing Authorization Bearer token (dt+...)")
|
| 571 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 572 |
payload = {
|
| 573 |
-
"df":
|
| 574 |
"id_col": id_col,
|
| 575 |
"time_col": time_col,
|
| 576 |
"y": y,
|
| 577 |
-
"lag_y":
|
| 578 |
-
"lagged_features":
|
| 579 |
"current_features": current_features,
|
| 580 |
-
"filter_by_significance":
|
| 581 |
-
"meanvar_test":
|
| 582 |
-
"signif":
|
| 583 |
}
|
| 584 |
return await _forward("/modelfit/", "POST", json_body=payload, user_token=user_token)
|
| 585 |
|
|
|
|
| 160 |
df[col] = df[col].astype(str)
|
| 161 |
return df.to_dict(orient="records")
|
| 162 |
|
| 163 |
+
|
| 164 |
+
async def _fetch_url_to_dataframe(data_url: str) -> pd.DataFrame:
|
| 165 |
+
"""Fetch data_url (https only), parse CSV/Excel/JSON to DataFrame. Raises HTTPException on error."""
|
| 166 |
+
if not data_url.strip().lower().startswith("https://"):
|
| 167 |
+
raise HTTPException(status_code=400, detail="data_url must be an HTTPS URL.")
|
| 168 |
+
async with httpx.AsyncClient(timeout=DATA_URL_FETCH_TIMEOUT, follow_redirects=True) as client:
|
| 169 |
+
r = await client.get(data_url)
|
| 170 |
+
r.raise_for_status()
|
| 171 |
+
raw = r.content
|
| 172 |
+
content_type = (r.headers.get("content-type") or "").lower()
|
| 173 |
+
if len(raw) > DATA_URL_MAX_BYTES:
|
| 174 |
+
raise HTTPException(status_code=413, detail=f"Data at URL exceeds {DATA_URL_MAX_BYTES // (1024*1024)}MB limit.")
|
| 175 |
+
path_lower = data_url.split("?")[0].lower()
|
| 176 |
+
try:
|
| 177 |
+
if "json" in content_type or path_lower.endswith(".json"):
|
| 178 |
+
df = pd.read_json(io.BytesIO(raw))
|
| 179 |
+
elif "spreadsheet" in content_type or "excel" in content_type or path_lower.endswith((".xlsx", ".xls")):
|
| 180 |
+
df = pd.read_excel(io.BytesIO(raw))
|
| 181 |
+
else:
|
| 182 |
+
df = pd.read_csv(io.BytesIO(raw))
|
| 183 |
+
except Exception as e:
|
| 184 |
+
raise HTTPException(status_code=400, detail=f"Could not parse data from URL: {str(e)[:200]}")
|
| 185 |
+
for col in df.columns:
|
| 186 |
+
if pd.api.types.is_datetime64_any_dtype(df[col]):
|
| 187 |
+
df[col] = df[col].astype(str)
|
| 188 |
+
return df
|
| 189 |
+
|
| 190 |
+
|
| 191 |
async def _forward(path: str, method: str = "GET", json_body=None, user_token: str | None = None):
|
| 192 |
"""
|
| 193 |
Forward request to the PRIVATE Space:
|
|
|
|
| 582 |
async def modelfit_chat(
|
| 583 |
req: Request,
|
| 584 |
data_url: str = Body(..., embed=True),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 585 |
):
|
| 586 |
+
"""
|
| 587 |
+
Fetch training data from data_url (HTTPS), infer schema from column order, then call /modelfit/ on the API.
|
| 588 |
+
Column order: 1st = id_col, 2nd = time_col, 3rd..second-to-last = current_features, last = y.
|
| 589 |
+
All other parameters use defaults. Returns JSON fit result.
|
| 590 |
+
"""
|
| 591 |
user_token = _extract_user_token(req)
|
| 592 |
if not user_token:
|
| 593 |
raise HTTPException(status_code=401, detail="Missing Authorization Bearer token (dt+...)")
|
| 594 |
+
df = await _fetch_url_to_dataframe(data_url)
|
| 595 |
+
cols = list(df.columns)
|
| 596 |
+
if len(cols) < 3:
|
| 597 |
+
raise HTTPException(
|
| 598 |
+
status_code=400,
|
| 599 |
+
detail="Data must have at least 3 columns (order: id_col, time_col, ...current_features..., y).",
|
| 600 |
+
)
|
| 601 |
+
id_col = cols[0]
|
| 602 |
+
time_col = cols[1]
|
| 603 |
+
y = cols[-1]
|
| 604 |
+
current_features = cols[2:-1] # empty if exactly 3 columns
|
| 605 |
payload = {
|
| 606 |
+
"df": df.to_dict(orient="records"),
|
| 607 |
"id_col": id_col,
|
| 608 |
"time_col": time_col,
|
| 609 |
"y": y,
|
| 610 |
+
"lag_y": None,
|
| 611 |
+
"lagged_features": {},
|
| 612 |
"current_features": current_features,
|
| 613 |
+
"filter_by_significance": False,
|
| 614 |
+
"meanvar_test": False,
|
| 615 |
+
"signif": 0.05,
|
| 616 |
}
|
| 617 |
return await _forward("/modelfit/", "POST", json_body=payload, user_token=user_token)
|
| 618 |
|